Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include <initguid.h> |
michael@0 | 7 | #include "DrawTargetD2D1.h" |
michael@0 | 8 | #include "DrawTargetD2D.h" |
michael@0 | 9 | #include "FilterNodeSoftware.h" |
michael@0 | 10 | #include "GradientStopsD2D.h" |
michael@0 | 11 | #include "SourceSurfaceD2D1.h" |
michael@0 | 12 | #include "SourceSurfaceD2D.h" |
michael@0 | 13 | #include "RadialGradientEffectD2D1.h" |
michael@0 | 14 | |
michael@0 | 15 | #include "HelpersD2D.h" |
michael@0 | 16 | #include "FilterNodeD2D1.h" |
michael@0 | 17 | #include "Tools.h" |
michael@0 | 18 | |
michael@0 | 19 | using namespace std; |
michael@0 | 20 | |
michael@0 | 21 | namespace mozilla { |
michael@0 | 22 | namespace gfx { |
michael@0 | 23 | |
michael@0 | 24 | uint64_t DrawTargetD2D1::mVRAMUsageDT; |
michael@0 | 25 | uint64_t DrawTargetD2D1::mVRAMUsageSS; |
michael@0 | 26 | ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; |
michael@0 | 27 | |
michael@0 | 28 | ID2D1Factory1 *D2DFactory1() |
michael@0 | 29 | { |
michael@0 | 30 | return DrawTargetD2D1::factory(); |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | DrawTargetD2D1::DrawTargetD2D1() |
michael@0 | 34 | : mClipsArePushed(false) |
michael@0 | 35 | { |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | DrawTargetD2D1::~DrawTargetD2D1() |
michael@0 | 39 | { |
michael@0 | 40 | PopAllClips(); |
michael@0 | 41 | |
michael@0 | 42 | mDC->EndDraw(); |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | TemporaryRef<SourceSurface> |
michael@0 | 46 | DrawTargetD2D1::Snapshot() |
michael@0 | 47 | { |
michael@0 | 48 | if (mSnapshot) { |
michael@0 | 49 | return mSnapshot; |
michael@0 | 50 | } |
michael@0 | 51 | PopAllClips(); |
michael@0 | 52 | |
michael@0 | 53 | mDC->Flush(); |
michael@0 | 54 | |
michael@0 | 55 | mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this); |
michael@0 | 56 | |
michael@0 | 57 | return mSnapshot; |
michael@0 | 58 | } |
michael@0 | 59 | |
michael@0 | 60 | void |
michael@0 | 61 | DrawTargetD2D1::Flush() |
michael@0 | 62 | { |
michael@0 | 63 | mDC->Flush(); |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | void |
michael@0 | 67 | DrawTargetD2D1::DrawSurface(SourceSurface *aSurface, |
michael@0 | 68 | const Rect &aDest, |
michael@0 | 69 | const Rect &aSource, |
michael@0 | 70 | const DrawSurfaceOptions &aSurfOptions, |
michael@0 | 71 | const DrawOptions &aOptions) |
michael@0 | 72 | { |
michael@0 | 73 | RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, ExtendMode::CLAMP); |
michael@0 | 74 | |
michael@0 | 75 | if (!image) { |
michael@0 | 76 | gfxWarning() << *this << ": Unable to get D2D image for surface."; |
michael@0 | 77 | return; |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); |
michael@0 | 81 | |
michael@0 | 82 | D2D1_RECT_F samplingBounds; |
michael@0 | 83 | |
michael@0 | 84 | if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) { |
michael@0 | 85 | samplingBounds = D2DRect(aSource); |
michael@0 | 86 | } else { |
michael@0 | 87 | samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height)); |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | Float xScale = aDest.width / aSource.width; |
michael@0 | 91 | Float yScale = aDest.height / aSource.height; |
michael@0 | 92 | |
michael@0 | 93 | RefPtr<ID2D1ImageBrush> brush; |
michael@0 | 94 | |
michael@0 | 95 | // Here we scale the source pattern up to the size and position where we want |
michael@0 | 96 | // it to be. |
michael@0 | 97 | Matrix transform; |
michael@0 | 98 | transform.Translate(aDest.x, aDest.y); |
michael@0 | 99 | transform.Scale(xScale, yScale); |
michael@0 | 100 | |
michael@0 | 101 | mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds), |
michael@0 | 102 | D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)), |
michael@0 | 103 | byRef(brush)); |
michael@0 | 104 | mDC->FillRectangle(D2DRect(aDest), brush); |
michael@0 | 105 | |
michael@0 | 106 | FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color())); |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | void |
michael@0 | 110 | DrawTargetD2D1::DrawFilter(FilterNode *aNode, |
michael@0 | 111 | const Rect &aSourceRect, |
michael@0 | 112 | const Point &aDestPoint, |
michael@0 | 113 | const DrawOptions &aOptions) |
michael@0 | 114 | { |
michael@0 | 115 | if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) { |
michael@0 | 116 | gfxWarning() << *this << ": Incompatible filter passed to DrawFilter."; |
michael@0 | 117 | return; |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); |
michael@0 | 121 | |
michael@0 | 122 | mDC->DrawImage(static_cast<FilterNodeD2D1*>(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | void |
michael@0 | 126 | DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface, |
michael@0 | 127 | const Point &aDest, |
michael@0 | 128 | const Color &aColor, |
michael@0 | 129 | const Point &aOffset, |
michael@0 | 130 | Float aSigma, |
michael@0 | 131 | CompositionOp aOperator) |
michael@0 | 132 | { |
michael@0 | 133 | MarkChanged(); |
michael@0 | 134 | mDC->SetTransform(D2D1::IdentityMatrix()); |
michael@0 | 135 | mTransformDirty = true; |
michael@0 | 136 | |
michael@0 | 137 | Matrix mat; |
michael@0 | 138 | RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); |
michael@0 | 139 | |
michael@0 | 140 | if (!mat.IsIdentity()) { |
michael@0 | 141 | gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces."; |
michael@0 | 142 | return; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | // Step 1, create the shadow effect. |
michael@0 | 146 | RefPtr<ID2D1Effect> shadowEffect; |
michael@0 | 147 | mDC->CreateEffect(CLSID_D2D1Shadow, byRef(shadowEffect)); |
michael@0 | 148 | shadowEffect->SetInput(0, image); |
michael@0 | 149 | shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma); |
michael@0 | 150 | D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a }; |
michael@0 | 151 | shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color); |
michael@0 | 152 | |
michael@0 | 153 | // Step 2, move the shadow effect into place. |
michael@0 | 154 | RefPtr<ID2D1Effect> affineTransformEffect; |
michael@0 | 155 | mDC->CreateEffect(CLSID_D2D12DAffineTransform, byRef(affineTransformEffect)); |
michael@0 | 156 | affineTransformEffect->SetInputEffect(0, shadowEffect); |
michael@0 | 157 | D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation(aOffset.x, aOffset.y); |
michael@0 | 158 | affineTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, matrix); |
michael@0 | 159 | |
michael@0 | 160 | // Step 3, create an effect that combines shadow and bitmap in one image. |
michael@0 | 161 | RefPtr<ID2D1Effect> compositeEffect; |
michael@0 | 162 | mDC->CreateEffect(CLSID_D2D1Composite, byRef(compositeEffect)); |
michael@0 | 163 | compositeEffect->SetInputEffect(0, affineTransformEffect); |
michael@0 | 164 | compositeEffect->SetInput(1, image); |
michael@0 | 165 | compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2DCompositionMode(aOperator)); |
michael@0 | 166 | |
michael@0 | 167 | D2D1_POINT_2F surfPoint = D2DPoint(aDest); |
michael@0 | 168 | mDC->DrawImage(compositeEffect, &surfPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator)); |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | void |
michael@0 | 172 | DrawTargetD2D1::ClearRect(const Rect &aRect) |
michael@0 | 173 | { |
michael@0 | 174 | MarkChanged(); |
michael@0 | 175 | |
michael@0 | 176 | mDC->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); |
michael@0 | 177 | mDC->Clear(); |
michael@0 | 178 | mDC->PopAxisAlignedClip(); |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | void |
michael@0 | 182 | DrawTargetD2D1::MaskSurface(const Pattern &aSource, |
michael@0 | 183 | SourceSurface *aMask, |
michael@0 | 184 | Point aOffset, |
michael@0 | 185 | const DrawOptions &aOptions) |
michael@0 | 186 | { |
michael@0 | 187 | RefPtr<ID2D1Bitmap> bitmap; |
michael@0 | 188 | |
michael@0 | 189 | RefPtr<ID2D1Image> image = GetImageForSurface(aMask, ExtendMode::CLAMP); |
michael@0 | 190 | |
michael@0 | 191 | PrepareForDrawing(aOptions.mCompositionOp, aSource); |
michael@0 | 192 | |
michael@0 | 193 | // FillOpacityMask only works if the antialias mode is MODE_ALIASED |
michael@0 | 194 | mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); |
michael@0 | 195 | |
michael@0 | 196 | IntSize size = aMask->GetSize(); |
michael@0 | 197 | Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height)); |
michael@0 | 198 | image->QueryInterface((ID2D1Bitmap**)&bitmap); |
michael@0 | 199 | if (!bitmap) { |
michael@0 | 200 | gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces."; |
michael@0 | 201 | return; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height)); |
michael@0 | 205 | RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha); |
michael@0 | 206 | mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); |
michael@0 | 207 | |
michael@0 | 208 | mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); |
michael@0 | 209 | |
michael@0 | 210 | FinalizeDrawing(aOptions.mCompositionOp, aSource); |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | void |
michael@0 | 214 | DrawTargetD2D1::CopySurface(SourceSurface *aSurface, |
michael@0 | 215 | const IntRect &aSourceRect, |
michael@0 | 216 | const IntPoint &aDestination) |
michael@0 | 217 | { |
michael@0 | 218 | MarkChanged(); |
michael@0 | 219 | |
michael@0 | 220 | mDC->SetTransform(D2D1::IdentityMatrix()); |
michael@0 | 221 | mTransformDirty = true; |
michael@0 | 222 | |
michael@0 | 223 | Matrix mat; |
michael@0 | 224 | RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); |
michael@0 | 225 | |
michael@0 | 226 | if (!mat.IsIdentity()) { |
michael@0 | 227 | gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface."; |
michael@0 | 228 | return; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)), |
michael@0 | 232 | D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y), |
michael@0 | 233 | Float(aSourceRect.XMost()), Float(aSourceRect.YMost())), |
michael@0 | 234 | D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | void |
michael@0 | 238 | DrawTargetD2D1::FillRect(const Rect &aRect, |
michael@0 | 239 | const Pattern &aPattern, |
michael@0 | 240 | const DrawOptions &aOptions) |
michael@0 | 241 | { |
michael@0 | 242 | PrepareForDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 243 | |
michael@0 | 244 | RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); |
michael@0 | 245 | mDC->FillRectangle(D2DRect(aRect), brush); |
michael@0 | 246 | |
michael@0 | 247 | FinalizeDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | void |
michael@0 | 251 | DrawTargetD2D1::StrokeRect(const Rect &aRect, |
michael@0 | 252 | const Pattern &aPattern, |
michael@0 | 253 | const StrokeOptions &aStrokeOptions, |
michael@0 | 254 | const DrawOptions &aOptions) |
michael@0 | 255 | { |
michael@0 | 256 | PrepareForDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 257 | |
michael@0 | 258 | RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); |
michael@0 | 259 | RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); |
michael@0 | 260 | |
michael@0 | 261 | mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); |
michael@0 | 262 | |
michael@0 | 263 | FinalizeDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | void |
michael@0 | 267 | DrawTargetD2D1::StrokeLine(const Point &aStart, |
michael@0 | 268 | const Point &aEnd, |
michael@0 | 269 | const Pattern &aPattern, |
michael@0 | 270 | const StrokeOptions &aStrokeOptions, |
michael@0 | 271 | const DrawOptions &aOptions) |
michael@0 | 272 | { |
michael@0 | 273 | PrepareForDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 274 | |
michael@0 | 275 | RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); |
michael@0 | 276 | RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); |
michael@0 | 277 | |
michael@0 | 278 | mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); |
michael@0 | 279 | |
michael@0 | 280 | FinalizeDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | void |
michael@0 | 284 | DrawTargetD2D1::Stroke(const Path *aPath, |
michael@0 | 285 | const Pattern &aPattern, |
michael@0 | 286 | const StrokeOptions &aStrokeOptions, |
michael@0 | 287 | const DrawOptions &aOptions) |
michael@0 | 288 | { |
michael@0 | 289 | if (aPath->GetBackendType() != BackendType::DIRECT2D) { |
michael@0 | 290 | gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; |
michael@0 | 291 | return; |
michael@0 | 292 | } |
michael@0 | 293 | const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath); |
michael@0 | 294 | |
michael@0 | 295 | PrepareForDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 296 | |
michael@0 | 297 | RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); |
michael@0 | 298 | RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); |
michael@0 | 299 | |
michael@0 | 300 | mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); |
michael@0 | 301 | |
michael@0 | 302 | FinalizeDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 303 | } |
michael@0 | 304 | |
michael@0 | 305 | void |
michael@0 | 306 | DrawTargetD2D1::Fill(const Path *aPath, |
michael@0 | 307 | const Pattern &aPattern, |
michael@0 | 308 | const DrawOptions &aOptions) |
michael@0 | 309 | { |
michael@0 | 310 | if (aPath->GetBackendType() != BackendType::DIRECT2D) { |
michael@0 | 311 | gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; |
michael@0 | 312 | return; |
michael@0 | 313 | } |
michael@0 | 314 | const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath); |
michael@0 | 315 | |
michael@0 | 316 | PrepareForDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 317 | |
michael@0 | 318 | RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); |
michael@0 | 319 | |
michael@0 | 320 | mDC->FillGeometry(d2dPath->mGeometry, brush); |
michael@0 | 321 | |
michael@0 | 322 | FinalizeDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 323 | } |
michael@0 | 324 | |
michael@0 | 325 | void |
michael@0 | 326 | DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, |
michael@0 | 327 | const GlyphBuffer &aBuffer, |
michael@0 | 328 | const Pattern &aPattern, |
michael@0 | 329 | const DrawOptions &aOptions, |
michael@0 | 330 | const GlyphRenderingOptions *aRenderingOptions) |
michael@0 | 331 | { |
michael@0 | 332 | if (aFont->GetType() != FontType::DWRITE) { |
michael@0 | 333 | gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; |
michael@0 | 334 | return; |
michael@0 | 335 | } |
michael@0 | 336 | |
michael@0 | 337 | ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont); |
michael@0 | 338 | |
michael@0 | 339 | IDWriteRenderingParams *params = nullptr; |
michael@0 | 340 | if (aRenderingOptions) { |
michael@0 | 341 | if (aRenderingOptions->GetType() != FontType::DWRITE) { |
michael@0 | 342 | gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; |
michael@0 | 343 | // This should never happen. |
michael@0 | 344 | MOZ_ASSERT(false); |
michael@0 | 345 | } else { |
michael@0 | 346 | params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderingOptions)->mParams; |
michael@0 | 347 | } |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | AntialiasMode aaMode = font->GetDefaultAAMode(); |
michael@0 | 351 | |
michael@0 | 352 | if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { |
michael@0 | 353 | aaMode = aOptions.mAntialiasMode; |
michael@0 | 354 | } |
michael@0 | 355 | |
michael@0 | 356 | PrepareForDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 357 | |
michael@0 | 358 | bool forceClearType = false; |
michael@0 | 359 | if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && |
michael@0 | 360 | aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) { |
michael@0 | 361 | forceClearType = true; |
michael@0 | 362 | } |
michael@0 | 363 | |
michael@0 | 364 | |
michael@0 | 365 | D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; |
michael@0 | 366 | |
michael@0 | 367 | switch (aaMode) { |
michael@0 | 368 | case AntialiasMode::NONE: |
michael@0 | 369 | d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; |
michael@0 | 370 | break; |
michael@0 | 371 | case AntialiasMode::GRAY: |
michael@0 | 372 | d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; |
michael@0 | 373 | break; |
michael@0 | 374 | case AntialiasMode::SUBPIXEL: |
michael@0 | 375 | d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; |
michael@0 | 376 | break; |
michael@0 | 377 | default: |
michael@0 | 378 | d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && |
michael@0 | 382 | mFormat != SurfaceFormat::B8G8R8X8 && !forceClearType) { |
michael@0 | 383 | d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | mDC->SetTextAntialiasMode(d2dAAMode); |
michael@0 | 387 | |
michael@0 | 388 | if (params != mTextRenderingParams) { |
michael@0 | 389 | mDC->SetTextRenderingParams(params); |
michael@0 | 390 | mTextRenderingParams = params; |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); |
michael@0 | 394 | |
michael@0 | 395 | AutoDWriteGlyphRun autoRun; |
michael@0 | 396 | DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); |
michael@0 | 397 | |
michael@0 | 398 | if (brush) { |
michael@0 | 399 | mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); |
michael@0 | 400 | } |
michael@0 | 401 | |
michael@0 | 402 | FinalizeDrawing(aOptions.mCompositionOp, aPattern); |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | void |
michael@0 | 406 | DrawTargetD2D1::Mask(const Pattern &aSource, |
michael@0 | 407 | const Pattern &aMask, |
michael@0 | 408 | const DrawOptions &aOptions) |
michael@0 | 409 | { |
michael@0 | 410 | PrepareForDrawing(aOptions.mCompositionOp, aSource); |
michael@0 | 411 | |
michael@0 | 412 | RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha); |
michael@0 | 413 | RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f); |
michael@0 | 414 | mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, |
michael@0 | 415 | D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, |
michael@0 | 416 | D2D1::IdentityMatrix(), |
michael@0 | 417 | 1.0f, mask), |
michael@0 | 418 | nullptr); |
michael@0 | 419 | |
michael@0 | 420 | Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height); |
michael@0 | 421 | Matrix mat = mTransform; |
michael@0 | 422 | mat.Invert(); |
michael@0 | 423 | |
michael@0 | 424 | mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source); |
michael@0 | 425 | |
michael@0 | 426 | mDC->PopLayer(); |
michael@0 | 427 | |
michael@0 | 428 | FinalizeDrawing(aOptions.mCompositionOp, aSource); |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | void |
michael@0 | 432 | DrawTargetD2D1::PushClip(const Path *aPath) |
michael@0 | 433 | { |
michael@0 | 434 | if (aPath->GetBackendType() != BackendType::DIRECT2D) { |
michael@0 | 435 | gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; |
michael@0 | 436 | return; |
michael@0 | 437 | } |
michael@0 | 438 | |
michael@0 | 439 | RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath)); |
michael@0 | 440 | |
michael@0 | 441 | PushedClip clip; |
michael@0 | 442 | clip.mTransform = D2DMatrix(mTransform); |
michael@0 | 443 | clip.mPath = pathD2D; |
michael@0 | 444 | |
michael@0 | 445 | pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); |
michael@0 | 446 | |
michael@0 | 447 | mPushedClips.push_back(clip); |
michael@0 | 448 | |
michael@0 | 449 | // The transform of clips is relative to the world matrix, since we use the total |
michael@0 | 450 | // transform for the clips, make the world matrix identity. |
michael@0 | 451 | mDC->SetTransform(D2D1::IdentityMatrix()); |
michael@0 | 452 | mTransformDirty = true; |
michael@0 | 453 | |
michael@0 | 454 | if (mClipsArePushed) { |
michael@0 | 455 | PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform); |
michael@0 | 456 | } |
michael@0 | 457 | } |
michael@0 | 458 | |
michael@0 | 459 | void |
michael@0 | 460 | DrawTargetD2D1::PushClipRect(const Rect &aRect) |
michael@0 | 461 | { |
michael@0 | 462 | if (!mTransform.IsRectilinear()) { |
michael@0 | 463 | // Whoops, this isn't a rectangle in device space, Direct2D will not deal |
michael@0 | 464 | // with this transform the way we want it to. |
michael@0 | 465 | // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx |
michael@0 | 466 | |
michael@0 | 467 | RefPtr<PathBuilder> pathBuilder = CreatePathBuilder(); |
michael@0 | 468 | pathBuilder->MoveTo(aRect.TopLeft()); |
michael@0 | 469 | pathBuilder->LineTo(aRect.TopRight()); |
michael@0 | 470 | pathBuilder->LineTo(aRect.BottomRight()); |
michael@0 | 471 | pathBuilder->LineTo(aRect.BottomLeft()); |
michael@0 | 472 | pathBuilder->Close(); |
michael@0 | 473 | RefPtr<Path> path = pathBuilder->Finish(); |
michael@0 | 474 | return PushClip(path); |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | PushedClip clip; |
michael@0 | 478 | Rect rect = mTransform.TransformBounds(aRect); |
michael@0 | 479 | IntRect intRect; |
michael@0 | 480 | clip.mIsPixelAligned = rect.ToIntRect(&intRect); |
michael@0 | 481 | |
michael@0 | 482 | // Do not store the transform, just store the device space rectangle directly. |
michael@0 | 483 | clip.mBounds = D2DRect(rect); |
michael@0 | 484 | |
michael@0 | 485 | mPushedClips.push_back(clip); |
michael@0 | 486 | |
michael@0 | 487 | mDC->SetTransform(D2D1::IdentityMatrix()); |
michael@0 | 488 | mTransformDirty = true; |
michael@0 | 489 | |
michael@0 | 490 | if (mClipsArePushed) { |
michael@0 | 491 | mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); |
michael@0 | 492 | } |
michael@0 | 493 | } |
michael@0 | 494 | |
michael@0 | 495 | void |
michael@0 | 496 | DrawTargetD2D1::PopClip() |
michael@0 | 497 | { |
michael@0 | 498 | if (mClipsArePushed) { |
michael@0 | 499 | if (mPushedClips.back().mPath) { |
michael@0 | 500 | mDC->PopLayer(); |
michael@0 | 501 | } else { |
michael@0 | 502 | mDC->PopAxisAlignedClip(); |
michael@0 | 503 | } |
michael@0 | 504 | } |
michael@0 | 505 | mPushedClips.pop_back(); |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | TemporaryRef<SourceSurface> |
michael@0 | 509 | DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData, |
michael@0 | 510 | const IntSize &aSize, |
michael@0 | 511 | int32_t aStride, |
michael@0 | 512 | SurfaceFormat aFormat) const |
michael@0 | 513 | { |
michael@0 | 514 | RefPtr<ID2D1Bitmap1> bitmap; |
michael@0 | 515 | |
michael@0 | 516 | HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride, |
michael@0 | 517 | D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)), |
michael@0 | 518 | byRef(bitmap)); |
michael@0 | 519 | |
michael@0 | 520 | if (!bitmap) { |
michael@0 | 521 | return nullptr; |
michael@0 | 522 | } |
michael@0 | 523 | |
michael@0 | 524 | return new SourceSurfaceD2D1(bitmap.get(), mDC, aFormat, aSize); |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | TemporaryRef<DrawTarget> |
michael@0 | 528 | DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const |
michael@0 | 529 | { |
michael@0 | 530 | RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1(); |
michael@0 | 531 | |
michael@0 | 532 | if (!dt->Init(aSize, aFormat)) { |
michael@0 | 533 | return nullptr; |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | return dt; |
michael@0 | 537 | } |
michael@0 | 538 | |
michael@0 | 539 | TemporaryRef<PathBuilder> |
michael@0 | 540 | DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const |
michael@0 | 541 | { |
michael@0 | 542 | RefPtr<ID2D1PathGeometry> path; |
michael@0 | 543 | HRESULT hr = factory()->CreatePathGeometry(byRef(path)); |
michael@0 | 544 | |
michael@0 | 545 | if (FAILED(hr)) { |
michael@0 | 546 | gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hr; |
michael@0 | 547 | return nullptr; |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | RefPtr<ID2D1GeometrySink> sink; |
michael@0 | 551 | hr = path->Open(byRef(sink)); |
michael@0 | 552 | if (FAILED(hr)) { |
michael@0 | 553 | gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hr; |
michael@0 | 554 | return nullptr; |
michael@0 | 555 | } |
michael@0 | 556 | |
michael@0 | 557 | if (aFillRule == FillRule::FILL_WINDING) { |
michael@0 | 558 | sink->SetFillMode(D2D1_FILL_MODE_WINDING); |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | return new PathBuilderD2D(sink, path, aFillRule); |
michael@0 | 562 | } |
michael@0 | 563 | |
michael@0 | 564 | TemporaryRef<GradientStops> |
michael@0 | 565 | DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const |
michael@0 | 566 | { |
michael@0 | 567 | D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops]; |
michael@0 | 568 | |
michael@0 | 569 | for (uint32_t i = 0; i < aNumStops; i++) { |
michael@0 | 570 | stops[i].position = rawStops[i].offset; |
michael@0 | 571 | stops[i].color = D2DColor(rawStops[i].color); |
michael@0 | 572 | } |
michael@0 | 573 | |
michael@0 | 574 | RefPtr<ID2D1GradientStopCollection> stopCollection; |
michael@0 | 575 | |
michael@0 | 576 | HRESULT hr = |
michael@0 | 577 | mDC->CreateGradientStopCollection(stops, aNumStops, |
michael@0 | 578 | D2D1_GAMMA_2_2, D2DExtend(aExtendMode), |
michael@0 | 579 | byRef(stopCollection)); |
michael@0 | 580 | delete [] stops; |
michael@0 | 581 | |
michael@0 | 582 | if (FAILED(hr)) { |
michael@0 | 583 | gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hr; |
michael@0 | 584 | return nullptr; |
michael@0 | 585 | } |
michael@0 | 586 | |
michael@0 | 587 | return new GradientStopsD2D(stopCollection); |
michael@0 | 588 | } |
michael@0 | 589 | |
michael@0 | 590 | TemporaryRef<FilterNode> |
michael@0 | 591 | DrawTargetD2D1::CreateFilter(FilterType aType) |
michael@0 | 592 | { |
michael@0 | 593 | return FilterNodeD2D1::Create(this, mDC, aType); |
michael@0 | 594 | } |
michael@0 | 595 | |
michael@0 | 596 | bool |
michael@0 | 597 | DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) |
michael@0 | 598 | { |
michael@0 | 599 | HRESULT hr; |
michael@0 | 600 | |
michael@0 | 601 | hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC)); |
michael@0 | 602 | |
michael@0 | 603 | if (FAILED(hr)) { |
michael@0 | 604 | gfxWarning() << *this << ": Error " << hr << " failed to initialize new DeviceContext."; |
michael@0 | 605 | return false; |
michael@0 | 606 | } |
michael@0 | 607 | |
michael@0 | 608 | D2D1_BITMAP_PROPERTIES1 props; |
michael@0 | 609 | props.dpiX = 96; |
michael@0 | 610 | props.dpiY = 96; |
michael@0 | 611 | props.pixelFormat = D2DPixelFormat(aFormat); |
michael@0 | 612 | props.colorContext = nullptr; |
michael@0 | 613 | props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; |
michael@0 | 614 | mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mBitmap)); |
michael@0 | 615 | |
michael@0 | 616 | if (FAILED(hr)) { |
michael@0 | 617 | gfxWarning() << *this << ": Error " << hr << " failed to create new CommandList."; |
michael@0 | 618 | return false; |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap)); |
michael@0 | 622 | |
michael@0 | 623 | mDC->SetTarget(mBitmap); |
michael@0 | 624 | |
michael@0 | 625 | mDC->BeginDraw(); |
michael@0 | 626 | |
michael@0 | 627 | mFormat = aFormat; |
michael@0 | 628 | mSize = aSize; |
michael@0 | 629 | |
michael@0 | 630 | return true; |
michael@0 | 631 | } |
michael@0 | 632 | |
michael@0 | 633 | /** |
michael@0 | 634 | * Private helpers. |
michael@0 | 635 | */ |
michael@0 | 636 | uint32_t |
michael@0 | 637 | DrawTargetD2D1::GetByteSize() const |
michael@0 | 638 | { |
michael@0 | 639 | return mSize.width * mSize.height * BytesPerPixel(mFormat); |
michael@0 | 640 | } |
michael@0 | 641 | |
michael@0 | 642 | ID2D1Factory1* |
michael@0 | 643 | DrawTargetD2D1::factory() |
michael@0 | 644 | { |
michael@0 | 645 | if (mFactory) { |
michael@0 | 646 | return mFactory; |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | HRESULT hr = D2DFactory()->QueryInterface((ID2D1Factory1**)&mFactory); |
michael@0 | 650 | |
michael@0 | 651 | if (FAILED(hr)) { |
michael@0 | 652 | return nullptr; |
michael@0 | 653 | } |
michael@0 | 654 | |
michael@0 | 655 | RadialGradientEffectD2D1::Register(mFactory); |
michael@0 | 656 | |
michael@0 | 657 | return mFactory; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | void |
michael@0 | 661 | DrawTargetD2D1::MarkChanged() |
michael@0 | 662 | { |
michael@0 | 663 | if (mSnapshot) { |
michael@0 | 664 | if (mSnapshot->hasOneRef()) { |
michael@0 | 665 | // Just destroy it, since no-one else knows about it. |
michael@0 | 666 | mSnapshot = nullptr; |
michael@0 | 667 | } else { |
michael@0 | 668 | mSnapshot->DrawTargetWillChange(); |
michael@0 | 669 | // The snapshot will no longer depend on this target. |
michael@0 | 670 | MOZ_ASSERT(!mSnapshot); |
michael@0 | 671 | } |
michael@0 | 672 | } |
michael@0 | 673 | if (mDependentTargets.size()) { |
michael@0 | 674 | // Copy mDependentTargets since the Flush()es below will modify it. |
michael@0 | 675 | TargetSet tmpTargets = mDependentTargets; |
michael@0 | 676 | for (TargetSet::iterator iter = tmpTargets.begin(); |
michael@0 | 677 | iter != tmpTargets.end(); iter++) { |
michael@0 | 678 | (*iter)->Flush(); |
michael@0 | 679 | } |
michael@0 | 680 | // The Flush() should have broken all dependencies on this target. |
michael@0 | 681 | MOZ_ASSERT(!mDependentTargets.size()); |
michael@0 | 682 | } |
michael@0 | 683 | } |
michael@0 | 684 | |
michael@0 | 685 | void |
michael@0 | 686 | DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern) |
michael@0 | 687 | { |
michael@0 | 688 | MarkChanged(); |
michael@0 | 689 | |
michael@0 | 690 | // It's important to do this before FlushTransformToDC! As this will cause |
michael@0 | 691 | // the transform to become dirty. |
michael@0 | 692 | if (!mClipsArePushed) { |
michael@0 | 693 | mClipsArePushed = true; |
michael@0 | 694 | PushClipsToDC(mDC); |
michael@0 | 695 | } |
michael@0 | 696 | |
michael@0 | 697 | FlushTransformToDC(); |
michael@0 | 698 | |
michael@0 | 699 | if (aOp == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { |
michael@0 | 700 | return; |
michael@0 | 701 | } |
michael@0 | 702 | |
michael@0 | 703 | mDC->SetTarget(mTempBitmap); |
michael@0 | 704 | mDC->Clear(D2D1::ColorF(0, 0)); |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | void |
michael@0 | 708 | DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) |
michael@0 | 709 | { |
michael@0 | 710 | bool patternSupported = IsPatternSupportedByD2D(aPattern); |
michael@0 | 711 | |
michael@0 | 712 | if (aOp == CompositionOp::OP_OVER && patternSupported) { |
michael@0 | 713 | return; |
michael@0 | 714 | } |
michael@0 | 715 | |
michael@0 | 716 | RefPtr<ID2D1Image> image; |
michael@0 | 717 | mDC->GetTarget(byRef(image)); |
michael@0 | 718 | |
michael@0 | 719 | mDC->SetTarget(mBitmap); |
michael@0 | 720 | |
michael@0 | 721 | if (patternSupported) { |
michael@0 | 722 | mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); |
michael@0 | 723 | return; |
michael@0 | 724 | } |
michael@0 | 725 | |
michael@0 | 726 | mDC->SetTransform(D2D1::IdentityMatrix()); |
michael@0 | 727 | mTransformDirty = true; |
michael@0 | 728 | |
michael@0 | 729 | RefPtr<ID2D1Effect> radialGradientEffect; |
michael@0 | 730 | |
michael@0 | 731 | mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect)); |
michael@0 | 732 | const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern); |
michael@0 | 733 | |
michael@0 | 734 | radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION, |
michael@0 | 735 | static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection); |
michael@0 | 736 | radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y)); |
michael@0 | 737 | radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y)); |
michael@0 | 738 | radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1); |
michael@0 | 739 | radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); |
michael@0 | 740 | radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); |
michael@0 | 741 | radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform)); |
michael@0 | 742 | radialGradientEffect->SetInput(0, image); |
michael@0 | 743 | |
michael@0 | 744 | mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); |
michael@0 | 745 | } |
michael@0 | 746 | |
michael@0 | 747 | void |
michael@0 | 748 | DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource) |
michael@0 | 749 | { |
michael@0 | 750 | if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) { |
michael@0 | 751 | aSource->mDrawTarget->mDependentTargets.insert(this); |
michael@0 | 752 | mDependingOnTargets.insert(aSource->mDrawTarget); |
michael@0 | 753 | } |
michael@0 | 754 | } |
michael@0 | 755 | |
michael@0 | 756 | void |
michael@0 | 757 | DrawTargetD2D1::PopAllClips() |
michael@0 | 758 | { |
michael@0 | 759 | if (mClipsArePushed) { |
michael@0 | 760 | PopClipsFromDC(mDC); |
michael@0 | 761 | |
michael@0 | 762 | mClipsArePushed = false; |
michael@0 | 763 | } |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | void |
michael@0 | 767 | DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC) |
michael@0 | 768 | { |
michael@0 | 769 | mDC->SetTransform(D2D1::IdentityMatrix()); |
michael@0 | 770 | mTransformDirty = true; |
michael@0 | 771 | |
michael@0 | 772 | for (std::vector<PushedClip>::iterator iter = mPushedClips.begin(); |
michael@0 | 773 | iter != mPushedClips.end(); iter++) { |
michael@0 | 774 | if (iter->mPath) { |
michael@0 | 775 | PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform); |
michael@0 | 776 | } else { |
michael@0 | 777 | mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); |
michael@0 | 778 | } |
michael@0 | 779 | } |
michael@0 | 780 | } |
michael@0 | 781 | |
michael@0 | 782 | void |
michael@0 | 783 | DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC) |
michael@0 | 784 | { |
michael@0 | 785 | for (int i = mPushedClips.size() - 1; i >= 0; i--) { |
michael@0 | 786 | if (mPushedClips[i].mPath) { |
michael@0 | 787 | aDC->PopLayer(); |
michael@0 | 788 | } else { |
michael@0 | 789 | aDC->PopAxisAlignedClip(); |
michael@0 | 790 | } |
michael@0 | 791 | } |
michael@0 | 792 | } |
michael@0 | 793 | |
michael@0 | 794 | TemporaryRef<ID2D1Brush> |
michael@0 | 795 | DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) |
michael@0 | 796 | { |
michael@0 | 797 | if (!IsPatternSupportedByD2D(aPattern)) { |
michael@0 | 798 | RefPtr<ID2D1SolidColorBrush> colBrush; |
michael@0 | 799 | mDC->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush)); |
michael@0 | 800 | return colBrush; |
michael@0 | 801 | } |
michael@0 | 802 | |
michael@0 | 803 | if (aPattern.GetType() == PatternType::COLOR) { |
michael@0 | 804 | RefPtr<ID2D1SolidColorBrush> colBrush; |
michael@0 | 805 | Color color = static_cast<const ColorPattern*>(&aPattern)->mColor; |
michael@0 | 806 | mDC->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, |
michael@0 | 807 | color.b, color.a), |
michael@0 | 808 | D2D1::BrushProperties(aAlpha), |
michael@0 | 809 | byRef(colBrush)); |
michael@0 | 810 | return colBrush; |
michael@0 | 811 | } else if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { |
michael@0 | 812 | RefPtr<ID2D1LinearGradientBrush> gradBrush; |
michael@0 | 813 | const LinearGradientPattern *pat = |
michael@0 | 814 | static_cast<const LinearGradientPattern*>(&aPattern); |
michael@0 | 815 | |
michael@0 | 816 | GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get()); |
michael@0 | 817 | |
michael@0 | 818 | if (!stops) { |
michael@0 | 819 | gfxDebug() << "No stops specified for gradient pattern."; |
michael@0 | 820 | return nullptr; |
michael@0 | 821 | } |
michael@0 | 822 | |
michael@0 | 823 | if (pat->mBegin == pat->mEnd) { |
michael@0 | 824 | RefPtr<ID2D1SolidColorBrush> colBrush; |
michael@0 | 825 | uint32_t stopCount = stops->mStopCollection->GetGradientStopCount(); |
michael@0 | 826 | vector<D2D1_GRADIENT_STOP> d2dStops(stopCount); |
michael@0 | 827 | stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount); |
michael@0 | 828 | mDC->CreateSolidColorBrush(d2dStops.back().color, |
michael@0 | 829 | D2D1::BrushProperties(aAlpha), |
michael@0 | 830 | byRef(colBrush)); |
michael@0 | 831 | return colBrush; |
michael@0 | 832 | } |
michael@0 | 833 | |
michael@0 | 834 | mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), |
michael@0 | 835 | D2DPoint(pat->mEnd)), |
michael@0 | 836 | D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), |
michael@0 | 837 | stops->mStopCollection, |
michael@0 | 838 | byRef(gradBrush)); |
michael@0 | 839 | return gradBrush; |
michael@0 | 840 | } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { |
michael@0 | 841 | RefPtr<ID2D1RadialGradientBrush> gradBrush; |
michael@0 | 842 | const RadialGradientPattern *pat = |
michael@0 | 843 | static_cast<const RadialGradientPattern*>(&aPattern); |
michael@0 | 844 | |
michael@0 | 845 | GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get()); |
michael@0 | 846 | |
michael@0 | 847 | if (!stops) { |
michael@0 | 848 | gfxDebug() << "No stops specified for gradient pattern."; |
michael@0 | 849 | return nullptr; |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | // This will not be a complex radial gradient brush. |
michael@0 | 853 | mDC->CreateRadialGradientBrush( |
michael@0 | 854 | D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2), |
michael@0 | 855 | D2DPoint(pat->mCenter1 - pat->mCenter2), |
michael@0 | 856 | pat->mRadius2, pat->mRadius2), |
michael@0 | 857 | D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), |
michael@0 | 858 | stops->mStopCollection, |
michael@0 | 859 | byRef(gradBrush)); |
michael@0 | 860 | |
michael@0 | 861 | return gradBrush; |
michael@0 | 862 | } else if (aPattern.GetType() == PatternType::SURFACE) { |
michael@0 | 863 | const SurfacePattern *pat = |
michael@0 | 864 | static_cast<const SurfacePattern*>(&aPattern); |
michael@0 | 865 | |
michael@0 | 866 | if (!pat->mSurface) { |
michael@0 | 867 | gfxDebug() << "No source surface specified for surface pattern"; |
michael@0 | 868 | return nullptr; |
michael@0 | 869 | } |
michael@0 | 870 | |
michael@0 | 871 | |
michael@0 | 872 | Matrix mat = pat->mMatrix; |
michael@0 | 873 | |
michael@0 | 874 | RefPtr<ID2D1ImageBrush> imageBrush; |
michael@0 | 875 | RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode); |
michael@0 | 876 | mDC->CreateImageBrush(image, |
michael@0 | 877 | D2D1::ImageBrushProperties(D2D1::RectF(0, 0, |
michael@0 | 878 | Float(pat->mSurface->GetSize().width), |
michael@0 | 879 | Float(pat->mSurface->GetSize().height)), |
michael@0 | 880 | D2DExtend(pat->mExtendMode), D2DExtend(pat->mExtendMode), |
michael@0 | 881 | D2DInterpolationMode(pat->mFilter)), |
michael@0 | 882 | D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), |
michael@0 | 883 | byRef(imageBrush)); |
michael@0 | 884 | return imageBrush; |
michael@0 | 885 | } |
michael@0 | 886 | |
michael@0 | 887 | gfxWarning() << "Invalid pattern type detected."; |
michael@0 | 888 | return nullptr; |
michael@0 | 889 | } |
michael@0 | 890 | |
michael@0 | 891 | TemporaryRef<ID2D1Image> |
michael@0 | 892 | DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, |
michael@0 | 893 | ExtendMode aExtendMode) |
michael@0 | 894 | { |
michael@0 | 895 | RefPtr<ID2D1Image> image; |
michael@0 | 896 | |
michael@0 | 897 | switch (aSurface->GetType()) { |
michael@0 | 898 | case SurfaceType::D2D1_1_IMAGE: |
michael@0 | 899 | { |
michael@0 | 900 | SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface); |
michael@0 | 901 | image = surf->GetImage(); |
michael@0 | 902 | AddDependencyOnSource(surf); |
michael@0 | 903 | } |
michael@0 | 904 | break; |
michael@0 | 905 | default: |
michael@0 | 906 | { |
michael@0 | 907 | RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface(); |
michael@0 | 908 | if (!dataSurf) { |
michael@0 | 909 | gfxWarning() << "Invalid surface type."; |
michael@0 | 910 | return nullptr; |
michael@0 | 911 | } |
michael@0 | 912 | |
michael@0 | 913 | image = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode, |
michael@0 | 914 | aSourceTransform, mDC); |
michael@0 | 915 | |
michael@0 | 916 | return image; |
michael@0 | 917 | } |
michael@0 | 918 | break; |
michael@0 | 919 | } |
michael@0 | 920 | |
michael@0 | 921 | return image; |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | TemporaryRef<SourceSurface> |
michael@0 | 925 | DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const |
michael@0 | 926 | { |
michael@0 | 927 | if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { |
michael@0 | 928 | return aSurface; |
michael@0 | 929 | } |
michael@0 | 930 | |
michael@0 | 931 | RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); |
michael@0 | 932 | |
michael@0 | 933 | DataSourceSurface::MappedSurface map; |
michael@0 | 934 | if (!data->Map(DataSourceSurface::MapType::READ, &map)) { |
michael@0 | 935 | return nullptr; |
michael@0 | 936 | } |
michael@0 | 937 | |
michael@0 | 938 | RefPtr<ID2D1Bitmap1> bitmap; |
michael@0 | 939 | HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.mData, map.mStride, |
michael@0 | 940 | D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())), |
michael@0 | 941 | byRef(bitmap)); |
michael@0 | 942 | |
michael@0 | 943 | data->Unmap(); |
michael@0 | 944 | |
michael@0 | 945 | if (!bitmap) { |
michael@0 | 946 | return data; |
michael@0 | 947 | } |
michael@0 | 948 | |
michael@0 | 949 | return new SourceSurfaceD2D1(bitmap.get(), mDC, data->GetFormat(), data->GetSize()); |
michael@0 | 950 | } |
michael@0 | 951 | |
michael@0 | 952 | void |
michael@0 | 953 | DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) |
michael@0 | 954 | { |
michael@0 | 955 | D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE; |
michael@0 | 956 | |
michael@0 | 957 | if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { |
michael@0 | 958 | options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; |
michael@0 | 959 | } |
michael@0 | 960 | |
michael@0 | 961 | mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, |
michael@0 | 962 | D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, |
michael@0 | 963 | 1.0, nullptr, options), nullptr); |
michael@0 | 964 | } |
michael@0 | 965 | |
michael@0 | 966 | } |
michael@0 | 967 | } |