1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/HelpersD2D.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,581 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef MOZILLA_GFX_HELPERSD2D_H_ 1.10 +#define MOZILLA_GFX_HELPERSD2D_H_ 1.11 + 1.12 +#ifndef USE_D2D1_1 1.13 +#include "moz-d2d1-1.h" 1.14 +#else 1.15 +#include <d2d1_1.h> 1.16 +#endif 1.17 + 1.18 +#include <vector> 1.19 + 1.20 +#include <dwrite.h> 1.21 +#include "2D.h" 1.22 +#include "Logging.h" 1.23 +#include "Tools.h" 1.24 +#include "ImageScaling.h" 1.25 + 1.26 +#include "ScaledFontDWrite.h" 1.27 + 1.28 +#undef min 1.29 +#undef max 1.30 + 1.31 +namespace mozilla { 1.32 +namespace gfx { 1.33 + 1.34 +ID2D1Factory* D2DFactory(); 1.35 + 1.36 +#ifdef USE_D2D1_1 1.37 +ID2D1Factory1* D2DFactory1(); 1.38 +#endif 1.39 + 1.40 +static inline D2D1_POINT_2F D2DPoint(const Point &aPoint) 1.41 +{ 1.42 + return D2D1::Point2F(aPoint.x, aPoint.y); 1.43 +} 1.44 + 1.45 +static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize) 1.46 +{ 1.47 + return D2D1::SizeU(aSize.width, aSize.height); 1.48 +} 1.49 + 1.50 +static inline D2D1_RECT_F D2DRect(const Rect &aRect) 1.51 +{ 1.52 + return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost()); 1.53 +} 1.54 + 1.55 +static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode) 1.56 +{ 1.57 + D2D1_EXTEND_MODE extend; 1.58 + switch (aExtendMode) { 1.59 + case ExtendMode::REPEAT: 1.60 + extend = D2D1_EXTEND_MODE_WRAP; 1.61 + break; 1.62 + case ExtendMode::REFLECT: 1.63 + extend = D2D1_EXTEND_MODE_MIRROR; 1.64 + break; 1.65 + default: 1.66 + extend = D2D1_EXTEND_MODE_CLAMP; 1.67 + } 1.68 + 1.69 + return extend; 1.70 +} 1.71 + 1.72 +static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter) 1.73 +{ 1.74 + switch (aFilter) { 1.75 + case Filter::POINT: 1.76 + return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; 1.77 + default: 1.78 + return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; 1.79 + } 1.80 +} 1.81 + 1.82 +#ifdef USE_D2D1_1 1.83 +static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter) 1.84 +{ 1.85 + switch (aFilter) { 1.86 + case Filter::POINT: 1.87 + return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; 1.88 + default: 1.89 + return D2D1_INTERPOLATION_MODE_LINEAR; 1.90 + } 1.91 +} 1.92 + 1.93 +static inline D2D1_MATRIX_5X4_F D2DMatrix5x4(const Matrix5x4 &aMatrix) 1.94 +{ 1.95 + return D2D1::Matrix5x4F(aMatrix._11, aMatrix._12, aMatrix._13, aMatrix._14, 1.96 + aMatrix._21, aMatrix._22, aMatrix._23, aMatrix._24, 1.97 + aMatrix._31, aMatrix._32, aMatrix._33, aMatrix._34, 1.98 + aMatrix._41, aMatrix._42, aMatrix._43, aMatrix._44, 1.99 + aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54); 1.100 +} 1.101 + 1.102 +static inline D2D1_VECTOR_3F D2DVector3D(const Point3D &aPoint) 1.103 +{ 1.104 + return D2D1::Vector3F(aPoint.x, aPoint.y, aPoint.z); 1.105 +} 1.106 + 1.107 +#endif 1.108 + 1.109 +static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode) 1.110 +{ 1.111 + switch (aMode) { 1.112 + case AntialiasMode::NONE: 1.113 + return D2D1_ANTIALIAS_MODE_ALIASED; 1.114 + default: 1.115 + return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; 1.116 + } 1.117 +} 1.118 + 1.119 +static inline D2D1_MATRIX_3X2_F D2DMatrix(const Matrix &aTransform) 1.120 +{ 1.121 + return D2D1::Matrix3x2F(aTransform._11, aTransform._12, 1.122 + aTransform._21, aTransform._22, 1.123 + aTransform._31, aTransform._32); 1.124 +} 1.125 + 1.126 +static inline D2D1_COLOR_F D2DColor(const Color &aColor) 1.127 +{ 1.128 + return D2D1::ColorF(aColor.r, aColor.g, aColor.b, aColor.a); 1.129 +} 1.130 + 1.131 +static inline IntSize ToIntSize(const D2D1_SIZE_U &aSize) 1.132 +{ 1.133 + return IntSize(aSize.width, aSize.height); 1.134 +} 1.135 + 1.136 +static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat) 1.137 +{ 1.138 + switch(aFormat.format) { 1.139 + case DXGI_FORMAT_A8_UNORM: 1.140 + return SurfaceFormat::A8; 1.141 + case DXGI_FORMAT_B8G8R8A8_UNORM: 1.142 + if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) { 1.143 + return SurfaceFormat::B8G8R8X8; 1.144 + } else { 1.145 + return SurfaceFormat::B8G8R8A8; 1.146 + } 1.147 + default: 1.148 + return SurfaceFormat::B8G8R8A8; 1.149 + } 1.150 +} 1.151 + 1.152 +static inline Rect ToRect(const D2D1_RECT_F &aRect) 1.153 +{ 1.154 + return Rect(aRect.left, aRect.top, aRect.right - aRect.left, aRect.bottom - aRect.top); 1.155 +} 1.156 + 1.157 +static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform) 1.158 +{ 1.159 + return Matrix(aTransform._11, aTransform._12, 1.160 + aTransform._21, aTransform._22, 1.161 + aTransform._31, aTransform._32); 1.162 +} 1.163 + 1.164 +static inline Point ToPoint(const D2D1_POINT_2F &aPoint) 1.165 +{ 1.166 + return Point(aPoint.x, aPoint.y); 1.167 +} 1.168 + 1.169 +static inline DXGI_FORMAT DXGIFormat(SurfaceFormat aFormat) 1.170 +{ 1.171 + switch (aFormat) { 1.172 + case SurfaceFormat::B8G8R8A8: 1.173 + return DXGI_FORMAT_B8G8R8A8_UNORM; 1.174 + case SurfaceFormat::B8G8R8X8: 1.175 + return DXGI_FORMAT_B8G8R8A8_UNORM; 1.176 + case SurfaceFormat::A8: 1.177 + return DXGI_FORMAT_A8_UNORM; 1.178 + default: 1.179 + return DXGI_FORMAT_UNKNOWN; 1.180 + } 1.181 +} 1.182 + 1.183 +static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat) 1.184 +{ 1.185 + switch (aFormat) { 1.186 + case SurfaceFormat::B8G8R8X8: 1.187 + return D2D1_ALPHA_MODE_IGNORE; 1.188 + default: 1.189 + return D2D1_ALPHA_MODE_PREMULTIPLIED; 1.190 + } 1.191 +} 1.192 + 1.193 +static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat) 1.194 +{ 1.195 + return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat)); 1.196 +} 1.197 + 1.198 +#ifdef USE_D2D1_1 1.199 +static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp) 1.200 +{ 1.201 + switch(aOp) { 1.202 + case CompositionOp::OP_OVER: 1.203 + return D2D1_COMPOSITE_MODE_SOURCE_OVER; 1.204 + case CompositionOp::OP_ADD: 1.205 + return D2D1_COMPOSITE_MODE_PLUS; 1.206 + case CompositionOp::OP_ATOP: 1.207 + return D2D1_COMPOSITE_MODE_SOURCE_ATOP; 1.208 + case CompositionOp::OP_OUT: 1.209 + return D2D1_COMPOSITE_MODE_SOURCE_OUT; 1.210 + case CompositionOp::OP_IN: 1.211 + return D2D1_COMPOSITE_MODE_SOURCE_IN; 1.212 + case CompositionOp::OP_SOURCE: 1.213 + return D2D1_COMPOSITE_MODE_SOURCE_COPY; 1.214 + case CompositionOp::OP_DEST_IN: 1.215 + return D2D1_COMPOSITE_MODE_DESTINATION_IN; 1.216 + case CompositionOp::OP_DEST_OUT: 1.217 + return D2D1_COMPOSITE_MODE_DESTINATION_OUT; 1.218 + case CompositionOp::OP_DEST_OVER: 1.219 + return D2D1_COMPOSITE_MODE_DESTINATION_OVER; 1.220 + case CompositionOp::OP_DEST_ATOP: 1.221 + return D2D1_COMPOSITE_MODE_DESTINATION_ATOP; 1.222 + case CompositionOp::OP_XOR: 1.223 + return D2D1_COMPOSITE_MODE_XOR; 1.224 + default: 1.225 + return D2D1_COMPOSITE_MODE_SOURCE_OVER; 1.226 + } 1.227 +} 1.228 +#endif 1.229 + 1.230 +static inline bool IsPatternSupportedByD2D(const Pattern &aPattern) 1.231 +{ 1.232 + if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) { 1.233 + return true; 1.234 + } 1.235 + 1.236 + const RadialGradientPattern *pat = 1.237 + static_cast<const RadialGradientPattern*>(&aPattern); 1.238 + 1.239 + if (pat->mRadius1 != 0) { 1.240 + return false; 1.241 + } 1.242 + 1.243 + Point diff = pat->mCenter2 - pat->mCenter1; 1.244 + 1.245 + if (sqrt(diff.x * diff.x + diff.y * diff.y) >= pat->mRadius2) { 1.246 + // Inner point lies outside the circle. 1.247 + return false; 1.248 + } 1.249 + 1.250 + return true; 1.251 +} 1.252 + 1.253 +/** 1.254 + * This structure is used to pass rectangles to our shader constant. We can use 1.255 + * this for passing rectangular areas to SetVertexShaderConstant. In the format 1.256 + * of a 4 component float(x,y,width,height). Our vertex shader can then use 1.257 + * this to construct rectangular positions from the 0,0-1,1 quad that we source 1.258 + * it with. 1.259 + */ 1.260 +struct ShaderConstantRectD3D10 1.261 +{ 1.262 + float mX, mY, mWidth, mHeight; 1.263 + ShaderConstantRectD3D10(float aX, float aY, float aWidth, float aHeight) 1.264 + : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight) 1.265 + { } 1.266 + 1.267 + // For easy passing to SetVertexShaderConstantF. 1.268 + operator float* () { return &mX; } 1.269 +}; 1.270 + 1.271 +static inline DWRITE_MATRIX 1.272 +DWriteMatrixFromMatrix(Matrix &aMatrix) 1.273 +{ 1.274 + DWRITE_MATRIX mat; 1.275 + mat.m11 = aMatrix._11; 1.276 + mat.m12 = aMatrix._12; 1.277 + mat.m21 = aMatrix._21; 1.278 + mat.m22 = aMatrix._22; 1.279 + mat.dx = aMatrix._31; 1.280 + mat.dy = aMatrix._32; 1.281 + return mat; 1.282 +} 1.283 + 1.284 +class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN 1.285 +{ 1.286 + static const unsigned kNumAutoGlyphs = 256; 1.287 + 1.288 +public: 1.289 + AutoDWriteGlyphRun() { 1.290 + glyphCount = 0; 1.291 + } 1.292 + 1.293 + ~AutoDWriteGlyphRun() { 1.294 + if (glyphCount > kNumAutoGlyphs) { 1.295 + delete[] glyphIndices; 1.296 + delete[] glyphAdvances; 1.297 + delete[] glyphOffsets; 1.298 + } 1.299 + } 1.300 + 1.301 + void allocate(unsigned aNumGlyphs) { 1.302 + glyphCount = aNumGlyphs; 1.303 + if (aNumGlyphs <= kNumAutoGlyphs) { 1.304 + glyphIndices = &mAutoIndices[0]; 1.305 + glyphAdvances = &mAutoAdvances[0]; 1.306 + glyphOffsets = &mAutoOffsets[0]; 1.307 + } else { 1.308 + glyphIndices = new UINT16[aNumGlyphs]; 1.309 + glyphAdvances = new FLOAT[aNumGlyphs]; 1.310 + glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs]; 1.311 + } 1.312 + } 1.313 + 1.314 +private: 1.315 + DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs]; 1.316 + FLOAT mAutoAdvances[kNumAutoGlyphs]; 1.317 + UINT16 mAutoIndices[kNumAutoGlyphs]; 1.318 +}; 1.319 + 1.320 +static inline void 1.321 +DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, AutoDWriteGlyphRun *run) 1.322 +{ 1.323 + run->allocate(aGlyphs.mNumGlyphs); 1.324 + 1.325 + FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances); 1.326 + UINT16 *indices = const_cast<UINT16*>(run->glyphIndices); 1.327 + DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets); 1.328 + 1.329 + memset(advances, 0, sizeof(FLOAT) * aGlyphs.mNumGlyphs); 1.330 + for (unsigned int i = 0; i < aGlyphs.mNumGlyphs; i++) { 1.331 + indices[i] = aGlyphs.mGlyphs[i].mIndex; 1.332 + offsets[i].advanceOffset = aGlyphs.mGlyphs[i].mPosition.x; 1.333 + offsets[i].ascenderOffset = -aGlyphs.mGlyphs[i].mPosition.y; 1.334 + } 1.335 + 1.336 + run->bidiLevel = 0; 1.337 + run->fontFace = aFont->mFontFace; 1.338 + run->fontEmSize = aFont->GetSize(); 1.339 + run->glyphCount = aGlyphs.mNumGlyphs; 1.340 + run->isSideways = FALSE; 1.341 +} 1.342 + 1.343 +static TemporaryRef<ID2D1Geometry> 1.344 +ConvertRectToGeometry(const D2D1_RECT_F& aRect) 1.345 +{ 1.346 + RefPtr<ID2D1RectangleGeometry> rectGeom; 1.347 + D2DFactory()->CreateRectangleGeometry(&aRect, byRef(rectGeom)); 1.348 + return rectGeom.forget(); 1.349 +} 1.350 + 1.351 +static TemporaryRef<ID2D1Geometry> 1.352 +GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) 1.353 +{ 1.354 + RefPtr<ID2D1PathGeometry> tmpGeometry; 1.355 + D2DFactory()->CreatePathGeometry(byRef(tmpGeometry)); 1.356 + RefPtr<ID2D1GeometrySink> currentSink; 1.357 + tmpGeometry->Open(byRef(currentSink)); 1.358 + aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, 1.359 + aTransform, currentSink); 1.360 + currentSink->Close(); 1.361 + return tmpGeometry; 1.362 +} 1.363 + 1.364 +static TemporaryRef<ID2D1Geometry> 1.365 +IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB) 1.366 +{ 1.367 + RefPtr<ID2D1PathGeometry> pathGeom; 1.368 + D2DFactory()->CreatePathGeometry(byRef(pathGeom)); 1.369 + RefPtr<ID2D1GeometrySink> sink; 1.370 + pathGeom->Open(byRef(sink)); 1.371 + aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink); 1.372 + sink->Close(); 1.373 + 1.374 + return pathGeom; 1.375 +} 1.376 + 1.377 +static TemporaryRef<ID2D1StrokeStyle> 1.378 +CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions) 1.379 +{ 1.380 + RefPtr<ID2D1StrokeStyle> style; 1.381 + 1.382 + D2D1_CAP_STYLE capStyle; 1.383 + D2D1_LINE_JOIN joinStyle; 1.384 + 1.385 + switch (aStrokeOptions.mLineCap) { 1.386 + case CapStyle::BUTT: 1.387 + capStyle = D2D1_CAP_STYLE_FLAT; 1.388 + break; 1.389 + case CapStyle::ROUND: 1.390 + capStyle = D2D1_CAP_STYLE_ROUND; 1.391 + break; 1.392 + case CapStyle::SQUARE: 1.393 + capStyle = D2D1_CAP_STYLE_SQUARE; 1.394 + break; 1.395 + } 1.396 + 1.397 + switch (aStrokeOptions.mLineJoin) { 1.398 + case JoinStyle::MITER: 1.399 + joinStyle = D2D1_LINE_JOIN_MITER; 1.400 + break; 1.401 + case JoinStyle::MITER_OR_BEVEL: 1.402 + joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL; 1.403 + break; 1.404 + case JoinStyle::ROUND: 1.405 + joinStyle = D2D1_LINE_JOIN_ROUND; 1.406 + break; 1.407 + case JoinStyle::BEVEL: 1.408 + joinStyle = D2D1_LINE_JOIN_BEVEL; 1.409 + break; 1.410 + } 1.411 + 1.412 + 1.413 + HRESULT hr; 1.414 + if (aStrokeOptions.mDashPattern) { 1.415 + typedef std::vector<Float> FloatVector; 1.416 + // D2D "helpfully" multiplies the dash pattern by the line width. 1.417 + // That's not what cairo does, or is what <canvas>'s dash wants. 1.418 + // So fix the multiplication in advance. 1.419 + Float lineWidth = aStrokeOptions.mLineWidth; 1.420 + FloatVector dash(aStrokeOptions.mDashPattern, 1.421 + aStrokeOptions.mDashPattern + aStrokeOptions.mDashLength); 1.422 + for (FloatVector::iterator it = dash.begin(); it != dash.end(); ++it) { 1.423 + *it /= lineWidth; 1.424 + } 1.425 + 1.426 + hr = D2DFactory()->CreateStrokeStyle( 1.427 + D2D1::StrokeStyleProperties(capStyle, capStyle, 1.428 + capStyle, joinStyle, 1.429 + aStrokeOptions.mMiterLimit, 1.430 + D2D1_DASH_STYLE_CUSTOM, 1.431 + aStrokeOptions.mDashOffset / lineWidth), 1.432 + &dash[0], // data() is not C++98, although it's in recent gcc 1.433 + // and VC10's STL 1.434 + dash.size(), 1.435 + byRef(style)); 1.436 + } else { 1.437 + hr = D2DFactory()->CreateStrokeStyle( 1.438 + D2D1::StrokeStyleProperties(capStyle, capStyle, 1.439 + capStyle, joinStyle, 1.440 + aStrokeOptions.mMiterLimit), 1.441 + nullptr, 0, byRef(style)); 1.442 + } 1.443 + 1.444 + if (FAILED(hr)) { 1.445 + gfxWarning() << "Failed to create Direct2D stroke style."; 1.446 + } 1.447 + 1.448 + return style; 1.449 +} 1.450 + 1.451 +// This creates a (partially) uploaded bitmap for a DataSourceSurface. It 1.452 +// uploads the minimum requirement and possibly downscales. It adjusts the 1.453 +// input Matrix to compensate. 1.454 +static TemporaryRef<ID2D1Bitmap> 1.455 +CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform, 1.456 + const IntSize &aDestinationSize, ExtendMode aExtendMode, 1.457 + Matrix &aSourceTransform, ID2D1RenderTarget *aRT) 1.458 +{ 1.459 + RefPtr<ID2D1Bitmap> bitmap; 1.460 + 1.461 + // This is where things get complicated. The source surface was 1.462 + // created for a surface that was too large to fit in a texture. 1.463 + // We'll need to figure out if we can work with a partial upload 1.464 + // or downsample in software. 1.465 + 1.466 + Matrix transform = aDestinationTransform; 1.467 + Matrix invTransform = transform = aSourceTransform * transform; 1.468 + if (!invTransform.Invert()) { 1.469 + // Singular transform, nothing to be drawn. 1.470 + return nullptr; 1.471 + } 1.472 + 1.473 + Rect rect(0, 0, Float(aDestinationSize.width), Float(aDestinationSize.height)); 1.474 + 1.475 + // Calculate the rectangle of the source mapped to our surface. 1.476 + rect = invTransform.TransformBounds(rect); 1.477 + rect.RoundOut(); 1.478 + 1.479 + IntSize size = aSurface->GetSize(); 1.480 + 1.481 + Rect uploadRect(0, 0, Float(size.width), Float(size.height)); 1.482 + 1.483 + // Limit the uploadRect as much as possible without supporting discontiguous uploads 1.484 + // 1.485 + // region we will paint from 1.486 + // uploadRect 1.487 + // .---------------. .---------------. resulting uploadRect 1.488 + // | |rect | | 1.489 + // | .---------. .----. .----. .---------------. 1.490 + // | | | ----> | | | | ----> | | 1.491 + // | '---------' '----' '----' '---------------' 1.492 + // '---------------' '---------------' 1.493 + // 1.494 + // 1.495 + 1.496 + if (uploadRect.Contains(rect)) { 1.497 + // Extend mode is irrelevant, the displayed rect is completely contained 1.498 + // by the source bitmap. 1.499 + uploadRect = rect; 1.500 + } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) { 1.501 + // Calculate the rectangle on the source bitmap that touches our 1.502 + // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee 1.503 + // correct behaviour in this case. 1.504 + uploadRect = uploadRect.Intersect(rect); 1.505 + 1.506 + // We now proceed to check if we can limit at least one dimension of the 1.507 + // upload rect safely without looking at extend mode. 1.508 + } else if (rect.x >= 0 && rect.XMost() < size.width) { 1.509 + uploadRect.x = rect.x; 1.510 + uploadRect.width = rect.width; 1.511 + } else if (rect.y >= 0 && rect.YMost() < size.height) { 1.512 + uploadRect.y = rect.y; 1.513 + uploadRect.height = rect.height; 1.514 + } 1.515 + 1.516 + 1.517 + int stride = aSurface->Stride(); 1.518 + 1.519 + if (uploadRect.width <= aRT->GetMaximumBitmapSize() && 1.520 + uploadRect.height <= aRT->GetMaximumBitmapSize()) { 1.521 + 1.522 + // A partial upload will suffice. 1.523 + aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)), 1.524 + aSurface->GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * stride, 1.525 + stride, 1.526 + D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())), 1.527 + byRef(bitmap)); 1.528 + 1.529 + aSourceTransform.Translate(uploadRect.x, uploadRect.y); 1.530 + 1.531 + return bitmap; 1.532 + } else { 1.533 + int Bpp = BytesPerPixel(aSurface->GetFormat()); 1.534 + 1.535 + if (Bpp != 4) { 1.536 + // This shouldn't actually happen in practice! 1.537 + MOZ_ASSERT(false); 1.538 + return nullptr; 1.539 + } 1.540 + 1.541 + ImageHalfScaler scaler(aSurface->GetData(), stride, size); 1.542 + 1.543 + // Calculate the maximum width/height of the image post transform. 1.544 + Point topRight = transform * Point(Float(size.width), 0); 1.545 + Point topLeft = transform * Point(0, 0); 1.546 + Point bottomRight = transform * Point(Float(size.width), Float(size.height)); 1.547 + Point bottomLeft = transform * Point(0, Float(size.height)); 1.548 + 1.549 + IntSize scaleSize; 1.550 + 1.551 + scaleSize.width = int32_t(std::max(Distance(topRight, topLeft), 1.552 + Distance(bottomRight, bottomLeft))); 1.553 + scaleSize.height = int32_t(std::max(Distance(topRight, bottomRight), 1.554 + Distance(topLeft, bottomLeft))); 1.555 + 1.556 + if (unsigned(scaleSize.width) > aRT->GetMaximumBitmapSize()) { 1.557 + // Ok, in this case we'd really want a downscale of a part of the bitmap, 1.558 + // perhaps we can do this later but for simplicity let's do something 1.559 + // different here and assume it's good enough, this should be rare! 1.560 + scaleSize.width = 4095; 1.561 + } 1.562 + if (unsigned(scaleSize.height) > aRT->GetMaximumBitmapSize()) { 1.563 + scaleSize.height = 4095; 1.564 + } 1.565 + 1.566 + scaler.ScaleForSize(scaleSize); 1.567 + 1.568 + IntSize newSize = scaler.GetSize(); 1.569 + 1.570 + aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height), 1.571 + scaler.GetScaledData(), scaler.GetStride(), 1.572 + D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())), 1.573 + byRef(bitmap)); 1.574 + 1.575 + aSourceTransform.Scale(Float(size.width / newSize.width), 1.576 + Float(size.height / newSize.height)); 1.577 + return bitmap; 1.578 + } 1.579 +} 1.580 + 1.581 +} 1.582 +} 1.583 + 1.584 +#endif /* MOZILLA_GFX_HELPERSD2D_H_ */