Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / gfx / 2d / HelpersD2D.h
blobecb79263354932c5208e4533935549c9ca6c5531
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_
9 #ifndef USE_D2D1_1
10 #include "moz-d2d1-1.h"
11 #else
12 #include <d2d1_1.h>
13 #endif
15 #include <vector>
17 #include <dwrite.h>
18 #include "2D.h"
19 #include "Logging.h"
20 #include "Tools.h"
21 #include "ImageScaling.h"
23 #include "ScaledFontDWrite.h"
25 #undef min
26 #undef max
28 namespace mozilla {
29 namespace gfx {
31 ID2D1Factory* D2DFactory();
33 #ifdef USE_D2D1_1
34 ID2D1Factory1* D2DFactory1();
35 #endif
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;
58 break;
59 case ExtendMode::REFLECT:
60 extend = D2D1_EXTEND_MODE_MIRROR;
61 break;
62 default:
63 extend = D2D1_EXTEND_MODE_CLAMP;
66 return extend;
69 static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter)
71 switch (aFilter) {
72 case Filter::POINT:
73 return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
74 default:
75 return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
79 #ifdef USE_D2D1_1
80 static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter)
82 switch (aFilter) {
83 case Filter::POINT:
84 return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
85 default:
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);
104 #endif
106 static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode)
108 switch (aMode) {
109 case AntialiasMode::NONE:
110 return D2D1_ANTIALIAS_MODE_ALIASED;
111 default:
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;
141 } else {
142 return SurfaceFormat::B8G8R8A8;
144 default:
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)
168 switch (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;
175 default:
176 return DXGI_FORMAT_UNKNOWN;
180 static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat)
182 switch (aFormat) {
183 case SurfaceFormat::B8G8R8X8:
184 return D2D1_ALPHA_MODE_IGNORE;
185 default:
186 return D2D1_ALPHA_MODE_PREMULTIPLIED;
190 static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
192 return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat));
195 #ifdef USE_D2D1_1
196 static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
198 switch(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;
221 default:
222 return D2D1_COMPOSITE_MODE_SOURCE_OVER;
225 #endif
227 static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
229 if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) {
230 return true;
233 const RadialGradientPattern *pat =
234 static_cast<const RadialGradientPattern*>(&aPattern);
236 if (pat->mRadius1 != 0) {
237 return false;
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.
244 return false;
247 return true;
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
255 * it with.
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)
271 DWRITE_MATRIX mat;
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;
278 return mat;
281 class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN
283 static const unsigned kNumAutoGlyphs = 256;
285 public:
286 AutoDWriteGlyphRun() {
287 glyphCount = 0;
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];
304 } else {
305 glyphIndices = new UINT16[aNumGlyphs];
306 glyphAdvances = new FLOAT[aNumGlyphs];
307 glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs];
311 private:
312 DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs];
313 FLOAT mAutoAdvances[kNumAutoGlyphs];
314 UINT16 mAutoIndices[kNumAutoGlyphs];
317 static inline void
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;
333 run->bidiLevel = 0;
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);
369 sink->Close();
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) {
383 case CapStyle::BUTT:
384 capStyle = D2D1_CAP_STYLE_FLAT;
385 break;
386 case CapStyle::ROUND:
387 capStyle = D2D1_CAP_STYLE_ROUND;
388 break;
389 case CapStyle::SQUARE:
390 capStyle = D2D1_CAP_STYLE_SQUARE;
391 break;
394 switch (aStrokeOptions.mLineJoin) {
395 case JoinStyle::MITER:
396 joinStyle = D2D1_LINE_JOIN_MITER;
397 break;
398 case JoinStyle::MITER_OR_BEVEL:
399 joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL;
400 break;
401 case JoinStyle::ROUND:
402 joinStyle = D2D1_LINE_JOIN_ROUND;
403 break;
404 case JoinStyle::BEVEL:
405 joinStyle = D2D1_LINE_JOIN_BEVEL;
406 break;
410 HRESULT hr;
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) {
420 *it /= lineWidth;
423 hr = D2DFactory()->CreateStrokeStyle(
424 D2D1::StrokeStyleProperties(capStyle, capStyle,
425 capStyle, joinStyle,
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
430 // and VC10's STL
431 dash.size(),
432 byRef(style));
433 } else {
434 hr = D2DFactory()->CreateStrokeStyle(
435 D2D1::StrokeStyleProperties(capStyle, capStyle,
436 capStyle, joinStyle,
437 aStrokeOptions.mMiterLimit),
438 nullptr, 0, byRef(style));
441 if (FAILED(hr)) {
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.
467 return nullptr;
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);
474 rect.RoundOut();
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
483 // uploadRect
484 // .---------------. .---------------. resulting uploadRect
485 // | |rect | |
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.
496 uploadRect = rect;
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,
522 stride,
523 D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
524 byRef(bitmap));
526 aSourceTransform.Translate(uploadRect.x, uploadRect.y);
528 return bitmap.forget();
529 } else {
530 int Bpp = BytesPerPixel(aSurface->GetFormat());
532 if (Bpp != 4) {
533 // This shouldn't actually happen in practice!
534 MOZ_ASSERT(false);
535 return nullptr;
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));
546 IntSize scaleSize;
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())),
570 byRef(bitmap));
572 aSourceTransform.Scale(Float(size.width / newSize.width),
573 Float(size.height / newSize.height));
574 return bitmap.forget();
581 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */