mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
c7dc12e90f
- Bug 1251253 - prevent null pointer dereference of |aContext| in CacheStorageService::DoomStorageEntries. r=mayhemer (35b449c612)
- Bug 1260498 - Make test_rel_preconnect work in e10s mode. r=mcmanus (e6823ce4c4)
- Bug 1016628 - Add prefetch abilities to the predictor. r=mayhemer (53ab180c97)
- Bug 1258482 - FileList should contain only Files, not Directories, r=smaug (ff78125454)
- Bug 1258694 - Implement Directory::GetFiles(), r=smaug (305784524e)
- Bug 1263992 - patch 1 - Remove DirectoryType enum, r=smaug (89e1a59041)
- Bug 1263992 - patch 2 - Support the creation of directories from FileSystemTasks, r=smaug (c569092cef)
- Bug 1243586 - Implement Upgrade-Insecure-Requests HTTP Request Header Field. r=rbarnes (4b8a84c656)
- Bug 1262572 - http 0.9 telemetry. r=hurley (6006881336)
- Bug 587177 - Update all comments before SetOriginalURI to reflect reality, r=mcmanus (b2fedb0728)
- Bug 1261632 - Assert that OnStopRequest is called only once. r=michal (c35b1922b9)
- Bug 1232422 - Convert 5 tests within netwerk/test to use AsyncOpen2 (r=mcmanus) (4af8d43814)
- Bug 831450 - No Range Requests against weak Etag r=mayhemer (9b4a159e1e)
- Bug 1214277 - Avoid bypassing opening a cache entry for possibly intercepted channels; r=mcmanus (c5b0de6990)
- partial apply Bug 1234369 - Convert 25 tests within netwerk/test to use AsyncOpen2 (1b81d5a303)
- Bug 299031 - heuristic cache rule for 410 should be longer r=mayhemer (848834fc31)
- Bug 1121447 - trust cache less for error codes r=mayhemer (0424fec819)
- Bug 1125916 - Check whether loadInfo and loadContext match. r=sicking, r=jduell (6740850922)
- Bug 1258778 - Purge the skia glyph cache when receiving a low memory notice. r=erahm (633c60b0c6)
- Bug 1125916 - Add SEC_FORCE_PRIVATE_BROWSING to LoadInfo. r=sicking, r=jduell (10b5a1cacb)
- Bug 1105556 - test fixes. r=sicking, ckerschb (845d0dbd65)
- Bug 1258481 - Use RegionBuilder for nsRegion IPC. r=jrmuizel (616c279297)
- Bug 1014691 - Fix an include-what-you-use error in TestCompositor.cpp. r=kats (2797f83f1d)
- Bug 1256408 - Add graphics microbenchmarking. r=mstange (49b11b051b)
- Bug 1258481 - Add a RegionBuilder for accumulating rects. r=jrmuizel (acd79192db)
- Track whether or not remote layers have acknowledged compositor changes. (bug 1256517 part 1, r=mattwoodrow) (e3cc77ed41)
- Move compositable field out of individual compositable ops. (bug 1256517 part 2, r=mattwoodrow) (1d4a063df3)
- Bug 1241058: Assure several operations properly operate on the current group target. r=jrmuizel (6119e2b4db)
- Bug 1247700: Avoid crash from invalid fonts. r=bas (c4c2799b94)
- Bug 1242421 - remove useless null check. r=roc@ocallahan.org (44faf6556d)
- Add instrumentation for when content processes fail to acquire D3D11 devices. (bug 1247539, r=milan) (bd9265d78e)
- Make access to gfxWindowsPlatform D3D11 devices thread-safe. (bug 1258174, r=bas) (032e74b163)
- Disable device access on textures created against stale layers. (bug 1256517 part 3, r=nical) (ffcebbdee6)
- Block compositable updates from stale layers. (bug 1256517 part 4, r=mattwoodrow,nical) (fc83339f2d)
- Fix build bustage for bug 1256517 r=broken tree (3952871373)
- Bug 1256678 - Replace DrawTargetCairo::FillGlyphs crashes with other crashes - r=bobowen (b7245ba436)
- Bug 1120485. Log CloseHandle error reason during MessageChannel shutdown failure. r=milan (2f81d9c2aa)
- Bug 1242448 - Ensure the tile pool does not hold textures during shutdown. r=edwin (d31c304258)
- Bug 1258851 - Propagate the isScrollbarContainer layer flag to the compositor. r=mattwoodrow (b9906d7557)
- Bug 1260391: Transfer |CompositableOperation| in |AsyncChildMessageData|, r=dvander (1def34c5f8)
- Bug 1252324 - add DrawTarget API for 3D transforms for use in layers. r=jrmuizel (bc80529422)
- Bug 1255342 - implement DrawTargetCairo::LockBits for Cairo Win32 surfaces. r=sotaro (6f5661691b)
- Bug 1263480 - Don't let cairo go into an error state when DrawSurface is called with an empty destination rectangle. r=lsalzman (4300940101)
- Bug 1251241 - return from DrawTargetCairo::FillGlyphs if |aFont| is ullptr. r=roc (bb92f95ccd)
- Bug 1255320 - Create DrawTarget with DIB as similar DrawTarget r=jrmuizel (03f1da030d)
- Bug 1215265 - Put shutting down gfx ipdl protocols for child processes behind a pref. r=sotaro (aa781b37f9)
- Bug 1262898: Keep the GeckoChildProcessHost alive for the lifetime of the CompositorBridge and ImageBridge parent actors. r=jimm r=nical (dcca3b54e1)
- Bug 1251619: Remove unused gfxPlatform::CreateDrawTargetForUpdateSurface r=mchang (b29565995e)
- Bug 1255973 - Remove redundant overrides from gfxPlatform subclasses. r=jfkthame (d45f8a6640)
- Bug 1259466. Rename layers.offmainthreadcomposition.enabled. r=milan We would rather people not use this pref. (f362da1bd3)
- Bug 881609: Call InitLayersAccelerationPrefs only once. r=nrc (faed10a0d4)
- Bug 1209780. Mark some DrawResult's as unused in layout/svg. r=seth (48192d6b34)
- Bug 1251115 - Fix incorrect rendering result while mask path is not resolvable; r=mstange (a52b478fdf)
- Bug 1228354 - Part1 - Support luminance mask mode. r=mstange r=bas (b03abbe8a6)
- Bug 1228354 - Part2. Add test case for mask-mode. r=heycam (6bea36a70c)
- Bug 1259802: Add type replacement annotations to simplify rust binding generation for nsStyleStruct.h, r=bholley (48c13e62f2)
- Bug 1261754 - Part 1: Improve static assertions for style struct bits. r=dholbert (2ce6d994a5)
- Bug 1261552 - Reimplement default placement-new for style structs. r=heycam (db9d7782e2)
- Bug 1261552 - Introduce StaticPresData and hoist some shared functionality into it. r=heycam (adf2e16b4d)
- Bug 1226627 - Truncate the result in ZoomText/UnZoomText rather than rounding it for better performance. r=roc (f1d1084ba1)
- Bug 1247777 - Part1: parse and compute -webkit-text-fill-color property. r=heycam (fc4161355c)
- Bug 1247777 - Part2: implement -webkit-text-fill-color rendering. r=jfkthame (0f30da9c5b)
- Bug 1247777 - Part3: reftests for -webkit-text-fill-color. r=jfkthame Add this test into web-platform-tests. (02e41db8cc)
- Bug 1043461 - Followup to ensure we still test custom property position when the UA style sheet doesn't have custom properties in it. r=dholbert (48df73d684)
- Bug 1247777 - Part4.1: replace windows-style line endings with unix-style line endings. r=bz (be8ba60960)
- Bug 1247777 - Part4.2: add compatible webkit prefixed properties in CSS properties ordering check test. r=bz (7b78825e14)
- Bug 1261552 - Introduce StyleStructContext, and make all style struct constructors take it. r=heycam (65b3966841)
- Bug 1258017 - Use an nsCOMPtr to hold onto the nsIStyleRule. r=dbaron (e88d7e368f)
- Bug 1258017 - Use a RefPtr to hold onto the parent style context. r=dbaron (6a7289ca43)
- Bug 1258017 - Redesign and simplify rule tree GC. r=dbaron (3bf60a9b04)
- Bug 1253149 - Remove the #ifdef __cplusplus bits from ServoBindings.h. r=SimonSapin (bf2b18a470)
- Bug 1251496 - Forward stylesheet management to RawServoStyleSet. r=heycam (0a3aa90b2d)
- Bug 1260310 - Generalize nsStyleContext to support resolving styles from either nsRuleNode or ServoComputedValues. r=heycam (82b6d5d008)
- Bug 1258017 - Cleanup fixes for trunk. r=me (674a65815a)
- Bug 1236400 part 1: Add internal enum values to represent "display: -webkit-box" & "display: -webkit-inline-box". r=mats (509c94da15)
- Bug 1236400 part 2: Extend NeedsAnonFlexOrGridItem() & related code to wrap all inline-level -webkit-box children in an anonymous flex item. r=mats (dc11b9b09f)
- Bug 1236400 part 3: If webkit prefix support is enabled, skip CSS Parser code that converts "display: -webkit-box" directly to "display: flex". r=mats (e09b459124)
- Bug 1236400 part 4: Add reftests to test how non-block-level content gets wrapped inside a -webkit-box. (no review) (46e4d8cb07)
- Bug 1261754 - Part 2: Make quotes computed values shareable between different structs. r=dholbert (a78e43b706)
- Bug 1261754 - Part 3: Move quotes from nsStyleQuotes to nsStyleList and delete nsStyleQuotes. r=dholbert (fdcd9aaa3f)
- Bug 1209273 - Part 1: Support for adjust-color CSS property. r=dbaron (818a7fe0ff)
- Bug 1209273 - Part 2: Force printing background if color-adjust: exact. r=dbaron (ffd52c0dbc)
- Bug 1261754 - Part 4: Move image-rendering from nsStyleSVG to nsStyleVisibility. r=dholbert (ee8372fb94)
- Bug 1261754 - Part 5: Move text-rendering from nsStyleSVG to nsStyleText. r=dholbert (c13a11313d)
- Bug 1261754 - Part 6: Move vertical-align from nsStyleTextReset to nsStyleDisplay. r=dholbert (d374b3700b)
- Bug 1261754 - Part 7: Move pointer-events from nsStyleVisibility to nsStyleUserInterface. r=dholbert (8693251243)
- Bug 1261754 - Part 8: Move box-shadow from nsStyleBorder to a new nsStyleEffects struct. r=dholbert (8263476827)
- Bug 1261754 - Part 9: Move clip from nsStyleDisplay to nsStyleEffects. r=dholbert (5418597309)
- Bug 1261754 - Part 10: Move mix-blend-mode from nsStyleDisplay to nsStyleEffects. r=dholbert (ebae613929)
- Bug 1261754 - Part 11: Move opacity from nsStyleDisplay to nsStyleEffects. r=dholbert (589292af44)
- Bug 1187851 patch 6 - Make dynamic changes to filter change fixed position containing block for descendants. r=roc (003a3aa6ce)
- Bug 1261754 - Part 12: Move filter from nsStyleSVGReset to nsStyleEffects. r=dholbert (78d87914f9)
- Bug 1259513: Make gfxContext constructor private, use a utility function that can return nullptr. r=bas,lsalzman (43df6e429f)
- Bug 1259785: Do a proper flush when taking a snapshot so our dependent targets and command lists get appropriately cleared. r=jrmuizel (9f7372cce1)
- Bug 1251431 - Part 1: Allow usage of an A8 source pattern to MaskSurface for D2D 1.1 Moz2D backend. r=jwatt (632eb6d2da)
- Bug 1251431 - Part 2: Do not apply the device transform when drawing to an already intermediate surface. r=jwatt (3a24f4a5c6)
- Bug 1251431 - GCC compilation fixup. (2356f0a58c)
- Bug 1238328: Purge stored command lists by calling EndDraw/BeginDraw on a regular basis when they're used. r=jrmuizel (33f47b281f)
- Bug 1246641: Also execute an occasional EndDraw for CommandLists used by non-operator OVER drawing. r=jrmuizel (b3e03ad111)
- Bug 1258168: Push ClearType compatible clipping layers when the last pushed layer was marked as opaque. r=jrmuizel (bd069ad7b6)
- Bug 1264736: Crash sooner if we can't get a valid command list, at least in nightly/aurora. r=bas (fb4bb56815)
- Bug 1255438 - create nsI{Mutable,}Array directly; r=keeler (1b802b23b7)
- Bug 1255438 - fix OS X warning bustage and reopen this CLOSED TREE; r=me (07a05910a6)
- bug 1197314: Remove PR_snprintf calls in security/manager/ssl/ r=keeler (f2271aad87)
- Bug 1258298 - Switch more Scoped.h templates in PSM to UniquePtr equivalents. r=keeler (2ee1a85d8e)
- Bug 1191414 - gather telemetry on usage of <keygen>. r=keeler,r=vladan (150bad38a1)
- Bug 1260644 - Use UniquePLArenaPool to manage PLArenaPools in PSM. r=keeler (9e8ad9c0d4)
- Bug 1247250 - Enable TLS 1.3 anti-downgrade on non-secure fallback. r=keeler (7a950b427a)
- Bug 1215796 - Remove the static fallback whitelist. r=keeler (fa55b5920b)
- bug 1254667 - change certificate verification SHA1 policy to "allow or locally-installed roots" r=jcj (5d0bb9e8b1)
- bug 1245280 - add policy mechanism to optionally enforce BRs for falling back to subject CN r=Cykesiopka,mgoodwin (ecd4f2180a)
- Bug 1254653 - Add telemetry to measure how often we encounter EV certificates r=keeler (9da287b490)
- Bug 1259909 - Obviate char PORT_Free() calls in PSM. r=keeler (b7ba2a47da)
- Bug 1252882 - Add a Content Signature Service r=keeler,r=franziskus,r=Cykesiopka (8b806022a0)
- Bug 1255784 - u2f tests should use SpecialPowers.pushPrefEnv, r=jjones (839a58476f)
- Bug 1244960 - Complete FIDO u2f NSSToken (Part 1). r=keeler, r=baku (3d64aa2b7c)
- Bug 1244960 - FIDO u2f NSSToken (Part 2): Use Attestation Certificates. r=keeler (aee3ffc830)
- Bug 1244960 - FIDO u2f NSSToken (Part 3): Review updates. r=keeler (b2f81c2b72)
- Bug 1244960 - FIDO u2f NSSToken (Part 4): Correct FacetID base algorithm. r=keeler (9e70506580)
- Bug 1244960 - FIDO u2f NSSToken (Part 5): Review updates. r=keeler (62a28f2502)
- Bug 1231643 - Part 1. Create skia-A8-surface for mask composition when backendtype of the source DrawTarget is CG; r=mstange (dd03d86f55)
- Bug 1244598 - Move resource files of w3c-css/masking into ./support subdir. r=dbaron (4c9e789191)
- Bug 1243675 - Part 1. Add mask-image property reftest. r=dbaron (18e5dfa90b)
- Bug 1243675 - Part 2. Add mask-clip property reftest. r=dbaron (ddf834d408)
- Bug 1243675 - Part 3. Add mask-position property reftest. r=dbaron (68cae7c7e6)
- Bug 1243675 - Part 4. Add mask-repeat property reftest. r=dbaron (0a3ed45377)
- Bug 1243675 - Part 5. Add mask-origin property reftest. r=dbaron (f5785145a7)
- Bug 1243675 - Part 6. Add mask-size property reftest. r=dbaron (1ab2040973)
- Bug 1231643 - Part 2. Enable mask-composite reftest; r=dbaron (8c3b863d97)
- Bug 1263622 - Fixed nsNSSComponent.cpp compilation on mingw. r=dkeeler,ted (0e651c0211)
- Bug 1266249 - Remove mHasCachedOutline. r=dbaron (c46459acf2)
- Bug 1235634 - Construct nsNSSShutdownList::singleton lazily on first use r=keeler (1b53753c2e)
- Bug 1262645 - Address misc issues with nsGetUserCertChoice(). r=keeler (ec675be29a)
- Bug 1238001 - Allow TLS info to be updated on renegotiation, r=keeler (a2ec0c8a07)
- Bug 1201437 - Add new WebProgress state flag for user-overridden cert. r=keeler (0b9edbc8d8)
- Bug 1201437 - Make cert override tests check for STATE_CERT_USER_OVERRIDDEN. r=keeler (5246515084)
- bug 1261936 - stop using the subject common name in certificate verification error messages r=Cykesiopka (982cf43a11)
- bug 1230234 - fix a leak in client auth certificate handling r=Cykesiopka (6e83f81218)
- Bug 1260643 - Convert most uses of ScopedCERTCertificate in PSM to UniqueCERTCertificate. r=keeler (806b895c41)
- Bug 1207137 - Set a security state flag when weak crypto override is needed. r=keeler Bug 1254306 - Do not check the fallback limit version for the RC4 fallback. r=keeler (8b5cb7101f)
- Bug 1253010 - part 3 - create all nsIDateTimeFormat instances directly; r=smontagu (c1aa5d1d62)
- Bug 1260310 - Create servo style contexts from ServoStyleSet. r=heycam (05f876eb13)
- Bug 759568 - Part 1. Parse background-clip:text; r=dholbert r=heycam (d013b8fd84)
- Bug 1251995 part 6 - Use struct to pass params for nsTextFrame::PainText* functions. r=jfkthame (3b9c163eab)
- Bug 759568 - Part 2. Render background-clip:text; r=jfkthame (e534e048bf)
- Bug 759568 - Part 4. mochitest for background-clip:text; r=heycom (3e548ebf99)
- Bug 759568 - Part 5. reftest for background-clip:text; r=dbaron (43d2915305)
- Bug 759568 - Part 6. Remove unused nsDisplayList::mVisibleRect; r=jfkthame (960a85de40)
- Bug 1264910 - Simplify pref callback register/unregister in nsLayoutUtils. r=dholbert (f50219f117)
- Bug 1097499 part 1 - Control support of 'text-combine-upright: digits' via a separate pref. r=heycam (37df36e815)
- Bug 1261062 - When constraining the displayport by the max texture size, maintain the relative distribution of the margins. r=dvander (9a9423bdf1)
- Bug 1246290 - Add a pref to allow disabling APZ on documents which have scroll-linked effects. r=botond (781b63c578)
- Bug 1263347 - When checking if displayport changes should schedule a paint, make sure to use the proper displayport. r=mstange (998f59843e)
- Bug 1097499 part 15 - Add reftests for text-combine-upright. r=jfkthame (843bea00bc)
- Bug 1097499 followup - Fix metadata of tests submitted to w3c. DONTBUILD (e671b5b38b)
- Bug 1097499 followup 2 - Fix metadata of tests submitted to w3c. DONTBUILD (abf0895450)
- Bug 1097499 part 2 - Add a macro to simplify usage of nsStyleContext::GetUniqueStyleData. r=heycam (10486f1f24)
- Bug 1097499 part 3 - Add a separate anonbox for text nodes. r=heycam (7dd4347215)
- Bug 1097499 part 4 - Adjust computed value of writing-mode on text frames when text-combine-upright is used. r=heycam (c193f14b27)
- Bug 1097499 part 5 - Layout text combine upright. r=jfkthame (c21422930b)
- Bug 1097499 part 6 - Inherit move direction from parent for horizontal-in-vertical text. r=jfkthame (cf436b8494)
- Bug 1097499 part 7 - Add reverse function of GetFullWidth. r=emk (32d02e7437)
- Bug 1097499 part 8 - Move CountGraphemeClusters to mozilla::unicode. r=emk (e2b8942e53)
- Bug 1156588 - Add crashtest. (237adb0604)
- Bug 1234622. Tweak how nsDocumentViewer::FindContainerView finds the parent presshell. r=bz (d1e76ae2e9)
- Bug 1245978 part 1: Make nsDocumentViewer::CreateStyleSet directly return the thing it creates. r=heycam (ede16260a4)
- Bug 1245978 part 2: Drop redundant 'virtual' keyword from NS_DECL_NSIDOCUMENTVIEWERPRINT macro (which already includes 'override' keyword). r=heycam (42b8962e4f)
- Bug 1183879 - Soften "non-subdocument frame" warning to also allow dummy nsFrames, which exist while subdocument is loading. r=dholbert (6ebcb53421)
- Bug 1259246. Move nsIPresShell::GetRealPrimaryFrameFor to nsLayoutUtils::GetRealPrimaryFrameFor. r=dholbert (d3efd2f03a)
- Bug 645647 part 1 - Don't let empty bullet frames block suppressing white-space in intrinsic size calculations. r=dholbert (2ce0a86bfb)
- Bug 645647 part 2 - Reftests. (496e491990)
- Bug 645647 part 3 - Remove unused trailingTextFrame member. r=dholbert (bd26ea25e6)
- Bug 645647 part 4 - Add an 'm' prefix to some members to follow our naming conventions. r=dholbert (fe3c5240c9)
- Bug 1097499 part 9 - Transform full-width characters to non-full-width correspondents for combined text. r=jfkthame (5b1eafe2a7)
- Bug 1097499 part 10 - Add fwid/hwid/twid/qwid font feature support to gfx. r=jfkthame (682698dd38)
- Bug 1097499 part 11 - Set width variant for text-combined frame. r=jfkthame (937f61e0e9)
- Bug 1097499 part 12 - Handle spacing sensibly for text-combine-upright. r=jfkthame (9ae1ab2941)
- Bug 1220438 - Correct baseline offset computation of text decoration for vertical-rl. r=jfkthame (10ad32d702)
- Bug 1258636 part 1 - Use structs to pass params for decoration-related functions in nsCSSRendering. r=jfkthame (deef7071f1)
- Bug 1258636 part 2 - Use struct to pass params for nsTextFrame::PaintDecorationLine. r=jfkthame (df5bde2547)
- Bug 1229743 part 1 - Simplify text decoration handling code with lambda function and range-based for loop. r=jfkthame (51cd3ea4ca)
- Bug 1229743 part 2 - Fix up decoration rect computation for vertical-rl and sideways-lr. r=jfkthame (0113279f53)
- Bug 1251995 part 7 - Use struct to pass params for nsTextFrame::Paint*Shadow functions. r=jfkthame (e81ba231aa)
- Bug 759568 - Part 3. Render text-selection beneath background image; r=jfkthame (e6757762ff)
- Bug 1097499 part 13 - Draw decoration line properly for text-combine-upright. r=jfkthame (8f4be7f987)
- Bug 1264120. Remove usage of nsAutoPtr from gfx/src. r=jfkthame (6831454d8c)
- Bug 1119619 - Allow font-selection to fall back to an alternative face within the same family if the first-found face was not Regular, to handle cases where some styled faces have a reduced character set. r=m_kato (d8851b2877)
- Bug 1243226 - relax the limit on fontconfig generics. r=heycam, a=me (05df737d0e)
- Bug 1245811 - part 1 (based on patch by Andrew Comminos) - Replace gfxPlatformFontList::FindFamily with FindAndAddFamilies to allow for the possibility of the implementation returning multiple font families (e.g. when fontconfig has 'prefer' aliases). r=karlt (2bef9fafb0)
- Bug 1245811 - part 2 (based on patch by Andrew Comminos) - Let gfxFcPlatformFontList return multiple families for a given name once fontconfig substitutions have been applied. r=karlt (1ffb425a0e)
- Bug 1265452 - Remove use of nsAutoPtr from gfx/thebes. r=jrmuizel (d02c913ad5)
- Bug 1265459 - Replace uses of nsAutoPtr<gfxTextRun> with UniquePtr, and let MakeTextRun and similar methods return a UniquePtr. r=jrmuizel (da32e376b7)
- Bug 1097499 part 14 - Draw emphasis marks properly for text-combine-upright. r=jfkthame (c9115615c6)
- Bug 1097499 part 16 - Enable text-combine-upright by default. r=jfkthame (b616987f95)
- Bug 1261699 - preserve user fontconfig autohint settings in Cairo glyph rendering options. r=jfkthame (3e46dff5ff)
- Bug 1216001 - Fix a typo that eliminated a possible paint optimization. r=xidorn (6a350cadb7)
- Bug 1261568 - part1: take -webkit-text-fill-color into consideration while (d49cf427ab)
- Bug 1261568 - part2.1: update manifest before adding test. r=jgraham Bug 1261568 - part2.2: add reftest. r=jfkthame (ef3c22cfb4)
1828 lines
58 KiB
C++
1828 lines
58 KiB
C++
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include <initguid.h>
|
|
#include "DrawTargetD2D1.h"
|
|
#include "FilterNodeSoftware.h"
|
|
#include "GradientStopsD2D.h"
|
|
#include "SourceSurfaceD2D1.h"
|
|
#include "RadialGradientEffectD2D1.h"
|
|
|
|
#include "HelpersD2D.h"
|
|
#include "FilterNodeD2D1.h"
|
|
#include "ExtendInputEffectD2D1.h"
|
|
#include "Tools.h"
|
|
|
|
using namespace std;
|
|
|
|
// decltype is not usable for overloaded functions.
|
|
typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
|
|
D2D1_FACTORY_TYPE factoryType,
|
|
REFIID iid,
|
|
CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
|
|
void **factory
|
|
);
|
|
|
|
namespace mozilla {
|
|
namespace gfx {
|
|
|
|
uint64_t DrawTargetD2D1::mVRAMUsageDT;
|
|
uint64_t DrawTargetD2D1::mVRAMUsageSS;
|
|
IDWriteFactory *DrawTargetD2D1::mDWriteFactory;
|
|
ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr;
|
|
|
|
ID2D1Factory1 *D2DFactory1()
|
|
{
|
|
return DrawTargetD2D1::factory();
|
|
}
|
|
|
|
DrawTargetD2D1::DrawTargetD2D1()
|
|
: mPushedLayers(1)
|
|
, mUsedCommandListsSincePurge(0)
|
|
{
|
|
}
|
|
|
|
DrawTargetD2D1::~DrawTargetD2D1()
|
|
{
|
|
PopAllClips();
|
|
|
|
if (mSnapshot) {
|
|
// We may hold the only reference. MarkIndependent will clear mSnapshot;
|
|
// keep the snapshot object alive so it doesn't get destroyed while
|
|
// MarkIndependent is running.
|
|
RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
|
|
// mSnapshot can be treated as independent of this DrawTarget since we know
|
|
// this DrawTarget won't change again.
|
|
deathGrip->MarkIndependent();
|
|
// mSnapshot will be cleared now.
|
|
}
|
|
|
|
if (mDC) {
|
|
// The only way mDC can be null is if Init failed, but it can happen and the
|
|
// destructor is the only place where we need to check for it since the
|
|
// DrawTarget will destroyed right after Init fails.
|
|
mDC->EndDraw();
|
|
}
|
|
|
|
// Targets depending on us can break that dependency, since we're obviously not going to
|
|
// be modified in the future.
|
|
for (auto iter = mDependentTargets.begin();
|
|
iter != mDependentTargets.end(); iter++) {
|
|
(*iter)->mDependingOnTargets.erase(this);
|
|
}
|
|
// Our dependencies on other targets no longer matter.
|
|
for (TargetSet::iterator iter = mDependingOnTargets.begin();
|
|
iter != mDependingOnTargets.end(); iter++) {
|
|
(*iter)->mDependentTargets.erase(this);
|
|
}
|
|
}
|
|
|
|
already_AddRefed<SourceSurface>
|
|
DrawTargetD2D1::Snapshot()
|
|
{
|
|
if (mSnapshot) {
|
|
RefPtr<SourceSurface> snapshot(mSnapshot);
|
|
return snapshot.forget();
|
|
}
|
|
PopAllClips();
|
|
|
|
Flush();
|
|
|
|
mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
|
|
|
|
RefPtr<SourceSurface> snapshot(mSnapshot);
|
|
return snapshot.forget();
|
|
}
|
|
|
|
// Command lists are kept around by device contexts until EndDraw is called,
|
|
// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
|
|
// are expensive though, especially relatively when little work is done, so
|
|
// we try to reduce the amount of times we execute these purges.
|
|
static const uint32_t kPushedLayersBeforePurge = 25;
|
|
|
|
void
|
|
DrawTargetD2D1::Flush()
|
|
{
|
|
if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
|
|
mPushedLayers.size() == 1) {
|
|
// It's important to pop all clips as otherwise layers can forget about
|
|
// their clip when doing an EndDraw. When we have layers pushed we cannot
|
|
// easily pop all underlying clips to delay the purge until we have no
|
|
// layers pushed.
|
|
PopAllClips();
|
|
mUsedCommandListsSincePurge = 0;
|
|
mDC->EndDraw();
|
|
mDC->BeginDraw();
|
|
} else {
|
|
mDC->Flush();
|
|
}
|
|
|
|
// We no longer depend on any target.
|
|
for (TargetSet::iterator iter = mDependingOnTargets.begin();
|
|
iter != mDependingOnTargets.end(); iter++) {
|
|
(*iter)->mDependentTargets.erase(this);
|
|
}
|
|
mDependingOnTargets.clear();
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::DrawSurface(SourceSurface *aSurface,
|
|
const Rect &aDest,
|
|
const Rect &aSource,
|
|
const DrawSurfaceOptions &aSurfOptions,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
|
|
|
D2D1_RECT_F samplingBounds;
|
|
|
|
if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) {
|
|
samplingBounds = D2DRect(aSource);
|
|
} else {
|
|
samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height));
|
|
}
|
|
|
|
Float xScale = aDest.width / aSource.width;
|
|
Float yScale = aDest.height / aSource.height;
|
|
|
|
RefPtr<ID2D1ImageBrush> brush;
|
|
|
|
// Here we scale the source pattern up to the size and position where we want
|
|
// it to be.
|
|
Matrix transform;
|
|
transform.PreTranslate(aDest.x - aSource.x * xScale, aDest.y - aSource.y * yScale);
|
|
transform.PreScale(xScale, yScale);
|
|
|
|
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, transform, ExtendMode::CLAMP);
|
|
|
|
if (!image) {
|
|
gfxWarning() << *this << ": Unable to get D2D image for surface.";
|
|
return;
|
|
}
|
|
|
|
RefPtr<ID2D1Bitmap> bitmap;
|
|
if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
|
|
// If this is called with a DataSourceSurface it might do a partial upload
|
|
// that our DrawBitmap call doesn't support.
|
|
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
|
}
|
|
|
|
if (bitmap && aSurfOptions.mSamplingBounds == SamplingBounds::UNBOUNDED) {
|
|
mDC->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(aSource));
|
|
} else {
|
|
// This has issues ignoring the alpha channel on windows 7 with images marked opaque.
|
|
MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::B8G8R8X8);
|
|
mDC->CreateImageBrush(image,
|
|
D2D1::ImageBrushProperties(samplingBounds,
|
|
D2D1_EXTEND_MODE_CLAMP,
|
|
D2D1_EXTEND_MODE_CLAMP,
|
|
D2DInterpolationMode(aSurfOptions.mFilter)),
|
|
D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
|
|
getter_AddRefs(brush));
|
|
mDC->FillRectangle(D2DRect(aDest), brush);
|
|
}
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::DrawFilter(FilterNode *aNode,
|
|
const Rect &aSourceRect,
|
|
const Point &aDestPoint,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
|
|
gfxWarning() << *this << ": Incompatible filter passed to DrawFilter.";
|
|
return;
|
|
}
|
|
|
|
PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
|
|
|
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
|
|
|
FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
|
|
node->WillDraw(this);
|
|
|
|
mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
|
const Point &aDest,
|
|
const Color &aColor,
|
|
const Point &aOffset,
|
|
Float aSigma,
|
|
CompositionOp aOperator)
|
|
{
|
|
MarkChanged();
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
Matrix mat;
|
|
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
|
|
|
|
if (!image) {
|
|
gfxWarning() << "Couldn't get image for surface.";
|
|
return;
|
|
}
|
|
|
|
if (!mat.IsIdentity()) {
|
|
gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces.";
|
|
return;
|
|
}
|
|
|
|
// Step 1, create the shadow effect.
|
|
RefPtr<ID2D1Effect> shadowEffect;
|
|
HRESULT hr = mDC->CreateEffect(CLSID_D2D1Shadow, getter_AddRefs(shadowEffect));
|
|
if (FAILED(hr) || !shadowEffect) {
|
|
gfxWarning() << "Failed to create shadow effect. Code: " << hexa(hr);
|
|
return;
|
|
}
|
|
shadowEffect->SetInput(0, image);
|
|
shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma);
|
|
D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a };
|
|
shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
|
|
|
|
D2D1_POINT_2F shadowPoint = D2DPoint(aDest + aOffset);
|
|
mDC->DrawImage(shadowEffect, &shadowPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
|
|
|
|
D2D1_POINT_2F imgPoint = D2DPoint(aDest);
|
|
mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::ClearRect(const Rect &aRect)
|
|
{
|
|
MarkChanged();
|
|
|
|
PopAllClips();
|
|
|
|
PushClipRect(aRect);
|
|
|
|
if (mTransformDirty ||
|
|
!mTransform.IsIdentity()) {
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
}
|
|
|
|
D2D1_RECT_F clipRect;
|
|
bool isPixelAligned;
|
|
if (mTransform.IsRectilinear() &&
|
|
GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
|
|
mDC->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
|
mDC->Clear();
|
|
mDC->PopAxisAlignedClip();
|
|
|
|
PopClip();
|
|
return;
|
|
}
|
|
|
|
RefPtr<ID2D1CommandList> list;
|
|
mUsedCommandListsSincePurge++;
|
|
mDC->CreateCommandList(getter_AddRefs(list));
|
|
mDC->SetTarget(list);
|
|
|
|
IntRect addClipRect;
|
|
RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
|
|
|
|
RefPtr<ID2D1SolidColorBrush> brush;
|
|
mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
|
|
mDC->PushAxisAlignedClip(D2D1::RectF(addClipRect.x, addClipRect.y, addClipRect.XMost(), addClipRect.YMost()), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
|
mDC->FillGeometry(geom, brush);
|
|
mDC->PopAxisAlignedClip();
|
|
|
|
mDC->SetTarget(CurrentTarget());
|
|
list->Close();
|
|
|
|
mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT);
|
|
|
|
PopClip();
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::MaskSurface(const Pattern &aSource,
|
|
SourceSurface *aMask,
|
|
Point aOffset,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
MarkChanged();
|
|
|
|
RefPtr<ID2D1Bitmap> bitmap;
|
|
|
|
Matrix mat = Matrix::Translation(aOffset);
|
|
RefPtr<ID2D1Image> image = GetImageForSurface(aMask, mat, ExtendMode::CLAMP, nullptr);
|
|
|
|
MOZ_ASSERT(!mat.HasNonTranslation());
|
|
aOffset.x = mat._31;
|
|
aOffset.y = mat._32;
|
|
|
|
if (!image) {
|
|
gfxWarning() << "Failed to get image for surface.";
|
|
return;
|
|
}
|
|
|
|
PrepareForDrawing(aOptions.mCompositionOp, aSource);
|
|
|
|
// FillOpacityMask only works if the antialias mode is MODE_ALIASED
|
|
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
|
|
|
|
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
|
if (!bitmap) {
|
|
gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
|
|
return;
|
|
}
|
|
|
|
IntSize size = IntSize(bitmap->GetSize().width, bitmap->GetSize().height);
|
|
|
|
Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
|
|
|
|
Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
|
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
|
|
mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
|
|
|
|
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aSource);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::CopySurface(SourceSurface *aSurface,
|
|
const IntRect &aSourceRect,
|
|
const IntPoint &aDestination)
|
|
{
|
|
MarkChanged();
|
|
|
|
PopAllClips();
|
|
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
Matrix mat;
|
|
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
|
|
|
|
if (!image) {
|
|
gfxWarning() << "Couldn't get image for surface.";
|
|
return;
|
|
}
|
|
|
|
if (!mat.IsIdentity()) {
|
|
gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface.";
|
|
return;
|
|
}
|
|
|
|
RefPtr<ID2D1Bitmap> bitmap;
|
|
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
|
|
|
if (bitmap && mFormat == SurfaceFormat::A8) {
|
|
RefPtr<ID2D1SolidColorBrush> brush;
|
|
mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
|
|
D2D1::BrushProperties(), getter_AddRefs(brush));
|
|
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
|
|
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
|
|
mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
|
|
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
|
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
|
|
return;
|
|
}
|
|
|
|
Rect srcRect(Float(aSourceRect.x), Float(aSourceRect.y),
|
|
Float(aSourceRect.width), Float(aSourceRect.height));
|
|
|
|
Rect dstRect(Float(aDestination.x), Float(aDestination.y),
|
|
Float(aSourceRect.width), Float(aSourceRect.height));
|
|
|
|
if (bitmap) {
|
|
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
|
|
mDC->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f,
|
|
D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
|
|
D2DRect(srcRect));
|
|
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
|
|
return;
|
|
}
|
|
|
|
mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)),
|
|
D2DRect(srcRect), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
|
|
D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::FillRect(const Rect &aRect,
|
|
const Pattern &aPattern,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
|
|
|
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
|
|
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
|
mDC->FillRectangle(D2DRect(aRect), brush);
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::StrokeRect(const Rect &aRect,
|
|
const Pattern &aPattern,
|
|
const StrokeOptions &aStrokeOptions,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
|
|
|
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
|
|
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
|
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
|
|
|
mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle);
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::StrokeLine(const Point &aStart,
|
|
const Point &aEnd,
|
|
const Pattern &aPattern,
|
|
const StrokeOptions &aStrokeOptions,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
|
|
|
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
|
|
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
|
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
|
|
|
mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle);
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::Stroke(const Path *aPath,
|
|
const Pattern &aPattern,
|
|
const StrokeOptions &aStrokeOptions,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
|
|
gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
|
|
return;
|
|
}
|
|
const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
|
|
|
|
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
|
|
|
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
|
|
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
|
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
|
|
|
mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle);
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::Fill(const Path *aPath,
|
|
const Pattern &aPattern,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
|
|
gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
|
|
return;
|
|
}
|
|
const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
|
|
|
|
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
|
|
|
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
|
|
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
|
|
|
mDC->FillGeometry(d2dPath->mGeometry, brush);
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::FillGlyphs(ScaledFont *aFont,
|
|
const GlyphBuffer &aBuffer,
|
|
const Pattern &aPattern,
|
|
const DrawOptions &aOptions,
|
|
const GlyphRenderingOptions *aRenderingOptions)
|
|
{
|
|
if (aFont->GetType() != FontType::DWRITE) {
|
|
gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
|
|
return;
|
|
}
|
|
|
|
ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont);
|
|
|
|
IDWriteRenderingParams *params = nullptr;
|
|
if (aRenderingOptions) {
|
|
if (aRenderingOptions->GetType() != FontType::DWRITE) {
|
|
gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
|
|
// This should never happen.
|
|
MOZ_ASSERT(false);
|
|
} else {
|
|
params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderingOptions)->mParams;
|
|
}
|
|
}
|
|
|
|
AntialiasMode aaMode = font->GetDefaultAAMode();
|
|
|
|
if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
|
|
aaMode = aOptions.mAntialiasMode;
|
|
}
|
|
|
|
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
|
|
|
bool forceClearType = false;
|
|
if (!CurrentLayer().mIsOpaque && mPermitSubpixelAA &&
|
|
aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) {
|
|
forceClearType = true;
|
|
}
|
|
|
|
|
|
D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
|
|
|
|
switch (aaMode) {
|
|
case AntialiasMode::NONE:
|
|
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
|
|
break;
|
|
case AntialiasMode::GRAY:
|
|
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
|
|
break;
|
|
case AntialiasMode::SUBPIXEL:
|
|
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
|
|
break;
|
|
default:
|
|
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
|
|
}
|
|
|
|
if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
|
|
!CurrentLayer().mIsOpaque && !forceClearType) {
|
|
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
|
|
}
|
|
|
|
mDC->SetTextAntialiasMode(d2dAAMode);
|
|
|
|
if (params != mTextRenderingParams) {
|
|
mDC->SetTextRenderingParams(params);
|
|
mTextRenderingParams = params;
|
|
}
|
|
|
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
|
|
|
AutoDWriteGlyphRun autoRun;
|
|
DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
|
|
|
|
bool needsRepushedLayers = false;
|
|
if (forceClearType) {
|
|
D2D1_RECT_F rect;
|
|
bool isAligned;
|
|
needsRepushedLayers = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
|
|
|
|
// If we have a complex clip in our stack and we have a transparent
|
|
// background, and subpixel AA is permitted, we need to repush our layer
|
|
// stack limited by the glyph run bounds initializing our layers for
|
|
// subpixel AA.
|
|
if (needsRepushedLayers) {
|
|
mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
|
|
DWRITE_MEASURING_MODE_NATURAL, &rect);
|
|
rect.left = std::floor(rect.left);
|
|
rect.right = std::ceil(rect.right);
|
|
rect.top = std::floor(rect.top);
|
|
rect.bottom = std::ceil(rect.bottom);
|
|
|
|
PopAllClips();
|
|
|
|
if (!mTransform.IsRectilinear()) {
|
|
// We must limit the pixels we touch to the -user space- bounds of
|
|
// the glyphs being drawn. In order not to get transparent pixels
|
|
// copied up in our pushed layer stack.
|
|
D2D1_RECT_F userRect;
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
|
|
DWRITE_MEASURING_MODE_NATURAL, &userRect);
|
|
|
|
RefPtr<ID2D1PathGeometry> path;
|
|
factory()->CreatePathGeometry(getter_AddRefs(path));
|
|
RefPtr<ID2D1GeometrySink> sink;
|
|
path->Open(getter_AddRefs(sink));
|
|
AddRectToSink(sink, userRect);
|
|
sink->Close();
|
|
|
|
mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED,
|
|
D2DMatrix(mTransform), 1.0f, nullptr,
|
|
D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND |
|
|
D2D1_LAYER_OPTIONS1_IGNORE_ALPHA), nullptr);
|
|
}
|
|
|
|
PushClipsToDC(mDC, true, rect);
|
|
mDC->SetTransform(D2DMatrix(mTransform));
|
|
}
|
|
}
|
|
|
|
if (brush) {
|
|
mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
|
|
}
|
|
|
|
if (needsRepushedLayers) {
|
|
PopClipsFromDC(mDC);
|
|
|
|
if (!mTransform.IsRectilinear()) {
|
|
mDC->PopLayer();
|
|
}
|
|
}
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::Mask(const Pattern &aSource,
|
|
const Pattern &aMask,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
PrepareForDrawing(aOptions.mCompositionOp, aSource);
|
|
|
|
RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha);
|
|
RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f);
|
|
mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
|
|
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
|
D2D1::IdentityMatrix(),
|
|
1.0f, mask),
|
|
nullptr);
|
|
|
|
Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height);
|
|
Matrix mat = mTransform;
|
|
mat.Invert();
|
|
|
|
mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source);
|
|
|
|
mDC->PopLayer();
|
|
|
|
FinalizeDrawing(aOptions.mCompositionOp, aSource);
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PushClip(const Path *aPath)
|
|
{
|
|
if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
|
|
gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
|
|
return;
|
|
}
|
|
|
|
mCurrentClippedGeometry = nullptr;
|
|
|
|
RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
|
|
|
|
PushedClip clip;
|
|
clip.mTransform = D2DMatrix(mTransform);
|
|
clip.mPath = pathD2D;
|
|
|
|
pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
|
|
|
|
CurrentLayer().mPushedClips.push_back(clip);
|
|
|
|
// The transform of clips is relative to the world matrix, since we use the total
|
|
// transform for the clips, make the world matrix identity.
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
if (CurrentLayer().mClipsArePushed) {
|
|
PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform);
|
|
}
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
|
{
|
|
if (!mTransform.IsRectilinear()) {
|
|
// Whoops, this isn't a rectangle in device space, Direct2D will not deal
|
|
// with this transform the way we want it to.
|
|
// See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
|
|
|
|
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
|
pathBuilder->MoveTo(aRect.TopLeft());
|
|
pathBuilder->LineTo(aRect.TopRight());
|
|
pathBuilder->LineTo(aRect.BottomRight());
|
|
pathBuilder->LineTo(aRect.BottomLeft());
|
|
pathBuilder->Close();
|
|
RefPtr<Path> path = pathBuilder->Finish();
|
|
return PushClip(path);
|
|
}
|
|
|
|
mCurrentClippedGeometry = nullptr;
|
|
|
|
PushedClip clip;
|
|
Rect rect = mTransform.TransformBounds(aRect);
|
|
IntRect intRect;
|
|
clip.mIsPixelAligned = rect.ToIntRect(&intRect);
|
|
|
|
// Do not store the transform, just store the device space rectangle directly.
|
|
clip.mBounds = D2DRect(rect);
|
|
|
|
CurrentLayer().mPushedClips.push_back(clip);
|
|
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
if (CurrentLayer().mClipsArePushed) {
|
|
mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
|
}
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PopClip()
|
|
{
|
|
mCurrentClippedGeometry = nullptr;
|
|
|
|
if (CurrentLayer().mClipsArePushed) {
|
|
if (CurrentLayer().mPushedClips.back().mPath) {
|
|
mDC->PopLayer();
|
|
} else {
|
|
mDC->PopAxisAlignedClip();
|
|
}
|
|
}
|
|
CurrentLayer().mPushedClips.pop_back();
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
|
|
const Matrix& aMaskTransform, const IntRect& aBounds,
|
|
bool aCopyBackground)
|
|
{
|
|
D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
|
|
|
|
if (aOpaque) {
|
|
options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
|
|
}
|
|
if (aCopyBackground) {
|
|
options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
|
|
}
|
|
|
|
RefPtr<ID2D1BitmapBrush> mask;
|
|
|
|
Matrix maskTransform = aMaskTransform;
|
|
|
|
RefPtr<ID2D1PathGeometry> clip;
|
|
if (aMask) {
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
RefPtr<ID2D1Image> image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
|
|
|
|
// The mask is given in user space. Our layer will apply it in device space.
|
|
maskTransform = maskTransform * mTransform;
|
|
|
|
if (image) {
|
|
RefPtr<ID2D1Bitmap> bitmap;
|
|
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
|
|
|
mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask));
|
|
MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface.
|
|
|
|
factory()->CreatePathGeometry(getter_AddRefs(clip));
|
|
RefPtr<ID2D1GeometrySink> sink;
|
|
clip->Open(getter_AddRefs(sink));
|
|
AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height));
|
|
sink->Close();
|
|
} else {
|
|
gfxCriticalError() << "Failed to get image for mask surface!";
|
|
}
|
|
}
|
|
|
|
PushAllClips();
|
|
|
|
mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED, D2DMatrix(maskTransform), aOpacity, mask, options), nullptr);
|
|
PushedLayer pushedLayer;
|
|
pushedLayer.mClipsArePushed = false;
|
|
pushedLayer.mIsOpaque = aOpaque;
|
|
pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA;
|
|
mPermitSubpixelAA = aOpaque;
|
|
|
|
mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
|
|
mPushedLayers.push_back(pushedLayer);
|
|
|
|
mDC->SetTarget(CurrentTarget());
|
|
|
|
mUsedCommandListsSincePurge++;
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PopLayer()
|
|
{
|
|
MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
|
|
|
|
RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
|
|
mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA;
|
|
|
|
mPushedLayers.pop_back();
|
|
mDC->SetTarget(CurrentTarget());
|
|
|
|
list->Close();
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
DCCommandSink sink(mDC);
|
|
list->Stream(&sink);
|
|
|
|
mDC->PopLayer();
|
|
}
|
|
|
|
already_AddRefed<SourceSurface>
|
|
DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData,
|
|
const IntSize &aSize,
|
|
int32_t aStride,
|
|
SurfaceFormat aFormat) const
|
|
{
|
|
RefPtr<ID2D1Bitmap1> bitmap;
|
|
|
|
HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride,
|
|
D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)),
|
|
getter_AddRefs(bitmap));
|
|
|
|
if (FAILED(hr) || !bitmap) {
|
|
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr) << " format " << (int)aFormat;
|
|
return nullptr;
|
|
}
|
|
|
|
return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, aFormat, aSize);
|
|
}
|
|
|
|
already_AddRefed<DrawTarget>
|
|
DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
|
|
{
|
|
RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1();
|
|
|
|
if (!dt->Init(aSize, aFormat)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return dt.forget();
|
|
}
|
|
|
|
already_AddRefed<PathBuilder>
|
|
DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const
|
|
{
|
|
RefPtr<ID2D1PathGeometry> path;
|
|
HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hexa(hr);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<ID2D1GeometrySink> sink;
|
|
hr = path->Open(getter_AddRefs(sink));
|
|
if (FAILED(hr)) {
|
|
gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hexa(hr);
|
|
return nullptr;
|
|
}
|
|
|
|
if (aFillRule == FillRule::FILL_WINDING) {
|
|
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
|
|
}
|
|
|
|
return MakeAndAddRef<PathBuilderD2D>(sink, path, aFillRule, BackendType::DIRECT2D1_1);
|
|
}
|
|
|
|
already_AddRefed<GradientStops>
|
|
DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
|
|
{
|
|
if (aNumStops == 0) {
|
|
gfxWarning() << *this << ": Failed to create GradientStopCollection with no stops.";
|
|
return nullptr;
|
|
}
|
|
|
|
D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
|
|
|
|
for (uint32_t i = 0; i < aNumStops; i++) {
|
|
stops[i].position = rawStops[i].offset;
|
|
stops[i].color = D2DColor(rawStops[i].color);
|
|
}
|
|
|
|
RefPtr<ID2D1GradientStopCollection> stopCollection;
|
|
|
|
HRESULT hr =
|
|
mDC->CreateGradientStopCollection(stops, aNumStops,
|
|
D2D1_GAMMA_2_2, D2DExtend(aExtendMode, Axis::BOTH),
|
|
getter_AddRefs(stopCollection));
|
|
delete [] stops;
|
|
|
|
if (FAILED(hr)) {
|
|
gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hexa(hr);
|
|
return nullptr;
|
|
}
|
|
|
|
return MakeAndAddRef<GradientStopsD2D>(stopCollection, Factory::GetDirect3D11Device());
|
|
}
|
|
|
|
already_AddRefed<FilterNode>
|
|
DrawTargetD2D1::CreateFilter(FilterType aType)
|
|
{
|
|
return FilterNodeD2D1::Create(mDC, aType);
|
|
}
|
|
|
|
bool
|
|
DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ID2D1Device* device = Factory::GetD2D1Device();
|
|
if (!device) {
|
|
return false;
|
|
}
|
|
|
|
hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
|
|
return false;
|
|
}
|
|
|
|
RefPtr<IDXGISurface> dxgiSurface;
|
|
aTexture->QueryInterface(__uuidof(IDXGISurface),
|
|
(void**)((IDXGISurface**)getter_AddRefs(dxgiSurface)));
|
|
if (!dxgiSurface) {
|
|
gfxCriticalError() <<"[D2D1.1] Failed to obtain a DXGI surface.";
|
|
return false;
|
|
}
|
|
|
|
D2D1_BITMAP_PROPERTIES1 props;
|
|
props.dpiX = 96;
|
|
props.dpiY = 96;
|
|
props.pixelFormat = D2DPixelFormat(aFormat);
|
|
props.colorContext = nullptr;
|
|
props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
|
|
hr = mDC->CreateBitmapFromDxgiSurface(dxgiSurface, props, (ID2D1Bitmap1**)getter_AddRefs(mBitmap));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError() << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr) << " format " << (int)aFormat;
|
|
return false;
|
|
}
|
|
|
|
mFormat = aFormat;
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
aTexture->GetDesc(&desc);
|
|
mSize.width = desc.Width;
|
|
mSize.height = desc.Height;
|
|
|
|
// This single solid color brush system is not very 'threadsafe', however,
|
|
// issueing multiple drawing commands simultaneously to a single drawtarget
|
|
// from multiple threads is unexpected since there's no way to guarantee
|
|
// ordering in that situation anyway.
|
|
hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I1).";
|
|
return false;
|
|
}
|
|
|
|
mDC->SetTarget(CurrentTarget());
|
|
|
|
mDC->BeginDraw();
|
|
|
|
CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ID2D1Device* device = Factory::GetD2D1Device();
|
|
if (!device) {
|
|
return false;
|
|
}
|
|
|
|
hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
|
|
return false;
|
|
}
|
|
|
|
if (mDC->GetMaximumBitmapSize() < UINT32(aSize.width) ||
|
|
mDC->GetMaximumBitmapSize() < UINT32(aSize.height)) {
|
|
// This is 'ok', so don't assert
|
|
gfxCriticalNote << "[D2D1.1] Attempt to use unsupported surface size " << aSize;
|
|
return false;
|
|
}
|
|
|
|
D2D1_BITMAP_PROPERTIES1 props;
|
|
props.dpiX = 96;
|
|
props.dpiY = 96;
|
|
props.pixelFormat = D2DPixelFormat(aFormat);
|
|
props.colorContext = nullptr;
|
|
props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
|
|
hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)getter_AddRefs(mBitmap));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << aSize << " Code: " << hexa(hr) << " format " << (int)aFormat;
|
|
return false;
|
|
}
|
|
|
|
mDC->SetTarget(CurrentTarget());
|
|
|
|
hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I2).";
|
|
return false;
|
|
}
|
|
|
|
mDC->BeginDraw();
|
|
|
|
CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
|
|
|
|
mDC->Clear();
|
|
|
|
mFormat = aFormat;
|
|
mSize = aSize;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Private helpers.
|
|
*/
|
|
uint32_t
|
|
DrawTargetD2D1::GetByteSize() const
|
|
{
|
|
return mSize.width * mSize.height * BytesPerPixel(mFormat);
|
|
}
|
|
|
|
ID2D1Factory1*
|
|
DrawTargetD2D1::factory()
|
|
{
|
|
if (mFactory) {
|
|
return mFactory;
|
|
}
|
|
|
|
RefPtr<ID2D1Factory> factory;
|
|
D2D1CreateFactoryFunc createD2DFactory;
|
|
HMODULE d2dModule = LoadLibraryW(L"d2d1.dll");
|
|
createD2DFactory = (D2D1CreateFactoryFunc)
|
|
GetProcAddress(d2dModule, "D2D1CreateFactory");
|
|
|
|
if (!createD2DFactory) {
|
|
gfxWarning() << "Failed to locate D2D1CreateFactory function.";
|
|
return nullptr;
|
|
}
|
|
|
|
D2D1_FACTORY_OPTIONS options;
|
|
#ifdef _DEBUG
|
|
options.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
|
|
#else
|
|
options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
|
|
#endif
|
|
//options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
|
|
|
|
HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
|
|
__uuidof(ID2D1Factory),
|
|
&options,
|
|
getter_AddRefs(factory));
|
|
|
|
if (FAILED(hr) || !factory) {
|
|
gfxCriticalNote << "Failed to create a D2D1 content device: " << hexa(hr);
|
|
return nullptr;
|
|
}
|
|
|
|
hr = factory->QueryInterface((ID2D1Factory1**)&mFactory);
|
|
if (FAILED(hr) || !mFactory) {
|
|
return nullptr;
|
|
}
|
|
|
|
ExtendInputEffectD2D1::Register(mFactory);
|
|
RadialGradientEffectD2D1::Register(mFactory);
|
|
|
|
return mFactory;
|
|
}
|
|
|
|
IDWriteFactory*
|
|
DrawTargetD2D1::GetDWriteFactory()
|
|
{
|
|
if (mDWriteFactory) {
|
|
return mDWriteFactory;
|
|
}
|
|
|
|
decltype(DWriteCreateFactory)* createDWriteFactory;
|
|
HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll");
|
|
createDWriteFactory = (decltype(DWriteCreateFactory)*)
|
|
GetProcAddress(dwriteModule, "DWriteCreateFactory");
|
|
|
|
if (!createDWriteFactory) {
|
|
gfxWarning() << "Failed to locate DWriteCreateFactory function.";
|
|
return nullptr;
|
|
}
|
|
|
|
HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
|
|
reinterpret_cast<IUnknown**>(&mDWriteFactory));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxWarning() << "Failed to create DWrite Factory.";
|
|
}
|
|
|
|
return mDWriteFactory;
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::CleanupD2D()
|
|
{
|
|
if (mFactory) {
|
|
RadialGradientEffectD2D1::Unregister(mFactory);
|
|
ExtendInputEffectD2D1::Unregister(mFactory);
|
|
mFactory->Release();
|
|
mFactory = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::MarkChanged()
|
|
{
|
|
if (mSnapshot) {
|
|
if (mSnapshot->hasOneRef()) {
|
|
// Just destroy it, since no-one else knows about it.
|
|
mSnapshot = nullptr;
|
|
} else {
|
|
mSnapshot->DrawTargetWillChange();
|
|
// The snapshot will no longer depend on this target.
|
|
MOZ_ASSERT(!mSnapshot);
|
|
}
|
|
}
|
|
if (mDependentTargets.size()) {
|
|
// Copy mDependentTargets since the Flush()es below will modify it.
|
|
TargetSet tmpTargets = mDependentTargets;
|
|
for (TargetSet::iterator iter = tmpTargets.begin();
|
|
iter != tmpTargets.end(); iter++) {
|
|
(*iter)->Flush();
|
|
}
|
|
// The Flush() should have broken all dependencies on this target.
|
|
MOZ_ASSERT(!mDependentTargets.size());
|
|
}
|
|
}
|
|
|
|
bool
|
|
DrawTargetD2D1::ShouldClipTemporarySurfaceDrawing(CompositionOp aOp,
|
|
const Pattern& aPattern,
|
|
bool aClipIsComplex)
|
|
{
|
|
bool patternSupported = IsPatternSupportedByD2D(aPattern);
|
|
return patternSupported && !CurrentLayer().mIsOpaque && D2DSupportsCompositeMode(aOp) &&
|
|
IsOperatorBoundByMask(aOp) && aClipIsComplex;
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|
{
|
|
MarkChanged();
|
|
|
|
bool patternSupported = IsPatternSupportedByD2D(aPattern);
|
|
|
|
if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
|
|
// It's important to do this before FlushTransformToDC! As this will cause
|
|
// the transform to become dirty.
|
|
PushAllClips();
|
|
|
|
FlushTransformToDC();
|
|
|
|
if (aOp != CompositionOp::OP_OVER)
|
|
mDC->SetPrimitiveBlend(D2DPrimitiveBlendMode(aOp));
|
|
|
|
return;
|
|
}
|
|
|
|
HRESULT result = mDC->CreateCommandList(getter_AddRefs(mCommandList));
|
|
mDC->SetTarget(mCommandList);
|
|
mUsedCommandListsSincePurge++;
|
|
|
|
// This is where we should have a valid command list. If we don't, something is
|
|
// wrong, and it's likely an OOM.
|
|
if (!mCommandList) {
|
|
gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D2D1.1 command list on creation " << mUsedCommandListsSincePurge << ", " << gfx::hexa(result);
|
|
}
|
|
|
|
D2D1_RECT_F rect;
|
|
bool isAligned;
|
|
bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
|
|
|
|
if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
|
|
PushClipsToDC(mDC);
|
|
}
|
|
|
|
FlushTransformToDC();
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|
{
|
|
bool patternSupported = IsPatternSupportedByD2D(aPattern);
|
|
|
|
if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
|
|
if (aOp != CompositionOp::OP_OVER)
|
|
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
|
|
return;
|
|
}
|
|
|
|
D2D1_RECT_F rect;
|
|
bool isAligned;
|
|
bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
|
|
|
|
if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
|
|
PopClipsFromDC(mDC);
|
|
}
|
|
|
|
mDC->SetTarget(CurrentTarget());
|
|
if (!mCommandList) {
|
|
gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D21.1 command list on finalize";
|
|
return;
|
|
}
|
|
mCommandList->Close();
|
|
|
|
RefPtr<ID2D1CommandList> source = mCommandList;
|
|
mCommandList = nullptr;
|
|
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
if (patternSupported) {
|
|
if (D2DSupportsCompositeMode(aOp)) {
|
|
RefPtr<ID2D1Image> tmpImage;
|
|
if (clipIsComplex) {
|
|
PopAllClips();
|
|
if (!IsOperatorBoundByMask(aOp)) {
|
|
tmpImage = GetImageForLayerContent();
|
|
}
|
|
}
|
|
mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
|
|
|
|
if (tmpImage) {
|
|
RefPtr<ID2D1ImageBrush> brush;
|
|
RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
|
|
mDC->CreateImageBrush(tmpImage, D2D1::ImageBrushProperties(D2D1::RectF(0, 0, mSize.width, mSize.height)),
|
|
getter_AddRefs(brush));
|
|
|
|
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
|
|
mDC->FillGeometry(inverseGeom, brush);
|
|
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
|
|
}
|
|
return;
|
|
}
|
|
|
|
RefPtr<ID2D1Effect> blendEffect;
|
|
HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
|
|
|
|
if (FAILED(hr) || !blendEffect) {
|
|
gfxWarning() << "Failed to create blend effect!";
|
|
return;
|
|
}
|
|
|
|
RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent();
|
|
|
|
blendEffect->SetInput(0, tmpImage);
|
|
blendEffect->SetInput(1, source);
|
|
blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
|
|
|
|
mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
|
|
return;
|
|
}
|
|
|
|
const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
|
|
if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
|
|
// Draw nothing!
|
|
return;
|
|
}
|
|
|
|
RefPtr<ID2D1Effect> radialGradientEffect;
|
|
|
|
HRESULT hr = mDC->CreateEffect(CLSID_RadialGradientEffect, getter_AddRefs(radialGradientEffect));
|
|
if (FAILED(hr) || !radialGradientEffect) {
|
|
gfxWarning() << "Failed to create radial gradient effect. Code: " << hexa(hr);
|
|
return;
|
|
}
|
|
|
|
radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION,
|
|
static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection);
|
|
radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y));
|
|
radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y));
|
|
radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1);
|
|
radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
|
|
radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
|
|
radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform));
|
|
radialGradientEffect->SetInput(0, source);
|
|
|
|
mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource)
|
|
{
|
|
if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) {
|
|
aSource->mDrawTarget->mDependentTargets.insert(this);
|
|
mDependingOnTargets.insert(aSource->mDrawTarget);
|
|
}
|
|
}
|
|
|
|
static D2D1_RECT_F
|
|
IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
|
|
{
|
|
D2D1_RECT_F result;
|
|
result.left = max(aRect1.left, aRect2.left);
|
|
result.top = max(aRect1.top, aRect2.top);
|
|
result.right = min(aRect1.right, aRect2.right);
|
|
result.bottom = min(aRect1.bottom, aRect2.bottom);
|
|
|
|
result.right = max(result.right, result.left);
|
|
result.bottom = max(result.bottom, result.top);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
|
|
{
|
|
if (!CurrentLayer().mPushedClips.size()) {
|
|
return false;
|
|
}
|
|
|
|
aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
|
|
for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) {
|
|
if (iter->mPath) {
|
|
return false;
|
|
}
|
|
aClipRect = IntersectRect(aClipRect, iter->mBounds);
|
|
if (!iter->mIsPixelAligned) {
|
|
aIsPixelAligned = false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
already_AddRefed<ID2D1Image>
|
|
DrawTargetD2D1::GetImageForLayerContent()
|
|
{
|
|
if (!CurrentLayer().mCurrentList) {
|
|
RefPtr<ID2D1Bitmap> tmpBitmap;
|
|
HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
|
|
// For now, crash in this scenario; this should happen because tmpBitmap is
|
|
// null and CopyFromBitmap call below dereferences it.
|
|
// return;
|
|
}
|
|
mDC->Flush();
|
|
|
|
tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
|
|
return tmpBitmap.forget();
|
|
} else {
|
|
PopAllClips();
|
|
|
|
RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
|
|
mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
|
|
mDC->SetTarget(CurrentTarget());
|
|
list->Close();
|
|
|
|
DCCommandSink sink(mDC);
|
|
list->Stream(&sink);
|
|
|
|
PushAllClips();
|
|
|
|
return list.forget();
|
|
}
|
|
}
|
|
|
|
already_AddRefed<ID2D1Geometry>
|
|
DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
|
{
|
|
if (mCurrentClippedGeometry) {
|
|
*aClipBounds = mCurrentClipBounds;
|
|
RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
|
|
return clippedGeometry.forget();
|
|
}
|
|
|
|
MOZ_ASSERT(CurrentLayer().mPushedClips.size());
|
|
|
|
mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
|
|
|
|
// if pathGeom is null then pathRect represents the path.
|
|
RefPtr<ID2D1Geometry> pathGeom;
|
|
D2D1_RECT_F pathRect;
|
|
bool pathRectIsAxisAligned = false;
|
|
auto iter = CurrentLayer().mPushedClips.begin();
|
|
|
|
if (iter->mPath) {
|
|
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
|
} else {
|
|
pathRect = iter->mBounds;
|
|
pathRectIsAxisAligned = iter->mIsPixelAligned;
|
|
}
|
|
|
|
iter++;
|
|
for (;iter != CurrentLayer().mPushedClips.end(); iter++) {
|
|
// Do nothing but add it to the current clip bounds.
|
|
if (!iter->mPath && iter->mIsPixelAligned) {
|
|
mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
|
|
IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
|
|
int32_t(iter->mBounds.right - iter->mBounds.left),
|
|
int32_t(iter->mBounds.bottom - iter->mBounds.top)));
|
|
continue;
|
|
}
|
|
|
|
if (!pathGeom) {
|
|
if (pathRectIsAxisAligned) {
|
|
mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
|
|
IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
|
|
int32_t(pathRect.right - pathRect.left),
|
|
int32_t(pathRect.bottom - pathRect.top)));
|
|
}
|
|
if (iter->mPath) {
|
|
// See if pathRect needs to go into the path geometry.
|
|
if (!pathRectIsAxisAligned) {
|
|
pathGeom = ConvertRectToGeometry(pathRect);
|
|
} else {
|
|
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
|
}
|
|
} else {
|
|
pathRect = IntersectRect(pathRect, iter->mBounds);
|
|
pathRectIsAxisAligned = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
RefPtr<ID2D1PathGeometry> newGeom;
|
|
factory()->CreatePathGeometry(getter_AddRefs(newGeom));
|
|
|
|
RefPtr<ID2D1GeometrySink> currentSink;
|
|
newGeom->Open(getter_AddRefs(currentSink));
|
|
|
|
if (iter->mPath) {
|
|
pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
|
|
iter->mTransform, currentSink);
|
|
} else {
|
|
RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
|
|
pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
|
|
D2D1::IdentityMatrix(), currentSink);
|
|
}
|
|
|
|
currentSink->Close();
|
|
|
|
pathGeom = newGeom.forget();
|
|
}
|
|
|
|
// For now we need mCurrentClippedGeometry to always be non-nullptr. This
|
|
// method might seem a little strange but it is just fine, if pathGeom is
|
|
// nullptr pathRect will always still contain 1 clip unaccounted for
|
|
// regardless of mCurrentClipBounds.
|
|
if (!pathGeom) {
|
|
pathGeom = ConvertRectToGeometry(pathRect);
|
|
}
|
|
mCurrentClippedGeometry = pathGeom.forget();
|
|
*aClipBounds = mCurrentClipBounds;
|
|
RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
|
|
return clippedGeometry.forget();
|
|
}
|
|
|
|
already_AddRefed<ID2D1Geometry>
|
|
DrawTargetD2D1::GetInverseClippedGeometry()
|
|
{
|
|
IntRect bounds;
|
|
RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&bounds);
|
|
RefPtr<ID2D1RectangleGeometry> rectGeom;
|
|
RefPtr<ID2D1PathGeometry> inverseGeom;
|
|
|
|
factory()->CreateRectangleGeometry(D2D1::RectF(0, 0, mSize.width, mSize.height), getter_AddRefs(rectGeom));
|
|
factory()->CreatePathGeometry(getter_AddRefs(inverseGeom));
|
|
RefPtr<ID2D1GeometrySink> sink;
|
|
inverseGeom->Open(getter_AddRefs(sink));
|
|
rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE, D2D1::IdentityMatrix(), sink);
|
|
sink->Close();
|
|
|
|
return inverseGeom.forget();
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PopAllClips()
|
|
{
|
|
if (CurrentLayer().mClipsArePushed) {
|
|
PopClipsFromDC(mDC);
|
|
|
|
CurrentLayer().mClipsArePushed = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PushAllClips()
|
|
{
|
|
if (!CurrentLayer().mClipsArePushed) {
|
|
PushClipsToDC(mDC);
|
|
|
|
CurrentLayer().mClipsArePushed = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
|
|
{
|
|
mDC->SetTransform(D2D1::IdentityMatrix());
|
|
mTransformDirty = true;
|
|
|
|
for (auto iter = CurrentLayer().mPushedClips.begin(); iter != CurrentLayer().mPushedClips.end(); iter++) {
|
|
if (iter->mPath) {
|
|
PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform, aForceIgnoreAlpha, aMaxRect);
|
|
} else {
|
|
mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
|
|
{
|
|
for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) {
|
|
if (CurrentLayer().mPushedClips[i].mPath) {
|
|
aDC->PopLayer();
|
|
} else {
|
|
aDC->PopAxisAlignedClip();
|
|
}
|
|
}
|
|
}
|
|
|
|
already_AddRefed<ID2D1Brush>
|
|
DrawTargetD2D1::CreateTransparentBlackBrush()
|
|
{
|
|
return GetSolidColorBrush(D2D1::ColorF(0, 0));
|
|
}
|
|
|
|
already_AddRefed<ID2D1SolidColorBrush>
|
|
DrawTargetD2D1::GetSolidColorBrush(const D2D_COLOR_F& aColor)
|
|
{
|
|
RefPtr<ID2D1SolidColorBrush> brush = mSolidColorBrush;
|
|
brush->SetColor(aColor);
|
|
return brush.forget();
|
|
}
|
|
|
|
already_AddRefed<ID2D1Brush>
|
|
DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
|
|
{
|
|
if (!IsPatternSupportedByD2D(aPattern)) {
|
|
return GetSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f));
|
|
}
|
|
|
|
if (aPattern.GetType() == PatternType::COLOR) {
|
|
Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
|
|
return GetSolidColorBrush(D2D1::ColorF(color.r, color.g, color.b, color.a * aAlpha));
|
|
}
|
|
if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
|
|
RefPtr<ID2D1LinearGradientBrush> gradBrush;
|
|
const LinearGradientPattern *pat =
|
|
static_cast<const LinearGradientPattern*>(&aPattern);
|
|
|
|
GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
|
|
|
|
if (!stops) {
|
|
gfxDebug() << "No stops specified for gradient pattern.";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
if (pat->mBegin == pat->mEnd) {
|
|
uint32_t stopCount = stops->mStopCollection->GetGradientStopCount();
|
|
vector<D2D1_GRADIENT_STOP> d2dStops(stopCount);
|
|
stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount);
|
|
d2dStops.back().color.a *= aAlpha;
|
|
return GetSolidColorBrush(d2dStops.back().color);
|
|
}
|
|
|
|
mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
|
|
D2DPoint(pat->mEnd)),
|
|
D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
|
|
stops->mStopCollection,
|
|
getter_AddRefs(gradBrush));
|
|
|
|
if (!gradBrush) {
|
|
gfxWarning() << "Couldn't create gradient brush.";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
return gradBrush.forget();
|
|
}
|
|
if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
|
|
RefPtr<ID2D1RadialGradientBrush> gradBrush;
|
|
const RadialGradientPattern *pat =
|
|
static_cast<const RadialGradientPattern*>(&aPattern);
|
|
|
|
GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
|
|
|
|
if (!stops) {
|
|
gfxDebug() << "No stops specified for gradient pattern.";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
// This will not be a complex radial gradient brush.
|
|
mDC->CreateRadialGradientBrush(
|
|
D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2),
|
|
D2DPoint(pat->mCenter1 - pat->mCenter2),
|
|
pat->mRadius2, pat->mRadius2),
|
|
D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
|
|
stops->mStopCollection,
|
|
getter_AddRefs(gradBrush));
|
|
|
|
if (!gradBrush) {
|
|
gfxWarning() << "Couldn't create gradient brush.";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
return gradBrush.forget();
|
|
}
|
|
if (aPattern.GetType() == PatternType::SURFACE) {
|
|
const SurfacePattern *pat =
|
|
static_cast<const SurfacePattern*>(&aPattern);
|
|
|
|
if (!pat->mSurface) {
|
|
gfxDebug() << "No source surface specified for surface pattern";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
D2D1_RECT_F samplingBounds;
|
|
Matrix mat = pat->mMatrix;
|
|
|
|
MOZ_ASSERT(pat->mSurface->IsValid());
|
|
|
|
RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
|
|
|
|
if (pat->mSurface->GetFormat() == SurfaceFormat::A8) {
|
|
// See bug 1251431, at least FillOpacityMask does not appear to allow a source bitmapbrush
|
|
// with source format A8. This creates a BGRA surface with the same alpha values that
|
|
// the A8 surface has.
|
|
RefPtr<ID2D1Bitmap> bitmap;
|
|
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
|
if (bitmap) {
|
|
RefPtr<ID2D1Image> oldTarget;
|
|
RefPtr<ID2D1Bitmap1> tmpBitmap;
|
|
mDC->CreateBitmap(D2D1::SizeU(pat->mSurface->GetSize().width, pat->mSurface->GetSize().height), nullptr, 0,
|
|
D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
|
|
getter_AddRefs(tmpBitmap));
|
|
mDC->GetTarget(getter_AddRefs(oldTarget));
|
|
mDC->SetTarget(tmpBitmap);
|
|
|
|
RefPtr<ID2D1SolidColorBrush> brush;
|
|
mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
|
|
mDC->FillOpacityMask(bitmap, brush);
|
|
mDC->SetTarget(oldTarget);
|
|
image = tmpBitmap;
|
|
}
|
|
}
|
|
|
|
if (!image) {
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
if (pat->mSamplingRect.IsEmpty()) {
|
|
RefPtr<ID2D1Bitmap> bitmap;
|
|
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
|
if (bitmap) {
|
|
/**
|
|
* Create the brush with the proper repeat modes.
|
|
*/
|
|
RefPtr<ID2D1BitmapBrush> bitmapBrush;
|
|
D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
|
|
D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
|
|
|
|
mDC->CreateBitmapBrush(bitmap,
|
|
D2D1::BitmapBrushProperties(xRepeat, yRepeat,
|
|
D2DFilter(pat->mFilter)),
|
|
D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
|
|
getter_AddRefs(bitmapBrush));
|
|
if (!bitmapBrush) {
|
|
gfxWarning() << "Couldn't create bitmap brush!";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
return bitmapBrush.forget();
|
|
}
|
|
}
|
|
|
|
RefPtr<ID2D1ImageBrush> imageBrush;
|
|
if (pat->mSamplingRect.IsEmpty()) {
|
|
samplingBounds = D2D1::RectF(0, 0,
|
|
Float(pat->mSurface->GetSize().width),
|
|
Float(pat->mSurface->GetSize().height));
|
|
} else if (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
|
|
samplingBounds = D2DRect(pat->mSamplingRect);
|
|
mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y);
|
|
} else {
|
|
// We will do a partial upload of the sampling restricted area from GetImageForSurface.
|
|
samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height);
|
|
}
|
|
|
|
D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
|
|
D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
|
|
|
|
mDC->CreateImageBrush(image,
|
|
D2D1::ImageBrushProperties(samplingBounds,
|
|
xRepeat,
|
|
yRepeat,
|
|
D2DInterpolationMode(pat->mFilter)),
|
|
D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
|
|
getter_AddRefs(imageBrush));
|
|
|
|
if (!imageBrush) {
|
|
gfxWarning() << "Couldn't create image brush!";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
return imageBrush.forget();
|
|
}
|
|
|
|
gfxWarning() << "Invalid pattern type detected.";
|
|
return CreateTransparentBlackBrush();
|
|
}
|
|
|
|
already_AddRefed<ID2D1Image>
|
|
DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
|
|
ExtendMode aExtendMode, const IntRect* aSourceRect)
|
|
{
|
|
RefPtr<ID2D1Image> image;
|
|
|
|
switch (aSurface->GetType()) {
|
|
case SurfaceType::D2D1_1_IMAGE:
|
|
{
|
|
SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface);
|
|
image = surf->GetImage();
|
|
AddDependencyOnSource(surf);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
|
|
if (!dataSurf) {
|
|
gfxWarning() << "Invalid surface type.";
|
|
return nullptr;
|
|
}
|
|
return CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode,
|
|
aSourceTransform, mDC, aSourceRect);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return image.forget();
|
|
}
|
|
|
|
already_AddRefed<SourceSurface>
|
|
DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const
|
|
{
|
|
if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
|
|
RefPtr<SourceSurface> surface(aSurface);
|
|
return surface.forget();
|
|
}
|
|
|
|
RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
|
|
|
|
RefPtr<ID2D1Bitmap1> bitmap;
|
|
{
|
|
DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
|
|
if (MOZ2D_WARN_IF(!map.IsMapped())) {
|
|
return nullptr;
|
|
}
|
|
|
|
HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.GetData(), map.GetStride(),
|
|
D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())),
|
|
getter_AddRefs(bitmap));
|
|
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(data->GetSize()))) << "[D2D1.1] 4CreateBitmap failure " << data->GetSize() << " Code: " << hexa(hr) << " format " << (int)data->GetFormat();
|
|
}
|
|
}
|
|
|
|
if (!bitmap) {
|
|
return data.forget();
|
|
}
|
|
|
|
return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, data->GetFormat(), data->GetSize());
|
|
}
|
|
|
|
void
|
|
DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
|
|
bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
|
|
{
|
|
D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
|
|
|
|
if (CurrentLayer().mIsOpaque || aForceIgnoreAlpha) {
|
|
options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
|
|
}
|
|
|
|
mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry,
|
|
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
|
|
1.0, nullptr, options), nullptr);
|
|
}
|
|
|
|
}
|
|
}
|