Bug 715652 - Implement significantly more of the 2D API for the Cairo backend. r=jrmuizel

--HG--
extra : rebase_source : b23314691fef65db2518d3e738a66039e3c18909
This commit is contained in:
Joe Drew 2012-01-09 16:50:01 -05:00
Родитель 64c03d88cb
Коммит 188a24ed30
5 изменённых файлов: 916 добавлений и 76 удалений

Просмотреть файл

@ -35,24 +35,54 @@
* ***** END LICENSE BLOCK ***** */
#include "DrawTargetCairo.h"
#include "SourceSurfaceCairo.h"
#include "cairo.h"
#include "Blur.h"
#ifdef CAIRO_HAS_QUARTZ_SURFACE
#include "cairo-quartz.h"
#include <ApplicationServices/ApplicationServices.h>
#endif
#ifdef CAIRO_HAS_XLIB_SURFACE
#include "cairo-xlib.h"
#endif
#include <algorithm>
namespace mozilla {
namespace gfx {
cairo_operator_t
static cairo_operator_t
GfxOpToCairoOp(CompositionOp op)
{
switch (op)
{
case OP_OVER:
return CAIRO_OPERATOR_OVER;
case OP_SOURCE:
return CAIRO_OPERATOR_SOURCE;
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;
}
@ -60,7 +90,7 @@ GfxOpToCairoOp(CompositionOp op)
return CAIRO_OPERATOR_OVER;
}
cairo_filter_t
static cairo_filter_t
GfxFilterToCairoFilter(Filter filter)
{
switch (filter)
@ -74,7 +104,23 @@ GfxFilterToCairoFilter(Filter filter)
return CAIRO_FILTER_BILINEAR;
}
cairo_format_t
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;
}
return CAIRO_EXTEND_PAD;
}
static cairo_format_t
GfxFormatToCairoFormat(SurfaceFormat format)
{
switch (format)
@ -90,12 +136,223 @@ GfxFormatToCairoFormat(SurfaceFormat format)
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;
}
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;
}
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;
}
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;
}
static bool
GetCairoSurfaceSize(cairo_surface_t* surface, IntSize& size)
{
switch (cairo_surface_get_type(surface))
{
case CAIRO_SURFACE_TYPE_IMAGE:
{
size.width = cairo_image_surface_get_width(surface);
size.height = cairo_image_surface_get_height(surface);
return true;
}
#ifdef CAIRO_HAS_XLIB_SURFACE
case CAIRO_SURFACE_TYPE_XLIB:
{
size.width = cairo_xlib_surface_get_width(surface);
size.height = cairo_xlib_surface_get_height(surface);
return true;
}
#endif
#ifdef CAIRO_HAS_QUARTZ_SURFACE
case CAIRO_SURFACE_TYPE_QUARTZ:
{
CGContextRef cgc = cairo_quartz_surface_get_cg_context(surface);
// It's valid to call these CGBitmapContext functions on non-bitmap
// contexts; they'll just return 0 in that case.
size.width = CGBitmapContextGetWidth(cgc);
size.height = CGBitmapContextGetWidth(cgc);
return size.width != 0;
}
#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*
GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
{
cairo_pattern_t* pat = NULL;
switch (aPattern.GetType())
{
case PATTERN_COLOR:
{
Color color = static_cast<const ColorPattern&>(aPattern).mColor;
pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha);
break;
}
case PATTERN_SURFACE:
{
const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
cairo_surface_t* surf = NULL;
if (pattern.mSurface->GetType() == SURFACE_CAIRO) {
const SourceSurfaceCairo* sourcesurf = static_cast<const SourceSurfaceCairo*>(pattern.mSurface.get());
surf = sourcesurf->GetSurface();
cairo_surface_reference(surf);
} else if (pattern.mSurface->GetType() == SURFACE_CAIRO_IMAGE) {
const DataSourceSurfaceCairo* sourcesurf =
static_cast<const DataSourceSurfaceCairo*>(pattern.mSurface.get());
surf = sourcesurf->GetSurface();
cairo_surface_reference(surf);
} else {
RefPtr<DataSourceSurface> sourcesurf = pattern.mSurface->GetDataSurface();
surf = cairo_image_surface_create_for_data(sourcesurf->GetData(),
GfxFormatToCairoFormat(sourcesurf->GetFormat()),
sourcesurf->GetSize().width,
sourcesurf->GetSize().height,
sourcesurf->Stride());
}
pat = cairo_pattern_create_for_surface(surf);
cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(pattern.mFilter));
cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode));
cairo_surface_destroy(surf);
break;
}
case PATTERN_LINEAR_GRADIENT:
{
const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
RefPtr<GradientStops> stops = pattern.mStops;
if (stops->GetBackendType() == BACKEND_CAIRO) {
pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
pattern.mEnd.x, pattern.mEnd.y);
const std::vector<GradientStop>& stops =
static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
for (std::vector<GradientStop>::const_iterator i = stops.begin();
i != stops.end();
++i) {
cairo_pattern_add_color_stop_rgba(pat, i->offset, i->color.r,
i->color.g, i->color.b,
i->color.a);
}
}
break;
}
case PATTERN_RADIAL_GRADIENT:
{
const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
RefPtr<GradientStops> stops = pattern.mStops;
if (stops->GetBackendType() == BACKEND_CAIRO) {
pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1,
pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2);
const std::vector<GradientStop>& stops =
static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
for (std::vector<GradientStop>::const_iterator i = stops.begin();
i != stops.end();
++i) {
cairo_pattern_add_color_stop_rgba(pat, i->offset, i->color.r,
i->color.g, i->color.b,
i->color.a);
}
}
break;
}
}
return pat;
}
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)
return false;
return true;
}
DrawTargetCairo::DrawTargetCairo()
: mContext(NULL)
{
@ -103,12 +360,30 @@ DrawTargetCairo::DrawTargetCairo()
DrawTargetCairo::~DrawTargetCairo()
{
MarkSnapshotsIndependent();
cairo_destroy(mContext);
}
IntSize
DrawTargetCairo::GetSize()
{
return IntSize();
}
TemporaryRef<SourceSurface>
DrawTargetCairo::Snapshot()
{
cairo_surface_t* csurf = cairo_get_target(mContext);
IntSize size;
if (GetCairoSurfaceSize(csurf, size)) {
cairo_content_t content = cairo_surface_get_content(csurf);
RefPtr<SourceSurfaceCairo> surf = new SourceSurfaceCairo(csurf, size,
CairoContentToGfxFormat(content),
this);
AppendSnapshot(surf);
return surf;
}
return NULL;
}
@ -119,6 +394,16 @@ DrawTargetCairo::Flush()
cairo_surface_flush(surf);
}
void
DrawTargetCairo::PrepareForDrawing(cairo_t* aContext)
{
MarkChanged();
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(mTransform, mat);
cairo_set_matrix(aContext, &mat);
}
void
DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
@ -126,12 +411,14 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aOptions)
{
PrepareForDrawing(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());
cairo_matrix_translate(&src_mat, aSource.X(), aSource.Y());
cairo_surface_t* surf = NULL;
if (aSurface->GetType() == SURFACE_CAIRO) {
@ -142,31 +429,304 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
cairo_pattern_set_matrix(pat, &src_mat);
cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter));
cairo_save(mContext);
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
cairo_rectangle(mContext, aDest.X(), aDest.Y(),
aDest.Width(), aDest.Height());
cairo_fill(mContext);
cairo_translate(mContext, aDest.X(), aDest.Y());
cairo_set_source(mContext, pat);
cairo_new_path(mContext);
cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
cairo_clip(mContext);
cairo_paint_with_alpha(mContext, aOptions.mAlpha);
cairo_restore(mContext);
cairo_pattern_destroy(pat);
}
void
DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
const Point &aOffset,
Float aSigma,
CompositionOp aOperator)
{
MarkChanged();
if (aSurface->GetType() != SURFACE_CAIRO) {
return;
}
SourceSurfaceCairo* sourcesurf = static_cast<SourceSurfaceCairo*>(aSurface);
Float width = aSurface->GetSize().width,
height = aSurface->GetSize().height;
Rect extents(0, 0, width, height);
AlphaBoxBlur blur(extents, IntSize(0, 0),
AlphaBoxBlur::CalculateBlurRadius(Point(aSigma, aSigma)),
NULL, NULL);
if (!blur.GetData()) {
return;
}
IntSize blursize = blur.GetSize();
cairo_surface_t* blursurf = cairo_image_surface_create_for_data(blur.GetData(),
CAIRO_FORMAT_A8,
blursize.width,
blursize.height,
blur.GetStride());
// Draw the source surface into the surface we're going to blur.
cairo_surface_t* surf = sourcesurf->GetSurface();
cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
cairo_t* ctx = cairo_create(blursurf);
cairo_set_source(ctx, pat);
IntRect blurrect = blur.GetRect();
cairo_new_path(ctx);
cairo_rectangle(ctx, blurrect.x, blurrect.y, blurrect.width, blurrect.height);
cairo_clip(ctx);
cairo_paint(ctx);
cairo_destroy(ctx);
// Blur the result, then use that blurred result as a mask to draw the shadow
// colour to the surface.
blur.Blur();
cairo_save(mContext);
cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
cairo_identity_matrix(mContext);
cairo_translate(mContext, aDest.x, aDest.y);
cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
// Now that the shadow has been drawn, we can draw the surface on top.
cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
cairo_set_source(mContext, pat);
cairo_new_path(mContext);
cairo_rectangle(mContext, 0, 0, width, height);
cairo_clip(mContext);
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,
const StrokeOptions& aStrokeOptions,
const DrawOptions& aOptions,
DrawPatternType aDrawType)
{
cairo_save(mContext);
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
bool needIntermediate = NeedIntermediateSurface(aPattern, aOptions);
if (needIntermediate) {
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());
if (aDrawType == DRAW_STROKE) {
SetStrokeOptions(mContext, aStrokeOptions);
cairo_stroke(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_pattern_destroy(pat);
}
if (needIntermediate) {
cairo_pop_group_to_source(mContext);
cairo_paint_with_alpha(mContext, aOptions.mAlpha);
}
cairo_restore(mContext);
}
void
DrawTargetCairo::FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
PrepareForDrawing(mContext);
DrawPattern(aRect, aPattern, StrokeOptions(), aOptions, DRAW_FILL);
}
void
DrawTargetCairo::CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination)
{
PrepareForDrawing(mContext);
}
void
DrawTargetCairo::ClearRect(const Rect& aRect)
{
PrepareForDrawing(mContext);
cairo_save(mContext);
cairo_new_path(mContext);
cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(mContext, aRect.X(), aRect.Y(),
aRect.Width(), aRect.Height());
cairo_fill(mContext);
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
cairo_restore(mContext);
}
if (aPattern.GetType() == PATTERN_COLOR) {
Color color = static_cast<const ColorPattern&>(aPattern).mColor;
cairo_set_source_rgba(mContext, color.r, color.g,
color.b, color.a);
void
DrawTargetCairo::StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
PrepareForDrawing(mContext);
DrawPattern(aRect, 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);
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_fill(mContext);
cairo_restore(mContext);
}
void
DrawTargetCairo::Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
PrepareForDrawing(mContext);
}
void
DrawTargetCairo::Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions /* = DrawOptions() */)
{
PrepareForDrawing(mContext);
}
void
DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
PrepareForDrawing(mContext);
}
void
DrawTargetCairo::PushClip(const Path *aPath)
{
}
void
DrawTargetCairo::PopClip()
{
}
TemporaryRef<PathBuilder>
DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const
{
return NULL;
}
TemporaryRef<GradientStops>
DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const
{
RefPtr<GradientStopsCairo> stops = new GradientStopsCairo(aStops, aNumStops);
return stops;
}
TemporaryRef<SourceSurface>
@ -180,8 +740,7 @@ DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
aSize.width,
aSize.height,
aStride);
RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo();
source_surf->InitFromSurface(surf, aSize, aFormat);
RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
cairo_surface_destroy(surf);
return source_surf;
}
@ -189,12 +748,38 @@ DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
TemporaryRef<SourceSurface>
DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
{
return NULL;
return aSurface;
}
TemporaryRef<SourceSurface>
DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
{
if (aSurface.mType == NATIVE_SURFACE_CAIRO_SURFACE) {
IntSize size;
cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
if (GetCairoSurfaceSize(surf, size)) {
RefPtr<SourceSurfaceCairo> sourcesurf =
new SourceSurfaceCairo(surf, size, aSurface.mFormat);
return sourcesurf;
}
}
return NULL;
}
TemporaryRef<DrawTarget>
DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{
cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
GfxFormatToCairoContent(aFormat),
aSize.width, aSize.height);
if (!cairo_surface_status(similar)) {
RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
target->Init(similar);
return target;
}
return NULL;
}
@ -206,14 +791,58 @@ DrawTargetCairo::Init(cairo_surface_t* aSurface)
return true;
}
void
DrawTargetCairo::SetTransform(const Matrix& aTransform)
void *
DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType)
{
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(aTransform, mat);
cairo_set_matrix(mContext, &mat);
mTransform = aTransform;
if (aType == NATIVE_SURFACE_CAIRO_SURFACE) {
return cairo_get_target(mContext);
}
return NULL;
}
void
DrawTargetCairo::MarkSnapshotsIndependent()
{
// Make a copy of the vector, since MarkIndependent implicitly modifies mSnapshots.
std::vector<SourceSurfaceCairo*> snapshots = mSnapshots;
for (std::vector<SourceSurfaceCairo*>::iterator iter = snapshots.begin();
iter != snapshots.end();
++iter) {
(*iter)->MarkIndependent();
}
}
void
DrawTargetCairo::AppendSnapshot(SourceSurfaceCairo* aSnapshot)
{
mSnapshots.push_back(aSnapshot);
}
void
DrawTargetCairo::RemoveSnapshot(SourceSurfaceCairo* aSnapshot)
{
std::vector<SourceSurfaceCairo*>::iterator iter = std::find(mSnapshots.begin(),
mSnapshots.end(),
aSnapshot);
if (iter != mSnapshots.end()) {
mSnapshots.erase(iter);
}
}
void
DrawTargetCairo::MarkChanged()
{
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();
}
}
}
}

Просмотреть файл

@ -40,9 +40,36 @@
#include "2D.h"
#include "cairo.h"
#include <vector>
namespace mozilla {
namespace gfx {
class SourceSurfaceCairo;
class GradientStopsCairo : public GradientStops
{
public:
GradientStopsCairo(GradientStop* aStops, uint32_t aNumStops)
{
for (uint32_t i = 0; i < aNumStops; ++i) {
mStops.push_back(aStops[i]);
}
}
virtual ~GradientStopsCairo() {}
const std::vector<GradientStop>& GetStops() const
{
return mStops;
}
virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
private:
std::vector<GradientStop> mStops;
};
class DrawTargetCairo : public DrawTarget
{
public:
@ -51,7 +78,7 @@ public:
virtual BackendType GetType() const { return BACKEND_CAIRO; }
virtual TemporaryRef<SourceSurface> Snapshot();
virtual IntSize GetSize() { return IntSize(); }
virtual IntSize GetSize();
virtual void Flush();
virtual void DrawSurface(SourceSurface *aSurface,
@ -64,16 +91,13 @@ public:
const Color &aColor,
const Point &aOffset,
Float aSigma,
CompositionOp aOperator)
{ }
CompositionOp aOperator);
virtual void ClearRect(const Rect &aRect)
{ }
virtual void ClearRect(const Rect &aRect);
virtual void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination)
{ }
const IntPoint &aDestination);
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
@ -81,41 +105,37 @@ public:
virtual void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions)
{ return; }
const DrawOptions &aOptions);
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions = DrawOptions())
{ return; }
{ }
virtual void PushClip(const Path *aPath) { }
virtual void PushClipRect(const Rect &aRect) { }
virtual void PopClip() { }
virtual void PushClip(const Path *aPath);
virtual void PushClipRect(const Rect &aRect)
{ }
virtual void PopClip();
virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const { return NULL; }
virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
@ -125,22 +145,43 @@ public:
virtual TemporaryRef<SourceSurface>
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
virtual TemporaryRef<DrawTarget>
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{ return NULL; }
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = EXTEND_CLAMP) const
{ return NULL; }
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
ExtendMode aExtendMode = EXTEND_CLAMP) const;
virtual void *GetNativeSurface(NativeSurfaceType aType)
{ return NULL; }
virtual void SetTransform(const Matrix& aTransform);
virtual void *GetNativeSurface(NativeSurfaceType aType);
bool Init(cairo_surface_t* aSurface);
private:
private: // methods
void PrepareForDrawing(cairo_t* aContext);
enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
void DrawPattern(const Rect& aRect,
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();
// 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;
};
}

Просмотреть файл

@ -36,18 +36,76 @@
* ***** END LICENSE BLOCK ***** */
#include "SourceSurfaceCairo.h"
#include "DrawTargetCairo.h"
#include "cairo.h"
namespace mozilla {
namespace gfx {
SourceSurfaceCairo::SourceSurfaceCairo()
static 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 SurfaceFormat
CairoFormatToSurfaceFormat(cairo_format_t format)
{
switch (format)
{
case CAIRO_FORMAT_ARGB32:
return FORMAT_B8G8R8A8;
case CAIRO_FORMAT_RGB24:
return FORMAT_B8G8R8X8;
case CAIRO_FORMAT_A8:
return FORMAT_A8;
}
return FORMAT_B8G8R8A8;
}
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;
}
return CAIRO_CONTENT_COLOR_ALPHA;
}
SourceSurfaceCairo::SourceSurfaceCairo(cairo_surface_t* aSurface,
const IntSize& aSize,
const SurfaceFormat& aFormat,
DrawTargetCairo* aDrawTarget /* = NULL */)
: mSize(aSize)
, mFormat(aFormat)
, mSurface(aSurface)
, mDrawTarget(aDrawTarget)
{
cairo_surface_reference(mSurface);
}
SourceSurfaceCairo::~SourceSurfaceCairo()
{
MarkIndependent();
cairo_surface_destroy(mSurface);
}
@ -66,26 +124,108 @@ SourceSurfaceCairo::GetFormat() const
TemporaryRef<DataSourceSurface>
SourceSurfaceCairo::GetDataSurface()
{
return NULL;
RefPtr<DataSourceSurfaceCairo> dataSurf;
if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
dataSurf = new DataSourceSurfaceCairo(mSurface);
} else {
cairo_surface_t* imageSurf = cairo_image_surface_create(GfxFormatToCairoFormat(mFormat),
mSize.width, mSize.height);
// Fill the new image surface with the contents of our surface.
cairo_t* ctx = cairo_create(imageSurf);
cairo_pattern_t* pat = cairo_pattern_create_for_surface(mSurface);
cairo_set_source(ctx, pat);
cairo_paint(ctx);
cairo_destroy(ctx);
dataSurf = new DataSourceSurfaceCairo(imageSurf);
cairo_surface_destroy(imageSurf);
}
return dataSurf;
}
cairo_surface_t*
SourceSurfaceCairo::GetSurface()
SourceSurfaceCairo::GetSurface() const
{
return mSurface;
}
bool
SourceSurfaceCairo::InitFromSurface(cairo_surface_t* aSurface,
const IntSize& aSize,
const SurfaceFormat& aFormat)
void
SourceSurfaceCairo::DrawTargetWillChange()
{
mSurface = aSurface;
cairo_surface_reference(mSurface);
mSize = aSize;
mFormat = aFormat;
if (mDrawTarget) {
mDrawTarget = NULL;
return true;
// We're about to lose our version of the surface, so make a copy of it.
cairo_surface_t* surface = cairo_surface_create_similar(mSurface,
GfxFormatToCairoContent(mFormat),
mSize.width, mSize.height);
cairo_t* ctx = cairo_create(surface);
cairo_pattern_t* pat = cairo_pattern_create_for_surface(mSurface);
cairo_set_source(ctx, pat);
cairo_paint(ctx);
cairo_destroy(ctx);
// Swap in this new surface.
cairo_surface_destroy(mSurface);
mSurface = surface;
}
}
void
SourceSurfaceCairo::MarkIndependent()
{
if (mDrawTarget) {
mDrawTarget->RemoveSnapshot(this);
mDrawTarget = NULL;
}
}
DataSourceSurfaceCairo::DataSourceSurfaceCairo(cairo_surface_t* imageSurf)
: mImageSurface(imageSurf)
{
cairo_surface_reference(mImageSurface);
}
DataSourceSurfaceCairo::~DataSourceSurfaceCairo()
{
cairo_surface_destroy(mImageSurface);
}
unsigned char *
DataSourceSurfaceCairo::GetData()
{
return cairo_image_surface_get_data(mImageSurface);
}
int32_t
DataSourceSurfaceCairo::Stride()
{
return cairo_image_surface_get_stride(mImageSurface);
}
IntSize
DataSourceSurfaceCairo::GetSize() const
{
IntSize size;
size.width = cairo_image_surface_get_width(mImageSurface);
size.height = cairo_image_surface_get_height(mImageSurface);
return size;
}
SurfaceFormat
DataSourceSurfaceCairo::GetFormat() const
{
return CairoFormatToSurfaceFormat(cairo_image_surface_get_format(mImageSurface));
}
cairo_surface_t*
DataSourceSurfaceCairo::GetSurface() const
{
return mImageSurface;
}
}

