author | Joe Drew <joe@drew.ca> |
Mon, 09 Jan 2012 17:15:10 -0500 | |
changeset 84236 | 14930a83054b9cfda27d7e60f54680215fd8ac63 |
parent 84235 | 4b4d5cd00d43f83f097b2b7a0b7d1e407b5e4081 |
child 84237 | 7349c6b4ac7f856f12b606894872c526e8ffae28 |
push id | 21832 |
push user | bmo@edmorley.co.uk |
push date | Wed, 11 Jan 2012 17:04:15 +0000 |
treeherder | mozilla-central@40c9f9ff9fd5 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jrmuizel |
bugs | 707848 |
milestone | 12.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -32,16 +32,18 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "DrawTargetCairo.h" #include "SourceSurfaceCairo.h" +#include "PathCairo.h" +#include "HelpersCairo.h" #include "cairo.h" #include "Blur.h" #ifdef CAIRO_HAS_QUARTZ_SURFACE #include "cairo-quartz.h" #include <ApplicationServices/ApplicationServices.h> @@ -51,161 +53,44 @@ #include "cairo-xlib.h" #endif #include <algorithm> namespace mozilla { namespace gfx { -static cairo_operator_t -GfxOpToCairoOp(CompositionOp op) +namespace { + +// An RAII class to prepare to draw a context and optional path. Saves and +// restores the context on construction/destruction. +class AutoPrepareForDrawing { - switch (op) +public: + AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx) + : mCtx(ctx) { - case OP_OVER: - return CAIRO_OPERATOR_OVER; - case OP_ADD: - return CAIRO_OPERATOR_ADD; - case OP_ATOP: - return CAIRO_OPERATOR_ATOP; - case OP_OUT: - return CAIRO_OPERATOR_OUT; - case OP_IN: - return CAIRO_OPERATOR_IN; - case OP_SOURCE: - return CAIRO_OPERATOR_SOURCE; - case OP_DEST_IN: - return CAIRO_OPERATOR_DEST_IN; - case OP_DEST_OUT: - return CAIRO_OPERATOR_DEST_OUT; - case OP_DEST_OVER: - return CAIRO_OPERATOR_DEST_OVER; - case OP_DEST_ATOP: - return CAIRO_OPERATOR_DEST_ATOP; - case OP_XOR: - return CAIRO_OPERATOR_XOR; - case OP_COUNT: - break; - } - - return CAIRO_OPERATOR_OVER; -} - -static cairo_filter_t -GfxFilterToCairoFilter(Filter filter) -{ - switch (filter) - { - case FILTER_LINEAR: - return CAIRO_FILTER_BILINEAR; - case FILTER_POINT: - return CAIRO_FILTER_NEAREST; - } - - return CAIRO_FILTER_BILINEAR; -} - -static cairo_extend_t -GfxExtendToCairoExtend(ExtendMode extend) -{ - switch (extend) - { - case EXTEND_CLAMP: - return CAIRO_EXTEND_PAD; - case EXTEND_REPEAT: - return CAIRO_EXTEND_REPEAT; - case EXTEND_REFLECT: - return CAIRO_EXTEND_REFLECT; + dt->PrepareForDrawing(ctx); + cairo_save(mCtx); } - return CAIRO_EXTEND_PAD; -} - -static cairo_format_t -GfxFormatToCairoFormat(SurfaceFormat format) -{ - switch (format) + AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path) + : mCtx(ctx) { - case FORMAT_B8G8R8A8: - return CAIRO_FORMAT_ARGB32; - case FORMAT_B8G8R8X8: - return CAIRO_FORMAT_RGB24; - case FORMAT_A8: - return CAIRO_FORMAT_A8; - } - - return CAIRO_FORMAT_ARGB32; -} - -static cairo_content_t -GfxFormatToCairoContent(SurfaceFormat format) -{ - switch (format) - { - case FORMAT_B8G8R8A8: - return CAIRO_CONTENT_COLOR_ALPHA; - case FORMAT_B8G8R8X8: - return CAIRO_CONTENT_COLOR; - case FORMAT_A8: - return CAIRO_CONTENT_ALPHA; + dt->PrepareForDrawing(ctx, path); + cairo_save(mCtx); } - return CAIRO_CONTENT_COLOR_ALPHA; -} - -static cairo_line_join_t -GfxLineJoinToCairoLineJoin(JoinStyle style) -{ - switch (style) - { - case JOIN_BEVEL: - return CAIRO_LINE_JOIN_BEVEL; - case JOIN_ROUND: - return CAIRO_LINE_JOIN_ROUND; - case JOIN_MITER: - return CAIRO_LINE_JOIN_MITER; - case JOIN_MITER_OR_BEVEL: - return CAIRO_LINE_JOIN_MITER; - } - - return CAIRO_LINE_JOIN_MITER; -} + ~AutoPrepareForDrawing() { cairo_restore(mCtx); } -static cairo_line_cap_t -GfxLineCapToCairoLineCap(CapStyle style) -{ - switch (style) - { - case CAP_BUTT: - return CAIRO_LINE_CAP_BUTT; - case CAP_ROUND: - return CAIRO_LINE_CAP_ROUND; - case CAP_SQUARE: - return CAIRO_LINE_CAP_SQUARE; - } - - return CAIRO_LINE_CAP_BUTT; -} +private: + cairo_t* mCtx; +}; -static SurfaceFormat -CairoContentToGfxFormat(cairo_content_t content) -{ - switch (content) - { - case CAIRO_CONTENT_COLOR_ALPHA: - return FORMAT_B8G8R8A8; - case CAIRO_CONTENT_COLOR: - return FORMAT_B8G8R8X8; - case CAIRO_CONTENT_ALPHA: - return FORMAT_A8; - } - - return FORMAT_B8G8R8A8; -} +} // end anonymous namespace static bool GetCairoSurfaceSize(cairo_surface_t* surface, IntSize& size) { switch (cairo_surface_get_type(surface)) { case CAIRO_SURFACE_TYPE_IMAGE: { @@ -236,23 +121,17 @@ GetCairoSurfaceSize(cairo_surface_t* sur } #endif default: return false; } } -void -GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval) -{ - cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32); -} - -cairo_pattern_t* +static cairo_pattern_t* GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha) { cairo_pattern_t* pat = NULL; switch (aPattern.GetType()) { case PATTERN_COLOR: { @@ -334,17 +213,17 @@ GfxPatternToCairoPattern(const Pattern& break; } } return pat; } -bool +static bool NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions) { // We pre-multiply colours' alpha by the global alpha, so we don't need to // use an intermediate surface for them. if (aPattern.GetType() == PATTERN_COLOR) return false; if (aOptions.mAlpha == 1.0) @@ -356,16 +235,19 @@ NeedIntermediateSurface(const Pattern& a DrawTargetCairo::DrawTargetCairo() : mContext(NULL) { } DrawTargetCairo::~DrawTargetCairo() { MarkSnapshotsIndependent(); + if (mPathObserver) { + mPathObserver->ForgetDrawTarget(); + } cairo_destroy(mContext); } IntSize DrawTargetCairo::GetSize() { return IntSize(); } @@ -390,33 +272,29 @@ DrawTargetCairo::Snapshot() void DrawTargetCairo::Flush() { cairo_surface_t* surf = cairo_get_target(mContext); cairo_surface_flush(surf); } void -DrawTargetCairo::PrepareForDrawing(cairo_t* aContext) +DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = NULL */) { - MarkChanged(); - - cairo_matrix_t mat; - GfxMatrixToCairoMatrix(mTransform, mat); - cairo_set_matrix(aContext, &mat); + WillChange(aPath); } void DrawTargetCairo::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext); float sx = aSource.Width() / aDest.Width(); float sy = aSource.Height() / aDest.Height(); cairo_matrix_t src_mat; cairo_matrix_init_scale(&src_mat, sx, sy); cairo_matrix_translate(&src_mat, aSource.X(), aSource.Y()); @@ -450,17 +328,17 @@ DrawTargetCairo::DrawSurface(SourceSurfa void DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator) { - MarkChanged(); + WillChange(); if (aSurface->GetType() != SURFACE_CAIRO) { return; } SourceSurfaceCairo* sourcesurf = static_cast<SourceSurfaceCairo*>(aSurface); Float width = aSurface->GetSize().width, @@ -525,113 +403,81 @@ DrawTargetCairo::DrawSurfaceWithShadow(S cairo_paint(mContext); cairo_restore(mContext); cairo_pattern_destroy(pat); } void -SetStrokeOptions(cairo_t* aCtx, const StrokeOptions& aStrokeOptions) -{ - cairo_set_line_width(aCtx, aStrokeOptions.mLineWidth); - - cairo_set_miter_limit(aCtx, aStrokeOptions.mMiterLimit); - - if (aStrokeOptions.mDashPattern) { - // Convert array of floats to array of doubles - std::vector<double> dashes(aStrokeOptions.mDashLength); - for (size_t i = 0; i < aStrokeOptions.mDashLength; ++i) { - dashes[i] = aStrokeOptions.mDashPattern[i]; - } - cairo_set_dash(aCtx, &dashes[0], aStrokeOptions.mDashLength, - aStrokeOptions.mDashOffset); - } - - cairo_set_line_join(aCtx, GfxLineJoinToCairoLineJoin(aStrokeOptions.mLineJoin)); - - cairo_set_line_cap(aCtx, GfxLineCapToCairoLineCap(aStrokeOptions.mLineCap)); -} - -void -DrawTargetCairo::DrawPattern(const Rect& aRect, - const Pattern& aPattern, +DrawTargetCairo::DrawPattern(const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aOptions, DrawPatternType aDrawType) { - cairo_save(mContext); + cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha); + cairo_set_source(mContext, pat); - cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); - - bool needIntermediate = NeedIntermediateSurface(aPattern, aOptions); - if (needIntermediate) { + if (NeedIntermediateSurface(aPattern, aOptions)) { cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA); // Don't want operators to be applied twice - cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE); - } - - cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha); - - if (pat) { - cairo_set_source(mContext, pat); - - cairo_new_path(mContext); - cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height()); + cairo_set_operator(mContext, CAIRO_OPERATOR_OVER); if (aDrawType == DRAW_STROKE) { - SetStrokeOptions(mContext, aStrokeOptions); - cairo_stroke(mContext); + SetCairoStrokeOptions(mContext, aStrokeOptions); + cairo_stroke_preserve(mContext); } else { - // It's possible that we could simply always clip and paint here, but the - // old canvas implementation didn't, so to maintain similar performance - // characteristics we choose the same. - if (needIntermediate || aOptions.mAlpha == 1.0) { - cairo_fill(mContext); - } else { - cairo_clip(mContext); - cairo_paint(mContext); - } + cairo_fill_preserve(mContext); } - cairo_pattern_destroy(pat); + cairo_pop_group_to_source(mContext); + + // Now draw the content using the desired operator + cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); + cairo_paint_with_alpha(mContext, aOptions.mAlpha); + } else { + cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); + + if (aDrawType == DRAW_STROKE) { + SetCairoStrokeOptions(mContext, aStrokeOptions); + cairo_stroke_preserve(mContext); + } else { + cairo_fill_preserve(mContext); + } } - if (needIntermediate) { - cairo_pop_group_to_source(mContext); - - cairo_paint_with_alpha(mContext, aOptions.mAlpha); - } - - cairo_restore(mContext); + cairo_pattern_destroy(pat); } void DrawTargetCairo::FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext); - DrawPattern(aRect, aPattern, StrokeOptions(), aOptions, DRAW_FILL); + cairo_new_path(mContext); + cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height()); + + DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); } void DrawTargetCairo::CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, const IntPoint &aDestination) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext); } void DrawTargetCairo::ClearRect(const Rect& aRect) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext); cairo_save(mContext); cairo_new_path(mContext); cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR); cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); cairo_fill(mContext); @@ -640,91 +486,118 @@ DrawTargetCairo::ClearRect(const Rect& a } void DrawTargetCairo::StrokeRect(const Rect &aRect, const Pattern &aPattern, const StrokeOptions &aStrokeOptions /* = StrokeOptions() */, const DrawOptions &aOptions /* = DrawOptions() */) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext); - DrawPattern(aRect, aPattern, aStrokeOptions, aOptions, DRAW_STROKE); + cairo_new_path(mContext); + cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height()); + + DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE); } void DrawTargetCairo::StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern, const StrokeOptions &aStrokeOptions /* = StrokeOptions() */, const DrawOptions &aOptions /* = DrawOptions() */) { - PrepareForDrawing(mContext); - - cairo_save(mContext); - - cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha); - if (pat) { - SetStrokeOptions(mContext, aStrokeOptions); - cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); - - cairo_set_source(mContext, pat); + AutoPrepareForDrawing prep(this, mContext); - cairo_new_path(mContext); - cairo_move_to(mContext, aStart.x, aStart.y); - cairo_line_to(mContext, aEnd.x, aEnd.y); + cairo_new_path(mContext); + cairo_move_to(mContext, aStart.x, aStart.y); + cairo_line_to(mContext, aEnd.x, aEnd.y); - cairo_stroke(mContext); - - cairo_pattern_destroy(pat); - } - - cairo_restore(mContext); + DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE); } void DrawTargetCairo::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions /* = StrokeOptions() */, const DrawOptions &aOptions /* = DrawOptions() */) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext, aPath); + + if (aPath->GetBackendType() != BACKEND_CAIRO) + return; + + PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath)); + path->CopyPathTo(mContext, this); + + DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE); } void DrawTargetCairo::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions /* = DrawOptions() */) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext, aPath); + + if (aPath->GetBackendType() != BACKEND_CAIRO) + return; + + PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath)); + path->CopyPathTo(mContext, this); + + DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); } void DrawTargetCairo::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions) { - PrepareForDrawing(mContext); + AutoPrepareForDrawing prep(this, mContext); +} + +void +DrawTargetCairo::Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions /* = DrawOptions() */) +{ + AutoPrepareForDrawing prep(this, mContext); } void DrawTargetCairo::PushClip(const Path *aPath) { } void +DrawTargetCairo::PushClipRect(const Rect& aRect) +{ +} + +void DrawTargetCairo::PopClip() { } TemporaryRef<PathBuilder> DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const { - return NULL; + RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mContext, + const_cast<DrawTargetCairo*>(this), + aFillRule); + + // Creating a PathBuilder implicitly resets our mPathObserver, as it calls + // SetPathObserver() on us. Since this guarantees our old path is saved off, + // it's safe to reset the path here. + cairo_new_path(mContext); + + return builder; } TemporaryRef<GradientStops> DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { RefPtr<GradientStopsCairo> stops = new GradientStopsCairo(aStops, aNumStops); return stops; } @@ -826,23 +699,52 @@ DrawTargetCairo::RemoveSnapshot(SourceSu mSnapshots.end(), aSnapshot); if (iter != mSnapshots.end()) { mSnapshots.erase(iter); } } void -DrawTargetCairo::MarkChanged() +DrawTargetCairo::WillChange(const Path* aPath /* = NULL */) { if (!mSnapshots.empty()) { for (std::vector<SourceSurfaceCairo*>::iterator iter = mSnapshots.begin(); iter != mSnapshots.end(); ++iter) { (*iter)->DrawTargetWillChange(); } // All snapshots will now have copied data. mSnapshots.clear(); } + + if (aPath && mPathObserver && !mPathObserver->ContainsPath(aPath)) { + mPathObserver->PathWillChange(); + mPathObserver = NULL; + } +} + +void +DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver) +{ + if (mPathObserver && mPathObserver != aPathObserver) { + mPathObserver->PathWillChange(); + } + mPathObserver = aPathObserver; } +void +DrawTargetCairo::SetTransform(const Matrix& aTransform) +{ + // We're about to logically change our transformation. Our current path will + // need to change, because Cairo stores paths in device space. + if (mPathObserver) { + mPathObserver->MatrixWillChange(aTransform); + } + + mTransform = aTransform; + + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(mTransform, mat); + cairo_set_matrix(mContext, &mat); +} } }
--- a/gfx/2d/DrawTargetCairo.h +++ b/gfx/2d/DrawTargetCairo.h @@ -34,16 +34,17 @@ * * ***** END LICENSE BLOCK ***** */ #ifndef _MOZILLA_GFX_DRAWTARGET_CAIRO_H_ #define _MOZILLA_GFX_DRAWTARGET_CAIRO_H_ #include "2D.h" #include "cairo.h" +#include "PathCairo.h" #include <vector> namespace mozilla { namespace gfx { class SourceSurfaceCairo; @@ -122,22 +123,20 @@ public: const DrawOptions &aOptions = DrawOptions()); virtual void FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions); virtual void Mask(const Pattern &aSource, const Pattern &aMask, - const DrawOptions &aOptions = DrawOptions()) - { } + const DrawOptions &aOptions = DrawOptions()); virtual void PushClip(const Path *aPath); - virtual void PushClipRect(const Rect &aRect) - { } + virtual void PushClipRect(const Rect &aRect); virtual void PopClip(); virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const; virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) const; @@ -151,40 +150,48 @@ public: CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = EXTEND_CLAMP) const; virtual void *GetNativeSurface(NativeSurfaceType aType); bool Init(cairo_surface_t* aSurface); -private: // methods - void PrepareForDrawing(cairo_t* aContext); + void SetPathObserver(CairoPathContext* aPathObserver); + + virtual void SetTransform(const Matrix& aTransform); + // Call to set up aContext for drawing (with the current transform, etc). + // Pass the path you're going to be using if you have one. + // Implicitly calls WillChange(aPath). + void PrepareForDrawing(cairo_t* aContext, const Path* aPath = NULL); + +private: // methods enum DrawPatternType { DRAW_FILL, DRAW_STROKE }; - void DrawPattern(const Rect& aRect, - const Pattern& aPattern, + void DrawPattern(const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aOptions, DrawPatternType aDrawType); // Copy-on-write support for snapshot surfaces. friend class SourceSurfaceCairo; void AppendSnapshot(SourceSurfaceCairo* aSnapshot); void RemoveSnapshot(SourceSurfaceCairo* aSnapshot); // Call before you make any changes to the backing surface with which this - // context is associated. - void MarkChanged(); + // context is associated. Pass the path you're going to be using if you have + // one. + void WillChange(const Path* aPath = NULL); // Call if there is any reason to disassociate all snapshots from this draw // target; for example, because we're going to be destroyed. void MarkSnapshotsIndependent(); private: // data cairo_t* mContext; std::vector<SourceSurfaceCairo*> mSnapshots; + mutable RefPtr<CairoPathContext> mPathObserver; }; } } #endif // _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
new file mode 100644 --- /dev/null +++ b/gfx/2d/HelpersCairo.h @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef MOZILLA_GFX_HELPERSCAIRO_H_ +#define MOZILLA_GFX_HELPERSCAIRO_H_ + +#include "2D.h" +#include "cairo.h" + +namespace mozilla { +namespace gfx { + +static inline cairo_operator_t +GfxOpToCairoOp(CompositionOp op) +{ + switch (op) + { + case OP_OVER: + return CAIRO_OPERATOR_OVER; + case OP_ADD: + return CAIRO_OPERATOR_ADD; + case OP_ATOP: + return CAIRO_OPERATOR_ATOP; + case OP_OUT: + return CAIRO_OPERATOR_OUT; + case OP_IN: + return CAIRO_OPERATOR_IN; + case OP_SOURCE: + return CAIRO_OPERATOR_SOURCE; + case OP_DEST_IN: + return CAIRO_OPERATOR_DEST_IN; + case OP_DEST_OUT: + return CAIRO_OPERATOR_DEST_OUT; + case OP_DEST_OVER: + return CAIRO_OPERATOR_DEST_OVER; + case OP_DEST_ATOP: + return CAIRO_OPERATOR_DEST_ATOP; + case OP_XOR: + return CAIRO_OPERATOR_XOR; + case OP_COUNT: + break; + } + + return CAIRO_OPERATOR_OVER; +} + +static inline cairo_filter_t +GfxFilterToCairoFilter(Filter filter) +{ + switch (filter) + { + case FILTER_LINEAR: + return CAIRO_FILTER_BILINEAR; + case FILTER_POINT: + return CAIRO_FILTER_NEAREST; + } + + return CAIRO_FILTER_BILINEAR; +} + +static inline cairo_extend_t +GfxExtendToCairoExtend(ExtendMode extend) +{ + switch (extend) + { + case EXTEND_CLAMP: + return CAIRO_EXTEND_PAD; + case EXTEND_REPEAT: + return CAIRO_EXTEND_REPEAT; + case EXTEND_REFLECT: + return CAIRO_EXTEND_REFLECT; + } + + return CAIRO_EXTEND_PAD; +} + +static inline cairo_format_t +GfxFormatToCairoFormat(SurfaceFormat format) +{ + switch (format) + { + case FORMAT_B8G8R8A8: + return CAIRO_FORMAT_ARGB32; + case FORMAT_B8G8R8X8: + return CAIRO_FORMAT_RGB24; + case FORMAT_A8: + return CAIRO_FORMAT_A8; + } + + return CAIRO_FORMAT_ARGB32; +} + +static inline cairo_content_t +GfxFormatToCairoContent(SurfaceFormat format) +{ + switch (format) + { + case FORMAT_B8G8R8A8: + return CAIRO_CONTENT_COLOR_ALPHA; + case FORMAT_B8G8R8X8: + return CAIRO_CONTENT_COLOR; + case FORMAT_A8: + return CAIRO_CONTENT_ALPHA; + } + + return CAIRO_CONTENT_COLOR_ALPHA; +} + +static inline cairo_line_join_t +GfxLineJoinToCairoLineJoin(JoinStyle style) +{ + switch (style) + { + case JOIN_BEVEL: + return CAIRO_LINE_JOIN_BEVEL; + case JOIN_ROUND: + return CAIRO_LINE_JOIN_ROUND; + case JOIN_MITER: + return CAIRO_LINE_JOIN_MITER; + case JOIN_MITER_OR_BEVEL: + return CAIRO_LINE_JOIN_MITER; + } + + return CAIRO_LINE_JOIN_MITER; +} + +static inline cairo_line_cap_t +GfxLineCapToCairoLineCap(CapStyle style) +{ + switch (style) + { + case CAP_BUTT: + return CAIRO_LINE_CAP_BUTT; + case CAP_ROUND: + return CAIRO_LINE_CAP_ROUND; + case CAP_SQUARE: + return CAIRO_LINE_CAP_SQUARE; + } + + return CAIRO_LINE_CAP_BUTT; +} + +static inline SurfaceFormat +CairoContentToGfxFormat(cairo_content_t content) +{ + switch (content) + { + case CAIRO_CONTENT_COLOR_ALPHA: + return FORMAT_B8G8R8A8; + case CAIRO_CONTENT_COLOR: + return FORMAT_B8G8R8X8; + case CAIRO_CONTENT_ALPHA: + return FORMAT_A8; + } + + return FORMAT_B8G8R8A8; +} + +static inline void +GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval) +{ + cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32); +} + +static inline void +SetCairoStrokeOptions(cairo_t* aCtx, const StrokeOptions& aStrokeOptions) +{ + cairo_set_line_width(aCtx, aStrokeOptions.mLineWidth); + + cairo_set_miter_limit(aCtx, aStrokeOptions.mMiterLimit); + + if (aStrokeOptions.mDashPattern) { + // Convert array of floats to array of doubles + std::vector<double> dashes(aStrokeOptions.mDashLength); + for (size_t i = 0; i < aStrokeOptions.mDashLength; ++i) { + dashes[i] = aStrokeOptions.mDashPattern[i]; + } + cairo_set_dash(aCtx, &dashes[0], aStrokeOptions.mDashLength, + aStrokeOptions.mDashOffset); + } + + cairo_set_line_join(aCtx, GfxLineJoinToCairoLineJoin(aStrokeOptions.mLineJoin)); + + cairo_set_line_cap(aCtx, GfxLineCapToCairoLineCap(aStrokeOptions.mLineCap)); +} + +static inline cairo_fill_rule_t +GfxFillRuleToCairoFillRule(FillRule rule) +{ + switch (rule) + { + case FILL_WINDING: + return CAIRO_FILL_RULE_WINDING; + case FILL_EVEN_ODD: + return CAIRO_FILL_RULE_EVEN_ODD; + } + + return CAIRO_FILL_RULE_WINDING; +} + +} +} + +#endif /* MOZILLA_GFX_HELPERSCAIRO_H_ */
--- a/gfx/2d/Makefile.in +++ b/gfx/2d/Makefile.in @@ -64,16 +64,17 @@ EXPORTS_mozilla/gfx = \ Types.h \ $(NULL) CPPSRCS = \ Factory.cpp \ Matrix.cpp \ DrawTargetCairo.cpp \ SourceSurfaceCairo.cpp \ + PathCairo.cpp \ Blur.cpp \ $(NULL) DEFINES += -DMOZ_GFX -DUSE_CAIRO ifdef MOZ_ENABLE_SKIA CPPSRCS += \
new file mode 100644 --- /dev/null +++ b/gfx/2d/PathCairo.cpp @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PathCairo.h" +#include <math.h> +#include "DrawTargetCairo.h" +#include "Logging.h" +#include "PathHelpers.h" +#include "HelpersCairo.h" + +namespace mozilla { +namespace gfx { + +CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, + FillRule aFillRule, + const Matrix& aTransform /* = Matrix() */) + : mTransform(aTransform) + , mContext(aCtx) + , mDrawTarget(aDrawTarget) + , mFillRule(aFillRule) +{ + cairo_reference(mContext); + cairo_set_fill_rule(mContext, GfxFillRuleToCairoFillRule(mFillRule)); + + // If we don't have an identity transformation, we need to have a separate + // context from the draw target, because we can't set a transformation on its + // context. + if (mDrawTarget && !mTransform.IsIdentity()) { + DuplicateContextAndPath(mTransform); + + ForgetDrawTarget(); + } else if (mDrawTarget) { + mDrawTarget->SetPathObserver(this); + } +} + +CairoPathContext::~CairoPathContext() +{ + if (mDrawTarget) { + mDrawTarget->SetPathObserver(NULL); + } + cairo_destroy(mContext); +} + +void +CairoPathContext::DuplicateContextAndPath(const Matrix& aMatrix /* = Matrix() */) +{ + // Duplicate the path. + cairo_path_t* path = cairo_copy_path(mContext); + cairo_fill_rule_t rule = cairo_get_fill_rule(mContext); + + // Duplicate the context. + cairo_surface_t* surf = cairo_get_target(mContext); + cairo_destroy(mContext); + mContext = cairo_create(surf); + + // Transform the context. + cairo_matrix_t matrix; + GfxMatrixToCairoMatrix(aMatrix, matrix); + cairo_transform(mContext, &matrix); + + // Add the path, and throw away our duplicate. + cairo_append_path(mContext, path); + cairo_set_fill_rule(mContext, rule); + cairo_path_destroy(path); +} + +void +CairoPathContext::PathWillChange() +{ + // Once we've copied out the context's path, there's no use to holding on to + // the draw target. Thus, there's nothing for us to do if we're independent + // of the draw target, since we'll have already copied out the context's + // path. + if (mDrawTarget) { + // The context we point to is going to change from under us. To continue + // using this path, we need to copy it to a new context. + DuplicateContextAndPath(); + + ForgetDrawTarget(); + } +} + +void +CairoPathContext::MatrixWillChange(const Matrix& aNewMatrix) +{ + // Cairo paths are stored in device space. Since we logically operate in user + // space, we want to make it so our path will be in the same location if and + // when our path is copied out. + // To effect this, we copy out our path (which, in Cairo, implicitly converts + // to user space), then temporarily set the context to have the new + // transform. We then set the path, which ensures that the points are all + // transformed correctly. Finally, we set the matrix back to its original + // value. + cairo_path_t* path = cairo_copy_path(mContext); + + cairo_matrix_t origMatrix; + cairo_get_matrix(mContext, &origMatrix); + + cairo_matrix_t newMatrix; + GfxMatrixToCairoMatrix(aNewMatrix, newMatrix); + cairo_set_matrix(mContext, &newMatrix); + + cairo_new_path(mContext); + cairo_append_path(mContext, path); + cairo_path_destroy(path); + + cairo_set_matrix(mContext, &origMatrix); +} + +void +CairoPathContext::CopyPathTo(cairo_t* aToContext) +{ + if (aToContext != mContext) { + cairo_set_fill_rule(aToContext, GfxFillRuleToCairoFillRule(mFillRule)); + + cairo_matrix_t origMat; + cairo_get_matrix(aToContext, &origMat); + + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(mTransform, mat); + cairo_transform(aToContext, &mat); + + // cairo_copy_path gives us a user-space copy of the path, so we don't have + // to worry about transformations here. + cairo_path_t* path = cairo_copy_path(mContext); + cairo_new_path(aToContext); + cairo_append_path(aToContext, path); + cairo_path_destroy(path); + + cairo_set_matrix(aToContext, &origMat); + } +} + +void +CairoPathContext::ForgetDrawTarget() +{ + mDrawTarget = NULL; +} + +bool +CairoPathContext::ContainsPath(const Path* aPath) +{ + if (aPath->GetBackendType() != BACKEND_CAIRO) { + return false; + } + + const PathCairo* path = static_cast<const PathCairo*>(aPath); + RefPtr<CairoPathContext> ctx = const_cast<PathCairo*>(path)->GetPathContext(); + return ctx == this; +} + +PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext, + const Matrix& aTransform /* = Matrix() */) + : mFillRule(aPathContext->GetFillRule()) +{ + RefPtr<DrawTargetCairo> drawTarget = aPathContext->GetDrawTarget(); + mPathContext = new CairoPathContext(*aPathContext, drawTarget, mFillRule, + aPathContext->GetTransform() * aTransform); + + // We need to ensure that we are allowed to modify the path currently set on + // aPathContext. If we don't have a draw target, CairoPathContext's + // constructor has no way to make aPathContext duplicate its path (normally, + // calling drawTarget->SetPathObserver() would do so). In this case, we + // explicitly make aPathContext copy out its context and path, leaving our + // path alone. + if (!drawTarget) { + aPathContext->DuplicateContextAndPath(); + } +} + +PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule) + : mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule)) + , mFillRule(aFillRule) +{} + +void +PathBuilderCairo::MoveTo(const Point &aPoint) +{ + cairo_move_to(*mPathContext, aPoint.x, aPoint.y); +} + +void +PathBuilderCairo::LineTo(const Point &aPoint) +{ + cairo_line_to(*mPathContext, aPoint.x, aPoint.y); +} + +void +PathBuilderCairo::BezierTo(const Point &aCP1, + const Point &aCP2, + const Point &aCP3) +{ + cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y); +} + +void +PathBuilderCairo::QuadraticBezierTo(const Point &aCP1, + const Point &aCP2) +{ + // We need to elevate the degree of this quadratic Bézier to cubic, so we're + // going to add an intermediate control point, and recompute control point 1. + // The first and last control points remain the same. + // This formula can be found on http://fontforge.sourceforge.net/bezier.html + Point CP0 = CurrentPoint(); + Point CP1 = (CP0 + aCP1 * 2.0) / 3.0; + Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0; + Point CP3 = aCP2; + + cairo_curve_to(*mPathContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y); +} + +void +PathBuilderCairo::Close() +{ + cairo_close_path(*mPathContext); +} + +void +PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle, + float aEndAngle, bool aAntiClockwise) +{ + ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise); +} + +Point +PathBuilderCairo::CurrentPoint() const +{ + double x, y; + cairo_get_current_point(*mPathContext, &x, &y); + return Point(x, y); +} + +TemporaryRef<Path> +PathBuilderCairo::Finish() +{ + RefPtr<PathCairo> path = new PathCairo(*mPathContext, + mPathContext->GetDrawTarget(), + mFillRule, + mPathContext->GetTransform()); + return path; +} + +TemporaryRef<CairoPathContext> +PathBuilderCairo::GetPathContext() +{ + return mPathContext; +} + +PathCairo::PathCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule, const Matrix& aTransform) + : mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule, aTransform)) + , mFillRule(aFillRule) +{} + +TemporaryRef<PathBuilder> +PathCairo::CopyToBuilder(FillRule aFillRule) const +{ + // Note: This PathBuilderCairo constructor causes our mPathContext to copy + // out the path, since the path builder is going to change the path on us. + RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext); + return builder; +} + +TemporaryRef<PathBuilder> +PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const +{ + // Note: This PathBuilderCairo constructor causes our mPathContext to copy + // out the path, since the path builder is going to change the path on us. + RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext, + aTransform); + return builder; +} + +bool +PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const +{ + Matrix inverse = aTransform; + inverse.Invert(); + Point transformed = inverse * aPoint; + + return cairo_in_fill(*mPathContext, transformed.x, transformed.y); +} + +Rect +PathCairo::GetBounds(const Matrix &aTransform) const +{ + double x1, y1, x2, y2; + + cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2); + Rect bounds(x1, y1, x2 - x1, y2 - y1); + return aTransform.TransformBounds(bounds); +} + +Rect +PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions, + const Matrix &aTransform) const +{ + double x1, y1, x2, y2; + + SetCairoStrokeOptions(*mPathContext, aStrokeOptions); + + cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2); + Rect bounds(x1, y1, x2 - x1, y2 - y1); + return aTransform.TransformBounds(bounds); +} + +TemporaryRef<CairoPathContext> +PathCairo::GetPathContext() +{ + return mPathContext; +} + +void +PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget) +{ + if (mPathContext->GetContext() != aContext) { + mPathContext->CopyPathTo(aContext); + + // Since aDrawTarget wants us to be the current path on its context, we + // should also listen to it for updates to that path (as an optimization). + // The easiest way to do this is to just recreate mPathContext, since it + // registers with aDrawTarget for updates. + mPathContext = new CairoPathContext(aContext, aDrawTarget, + mPathContext->GetFillRule(), + mPathContext->GetTransform()); + } +} + +} +}
new file mode 100644 --- /dev/null +++ b/gfx/2d/PathCairo.h @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef MOZILLA_GFX_PATH_CAIRO_H_ +#define MOZILLA_GFX_PATH_CAIRO_H_ + +#include "2D.h" +#include "cairo.h" + +namespace mozilla { +namespace gfx { + +class DrawTargetCairo; + +// A reference to a cairo context that can maintain and set a path. +// +// This class exists to make it possible for us to not construct paths manually +// using cairo_path_t, which in the common case is a speed and memory +// optimization (as the cairo_t maintains the path for us, and we don't have to +// use cairo_append_path). Instead, we can share a cairo_t with a DrawTarget, +// and have it inform us when we need to make a copy of the path. +// +// Exactly one Path* object represents the current path on a given DrawTarget's +// context. That Path* object registers its CairoPathContext with the +// DrawTarget it's associated with. If that DrawTarget is going to change its +// path, it has to tell the CairoPathContext beforehand so the path can be +// saved off. +// The path ownership is transferred to every new instance of CairoPathContext +// in the constructor. We inform the draw target of the new context object, +// which causes us to save off a copy of the path, as we're not going to be +// informed upon changes any more. +// Any transformation on aCtx is not applied to this path, though a path can be +// transformed separately from its context by passing a matrix to the +// constructor. +class CairoPathContext : public RefCounted<CairoPathContext> +{ +public: + // Construct a CairoPathContext and set it to be the path observer of + // aDrawTarget. Optionally, this path can be transformed by aMatrix. + CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, + FillRule aFillRule, + const Matrix& aMatrix = Matrix()); + ~CairoPathContext(); + + // Copy the path on mContext to be the path on aToContext, if they aren't the + // same. + void CopyPathTo(cairo_t* aToContext); + + // This method must be called by the draw target before it changes the path + // currently on the cairo context. + void PathWillChange(); + + // This method must be called by the draw target whenever it is going to + // change the current transformation on mContext. + void MatrixWillChange(const Matrix& aMatrix); + + // This method must be called as the draw target is dying. In this case, we + // forget our reference to the draw target, and become the only reference to + // our context. + void ForgetDrawTarget(); + + // Create a duplicate context, and copy this path to that context. Optionally, + // the new context can be transformed. + void DuplicateContextAndPath(const Matrix& aMatrix = Matrix()); + + // Returns true if this CairoPathContext represents path. + bool ContainsPath(const Path* path); + + cairo_t* GetContext() const { return mContext; } + DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; } + Matrix GetTransform() const { return mTransform; } + FillRule GetFillRule() const { return mFillRule; } + + operator cairo_t* () const { return mContext; } + +private: // methods + CairoPathContext(const CairoPathContext&) MOZ_DELETE; + +private: // data + Matrix mTransform; + cairo_t* mContext; + // Not a RefPtr to avoid cycles. + DrawTargetCairo* mDrawTarget; + FillRule mFillRule; +}; + +class PathBuilderCairo : public PathBuilder +{ +public: + // This constructor implicitly takes ownership of aCtx by calling + // aDrawTarget->SetPathObserver(). Therefore, if the draw target has a path + // observer, this constructor will cause it to copy out its path. + // The path currently set on aCtx is not changed. + PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule); + + // This constructor, called with a CairoPathContext*, implicitly takes + // ownership of the path, and therefore makes aPathContext copy out its path + // regardless of whether it has a pointer to a DrawTargetCairo. + // The path currently set on aPathContext is not changed. + explicit PathBuilderCairo(CairoPathContext* aPathContext, + const Matrix& aTransform = Matrix()); + + virtual void MoveTo(const Point &aPoint); + virtual void LineTo(const Point &aPoint); + virtual void BezierTo(const Point &aCP1, + const Point &aCP2, + const Point &aCP3); + virtual void QuadraticBezierTo(const Point &aCP1, + const Point &aCP2); + virtual void Close(); + virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle, + float aEndAngle, bool aAntiClockwise = false); + virtual Point CurrentPoint() const; + virtual TemporaryRef<Path> Finish(); + + TemporaryRef<CairoPathContext> GetPathContext(); + +private: // methods + void SetFillRule(FillRule aFillRule); + +private: // data + RefPtr<CairoPathContext> mPathContext; + FillRule mFillRule; +}; + +class PathCairo : public Path +{ +public: + PathCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule, const Matrix& aTransform); + + virtual BackendType GetBackendType() const { return BACKEND_CAIRO; } + + virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform, + FillRule aFillRule = FILL_WINDING) const; + + virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const; + + virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const; + + virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions, + const Matrix &aTransform = Matrix()) const; + + virtual FillRule GetFillRule() const { return mFillRule; } + + TemporaryRef<CairoPathContext> GetPathContext(); + + // Set this path to be the current path for aContext (if it's not already + // aContext's path). You must pass the draw target associated with the + // context as aDrawTarget. + void CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget); + +private: + RefPtr<CairoPathContext> mPathContext; + FillRule mFillRule; +}; + +} +} + +#endif /* MOZILLA_GFX_PATH_CAIRO_H_ */