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: #include "gfxTypes.h" michael@0: #include "gfxPattern.h" michael@0: #include "gfxASurface.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "gfxGradientCache.h" michael@0: michael@0: #include "cairo.h" michael@0: michael@0: #include michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: gfxPattern::gfxPattern(cairo_pattern_t *aPattern) michael@0: : mGfxPattern(nullptr) michael@0: { michael@0: mPattern = cairo_pattern_reference(aPattern); michael@0: } michael@0: michael@0: gfxPattern::gfxPattern(const gfxRGBA& aColor) michael@0: : mGfxPattern(nullptr) michael@0: { michael@0: mPattern = cairo_pattern_create_rgba(aColor.r, aColor.g, aColor.b, aColor.a); michael@0: } michael@0: michael@0: // from another surface michael@0: gfxPattern::gfxPattern(gfxASurface *surface) michael@0: : mGfxPattern(nullptr) michael@0: { michael@0: mPattern = cairo_pattern_create_for_surface(surface->CairoSurface()); michael@0: } michael@0: michael@0: // linear michael@0: gfxPattern::gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1) michael@0: : mGfxPattern(nullptr) michael@0: { michael@0: mPattern = cairo_pattern_create_linear(x0, y0, x1, y1); michael@0: } michael@0: michael@0: // radial michael@0: gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0, michael@0: gfxFloat cx1, gfxFloat cy1, gfxFloat radius1) michael@0: : mGfxPattern(nullptr) michael@0: { michael@0: mPattern = cairo_pattern_create_radial(cx0, cy0, radius0, michael@0: cx1, cy1, radius1); michael@0: } michael@0: michael@0: // Azure michael@0: gfxPattern::gfxPattern(SourceSurface *aSurface, const Matrix &aTransform) michael@0: : mPattern(nullptr) michael@0: , mGfxPattern(nullptr) michael@0: , mSourceSurface(aSurface) michael@0: , mTransform(aTransform) michael@0: , mExtend(EXTEND_NONE) michael@0: { michael@0: } michael@0: michael@0: gfxPattern::~gfxPattern() michael@0: { michael@0: cairo_pattern_destroy(mPattern); michael@0: michael@0: if (mGfxPattern) { michael@0: mGfxPattern->~Pattern(); michael@0: } michael@0: } michael@0: michael@0: cairo_pattern_t * michael@0: gfxPattern::CairoPattern() michael@0: { michael@0: return mPattern; michael@0: } michael@0: michael@0: void michael@0: gfxPattern::AddColorStop(gfxFloat offset, const gfxRGBA& c) michael@0: { michael@0: if (mPattern) { michael@0: mStops = nullptr; michael@0: if (gfxPlatform::GetCMSMode() == eCMSMode_All) { michael@0: gfxRGBA cms; michael@0: qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); michael@0: if (transform) michael@0: gfxPlatform::TransformPixel(c, cms, transform); michael@0: michael@0: // Use the original alpha to avoid unnecessary float->byte->float michael@0: // conversion errors michael@0: cairo_pattern_add_color_stop_rgba(mPattern, offset, michael@0: cms.r, cms.g, cms.b, c.a); michael@0: } michael@0: else michael@0: cairo_pattern_add_color_stop_rgba(mPattern, offset, c.r, c.g, c.b, c.a); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxPattern::SetColorStops(mozilla::RefPtr aStops) michael@0: { michael@0: mStops = aStops; michael@0: } michael@0: michael@0: void michael@0: gfxPattern::CacheColorStops(mozilla::gfx::DrawTarget *aDT) michael@0: { michael@0: if (mPattern) { michael@0: mStops = nullptr; michael@0: nsTArray stops; michael@0: int count = 0; michael@0: cairo_pattern_get_color_stop_count(mPattern, &count); michael@0: stops.SetLength(count); michael@0: for (int n = 0; n < count; ++n) { michael@0: double offset, r, g, b, a; michael@0: cairo_pattern_get_color_stop_rgba(mPattern, n, &offset, &r, &g, &b, &a); michael@0: stops[n].color = mozilla::gfx::Color(r, g, b, a); michael@0: stops[n].offset = offset; michael@0: } michael@0: mStops = gfxGradientCache::GetOrCreateGradientStops(aDT, michael@0: stops, michael@0: (cairo_pattern_get_extend(mPattern) == CAIRO_EXTEND_REPEAT) michael@0: ? mozilla::gfx::ExtendMode::REPEAT michael@0: : mozilla::gfx::ExtendMode::CLAMP); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxPattern::SetMatrix(const gfxMatrix& matrix) michael@0: { michael@0: if (mPattern) { michael@0: cairo_matrix_t mat = *reinterpret_cast(&matrix); michael@0: cairo_pattern_set_matrix(mPattern, &mat); michael@0: } else { michael@0: mTransform = ToMatrix(matrix); michael@0: // Cairo-pattern matrices specify the conversion from DrawTarget to pattern michael@0: // space. Azure pattern matrices specify the conversion from pattern to michael@0: // DrawTarget space. michael@0: mTransform.Invert(); michael@0: } michael@0: } michael@0: michael@0: gfxMatrix michael@0: gfxPattern::GetMatrix() const michael@0: { michael@0: if (mPattern) { michael@0: cairo_matrix_t mat; michael@0: cairo_pattern_get_matrix(mPattern, &mat); michael@0: return gfxMatrix(*reinterpret_cast(&mat)); michael@0: } else { michael@0: // invert at the higher precision of gfxMatrix michael@0: // cause we need to convert at some point anyways michael@0: gfxMatrix mat = ThebesMatrix(mTransform); michael@0: mat.Invert(); michael@0: return mat; michael@0: } michael@0: } michael@0: michael@0: gfxMatrix michael@0: gfxPattern::GetInverseMatrix() const michael@0: { michael@0: if (mPattern) { michael@0: cairo_matrix_t mat; michael@0: cairo_pattern_get_matrix(mPattern, &mat); michael@0: cairo_matrix_invert(&mat); michael@0: return gfxMatrix(*reinterpret_cast(&mat)); michael@0: } else { michael@0: return ThebesMatrix(mTransform); michael@0: } michael@0: } michael@0: michael@0: Pattern* michael@0: gfxPattern::GetPattern(DrawTarget *aTarget, Matrix *aPatternTransform) michael@0: { michael@0: if (mGfxPattern) { michael@0: mGfxPattern->~Pattern(); michael@0: mGfxPattern = nullptr; michael@0: } michael@0: michael@0: if (!mPattern) { michael@0: Matrix adjustedMatrix = mTransform; michael@0: if (aPatternTransform) michael@0: AdjustTransformForPattern(adjustedMatrix, aTarget->GetTransform(), aPatternTransform); michael@0: mGfxPattern = new (mSurfacePattern.addr()) michael@0: SurfacePattern(mSourceSurface, ToExtendMode(mExtend), adjustedMatrix, mFilter); michael@0: return mGfxPattern; michael@0: } michael@0: michael@0: GraphicsExtend extend = (GraphicsExtend)cairo_pattern_get_extend(mPattern); michael@0: michael@0: switch (cairo_pattern_get_type(mPattern)) { michael@0: case CAIRO_PATTERN_TYPE_SOLID: michael@0: { michael@0: double r, g, b, a; michael@0: cairo_pattern_get_rgba(mPattern, &r, &g, &b, &a); michael@0: michael@0: new (mColorPattern.addr()) ColorPattern(Color(r, g, b, a)); michael@0: return mColorPattern.addr(); michael@0: } michael@0: case CAIRO_PATTERN_TYPE_SURFACE: michael@0: { michael@0: GraphicsFilter filter = (GraphicsFilter)cairo_pattern_get_filter(mPattern); michael@0: cairo_matrix_t mat; michael@0: cairo_pattern_get_matrix(mPattern, &mat); michael@0: gfxMatrix matrix(*reinterpret_cast(&mat)); michael@0: michael@0: cairo_surface_t *surf = nullptr; michael@0: cairo_pattern_get_surface(mPattern, &surf); michael@0: michael@0: if (!mSourceSurface) { michael@0: nsRefPtr gfxSurf = gfxASurface::Wrap(surf); michael@0: // The underlying surface here will be kept around by the gfxPattern. michael@0: // This function is intended to be used right away. michael@0: mSourceSurface = michael@0: gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTarget, gfxSurf); michael@0: } michael@0: michael@0: if (mSourceSurface) { michael@0: Matrix newMat = ToMatrix(matrix); michael@0: michael@0: AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); michael@0: michael@0: double x, y; michael@0: cairo_surface_get_device_offset(surf, &x, &y); michael@0: newMat.Translate(-x, -y); michael@0: mGfxPattern = new (mSurfacePattern.addr()) michael@0: SurfacePattern(mSourceSurface, ToExtendMode(extend), newMat, ToFilter(filter)); michael@0: return mGfxPattern; michael@0: } michael@0: break; michael@0: } michael@0: case CAIRO_PATTERN_TYPE_LINEAR: michael@0: { michael@0: double x1, y1, x2, y2; michael@0: cairo_pattern_get_linear_points(mPattern, &x1, &y1, &x2, &y2); michael@0: if (!mStops) { michael@0: int count = 0; michael@0: cairo_pattern_get_color_stop_count(mPattern, &count); michael@0: michael@0: std::vector stops; michael@0: michael@0: for (int i = 0; i < count; i++) { michael@0: GradientStop stop; michael@0: double r, g, b, a, offset; michael@0: cairo_pattern_get_color_stop_rgba(mPattern, i, &offset, &r, &g, &b, &a); michael@0: michael@0: stop.offset = offset; michael@0: stop.color = Color(Float(r), Float(g), Float(b), Float(a)); michael@0: stops.push_back(stop); michael@0: } michael@0: michael@0: mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend)); michael@0: } michael@0: michael@0: if (mStops) { michael@0: cairo_matrix_t mat; michael@0: cairo_pattern_get_matrix(mPattern, &mat); michael@0: gfxMatrix matrix(*reinterpret_cast(&mat)); michael@0: michael@0: Matrix newMat = ToMatrix(matrix); michael@0: michael@0: AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); michael@0: michael@0: mGfxPattern = new (mLinearGradientPattern.addr()) michael@0: LinearGradientPattern(Point(x1, y1), Point(x2, y2), mStops, newMat); michael@0: michael@0: return mGfxPattern; michael@0: } michael@0: break; michael@0: } michael@0: case CAIRO_PATTERN_TYPE_RADIAL: michael@0: { michael@0: if (!mStops) { michael@0: int count = 0; michael@0: cairo_pattern_get_color_stop_count(mPattern, &count); michael@0: michael@0: std::vector stops; michael@0: michael@0: for (int i = 0; i < count; i++) { michael@0: GradientStop stop; michael@0: double r, g, b, a, offset; michael@0: cairo_pattern_get_color_stop_rgba(mPattern, i, &offset, &r, &g, &b, &a); michael@0: michael@0: stop.offset = offset; michael@0: stop.color = Color(Float(r), Float(g), Float(b), Float(a)); michael@0: stops.push_back(stop); michael@0: } michael@0: michael@0: mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend)); michael@0: } michael@0: michael@0: if (mStops) { michael@0: cairo_matrix_t mat; michael@0: cairo_pattern_get_matrix(mPattern, &mat); michael@0: gfxMatrix matrix(*reinterpret_cast(&mat)); michael@0: michael@0: Matrix newMat = ToMatrix(matrix); michael@0: michael@0: AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); michael@0: michael@0: double x1, y1, x2, y2, r1, r2; michael@0: cairo_pattern_get_radial_circles(mPattern, &x1, &y1, &r1, &x2, &y2, &r2); michael@0: mGfxPattern = new (mRadialGradientPattern.addr()) michael@0: RadialGradientPattern(Point(x1, y1), Point(x2, y2), r1, r2, mStops, newMat); michael@0: michael@0: return mGfxPattern; michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: /* Reassure the compiler we are handling all the enum values. */ michael@0: break; michael@0: } michael@0: michael@0: new (mColorPattern.addr()) ColorPattern(Color(0, 0, 0, 0)); michael@0: return mColorPattern.addr(); michael@0: } michael@0: michael@0: void michael@0: gfxPattern::SetExtend(GraphicsExtend extend) michael@0: { michael@0: if (mPattern) { michael@0: mStops = nullptr; michael@0: if (extend == EXTEND_PAD_EDGE) { michael@0: if (cairo_pattern_get_type(mPattern) == CAIRO_PATTERN_TYPE_SURFACE) { michael@0: cairo_surface_t *surf = nullptr; michael@0: michael@0: cairo_pattern_get_surface (mPattern, &surf); michael@0: if (surf) { michael@0: switch (cairo_surface_get_type(surf)) { michael@0: case CAIRO_SURFACE_TYPE_WIN32_PRINTING: michael@0: case CAIRO_SURFACE_TYPE_QUARTZ: michael@0: extend = EXTEND_NONE; michael@0: break; michael@0: michael@0: case CAIRO_SURFACE_TYPE_WIN32: michael@0: case CAIRO_SURFACE_TYPE_XLIB: michael@0: default: michael@0: extend = EXTEND_PAD; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // if something went wrong, or not a surface pattern, use PAD michael@0: if (extend == EXTEND_PAD_EDGE) michael@0: extend = EXTEND_PAD; michael@0: } michael@0: michael@0: cairo_pattern_set_extend(mPattern, (cairo_extend_t)extend); michael@0: } else { michael@0: // This is always a surface pattern and will default to EXTEND_PAD michael@0: // for EXTEND_PAD_EDGE. michael@0: mExtend = extend; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: gfxPattern::IsOpaque() michael@0: { michael@0: if (mPattern) { michael@0: switch (cairo_pattern_get_type(mPattern)) { michael@0: case CAIRO_PATTERN_TYPE_SURFACE: michael@0: { michael@0: cairo_surface_t *surf = nullptr; michael@0: cairo_pattern_get_surface(mPattern, &surf); michael@0: michael@0: if (cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR) { michael@0: return true; michael@0: } michael@0: } michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (mSourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: gfxPattern::GraphicsExtend michael@0: gfxPattern::Extend() const michael@0: { michael@0: if (mPattern) { michael@0: return (GraphicsExtend)cairo_pattern_get_extend(mPattern); michael@0: } else { michael@0: return mExtend; michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxPattern::SetFilter(GraphicsFilter filter) michael@0: { michael@0: if (mPattern) { michael@0: cairo_pattern_set_filter(mPattern, (cairo_filter_t)(int)filter); michael@0: } else { michael@0: mFilter = ToFilter(filter); michael@0: } michael@0: } michael@0: michael@0: GraphicsFilter michael@0: gfxPattern::Filter() const michael@0: { michael@0: if (mPattern) { michael@0: return (GraphicsFilter)cairo_pattern_get_filter(mPattern); michael@0: } else { michael@0: return ThebesFilter(mFilter); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: gfxPattern::GetSolidColor(gfxRGBA& aColor) michael@0: { michael@0: return cairo_pattern_get_rgba(mPattern, michael@0: &aColor.r, michael@0: &aColor.g, michael@0: &aColor.b, michael@0: &aColor.a) == CAIRO_STATUS_SUCCESS; michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxPattern::GetSurface() michael@0: { michael@0: if (mPattern) { michael@0: cairo_surface_t *surf = nullptr; michael@0: michael@0: if (cairo_pattern_get_surface (mPattern, &surf) != CAIRO_STATUS_SUCCESS) michael@0: return nullptr; michael@0: michael@0: return gfxASurface::Wrap(surf); michael@0: } else { michael@0: // We should never be trying to get the surface off an Azure gfx Pattern. michael@0: NS_ERROR("Attempt to get surface off an Azure gfxPattern!"); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: gfxPattern::GraphicsPatternType michael@0: gfxPattern::GetType() const michael@0: { michael@0: if (mPattern) { michael@0: return (GraphicsPatternType) cairo_pattern_get_type(mPattern); michael@0: } else { michael@0: // We should never be trying to get the type off an Azure gfx Pattern. michael@0: MOZ_ASSERT(0); michael@0: return PATTERN_SURFACE; michael@0: } michael@0: } michael@0: michael@0: int michael@0: gfxPattern::CairoStatus() michael@0: { michael@0: if (mPattern) { michael@0: return cairo_pattern_status(mPattern); michael@0: } else { michael@0: // An Azure pattern as this point is never in error status. michael@0: return CAIRO_STATUS_SUCCESS; michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxPattern::AdjustTransformForPattern(Matrix &aPatternTransform, michael@0: const Matrix &aCurrentTransform, michael@0: const Matrix *aOriginalTransform) michael@0: { michael@0: aPatternTransform.Invert(); michael@0: if (!aOriginalTransform) { michael@0: // User space is unchanged, so to get from pattern space to user space, michael@0: // just invert the cairo matrix. michael@0: aPatternTransform.NudgeToIntegers(); michael@0: return; michael@0: } michael@0: // aPatternTransform now maps from pattern space to the user space defined michael@0: // by *aOriginalTransform. michael@0: michael@0: Matrix mat = aCurrentTransform; michael@0: mat.Invert(); michael@0: // mat maps from device space to current user space michael@0: michael@0: // First, transform from pattern space to original user space. Then transform michael@0: // from original user space to device space. Then transform from michael@0: // device space to current user space. michael@0: aPatternTransform = aPatternTransform * *aOriginalTransform * mat; michael@0: aPatternTransform.NudgeToIntegers(); michael@0: }