Files
palemoon27/gfx/2d/DrawTargetD2D1.cpp
T
roytam1 157a125630 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1144778 - Send an HTTP Date request header with telemetry pings. r=dexter (b630ff3123)
- Bug 1268581 - Reduce Telemetry initialization delay in tests. r=dexter (08ea2597f4)
- Bug 1205898 - Making TelemetryStopwatch.jsm handle keyed histograms. r=gfritzsche (2e1d793bc0)
- Bug 1262386 - Move Scheduler shutdown() next to init(). r=dexter (9aa12c0859)
- Bug 1162538 - Add test coverage for Telemetry archive probes. r=gfritzsche (f7f9095e82)
- Bug 1186955 - Add test coverage. r=gfritzsche (cc76ba4fda)
- Bug 1262386 - Trigger Telemetry scheduler tick after waking up from sleep. r=dexter (27b03357bf)
- Bug 1178005 - Fix unintended octal literals (00 => 0). r=Mossop (dd738033b1)
- Bug 1222054 - Fix broken tests. r=gfritzsche (a6c6fa3445)
- Bug 1161515 - Reset UUID generator with fakeGenerateUUID(). r=gfritzsche (460ed17cb5)
- Bug 1209912 - Use JSON.parse instead of nsIJSON.decode in Telemetry tests. r=Dexter (7ca55a9b5c)
- Bug 1245136 - Discard old-format pending Telemetry files. r=gfritzsche (57f2a19c9d)
- Bug 1034138 - d. Test BackgroundHangMonitor permahangs; r=vladan (7313b143bc)
- Bug 1016629 - h. Fix wrong sysinfo usage; r=trivial (9823cd5ed0)
- Bug 1197612 - In test_TelemetrySendOldPings.js, set the FHR pref to the value the tests assume it has. r=gfritzsche (06a41428b2)
- Bug 1136082 - test_TelemetrySendOldPings.js is missing AddonManager initialization. r=froydnj (cfc700dd44)
- Bug 1239480 - Make test_TelemetryReportingPolicy.js set the minimum policy version pref for the right channel. r=gfritzsche (e44841aa2a)
- Bug 1230213 - test_TelemetryLog yields from a non generator function. r=dexter (34f28fb0b1)
- Bug 1226178 - Fix deprecated octal literals warnings in Telemetry tests. r=gfritzsche (88208f0f2e)
- Bug 1174111 - |test_sendTimeout| in test_TelemetryControllerShutdown.js must not wait on ping submission. r=gfritzsche (3b010e9743)
- Bug 1120379 - Add tests for the deletion ping. r=gfritzsche (61e6fbd8a1)
- Bug 1178262 - Add test-coverage for sending persisted deletion pings. r=gfritzsche (657fbbbcc2)
- Bug 1174674 - Add test coverage. r=gfritzsche (426212e94c)
- Bug 1167456 - Add test coverage. r=gfritzsche (4842d38e2e)
- Bug 1198364 - Set FHR upload pref in telemetry/tests/unit/head.js to avoid test failures. r=gfritzsche (024599364e)
- Bug 1149284 - Restore Telemetry tests. r=rvitillo, r=sstamm (88d4022e6b)
- add esl (c459da1cd3)
- Bug 1251785 - Remove remaining references to MOZILLA_XPCOMRT_API from toolkit. r=froydnj (5b5708031d)
- Bug 1120379 - Add the documentation for deletion pings. r=gfritzsche (723f44f669)
- Bug 1241599 - Add 'core' ping telemetry docs. r=gfritzsche (35fcc54195)
- Bug 1241697 - Add docs for 'experiments' field in core ping. r=mfinkle (3c408f1a61)
- Bug 1247572 - Add profileDate field to Android core ping docs. r=gfritzsche (79b1c71b53)
- Bug 1249288 - Update telemetry docs to include defaultSearch. r=gfritzsche,rnewman (3807061291)
- Bug 1257595 - Document that the 'core' pings profileDate field can be missing. r=mcomella (f40392cb2d)
- Bug 1246816 - Add docs for profileDate. r=gfritzsche (7248d799aa)
- Bug 1263761 - Update defaultSearch in core ping docs for custom search engines. r=gfritzsche (e516ecbab3)
- Bug 1247982 - Lock request headers. r=mcmanus (8a5940a109)
- Bug 1251332 - add PAC support for reversed ranges; r=bagder (7ac8fd97cb)
- Bug 1178337 - Part 1: Supports all referrer policies for element atrributes. r=sicking, r=Gijs, r=mcmanus (85b7421b02)
- Bug 1187357 - rename referrer attribute to referrerpolicy in tests. r=hsivonen (da334bf712)
- Bug 1178337 - Part 2: Update/add test cases. r=sicking. (acec860fd8)
- Bug 1260766 - the comparison of OriginAttributes in nsFrameLoader::Swap* methods should consider the usercontextid attribute of the owning element, r=smaug (82f8895bec)
- bits of Bug 1110485 P1 (f8908eb35f)
- Bug 1195968 - Check how CanvasFilterChainObserver accesses the CanvasRenderingContext2D. r=mstange (eecc12329b)
- Bug 1260960 - Check skiaGL is enabled before calling skiaGLTex(), r=snorp (82c069fcc9)
- align test stuff (c0ccc11ed9)
- Bug 1245256 - GMP Plugins now installed in flat manner to accomodate widevine. r=spohl (ae0eb69979)
- Bug 1245649: Turn on use-isnan, no-unexpected-multiline (0633b3470f)
- Bug 1079665 - [e10s] Findbar focusContent doesn't work when a link was found. r=enndeakin (b349e62d5a)
- Bug 1174291 - Fix ctrl-return for e10s findbar. r=evilpie (6f0bf0b149)
- Bug 1174289 - Remove fake focus-ring after e10s findbar is closed. r=evilpie (302ffe7559)
- Bug 1079665 - follow up, remove accidental line included from different patch. r=me (0904fe927c)
- Bug 1260208 - part 1 - use C I/O facilities in TestWebGLElementArrayCache instead of C++ ones; r=BenWa The libc++ included with the Android NDK does not seem to work correctly with std::cerr; writing to it (or to std::cout, as confirmed by tests) causes the process to hang indefinitely, causing test failures.  Using fprintf and stderr, however, seems to work correctly. (4279c2d09d)
- Bug 1260208 - part 5 - add libc++ license to about:license when using it on Android; r=gerv,nalexander (4bcf23794a)
- Bug 1260208 - part 0 - correctly compare EHTable when sorting; r=froydnj operator< for EHTable compares the LHS start PC with the RHS *end* PC. Because the ranges are non-overlapping, this works fine for two distinct EHTables. However, the comparison doesn't work if LHS and RHS refer to the same EHTable; in that case operator< returns true, even though it should return false because the two operands are identical. (dc2af36cec)
- Bug 1254908 - ./mach bootstrap doesn't work on CentOS/Fedora when it attempts to upgrade Mercurial.  r=gps (d288b314ec)
- Bug 1239413. Clarify comment about the context flags. (c2b01ef6d1)
- Bug 1146875 - Fix the leak-gauge.html broken and add a encoding declaration. r=dbaron (8df2510774)
- Bug 1132499 - part 1 - convert nsTextEditorState::mValue to use Maybe instead of heap allocation; r=ehsan (2644faebbb)
- Bug 1132499 - part 2 - convert nsTextEditorState::mValue to be an nsString; r=ehsan (6e0df4d1dc)
- Bug 409885 - Use SetHostPort in nsHTMLDocument::SetDomain. r=bz (2d289a79c5)
- Bug 1269475 - Fix uninitialized variable warning. r=jdm (881fa29803)
- Bug 1263935 - Expose native version of mapURIToAddonId via amIAddonPathService. r=mossop (04da9fe703)
- missing bit of Bug 1255040 Add webidl for install/uninstall (63b5489b3d)
- Bug 1234974 - Handle null GetOwner() in |BluetoothAdapter::IsBluetoothCertifiedApp|, r=shuang (e4dde47d7f)
- Bug 1241117 - Fix mAppUuid access before assign in BluetoothGatt::Connect. r=joliu (3920535f2a)
- Bug 1238424 - Fix a missing header in Bluetooth module. r=brsun (438fd74375)
- Bug 1267958: Only complain for negative size, not merely zero. r=jrmuizel (194bd2a754)
- Bug 1320621 - Make sure --enable-system-hunspell picks up system headers. r=glandium (125c4ea215)
- Bug 1266430: Support offsets in DrawTargetD2D1::CopySurface when using PartialUploading. r=jrmuizel (de8630b117)
- Bug 1267363. Disable hinting on osx fonts only if we have expclity grayscale AA. r=mstange (c4c32fd7cc)
- Bug 1134549 - Switch FlattenBezier from floats to doubles. r=bas (5753ab16a3)
- Bug 1240437 part 2: Follow-up to fix implicit constrcutor static analysis failure. r=me (525d9d9a9f)
- Bug 1256678 - Backout diagnostic patch rev 39c895b67af2 - r=me (f63698de77)
- Bug 1256678 - Account for truncated font names in ScaledFontWin::GetFontFileData() - r=jfkthame (a6930e0a33)
- Bug 1256678 - Re-add nightly-only crash if ScaledFontWin::GetFontFileData fails to find a matching TrueType collection font - r=jfkthame (8bec2d43a2)
- Bug 1260350 - GFX: 2D: Use ConvolveHorizontally_LS3. r=jrmuizel (9d2f372593)
- Bug 1260112 - GFX: 2D: Fix unaligned access in ConvolveVertically/Horizontally_LS3. r=huangwenjun06 (43ba8f7b05)
- Bug 1260885 - Make SurfaceFormat::R5G6B5_UINT16 properly round-trip through SourceSurfaceCairo. r=lsalzman (bc8f0c0876)
- Bug 1262415 - make Accessible to keep weak pointers to parent and children, r=yzen (c0b987fe10)
- Bug 1267271 - skip empty surfaces in DrawTargetSkia::DrawSurfaceWithShadow. r=mchang (fd488c918a)
- Bug 1249600. Lookup font and font family from font face when requesting SkTypeface. r=bas (0281313c33)
- Bug 1268096. Sync the D3D11 rasterizer state if pointDrawMode or multisample changes. (25b2af8b26)
- Bug 1203132 - Add an SVG for the B2G flow of touch events. r=botond (2ce3b0ddc4)
- Bug 1180322. Add a document describing the history of layers (b3351dbba3)
- Bug 1235223. Update history document. (212d356bd0)
- Bug 1235223 - Fix typos in layers history document. (f19a5802d8)
- Bug 1235223 - Fix another typo in the layers history document. (9663fe5447)
- Bug 1265112 - Add support for NV_texture_barrier to GLContext. r=jrmuizel (f1f5fb1a3a)
- Bug 1240730 - Initialize all GLLibraryEGL symbols in the ctor. r=mattwoodrow (dae8955458)
- Bug 1240730 - Initialize all GLXLibrary members in the ctor. r=mattwoodrow (8218ed6fd9)
- bug 1250485 - make the ctor for GLXLibrary constexpr r=jrmuizel (a571491f0a)
- mitor spacing (0083550ab2)
- Bug 1256492 - Cast to proper width to avoid C4312 on VS2015; r=jrmuizel (1bb2ed20d3)
- Bug 1266878 - Fix off-by-one error in ParamTraits<StereoMode> - r=nical (f467d496b8)
- Bug 1258758 - rename GetEffectiveClipRect to GetLocalClipRect. r=kats (897798948f)
- Bug 1249936 - add a LayerComposite::GetShadowTransform method & simplify Layer::GetLocalTransform. r=botond (fe492a1327)
- Bug 1269032 - Add support for user multipliers to pan gesture inputs. r=mstange (ea7698dca2)
- Bug 1265510 - Add some scroll-snapping logging to APZC. r=botond (6f2369163a)
- Bug 1268523 - Avoid leaking tasks when they are posted to a destroyed APZC instance. r=botond (70e1e63b7b)
- Bug 1265510 - Ensure that new input blocks still allow APZCs with interrupted animations to scroll-snap. r=botond (01341b554b)
- Bug 1269068 - Zero out the axis velocity once the wheel scroll animation is terminating. r=botond (9a9f384485)
- Bug 1246056 - Ensure that the MockContentController's timestamp is always >= GetStartupTime(). r=botond (4fe53e1f24)
- Bug 1256341 - Add a gtest to catch scenarios where a tap timeout is interrupted by a non-touch block. r=botond (21a15f5a5a)
- Bug 1266833 - When the scroll position is clamped during a frame reconstruction, send a scroll offset update to APZ. r=tnikkel (c0acd70a3d)
- Bug 1268517 - Remove unused function. r=botond (534e4d9bae)
- Bug 1213095 - Fix APZEventState build with logging enabled. r=botond (ac7c1e9401)
- Bug 1250954 - Correct the SharedSurfaceTextureClient when forwarder changed. r=jgilbert (0a389bc0fa)
- Bug 1265873 - Use gfxSharedReadLock in TextureClientPool r=nical (e3ed936a8a)
- Bug 1265468 - Update the compositor pointer on all TextureSources in the list when recycling in ImageHost. r=nical (78f29244bb)
- Bug 1252237 - Remove the TextureHost shmem mapping failed assert. r=milan (d0b7230dff)
- Bug 1265112 - When rendering blend modes with CompositorOGL, use NV_texture_barrier (if available) to sample directly from the framebuffer. r=jrmuizel (6f3ae237a5)
- Bug 1268878 - Include various of gfxFontEntry's sub-objects in memory reporting. r=njn (b960415fa8)
- Bug 1268951 - Reallocate sanitized user font data into an appropriately-sized block, to reduce ongoing RAM footprint. r=jrmuizel (a26e9dd632)
- Bug 1123416 - Part 1: Make topsrcdir a valid Gradle project root. r=sebastian (77b0b35fc1)
- Bug 1260672: Remove 'nsAutoPtr.h' and clean up includes of Gonk diskspace watcher, r=gsvelto (99c4c35e3d)
- Bug 1167535 - Fix fanotify 4g size limit. r=fabrice (c62015e454)
- Bug 1167817 - Switch back to printf_stderr in GonkDiskSpaceWatcher.cpp r=dhylands (110445f628)
- Bug 1260672: Use |UniquePtr| for Gonk alarms, r=gsvelto (03678b9015)
- Bug 1260672: Use |UniquePtr| for Gonk sensors, r=gsvelto (545ec3fc70)
- Bug 1263845.  When a parent changes from auto height to non-auto height or vice versa, a percentage height non-block child needs to realize it's doing a vertical resize.  r=dbaron (05db492bc8)
- Bug 1268009 - If APZ is force-disabled, disable paint skipping even for apz-originated scrolls. r=kats (c02f8755cd)
- Bug 1267555 part 1 - [css-grid] Make grid-aligned abs.pos. descendants that span from 'auto' to the first/last line stretch to the outer edge of the first/last track respectively.  r=dholbert (88f6261043)
- Bug 1267555 part 2 - [css-grid] Adjust reftests for abs.pos. "auto/first-line" and "last-line/auto" change. (43d03ab15b)
2024-09-25 09:21:53 +08:00

1834 lines
59 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 = Matrix::Translation(aDestination.x - aSourceRect.x, aDestination.y - aSourceRect.y);
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
if (!image) {
gfxWarning() << "Couldn't get image for surface.";
return;
}
if (mat.HasNonIntegerTranslation()) {
gfxDebug() << *this << ": At this point scaled partial uploads are not supported for CopySurface.";
return;
}
IntRect sourceRect = aSourceRect;
sourceRect.x += (aDestination.x - aSourceRect.x) - mat._31;
sourceRect.width -= (aDestination.x - aSourceRect.x) - mat._31;
sourceRect.y += (aDestination.y - aSourceRect.y) - mat._32;
sourceRect.height -= (aDestination.y - aSourceRect.y) - mat._32;
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(sourceRect.x), Float(sourceRect.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);
}
}
}