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 #ifndef MOZILLA_GFX_HELPERSD2D_H_
7 #define MOZILLA_GFX_HELPERSD2D_H_
10 #include "moz-d2d1-1.h"
21 #include "ImageScaling.h"
23 #include "ScaledFontDWrite.h"
31 ID2D1Factory
* D2DFactory();
34 ID2D1Factory1
* D2DFactory1();
37 static inline D2D1_POINT_2F
D2DPoint(const Point
&aPoint
)
39 return D2D1::Point2F(aPoint
.x
, aPoint
.y
);
42 static inline D2D1_SIZE_U
D2DIntSize(const IntSize
&aSize
)
44 return D2D1::SizeU(aSize
.width
, aSize
.height
);
47 static inline D2D1_RECT_F
D2DRect(const Rect
&aRect
)
49 return D2D1::RectF(aRect
.x
, aRect
.y
, aRect
.XMost(), aRect
.YMost());
52 static inline D2D1_EXTEND_MODE
D2DExtend(ExtendMode aExtendMode
)
54 D2D1_EXTEND_MODE extend
;
55 switch (aExtendMode
) {
56 case ExtendMode::REPEAT
:
57 extend
= D2D1_EXTEND_MODE_WRAP
;
59 case ExtendMode::REFLECT
:
60 extend
= D2D1_EXTEND_MODE_MIRROR
;
63 extend
= D2D1_EXTEND_MODE_CLAMP
;
69 static inline D2D1_BITMAP_INTERPOLATION_MODE
D2DFilter(const Filter
&aFilter
)
73 return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
;
75 return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR
;
80 static inline D2D1_INTERPOLATION_MODE
D2DInterpolationMode(const Filter
&aFilter
)
84 return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
;
86 return D2D1_INTERPOLATION_MODE_LINEAR
;
90 static inline D2D1_MATRIX_5X4_F
D2DMatrix5x4(const Matrix5x4
&aMatrix
)
92 return D2D1::Matrix5x4F(aMatrix
._11
, aMatrix
._12
, aMatrix
._13
, aMatrix
._14
,
93 aMatrix
._21
, aMatrix
._22
, aMatrix
._23
, aMatrix
._24
,
94 aMatrix
._31
, aMatrix
._32
, aMatrix
._33
, aMatrix
._34
,
95 aMatrix
._41
, aMatrix
._42
, aMatrix
._43
, aMatrix
._44
,
96 aMatrix
._51
, aMatrix
._52
, aMatrix
._53
, aMatrix
._54
);
99 static inline D2D1_VECTOR_3F
D2DVector3D(const Point3D
&aPoint
)
101 return D2D1::Vector3F(aPoint
.x
, aPoint
.y
, aPoint
.z
);
106 static inline D2D1_ANTIALIAS_MODE
D2DAAMode(AntialiasMode aMode
)
109 case AntialiasMode::NONE
:
110 return D2D1_ANTIALIAS_MODE_ALIASED
;
112 return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
;
116 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const Matrix
&aTransform
)
118 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
,
119 aTransform
._21
, aTransform
._22
,
120 aTransform
._31
, aTransform
._32
);
123 static inline D2D1_COLOR_F
D2DColor(const Color
&aColor
)
125 return D2D1::ColorF(aColor
.r
, aColor
.g
, aColor
.b
, aColor
.a
);
128 static inline IntSize
ToIntSize(const D2D1_SIZE_U
&aSize
)
130 return IntSize(aSize
.width
, aSize
.height
);
133 static inline SurfaceFormat
ToPixelFormat(const D2D1_PIXEL_FORMAT
&aFormat
)
135 switch(aFormat
.format
) {
136 case DXGI_FORMAT_A8_UNORM
:
137 return SurfaceFormat::A8
;
138 case DXGI_FORMAT_B8G8R8A8_UNORM
:
139 if (aFormat
.alphaMode
== D2D1_ALPHA_MODE_IGNORE
) {
140 return SurfaceFormat::B8G8R8X8
;
142 return SurfaceFormat::B8G8R8A8
;
145 return SurfaceFormat::B8G8R8A8
;
149 static inline Rect
ToRect(const D2D1_RECT_F
&aRect
)
151 return Rect(aRect
.left
, aRect
.top
, aRect
.right
- aRect
.left
, aRect
.bottom
- aRect
.top
);
154 static inline Matrix
ToMatrix(const D2D1_MATRIX_3X2_F
&aTransform
)
156 return Matrix(aTransform
._11
, aTransform
._12
,
157 aTransform
._21
, aTransform
._22
,
158 aTransform
._31
, aTransform
._32
);
161 static inline Point
ToPoint(const D2D1_POINT_2F
&aPoint
)
163 return Point(aPoint
.x
, aPoint
.y
);
166 static inline DXGI_FORMAT
DXGIFormat(SurfaceFormat aFormat
)
169 case SurfaceFormat::B8G8R8A8
:
170 return DXGI_FORMAT_B8G8R8A8_UNORM
;
171 case SurfaceFormat::B8G8R8X8
:
172 return DXGI_FORMAT_B8G8R8A8_UNORM
;
173 case SurfaceFormat::A8
:
174 return DXGI_FORMAT_A8_UNORM
;
176 return DXGI_FORMAT_UNKNOWN
;
180 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
)
192 return D2D1::PixelFormat(DXGIFormat(aFormat
), D2DAlphaModeForFormat(aFormat
));
196 static inline D2D1_COMPOSITE_MODE
D2DCompositionMode(CompositionOp aOp
)
199 case CompositionOp::OP_OVER
:
200 return D2D1_COMPOSITE_MODE_SOURCE_OVER
;
201 case CompositionOp::OP_ADD
:
202 return D2D1_COMPOSITE_MODE_PLUS
;
203 case CompositionOp::OP_ATOP
:
204 return D2D1_COMPOSITE_MODE_SOURCE_ATOP
;
205 case CompositionOp::OP_OUT
:
206 return D2D1_COMPOSITE_MODE_SOURCE_OUT
;
207 case CompositionOp::OP_IN
:
208 return D2D1_COMPOSITE_MODE_SOURCE_IN
;
209 case CompositionOp::OP_SOURCE
:
210 return D2D1_COMPOSITE_MODE_SOURCE_COPY
;
211 case CompositionOp::OP_DEST_IN
:
212 return D2D1_COMPOSITE_MODE_DESTINATION_IN
;
213 case CompositionOp::OP_DEST_OUT
:
214 return D2D1_COMPOSITE_MODE_DESTINATION_OUT
;
215 case CompositionOp::OP_DEST_OVER
:
216 return D2D1_COMPOSITE_MODE_DESTINATION_OVER
;
217 case CompositionOp::OP_DEST_ATOP
:
218 return D2D1_COMPOSITE_MODE_DESTINATION_ATOP
;
219 case CompositionOp::OP_XOR
:
220 return D2D1_COMPOSITE_MODE_XOR
;
222 return D2D1_COMPOSITE_MODE_SOURCE_OVER
;
227 static inline bool IsPatternSupportedByD2D(const Pattern
&aPattern
)
229 if (aPattern
.GetType() != PatternType::RADIAL_GRADIENT
) {
233 const RadialGradientPattern
*pat
=
234 static_cast<const RadialGradientPattern
*>(&aPattern
);
236 if (pat
->mRadius1
!= 0) {
240 Point diff
= pat
->mCenter2
- pat
->mCenter1
;
242 if (sqrt(diff
.x
* diff
.x
+ diff
.y
* diff
.y
) >= pat
->mRadius2
) {
243 // Inner point lies outside the circle.
251 * This structure is used to pass rectangles to our shader constant. We can use
252 * this for passing rectangular areas to SetVertexShaderConstant. In the format
253 * of a 4 component float(x,y,width,height). Our vertex shader can then use
254 * this to construct rectangular positions from the 0,0-1,1 quad that we source
257 struct ShaderConstantRectD3D10
259 float mX
, mY
, mWidth
, mHeight
;
260 ShaderConstantRectD3D10(float aX
, float aY
, float aWidth
, float aHeight
)
261 : mX(aX
), mY(aY
), mWidth(aWidth
), mHeight(aHeight
)
264 // For easy passing to SetVertexShaderConstantF.
265 operator float* () { return &mX
; }
268 static inline DWRITE_MATRIX
269 DWriteMatrixFromMatrix(Matrix
&aMatrix
)
272 mat
.m11
= aMatrix
._11
;
273 mat
.m12
= aMatrix
._12
;
274 mat
.m21
= aMatrix
._21
;
275 mat
.m22
= aMatrix
._22
;
276 mat
.dx
= aMatrix
._31
;
277 mat
.dy
= aMatrix
._32
;
281 class AutoDWriteGlyphRun
: public DWRITE_GLYPH_RUN
283 static const unsigned kNumAutoGlyphs
= 256;
286 AutoDWriteGlyphRun() {
290 ~AutoDWriteGlyphRun() {
291 if (glyphCount
> kNumAutoGlyphs
) {
292 delete[] glyphIndices
;
293 delete[] glyphAdvances
;
294 delete[] glyphOffsets
;
298 void allocate(unsigned aNumGlyphs
) {
299 glyphCount
= aNumGlyphs
;
300 if (aNumGlyphs
<= kNumAutoGlyphs
) {
301 glyphIndices
= &mAutoIndices
[0];
302 glyphAdvances
= &mAutoAdvances
[0];
303 glyphOffsets
= &mAutoOffsets
[0];
305 glyphIndices
= new UINT16
[aNumGlyphs
];
306 glyphAdvances
= new FLOAT
[aNumGlyphs
];
307 glyphOffsets
= new DWRITE_GLYPH_OFFSET
[aNumGlyphs
];
312 DWRITE_GLYPH_OFFSET mAutoOffsets
[kNumAutoGlyphs
];
313 FLOAT mAutoAdvances
[kNumAutoGlyphs
];
314 UINT16 mAutoIndices
[kNumAutoGlyphs
];
318 DWriteGlyphRunFromGlyphs(const GlyphBuffer
&aGlyphs
, ScaledFontDWrite
*aFont
, AutoDWriteGlyphRun
*run
)
320 run
->allocate(aGlyphs
.mNumGlyphs
);
322 FLOAT
*advances
= const_cast<FLOAT
*>(run
->glyphAdvances
);
323 UINT16
*indices
= const_cast<UINT16
*>(run
->glyphIndices
);
324 DWRITE_GLYPH_OFFSET
*offsets
= const_cast<DWRITE_GLYPH_OFFSET
*>(run
->glyphOffsets
);
326 memset(advances
, 0, sizeof(FLOAT
) * aGlyphs
.mNumGlyphs
);
327 for (unsigned int i
= 0; i
< aGlyphs
.mNumGlyphs
; i
++) {
328 indices
[i
] = aGlyphs
.mGlyphs
[i
].mIndex
;
329 offsets
[i
].advanceOffset
= aGlyphs
.mGlyphs
[i
].mPosition
.x
;
330 offsets
[i
].ascenderOffset
= -aGlyphs
.mGlyphs
[i
].mPosition
.y
;
334 run
->fontFace
= aFont
->mFontFace
;
335 run
->fontEmSize
= aFont
->GetSize();
336 run
->glyphCount
= aGlyphs
.mNumGlyphs
;
337 run
->isSideways
= FALSE
;
340 static TemporaryRef
<ID2D1Geometry
>
341 ConvertRectToGeometry(const D2D1_RECT_F
& aRect
)
343 RefPtr
<ID2D1RectangleGeometry
> rectGeom
;
344 D2DFactory()->CreateRectangleGeometry(&aRect
, byRef(rectGeom
));
345 return rectGeom
.forget();
348 static TemporaryRef
<ID2D1Geometry
>
349 GetTransformedGeometry(ID2D1Geometry
*aGeometry
, const D2D1_MATRIX_3X2_F
&aTransform
)
351 RefPtr
<ID2D1PathGeometry
> tmpGeometry
;
352 D2DFactory()->CreatePathGeometry(byRef(tmpGeometry
));
353 RefPtr
<ID2D1GeometrySink
> currentSink
;
354 tmpGeometry
->Open(byRef(currentSink
));
355 aGeometry
->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES
,
356 aTransform
, currentSink
);
357 currentSink
->Close();
358 return tmpGeometry
.forget();
361 static TemporaryRef
<ID2D1Geometry
>
362 IntersectGeometry(ID2D1Geometry
*aGeometryA
, ID2D1Geometry
*aGeometryB
)
364 RefPtr
<ID2D1PathGeometry
> pathGeom
;
365 D2DFactory()->CreatePathGeometry(byRef(pathGeom
));
366 RefPtr
<ID2D1GeometrySink
> sink
;
367 pathGeom
->Open(byRef(sink
));
368 aGeometryA
->CombineWithGeometry(aGeometryB
, D2D1_COMBINE_MODE_INTERSECT
, nullptr, sink
);
371 return pathGeom
.forget();
374 static TemporaryRef
<ID2D1StrokeStyle
>
375 CreateStrokeStyleForOptions(const StrokeOptions
&aStrokeOptions
)
377 RefPtr
<ID2D1StrokeStyle
> style
;
379 D2D1_CAP_STYLE capStyle
;
380 D2D1_LINE_JOIN joinStyle
;
382 switch (aStrokeOptions
.mLineCap
) {
384 capStyle
= D2D1_CAP_STYLE_FLAT
;
386 case CapStyle::ROUND
:
387 capStyle
= D2D1_CAP_STYLE_ROUND
;
389 case CapStyle::SQUARE
:
390 capStyle
= D2D1_CAP_STYLE_SQUARE
;
394 switch (aStrokeOptions
.mLineJoin
) {
395 case JoinStyle::MITER
:
396 joinStyle
= D2D1_LINE_JOIN_MITER
;
398 case JoinStyle::MITER_OR_BEVEL
:
399 joinStyle
= D2D1_LINE_JOIN_MITER_OR_BEVEL
;
401 case JoinStyle::ROUND
:
402 joinStyle
= D2D1_LINE_JOIN_ROUND
;
404 case JoinStyle::BEVEL
:
405 joinStyle
= D2D1_LINE_JOIN_BEVEL
;
411 if (aStrokeOptions
.mDashPattern
) {
412 typedef std::vector
<Float
> FloatVector
;
413 // D2D "helpfully" multiplies the dash pattern by the line width.
414 // That's not what cairo does, or is what <canvas>'s dash wants.
415 // So fix the multiplication in advance.
416 Float lineWidth
= aStrokeOptions
.mLineWidth
;
417 FloatVector
dash(aStrokeOptions
.mDashPattern
,
418 aStrokeOptions
.mDashPattern
+ aStrokeOptions
.mDashLength
);
419 for (FloatVector::iterator it
= dash
.begin(); it
!= dash
.end(); ++it
) {
423 hr
= D2DFactory()->CreateStrokeStyle(
424 D2D1::StrokeStyleProperties(capStyle
, capStyle
,
426 aStrokeOptions
.mMiterLimit
,
427 D2D1_DASH_STYLE_CUSTOM
,
428 aStrokeOptions
.mDashOffset
/ lineWidth
),
429 &dash
[0], // data() is not C++98, although it's in recent gcc
434 hr
= D2DFactory()->CreateStrokeStyle(
435 D2D1::StrokeStyleProperties(capStyle
, capStyle
,
437 aStrokeOptions
.mMiterLimit
),
438 nullptr, 0, byRef(style
));
442 gfxWarning() << "Failed to create Direct2D stroke style.";
445 return style
.forget();
448 // This creates a (partially) uploaded bitmap for a DataSourceSurface. It
449 // uploads the minimum requirement and possibly downscales. It adjusts the
450 // input Matrix to compensate.
451 static TemporaryRef
<ID2D1Bitmap
>
452 CreatePartialBitmapForSurface(DataSourceSurface
*aSurface
, const Matrix
&aDestinationTransform
,
453 const IntSize
&aDestinationSize
, ExtendMode aExtendMode
,
454 Matrix
&aSourceTransform
, ID2D1RenderTarget
*aRT
)
456 RefPtr
<ID2D1Bitmap
> bitmap
;
458 // This is where things get complicated. The source surface was
459 // created for a surface that was too large to fit in a texture.
460 // We'll need to figure out if we can work with a partial upload
461 // or downsample in software.
463 Matrix transform
= aDestinationTransform
;
464 Matrix invTransform
= transform
= aSourceTransform
* transform
;
465 if (!invTransform
.Invert()) {
466 // Singular transform, nothing to be drawn.
470 Rect
rect(0, 0, Float(aDestinationSize
.width
), Float(aDestinationSize
.height
));
472 // Calculate the rectangle of the source mapped to our surface.
473 rect
= invTransform
.TransformBounds(rect
);
476 IntSize size
= aSurface
->GetSize();
478 Rect
uploadRect(0, 0, Float(size
.width
), Float(size
.height
));
480 // Limit the uploadRect as much as possible without supporting discontiguous uploads
482 // region we will paint from
484 // .---------------. .---------------. resulting uploadRect
486 // | .---------. .----. .----. .---------------.
487 // | | | ----> | | | | ----> | |
488 // | '---------' '----' '----' '---------------'
489 // '---------------' '---------------'
493 if (uploadRect
.Contains(rect
)) {
494 // Extend mode is irrelevant, the displayed rect is completely contained
495 // by the source bitmap.
497 } else if (aExtendMode
== ExtendMode::CLAMP
&& uploadRect
.Intersects(rect
)) {
498 // Calculate the rectangle on the source bitmap that touches our
499 // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
500 // correct behaviour in this case.
501 uploadRect
= uploadRect
.Intersect(rect
);
503 // We now proceed to check if we can limit at least one dimension of the
504 // upload rect safely without looking at extend mode.
505 } else if (rect
.x
>= 0 && rect
.XMost() < size
.width
) {
506 uploadRect
.x
= rect
.x
;
507 uploadRect
.width
= rect
.width
;
508 } else if (rect
.y
>= 0 && rect
.YMost() < size
.height
) {
509 uploadRect
.y
= rect
.y
;
510 uploadRect
.height
= rect
.height
;
514 int stride
= aSurface
->Stride();
516 if (uploadRect
.width
<= aRT
->GetMaximumBitmapSize() &&
517 uploadRect
.height
<= aRT
->GetMaximumBitmapSize()) {
519 // A partial upload will suffice.
520 aRT
->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect
.width
), uint32_t(uploadRect
.height
)),
521 aSurface
->GetData() + int(uploadRect
.x
) * 4 + int(uploadRect
.y
) * stride
,
523 D2D1::BitmapProperties(D2DPixelFormat(aSurface
->GetFormat())),
526 aSourceTransform
.Translate(uploadRect
.x
, uploadRect
.y
);
528 return bitmap
.forget();
530 int Bpp
= BytesPerPixel(aSurface
->GetFormat());
533 // This shouldn't actually happen in practice!
538 ImageHalfScaler
scaler(aSurface
->GetData(), stride
, size
);
540 // Calculate the maximum width/height of the image post transform.
541 Point topRight
= transform
* Point(Float(size
.width
), 0);
542 Point topLeft
= transform
* Point(0, 0);
543 Point bottomRight
= transform
* Point(Float(size
.width
), Float(size
.height
));
544 Point bottomLeft
= transform
* Point(0, Float(size
.height
));
548 scaleSize
.width
= int32_t(std::max(Distance(topRight
, topLeft
),
549 Distance(bottomRight
, bottomLeft
)));
550 scaleSize
.height
= int32_t(std::max(Distance(topRight
, bottomRight
),
551 Distance(topLeft
, bottomLeft
)));
553 if (unsigned(scaleSize
.width
) > aRT
->GetMaximumBitmapSize()) {
554 // Ok, in this case we'd really want a downscale of a part of the bitmap,
555 // perhaps we can do this later but for simplicity let's do something
556 // different here and assume it's good enough, this should be rare!
557 scaleSize
.width
= 4095;
559 if (unsigned(scaleSize
.height
) > aRT
->GetMaximumBitmapSize()) {
560 scaleSize
.height
= 4095;
563 scaler
.ScaleForSize(scaleSize
);
565 IntSize newSize
= scaler
.GetSize();
567 aRT
->CreateBitmap(D2D1::SizeU(newSize
.width
, newSize
.height
),
568 scaler
.GetScaledData(), scaler
.GetStride(),
569 D2D1::BitmapProperties(D2DPixelFormat(aSurface
->GetFormat())),
572 aSourceTransform
.Scale(Float(size
.width
/ newSize
.width
),
573 Float(size
.height
/ newSize
.height
));
574 return bitmap
.forget();
581 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */