mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
f54df1b8ab
--HG-- extra : rebase_source : 6103e88ec0de91c16aa36829db1e8c988920bcfe
803 lines
24 KiB
C++
803 lines
24 KiB
C++
/* -*- 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 "DrawTargetCairo.h"
|
|
|
|
#include "SourceSurfaceCairo.h"
|
|
#include "PathCairo.h"
|
|
#include "HelpersCairo.h"
|
|
#include "ScaledFontBase.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 {
|
|
|
|
namespace {
|
|
|
|
// An RAII class to prepare to draw a context and optional path. Saves and
|
|
// restores the context on construction/destruction.
|
|
class AutoPrepareForDrawing
|
|
{
|
|
public:
|
|
AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx)
|
|
: mCtx(ctx)
|
|
{
|
|
dt->PrepareForDrawing(ctx);
|
|
cairo_save(mCtx);
|
|
}
|
|
|
|
AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path)
|
|
: mCtx(ctx)
|
|
{
|
|
dt->PrepareForDrawing(ctx, path);
|
|
cairo_save(mCtx);
|
|
}
|
|
|
|
~AutoPrepareForDrawing() { cairo_restore(mCtx); }
|
|
|
|
private:
|
|
cairo_t* mCtx;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
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 true;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
PatternIsCompatible(const Pattern& aPattern)
|
|
{
|
|
switch (aPattern.GetType())
|
|
{
|
|
case PATTERN_LINEAR_GRADIENT:
|
|
{
|
|
const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
|
|
return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
|
|
}
|
|
case PATTERN_RADIAL_GRADIENT:
|
|
{
|
|
const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
|
|
return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Never returns NULL. As such, you must always pass in Cairo-compatible
|
|
// patterns, most notably gradients with a GradientStopCairo.
|
|
// The pattern returned must have cairo_pattern_destroy() called on it by the
|
|
// caller.
|
|
// As the cairo_pattern_t returned may depend on the Pattern passed in, the
|
|
// lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
|
|
// Pattern passed in.
|
|
static cairo_pattern_t*
|
|
GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
|
|
{
|
|
cairo_pattern_t* pat;
|
|
|
|
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;
|
|
|
|
// After this block, |surf| always has an extra cairo reference to be
|
|
// destroyed. This makes creating new surfaces or reusing old ones more
|
|
// uniform.
|
|
if (pattern.mSurface->GetType() == SURFACE_CAIRO) {
|
|
const SourceSurfaceCairo* source = static_cast<const SourceSurfaceCairo*>(pattern.mSurface.get());
|
|
surf = source->GetSurface();
|
|
cairo_surface_reference(surf);
|
|
} else if (pattern.mSurface->GetType() == SURFACE_CAIRO_IMAGE) {
|
|
const DataSourceSurfaceCairo* source =
|
|
static_cast<const DataSourceSurfaceCairo*>(pattern.mSurface.get());
|
|
surf = source->GetSurface();
|
|
cairo_surface_reference(surf);
|
|
} else {
|
|
RefPtr<DataSourceSurface> source = pattern.mSurface->GetDataSurface();
|
|
surf = cairo_image_surface_create_for_data(source->GetData(),
|
|
GfxFormatToCairoFormat(source->GetFormat()),
|
|
source->GetSize().width,
|
|
source->GetSize().height,
|
|
source->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);
|
|
|
|
pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
|
|
pattern.mEnd.x, pattern.mEnd.y);
|
|
|
|
MOZ_ASSERT(pattern.mStops->GetBackendType() == BACKEND_CAIRO);
|
|
const std::vector<GradientStop>& stops =
|
|
static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
|
|
for (size_t i = 0; i < stops.size(); ++i) {
|
|
const GradientStop& stop = stops[i];
|
|
cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
|
|
stop.color.g, stop.color.b,
|
|
stop.color.a);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case PATTERN_RADIAL_GRADIENT:
|
|
{
|
|
const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
|
|
|
|
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 (size_t i = 0; i < stops.size(); ++i) {
|
|
const GradientStop& stop = stops[i];
|
|
cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
|
|
stop.color.g, stop.color.b,
|
|
stop.color.a);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// We should support all pattern types!
|
|
MOZ_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
return pat;
|
|
}
|
|
|
|
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)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
DrawTargetCairo::DrawTargetCairo()
|
|
: mContext(NULL)
|
|
{
|
|
}
|
|
|
|
DrawTargetCairo::~DrawTargetCairo()
|
|
{
|
|
MarkSnapshotsIndependent();
|
|
if (mPathObserver) {
|
|
mPathObserver->ForgetDrawTarget();
|
|
}
|
|
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;
|
|
}
|
|
|
|
void
|
|
DrawTargetCairo::Flush()
|
|
{
|
|
cairo_surface_t* surf = cairo_get_target(mContext);
|
|
cairo_surface_flush(surf);
|
|
}
|
|
|
|
void
|
|
DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = NULL */)
|
|
{
|
|
WillChange(aPath);
|
|
}
|
|
|
|
void
|
|
DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
|
|
const Rect &aDest,
|
|
const Rect &aSource,
|
|
const DrawSurfaceOptions &aSurfOptions,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
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());
|
|
|
|
cairo_surface_t* surf = NULL;
|
|
if (aSurface->GetType() == SURFACE_CAIRO) {
|
|
surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
|
|
}
|
|
|
|
cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
|
|
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_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)
|
|
{
|
|
WillChange();
|
|
|
|
if (aSurface->GetType() != SURFACE_CAIRO) {
|
|
return;
|
|
}
|
|
|
|
SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
|
|
|
|
Float width = aSurface->GetSize().width;
|
|
Float 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 = source->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
|
|
DrawTargetCairo::DrawPattern(const Pattern& aPattern,
|
|
const StrokeOptions& aStrokeOptions,
|
|
const DrawOptions& aOptions,
|
|
DrawPatternType aDrawType)
|
|
{
|
|
if (!PatternIsCompatible(aPattern)) {
|
|
return;
|
|
}
|
|
|
|
cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
|
|
cairo_set_source(mContext, pat);
|
|
|
|
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_OVER);
|
|
|
|
if (aDrawType == DRAW_STROKE) {
|
|
SetCairoStrokeOptions(mContext, aStrokeOptions);
|
|
cairo_stroke_preserve(mContext);
|
|
} else {
|
|
cairo_fill_preserve(mContext);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
cairo_pattern_destroy(pat);
|
|
}
|
|
|
|
void
|
|
DrawTargetCairo::FillRect(const Rect &aRect,
|
|
const Pattern &aPattern,
|
|
const DrawOptions &aOptions)
|
|
{
|
|
AutoPrepareForDrawing prep(this, mContext);
|
|
|
|
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)
|
|
{
|
|
AutoPrepareForDrawing prep(this, mContext);
|
|
}
|
|
|
|
void
|
|
DrawTargetCairo::ClearRect(const Rect& aRect)
|
|
{
|
|
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);
|
|
|
|
cairo_restore(mContext);
|
|
}
|
|
|
|
void
|
|
DrawTargetCairo::StrokeRect(const Rect &aRect,
|
|
const Pattern &aPattern,
|
|
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
|
|
const DrawOptions &aOptions /* = DrawOptions() */)
|
|
{
|
|
AutoPrepareForDrawing prep(this, mContext);
|
|
|
|
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() */)
|
|
{
|
|
AutoPrepareForDrawing prep(this, mContext);
|
|
|
|
cairo_new_path(mContext);
|
|
cairo_move_to(mContext, aStart.x, aStart.y);
|
|
cairo_line_to(mContext, aEnd.x, aEnd.y);
|
|
|
|
DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
|
|
}
|
|
|
|
void
|
|
DrawTargetCairo::Stroke(const Path *aPath,
|
|
const Pattern &aPattern,
|
|
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
|
|
const DrawOptions &aOptions /* = DrawOptions() */)
|
|
{
|
|
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() */)
|
|
{
|
|
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)
|
|
{
|
|
AutoPrepareForDrawing prep(this, mContext);
|
|
|
|
ScaledFontBase* scaledFont = static_cast<ScaledFontBase*>(aFont);
|
|
cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont());
|
|
|
|
cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
|
|
cairo_set_source(mContext, pat);
|
|
cairo_pattern_destroy(pat);
|
|
|
|
// Convert our GlyphBuffer into an array of Cairo glyphs.
|
|
std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
|
|
for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
|
|
glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
|
|
glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
|
|
glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
|
|
}
|
|
|
|
cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
|
|
}
|
|
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
TemporaryRef<SourceSurface>
|
|
DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
|
|
const IntSize &aSize,
|
|
int32_t aStride,
|
|
SurfaceFormat aFormat) const
|
|
{
|
|
cairo_surface_t* surf = cairo_image_surface_create_for_data(aData,
|
|
GfxFormatToCairoFormat(aFormat),
|
|
aSize.width,
|
|
aSize.height,
|
|
aStride);
|
|
RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
|
|
cairo_surface_destroy(surf);
|
|
return source_surf;
|
|
}
|
|
|
|
TemporaryRef<SourceSurface>
|
|
DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
|
|
{
|
|
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> source =
|
|
new SourceSurfaceCairo(surf, size, aSurface.mFormat);
|
|
return source;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool
|
|
DrawTargetCairo::Init(cairo_surface_t* aSurface)
|
|
{
|
|
mContext = cairo_create(aSurface);
|
|
|
|
return true;
|
|
}
|
|
|
|
void *
|
|
DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType)
|
|
{
|
|
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::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);
|
|
}
|
|
|
|
}
|
|
}
|