gfx/2d/HelpersD2D.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #ifndef MOZILLA_GFX_HELPERSD2D_H_
michael@0 7 #define MOZILLA_GFX_HELPERSD2D_H_
michael@0 8
michael@0 9 #ifndef USE_D2D1_1
michael@0 10 #include "moz-d2d1-1.h"
michael@0 11 #else
michael@0 12 #include <d2d1_1.h>
michael@0 13 #endif
michael@0 14
michael@0 15 #include <vector>
michael@0 16
michael@0 17 #include <dwrite.h>
michael@0 18 #include "2D.h"
michael@0 19 #include "Logging.h"
michael@0 20 #include "Tools.h"
michael@0 21 #include "ImageScaling.h"
michael@0 22
michael@0 23 #include "ScaledFontDWrite.h"
michael@0 24
michael@0 25 #undef min
michael@0 26 #undef max
michael@0 27
michael@0 28 namespace mozilla {
michael@0 29 namespace gfx {
michael@0 30
michael@0 31 ID2D1Factory* D2DFactory();
michael@0 32
michael@0 33 #ifdef USE_D2D1_1
michael@0 34 ID2D1Factory1* D2DFactory1();
michael@0 35 #endif
michael@0 36
michael@0 37 static inline D2D1_POINT_2F D2DPoint(const Point &aPoint)
michael@0 38 {
michael@0 39 return D2D1::Point2F(aPoint.x, aPoint.y);
michael@0 40 }
michael@0 41
michael@0 42 static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize)
michael@0 43 {
michael@0 44 return D2D1::SizeU(aSize.width, aSize.height);
michael@0 45 }
michael@0 46
michael@0 47 static inline D2D1_RECT_F D2DRect(const Rect &aRect)
michael@0 48 {
michael@0 49 return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost());
michael@0 50 }
michael@0 51
michael@0 52 static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode)
michael@0 53 {
michael@0 54 D2D1_EXTEND_MODE extend;
michael@0 55 switch (aExtendMode) {
michael@0 56 case ExtendMode::REPEAT:
michael@0 57 extend = D2D1_EXTEND_MODE_WRAP;
michael@0 58 break;
michael@0 59 case ExtendMode::REFLECT:
michael@0 60 extend = D2D1_EXTEND_MODE_MIRROR;
michael@0 61 break;
michael@0 62 default:
michael@0 63 extend = D2D1_EXTEND_MODE_CLAMP;
michael@0 64 }
michael@0 65
michael@0 66 return extend;
michael@0 67 }
michael@0 68
michael@0 69 static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter)
michael@0 70 {
michael@0 71 switch (aFilter) {
michael@0 72 case Filter::POINT:
michael@0 73 return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
michael@0 74 default:
michael@0 75 return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
michael@0 76 }
michael@0 77 }
michael@0 78
michael@0 79 #ifdef USE_D2D1_1
michael@0 80 static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter)
michael@0 81 {
michael@0 82 switch (aFilter) {
michael@0 83 case Filter::POINT:
michael@0 84 return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
michael@0 85 default:
michael@0 86 return D2D1_INTERPOLATION_MODE_LINEAR;
michael@0 87 }
michael@0 88 }
michael@0 89
michael@0 90 static inline D2D1_MATRIX_5X4_F D2DMatrix5x4(const Matrix5x4 &aMatrix)
michael@0 91 {
michael@0 92 return D2D1::Matrix5x4F(aMatrix._11, aMatrix._12, aMatrix._13, aMatrix._14,
michael@0 93 aMatrix._21, aMatrix._22, aMatrix._23, aMatrix._24,
michael@0 94 aMatrix._31, aMatrix._32, aMatrix._33, aMatrix._34,
michael@0 95 aMatrix._41, aMatrix._42, aMatrix._43, aMatrix._44,
michael@0 96 aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54);
michael@0 97 }
michael@0 98
michael@0 99 static inline D2D1_VECTOR_3F D2DVector3D(const Point3D &aPoint)
michael@0 100 {
michael@0 101 return D2D1::Vector3F(aPoint.x, aPoint.y, aPoint.z);
michael@0 102 }
michael@0 103
michael@0 104 #endif
michael@0 105
michael@0 106 static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode)
michael@0 107 {
michael@0 108 switch (aMode) {
michael@0 109 case AntialiasMode::NONE:
michael@0 110 return D2D1_ANTIALIAS_MODE_ALIASED;
michael@0 111 default:
michael@0 112 return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
michael@0 113 }
michael@0 114 }
michael@0 115
michael@0 116 static inline D2D1_MATRIX_3X2_F D2DMatrix(const Matrix &aTransform)
michael@0 117 {
michael@0 118 return D2D1::Matrix3x2F(aTransform._11, aTransform._12,
michael@0 119 aTransform._21, aTransform._22,
michael@0 120 aTransform._31, aTransform._32);
michael@0 121 }
michael@0 122
michael@0 123 static inline D2D1_COLOR_F D2DColor(const Color &aColor)
michael@0 124 {
michael@0 125 return D2D1::ColorF(aColor.r, aColor.g, aColor.b, aColor.a);
michael@0 126 }
michael@0 127
michael@0 128 static inline IntSize ToIntSize(const D2D1_SIZE_U &aSize)
michael@0 129 {
michael@0 130 return IntSize(aSize.width, aSize.height);
michael@0 131 }
michael@0 132
michael@0 133 static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat)
michael@0 134 {
michael@0 135 switch(aFormat.format) {
michael@0 136 case DXGI_FORMAT_A8_UNORM:
michael@0 137 return SurfaceFormat::A8;
michael@0 138 case DXGI_FORMAT_B8G8R8A8_UNORM:
michael@0 139 if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) {
michael@0 140 return SurfaceFormat::B8G8R8X8;
michael@0 141 } else {
michael@0 142 return SurfaceFormat::B8G8R8A8;
michael@0 143 }
michael@0 144 default:
michael@0 145 return SurfaceFormat::B8G8R8A8;
michael@0 146 }
michael@0 147 }
michael@0 148
michael@0 149 static inline Rect ToRect(const D2D1_RECT_F &aRect)
michael@0 150 {
michael@0 151 return Rect(aRect.left, aRect.top, aRect.right - aRect.left, aRect.bottom - aRect.top);
michael@0 152 }
michael@0 153
michael@0 154 static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform)
michael@0 155 {
michael@0 156 return Matrix(aTransform._11, aTransform._12,
michael@0 157 aTransform._21, aTransform._22,
michael@0 158 aTransform._31, aTransform._32);
michael@0 159 }
michael@0 160
michael@0 161 static inline Point ToPoint(const D2D1_POINT_2F &aPoint)
michael@0 162 {
michael@0 163 return Point(aPoint.x, aPoint.y);
michael@0 164 }
michael@0 165
michael@0 166 static inline DXGI_FORMAT DXGIFormat(SurfaceFormat aFormat)
michael@0 167 {
michael@0 168 switch (aFormat) {
michael@0 169 case SurfaceFormat::B8G8R8A8:
michael@0 170 return DXGI_FORMAT_B8G8R8A8_UNORM;
michael@0 171 case SurfaceFormat::B8G8R8X8:
michael@0 172 return DXGI_FORMAT_B8G8R8A8_UNORM;
michael@0 173 case SurfaceFormat::A8:
michael@0 174 return DXGI_FORMAT_A8_UNORM;
michael@0 175 default:
michael@0 176 return DXGI_FORMAT_UNKNOWN;
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat)
michael@0 181 {
michael@0 182 switch (aFormat) {
michael@0 183 case SurfaceFormat::B8G8R8X8:
michael@0 184 return D2D1_ALPHA_MODE_IGNORE;
michael@0 185 default:
michael@0 186 return D2D1_ALPHA_MODE_PREMULTIPLIED;
michael@0 187 }
michael@0 188 }
michael@0 189
michael@0 190 static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
michael@0 191 {
michael@0 192 return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat));
michael@0 193 }
michael@0 194
michael@0 195 #ifdef USE_D2D1_1
michael@0 196 static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
michael@0 197 {
michael@0 198 switch(aOp) {
michael@0 199 case CompositionOp::OP_OVER:
michael@0 200 return D2D1_COMPOSITE_MODE_SOURCE_OVER;
michael@0 201 case CompositionOp::OP_ADD:
michael@0 202 return D2D1_COMPOSITE_MODE_PLUS;
michael@0 203 case CompositionOp::OP_ATOP:
michael@0 204 return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
michael@0 205 case CompositionOp::OP_OUT:
michael@0 206 return D2D1_COMPOSITE_MODE_SOURCE_OUT;
michael@0 207 case CompositionOp::OP_IN:
michael@0 208 return D2D1_COMPOSITE_MODE_SOURCE_IN;
michael@0 209 case CompositionOp::OP_SOURCE:
michael@0 210 return D2D1_COMPOSITE_MODE_SOURCE_COPY;
michael@0 211 case CompositionOp::OP_DEST_IN:
michael@0 212 return D2D1_COMPOSITE_MODE_DESTINATION_IN;
michael@0 213 case CompositionOp::OP_DEST_OUT:
michael@0 214 return D2D1_COMPOSITE_MODE_DESTINATION_OUT;
michael@0 215 case CompositionOp::OP_DEST_OVER:
michael@0 216 return D2D1_COMPOSITE_MODE_DESTINATION_OVER;
michael@0 217 case CompositionOp::OP_DEST_ATOP:
michael@0 218 return D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
michael@0 219 case CompositionOp::OP_XOR:
michael@0 220 return D2D1_COMPOSITE_MODE_XOR;
michael@0 221 default:
michael@0 222 return D2D1_COMPOSITE_MODE_SOURCE_OVER;
michael@0 223 }
michael@0 224 }
michael@0 225 #endif
michael@0 226
michael@0 227 static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
michael@0 228 {
michael@0 229 if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) {
michael@0 230 return true;
michael@0 231 }
michael@0 232
michael@0 233 const RadialGradientPattern *pat =
michael@0 234 static_cast<const RadialGradientPattern*>(&aPattern);
michael@0 235
michael@0 236 if (pat->mRadius1 != 0) {
michael@0 237 return false;
michael@0 238 }
michael@0 239
michael@0 240 Point diff = pat->mCenter2 - pat->mCenter1;
michael@0 241
michael@0 242 if (sqrt(diff.x * diff.x + diff.y * diff.y) >= pat->mRadius2) {
michael@0 243 // Inner point lies outside the circle.
michael@0 244 return false;
michael@0 245 }
michael@0 246
michael@0 247 return true;
michael@0 248 }
michael@0 249
michael@0 250 /**
michael@0 251 * This structure is used to pass rectangles to our shader constant. We can use
michael@0 252 * this for passing rectangular areas to SetVertexShaderConstant. In the format
michael@0 253 * of a 4 component float(x,y,width,height). Our vertex shader can then use
michael@0 254 * this to construct rectangular positions from the 0,0-1,1 quad that we source
michael@0 255 * it with.
michael@0 256 */
michael@0 257 struct ShaderConstantRectD3D10
michael@0 258 {
michael@0 259 float mX, mY, mWidth, mHeight;
michael@0 260 ShaderConstantRectD3D10(float aX, float aY, float aWidth, float aHeight)
michael@0 261 : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight)
michael@0 262 { }
michael@0 263
michael@0 264 // For easy passing to SetVertexShaderConstantF.
michael@0 265 operator float* () { return &mX; }
michael@0 266 };
michael@0 267
michael@0 268 static inline DWRITE_MATRIX
michael@0 269 DWriteMatrixFromMatrix(Matrix &aMatrix)
michael@0 270 {
michael@0 271 DWRITE_MATRIX mat;
michael@0 272 mat.m11 = aMatrix._11;
michael@0 273 mat.m12 = aMatrix._12;
michael@0 274 mat.m21 = aMatrix._21;
michael@0 275 mat.m22 = aMatrix._22;
michael@0 276 mat.dx = aMatrix._31;
michael@0 277 mat.dy = aMatrix._32;
michael@0 278 return mat;
michael@0 279 }
michael@0 280
michael@0 281 class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN
michael@0 282 {
michael@0 283 static const unsigned kNumAutoGlyphs = 256;
michael@0 284
michael@0 285 public:
michael@0 286 AutoDWriteGlyphRun() {
michael@0 287 glyphCount = 0;
michael@0 288 }
michael@0 289
michael@0 290 ~AutoDWriteGlyphRun() {
michael@0 291 if (glyphCount > kNumAutoGlyphs) {
michael@0 292 delete[] glyphIndices;
michael@0 293 delete[] glyphAdvances;
michael@0 294 delete[] glyphOffsets;
michael@0 295 }
michael@0 296 }
michael@0 297
michael@0 298 void allocate(unsigned aNumGlyphs) {
michael@0 299 glyphCount = aNumGlyphs;
michael@0 300 if (aNumGlyphs <= kNumAutoGlyphs) {
michael@0 301 glyphIndices = &mAutoIndices[0];
michael@0 302 glyphAdvances = &mAutoAdvances[0];
michael@0 303 glyphOffsets = &mAutoOffsets[0];
michael@0 304 } else {
michael@0 305 glyphIndices = new UINT16[aNumGlyphs];
michael@0 306 glyphAdvances = new FLOAT[aNumGlyphs];
michael@0 307 glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs];
michael@0 308 }
michael@0 309 }
michael@0 310
michael@0 311 private:
michael@0 312 DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs];
michael@0 313 FLOAT mAutoAdvances[kNumAutoGlyphs];
michael@0 314 UINT16 mAutoIndices[kNumAutoGlyphs];
michael@0 315 };
michael@0 316
michael@0 317 static inline void
michael@0 318 DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, AutoDWriteGlyphRun *run)
michael@0 319 {
michael@0 320 run->allocate(aGlyphs.mNumGlyphs);
michael@0 321
michael@0 322 FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances);
michael@0 323 UINT16 *indices = const_cast<UINT16*>(run->glyphIndices);
michael@0 324 DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets);
michael@0 325
michael@0 326 memset(advances, 0, sizeof(FLOAT) * aGlyphs.mNumGlyphs);
michael@0 327 for (unsigned int i = 0; i < aGlyphs.mNumGlyphs; i++) {
michael@0 328 indices[i] = aGlyphs.mGlyphs[i].mIndex;
michael@0 329 offsets[i].advanceOffset = aGlyphs.mGlyphs[i].mPosition.x;
michael@0 330 offsets[i].ascenderOffset = -aGlyphs.mGlyphs[i].mPosition.y;
michael@0 331 }
michael@0 332
michael@0 333 run->bidiLevel = 0;
michael@0 334 run->fontFace = aFont->mFontFace;
michael@0 335 run->fontEmSize = aFont->GetSize();
michael@0 336 run->glyphCount = aGlyphs.mNumGlyphs;
michael@0 337 run->isSideways = FALSE;
michael@0 338 }
michael@0 339
michael@0 340 static TemporaryRef<ID2D1Geometry>
michael@0 341 ConvertRectToGeometry(const D2D1_RECT_F& aRect)
michael@0 342 {
michael@0 343 RefPtr<ID2D1RectangleGeometry> rectGeom;
michael@0 344 D2DFactory()->CreateRectangleGeometry(&aRect, byRef(rectGeom));
michael@0 345 return rectGeom.forget();
michael@0 346 }
michael@0 347
michael@0 348 static TemporaryRef<ID2D1Geometry>
michael@0 349 GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
michael@0 350 {
michael@0 351 RefPtr<ID2D1PathGeometry> tmpGeometry;
michael@0 352 D2DFactory()->CreatePathGeometry(byRef(tmpGeometry));
michael@0 353 RefPtr<ID2D1GeometrySink> currentSink;
michael@0 354 tmpGeometry->Open(byRef(currentSink));
michael@0 355 aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
michael@0 356 aTransform, currentSink);
michael@0 357 currentSink->Close();
michael@0 358 return tmpGeometry;
michael@0 359 }
michael@0 360
michael@0 361 static TemporaryRef<ID2D1Geometry>
michael@0 362 IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
michael@0 363 {
michael@0 364 RefPtr<ID2D1PathGeometry> pathGeom;
michael@0 365 D2DFactory()->CreatePathGeometry(byRef(pathGeom));
michael@0 366 RefPtr<ID2D1GeometrySink> sink;
michael@0 367 pathGeom->Open(byRef(sink));
michael@0 368 aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink);
michael@0 369 sink->Close();
michael@0 370
michael@0 371 return pathGeom;
michael@0 372 }
michael@0 373
michael@0 374 static TemporaryRef<ID2D1StrokeStyle>
michael@0 375 CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
michael@0 376 {
michael@0 377 RefPtr<ID2D1StrokeStyle> style;
michael@0 378
michael@0 379 D2D1_CAP_STYLE capStyle;
michael@0 380 D2D1_LINE_JOIN joinStyle;
michael@0 381
michael@0 382 switch (aStrokeOptions.mLineCap) {
michael@0 383 case CapStyle::BUTT:
michael@0 384 capStyle = D2D1_CAP_STYLE_FLAT;
michael@0 385 break;
michael@0 386 case CapStyle::ROUND:
michael@0 387 capStyle = D2D1_CAP_STYLE_ROUND;
michael@0 388 break;
michael@0 389 case CapStyle::SQUARE:
michael@0 390 capStyle = D2D1_CAP_STYLE_SQUARE;
michael@0 391 break;
michael@0 392 }
michael@0 393
michael@0 394 switch (aStrokeOptions.mLineJoin) {
michael@0 395 case JoinStyle::MITER:
michael@0 396 joinStyle = D2D1_LINE_JOIN_MITER;
michael@0 397 break;
michael@0 398 case JoinStyle::MITER_OR_BEVEL:
michael@0 399 joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL;
michael@0 400 break;
michael@0 401 case JoinStyle::ROUND:
michael@0 402 joinStyle = D2D1_LINE_JOIN_ROUND;
michael@0 403 break;
michael@0 404 case JoinStyle::BEVEL:
michael@0 405 joinStyle = D2D1_LINE_JOIN_BEVEL;
michael@0 406 break;
michael@0 407 }
michael@0 408
michael@0 409
michael@0 410 HRESULT hr;
michael@0 411 if (aStrokeOptions.mDashPattern) {
michael@0 412 typedef std::vector<Float> FloatVector;
michael@0 413 // D2D "helpfully" multiplies the dash pattern by the line width.
michael@0 414 // That's not what cairo does, or is what <canvas>'s dash wants.
michael@0 415 // So fix the multiplication in advance.
michael@0 416 Float lineWidth = aStrokeOptions.mLineWidth;
michael@0 417 FloatVector dash(aStrokeOptions.mDashPattern,
michael@0 418 aStrokeOptions.mDashPattern + aStrokeOptions.mDashLength);
michael@0 419 for (FloatVector::iterator it = dash.begin(); it != dash.end(); ++it) {
michael@0 420 *it /= lineWidth;
michael@0 421 }
michael@0 422
michael@0 423 hr = D2DFactory()->CreateStrokeStyle(
michael@0 424 D2D1::StrokeStyleProperties(capStyle, capStyle,
michael@0 425 capStyle, joinStyle,
michael@0 426 aStrokeOptions.mMiterLimit,
michael@0 427 D2D1_DASH_STYLE_CUSTOM,
michael@0 428 aStrokeOptions.mDashOffset / lineWidth),
michael@0 429 &dash[0], // data() is not C++98, although it's in recent gcc
michael@0 430 // and VC10's STL
michael@0 431 dash.size(),
michael@0 432 byRef(style));
michael@0 433 } else {
michael@0 434 hr = D2DFactory()->CreateStrokeStyle(
michael@0 435 D2D1::StrokeStyleProperties(capStyle, capStyle,
michael@0 436 capStyle, joinStyle,
michael@0 437 aStrokeOptions.mMiterLimit),
michael@0 438 nullptr, 0, byRef(style));
michael@0 439 }
michael@0 440
michael@0 441 if (FAILED(hr)) {
michael@0 442 gfxWarning() << "Failed to create Direct2D stroke style.";
michael@0 443 }
michael@0 444
michael@0 445 return style;
michael@0 446 }
michael@0 447
michael@0 448 // This creates a (partially) uploaded bitmap for a DataSourceSurface. It
michael@0 449 // uploads the minimum requirement and possibly downscales. It adjusts the
michael@0 450 // input Matrix to compensate.
michael@0 451 static TemporaryRef<ID2D1Bitmap>
michael@0 452 CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
michael@0 453 const IntSize &aDestinationSize, ExtendMode aExtendMode,
michael@0 454 Matrix &aSourceTransform, ID2D1RenderTarget *aRT)
michael@0 455 {
michael@0 456 RefPtr<ID2D1Bitmap> bitmap;
michael@0 457
michael@0 458 // This is where things get complicated. The source surface was
michael@0 459 // created for a surface that was too large to fit in a texture.
michael@0 460 // We'll need to figure out if we can work with a partial upload
michael@0 461 // or downsample in software.
michael@0 462
michael@0 463 Matrix transform = aDestinationTransform;
michael@0 464 Matrix invTransform = transform = aSourceTransform * transform;
michael@0 465 if (!invTransform.Invert()) {
michael@0 466 // Singular transform, nothing to be drawn.
michael@0 467 return nullptr;
michael@0 468 }
michael@0 469
michael@0 470 Rect rect(0, 0, Float(aDestinationSize.width), Float(aDestinationSize.height));
michael@0 471
michael@0 472 // Calculate the rectangle of the source mapped to our surface.
michael@0 473 rect = invTransform.TransformBounds(rect);
michael@0 474 rect.RoundOut();
michael@0 475
michael@0 476 IntSize size = aSurface->GetSize();
michael@0 477
michael@0 478 Rect uploadRect(0, 0, Float(size.width), Float(size.height));
michael@0 479
michael@0 480 // Limit the uploadRect as much as possible without supporting discontiguous uploads
michael@0 481 //
michael@0 482 // region we will paint from
michael@0 483 // uploadRect
michael@0 484 // .---------------. .---------------. resulting uploadRect
michael@0 485 // | |rect | |
michael@0 486 // | .---------. .----. .----. .---------------.
michael@0 487 // | | | ----> | | | | ----> | |
michael@0 488 // | '---------' '----' '----' '---------------'
michael@0 489 // '---------------' '---------------'
michael@0 490 //
michael@0 491 //
michael@0 492
michael@0 493 if (uploadRect.Contains(rect)) {
michael@0 494 // Extend mode is irrelevant, the displayed rect is completely contained
michael@0 495 // by the source bitmap.
michael@0 496 uploadRect = rect;
michael@0 497 } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) {
michael@0 498 // Calculate the rectangle on the source bitmap that touches our
michael@0 499 // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
michael@0 500 // correct behaviour in this case.
michael@0 501 uploadRect = uploadRect.Intersect(rect);
michael@0 502
michael@0 503 // We now proceed to check if we can limit at least one dimension of the
michael@0 504 // upload rect safely without looking at extend mode.
michael@0 505 } else if (rect.x >= 0 && rect.XMost() < size.width) {
michael@0 506 uploadRect.x = rect.x;
michael@0 507 uploadRect.width = rect.width;
michael@0 508 } else if (rect.y >= 0 && rect.YMost() < size.height) {
michael@0 509 uploadRect.y = rect.y;
michael@0 510 uploadRect.height = rect.height;
michael@0 511 }
michael@0 512
michael@0 513
michael@0 514 int stride = aSurface->Stride();
michael@0 515
michael@0 516 if (uploadRect.width <= aRT->GetMaximumBitmapSize() &&
michael@0 517 uploadRect.height <= aRT->GetMaximumBitmapSize()) {
michael@0 518
michael@0 519 // A partial upload will suffice.
michael@0 520 aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
michael@0 521 aSurface->GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * stride,
michael@0 522 stride,
michael@0 523 D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
michael@0 524 byRef(bitmap));
michael@0 525
michael@0 526 aSourceTransform.Translate(uploadRect.x, uploadRect.y);
michael@0 527
michael@0 528 return bitmap;
michael@0 529 } else {
michael@0 530 int Bpp = BytesPerPixel(aSurface->GetFormat());
michael@0 531
michael@0 532 if (Bpp != 4) {
michael@0 533 // This shouldn't actually happen in practice!
michael@0 534 MOZ_ASSERT(false);
michael@0 535 return nullptr;
michael@0 536 }
michael@0 537
michael@0 538 ImageHalfScaler scaler(aSurface->GetData(), stride, size);
michael@0 539
michael@0 540 // Calculate the maximum width/height of the image post transform.
michael@0 541 Point topRight = transform * Point(Float(size.width), 0);
michael@0 542 Point topLeft = transform * Point(0, 0);
michael@0 543 Point bottomRight = transform * Point(Float(size.width), Float(size.height));
michael@0 544 Point bottomLeft = transform * Point(0, Float(size.height));
michael@0 545
michael@0 546 IntSize scaleSize;
michael@0 547
michael@0 548 scaleSize.width = int32_t(std::max(Distance(topRight, topLeft),
michael@0 549 Distance(bottomRight, bottomLeft)));
michael@0 550 scaleSize.height = int32_t(std::max(Distance(topRight, bottomRight),
michael@0 551 Distance(topLeft, bottomLeft)));
michael@0 552
michael@0 553 if (unsigned(scaleSize.width) > aRT->GetMaximumBitmapSize()) {
michael@0 554 // Ok, in this case we'd really want a downscale of a part of the bitmap,
michael@0 555 // perhaps we can do this later but for simplicity let's do something
michael@0 556 // different here and assume it's good enough, this should be rare!
michael@0 557 scaleSize.width = 4095;
michael@0 558 }
michael@0 559 if (unsigned(scaleSize.height) > aRT->GetMaximumBitmapSize()) {
michael@0 560 scaleSize.height = 4095;
michael@0 561 }
michael@0 562
michael@0 563 scaler.ScaleForSize(scaleSize);
michael@0 564
michael@0 565 IntSize newSize = scaler.GetSize();
michael@0 566
michael@0 567 aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height),
michael@0 568 scaler.GetScaledData(), scaler.GetStride(),
michael@0 569 D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
michael@0 570 byRef(bitmap));
michael@0 571
michael@0 572 aSourceTransform.Scale(Float(size.width / newSize.width),
michael@0 573 Float(size.height / newSize.height));
michael@0 574 return bitmap;
michael@0 575 }
michael@0 576 }
michael@0 577
michael@0 578 }
michael@0 579 }
michael@0 580
michael@0 581 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */

mercurial