gfx/2d/DrawTargetD2D1.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial