1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/DrawTargetD2D1.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,967 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <initguid.h> 1.10 +#include "DrawTargetD2D1.h" 1.11 +#include "DrawTargetD2D.h" 1.12 +#include "FilterNodeSoftware.h" 1.13 +#include "GradientStopsD2D.h" 1.14 +#include "SourceSurfaceD2D1.h" 1.15 +#include "SourceSurfaceD2D.h" 1.16 +#include "RadialGradientEffectD2D1.h" 1.17 + 1.18 +#include "HelpersD2D.h" 1.19 +#include "FilterNodeD2D1.h" 1.20 +#include "Tools.h" 1.21 + 1.22 +using namespace std; 1.23 + 1.24 +namespace mozilla { 1.25 +namespace gfx { 1.26 + 1.27 +uint64_t DrawTargetD2D1::mVRAMUsageDT; 1.28 +uint64_t DrawTargetD2D1::mVRAMUsageSS; 1.29 +ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; 1.30 + 1.31 +ID2D1Factory1 *D2DFactory1() 1.32 +{ 1.33 + return DrawTargetD2D1::factory(); 1.34 +} 1.35 + 1.36 +DrawTargetD2D1::DrawTargetD2D1() 1.37 + : mClipsArePushed(false) 1.38 +{ 1.39 +} 1.40 + 1.41 +DrawTargetD2D1::~DrawTargetD2D1() 1.42 +{ 1.43 + PopAllClips(); 1.44 + 1.45 + mDC->EndDraw(); 1.46 +} 1.47 + 1.48 +TemporaryRef<SourceSurface> 1.49 +DrawTargetD2D1::Snapshot() 1.50 +{ 1.51 + if (mSnapshot) { 1.52 + return mSnapshot; 1.53 + } 1.54 + PopAllClips(); 1.55 + 1.56 + mDC->Flush(); 1.57 + 1.58 + mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this); 1.59 + 1.60 + return mSnapshot; 1.61 +} 1.62 + 1.63 +void 1.64 +DrawTargetD2D1::Flush() 1.65 +{ 1.66 + mDC->Flush(); 1.67 +} 1.68 + 1.69 +void 1.70 +DrawTargetD2D1::DrawSurface(SourceSurface *aSurface, 1.71 + const Rect &aDest, 1.72 + const Rect &aSource, 1.73 + const DrawSurfaceOptions &aSurfOptions, 1.74 + const DrawOptions &aOptions) 1.75 +{ 1.76 + RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, ExtendMode::CLAMP); 1.77 + 1.78 + if (!image) { 1.79 + gfxWarning() << *this << ": Unable to get D2D image for surface."; 1.80 + return; 1.81 + } 1.82 + 1.83 + PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); 1.84 + 1.85 + D2D1_RECT_F samplingBounds; 1.86 + 1.87 + if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) { 1.88 + samplingBounds = D2DRect(aSource); 1.89 + } else { 1.90 + samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height)); 1.91 + } 1.92 + 1.93 + Float xScale = aDest.width / aSource.width; 1.94 + Float yScale = aDest.height / aSource.height; 1.95 + 1.96 + RefPtr<ID2D1ImageBrush> brush; 1.97 + 1.98 + // Here we scale the source pattern up to the size and position where we want 1.99 + // it to be. 1.100 + Matrix transform; 1.101 + transform.Translate(aDest.x, aDest.y); 1.102 + transform.Scale(xScale, yScale); 1.103 + 1.104 + mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds), 1.105 + D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)), 1.106 + byRef(brush)); 1.107 + mDC->FillRectangle(D2DRect(aDest), brush); 1.108 + 1.109 + FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color())); 1.110 +} 1.111 + 1.112 +void 1.113 +DrawTargetD2D1::DrawFilter(FilterNode *aNode, 1.114 + const Rect &aSourceRect, 1.115 + const Point &aDestPoint, 1.116 + const DrawOptions &aOptions) 1.117 +{ 1.118 + if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) { 1.119 + gfxWarning() << *this << ": Incompatible filter passed to DrawFilter."; 1.120 + return; 1.121 + } 1.122 + 1.123 + PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); 1.124 + 1.125 + mDC->DrawImage(static_cast<FilterNodeD2D1*>(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); 1.126 +} 1.127 + 1.128 +void 1.129 +DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface, 1.130 + const Point &aDest, 1.131 + const Color &aColor, 1.132 + const Point &aOffset, 1.133 + Float aSigma, 1.134 + CompositionOp aOperator) 1.135 +{ 1.136 + MarkChanged(); 1.137 + mDC->SetTransform(D2D1::IdentityMatrix()); 1.138 + mTransformDirty = true; 1.139 + 1.140 + Matrix mat; 1.141 + RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); 1.142 + 1.143 + if (!mat.IsIdentity()) { 1.144 + gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces."; 1.145 + return; 1.146 + } 1.147 + 1.148 + // Step 1, create the shadow effect. 1.149 + RefPtr<ID2D1Effect> shadowEffect; 1.150 + mDC->CreateEffect(CLSID_D2D1Shadow, byRef(shadowEffect)); 1.151 + shadowEffect->SetInput(0, image); 1.152 + shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma); 1.153 + D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a }; 1.154 + shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color); 1.155 + 1.156 + // Step 2, move the shadow effect into place. 1.157 + RefPtr<ID2D1Effect> affineTransformEffect; 1.158 + mDC->CreateEffect(CLSID_D2D12DAffineTransform, byRef(affineTransformEffect)); 1.159 + affineTransformEffect->SetInputEffect(0, shadowEffect); 1.160 + D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation(aOffset.x, aOffset.y); 1.161 + affineTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, matrix); 1.162 + 1.163 + // Step 3, create an effect that combines shadow and bitmap in one image. 1.164 + RefPtr<ID2D1Effect> compositeEffect; 1.165 + mDC->CreateEffect(CLSID_D2D1Composite, byRef(compositeEffect)); 1.166 + compositeEffect->SetInputEffect(0, affineTransformEffect); 1.167 + compositeEffect->SetInput(1, image); 1.168 + compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2DCompositionMode(aOperator)); 1.169 + 1.170 + D2D1_POINT_2F surfPoint = D2DPoint(aDest); 1.171 + mDC->DrawImage(compositeEffect, &surfPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator)); 1.172 +} 1.173 + 1.174 +void 1.175 +DrawTargetD2D1::ClearRect(const Rect &aRect) 1.176 +{ 1.177 + MarkChanged(); 1.178 + 1.179 + mDC->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); 1.180 + mDC->Clear(); 1.181 + mDC->PopAxisAlignedClip(); 1.182 +} 1.183 + 1.184 +void 1.185 +DrawTargetD2D1::MaskSurface(const Pattern &aSource, 1.186 + SourceSurface *aMask, 1.187 + Point aOffset, 1.188 + const DrawOptions &aOptions) 1.189 +{ 1.190 + RefPtr<ID2D1Bitmap> bitmap; 1.191 + 1.192 + RefPtr<ID2D1Image> image = GetImageForSurface(aMask, ExtendMode::CLAMP); 1.193 + 1.194 + PrepareForDrawing(aOptions.mCompositionOp, aSource); 1.195 + 1.196 + // FillOpacityMask only works if the antialias mode is MODE_ALIASED 1.197 + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); 1.198 + 1.199 + IntSize size = aMask->GetSize(); 1.200 + Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height)); 1.201 + image->QueryInterface((ID2D1Bitmap**)&bitmap); 1.202 + if (!bitmap) { 1.203 + gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces."; 1.204 + return; 1.205 + } 1.206 + 1.207 + Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height)); 1.208 + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha); 1.209 + mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); 1.210 + 1.211 + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); 1.212 + 1.213 + FinalizeDrawing(aOptions.mCompositionOp, aSource); 1.214 +} 1.215 + 1.216 +void 1.217 +DrawTargetD2D1::CopySurface(SourceSurface *aSurface, 1.218 + const IntRect &aSourceRect, 1.219 + const IntPoint &aDestination) 1.220 +{ 1.221 + MarkChanged(); 1.222 + 1.223 + mDC->SetTransform(D2D1::IdentityMatrix()); 1.224 + mTransformDirty = true; 1.225 + 1.226 + Matrix mat; 1.227 + RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); 1.228 + 1.229 + if (!mat.IsIdentity()) { 1.230 + gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface."; 1.231 + return; 1.232 + } 1.233 + 1.234 + mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)), 1.235 + D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y), 1.236 + Float(aSourceRect.XMost()), Float(aSourceRect.YMost())), 1.237 + D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); 1.238 +} 1.239 + 1.240 +void 1.241 +DrawTargetD2D1::FillRect(const Rect &aRect, 1.242 + const Pattern &aPattern, 1.243 + const DrawOptions &aOptions) 1.244 +{ 1.245 + PrepareForDrawing(aOptions.mCompositionOp, aPattern); 1.246 + 1.247 + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); 1.248 + mDC->FillRectangle(D2DRect(aRect), brush); 1.249 + 1.250 + FinalizeDrawing(aOptions.mCompositionOp, aPattern); 1.251 +} 1.252 + 1.253 +void 1.254 +DrawTargetD2D1::StrokeRect(const Rect &aRect, 1.255 + const Pattern &aPattern, 1.256 + const StrokeOptions &aStrokeOptions, 1.257 + const DrawOptions &aOptions) 1.258 +{ 1.259 + PrepareForDrawing(aOptions.mCompositionOp, aPattern); 1.260 + 1.261 + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); 1.262 + RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); 1.263 + 1.264 + mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); 1.265 + 1.266 + FinalizeDrawing(aOptions.mCompositionOp, aPattern); 1.267 +} 1.268 + 1.269 +void 1.270 +DrawTargetD2D1::StrokeLine(const Point &aStart, 1.271 + const Point &aEnd, 1.272 + const Pattern &aPattern, 1.273 + const StrokeOptions &aStrokeOptions, 1.274 + const DrawOptions &aOptions) 1.275 +{ 1.276 + PrepareForDrawing(aOptions.mCompositionOp, aPattern); 1.277 + 1.278 + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); 1.279 + RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); 1.280 + 1.281 + mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); 1.282 + 1.283 + FinalizeDrawing(aOptions.mCompositionOp, aPattern); 1.284 +} 1.285 + 1.286 +void 1.287 +DrawTargetD2D1::Stroke(const Path *aPath, 1.288 + const Pattern &aPattern, 1.289 + const StrokeOptions &aStrokeOptions, 1.290 + const DrawOptions &aOptions) 1.291 +{ 1.292 + if (aPath->GetBackendType() != BackendType::DIRECT2D) { 1.293 + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; 1.294 + return; 1.295 + } 1.296 + const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath); 1.297 + 1.298 + PrepareForDrawing(aOptions.mCompositionOp, aPattern); 1.299 + 1.300 + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); 1.301 + RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); 1.302 + 1.303 + mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); 1.304 + 1.305 + FinalizeDrawing(aOptions.mCompositionOp, aPattern); 1.306 +} 1.307 + 1.308 +void 1.309 +DrawTargetD2D1::Fill(const Path *aPath, 1.310 + const Pattern &aPattern, 1.311 + const DrawOptions &aOptions) 1.312 +{ 1.313 + if (aPath->GetBackendType() != BackendType::DIRECT2D) { 1.314 + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; 1.315 + return; 1.316 + } 1.317 + const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath); 1.318 + 1.319 + PrepareForDrawing(aOptions.mCompositionOp, aPattern); 1.320 + 1.321 + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); 1.322 + 1.323 + mDC->FillGeometry(d2dPath->mGeometry, brush); 1.324 + 1.325 + FinalizeDrawing(aOptions.mCompositionOp, aPattern); 1.326 +} 1.327 + 1.328 +void 1.329 +DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, 1.330 + const GlyphBuffer &aBuffer, 1.331 + const Pattern &aPattern, 1.332 + const DrawOptions &aOptions, 1.333 + const GlyphRenderingOptions *aRenderingOptions) 1.334 +{ 1.335 + if (aFont->GetType() != FontType::DWRITE) { 1.336 + gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; 1.337 + return; 1.338 + } 1.339 + 1.340 + ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont); 1.341 + 1.342 + IDWriteRenderingParams *params = nullptr; 1.343 + if (aRenderingOptions) { 1.344 + if (aRenderingOptions->GetType() != FontType::DWRITE) { 1.345 + gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; 1.346 + // This should never happen. 1.347 + MOZ_ASSERT(false); 1.348 + } else { 1.349 + params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderingOptions)->mParams; 1.350 + } 1.351 + } 1.352 + 1.353 + AntialiasMode aaMode = font->GetDefaultAAMode(); 1.354 + 1.355 + if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { 1.356 + aaMode = aOptions.mAntialiasMode; 1.357 + } 1.358 + 1.359 + PrepareForDrawing(aOptions.mCompositionOp, aPattern); 1.360 + 1.361 + bool forceClearType = false; 1.362 + if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && 1.363 + aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) { 1.364 + forceClearType = true; 1.365 + } 1.366 + 1.367 + 1.368 + D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; 1.369 + 1.370 + switch (aaMode) { 1.371 + case AntialiasMode::NONE: 1.372 + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; 1.373 + break; 1.374 + case AntialiasMode::GRAY: 1.375 + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; 1.376 + break; 1.377 + case AntialiasMode::SUBPIXEL: 1.378 + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; 1.379 + break; 1.380 + default: 1.381 + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; 1.382 + } 1.383 + 1.384 + if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && 1.385 + mFormat != SurfaceFormat::B8G8R8X8 && !forceClearType) { 1.386 + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; 1.387 + } 1.388 + 1.389 + mDC->SetTextAntialiasMode(d2dAAMode); 1.390 + 1.391 + if (params != mTextRenderingParams) { 1.392 + mDC->SetTextRenderingParams(params); 1.393 + mTextRenderingParams = params; 1.394 + } 1.395 + 1.396 + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); 1.397 + 1.398 + AutoDWriteGlyphRun autoRun; 1.399 + DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); 1.400 + 1.401 + if (brush) { 1.402 + mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); 1.403 + } 1.404 + 1.405 + FinalizeDrawing(aOptions.mCompositionOp, aPattern); 1.406 +} 1.407 + 1.408 +void 1.409 +DrawTargetD2D1::Mask(const Pattern &aSource, 1.410 + const Pattern &aMask, 1.411 + const DrawOptions &aOptions) 1.412 +{ 1.413 + PrepareForDrawing(aOptions.mCompositionOp, aSource); 1.414 + 1.415 + RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha); 1.416 + RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f); 1.417 + mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, 1.418 + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, 1.419 + D2D1::IdentityMatrix(), 1.420 + 1.0f, mask), 1.421 + nullptr); 1.422 + 1.423 + Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height); 1.424 + Matrix mat = mTransform; 1.425 + mat.Invert(); 1.426 + 1.427 + mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source); 1.428 + 1.429 + mDC->PopLayer(); 1.430 + 1.431 + FinalizeDrawing(aOptions.mCompositionOp, aSource); 1.432 +} 1.433 + 1.434 +void 1.435 +DrawTargetD2D1::PushClip(const Path *aPath) 1.436 +{ 1.437 + if (aPath->GetBackendType() != BackendType::DIRECT2D) { 1.438 + gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; 1.439 + return; 1.440 + } 1.441 + 1.442 + RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath)); 1.443 + 1.444 + PushedClip clip; 1.445 + clip.mTransform = D2DMatrix(mTransform); 1.446 + clip.mPath = pathD2D; 1.447 + 1.448 + pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); 1.449 + 1.450 + mPushedClips.push_back(clip); 1.451 + 1.452 + // The transform of clips is relative to the world matrix, since we use the total 1.453 + // transform for the clips, make the world matrix identity. 1.454 + mDC->SetTransform(D2D1::IdentityMatrix()); 1.455 + mTransformDirty = true; 1.456 + 1.457 + if (mClipsArePushed) { 1.458 + PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform); 1.459 + } 1.460 +} 1.461 + 1.462 +void 1.463 +DrawTargetD2D1::PushClipRect(const Rect &aRect) 1.464 +{ 1.465 + if (!mTransform.IsRectilinear()) { 1.466 + // Whoops, this isn't a rectangle in device space, Direct2D will not deal 1.467 + // with this transform the way we want it to. 1.468 + // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx 1.469 + 1.470 + RefPtr<PathBuilder> pathBuilder = CreatePathBuilder(); 1.471 + pathBuilder->MoveTo(aRect.TopLeft()); 1.472 + pathBuilder->LineTo(aRect.TopRight()); 1.473 + pathBuilder->LineTo(aRect.BottomRight()); 1.474 + pathBuilder->LineTo(aRect.BottomLeft()); 1.475 + pathBuilder->Close(); 1.476 + RefPtr<Path> path = pathBuilder->Finish(); 1.477 + return PushClip(path); 1.478 + } 1.479 + 1.480 + PushedClip clip; 1.481 + Rect rect = mTransform.TransformBounds(aRect); 1.482 + IntRect intRect; 1.483 + clip.mIsPixelAligned = rect.ToIntRect(&intRect); 1.484 + 1.485 + // Do not store the transform, just store the device space rectangle directly. 1.486 + clip.mBounds = D2DRect(rect); 1.487 + 1.488 + mPushedClips.push_back(clip); 1.489 + 1.490 + mDC->SetTransform(D2D1::IdentityMatrix()); 1.491 + mTransformDirty = true; 1.492 + 1.493 + if (mClipsArePushed) { 1.494 + mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); 1.495 + } 1.496 +} 1.497 + 1.498 +void 1.499 +DrawTargetD2D1::PopClip() 1.500 +{ 1.501 + if (mClipsArePushed) { 1.502 + if (mPushedClips.back().mPath) { 1.503 + mDC->PopLayer(); 1.504 + } else { 1.505 + mDC->PopAxisAlignedClip(); 1.506 + } 1.507 + } 1.508 + mPushedClips.pop_back(); 1.509 +} 1.510 + 1.511 +TemporaryRef<SourceSurface> 1.512 +DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData, 1.513 + const IntSize &aSize, 1.514 + int32_t aStride, 1.515 + SurfaceFormat aFormat) const 1.516 +{ 1.517 + RefPtr<ID2D1Bitmap1> bitmap; 1.518 + 1.519 + HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride, 1.520 + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)), 1.521 + byRef(bitmap)); 1.522 + 1.523 + if (!bitmap) { 1.524 + return nullptr; 1.525 + } 1.526 + 1.527 + return new SourceSurfaceD2D1(bitmap.get(), mDC, aFormat, aSize); 1.528 +} 1.529 + 1.530 +TemporaryRef<DrawTarget> 1.531 +DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const 1.532 +{ 1.533 + RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1(); 1.534 + 1.535 + if (!dt->Init(aSize, aFormat)) { 1.536 + return nullptr; 1.537 + } 1.538 + 1.539 + return dt; 1.540 +} 1.541 + 1.542 +TemporaryRef<PathBuilder> 1.543 +DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const 1.544 +{ 1.545 + RefPtr<ID2D1PathGeometry> path; 1.546 + HRESULT hr = factory()->CreatePathGeometry(byRef(path)); 1.547 + 1.548 + if (FAILED(hr)) { 1.549 + gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hr; 1.550 + return nullptr; 1.551 + } 1.552 + 1.553 + RefPtr<ID2D1GeometrySink> sink; 1.554 + hr = path->Open(byRef(sink)); 1.555 + if (FAILED(hr)) { 1.556 + gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hr; 1.557 + return nullptr; 1.558 + } 1.559 + 1.560 + if (aFillRule == FillRule::FILL_WINDING) { 1.561 + sink->SetFillMode(D2D1_FILL_MODE_WINDING); 1.562 + } 1.563 + 1.564 + return new PathBuilderD2D(sink, path, aFillRule); 1.565 +} 1.566 + 1.567 +TemporaryRef<GradientStops> 1.568 +DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const 1.569 +{ 1.570 + D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops]; 1.571 + 1.572 + for (uint32_t i = 0; i < aNumStops; i++) { 1.573 + stops[i].position = rawStops[i].offset; 1.574 + stops[i].color = D2DColor(rawStops[i].color); 1.575 + } 1.576 + 1.577 + RefPtr<ID2D1GradientStopCollection> stopCollection; 1.578 + 1.579 + HRESULT hr = 1.580 + mDC->CreateGradientStopCollection(stops, aNumStops, 1.581 + D2D1_GAMMA_2_2, D2DExtend(aExtendMode), 1.582 + byRef(stopCollection)); 1.583 + delete [] stops; 1.584 + 1.585 + if (FAILED(hr)) { 1.586 + gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hr; 1.587 + return nullptr; 1.588 + } 1.589 + 1.590 + return new GradientStopsD2D(stopCollection); 1.591 +} 1.592 + 1.593 +TemporaryRef<FilterNode> 1.594 +DrawTargetD2D1::CreateFilter(FilterType aType) 1.595 +{ 1.596 + return FilterNodeD2D1::Create(this, mDC, aType); 1.597 +} 1.598 + 1.599 +bool 1.600 +DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) 1.601 +{ 1.602 + HRESULT hr; 1.603 + 1.604 + hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC)); 1.605 + 1.606 + if (FAILED(hr)) { 1.607 + gfxWarning() << *this << ": Error " << hr << " failed to initialize new DeviceContext."; 1.608 + return false; 1.609 + } 1.610 + 1.611 + D2D1_BITMAP_PROPERTIES1 props; 1.612 + props.dpiX = 96; 1.613 + props.dpiY = 96; 1.614 + props.pixelFormat = D2DPixelFormat(aFormat); 1.615 + props.colorContext = nullptr; 1.616 + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; 1.617 + mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mBitmap)); 1.618 + 1.619 + if (FAILED(hr)) { 1.620 + gfxWarning() << *this << ": Error " << hr << " failed to create new CommandList."; 1.621 + return false; 1.622 + } 1.623 + 1.624 + mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap)); 1.625 + 1.626 + mDC->SetTarget(mBitmap); 1.627 + 1.628 + mDC->BeginDraw(); 1.629 + 1.630 + mFormat = aFormat; 1.631 + mSize = aSize; 1.632 + 1.633 + return true; 1.634 +} 1.635 + 1.636 +/** 1.637 + * Private helpers. 1.638 + */ 1.639 +uint32_t 1.640 +DrawTargetD2D1::GetByteSize() const 1.641 +{ 1.642 + return mSize.width * mSize.height * BytesPerPixel(mFormat); 1.643 +} 1.644 + 1.645 +ID2D1Factory1* 1.646 +DrawTargetD2D1::factory() 1.647 +{ 1.648 + if (mFactory) { 1.649 + return mFactory; 1.650 + } 1.651 + 1.652 + HRESULT hr = D2DFactory()->QueryInterface((ID2D1Factory1**)&mFactory); 1.653 + 1.654 + if (FAILED(hr)) { 1.655 + return nullptr; 1.656 + } 1.657 + 1.658 + RadialGradientEffectD2D1::Register(mFactory); 1.659 + 1.660 + return mFactory; 1.661 +} 1.662 + 1.663 +void 1.664 +DrawTargetD2D1::MarkChanged() 1.665 +{ 1.666 + if (mSnapshot) { 1.667 + if (mSnapshot->hasOneRef()) { 1.668 + // Just destroy it, since no-one else knows about it. 1.669 + mSnapshot = nullptr; 1.670 + } else { 1.671 + mSnapshot->DrawTargetWillChange(); 1.672 + // The snapshot will no longer depend on this target. 1.673 + MOZ_ASSERT(!mSnapshot); 1.674 + } 1.675 + } 1.676 + if (mDependentTargets.size()) { 1.677 + // Copy mDependentTargets since the Flush()es below will modify it. 1.678 + TargetSet tmpTargets = mDependentTargets; 1.679 + for (TargetSet::iterator iter = tmpTargets.begin(); 1.680 + iter != tmpTargets.end(); iter++) { 1.681 + (*iter)->Flush(); 1.682 + } 1.683 + // The Flush() should have broken all dependencies on this target. 1.684 + MOZ_ASSERT(!mDependentTargets.size()); 1.685 + } 1.686 +} 1.687 + 1.688 +void 1.689 +DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern) 1.690 +{ 1.691 + MarkChanged(); 1.692 + 1.693 + // It's important to do this before FlushTransformToDC! As this will cause 1.694 + // the transform to become dirty. 1.695 + if (!mClipsArePushed) { 1.696 + mClipsArePushed = true; 1.697 + PushClipsToDC(mDC); 1.698 + } 1.699 + 1.700 + FlushTransformToDC(); 1.701 + 1.702 + if (aOp == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { 1.703 + return; 1.704 + } 1.705 + 1.706 + mDC->SetTarget(mTempBitmap); 1.707 + mDC->Clear(D2D1::ColorF(0, 0)); 1.708 +} 1.709 + 1.710 +void 1.711 +DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) 1.712 +{ 1.713 + bool patternSupported = IsPatternSupportedByD2D(aPattern); 1.714 + 1.715 + if (aOp == CompositionOp::OP_OVER && patternSupported) { 1.716 + return; 1.717 + } 1.718 + 1.719 + RefPtr<ID2D1Image> image; 1.720 + mDC->GetTarget(byRef(image)); 1.721 + 1.722 + mDC->SetTarget(mBitmap); 1.723 + 1.724 + if (patternSupported) { 1.725 + mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); 1.726 + return; 1.727 + } 1.728 + 1.729 + mDC->SetTransform(D2D1::IdentityMatrix()); 1.730 + mTransformDirty = true; 1.731 + 1.732 + RefPtr<ID2D1Effect> radialGradientEffect; 1.733 + 1.734 + mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect)); 1.735 + const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern); 1.736 + 1.737 + radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION, 1.738 + static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection); 1.739 + radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y)); 1.740 + radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y)); 1.741 + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1); 1.742 + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); 1.743 + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); 1.744 + radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform)); 1.745 + radialGradientEffect->SetInput(0, image); 1.746 + 1.747 + mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); 1.748 +} 1.749 + 1.750 +void 1.751 +DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource) 1.752 +{ 1.753 + if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) { 1.754 + aSource->mDrawTarget->mDependentTargets.insert(this); 1.755 + mDependingOnTargets.insert(aSource->mDrawTarget); 1.756 + } 1.757 +} 1.758 + 1.759 +void 1.760 +DrawTargetD2D1::PopAllClips() 1.761 +{ 1.762 + if (mClipsArePushed) { 1.763 + PopClipsFromDC(mDC); 1.764 + 1.765 + mClipsArePushed = false; 1.766 + } 1.767 +} 1.768 + 1.769 +void 1.770 +DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC) 1.771 +{ 1.772 + mDC->SetTransform(D2D1::IdentityMatrix()); 1.773 + mTransformDirty = true; 1.774 + 1.775 + for (std::vector<PushedClip>::iterator iter = mPushedClips.begin(); 1.776 + iter != mPushedClips.end(); iter++) { 1.777 + if (iter->mPath) { 1.778 + PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform); 1.779 + } else { 1.780 + mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); 1.781 + } 1.782 + } 1.783 +} 1.784 + 1.785 +void 1.786 +DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC) 1.787 +{ 1.788 + for (int i = mPushedClips.size() - 1; i >= 0; i--) { 1.789 + if (mPushedClips[i].mPath) { 1.790 + aDC->PopLayer(); 1.791 + } else { 1.792 + aDC->PopAxisAlignedClip(); 1.793 + } 1.794 + } 1.795 +} 1.796 + 1.797 +TemporaryRef<ID2D1Brush> 1.798 +DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) 1.799 +{ 1.800 + if (!IsPatternSupportedByD2D(aPattern)) { 1.801 + RefPtr<ID2D1SolidColorBrush> colBrush; 1.802 + mDC->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush)); 1.803 + return colBrush; 1.804 + } 1.805 + 1.806 + if (aPattern.GetType() == PatternType::COLOR) { 1.807 + RefPtr<ID2D1SolidColorBrush> colBrush; 1.808 + Color color = static_cast<const ColorPattern*>(&aPattern)->mColor; 1.809 + mDC->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, 1.810 + color.b, color.a), 1.811 + D2D1::BrushProperties(aAlpha), 1.812 + byRef(colBrush)); 1.813 + return colBrush; 1.814 + } else if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { 1.815 + RefPtr<ID2D1LinearGradientBrush> gradBrush; 1.816 + const LinearGradientPattern *pat = 1.817 + static_cast<const LinearGradientPattern*>(&aPattern); 1.818 + 1.819 + GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get()); 1.820 + 1.821 + if (!stops) { 1.822 + gfxDebug() << "No stops specified for gradient pattern."; 1.823 + return nullptr; 1.824 + } 1.825 + 1.826 + if (pat->mBegin == pat->mEnd) { 1.827 + RefPtr<ID2D1SolidColorBrush> colBrush; 1.828 + uint32_t stopCount = stops->mStopCollection->GetGradientStopCount(); 1.829 + vector<D2D1_GRADIENT_STOP> d2dStops(stopCount); 1.830 + stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount); 1.831 + mDC->CreateSolidColorBrush(d2dStops.back().color, 1.832 + D2D1::BrushProperties(aAlpha), 1.833 + byRef(colBrush)); 1.834 + return colBrush; 1.835 + } 1.836 + 1.837 + mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), 1.838 + D2DPoint(pat->mEnd)), 1.839 + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), 1.840 + stops->mStopCollection, 1.841 + byRef(gradBrush)); 1.842 + return gradBrush; 1.843 + } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { 1.844 + RefPtr<ID2D1RadialGradientBrush> gradBrush; 1.845 + const RadialGradientPattern *pat = 1.846 + static_cast<const RadialGradientPattern*>(&aPattern); 1.847 + 1.848 + GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get()); 1.849 + 1.850 + if (!stops) { 1.851 + gfxDebug() << "No stops specified for gradient pattern."; 1.852 + return nullptr; 1.853 + } 1.854 + 1.855 + // This will not be a complex radial gradient brush. 1.856 + mDC->CreateRadialGradientBrush( 1.857 + D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2), 1.858 + D2DPoint(pat->mCenter1 - pat->mCenter2), 1.859 + pat->mRadius2, pat->mRadius2), 1.860 + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), 1.861 + stops->mStopCollection, 1.862 + byRef(gradBrush)); 1.863 + 1.864 + return gradBrush; 1.865 + } else if (aPattern.GetType() == PatternType::SURFACE) { 1.866 + const SurfacePattern *pat = 1.867 + static_cast<const SurfacePattern*>(&aPattern); 1.868 + 1.869 + if (!pat->mSurface) { 1.870 + gfxDebug() << "No source surface specified for surface pattern"; 1.871 + return nullptr; 1.872 + } 1.873 + 1.874 + 1.875 + Matrix mat = pat->mMatrix; 1.876 + 1.877 + RefPtr<ID2D1ImageBrush> imageBrush; 1.878 + RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode); 1.879 + mDC->CreateImageBrush(image, 1.880 + D2D1::ImageBrushProperties(D2D1::RectF(0, 0, 1.881 + Float(pat->mSurface->GetSize().width), 1.882 + Float(pat->mSurface->GetSize().height)), 1.883 + D2DExtend(pat->mExtendMode), D2DExtend(pat->mExtendMode), 1.884 + D2DInterpolationMode(pat->mFilter)), 1.885 + D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), 1.886 + byRef(imageBrush)); 1.887 + return imageBrush; 1.888 + } 1.889 + 1.890 + gfxWarning() << "Invalid pattern type detected."; 1.891 + return nullptr; 1.892 +} 1.893 + 1.894 +TemporaryRef<ID2D1Image> 1.895 +DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, 1.896 + ExtendMode aExtendMode) 1.897 +{ 1.898 + RefPtr<ID2D1Image> image; 1.899 + 1.900 + switch (aSurface->GetType()) { 1.901 + case SurfaceType::D2D1_1_IMAGE: 1.902 + { 1.903 + SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface); 1.904 + image = surf->GetImage(); 1.905 + AddDependencyOnSource(surf); 1.906 + } 1.907 + break; 1.908 + default: 1.909 + { 1.910 + RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface(); 1.911 + if (!dataSurf) { 1.912 + gfxWarning() << "Invalid surface type."; 1.913 + return nullptr; 1.914 + } 1.915 + 1.916 + image = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode, 1.917 + aSourceTransform, mDC); 1.918 + 1.919 + return image; 1.920 + } 1.921 + break; 1.922 + } 1.923 + 1.924 + return image; 1.925 +} 1.926 + 1.927 +TemporaryRef<SourceSurface> 1.928 +DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const 1.929 +{ 1.930 + if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { 1.931 + return aSurface; 1.932 + } 1.933 + 1.934 + RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); 1.935 + 1.936 + DataSourceSurface::MappedSurface map; 1.937 + if (!data->Map(DataSourceSurface::MapType::READ, &map)) { 1.938 + return nullptr; 1.939 + } 1.940 + 1.941 + RefPtr<ID2D1Bitmap1> bitmap; 1.942 + HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.mData, map.mStride, 1.943 + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())), 1.944 + byRef(bitmap)); 1.945 + 1.946 + data->Unmap(); 1.947 + 1.948 + if (!bitmap) { 1.949 + return data; 1.950 + } 1.951 + 1.952 + return new SourceSurfaceD2D1(bitmap.get(), mDC, data->GetFormat(), data->GetSize()); 1.953 +} 1.954 + 1.955 +void 1.956 +DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) 1.957 +{ 1.958 + D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE; 1.959 + 1.960 + if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { 1.961 + options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; 1.962 + } 1.963 + 1.964 + mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, 1.965 + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, 1.966 + 1.0, nullptr, options), nullptr); 1.967 +} 1.968 + 1.969 +} 1.970 +}