mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 918613: Convert cairo path code to use cairo_path_t. r=jrmuizel
This commit is contained in:
parent
ee910abc00
commit
d457c68e85
@ -505,7 +505,7 @@ public:
|
||||
* implementation in some backends, and more efficient implementation in
|
||||
* others.
|
||||
*/
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder) = 0;
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint = nullptr) = 0;
|
||||
|
||||
virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
|
||||
|
||||
|
@ -37,6 +37,8 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
cairo_surface_t *DrawTargetCairo::mDummySurface;
|
||||
|
||||
namespace {
|
||||
|
||||
// An RAII class to prepare to draw a context and optional path. Saves and
|
||||
@ -378,15 +380,11 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
|
||||
|
||||
DrawTargetCairo::DrawTargetCairo()
|
||||
: mContext(nullptr)
|
||||
, mPathObserver(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DrawTargetCairo::~DrawTargetCairo()
|
||||
{
|
||||
if (mPathObserver) {
|
||||
mPathObserver->ForgetDrawTarget();
|
||||
}
|
||||
cairo_destroy(mContext);
|
||||
if (mSurface) {
|
||||
cairo_surface_destroy(mSurface);
|
||||
@ -429,6 +427,18 @@ DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nul
|
||||
WillChange(aPath);
|
||||
}
|
||||
|
||||
cairo_surface_t*
|
||||
DrawTargetCairo::GetDummySurface()
|
||||
{
|
||||
if (mDummySurface) {
|
||||
return mDummySurface;
|
||||
}
|
||||
|
||||
mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
|
||||
|
||||
return mDummySurface;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
@ -708,7 +718,7 @@ DrawTargetCairo::Stroke(const Path *aPath,
|
||||
return;
|
||||
|
||||
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
|
||||
path->CopyPathTo(mContext, this);
|
||||
path->SetPathOnContext(mContext);
|
||||
|
||||
DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
|
||||
}
|
||||
@ -724,7 +734,7 @@ DrawTargetCairo::Fill(const Path *aPath,
|
||||
return;
|
||||
|
||||
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
|
||||
path->CopyPathTo(mContext, this);
|
||||
path->SetPathOnContext(mContext);
|
||||
|
||||
DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
|
||||
}
|
||||
@ -836,7 +846,7 @@ DrawTargetCairo::PushClip(const Path *aPath)
|
||||
cairo_save(mContext);
|
||||
|
||||
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
|
||||
path->CopyPathTo(mContext, this);
|
||||
path->SetPathOnContext(mContext);
|
||||
cairo_clip_preserve(mContext);
|
||||
}
|
||||
|
||||
@ -861,9 +871,7 @@ DrawTargetCairo::PopClip()
|
||||
TemporaryRef<PathBuilder>
|
||||
DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const
|
||||
{
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mContext,
|
||||
const_cast<DrawTargetCairo*>(this),
|
||||
aFillRule);
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
|
||||
|
||||
return builder;
|
||||
}
|
||||
@ -1071,21 +1079,6 @@ void
|
||||
DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
|
||||
{
|
||||
MarkSnapshotIndependent();
|
||||
|
||||
if (mPathObserver &&
|
||||
(!aPath || !mPathObserver->ContainsPath(aPath))) {
|
||||
mPathObserver->PathWillChange();
|
||||
mPathObserver = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver)
|
||||
{
|
||||
if (mPathObserver && mPathObserver != aPathObserver) {
|
||||
mPathObserver->PathWillChange();
|
||||
}
|
||||
mPathObserver = aPathObserver;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -142,8 +142,6 @@ public:
|
||||
|
||||
bool Init(cairo_surface_t* aSurface, const IntSize& aSize);
|
||||
|
||||
void SetPathObserver(CairoPathContext* aPathObserver);
|
||||
|
||||
virtual void SetTransform(const Matrix& aTransform);
|
||||
|
||||
// Call to set up aContext for drawing (with the current transform, etc).
|
||||
@ -151,6 +149,8 @@ public:
|
||||
// Implicitly calls WillChange(aPath).
|
||||
void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr);
|
||||
|
||||
static cairo_surface_t *GetDummySurface();
|
||||
|
||||
private: // methods
|
||||
// Init cairo surface without doing a cairo_surface_reference() call.
|
||||
bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize);
|
||||
@ -181,12 +181,7 @@ private: // data
|
||||
// The latest snapshot of this surface. This needs to be told when this
|
||||
// target is modified. We keep it alive as a cache.
|
||||
RefPtr<SourceSurfaceCairo> mSnapshot;
|
||||
|
||||
// It is safe to use a regular pointer here because the CairoPathContext will
|
||||
// deregister itself on destruction. Using a RefPtr would extend the life-
|
||||
// span of the CairoPathContext. This causes a problem when
|
||||
// PathBuilderCairo.Finish()
|
||||
mutable CairoPathContext* mPathObserver;
|
||||
static cairo_surface_t *mDummySurface;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,138 +13,37 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget)
|
||||
: mContext(aCtx)
|
||||
, mDrawTarget(aDrawTarget)
|
||||
PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
|
||||
: mFillRule(aFillRule)
|
||||
{
|
||||
cairo_reference(mContext);
|
||||
|
||||
// A new path in the DrawTarget's context.
|
||||
aDrawTarget->SetPathObserver(this);
|
||||
cairo_new_path(mContext);
|
||||
}
|
||||
|
||||
CairoPathContext::CairoPathContext(CairoPathContext& aPathContext)
|
||||
: mContext(aPathContext.mContext)
|
||||
, mDrawTarget(nullptr)
|
||||
{
|
||||
cairo_reference(mContext);
|
||||
DuplicateContextAndPath();
|
||||
}
|
||||
|
||||
CairoPathContext::~CairoPathContext()
|
||||
{
|
||||
if (mDrawTarget) {
|
||||
DrawTargetCairo* drawTarget = mDrawTarget;
|
||||
ForgetDrawTarget();
|
||||
|
||||
// We need to set mDrawTarget to nullptr before we tell DrawTarget otherwise
|
||||
// we will think we need to make a defensive copy of the path.
|
||||
drawTarget->SetPathObserver(nullptr);
|
||||
}
|
||||
cairo_destroy(mContext);
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::DuplicateContextAndPath()
|
||||
{
|
||||
// Duplicate the path.
|
||||
cairo_path_t* path = cairo_copy_path(mContext);
|
||||
|
||||
// Duplicate the context.
|
||||
cairo_surface_t* surf = cairo_get_target(mContext);
|
||||
cairo_matrix_t matrix;
|
||||
cairo_get_matrix(mContext, &matrix);
|
||||
cairo_destroy(mContext);
|
||||
|
||||
mContext = cairo_create(surf);
|
||||
|
||||
// Set the matrix to match the source context so that the path is copied in
|
||||
// device space. After this point it doesn't matter what the transform is
|
||||
// set to because it's always swapped out before use.
|
||||
cairo_set_matrix(mContext, &matrix);
|
||||
|
||||
// Add the path, and throw away our duplicate.
|
||||
cairo_append_path(mContext, path);
|
||||
cairo_path_destroy(path);
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::ForgetDrawTarget()
|
||||
{
|
||||
// We don't need to set the path observer back to nullptr in this case
|
||||
// because ForgetDrawTarget() is trigged when the target has been
|
||||
// grabbed by another path observer.
|
||||
mDrawTarget = nullptr;
|
||||
}
|
||||
|
||||
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::CopyPathTo(cairo_t* aToContext, Matrix& aTransform)
|
||||
{
|
||||
if (aToContext != mContext) {
|
||||
CairoTempMatrix tempMatrix(mContext, aTransform);
|
||||
cairo_path_t* path = cairo_copy_path(mContext);
|
||||
cairo_new_path(aToContext);
|
||||
cairo_append_path(aToContext, path);
|
||||
cairo_path_destroy(path);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
FillRule aFillRule,
|
||||
const Matrix& aTransform /* = Matrix() */)
|
||||
: mPathContext(aPathContext)
|
||||
, mTransform(aTransform)
|
||||
, mFillRule(aFillRule)
|
||||
{}
|
||||
|
||||
PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule)
|
||||
: mPathContext(new CairoPathContext(aCtx, aDrawTarget))
|
||||
, mTransform(aDrawTarget->GetTransform())
|
||||
, mFillRule(aFillRule)
|
||||
{}
|
||||
|
||||
void
|
||||
PathBuilderCairo::MoveTo(const Point &aPoint)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
cairo_move_to(*mPathContext, aPoint.x, aPoint.y);
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_MOVE_TO;
|
||||
data.header.length = 2;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aPoint.x;
|
||||
data.point.y = aPoint.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mBeginPoint = mCurrentPoint = aPoint;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::LineTo(const Point &aPoint)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
cairo_line_to(*mPathContext, aPoint.x, aPoint.y);
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_LINE_TO;
|
||||
data.header.length = 2;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aPoint.x;
|
||||
data.point.y = aPoint.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = aPoint;
|
||||
}
|
||||
|
||||
void
|
||||
@ -152,18 +51,27 @@ PathBuilderCairo::BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_CURVE_TO;
|
||||
data.header.length = 4;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aCP1.x;
|
||||
data.point.y = aCP1.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aCP2.x;
|
||||
data.point.y = aCP2.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aCP3.x;
|
||||
data.point.y = aCP3.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = aCP3;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
|
||||
// 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.
|
||||
@ -173,14 +81,32 @@ PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
|
||||
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);
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_CURVE_TO;
|
||||
data.header.length = 4;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = CP1.x;
|
||||
data.point.y = CP1.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = CP2.x;
|
||||
data.point.y = CP2.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = CP3.x;
|
||||
data.point.y = CP3.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = aCP2;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::Close()
|
||||
{
|
||||
PrepareForWrite();
|
||||
cairo_close_path(*mPathContext);
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_CLOSE_PATH;
|
||||
data.header.length = 1;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = mBeginPoint;
|
||||
}
|
||||
|
||||
void
|
||||
@ -193,74 +119,78 @@ PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
Point
|
||||
PathBuilderCairo::CurrentPoint() const
|
||||
{
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
double x, y;
|
||||
cairo_get_current_point(*mPathContext, &x, &y);
|
||||
return Point((Float)x, (Float)y);
|
||||
return mCurrentPoint;
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
PathBuilderCairo::Finish()
|
||||
{
|
||||
return new PathCairo(mPathContext, mTransform, mFillRule);
|
||||
return new PathCairo(mFillRule, mPathData, mCurrentPoint);
|
||||
}
|
||||
|
||||
TemporaryRef<CairoPathContext>
|
||||
PathBuilderCairo::GetPathContext()
|
||||
PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
|
||||
: mFillRule(aFillRule)
|
||||
, mContainingContext(nullptr)
|
||||
, mCurrentPoint(aCurrentPoint)
|
||||
{
|
||||
return mPathContext;
|
||||
mPathData.swap(aPathData);
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::PrepareForWrite()
|
||||
PathCairo::PathCairo(cairo_t *aContext)
|
||||
: mFillRule(FILL_WINDING)
|
||||
, mContainingContext(nullptr)
|
||||
{
|
||||
// Only PathBuilder and PathCairo maintain references to CairoPathContext.
|
||||
// DrawTarget does not. If we're sharing a reference to the context then we
|
||||
// need to create a copy that we can modify. This provides copy on write
|
||||
// behaviour.
|
||||
if (mPathContext->refCount() != 1) {
|
||||
mPathContext = new CairoPathContext(*mPathContext);
|
||||
cairo_path_t *path = cairo_copy_path(aContext);
|
||||
|
||||
// XXX - mCurrentPoint is not properly set here, the same is true for the
|
||||
// D2D Path code, we never require current point when hitting this codepath
|
||||
// but this should be fixed.
|
||||
for (int i = 0; i < path->num_data; i++) {
|
||||
mPathData.push_back(path->data[i]);
|
||||
}
|
||||
|
||||
cairo_path_destroy(path);
|
||||
}
|
||||
|
||||
PathCairo::~PathCairo()
|
||||
{
|
||||
if (mContainingContext) {
|
||||
cairo_destroy(mContainingContext);
|
||||
}
|
||||
}
|
||||
|
||||
PathCairo::PathCairo(CairoPathContext* aPathContext, Matrix& aTransform,
|
||||
FillRule aFillRule)
|
||||
: mPathContext(aPathContext)
|
||||
, mTransform(aTransform)
|
||||
, mFillRule(aFillRule)
|
||||
{}
|
||||
|
||||
TemporaryRef<PathBuilder>
|
||||
PathCairo::CopyToBuilder(FillRule aFillRule) const
|
||||
{
|
||||
return new PathBuilderCairo(mPathContext, aFillRule, mTransform);
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
|
||||
|
||||
builder->mPathData = mPathData;
|
||||
builder->mCurrentPoint = mCurrentPoint;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
TemporaryRef<PathBuilder>
|
||||
PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
|
||||
{
|
||||
// We are given the transform we would apply from device space to user space.
|
||||
// However in cairo our path is in device space so we view the transform as
|
||||
// being the other way round. We therefore need to apply the inverse transform
|
||||
// to our current cairo transform.
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
|
||||
|
||||
return new PathBuilderCairo(mPathContext, aFillRule, mTransform * inverse);
|
||||
AppendPathToBuilder(builder, &aTransform);
|
||||
builder->mCurrentPoint = aTransform * mCurrentPoint;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
bool
|
||||
PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
|
||||
{
|
||||
CairoTempMatrix temp(*mPathContext, mTransform);
|
||||
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
Point transformed = inverse * aPoint;
|
||||
|
||||
// Needs the correct fill rule set.
|
||||
cairo_set_fill_rule(*mPathContext, GfxFillRuleToCairoFillRule(mFillRule));
|
||||
return cairo_in_fill(*mPathContext, transformed.x, transformed.y);
|
||||
EnsureContainingContext();
|
||||
|
||||
return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -268,24 +198,25 @@ PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
|
||||
const Point &aPoint,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
CairoTempMatrix temp(*mPathContext, mTransform);
|
||||
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
Point transformed = inverse * aPoint;
|
||||
|
||||
SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
|
||||
return cairo_in_stroke(*mPathContext, transformed.x, transformed.y);
|
||||
EnsureContainingContext();
|
||||
|
||||
SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
|
||||
|
||||
return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
|
||||
}
|
||||
|
||||
Rect
|
||||
PathCairo::GetBounds(const Matrix &aTransform) const
|
||||
{
|
||||
CairoTempMatrix temp(*mPathContext, mTransform);
|
||||
EnsureContainingContext();
|
||||
|
||||
double x1, y1, x2, y2;
|
||||
|
||||
cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2);
|
||||
cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
|
||||
Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
|
||||
return aTransform.TransformBounds(bounds);
|
||||
}
|
||||
@ -294,28 +225,69 @@ Rect
|
||||
PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
CairoTempMatrix temp(*mPathContext, mTransform);
|
||||
EnsureContainingContext();
|
||||
|
||||
double x1, y1, x2, y2;
|
||||
|
||||
SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
|
||||
SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
|
||||
|
||||
cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2);
|
||||
cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
|
||||
Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
|
||||
return aTransform.TransformBounds(bounds);
|
||||
}
|
||||
|
||||
TemporaryRef<CairoPathContext>
|
||||
PathCairo::GetPathContext()
|
||||
void
|
||||
PathCairo::EnsureContainingContext() const
|
||||
{
|
||||
return mPathContext;
|
||||
if (mContainingContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
|
||||
|
||||
SetPathOnContext(mContainingContext);
|
||||
}
|
||||
|
||||
void
|
||||
PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget)
|
||||
PathCairo::SetPathOnContext(cairo_t *aContext) const
|
||||
{
|
||||
mPathContext->CopyPathTo(aContext, mTransform);
|
||||
// Needs the correct fill rule set.
|
||||
cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
|
||||
|
||||
cairo_new_path(aContext);
|
||||
|
||||
if (mPathData.size()) {
|
||||
cairo_path_t path;
|
||||
path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
|
||||
path.num_data = mPathData.size();
|
||||
path.status = CAIRO_STATUS_SUCCESS;
|
||||
cairo_append_path(aContext, &path);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
|
||||
{
|
||||
if (aTransform) {
|
||||
int i = 0;
|
||||
while (i < mPathData.size()) {
|
||||
uint32_t pointCount = mPathData[i].header.length - 1;
|
||||
aBuilder->mPathData.push_back(mPathData[i]);
|
||||
i++;
|
||||
for (int c = 0; c < pointCount; c++) {
|
||||
cairo_path_data_t data;
|
||||
Point newPoint = *aTransform * Point(mPathData[i].point.x, mPathData[i].point.y);
|
||||
data.point.x = newPoint.x;
|
||||
data.point.y = newPoint.y;
|
||||
aBuilder->mPathData.push_back(data);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < mPathData.size(); i++) {
|
||||
aBuilder->mPathData.push_back(mPathData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,86 +8,18 @@
|
||||
|
||||
#include "2D.h"
|
||||
#include "cairo.h"
|
||||
#include <vector>
|
||||
|
||||
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 new empty CairoPathContext that uses the given draw target and
|
||||
// its cairo context. Using the existing context may save having to copy the
|
||||
// path later.
|
||||
CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget);
|
||||
|
||||
// Copy the path.
|
||||
CairoPathContext(CairoPathContext& aPathContext);
|
||||
|
||||
~CairoPathContext();
|
||||
|
||||
// Copy the path on mContext to be the path on aToContext, if they aren't the
|
||||
// same. At this point we set the fill rule for the destination context as
|
||||
// there is little point in doing this earlier.
|
||||
void CopyPathTo(cairo_t* aToContext, Matrix& aTransform);
|
||||
|
||||
// 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 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.
|
||||
void DuplicateContextAndPath();
|
||||
|
||||
// Returns true if this CairoPathContext represents path.
|
||||
bool ContainsPath(const Path* path);
|
||||
|
||||
cairo_t* GetContext() const { return mContext; }
|
||||
DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; }
|
||||
operator cairo_t* () const { return mContext; }
|
||||
|
||||
private: // data
|
||||
cairo_t* mContext;
|
||||
// Not a RefPtr to avoid cycles.
|
||||
DrawTargetCairo* mDrawTarget;
|
||||
};
|
||||
class PathCairo;
|
||||
|
||||
class PathBuilderCairo : public PathBuilder
|
||||
{
|
||||
public:
|
||||
// Creates a new empty path. It also 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.
|
||||
PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule);
|
||||
|
||||
// Creates a path builder out of an existing CairoPathContext with a new fill
|
||||
// rule and transform.
|
||||
PathBuilderCairo(CairoPathContext* aContext, FillRule aFillRule, const Matrix& aTransform = Matrix());
|
||||
PathBuilderCairo(FillRule aFillRule);
|
||||
|
||||
virtual void MoveTo(const Point &aPoint);
|
||||
virtual void LineTo(const Point &aPoint);
|
||||
@ -102,20 +34,23 @@ public:
|
||||
virtual Point CurrentPoint() const;
|
||||
virtual TemporaryRef<Path> Finish();
|
||||
|
||||
TemporaryRef<CairoPathContext> GetPathContext();
|
||||
|
||||
private: // data
|
||||
void PrepareForWrite();
|
||||
friend class PathCairo;
|
||||
|
||||
RefPtr<CairoPathContext> mPathContext;
|
||||
Matrix mTransform;
|
||||
FillRule mFillRule;
|
||||
std::vector<cairo_path_data_t> mPathData;
|
||||
// It's easiest to track this here, parsing the path data to find the current
|
||||
// point is a little tricky.
|
||||
Point mCurrentPoint;
|
||||
Point mBeginPoint;
|
||||
};
|
||||
|
||||
class PathCairo : public Path
|
||||
{
|
||||
public:
|
||||
PathCairo(CairoPathContext* aPathContex, Matrix& aTransform, FillRule aFillRule);
|
||||
PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint);
|
||||
PathCairo(cairo_t *aContext);
|
||||
~PathCairo();
|
||||
|
||||
virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
|
||||
|
||||
@ -136,17 +71,16 @@ public:
|
||||
|
||||
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);
|
||||
void SetPathOnContext(cairo_t *aContext) const;
|
||||
|
||||
void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const;
|
||||
private:
|
||||
RefPtr<CairoPathContext> mPathContext;
|
||||
Matrix mTransform;
|
||||
void EnsureContainingContext() const;
|
||||
|
||||
FillRule mFillRule;
|
||||
std::vector<cairo_path_data_t> mPathData;
|
||||
mutable cairo_t *mContainingContext;
|
||||
Point mCurrentPoint;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#ifdef USE_CAIRO
|
||||
#include "PathCairo.h"
|
||||
#include "DrawTargetCairo.h"
|
||||
#include "HelpersCairo.h"
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
@ -74,13 +76,18 @@ ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *a
|
||||
if (aTarget->GetType() == BACKEND_CAIRO) {
|
||||
MOZ_ASSERT(mScaledFont);
|
||||
|
||||
RefPtr<PathBuilder> builder_iface = aTarget->CreatePathBuilder();
|
||||
PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(builder_iface.get());
|
||||
DrawTarget *dt = const_cast<DrawTarget*>(aTarget);
|
||||
cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NATIVE_SURFACE_CAIRO_CONTEXT));
|
||||
|
||||
// Manually build the path for the PathBuilder.
|
||||
RefPtr<CairoPathContext> context = builder->GetPathContext();
|
||||
bool isNewContext = !ctx;
|
||||
if (!ctx) {
|
||||
ctx = cairo_create(DrawTargetCairo::GetDummySurface());
|
||||
cairo_matrix_t mat;
|
||||
GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat);
|
||||
cairo_set_matrix(ctx, &mat);
|
||||
}
|
||||
|
||||
cairo_set_scaled_font(*context, mScaledFont);
|
||||
cairo_set_scaled_font(ctx, mScaledFont);
|
||||
|
||||
// Convert our GlyphBuffer into an array of Cairo glyphs.
|
||||
std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
|
||||
@ -90,23 +97,33 @@ ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *a
|
||||
glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
|
||||
}
|
||||
|
||||
cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
|
||||
cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
|
||||
|
||||
return builder->Finish();
|
||||
RefPtr<PathCairo> newPath = new PathCairo(ctx);
|
||||
if (isNewContext) {
|
||||
cairo_destroy(ctx);
|
||||
}
|
||||
|
||||
return newPath;
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
|
||||
ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
|
||||
{
|
||||
#ifdef USE_CAIRO
|
||||
PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
|
||||
|
||||
RefPtr<CairoPathContext> context = builder->GetPathContext();
|
||||
|
||||
cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface());
|
||||
|
||||
cairo_set_scaled_font(*context, mScaledFont);
|
||||
if (aTransformHint) {
|
||||
cairo_matrix_t mat;
|
||||
GfxMatrixToCairoMatrix(*aTransformHint, mat);
|
||||
cairo_set_matrix(ctx, &mat);
|
||||
}
|
||||
|
||||
// Convert our GlyphBuffer into an array of Cairo glyphs.
|
||||
std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
|
||||
@ -116,7 +133,13 @@ ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBu
|
||||
glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
|
||||
}
|
||||
|
||||
cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
|
||||
cairo_set_scaled_font(ctx, mScaledFont);
|
||||
cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
|
||||
|
||||
RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
|
||||
cairo_destroy(ctx);
|
||||
|
||||
cairoPath->AppendPathToBuilder(builder);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ public:
|
||||
|
||||
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
|
||||
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
|
||||
|
||||
float GetSize() { return mSize; }
|
||||
|
||||
|
@ -324,7 +324,7 @@ ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget
|
||||
}
|
||||
|
||||
void
|
||||
ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
|
||||
ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
|
||||
{
|
||||
// XXX - Check path builder type!
|
||||
PathBuilderD2D *pathBuilderD2D =
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
virtual FontType GetType() const { return FONT_DWRITE; }
|
||||
|
||||
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
|
||||
|
||||
void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
|
||||
|
||||
|
@ -102,7 +102,7 @@ ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aT
|
||||
}
|
||||
|
||||
void
|
||||
ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
|
||||
ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
|
||||
{
|
||||
PathBuilderCG *pathBuilderCG =
|
||||
static_cast<PathBuilderCG*>(aBuilder);
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
virtual SkTypeface* GetSkTypeface();
|
||||
#endif
|
||||
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
|
||||
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
|
||||
virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
|
||||
|
||||
private:
|
||||
|
@ -2330,7 +2330,8 @@ struct GlyphBufferAzure {
|
||||
}
|
||||
if (aDrawMode & gfxFont::GLYPH_PATH) {
|
||||
aThebesContext->EnsurePathBuilder();
|
||||
aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder);
|
||||
Matrix mat = aDT->GetTransform();
|
||||
aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, &mat);
|
||||
}
|
||||
if ((aDrawMode & (gfxFont::GLYPH_STROKE | gfxFont::GLYPH_STROKE_UNDERNEATH)) ==
|
||||
gfxFont::GLYPH_STROKE) {
|
||||
|
Loading…
Reference in New Issue
Block a user