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 "DrawTargetCairo.h" michael@0: michael@0: #include "SourceSurfaceCairo.h" michael@0: #include "PathCairo.h" michael@0: #include "HelpersCairo.h" michael@0: #include "ScaledFontBase.h" michael@0: #include "BorrowedContext.h" michael@0: #include "FilterNodeSoftware.h" michael@0: michael@0: #include "cairo.h" michael@0: #include "cairo-tee.h" michael@0: #include michael@0: michael@0: #include "Blur.h" michael@0: #include "Logging.h" michael@0: #include "Tools.h" michael@0: michael@0: #ifdef CAIRO_HAS_QUARTZ_SURFACE michael@0: #include "cairo-quartz.h" michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef CAIRO_HAS_XLIB_SURFACE michael@0: #include "cairo-xlib.h" michael@0: #endif michael@0: michael@0: #ifdef CAIRO_HAS_WIN32_SURFACE michael@0: #include "cairo-win32.h" michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: cairo_surface_t *DrawTargetCairo::mDummySurface; michael@0: michael@0: namespace { michael@0: michael@0: // An RAII class to prepare to draw a context and optional path. Saves and michael@0: // restores the context on construction/destruction. michael@0: class AutoPrepareForDrawing michael@0: { michael@0: public: michael@0: AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx) michael@0: : mCtx(ctx) michael@0: { michael@0: dt->PrepareForDrawing(ctx); michael@0: cairo_save(mCtx); michael@0: MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform()); michael@0: } michael@0: michael@0: AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path) michael@0: : mCtx(ctx) michael@0: { michael@0: dt->PrepareForDrawing(ctx, path); michael@0: cairo_save(mCtx); michael@0: MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform()); michael@0: } michael@0: michael@0: ~AutoPrepareForDrawing() { cairo_restore(mCtx); } michael@0: michael@0: private: michael@0: #ifdef DEBUG michael@0: Matrix GetTransform() michael@0: { michael@0: cairo_matrix_t mat; michael@0: cairo_get_matrix(mCtx, &mat); michael@0: return Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0); michael@0: } michael@0: #endif michael@0: michael@0: cairo_t* mCtx; michael@0: }; michael@0: michael@0: michael@0: } // end anonymous namespace michael@0: michael@0: static bool michael@0: SupportsSelfCopy(cairo_surface_t* surface) michael@0: { michael@0: switch (cairo_surface_get_type(surface)) michael@0: { michael@0: #ifdef CAIRO_HAS_QUARTZ_SURFACE michael@0: case CAIRO_SURFACE_TYPE_QUARTZ: michael@0: return true; michael@0: #endif michael@0: #ifdef CAIRO_HAS_WIN32_SURFACE michael@0: case CAIRO_SURFACE_TYPE_WIN32: michael@0: case CAIRO_SURFACE_TYPE_WIN32_PRINTING: michael@0: return true; michael@0: #endif michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: PatternIsCompatible(const Pattern& aPattern) michael@0: { michael@0: switch (aPattern.GetType()) michael@0: { michael@0: case PatternType::LINEAR_GRADIENT: michael@0: { michael@0: const LinearGradientPattern& pattern = static_cast(aPattern); michael@0: return pattern.mStops->GetBackendType() == BackendType::CAIRO; michael@0: } michael@0: case PatternType::RADIAL_GRADIENT: michael@0: { michael@0: const RadialGradientPattern& pattern = static_cast(aPattern); michael@0: return pattern.mStops->GetBackendType() == BackendType::CAIRO; michael@0: } michael@0: default: michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: static cairo_user_data_key_t surfaceDataKey; michael@0: michael@0: void michael@0: ReleaseData(void* aData) michael@0: { michael@0: static_cast(aData)->Release(); michael@0: } michael@0: michael@0: /** michael@0: * Returns cairo surface for the given SourceSurface. michael@0: * If possible, it will use the cairo_surface associated with aSurface, michael@0: * otherwise, it will create a new cairo_surface. michael@0: * In either case, the caller must call cairo_surface_destroy on the michael@0: * result when it is done with it. michael@0: */ michael@0: cairo_surface_t* michael@0: GetCairoSurfaceForSourceSurface(SourceSurface *aSurface, bool aExistingOnly = false) michael@0: { michael@0: if (aSurface->GetType() == SurfaceType::CAIRO) { michael@0: cairo_surface_t* surf = static_cast(aSurface)->GetSurface(); michael@0: cairo_surface_reference(surf); michael@0: return surf; michael@0: } michael@0: michael@0: if (aSurface->GetType() == SurfaceType::CAIRO_IMAGE) { michael@0: cairo_surface_t* surf = michael@0: static_cast(aSurface)->GetSurface(); michael@0: cairo_surface_reference(surf); michael@0: return surf; michael@0: } michael@0: michael@0: if (aExistingOnly) { michael@0: return nullptr; michael@0: } michael@0: michael@0: RefPtr data = aSurface->GetDataSurface(); michael@0: if (!data) { michael@0: return nullptr; michael@0: } michael@0: michael@0: cairo_surface_t* surf = michael@0: cairo_image_surface_create_for_data(data->GetData(), michael@0: GfxFormatToCairoFormat(data->GetFormat()), michael@0: data->GetSize().width, michael@0: data->GetSize().height, michael@0: data->Stride()); michael@0: michael@0: // In certain scenarios, requesting larger than 8k image fails. Bug 803568 michael@0: // covers the details of how to run into it, but the full detailed michael@0: // investigation hasn't been done to determine the underlying cause. We michael@0: // will just handle the failure to allocate the surface to avoid a crash. michael@0: if (cairo_surface_status(surf)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: cairo_surface_set_user_data(surf, michael@0: &surfaceDataKey, michael@0: data.forget().drop(), michael@0: ReleaseData); michael@0: return surf; michael@0: } michael@0: michael@0: // An RAII class to temporarily clear any device offset set michael@0: // on a surface. Note that this does not take a reference to the michael@0: // surface. michael@0: class AutoClearDeviceOffset michael@0: { michael@0: public: michael@0: AutoClearDeviceOffset(SourceSurface* aSurface) michael@0: : mSurface(nullptr) michael@0: { michael@0: Init(aSurface); michael@0: } michael@0: michael@0: AutoClearDeviceOffset(const Pattern& aPattern) michael@0: : mSurface(nullptr) michael@0: { michael@0: if (aPattern.GetType() == PatternType::SURFACE) { michael@0: const SurfacePattern& pattern = static_cast(aPattern); michael@0: Init(pattern.mSurface); michael@0: } michael@0: } michael@0: michael@0: ~AutoClearDeviceOffset() michael@0: { michael@0: if (mSurface) { michael@0: cairo_surface_set_device_offset(mSurface, mX, mY); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: void Init(SourceSurface* aSurface) michael@0: { michael@0: cairo_surface_t* surface = GetCairoSurfaceForSourceSurface(aSurface, true); michael@0: if (surface) { michael@0: Init(surface); michael@0: cairo_surface_destroy(surface); michael@0: } michael@0: } michael@0: michael@0: void Init(cairo_surface_t *aSurface) michael@0: { michael@0: mSurface = aSurface; michael@0: cairo_surface_get_device_offset(mSurface, &mX, &mY); michael@0: cairo_surface_set_device_offset(mSurface, 0, 0); michael@0: } michael@0: michael@0: cairo_surface_t* mSurface; michael@0: double mX; michael@0: double mY; michael@0: }; michael@0: michael@0: // Never returns nullptr. As such, you must always pass in Cairo-compatible michael@0: // patterns, most notably gradients with a GradientStopCairo. michael@0: // The pattern returned must have cairo_pattern_destroy() called on it by the michael@0: // caller. michael@0: // As the cairo_pattern_t returned may depend on the Pattern passed in, the michael@0: // lifetime of the cairo_pattern_t returned must not exceed the lifetime of the michael@0: // Pattern passed in. michael@0: static cairo_pattern_t* michael@0: GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha) michael@0: { michael@0: cairo_pattern_t* pat; michael@0: const Matrix* matrix = nullptr; michael@0: michael@0: switch (aPattern.GetType()) michael@0: { michael@0: case PatternType::COLOR: michael@0: { michael@0: Color color = static_cast(aPattern).mColor; michael@0: pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha); michael@0: break; michael@0: } michael@0: michael@0: case PatternType::SURFACE: michael@0: { michael@0: const SurfacePattern& pattern = static_cast(aPattern); michael@0: cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface); michael@0: if (!surf) michael@0: return nullptr; michael@0: michael@0: pat = cairo_pattern_create_for_surface(surf); michael@0: michael@0: matrix = &pattern.mMatrix; michael@0: michael@0: cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(pattern.mFilter)); michael@0: cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode)); michael@0: michael@0: cairo_surface_destroy(surf); michael@0: break; michael@0: } michael@0: case PatternType::LINEAR_GRADIENT: michael@0: { michael@0: const LinearGradientPattern& pattern = static_cast(aPattern); michael@0: michael@0: pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y, michael@0: pattern.mEnd.x, pattern.mEnd.y); michael@0: michael@0: MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO); michael@0: GradientStopsCairo* cairoStops = static_cast(pattern.mStops.get()); michael@0: cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode())); michael@0: michael@0: matrix = &pattern.mMatrix; michael@0: michael@0: const std::vector& stops = cairoStops->GetStops(); michael@0: for (size_t i = 0; i < stops.size(); ++i) { michael@0: const GradientStop& stop = stops[i]; michael@0: cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r, michael@0: stop.color.g, stop.color.b, michael@0: stop.color.a); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: case PatternType::RADIAL_GRADIENT: michael@0: { michael@0: const RadialGradientPattern& pattern = static_cast(aPattern); michael@0: michael@0: pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1, michael@0: pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2); michael@0: michael@0: MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO); michael@0: GradientStopsCairo* cairoStops = static_cast(pattern.mStops.get()); michael@0: cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode())); michael@0: michael@0: matrix = &pattern.mMatrix; michael@0: michael@0: const std::vector& stops = cairoStops->GetStops(); michael@0: for (size_t i = 0; i < stops.size(); ++i) { michael@0: const GradientStop& stop = stops[i]; michael@0: cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r, michael@0: stop.color.g, stop.color.b, michael@0: stop.color.a); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: default: michael@0: { michael@0: // We should support all pattern types! michael@0: MOZ_ASSERT(false); michael@0: } michael@0: } michael@0: michael@0: // The pattern matrix is a matrix that transforms the pattern into user michael@0: // space. Cairo takes a matrix that converts from user space to pattern michael@0: // space. Cairo therefore needs the inverse. michael@0: if (matrix) { michael@0: cairo_matrix_t mat; michael@0: GfxMatrixToCairoMatrix(*matrix, mat); michael@0: cairo_matrix_invert(&mat); michael@0: cairo_pattern_set_matrix(pat, &mat); michael@0: } michael@0: michael@0: return pat; michael@0: } michael@0: michael@0: static bool michael@0: NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions) michael@0: { michael@0: // We pre-multiply colours' alpha by the global alpha, so we don't need to michael@0: // use an intermediate surface for them. michael@0: if (aPattern.GetType() == PatternType::COLOR) michael@0: return false; michael@0: michael@0: if (aOptions.mAlpha == 1.0) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: DrawTargetCairo::DrawTargetCairo() michael@0: : mContext(nullptr) michael@0: , mSurface(nullptr) michael@0: , mLockedBits(nullptr) michael@0: { michael@0: } michael@0: michael@0: DrawTargetCairo::~DrawTargetCairo() michael@0: { michael@0: cairo_destroy(mContext); michael@0: if (mSurface) { michael@0: cairo_surface_destroy(mSurface); michael@0: } michael@0: MOZ_ASSERT(!mLockedBits); michael@0: } michael@0: michael@0: IntSize michael@0: DrawTargetCairo::GetSize() michael@0: { michael@0: return mSize; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::Snapshot() michael@0: { michael@0: if (mSnapshot) { michael@0: return mSnapshot; michael@0: } michael@0: michael@0: IntSize size = GetSize(); michael@0: michael@0: cairo_content_t content = cairo_surface_get_content(mSurface); michael@0: mSnapshot = new SourceSurfaceCairo(mSurface, michael@0: size, michael@0: CairoContentToGfxFormat(content), michael@0: this); michael@0: return mSnapshot; michael@0: } michael@0: michael@0: bool michael@0: DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize, michael@0: int32_t* aStride, SurfaceFormat* aFormat) michael@0: { michael@0: if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) { michael@0: WillChange(); michael@0: michael@0: mLockedBits = cairo_image_surface_get_data(mSurface); michael@0: *aData = mLockedBits; michael@0: *aSize = GetSize(); michael@0: *aStride = cairo_image_surface_get_stride(mSurface); michael@0: *aFormat = GetFormat(); michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::ReleaseBits(uint8_t* aData) michael@0: { michael@0: MOZ_ASSERT(mLockedBits == aData); michael@0: mLockedBits = nullptr; michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::Flush() michael@0: { michael@0: cairo_surface_t* surf = cairo_get_target(mContext); michael@0: cairo_surface_flush(surf); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nullptr */) michael@0: { michael@0: WillChange(aPath); michael@0: } michael@0: michael@0: cairo_surface_t* michael@0: DrawTargetCairo::GetDummySurface() michael@0: { michael@0: if (mDummySurface) { michael@0: return mDummySurface; michael@0: } michael@0: michael@0: mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); michael@0: michael@0: return mDummySurface; michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::DrawSurface(SourceSurface *aSurface, michael@0: const Rect &aDest, michael@0: const Rect &aSource, michael@0: const DrawSurfaceOptions &aSurfOptions, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: AutoClearDeviceOffset clear(aSurface); michael@0: michael@0: float sx = aSource.Width() / aDest.Width(); michael@0: float sy = aSource.Height() / aDest.Height(); michael@0: michael@0: cairo_matrix_t src_mat; michael@0: cairo_matrix_init_translate(&src_mat, aSource.X(), aSource.Y()); michael@0: cairo_matrix_scale(&src_mat, sx, sy); michael@0: michael@0: cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface); michael@0: cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf); michael@0: cairo_surface_destroy(surf); michael@0: michael@0: cairo_pattern_set_matrix(pat, &src_mat); michael@0: cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter)); michael@0: cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD); michael@0: michael@0: cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); michael@0: michael@0: // If the destination rect covers the entire clipped area, then unbounded and bounded michael@0: // operations are identical, and we don't need to push a group. michael@0: bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) && michael@0: !aDest.Contains(GetUserSpaceClip()); michael@0: michael@0: cairo_translate(mContext, aDest.X(), aDest.Y()); michael@0: michael@0: if (needsGroup) { michael@0: cairo_push_group(mContext); michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height()); michael@0: cairo_set_source(mContext, pat); michael@0: cairo_fill(mContext); michael@0: cairo_pop_group_to_source(mContext); michael@0: } else { michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height()); michael@0: cairo_clip(mContext); michael@0: cairo_set_source(mContext, pat); michael@0: } michael@0: michael@0: cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); michael@0: michael@0: cairo_paint_with_alpha(mContext, aOptions.mAlpha); michael@0: michael@0: cairo_pattern_destroy(pat); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::DrawFilter(FilterNode *aNode, michael@0: const Rect &aSourceRect, michael@0: const Point &aDestPoint, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: FilterNodeSoftware* filter = static_cast(aNode); michael@0: filter->Draw(this, aSourceRect, aDestPoint, aOptions); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface, michael@0: const Point &aDest, michael@0: const Color &aColor, michael@0: const Point &aOffset, michael@0: Float aSigma, michael@0: CompositionOp aOperator) michael@0: { michael@0: if (aSurface->GetType() != SurfaceType::CAIRO) { michael@0: return; michael@0: } michael@0: michael@0: AutoClearDeviceOffset clear(aSurface); michael@0: michael@0: Float width = Float(aSurface->GetSize().width); michael@0: Float height = Float(aSurface->GetSize().height); michael@0: michael@0: SourceSurfaceCairo* source = static_cast(aSurface); michael@0: cairo_surface_t* sourcesurf = source->GetSurface(); michael@0: cairo_surface_t* blursurf; michael@0: cairo_surface_t* surf; michael@0: michael@0: // We only use the A8 surface for blurred shadows. Unblurred shadows can just michael@0: // use the RGBA surface directly. michael@0: if (cairo_surface_get_type(sourcesurf) == CAIRO_SURFACE_TYPE_TEE) { michael@0: blursurf = cairo_tee_surface_index(sourcesurf, 0); michael@0: surf = cairo_tee_surface_index(sourcesurf, 1); michael@0: michael@0: MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE); michael@0: Rect extents(0, 0, width, height); michael@0: AlphaBoxBlur blur(extents, michael@0: cairo_image_surface_get_stride(blursurf), michael@0: aSigma, aSigma); michael@0: blur.Blur(cairo_image_surface_get_data(blursurf)); michael@0: } else { michael@0: blursurf = sourcesurf; michael@0: surf = sourcesurf; michael@0: } michael@0: michael@0: WillChange(); michael@0: ClearSurfaceForUnboundedSource(aOperator); michael@0: michael@0: cairo_save(mContext); michael@0: cairo_set_operator(mContext, GfxOpToCairoOp(aOperator)); michael@0: cairo_identity_matrix(mContext); michael@0: cairo_translate(mContext, aDest.x, aDest.y); michael@0: michael@0: if (IsOperatorBoundByMask(aOperator)){ michael@0: cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a); michael@0: cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y); michael@0: michael@0: // Now that the shadow has been drawn, we can draw the surface on top. michael@0: cairo_set_source_surface(mContext, surf, 0, 0); michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, 0, 0, width, height); michael@0: cairo_fill(mContext); michael@0: } else { michael@0: cairo_push_group(mContext); michael@0: cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a); michael@0: cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y); michael@0: michael@0: // Now that the shadow has been drawn, we can draw the surface on top. michael@0: cairo_set_source_surface(mContext, surf, 0, 0); michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, 0, 0, width, height); michael@0: cairo_fill(mContext); michael@0: cairo_pop_group_to_source(mContext); michael@0: cairo_paint(mContext); michael@0: } michael@0: michael@0: cairo_restore(mContext); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::DrawPattern(const Pattern& aPattern, michael@0: const StrokeOptions& aStrokeOptions, michael@0: const DrawOptions& aOptions, michael@0: DrawPatternType aDrawType, michael@0: bool aPathBoundsClip) michael@0: { michael@0: if (!PatternIsCompatible(aPattern)) { michael@0: return; michael@0: } michael@0: michael@0: AutoClearDeviceOffset clear(aPattern); michael@0: michael@0: cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha); michael@0: if (!pat) { michael@0: return; michael@0: } michael@0: if (cairo_pattern_status(pat)) { michael@0: cairo_pattern_destroy(pat); michael@0: gfxWarning() << "Invalid pattern"; michael@0: return; michael@0: } michael@0: michael@0: cairo_set_source(mContext, pat); michael@0: michael@0: cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); michael@0: michael@0: if (NeedIntermediateSurface(aPattern, aOptions) || michael@0: (!IsOperatorBoundByMask(aOptions.mCompositionOp) && !aPathBoundsClip)) { michael@0: cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA); michael@0: michael@0: // Don't want operators to be applied twice michael@0: cairo_set_operator(mContext, CAIRO_OPERATOR_OVER); michael@0: michael@0: if (aDrawType == DRAW_STROKE) { michael@0: SetCairoStrokeOptions(mContext, aStrokeOptions); michael@0: cairo_stroke_preserve(mContext); michael@0: } else { michael@0: cairo_fill_preserve(mContext); michael@0: } michael@0: michael@0: cairo_pop_group_to_source(mContext); michael@0: michael@0: // Now draw the content using the desired operator michael@0: cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); michael@0: cairo_paint_with_alpha(mContext, aOptions.mAlpha); michael@0: } else { michael@0: cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); michael@0: michael@0: if (aDrawType == DRAW_STROKE) { michael@0: SetCairoStrokeOptions(mContext, aStrokeOptions); michael@0: cairo_stroke_preserve(mContext); michael@0: } else { michael@0: cairo_fill_preserve(mContext); michael@0: } michael@0: } michael@0: michael@0: cairo_pattern_destroy(pat); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::FillRect(const Rect &aRect, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height()); michael@0: michael@0: bool pathBoundsClip = false; michael@0: michael@0: if (aRect.Contains(GetUserSpaceClip())) { michael@0: pathBoundsClip = true; michael@0: } michael@0: michael@0: DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL, pathBoundsClip); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface, michael@0: const IntRect &aSource, michael@0: const IntPoint &aDest) michael@0: { michael@0: if (cairo_surface_status(aSurface)) { michael@0: gfxWarning() << "Invalid surface"; michael@0: return; michael@0: } michael@0: michael@0: cairo_identity_matrix(mContext); michael@0: michael@0: cairo_set_source_surface(mContext, aSurface, aDest.x - aSource.x, aDest.y - aSource.y); michael@0: cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE); michael@0: cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE); michael@0: michael@0: cairo_reset_clip(mContext); michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height); michael@0: cairo_fill(mContext); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::CopySurface(SourceSurface *aSurface, michael@0: const IntRect &aSource, michael@0: const IntPoint &aDest) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: AutoClearDeviceOffset clear(aSurface); michael@0: michael@0: if (!aSurface) { michael@0: gfxWarning() << "Unsupported surface type specified"; michael@0: return; michael@0: } michael@0: michael@0: cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface); michael@0: if (!surf) { michael@0: gfxWarning() << "Unsupported surface type specified"; michael@0: return; michael@0: } michael@0: michael@0: CopySurfaceInternal(surf, aSource, aDest); michael@0: cairo_surface_destroy(surf); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::CopyRect(const IntRect &aSource, michael@0: const IntPoint &aDest) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: michael@0: IntRect source = aSource; michael@0: cairo_surface_t* surf = mSurface; michael@0: michael@0: if (!SupportsSelfCopy(mSurface) && michael@0: aDest.y >= aSource.y && michael@0: aDest.y < aSource.YMost()) { michael@0: cairo_surface_t* similar = cairo_surface_create_similar(mSurface, michael@0: GfxFormatToCairoContent(GetFormat()), michael@0: aSource.width, aSource.height); michael@0: cairo_t* ctx = cairo_create(similar); michael@0: cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE); michael@0: cairo_set_source_surface(ctx, surf, -aSource.x, -aSource.y); michael@0: cairo_paint(ctx); michael@0: cairo_destroy(ctx); michael@0: michael@0: source.x = 0; michael@0: source.y = 0; michael@0: surf = similar; michael@0: } michael@0: michael@0: CopySurfaceInternal(surf, source, aDest); michael@0: michael@0: if (surf != mSurface) { michael@0: cairo_surface_destroy(surf); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::ClearRect(const Rect& aRect) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: michael@0: cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE); michael@0: cairo_new_path(mContext); michael@0: cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR); michael@0: cairo_rectangle(mContext, aRect.X(), aRect.Y(), michael@0: aRect.Width(), aRect.Height()); michael@0: cairo_fill(mContext); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::StrokeRect(const Rect &aRect, michael@0: const Pattern &aPattern, michael@0: const StrokeOptions &aStrokeOptions /* = StrokeOptions() */, michael@0: const DrawOptions &aOptions /* = DrawOptions() */) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height()); michael@0: michael@0: DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::StrokeLine(const Point &aStart, michael@0: const Point &aEnd, michael@0: const Pattern &aPattern, michael@0: const StrokeOptions &aStrokeOptions /* = StrokeOptions() */, michael@0: const DrawOptions &aOptions /* = DrawOptions() */) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: michael@0: cairo_new_path(mContext); michael@0: cairo_move_to(mContext, aStart.x, aStart.y); michael@0: cairo_line_to(mContext, aEnd.x, aEnd.y); michael@0: michael@0: DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::Stroke(const Path *aPath, michael@0: const Pattern &aPattern, michael@0: const StrokeOptions &aStrokeOptions /* = StrokeOptions() */, michael@0: const DrawOptions &aOptions /* = DrawOptions() */) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext, aPath); michael@0: michael@0: if (aPath->GetBackendType() != BackendType::CAIRO) michael@0: return; michael@0: michael@0: PathCairo* path = const_cast(static_cast(aPath)); michael@0: path->SetPathOnContext(mContext); michael@0: michael@0: DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::Fill(const Path *aPath, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions /* = DrawOptions() */) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext, aPath); michael@0: michael@0: if (aPath->GetBackendType() != BackendType::CAIRO) michael@0: return; michael@0: michael@0: PathCairo* path = const_cast(static_cast(aPath)); michael@0: path->SetPathOnContext(mContext); michael@0: michael@0: DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA) michael@0: { michael@0: DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA); michael@0: #ifdef MOZ_TREE_CAIRO michael@0: cairo_surface_set_subpixel_antialiasing(mSurface, michael@0: aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::FillGlyphs(ScaledFont *aFont, michael@0: const GlyphBuffer &aBuffer, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions, michael@0: const GlyphRenderingOptions*) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: AutoClearDeviceOffset clear(aPattern); michael@0: michael@0: ScaledFontBase* scaledFont = static_cast(aFont); michael@0: cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont()); michael@0: michael@0: cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha); michael@0: if (!pat) michael@0: return; michael@0: michael@0: cairo_set_source(mContext, pat); michael@0: cairo_pattern_destroy(pat); michael@0: michael@0: cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); michael@0: michael@0: // Convert our GlyphBuffer into an array of Cairo glyphs. michael@0: std::vector glyphs(aBuffer.mNumGlyphs); michael@0: for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { michael@0: glyphs[i].index = aBuffer.mGlyphs[i].mIndex; michael@0: glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; michael@0: glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; michael@0: } michael@0: michael@0: cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::Mask(const Pattern &aSource, michael@0: const Pattern &aMask, michael@0: const DrawOptions &aOptions /* = DrawOptions() */) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: AutoClearDeviceOffset clearSource(aSource); michael@0: AutoClearDeviceOffset clearMask(aMask); michael@0: michael@0: cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); michael@0: michael@0: cairo_pattern_t* source = GfxPatternToCairoPattern(aSource, aOptions.mAlpha); michael@0: if (!source) { michael@0: return; michael@0: } michael@0: michael@0: cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha); michael@0: if (!mask) { michael@0: cairo_pattern_destroy(source); michael@0: return; michael@0: } michael@0: michael@0: if (cairo_pattern_status(source) || cairo_pattern_status(mask)) { michael@0: cairo_pattern_destroy(source); michael@0: cairo_pattern_destroy(mask); michael@0: gfxWarning() << "Invalid pattern"; michael@0: return; michael@0: } michael@0: michael@0: cairo_set_source(mContext, source); michael@0: cairo_mask(mContext, mask); michael@0: michael@0: cairo_pattern_destroy(mask); michael@0: cairo_pattern_destroy(source); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::MaskSurface(const Pattern &aSource, michael@0: SourceSurface *aMask, michael@0: Point aOffset, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: AutoPrepareForDrawing prep(this, mContext); michael@0: AutoClearDeviceOffset clearSource(aSource); michael@0: AutoClearDeviceOffset clearMask(aMask); michael@0: michael@0: if (!PatternIsCompatible(aSource)) { michael@0: return; michael@0: } michael@0: michael@0: cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); michael@0: michael@0: cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha); michael@0: if (!pat) { michael@0: return; michael@0: } michael@0: michael@0: if (cairo_pattern_status(pat)) { michael@0: cairo_pattern_destroy(pat); michael@0: gfxWarning() << "Invalid pattern"; michael@0: return; michael@0: } michael@0: michael@0: cairo_set_source(mContext, pat); michael@0: michael@0: if (NeedIntermediateSurface(aSource, aOptions)) { michael@0: cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA); michael@0: michael@0: // Don't want operators to be applied twice michael@0: cairo_set_operator(mContext, CAIRO_OPERATOR_OVER); michael@0: michael@0: // Now draw the content using the desired operator michael@0: cairo_paint_with_alpha(mContext, aOptions.mAlpha); michael@0: michael@0: cairo_pop_group_to_source(mContext); michael@0: } michael@0: michael@0: cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask); michael@0: if (!surf) { michael@0: cairo_pattern_destroy(pat); michael@0: return; michael@0: } michael@0: cairo_pattern_t* mask = cairo_pattern_create_for_surface(surf); michael@0: cairo_matrix_t matrix; michael@0: michael@0: cairo_matrix_init_translate (&matrix, -aOffset.x, -aOffset.y); michael@0: cairo_pattern_set_matrix (mask, &matrix); michael@0: michael@0: cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); michael@0: michael@0: cairo_mask(mContext, mask); michael@0: michael@0: cairo_surface_destroy(surf); michael@0: cairo_pattern_destroy(mask); michael@0: cairo_pattern_destroy(pat); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::PushClip(const Path *aPath) michael@0: { michael@0: if (aPath->GetBackendType() != BackendType::CAIRO) { michael@0: return; michael@0: } michael@0: michael@0: WillChange(aPath); michael@0: cairo_save(mContext); michael@0: michael@0: PathCairo* path = const_cast(static_cast(aPath)); michael@0: path->SetPathOnContext(mContext); michael@0: cairo_clip_preserve(mContext); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::PushClipRect(const Rect& aRect) michael@0: { michael@0: WillChange(); michael@0: cairo_save(mContext); michael@0: michael@0: cairo_new_path(mContext); michael@0: cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); michael@0: cairo_clip_preserve(mContext); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::PopClip() michael@0: { michael@0: // save/restore does not affect the path, so no need to call WillChange() michael@0: michael@0: // cairo_restore will restore the transform too and we don't want to do that michael@0: // so we'll save it now and restore it after the cairo_restore michael@0: cairo_matrix_t mat; michael@0: cairo_get_matrix(mContext, &mat); michael@0: michael@0: cairo_restore(mContext); michael@0: michael@0: cairo_set_matrix(mContext, &mat); michael@0: michael@0: MOZ_ASSERT(cairo_status(mContext) || GetTransform() == Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0), michael@0: "Transforms are out of sync"); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const michael@0: { michael@0: RefPtr builder = new PathBuilderCairo(aFillRule); michael@0: michael@0: return builder; michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator) michael@0: { michael@0: if (aOperator != CompositionOp::OP_SOURCE) michael@0: return; michael@0: cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR); michael@0: // It doesn't really matter what the source is here, since Paint michael@0: // isn't bounded by the source and the mask covers the entire clip michael@0: // region. michael@0: cairo_paint(mContext); michael@0: } michael@0: michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, michael@0: ExtendMode aExtendMode) const michael@0: { michael@0: RefPtr stops = new GradientStopsCairo(aStops, aNumStops, michael@0: aExtendMode); michael@0: return stops; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::CreateFilter(FilterType aType) michael@0: { michael@0: return FilterNodeSoftware::Create(aType); michael@0: } michael@0: michael@0: /** michael@0: * Copies pixel data from aData into aSurface; aData must have the dimensions michael@0: * given in aSize, with a stride of aStride bytes and aPixelWidth bytes per pixel michael@0: */ michael@0: static void michael@0: CopyDataToCairoSurface(cairo_surface_t* aSurface, michael@0: unsigned char *aData, michael@0: const IntSize &aSize, michael@0: int32_t aStride, michael@0: int32_t aPixelWidth) michael@0: { michael@0: unsigned char* surfData = cairo_image_surface_get_data(aSurface); michael@0: int surfStride = cairo_image_surface_get_stride(aSurface); michael@0: // In certain scenarios, requesting larger than 8k image fails. Bug 803568 michael@0: // covers the details of how to run into it, but the full detailed michael@0: // investigation hasn't been done to determine the underlying cause. We michael@0: // will just handle the failure to allocate the surface to avoid a crash. michael@0: if (!surfData) { michael@0: return; michael@0: } michael@0: for (int32_t y = 0; y < aSize.height; ++y) { michael@0: memcpy(surfData + y * surfStride, michael@0: aData + y * aStride, michael@0: aSize.width * aPixelWidth); michael@0: } michael@0: cairo_surface_mark_dirty(aSurface); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData, michael@0: const IntSize &aSize, michael@0: int32_t aStride, michael@0: SurfaceFormat aFormat) const michael@0: { michael@0: cairo_surface_t* surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), michael@0: aSize.width, michael@0: aSize.height); michael@0: // In certain scenarios, requesting larger than 8k image fails. Bug 803568 michael@0: // covers the details of how to run into it, but the full detailed michael@0: // investigation hasn't been done to determine the underlying cause. We michael@0: // will just handle the failure to allocate the surface to avoid a crash. michael@0: if (cairo_surface_status(surf)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: CopyDataToCairoSurface(surf, aData, aSize, aStride, BytesPerPixel(aFormat)); michael@0: michael@0: RefPtr source_surf = new SourceSurfaceCairo(surf, aSize, aFormat); michael@0: cairo_surface_destroy(surf); michael@0: michael@0: return source_surf; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const michael@0: { michael@0: return aSurface; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const michael@0: { michael@0: if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) { michael@0: if (aSurface.mSize.width <= 0 || michael@0: aSurface.mSize.height <= 0) { michael@0: gfxWarning() << "Can't create a SourceSurface without a valid size"; michael@0: return nullptr; michael@0: } michael@0: cairo_surface_t* surf = static_cast(aSurface.mSurface); michael@0: RefPtr source = michael@0: new SourceSurfaceCairo(surf, aSurface.mSize, aSurface.mFormat); michael@0: return source; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const michael@0: { michael@0: cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext), michael@0: GfxFormatToCairoContent(aFormat), michael@0: aSize.width, aSize.height); michael@0: michael@0: if (!cairo_surface_status(similar)) { michael@0: RefPtr target = new DrawTargetCairo(); michael@0: target->InitAlreadyReferenced(similar, aSize); michael@0: return target; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) michael@0: { michael@0: mContext = cairo_create(aSurface); michael@0: mSurface = aSurface; michael@0: mSize = aSize; michael@0: mFormat = aFormat ? *aFormat : CairoContentToGfxFormat(cairo_surface_get_content(aSurface)); michael@0: michael@0: if (mFormat == SurfaceFormat::B8G8R8A8 || michael@0: mFormat == SurfaceFormat::R8G8B8A8) { michael@0: SetPermitSubpixelAA(false); michael@0: } else { michael@0: SetPermitSubpixelAA(true); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat, michael@0: float aSigma) const michael@0: { michael@0: cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext), michael@0: GfxFormatToCairoContent(aFormat), michael@0: aSize.width, aSize.height); michael@0: michael@0: if (cairo_surface_status(similar)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // If we don't have a blur then we can use the RGBA mask and keep all the michael@0: // operations in graphics memory. michael@0: if (aSigma == 0.0F) { michael@0: RefPtr target = new DrawTargetCairo(); michael@0: target->InitAlreadyReferenced(similar, aSize); michael@0: return target; michael@0: } michael@0: michael@0: cairo_surface_t* blursurf = cairo_image_surface_create(CAIRO_FORMAT_A8, michael@0: aSize.width, michael@0: aSize.height); michael@0: michael@0: if (cairo_surface_status(blursurf)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: cairo_surface_t* tee = cairo_tee_surface_create(blursurf); michael@0: cairo_surface_destroy(blursurf); michael@0: if (cairo_surface_status(tee)) { michael@0: cairo_surface_destroy(similar); michael@0: return nullptr; michael@0: } michael@0: michael@0: cairo_tee_surface_add(tee, similar); michael@0: cairo_surface_destroy(similar); michael@0: michael@0: RefPtr target = new DrawTargetCairo(); michael@0: target->InitAlreadyReferenced(tee, aSize); michael@0: return target; michael@0: } michael@0: michael@0: bool michael@0: DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) michael@0: { michael@0: cairo_surface_reference(aSurface); michael@0: return InitAlreadyReferenced(aSurface, aSize, aFormat); michael@0: } michael@0: michael@0: bool michael@0: DrawTargetCairo::Init(const IntSize& aSize, SurfaceFormat aFormat) michael@0: { michael@0: cairo_surface_t *surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height); michael@0: return InitAlreadyReferenced(surf, aSize); michael@0: } michael@0: michael@0: bool michael@0: DrawTargetCairo::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) michael@0: { michael@0: cairo_surface_t* surf = michael@0: cairo_image_surface_create_for_data(aData, michael@0: GfxFormatToCairoFormat(aFormat), michael@0: aSize.width, michael@0: aSize.height, michael@0: aStride); michael@0: return InitAlreadyReferenced(surf, aSize); michael@0: } michael@0: michael@0: void * michael@0: DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType) michael@0: { michael@0: if (aType == NativeSurfaceType::CAIRO_SURFACE) { michael@0: return cairo_get_target(mContext); michael@0: } michael@0: if (aType == NativeSurfaceType::CAIRO_CONTEXT) { michael@0: return mContext; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::MarkSnapshotIndependent() michael@0: { michael@0: if (mSnapshot) { michael@0: if (mSnapshot->refCount() > 1) { michael@0: // We only need to worry about snapshots that someone else knows about michael@0: mSnapshot->DrawTargetWillChange(); michael@0: } michael@0: mSnapshot = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */) michael@0: { michael@0: MarkSnapshotIndependent(); michael@0: MOZ_ASSERT(!mLockedBits); michael@0: } michael@0: michael@0: void michael@0: DrawTargetCairo::SetTransform(const Matrix& aTransform) michael@0: { michael@0: mTransform = aTransform; michael@0: michael@0: cairo_matrix_t mat; michael@0: GfxMatrixToCairoMatrix(mTransform, mat); michael@0: cairo_set_matrix(mContext, &mat); michael@0: } michael@0: michael@0: Rect michael@0: DrawTargetCairo::GetUserSpaceClip() michael@0: { michael@0: double clipX1, clipY1, clipX2, clipY2; michael@0: cairo_clip_extents(mContext, &clipX1, &clipY1, &clipX2, &clipY2); michael@0: return Rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); // Narrowing of doubles to floats michael@0: } michael@0: michael@0: cairo_t* michael@0: BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT) michael@0: { michael@0: if (aDT->GetType() != BackendType::CAIRO || aDT->IsDualDrawTarget()) { michael@0: return nullptr; michael@0: } michael@0: DrawTargetCairo* cairoDT = static_cast(aDT); michael@0: michael@0: cairoDT->WillChange(); michael@0: michael@0: // save the state to make it easier for callers to avoid mucking with things michael@0: cairo_save(cairoDT->mContext); michael@0: michael@0: // Neuter the DrawTarget while the context is being borrowed michael@0: cairo_t* cairo = cairoDT->mContext; michael@0: cairoDT->mContext = nullptr; michael@0: michael@0: return cairo; michael@0: } michael@0: michael@0: void michael@0: BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT, michael@0: cairo_t* aCairo) michael@0: { michael@0: if (aDT->GetType() != BackendType::CAIRO || aDT->IsDualDrawTarget()) { michael@0: return; michael@0: } michael@0: DrawTargetCairo* cairoDT = static_cast(aDT); michael@0: michael@0: cairo_restore(aCairo); michael@0: cairoDT->mContext = aCairo; michael@0: } michael@0: michael@0: } michael@0: }