Просмотреть файл

@ -42,27 +42,56 @@
namespace mozilla {
namespace gfx {
class DrawTargetCairo;
class SourceSurfaceCairo : public SourceSurface
{
public:
SourceSurfaceCairo();
~SourceSurfaceCairo();
// Create a SourceSurfaceCairo. The surface will not be copied, but simply
// referenced.
// If aDrawTarget is non-NULL, it is assumed that this is a snapshot source
// surface, and we'll call DrawTargetCairo::RemoveSnapshot(this) on it when
// we're destroyed.
SourceSurfaceCairo(cairo_surface_t* aSurface, const IntSize& aSize,
const SurfaceFormat& aFormat,
DrawTargetCairo* aDrawTarget = NULL);
virtual ~SourceSurfaceCairo();
virtual SurfaceType GetType() const { return SURFACE_CAIRO; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
cairo_surface_t* GetSurface();
cairo_surface_t* GetSurface() const;
bool InitFromSurface(cairo_surface_t* aSurface,
const IntSize& aSize,
const SurfaceFormat& aFormat);
private: // methods
friend class DrawTargetCairo;
void DrawTargetWillChange();
void MarkIndependent();
private:
private: // data
IntSize mSize;
SurfaceFormat mFormat;
cairo_surface_t* mSurface;
DrawTargetCairo* mDrawTarget;
};
class DataSourceSurfaceCairo : public DataSourceSurface
{
public:
DataSourceSurfaceCairo(cairo_surface_t* imageSurf);
virtual ~DataSourceSurfaceCairo();
virtual unsigned char *GetData();
virtual int32_t Stride();
virtual SurfaceType GetType() const { return SURFACE_CAIRO_IMAGE; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
cairo_surface_t* GetSurface() const;
private:
cairo_surface_t* mImageSurface;
};
}

Просмотреть файл

@ -84,7 +84,8 @@ enum FontType
enum NativeSurfaceType
{
NATIVE_SURFACE_D3D10_TEXTURE
NATIVE_SURFACE_D3D10_TEXTURE,
NATIVE_SURFACE_CAIRO_SURFACE
};
enum NativeFontType