1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef MOZILLA_GFX_HELPERSD2D_H_
8 #define MOZILLA_GFX_HELPERSD2D_H_
17 #include "ImageScaling.h"
19 #include "ScaledFontDWrite.h"
27 RefPtr
<ID2D1Factory1
> D2DFactory();
29 static inline D2D1_POINT_2F
D2DPoint(const Point
& aPoint
) {
30 return D2D1::Point2F(aPoint
.x
, aPoint
.y
);
33 static inline D2D1_SIZE_U
D2DIntSize(const IntSize
& aSize
) {
34 return D2D1::SizeU(aSize
.width
, aSize
.height
);
38 static inline D2D1_RECT_F
D2DRect(const T
& aRect
) {
39 return D2D1::RectF(aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost());
42 static inline D2D1_ROUNDED_RECT
D2DRoundedRect(const RoundedRect
& aRect
) {
43 return D2D1::RoundedRect(D2DRect(aRect
.rect
),
44 aRect
.corners
.BottomLeft().width
,
45 aRect
.corners
.BottomLeft().height
);
48 static inline D2D1_EXTEND_MODE
D2DExtend(ExtendMode aExtendMode
, Axis aAxis
) {
49 D2D1_EXTEND_MODE extend
;
50 switch (aExtendMode
) {
51 case ExtendMode::REPEAT
:
52 extend
= D2D1_EXTEND_MODE_WRAP
;
54 case ExtendMode::REPEAT_X
: {
55 extend
= aAxis
== Axis::X_AXIS
? D2D1_EXTEND_MODE_WRAP
56 : D2D1_EXTEND_MODE_CLAMP
;
59 case ExtendMode::REPEAT_Y
: {
60 extend
= aAxis
== Axis::Y_AXIS
? D2D1_EXTEND_MODE_WRAP
61 : D2D1_EXTEND_MODE_CLAMP
;
64 case ExtendMode::REFLECT
:
65 extend
= D2D1_EXTEND_MODE_MIRROR
;
68 extend
= D2D1_EXTEND_MODE_CLAMP
;
74 static inline D2D1_BITMAP_INTERPOLATION_MODE
D2DFilter(
75 const SamplingFilter aSamplingFilter
) {
76 switch (aSamplingFilter
) {
77 case SamplingFilter::POINT
:
78 return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
;
80 return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR
;
84 static inline D2D1_INTERPOLATION_MODE
D2DInterpolationMode(
85 const SamplingFilter aSamplingFilter
) {
86 switch (aSamplingFilter
) {
87 case SamplingFilter::POINT
:
88 return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
;
90 return D2D1_INTERPOLATION_MODE_LINEAR
;
94 static inline D2D1_MATRIX_5X4_F
D2DMatrix5x4(const Matrix5x4
& aMatrix
) {
95 return D2D1::Matrix5x4F(aMatrix
._11
, aMatrix
._12
, aMatrix
._13
, aMatrix
._14
,
96 aMatrix
._21
, aMatrix
._22
, aMatrix
._23
, aMatrix
._24
,
97 aMatrix
._31
, aMatrix
._32
, aMatrix
._33
, aMatrix
._34
,
98 aMatrix
._41
, aMatrix
._42
, aMatrix
._43
, aMatrix
._44
,
99 aMatrix
._51
, aMatrix
._52
, aMatrix
._53
, aMatrix
._54
);
102 static inline D2D1_VECTOR_3F
D2DVector3D(const Point3D
& aPoint
) {
103 return D2D1::Vector3F(aPoint
.x
, aPoint
.y
, aPoint
.z
);
106 static inline D2D1_ANTIALIAS_MODE
D2DAAMode(AntialiasMode aMode
) {
108 case AntialiasMode::NONE
:
109 return D2D1_ANTIALIAS_MODE_ALIASED
;
111 return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
;
115 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const Matrix
& aTransform
) {
116 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
, aTransform
._21
,
117 aTransform
._22
, aTransform
._31
, aTransform
._32
);
120 static inline D2D1_COLOR_F
D2DColor(const DeviceColor
& aColor
) {
121 return D2D1::ColorF(aColor
.r
, aColor
.g
, aColor
.b
, aColor
.a
);
124 static inline IntSize
ToIntSize(const D2D1_SIZE_U
& aSize
) {
125 return IntSize(aSize
.width
, aSize
.height
);
128 static inline SurfaceFormat
ToPixelFormat(const DXGI_FORMAT
& aFormat
) {
130 case DXGI_FORMAT_A8_UNORM
:
131 case DXGI_FORMAT_R8_UNORM
:
132 return SurfaceFormat::A8
;
134 return SurfaceFormat::B8G8R8A8
;
138 static inline SurfaceFormat
ToPixelFormat(const D2D1_PIXEL_FORMAT
& aFormat
) {
139 switch (aFormat
.format
) {
140 case DXGI_FORMAT_A8_UNORM
:
141 case DXGI_FORMAT_R8_UNORM
:
142 return SurfaceFormat::A8
;
143 case DXGI_FORMAT_B8G8R8A8_UNORM
:
144 if (aFormat
.alphaMode
== D2D1_ALPHA_MODE_IGNORE
) {
145 return SurfaceFormat::B8G8R8X8
;
147 return SurfaceFormat::B8G8R8A8
;
150 return SurfaceFormat::B8G8R8A8
;
154 static inline Rect
ToRect(const D2D1_RECT_F
& aRect
) {
155 return Rect(aRect
.left
, aRect
.top
, aRect
.right
- aRect
.left
,
156 aRect
.bottom
- aRect
.top
);
159 static inline Matrix
ToMatrix(const D2D1_MATRIX_3X2_F
& aTransform
) {
160 return Matrix(aTransform
._11
, aTransform
._12
, aTransform
._21
, aTransform
._22
,
161 aTransform
._31
, aTransform
._32
);
164 static inline Point
ToPoint(const D2D1_POINT_2F
& aPoint
) {
165 return Point(aPoint
.x
, aPoint
.y
);
168 static inline DXGI_FORMAT
DXGIFormat(SurfaceFormat aFormat
) {
170 case SurfaceFormat::B8G8R8A8
:
171 return DXGI_FORMAT_B8G8R8A8_UNORM
;
172 case SurfaceFormat::B8G8R8X8
:
173 return DXGI_FORMAT_B8G8R8A8_UNORM
;
174 case SurfaceFormat::A8
:
175 return DXGI_FORMAT_A8_UNORM
;
177 return DXGI_FORMAT_UNKNOWN
;
181 static inline D2D1_ALPHA_MODE
D2DAlphaModeForFormat(SurfaceFormat aFormat
) {
183 case SurfaceFormat::B8G8R8X8
:
184 return D2D1_ALPHA_MODE_IGNORE
;
186 return D2D1_ALPHA_MODE_PREMULTIPLIED
;
190 static inline D2D1_PIXEL_FORMAT
D2DPixelFormat(SurfaceFormat aFormat
) {
191 return D2D1::PixelFormat(DXGIFormat(aFormat
), D2DAlphaModeForFormat(aFormat
));
194 static inline bool D2DSupportsCompositeMode(CompositionOp aOp
) {
196 case CompositionOp::OP_OVER
:
197 case CompositionOp::OP_ADD
:
198 case CompositionOp::OP_ATOP
:
199 case CompositionOp::OP_OUT
:
200 case CompositionOp::OP_IN
:
201 case CompositionOp::OP_SOURCE
:
202 case CompositionOp::OP_DEST_IN
:
203 case CompositionOp::OP_DEST_OUT
:
204 case CompositionOp::OP_DEST_OVER
:
205 case CompositionOp::OP_DEST_ATOP
:
206 case CompositionOp::OP_XOR
:
207 case CompositionOp::OP_CLEAR
:
214 static inline D2D1_COMPOSITE_MODE
D2DCompositionMode(CompositionOp aOp
) {
216 case CompositionOp::OP_OVER
:
217 return D2D1_COMPOSITE_MODE_SOURCE_OVER
;
218 case CompositionOp::OP_ADD
:
219 return D2D1_COMPOSITE_MODE_PLUS
;
220 case CompositionOp::OP_ATOP
:
221 return D2D1_COMPOSITE_MODE_SOURCE_ATOP
;
222 case CompositionOp::OP_OUT
:
223 return D2D1_COMPOSITE_MODE_SOURCE_OUT
;
224 case CompositionOp::OP_IN
:
225 return D2D1_COMPOSITE_MODE_SOURCE_IN
;
226 case CompositionOp::OP_SOURCE
:
227 return D2D1_COMPOSITE_MODE_SOURCE_COPY
;
228 case CompositionOp::OP_DEST_IN
:
229 return D2D1_COMPOSITE_MODE_DESTINATION_IN
;
230 case CompositionOp::OP_DEST_OUT
:
231 return D2D1_COMPOSITE_MODE_DESTINATION_OUT
;
232 case CompositionOp::OP_DEST_OVER
:
233 return D2D1_COMPOSITE_MODE_DESTINATION_OVER
;
234 case CompositionOp::OP_DEST_ATOP
:
235 return D2D1_COMPOSITE_MODE_DESTINATION_ATOP
;
236 case CompositionOp::OP_XOR
:
237 return D2D1_COMPOSITE_MODE_XOR
;
238 case CompositionOp::OP_CLEAR
:
239 return D2D1_COMPOSITE_MODE_DESTINATION_OUT
;
241 return D2D1_COMPOSITE_MODE_SOURCE_OVER
;
245 static inline D2D1_BLEND_MODE
D2DBlendMode(CompositionOp aOp
) {
247 case CompositionOp::OP_MULTIPLY
:
248 return D2D1_BLEND_MODE_MULTIPLY
;
249 case CompositionOp::OP_SCREEN
:
250 return D2D1_BLEND_MODE_SCREEN
;
251 case CompositionOp::OP_OVERLAY
:
252 return D2D1_BLEND_MODE_OVERLAY
;
253 case CompositionOp::OP_DARKEN
:
254 return D2D1_BLEND_MODE_DARKEN
;
255 case CompositionOp::OP_LIGHTEN
:
256 return D2D1_BLEND_MODE_LIGHTEN
;
257 case CompositionOp::OP_COLOR_DODGE
:
258 return D2D1_BLEND_MODE_COLOR_DODGE
;
259 case CompositionOp::OP_COLOR_BURN
:
260 return D2D1_BLEND_MODE_COLOR_BURN
;
261 case CompositionOp::OP_HARD_LIGHT
:
262 return D2D1_BLEND_MODE_HARD_LIGHT
;
263 case CompositionOp::OP_SOFT_LIGHT
:
264 return D2D1_BLEND_MODE_SOFT_LIGHT
;
265 case CompositionOp::OP_DIFFERENCE
:
266 return D2D1_BLEND_MODE_DIFFERENCE
;
267 case CompositionOp::OP_EXCLUSION
:
268 return D2D1_BLEND_MODE_EXCLUSION
;
269 case CompositionOp::OP_HUE
:
270 return D2D1_BLEND_MODE_HUE
;
271 case CompositionOp::OP_SATURATION
:
272 return D2D1_BLEND_MODE_SATURATION
;
273 case CompositionOp::OP_COLOR
:
274 return D2D1_BLEND_MODE_COLOR
;
275 case CompositionOp::OP_LUMINOSITY
:
276 return D2D1_BLEND_MODE_LUMINOSITY
;
278 return D2D1_BLEND_MODE_MULTIPLY
;
282 static inline bool D2DSupportsPrimitiveBlendMode(CompositionOp aOp
) {
284 case CompositionOp::OP_OVER
:
285 // case CompositionOp::OP_SOURCE:
286 // case CompositionOp::OP_DARKEN:
287 case CompositionOp::OP_ADD
:
294 static inline D2D1_PRIMITIVE_BLEND
D2DPrimitiveBlendMode(CompositionOp aOp
) {
296 case CompositionOp::OP_OVER
:
297 return D2D1_PRIMITIVE_BLEND_SOURCE_OVER
;
298 // D2D1_PRIMITIVE_BLEND_COPY should leave pixels out of the source's
299 // bounds unchanged, but doesn't- breaking unbounded ops.
300 // D2D1_PRIMITIVE_BLEND_MIN doesn't quite work like darken either, as it
301 // accounts for the source alpha.
303 // case CompositionOp::OP_SOURCE:
304 // return D2D1_PRIMITIVE_BLEND_COPY;
305 // case CompositionOp::OP_DARKEN:
306 // return D2D1_PRIMITIVE_BLEND_MIN;
307 case CompositionOp::OP_ADD
:
308 return D2D1_PRIMITIVE_BLEND_ADD
;
310 return D2D1_PRIMITIVE_BLEND_SOURCE_OVER
;
314 static inline bool IsPatternSupportedByD2D(
315 const Pattern
& aPattern
, CompositionOp aOp
= CompositionOp::OP_OVER
) {
316 if (aOp
== CompositionOp::OP_CLEAR
) {
320 if (aPattern
.GetType() == PatternType::CONIC_GRADIENT
) {
324 if (aPattern
.GetType() != PatternType::RADIAL_GRADIENT
) {
328 const RadialGradientPattern
* pat
=
329 static_cast<const RadialGradientPattern
*>(&aPattern
);
331 if (pat
->mRadius1
!= 0) {
335 Point diff
= pat
->mCenter2
- pat
->mCenter1
;
337 if (sqrt(diff
.x
.value
* diff
.x
.value
+ diff
.y
.value
* diff
.y
.value
) >=
339 // Inner point lies outside the circle.
347 * This structure is used to pass rectangles to our shader constant. We can use
348 * this for passing rectangular areas to SetVertexShaderConstant. In the format
349 * of a 4 component float(x,y,width,height). Our vertex shader can then use
350 * this to construct rectangular positions from the 0,0-1,1 quad that we source
353 struct ShaderConstantRectD3D10
{
354 float mX
, mY
, mWidth
, mHeight
;
355 ShaderConstantRectD3D10(float aX
, float aY
, float aWidth
, float aHeight
)
356 : mX(aX
), mY(aY
), mWidth(aWidth
), mHeight(aHeight
) {}
358 // For easy passing to SetVertexShaderConstantF.
359 operator float*() { return &mX
; }
362 static inline DWRITE_MATRIX
DWriteMatrixFromMatrix(Matrix
& aMatrix
) {
364 mat
.m11
= aMatrix
._11
;
365 mat
.m12
= aMatrix
._12
;
366 mat
.m21
= aMatrix
._21
;
367 mat
.m22
= aMatrix
._22
;
368 mat
.dx
= aMatrix
._31
;
369 mat
.dy
= aMatrix
._32
;
373 class AutoDWriteGlyphRun
: public DWRITE_GLYPH_RUN
{
374 static const unsigned kNumAutoGlyphs
= 256;
377 AutoDWriteGlyphRun() { glyphCount
= 0; }
379 ~AutoDWriteGlyphRun() {
380 if (glyphCount
> kNumAutoGlyphs
) {
381 delete[] glyphIndices
;
382 delete[] glyphAdvances
;
383 delete[] glyphOffsets
;
387 void allocate(unsigned aNumGlyphs
) {
388 glyphCount
= aNumGlyphs
;
389 if (aNumGlyphs
<= kNumAutoGlyphs
) {
390 glyphIndices
= &mAutoIndices
[0];
391 glyphAdvances
= &mAutoAdvances
[0];
392 glyphOffsets
= &mAutoOffsets
[0];
394 glyphIndices
= new UINT16
[aNumGlyphs
];
395 glyphAdvances
= new FLOAT
[aNumGlyphs
];
396 glyphOffsets
= new DWRITE_GLYPH_OFFSET
[aNumGlyphs
];
401 DWRITE_GLYPH_OFFSET mAutoOffsets
[kNumAutoGlyphs
];
402 FLOAT mAutoAdvances
[kNumAutoGlyphs
];
403 UINT16 mAutoIndices
[kNumAutoGlyphs
];
406 static inline void DWriteGlyphRunFromGlyphs(const GlyphBuffer
& aGlyphs
,
407 ScaledFontDWrite
* aFont
,
408 AutoDWriteGlyphRun
* run
) {
409 run
->allocate(aGlyphs
.mNumGlyphs
);
411 FLOAT
* advances
= const_cast<FLOAT
*>(run
->glyphAdvances
);
412 UINT16
* indices
= const_cast<UINT16
*>(run
->glyphIndices
);
413 DWRITE_GLYPH_OFFSET
* offsets
=
414 const_cast<DWRITE_GLYPH_OFFSET
*>(run
->glyphOffsets
);
416 memset(advances
, 0, sizeof(FLOAT
) * aGlyphs
.mNumGlyphs
);
417 for (unsigned int i
= 0; i
< aGlyphs
.mNumGlyphs
; i
++) {
418 indices
[i
] = aGlyphs
.mGlyphs
[i
].mIndex
;
419 offsets
[i
].advanceOffset
= aGlyphs
.mGlyphs
[i
].mPosition
.x
;
420 offsets
[i
].ascenderOffset
= -aGlyphs
.mGlyphs
[i
].mPosition
.y
;
424 run
->fontFace
= aFont
->mFontFace
;
425 run
->fontEmSize
= aFont
->GetSize();
426 run
->glyphCount
= aGlyphs
.mNumGlyphs
;
427 run
->isSideways
= FALSE
;
430 static inline already_AddRefed
<ID2D1Geometry
> ConvertRectToGeometry(
431 const D2D1_RECT_F
& aRect
) {
432 RefPtr
<ID2D1RectangleGeometry
> rectGeom
;
433 D2DFactory()->CreateRectangleGeometry(&aRect
, getter_AddRefs(rectGeom
));
434 return rectGeom
.forget();
437 static inline already_AddRefed
<ID2D1Geometry
> GetTransformedGeometry(
438 ID2D1Geometry
* aGeometry
, const D2D1_MATRIX_3X2_F
& aTransform
) {
439 RefPtr
<ID2D1PathGeometry
> tmpGeometry
;
440 D2DFactory()->CreatePathGeometry(getter_AddRefs(tmpGeometry
));
441 RefPtr
<ID2D1GeometrySink
> currentSink
;
442 tmpGeometry
->Open(getter_AddRefs(currentSink
));
443 aGeometry
->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES
,
444 aTransform
, currentSink
);
445 currentSink
->Close();
446 return tmpGeometry
.forget();
449 static inline already_AddRefed
<ID2D1Geometry
> IntersectGeometry(
450 ID2D1Geometry
* aGeometryA
, ID2D1Geometry
* aGeometryB
) {
451 RefPtr
<ID2D1PathGeometry
> pathGeom
;
452 D2DFactory()->CreatePathGeometry(getter_AddRefs(pathGeom
));
453 RefPtr
<ID2D1GeometrySink
> sink
;
454 pathGeom
->Open(getter_AddRefs(sink
));
455 aGeometryA
->CombineWithGeometry(aGeometryB
, D2D1_COMBINE_MODE_INTERSECT
,
459 return pathGeom
.forget();
462 static inline already_AddRefed
<ID2D1StrokeStyle
> CreateStrokeStyleForOptions(
463 const StrokeOptions
& aStrokeOptions
) {
464 RefPtr
<ID2D1StrokeStyle
> style
;
466 D2D1_CAP_STYLE capStyle
;
467 D2D1_LINE_JOIN joinStyle
;
469 switch (aStrokeOptions
.mLineCap
) {
471 capStyle
= D2D1_CAP_STYLE_FLAT
;
473 case CapStyle::ROUND
:
474 capStyle
= D2D1_CAP_STYLE_ROUND
;
476 case CapStyle::SQUARE
:
477 capStyle
= D2D1_CAP_STYLE_SQUARE
;
481 switch (aStrokeOptions
.mLineJoin
) {
482 case JoinStyle::MITER
:
483 joinStyle
= D2D1_LINE_JOIN_MITER
;
485 case JoinStyle::MITER_OR_BEVEL
:
486 joinStyle
= D2D1_LINE_JOIN_MITER_OR_BEVEL
;
488 case JoinStyle::ROUND
:
489 joinStyle
= D2D1_LINE_JOIN_ROUND
;
491 case JoinStyle::BEVEL
:
492 joinStyle
= D2D1_LINE_JOIN_BEVEL
;
497 // We need to check mDashLength in addition to mDashPattern here since if
498 // mDashPattern is set but mDashLength is zero then the stroke will fail to
500 if (aStrokeOptions
.mDashLength
> 0 && aStrokeOptions
.mDashPattern
) {
501 typedef std::vector
<Float
> FloatVector
;
502 // D2D "helpfully" multiplies the dash pattern by the line width.
503 // That's not what cairo does, or is what <canvas>'s dash wants.
504 // So fix the multiplication in advance.
505 Float lineWidth
= aStrokeOptions
.mLineWidth
;
506 FloatVector
dash(aStrokeOptions
.mDashPattern
,
507 aStrokeOptions
.mDashPattern
+ aStrokeOptions
.mDashLength
);
508 for (FloatVector::iterator it
= dash
.begin(); it
!= dash
.end(); ++it
) {
512 hr
= D2DFactory()->CreateStrokeStyle(
513 D2D1::StrokeStyleProperties(
514 capStyle
, capStyle
, capStyle
, joinStyle
, aStrokeOptions
.mMiterLimit
,
515 D2D1_DASH_STYLE_CUSTOM
, aStrokeOptions
.mDashOffset
/ lineWidth
),
516 &dash
[0], // data() is not C++98, although it's in recent gcc
518 dash
.size(), getter_AddRefs(style
));
520 hr
= D2DFactory()->CreateStrokeStyle(
521 D2D1::StrokeStyleProperties(capStyle
, capStyle
, capStyle
, joinStyle
,
522 aStrokeOptions
.mMiterLimit
),
523 nullptr, 0, getter_AddRefs(style
));
527 gfxWarning() << "Failed to create Direct2D stroke style.";
530 return style
.forget();
533 // This creates a (partially) uploaded bitmap for a DataSourceSurface. It
534 // uploads the minimum requirement and possibly downscales. It adjusts the
535 // input Matrix to compensate.
536 static inline already_AddRefed
<ID2D1Bitmap
> CreatePartialBitmapForSurface(
537 DataSourceSurface
* aSurface
, const Matrix
& aDestinationTransform
,
538 const IntSize
& aDestinationSize
, ExtendMode aExtendMode
,
539 Matrix
& aSourceTransform
, ID2D1RenderTarget
* aRT
,
540 const IntRect
* aSourceRect
= nullptr) {
541 RefPtr
<ID2D1Bitmap
> bitmap
;
543 // This is where things get complicated. The source surface was
544 // created for a surface that was too large to fit in a texture.
545 // We'll need to figure out if we can work with a partial upload
546 // or downsample in software.
548 Matrix transform
= aDestinationTransform
;
549 Matrix invTransform
= transform
= aSourceTransform
* transform
;
550 if (!invTransform
.Invert()) {
551 // Singular transform, nothing to be drawn.
555 Rect
rect(0, 0, Float(aDestinationSize
.width
),
556 Float(aDestinationSize
.height
));
558 // Calculate the rectangle of the source mapped to our surface.
559 rect
= invTransform
.TransformBounds(rect
);
562 IntSize size
= aSurface
->GetSize();
564 Rect
uploadRect(0, 0, Float(size
.width
), Float(size
.height
));
566 uploadRect
= Rect(aSourceRect
->X(), aSourceRect
->Y(), aSourceRect
->Width(),
567 aSourceRect
->Height());
570 // Limit the uploadRect as much as possible without supporting discontiguous
574 // region we will paint from
576 // .---------------. .---------------. resulting uploadRect
578 // | .---------. .----. .----. .---------------.
579 // | | | ----> | | | | ----> | |
580 // | '---------' '----' '----' '---------------'
581 // '---------------' '---------------'
586 int Bpp
= BytesPerPixel(aSurface
->GetFormat());
588 if (uploadRect
.Contains(rect
)) {
589 // Extend mode is irrelevant, the displayed rect is completely contained
590 // by the source bitmap.
592 } else if (aExtendMode
== ExtendMode::CLAMP
&& uploadRect
.Intersects(rect
)) {
593 // Calculate the rectangle on the source bitmap that touches our
594 // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
595 // correct behaviour in this case.
596 uploadRect
= uploadRect
.Intersect(rect
);
598 // We now proceed to check if we can limit at least one dimension of the
599 // upload rect safely without looking at extend mode.
600 } else if (rect
.X() >= 0 && rect
.XMost() < size
.width
) {
601 uploadRect
.MoveToX(rect
.X());
602 uploadRect
.SetWidth(rect
.Width());
603 } else if (rect
.Y() >= 0 && rect
.YMost() < size
.height
) {
604 uploadRect
.MoveToY(rect
.Y());
605 uploadRect
.SetHeight(rect
.Height());
608 if (uploadRect
.IsEmpty()) {
609 // Nothing to be drawn.
613 if (uploadRect
.Width() <= aRT
->GetMaximumBitmapSize() &&
614 uploadRect
.Height() <= aRT
->GetMaximumBitmapSize()) {
616 // Scope to auto-Unmap() |mapping|.
617 DataSourceSurface::ScopedMap
mapping(aSurface
, DataSourceSurface::READ
);
618 if (MOZ2D_WARN_IF(!mapping
.IsMapped())) {
622 // A partial upload will suffice.
624 D2D1::SizeU(uint32_t(uploadRect
.Width()),
625 uint32_t(uploadRect
.Height())),
626 mapping
.GetData() + int(uploadRect
.X()) * Bpp
+
627 int(uploadRect
.Y()) * mapping
.GetStride(),
629 D2D1::BitmapProperties(D2DPixelFormat(aSurface
->GetFormat())),
630 getter_AddRefs(bitmap
));
633 aSourceTransform
.PreTranslate(uploadRect
.X(), uploadRect
.Y());
635 return bitmap
.forget();
638 // This shouldn't actually happen in practice!
644 // Scope to auto-Unmap() |mapping|.
645 DataSourceSurface::ScopedMap
mapping(aSurface
, DataSourceSurface::READ
);
646 if (MOZ2D_WARN_IF(!mapping
.IsMapped())) {
649 ImageHalfScaler
scaler(mapping
.GetData(), mapping
.GetStride(), size
);
651 // Calculate the maximum width/height of the image post transform.
652 Point topRight
= transform
.TransformPoint(Point(Float(size
.width
), 0));
653 Point topLeft
= transform
.TransformPoint(Point(0, 0));
654 Point bottomRight
= transform
.TransformPoint(
655 Point(Float(size
.width
), Float(size
.height
)));
656 Point bottomLeft
= transform
.TransformPoint(Point(0, Float(size
.height
)));
660 scaleSize
.width
= int32_t(std::max(Distance(topRight
, topLeft
),
661 Distance(bottomRight
, bottomLeft
)));
662 scaleSize
.height
= int32_t(std::max(Distance(topRight
, bottomRight
),
663 Distance(topLeft
, bottomLeft
)));
665 if (unsigned(scaleSize
.width
) > aRT
->GetMaximumBitmapSize()) {
666 // Ok, in this case we'd really want a downscale of a part of the
667 // bitmap, perhaps we can do this later but for simplicity let's do
668 // something different here and assume it's good enough, this should be
670 scaleSize
.width
= 4095;
672 if (unsigned(scaleSize
.height
) > aRT
->GetMaximumBitmapSize()) {
673 scaleSize
.height
= 4095;
676 scaler
.ScaleForSize(scaleSize
);
678 IntSize newSize
= scaler
.GetSize();
680 if (newSize
.IsEmpty()) {
685 D2D1::SizeU(newSize
.width
, newSize
.height
), scaler
.GetScaledData(),
687 D2D1::BitmapProperties(D2DPixelFormat(aSurface
->GetFormat())),
688 getter_AddRefs(bitmap
));
690 aSourceTransform
.PreScale(Float(size
.width
) / newSize
.width
,
691 Float(size
.height
) / newSize
.height
);
693 return bitmap
.forget();
697 static inline void AddRectToSink(ID2D1GeometrySink
* aSink
,
698 const D2D1_RECT_F
& aRect
) {
699 aSink
->BeginFigure(D2D1::Point2F(aRect
.left
, aRect
.top
),
700 D2D1_FIGURE_BEGIN_FILLED
);
701 aSink
->AddLine(D2D1::Point2F(aRect
.right
, aRect
.top
));
702 aSink
->AddLine(D2D1::Point2F(aRect
.right
, aRect
.bottom
));
703 aSink
->AddLine(D2D1::Point2F(aRect
.left
, aRect
.bottom
));
704 aSink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
707 class DCCommandSink
: public ID2D1CommandSink
{
709 explicit DCCommandSink(ID2D1DeviceContext
* aCtx
) : mCtx(aCtx
) {}
711 HRESULT STDMETHODCALLTYPE
QueryInterface(const IID
& aIID
, void** aPtr
) {
716 if (aIID
== IID_IUnknown
) {
717 *aPtr
= static_cast<IUnknown
*>(this);
719 } else if (aIID
== IID_ID2D1CommandSink
) {
720 *aPtr
= static_cast<ID2D1CommandSink
*>(this);
724 return E_NOINTERFACE
;
727 ULONG STDMETHODCALLTYPE
AddRef() { return 1; }
729 ULONG STDMETHODCALLTYPE
Release() { return 1; }
731 STDMETHODIMP
BeginDraw() {
732 // We don't want to do anything here!
735 STDMETHODIMP
EndDraw() {
736 // We don't want to do anything here!
740 STDMETHODIMP
SetAntialiasMode(D2D1_ANTIALIAS_MODE antialiasMode
) {
741 mCtx
->SetAntialiasMode(antialiasMode
);
745 STDMETHODIMP
SetTags(D2D1_TAG tag1
, D2D1_TAG tag2
) {
746 mCtx
->SetTags(tag1
, tag2
);
750 STDMETHODIMP
SetTextAntialiasMode(
751 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode
) {
752 mCtx
->SetTextAntialiasMode(textAntialiasMode
);
756 STDMETHODIMP
SetTextRenderingParams(
757 _In_opt_ IDWriteRenderingParams
* textRenderingParams
) {
758 mCtx
->SetTextRenderingParams(textRenderingParams
);
762 STDMETHODIMP
SetTransform(_In_ CONST D2D1_MATRIX_3X2_F
* transform
) {
763 mCtx
->SetTransform(transform
);
767 STDMETHODIMP
SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend
) {
768 mCtx
->SetPrimitiveBlend(primitiveBlend
);
772 STDMETHODIMP
SetUnitMode(D2D1_UNIT_MODE unitMode
) {
773 mCtx
->SetUnitMode(unitMode
);
777 STDMETHODIMP
Clear(_In_opt_ CONST D2D1_COLOR_F
* color
) {
782 STDMETHODIMP
DrawGlyphRun(
783 D2D1_POINT_2F baselineOrigin
, _In_ CONST DWRITE_GLYPH_RUN
* glyphRun
,
784 _In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION
* glyphRunDescription
,
785 _In_ ID2D1Brush
* foregroundBrush
, DWRITE_MEASURING_MODE measuringMode
) {
786 mCtx
->DrawGlyphRun(baselineOrigin
, glyphRun
, glyphRunDescription
,
787 foregroundBrush
, measuringMode
);
791 STDMETHODIMP
DrawLine(D2D1_POINT_2F point0
, D2D1_POINT_2F point1
,
792 _In_ ID2D1Brush
* brush
, FLOAT strokeWidth
,
793 _In_opt_ ID2D1StrokeStyle
* strokeStyle
) {
794 mCtx
->DrawLine(point0
, point1
, brush
, strokeWidth
, strokeStyle
);
798 STDMETHODIMP
DrawGeometry(_In_ ID2D1Geometry
* geometry
,
799 _In_ ID2D1Brush
* brush
, FLOAT strokeWidth
,
800 _In_opt_ ID2D1StrokeStyle
* strokeStyle
) {
801 mCtx
->DrawGeometry(geometry
, brush
, strokeWidth
, strokeStyle
);
805 STDMETHODIMP
DrawRectangle(_In_ CONST D2D1_RECT_F
* rect
,
806 _In_ ID2D1Brush
* brush
, FLOAT strokeWidth
,
807 _In_opt_ ID2D1StrokeStyle
* strokeStyle
) {
808 mCtx
->DrawRectangle(rect
, brush
, strokeWidth
, strokeStyle
);
812 STDMETHODIMP
DrawBitmap(
813 _In_ ID2D1Bitmap
* bitmap
,
814 _In_opt_ CONST D2D1_RECT_F
* destinationRectangle
, FLOAT opacity
,
815 D2D1_INTERPOLATION_MODE interpolationMode
,
816 _In_opt_ CONST D2D1_RECT_F
* sourceRectangle
,
817 _In_opt_ CONST D2D1_MATRIX_4X4_F
* perspectiveTransform
) {
818 mCtx
->DrawBitmap(bitmap
, destinationRectangle
, opacity
, interpolationMode
,
819 sourceRectangle
, perspectiveTransform
);
823 STDMETHODIMP
DrawImage(_In_ ID2D1Image
* image
,
824 _In_opt_ CONST D2D1_POINT_2F
* targetOffset
,
825 _In_opt_ CONST D2D1_RECT_F
* imageRectangle
,
826 D2D1_INTERPOLATION_MODE interpolationMode
,
827 D2D1_COMPOSITE_MODE compositeMode
) {
828 mCtx
->DrawImage(image
, targetOffset
, imageRectangle
, interpolationMode
,
833 STDMETHODIMP
DrawGdiMetafile(_In_ ID2D1GdiMetafile
* gdiMetafile
,
834 _In_opt_ CONST D2D1_POINT_2F
* targetOffset
) {
835 mCtx
->DrawGdiMetafile(gdiMetafile
, targetOffset
);
839 STDMETHODIMP
FillMesh(_In_ ID2D1Mesh
* mesh
, _In_ ID2D1Brush
* brush
) {
840 mCtx
->FillMesh(mesh
, brush
);
844 STDMETHODIMP
FillOpacityMask(_In_ ID2D1Bitmap
* opacityMask
,
845 _In_ ID2D1Brush
* brush
,
846 _In_opt_ CONST D2D1_RECT_F
* destinationRectangle
,
847 _In_opt_ CONST D2D1_RECT_F
* sourceRectangle
) {
848 mCtx
->FillOpacityMask(opacityMask
, brush
, destinationRectangle
,
853 STDMETHODIMP
FillGeometry(_In_ ID2D1Geometry
* geometry
,
854 _In_ ID2D1Brush
* brush
,
855 _In_opt_ ID2D1Brush
* opacityBrush
) {
856 mCtx
->FillGeometry(geometry
, brush
, opacityBrush
);
860 STDMETHODIMP
FillRectangle(_In_ CONST D2D1_RECT_F
* rect
,
861 _In_ ID2D1Brush
* brush
) {
862 mCtx
->FillRectangle(rect
, brush
);
866 STDMETHODIMP
PushAxisAlignedClip(_In_ CONST D2D1_RECT_F
* clipRect
,
867 D2D1_ANTIALIAS_MODE antialiasMode
) {
868 mCtx
->PushAxisAlignedClip(clipRect
, antialiasMode
);
872 STDMETHODIMP
PushLayer(_In_ CONST D2D1_LAYER_PARAMETERS1
* layerParameters1
,
873 _In_opt_ ID2D1Layer
* layer
) {
874 mCtx
->PushLayer(layerParameters1
, layer
);
878 STDMETHODIMP
PopAxisAlignedClip() {
879 mCtx
->PopAxisAlignedClip();
883 STDMETHODIMP
PopLayer() {
888 ID2D1DeviceContext
* mCtx
;
891 class MOZ_STACK_CLASS AutoRestoreFP final
{
894 // save the current floating point control word
895 _controlfp_s(&savedFPSetting
, 0, 0);
897 // set the floating point control word to its default value
898 _controlfp_s(&unused
, _CW_DEFAULT
, MCW_PC
);
902 // restore the saved floating point control word
903 _controlfp_s(&unused
, savedFPSetting
, MCW_PC
);
910 // Note that overrides of ID2D1SimplifiedGeometrySink methods in this class may
911 // get called from D2D with nonstandard floating point settings (see comments in
912 // bug 1134549) - use AutoRestoreFP to reset the floating point control word to
914 class StreamingGeometrySink
: public ID2D1SimplifiedGeometrySink
{
916 explicit StreamingGeometrySink(PathSink
* aSink
) : mSink(aSink
) {}
918 HRESULT STDMETHODCALLTYPE
QueryInterface(const IID
& aIID
, void** aPtr
) {
923 if (aIID
== IID_IUnknown
) {
924 *aPtr
= static_cast<IUnknown
*>(this);
926 } else if (aIID
== IID_ID2D1SimplifiedGeometrySink
) {
927 *aPtr
= static_cast<ID2D1SimplifiedGeometrySink
*>(this);
931 return E_NOINTERFACE
;
934 ULONG STDMETHODCALLTYPE
AddRef() { return 1; }
936 ULONG STDMETHODCALLTYPE
Release() { return 1; }
938 // We ignore SetFillMode, this depends on the destination sink.
939 STDMETHOD_(void, SetFillMode
)(D2D1_FILL_MODE aMode
) { return; }
940 STDMETHOD_(void, BeginFigure
)
941 (D2D1_POINT_2F aPoint
, D2D1_FIGURE_BEGIN aBegin
) {
942 AutoRestoreFP resetFloatingPoint
;
943 mSink
->MoveTo(ToPoint(aPoint
));
945 STDMETHOD_(void, AddLines
)(const D2D1_POINT_2F
* aLines
, UINT aCount
) {
946 AutoRestoreFP resetFloatingPoint
;
947 for (UINT i
= 0; i
< aCount
; i
++) {
948 mSink
->LineTo(ToPoint(aLines
[i
]));
951 STDMETHOD_(void, AddBeziers
)
952 (const D2D1_BEZIER_SEGMENT
* aSegments
, UINT aCount
) {
953 AutoRestoreFP resetFloatingPoint
;
954 for (UINT i
= 0; i
< aCount
; i
++) {
955 mSink
->BezierTo(ToPoint(aSegments
[i
].point1
),
956 ToPoint(aSegments
[i
].point2
),
957 ToPoint(aSegments
[i
].point3
));
960 STDMETHOD(Close
)() { /* Should never be called! */
963 STDMETHOD_(void, SetSegmentFlags
)
964 (D2D1_PATH_SEGMENT aFlags
) { /* Should never be called! */
967 STDMETHOD_(void, EndFigure
)(D2D1_FIGURE_END aEnd
) {
968 AutoRestoreFP resetFloatingPoint
;
969 if (aEnd
== D2D1_FIGURE_END_CLOSED
) {
970 return mSink
->Close();
979 } // namespace mozilla
981 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */