michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef MOZILLA_GFX_HELPERSD2D_H_ michael@0: #define MOZILLA_GFX_HELPERSD2D_H_ michael@0: michael@0: #ifndef USE_D2D1_1 michael@0: #include "moz-d2d1-1.h" michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: #include michael@0: #include "2D.h" michael@0: #include "Logging.h" michael@0: #include "Tools.h" michael@0: #include "ImageScaling.h" michael@0: michael@0: #include "ScaledFontDWrite.h" michael@0: michael@0: #undef min michael@0: #undef max michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: ID2D1Factory* D2DFactory(); michael@0: michael@0: #ifdef USE_D2D1_1 michael@0: ID2D1Factory1* D2DFactory1(); michael@0: #endif michael@0: michael@0: static inline D2D1_POINT_2F D2DPoint(const Point &aPoint) michael@0: { michael@0: return D2D1::Point2F(aPoint.x, aPoint.y); michael@0: } michael@0: michael@0: static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize) michael@0: { michael@0: return D2D1::SizeU(aSize.width, aSize.height); michael@0: } michael@0: michael@0: static inline D2D1_RECT_F D2DRect(const Rect &aRect) michael@0: { michael@0: return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost()); michael@0: } michael@0: michael@0: static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode) michael@0: { michael@0: D2D1_EXTEND_MODE extend; michael@0: switch (aExtendMode) { michael@0: case ExtendMode::REPEAT: michael@0: extend = D2D1_EXTEND_MODE_WRAP; michael@0: break; michael@0: case ExtendMode::REFLECT: michael@0: extend = D2D1_EXTEND_MODE_MIRROR; michael@0: break; michael@0: default: michael@0: extend = D2D1_EXTEND_MODE_CLAMP; michael@0: } michael@0: michael@0: return extend; michael@0: } michael@0: michael@0: static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter) michael@0: { michael@0: switch (aFilter) { michael@0: case Filter::POINT: michael@0: return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; michael@0: default: michael@0: return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; michael@0: } michael@0: } michael@0: michael@0: #ifdef USE_D2D1_1 michael@0: static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter) michael@0: { michael@0: switch (aFilter) { michael@0: case Filter::POINT: michael@0: return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; michael@0: default: michael@0: return D2D1_INTERPOLATION_MODE_LINEAR; michael@0: } michael@0: } michael@0: michael@0: static inline D2D1_MATRIX_5X4_F D2DMatrix5x4(const Matrix5x4 &aMatrix) michael@0: { michael@0: return D2D1::Matrix5x4F(aMatrix._11, aMatrix._12, aMatrix._13, aMatrix._14, michael@0: aMatrix._21, aMatrix._22, aMatrix._23, aMatrix._24, michael@0: aMatrix._31, aMatrix._32, aMatrix._33, aMatrix._34, michael@0: aMatrix._41, aMatrix._42, aMatrix._43, aMatrix._44, michael@0: aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54); michael@0: } michael@0: michael@0: static inline D2D1_VECTOR_3F D2DVector3D(const Point3D &aPoint) michael@0: { michael@0: return D2D1::Vector3F(aPoint.x, aPoint.y, aPoint.z); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode) michael@0: { michael@0: switch (aMode) { michael@0: case AntialiasMode::NONE: michael@0: return D2D1_ANTIALIAS_MODE_ALIASED; michael@0: default: michael@0: return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; michael@0: } michael@0: } michael@0: michael@0: static inline D2D1_MATRIX_3X2_F D2DMatrix(const Matrix &aTransform) michael@0: { michael@0: return D2D1::Matrix3x2F(aTransform._11, aTransform._12, michael@0: aTransform._21, aTransform._22, michael@0: aTransform._31, aTransform._32); michael@0: } michael@0: michael@0: static inline D2D1_COLOR_F D2DColor(const Color &aColor) michael@0: { michael@0: return D2D1::ColorF(aColor.r, aColor.g, aColor.b, aColor.a); michael@0: } michael@0: michael@0: static inline IntSize ToIntSize(const D2D1_SIZE_U &aSize) michael@0: { michael@0: return IntSize(aSize.width, aSize.height); michael@0: } michael@0: michael@0: static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat) michael@0: { michael@0: switch(aFormat.format) { michael@0: case DXGI_FORMAT_A8_UNORM: michael@0: return SurfaceFormat::A8; michael@0: case DXGI_FORMAT_B8G8R8A8_UNORM: michael@0: if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) { michael@0: return SurfaceFormat::B8G8R8X8; michael@0: } else { michael@0: return SurfaceFormat::B8G8R8A8; michael@0: } michael@0: default: michael@0: return SurfaceFormat::B8G8R8A8; michael@0: } michael@0: } michael@0: michael@0: static inline Rect ToRect(const D2D1_RECT_F &aRect) michael@0: { michael@0: return Rect(aRect.left, aRect.top, aRect.right - aRect.left, aRect.bottom - aRect.top); michael@0: } michael@0: michael@0: static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform) michael@0: { michael@0: return Matrix(aTransform._11, aTransform._12, michael@0: aTransform._21, aTransform._22, michael@0: aTransform._31, aTransform._32); michael@0: } michael@0: michael@0: static inline Point ToPoint(const D2D1_POINT_2F &aPoint) michael@0: { michael@0: return Point(aPoint.x, aPoint.y); michael@0: } michael@0: michael@0: static inline DXGI_FORMAT DXGIFormat(SurfaceFormat aFormat) michael@0: { michael@0: switch (aFormat) { michael@0: case SurfaceFormat::B8G8R8A8: michael@0: return DXGI_FORMAT_B8G8R8A8_UNORM; michael@0: case SurfaceFormat::B8G8R8X8: michael@0: return DXGI_FORMAT_B8G8R8A8_UNORM; michael@0: case SurfaceFormat::A8: michael@0: return DXGI_FORMAT_A8_UNORM; michael@0: default: michael@0: return DXGI_FORMAT_UNKNOWN; michael@0: } michael@0: } michael@0: michael@0: static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat) michael@0: { michael@0: switch (aFormat) { michael@0: case SurfaceFormat::B8G8R8X8: michael@0: return D2D1_ALPHA_MODE_IGNORE; michael@0: default: michael@0: return D2D1_ALPHA_MODE_PREMULTIPLIED; michael@0: } michael@0: } michael@0: michael@0: static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat) michael@0: { michael@0: return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat)); michael@0: } michael@0: michael@0: #ifdef USE_D2D1_1 michael@0: static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp) michael@0: { michael@0: switch(aOp) { michael@0: case CompositionOp::OP_OVER: michael@0: return D2D1_COMPOSITE_MODE_SOURCE_OVER; michael@0: case CompositionOp::OP_ADD: michael@0: return D2D1_COMPOSITE_MODE_PLUS; michael@0: case CompositionOp::OP_ATOP: michael@0: return D2D1_COMPOSITE_MODE_SOURCE_ATOP; michael@0: case CompositionOp::OP_OUT: michael@0: return D2D1_COMPOSITE_MODE_SOURCE_OUT; michael@0: case CompositionOp::OP_IN: michael@0: return D2D1_COMPOSITE_MODE_SOURCE_IN; michael@0: case CompositionOp::OP_SOURCE: michael@0: return D2D1_COMPOSITE_MODE_SOURCE_COPY; michael@0: case CompositionOp::OP_DEST_IN: michael@0: return D2D1_COMPOSITE_MODE_DESTINATION_IN; michael@0: case CompositionOp::OP_DEST_OUT: michael@0: return D2D1_COMPOSITE_MODE_DESTINATION_OUT; michael@0: case CompositionOp::OP_DEST_OVER: michael@0: return D2D1_COMPOSITE_MODE_DESTINATION_OVER; michael@0: case CompositionOp::OP_DEST_ATOP: michael@0: return D2D1_COMPOSITE_MODE_DESTINATION_ATOP; michael@0: case CompositionOp::OP_XOR: michael@0: return D2D1_COMPOSITE_MODE_XOR; michael@0: default: michael@0: return D2D1_COMPOSITE_MODE_SOURCE_OVER; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: static inline bool IsPatternSupportedByD2D(const Pattern &aPattern) michael@0: { michael@0: if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) { michael@0: return true; michael@0: } michael@0: michael@0: const RadialGradientPattern *pat = michael@0: static_cast(&aPattern); michael@0: michael@0: if (pat->mRadius1 != 0) { michael@0: return false; michael@0: } michael@0: michael@0: Point diff = pat->mCenter2 - pat->mCenter1; michael@0: michael@0: if (sqrt(diff.x * diff.x + diff.y * diff.y) >= pat->mRadius2) { michael@0: // Inner point lies outside the circle. michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * This structure is used to pass rectangles to our shader constant. We can use michael@0: * this for passing rectangular areas to SetVertexShaderConstant. In the format michael@0: * of a 4 component float(x,y,width,height). Our vertex shader can then use michael@0: * this to construct rectangular positions from the 0,0-1,1 quad that we source michael@0: * it with. michael@0: */ michael@0: struct ShaderConstantRectD3D10 michael@0: { michael@0: float mX, mY, mWidth, mHeight; michael@0: ShaderConstantRectD3D10(float aX, float aY, float aWidth, float aHeight) michael@0: : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight) michael@0: { } michael@0: michael@0: // For easy passing to SetVertexShaderConstantF. michael@0: operator float* () { return &mX; } michael@0: }; michael@0: michael@0: static inline DWRITE_MATRIX michael@0: DWriteMatrixFromMatrix(Matrix &aMatrix) michael@0: { michael@0: DWRITE_MATRIX mat; michael@0: mat.m11 = aMatrix._11; michael@0: mat.m12 = aMatrix._12; michael@0: mat.m21 = aMatrix._21; michael@0: mat.m22 = aMatrix._22; michael@0: mat.dx = aMatrix._31; michael@0: mat.dy = aMatrix._32; michael@0: return mat; michael@0: } michael@0: michael@0: class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN michael@0: { michael@0: static const unsigned kNumAutoGlyphs = 256; michael@0: michael@0: public: michael@0: AutoDWriteGlyphRun() { michael@0: glyphCount = 0; michael@0: } michael@0: michael@0: ~AutoDWriteGlyphRun() { michael@0: if (glyphCount > kNumAutoGlyphs) { michael@0: delete[] glyphIndices; michael@0: delete[] glyphAdvances; michael@0: delete[] glyphOffsets; michael@0: } michael@0: } michael@0: michael@0: void allocate(unsigned aNumGlyphs) { michael@0: glyphCount = aNumGlyphs; michael@0: if (aNumGlyphs <= kNumAutoGlyphs) { michael@0: glyphIndices = &mAutoIndices[0]; michael@0: glyphAdvances = &mAutoAdvances[0]; michael@0: glyphOffsets = &mAutoOffsets[0]; michael@0: } else { michael@0: glyphIndices = new UINT16[aNumGlyphs]; michael@0: glyphAdvances = new FLOAT[aNumGlyphs]; michael@0: glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs]; michael@0: } michael@0: } michael@0: michael@0: private: michael@0: DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs]; michael@0: FLOAT mAutoAdvances[kNumAutoGlyphs]; michael@0: UINT16 mAutoIndices[kNumAutoGlyphs]; michael@0: }; michael@0: michael@0: static inline void michael@0: DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, AutoDWriteGlyphRun *run) michael@0: { michael@0: run->allocate(aGlyphs.mNumGlyphs); michael@0: michael@0: FLOAT *advances = const_cast(run->glyphAdvances); michael@0: UINT16 *indices = const_cast(run->glyphIndices); michael@0: DWRITE_GLYPH_OFFSET *offsets = const_cast(run->glyphOffsets); michael@0: michael@0: memset(advances, 0, sizeof(FLOAT) * aGlyphs.mNumGlyphs); michael@0: for (unsigned int i = 0; i < aGlyphs.mNumGlyphs; i++) { michael@0: indices[i] = aGlyphs.mGlyphs[i].mIndex; michael@0: offsets[i].advanceOffset = aGlyphs.mGlyphs[i].mPosition.x; michael@0: offsets[i].ascenderOffset = -aGlyphs.mGlyphs[i].mPosition.y; michael@0: } michael@0: michael@0: run->bidiLevel = 0; michael@0: run->fontFace = aFont->mFontFace; michael@0: run->fontEmSize = aFont->GetSize(); michael@0: run->glyphCount = aGlyphs.mNumGlyphs; michael@0: run->isSideways = FALSE; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: ConvertRectToGeometry(const D2D1_RECT_F& aRect) michael@0: { michael@0: RefPtr rectGeom; michael@0: D2DFactory()->CreateRectangleGeometry(&aRect, byRef(rectGeom)); michael@0: return rectGeom.forget(); michael@0: } michael@0: michael@0: static TemporaryRef michael@0: GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) michael@0: { michael@0: RefPtr tmpGeometry; michael@0: D2DFactory()->CreatePathGeometry(byRef(tmpGeometry)); michael@0: RefPtr currentSink; michael@0: tmpGeometry->Open(byRef(currentSink)); michael@0: aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, michael@0: aTransform, currentSink); michael@0: currentSink->Close(); michael@0: return tmpGeometry; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB) michael@0: { michael@0: RefPtr pathGeom; michael@0: D2DFactory()->CreatePathGeometry(byRef(pathGeom)); michael@0: RefPtr sink; michael@0: pathGeom->Open(byRef(sink)); michael@0: aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink); michael@0: sink->Close(); michael@0: michael@0: return pathGeom; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions) michael@0: { michael@0: RefPtr style; michael@0: michael@0: D2D1_CAP_STYLE capStyle; michael@0: D2D1_LINE_JOIN joinStyle; michael@0: michael@0: switch (aStrokeOptions.mLineCap) { michael@0: case CapStyle::BUTT: michael@0: capStyle = D2D1_CAP_STYLE_FLAT; michael@0: break; michael@0: case CapStyle::ROUND: michael@0: capStyle = D2D1_CAP_STYLE_ROUND; michael@0: break; michael@0: case CapStyle::SQUARE: michael@0: capStyle = D2D1_CAP_STYLE_SQUARE; michael@0: break; michael@0: } michael@0: michael@0: switch (aStrokeOptions.mLineJoin) { michael@0: case JoinStyle::MITER: michael@0: joinStyle = D2D1_LINE_JOIN_MITER; michael@0: break; michael@0: case JoinStyle::MITER_OR_BEVEL: michael@0: joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL; michael@0: break; michael@0: case JoinStyle::ROUND: michael@0: joinStyle = D2D1_LINE_JOIN_ROUND; michael@0: break; michael@0: case JoinStyle::BEVEL: michael@0: joinStyle = D2D1_LINE_JOIN_BEVEL; michael@0: break; michael@0: } michael@0: michael@0: michael@0: HRESULT hr; michael@0: if (aStrokeOptions.mDashPattern) { michael@0: typedef std::vector FloatVector; michael@0: // D2D "helpfully" multiplies the dash pattern by the line width. michael@0: // That's not what cairo does, or is what 's dash wants. michael@0: // So fix the multiplication in advance. michael@0: Float lineWidth = aStrokeOptions.mLineWidth; michael@0: FloatVector dash(aStrokeOptions.mDashPattern, michael@0: aStrokeOptions.mDashPattern + aStrokeOptions.mDashLength); michael@0: for (FloatVector::iterator it = dash.begin(); it != dash.end(); ++it) { michael@0: *it /= lineWidth; michael@0: } michael@0: michael@0: hr = D2DFactory()->CreateStrokeStyle( michael@0: D2D1::StrokeStyleProperties(capStyle, capStyle, michael@0: capStyle, joinStyle, michael@0: aStrokeOptions.mMiterLimit, michael@0: D2D1_DASH_STYLE_CUSTOM, michael@0: aStrokeOptions.mDashOffset / lineWidth), michael@0: &dash[0], // data() is not C++98, although it's in recent gcc michael@0: // and VC10's STL michael@0: dash.size(), michael@0: byRef(style)); michael@0: } else { michael@0: hr = D2DFactory()->CreateStrokeStyle( michael@0: D2D1::StrokeStyleProperties(capStyle, capStyle, michael@0: capStyle, joinStyle, michael@0: aStrokeOptions.mMiterLimit), michael@0: nullptr, 0, byRef(style)); michael@0: } michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create Direct2D stroke style."; michael@0: } michael@0: michael@0: return style; michael@0: } michael@0: michael@0: // This creates a (partially) uploaded bitmap for a DataSourceSurface. It michael@0: // uploads the minimum requirement and possibly downscales. It adjusts the michael@0: // input Matrix to compensate. michael@0: static TemporaryRef michael@0: CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform, michael@0: const IntSize &aDestinationSize, ExtendMode aExtendMode, michael@0: Matrix &aSourceTransform, ID2D1RenderTarget *aRT) michael@0: { michael@0: RefPtr bitmap; michael@0: michael@0: // This is where things get complicated. The source surface was michael@0: // created for a surface that was too large to fit in a texture. michael@0: // We'll need to figure out if we can work with a partial upload michael@0: // or downsample in software. michael@0: michael@0: Matrix transform = aDestinationTransform; michael@0: Matrix invTransform = transform = aSourceTransform * transform; michael@0: if (!invTransform.Invert()) { michael@0: // Singular transform, nothing to be drawn. michael@0: return nullptr; michael@0: } michael@0: michael@0: Rect rect(0, 0, Float(aDestinationSize.width), Float(aDestinationSize.height)); michael@0: michael@0: // Calculate the rectangle of the source mapped to our surface. michael@0: rect = invTransform.TransformBounds(rect); michael@0: rect.RoundOut(); michael@0: michael@0: IntSize size = aSurface->GetSize(); michael@0: michael@0: Rect uploadRect(0, 0, Float(size.width), Float(size.height)); michael@0: michael@0: // Limit the uploadRect as much as possible without supporting discontiguous uploads michael@0: // michael@0: // region we will paint from michael@0: // uploadRect michael@0: // .---------------. .---------------. resulting uploadRect michael@0: // | |rect | | michael@0: // | .---------. .----. .----. .---------------. michael@0: // | | | ----> | | | | ----> | | michael@0: // | '---------' '----' '----' '---------------' michael@0: // '---------------' '---------------' michael@0: // michael@0: // michael@0: michael@0: if (uploadRect.Contains(rect)) { michael@0: // Extend mode is irrelevant, the displayed rect is completely contained michael@0: // by the source bitmap. michael@0: uploadRect = rect; michael@0: } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) { michael@0: // Calculate the rectangle on the source bitmap that touches our michael@0: // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee michael@0: // correct behaviour in this case. michael@0: uploadRect = uploadRect.Intersect(rect); michael@0: michael@0: // We now proceed to check if we can limit at least one dimension of the michael@0: // upload rect safely without looking at extend mode. michael@0: } else if (rect.x >= 0 && rect.XMost() < size.width) { michael@0: uploadRect.x = rect.x; michael@0: uploadRect.width = rect.width; michael@0: } else if (rect.y >= 0 && rect.YMost() < size.height) { michael@0: uploadRect.y = rect.y; michael@0: uploadRect.height = rect.height; michael@0: } michael@0: michael@0: michael@0: int stride = aSurface->Stride(); michael@0: michael@0: if (uploadRect.width <= aRT->GetMaximumBitmapSize() && michael@0: uploadRect.height <= aRT->GetMaximumBitmapSize()) { michael@0: michael@0: // A partial upload will suffice. michael@0: aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)), michael@0: aSurface->GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * stride, michael@0: stride, michael@0: D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())), michael@0: byRef(bitmap)); michael@0: michael@0: aSourceTransform.Translate(uploadRect.x, uploadRect.y); michael@0: michael@0: return bitmap; michael@0: } else { michael@0: int Bpp = BytesPerPixel(aSurface->GetFormat()); michael@0: michael@0: if (Bpp != 4) { michael@0: // This shouldn't actually happen in practice! michael@0: MOZ_ASSERT(false); michael@0: return nullptr; michael@0: } michael@0: michael@0: ImageHalfScaler scaler(aSurface->GetData(), stride, size); michael@0: michael@0: // Calculate the maximum width/height of the image post transform. michael@0: Point topRight = transform * Point(Float(size.width), 0); michael@0: Point topLeft = transform * Point(0, 0); michael@0: Point bottomRight = transform * Point(Float(size.width), Float(size.height)); michael@0: Point bottomLeft = transform * Point(0, Float(size.height)); michael@0: michael@0: IntSize scaleSize; michael@0: michael@0: scaleSize.width = int32_t(std::max(Distance(topRight, topLeft), michael@0: Distance(bottomRight, bottomLeft))); michael@0: scaleSize.height = int32_t(std::max(Distance(topRight, bottomRight), michael@0: Distance(topLeft, bottomLeft))); michael@0: michael@0: if (unsigned(scaleSize.width) > aRT->GetMaximumBitmapSize()) { michael@0: // Ok, in this case we'd really want a downscale of a part of the bitmap, michael@0: // perhaps we can do this later but for simplicity let's do something michael@0: // different here and assume it's good enough, this should be rare! michael@0: scaleSize.width = 4095; michael@0: } michael@0: if (unsigned(scaleSize.height) > aRT->GetMaximumBitmapSize()) { michael@0: scaleSize.height = 4095; michael@0: } michael@0: michael@0: scaler.ScaleForSize(scaleSize); michael@0: michael@0: IntSize newSize = scaler.GetSize(); michael@0: michael@0: aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height), michael@0: scaler.GetScaledData(), scaler.GetStride(), michael@0: D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())), michael@0: byRef(bitmap)); michael@0: michael@0: aSourceTransform.Scale(Float(size.width / newSize.width), michael@0: Float(size.height / newSize.height)); michael@0: return bitmap; michael@0: } michael@0: } michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* MOZILLA_GFX_HELPERSD2D_H_ */