Bug 692879. Implement CoreGraphics Azure backend. r=mwoodrow

--HG--
extra : rebase_source : 94f8c205e16e961b5407396c0d0d3b41067222dc
This commit is contained in:
Jeff Muizelaar 2012-01-09 13:54:44 -05:00
parent 1b86e0c8e4
commit 0ac4c9201a
20 changed files with 1760 additions and 130 deletions

View File

@ -19,7 +19,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bas Schouten <bschouten@mozilla.com>
* Jeff Muizelaar <jmuizelaar@mozilla.com>
*
* 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
@ -37,6 +37,9 @@
#include "DrawTargetCG.h"
#include "SourceSurfaceCG.h"
#include "Rect.h"
#include "ScaledFontMac.h"
#include "Tools.h"
#include <vector>
//CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
@ -48,6 +51,11 @@ static CGRect RectToCGRect(Rect r)
return CGRectMake(r.x, r.y, r.width, r.height);
}
static CGRect IntRectToCGRect(IntRect r)
{
return CGRectMake(r.x, r.y, r.width, r.height);
}
CGBlendMode ToBlendMode(CompositionOp op)
{
CGBlendMode mode;
@ -55,18 +63,40 @@ CGBlendMode ToBlendMode(CompositionOp op)
case OP_OVER:
mode = kCGBlendModeNormal;
break;
case OP_SOURCE:
mode = kCGBlendModeCopy;
break;
case OP_CLEAR:
mode = kCGBlendModeClear;
break;
case OP_ADD:
mode = kCGBlendModePlusLighter;
break;
case OP_ATOP:
mode = kCGBlendModeSourceAtop;
break;
case OP_OUT:
mode = kCGBlendModeSourceOut;
break;
case OP_IN:
mode = kCGBlendModeSourceIn;
break;
case OP_SOURCE:
mode = kCGBlendModeCopy;
break;
case OP_DEST_IN:
mode = kCGBlendModeDestinationIn;
break;
case OP_DEST_OUT:
mode = kCGBlendModeDestinationOut;
break;
case OP_DEST_OVER:
mode = kCGBlendModeDestinationOver;
break;
case OP_DEST_ATOP:
mode = kCGBlendModeDestinationAtop;
break;
case OP_XOR:
mode = kCGBlendModeXOR;
break;
/*
case OP_CLEAR:
mode = kCGBlendModeClear;
break;*/
default:
mode = kCGBlendModeNormal;
}
@ -81,12 +111,31 @@ DrawTargetCG::DrawTargetCG()
DrawTargetCG::~DrawTargetCG()
{
// We need to conditionally release these because Init can fail without initializing these.
if (mColorSpace)
CGColorSpaceRelease(mColorSpace);
if (mCg)
CGContextRelease(mCg);
}
TemporaryRef<SourceSurface>
DrawTargetCG::Snapshot()
{
return NULL;
RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG(CGBitmapContextCreateImage(mCg));
return newSurf;
}
TemporaryRef<DrawTarget>
DrawTargetCG::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{
// XXX: in thebes we use CGLayers to do this kind of thing. It probably makes sense
// to add that in somehow, but at a higher level
RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
if (newTarget->Init(aSize, aFormat)) {
return newTarget;
} else {
return NULL;
}
}
TemporaryRef<SourceSurface>
@ -97,7 +146,7 @@ DrawTargetCG::CreateSourceSurfaceFromData(unsigned char *aData,
{
RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG();
if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
return NULL;
}
@ -110,16 +159,67 @@ DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const
return NULL;
}
class UnboundnessFixer
{
CGRect mClipBounds;
CGLayerRef mLayer;
CGContextRef mCg;
public:
UnboundnessFixer() : mCg(NULL) {}
CGContextRef Check(CGContextRef baseCg, CompositionOp blend)
{
if (!IsOperatorBoundByMask(blend)) {
mClipBounds = CGContextGetClipBoundingBox(baseCg);
// TransparencyLayers aren't blended using the blend mode so
// we are forced to use CGLayers
//XXX: The size here is in default user space units, of the layer relative to the graphics context.
// is the clip bounds still correct if, for example, we have a scale applied to the context?
mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, NULL);
mCg = CGLayerGetContext(mLayer);
// CGContext's default to have the origin at the bottom left
// so flip it to the top left and adjust for the origin
// of the layer
CGContextTranslateCTM(mCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height);
CGContextScaleCTM(mCg, 1, -1);
return mCg;
} else {
return baseCg;
}
}
void Fix(CGContextRef baseCg)
{
if (mCg) {
CGContextTranslateCTM(baseCg, 0, mClipBounds.size.height);
CGContextScaleCTM(baseCg, 1, -1);
mClipBounds.origin.y *= -1;
CGContextDrawLayerAtPoint(baseCg, mClipBounds.origin, mLayer);
CGContextRelease(mCg);
}
}
};
void
DrawTargetCG::DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawOptions &aOptions,
const DrawSurfaceOptions &aSurfOptions)
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aDrawOptions)
{
CGImageRef image;
CGImageRef subimage = NULL;
if (aSurface->GetType() == COREGRAPHICS_IMAGE) {
if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
CGContextSaveGState(mCg);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
/* we have two options here:
* - create a subimage -- this is slower
@ -129,56 +229,737 @@ DrawTargetCG::DrawSurface(SourceSurface *aSurface,
image = subimage;
}
CGContextDrawImage(mCg, RectToCGRect(aDest), image);
CGContextScaleCTM(cg, 1, -1);
CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height),
aDest.width, aDest.height);
//XXX: we should implement this for patterns too
if (aSurfOptions.mFilter == FILTER_POINT)
CGContextSetInterpolationQuality(cg, kCGInterpolationNone);
CGContextDrawImage(cg, flippedRect, image);
fixer.Fix(mCg);
CGContextRestoreGState(mCg);
CGImageRelease(subimage);
}
}
static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
{
CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
return CGColorCreate(aColorSpace, components);
}
class GradientStopsCG : public GradientStops
{
public:
//XXX: The skia backend uses a vector and passes in aNumStops. It should do better
GradientStopsCG(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode)
{
//XXX: do the stops need to be in any particular order?
// what should we do about the color space here? we certainly shouldn't be
// recreating it all the time
std::vector<CGFloat> colors;
std::vector<CGFloat> offsets;
colors.reserve(aNumStops*4);
offsets.reserve(aNumStops);
for (uint32_t i = 0; i < aNumStops; i++) {
colors.push_back(aStops[i].color.r);
colors.push_back(aStops[i].color.g);
colors.push_back(aStops[i].color.b);
colors.push_back(aStops[i].color.a);
offsets.push_back(aStops[i].offset);
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
mGradient = CGGradientCreateWithColorComponents(colorSpace,
&colors.front(),
&offsets.front(),
aNumStops);
CGColorSpaceRelease(colorSpace);
}
virtual ~GradientStopsCG() {
CGGradientRelease(mGradient);
}
BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; }
CGGradientRef mGradient;
};
TemporaryRef<GradientStops>
DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
ExtendMode aExtendMode) const
{
return new GradientStopsCG(aStops, aNumStops, aExtendMode);
}
static void
DrawGradient(CGContextRef cg, const Pattern &aPattern)
{
if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) {
const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
// XXX: we should take the m out of the properties of LinearGradientPatterns
CGPoint startPoint = { pat.mBegin.x, pat.mBegin.y };
CGPoint endPoint = { pat.mEnd.x, pat.mEnd.y };
// Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?)
//if (startPoint.x == endPoint.x && startPoint.y == endPoint.y)
// return;
CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint,
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
} else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) {
const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
// XXX: we should take the m out of the properties of RadialGradientPatterns
CGPoint startCenter = { pat.mCenter1.x, pat.mCenter1.y };
CGFloat startRadius = pat.mRadius1;
CGPoint endCenter = { pat.mCenter2.x, pat.mCenter2.y };
CGFloat endRadius = pat.mRadius2;
//XXX: are there degenerate radial gradients that we should avoid drawing?
CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius,
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
} else {
assert(0);
}
}
static void
drawPattern(void *info, CGContextRef context)
{
CGImageRef image = static_cast<CGImageRef>(info);
CGRect rect = {{0, 0},
{static_cast<CGFloat>(CGImageGetWidth(image)),
static_cast<CGFloat>(CGImageGetHeight(image))}};
CGContextDrawImage(context, rect, image);
}
static void
releaseInfo(void *info)
{
CGImageRef image = static_cast<CGImageRef>(info);
CGImageRelease(image);
}
CGPatternCallbacks patternCallbacks = {
0,
drawPattern,
releaseInfo
};
static bool
isGradient(const Pattern &aPattern)
{
return aPattern.GetType() == PATTERN_LINEAR_GRADIENT || aPattern.GetType() == PATTERN_RADIAL_GRADIENT;
}
/* CoreGraphics patterns ignore the userspace transform so
* we need to multiply it in */
static CGPatternRef
CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace)
{
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
// XXX: is .get correct here?
CGImageRef image = static_cast<SourceSurfaceCG*>(pat.mSurface.get())->GetImage();
CGFloat xStep, yStep;
switch (pat.mExtendMode) {
case EXTEND_CLAMP:
// The 1 << 22 comes from Webkit see Pattern::createPlatformPattern() in PatternCG.cpp for more info
xStep = static_cast<CGFloat>(1 << 22);
yStep = static_cast<CGFloat>(1 << 22);
break;
case EXTEND_REFLECT:
assert(0);
case EXTEND_REPEAT:
xStep = static_cast<CGFloat>(CGImageGetWidth(image));
yStep = static_cast<CGFloat>(CGImageGetHeight(image));
// webkit uses wkCGPatternCreateWithImageAndTransform a wrapper around CGPatternCreateWithImage2
// this is done to avoid pixel-cracking along pattern boundaries
// (see https://bugs.webkit.org/show_bug.cgi?id=53055)
// typedef enum {
// wkPatternTilingNoDistortion,
// wkPatternTilingConstantSpacingMinimalDistortion,
// wkPatternTilingConstantSpacing
// } wkPatternTiling;
// extern CGPatternRef (*wkCGPatternCreateWithImageAndTransform)(CGImageRef, CGAffineTransform, int);
}
//XXX: We should be using CGContextDrawTiledImage when we can. Even though it
// creates a pattern, it seems to go down a faster path than using a delegate
// like we do below
CGRect bounds = {
{0, 0,},
{static_cast<CGFloat>(CGImageGetWidth(image)), static_cast<CGFloat>(CGImageGetHeight(image))}
};
CGAffineTransform transform = CGAffineTransformConcat(CGAffineTransformMakeScale(1, -1), aUserSpace);
transform = CGAffineTransformTranslate(transform, 0, -static_cast<float>(CGImageGetHeight(image)));
return CGPatternCreate(CGImageRetain(image), bounds, transform, xStep, yStep, kCGPatternTilingConstantSpacing,
true, &patternCallbacks);
}
static void
SetFillFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
{
assert(!isGradient(aPattern));
if (aPattern.GetType() == PATTERN_COLOR) {
const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
//XXX: we should cache colors
CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
CGContextSetFillColorWithColor(cg, cgcolor);
CGColorRelease(cgcolor);
} else if (aPattern.GetType() == PATTERN_SURFACE) {
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern (NULL);
CGContextSetFillColorSpace(cg, patternSpace);
CGColorSpaceRelease(patternSpace);
CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
CGFloat alpha = 1.;
CGContextSetFillPattern(cg, pattern, &alpha);
CGPatternRelease(pattern);
}
}
static void
SetStrokeFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
{
assert(!isGradient(aPattern));
if (aPattern.GetType() == PATTERN_COLOR) {
const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
//XXX: we should cache colors
CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
CGContextSetStrokeColorWithColor(cg, cgcolor);
CGColorRelease(cgcolor);
} else if (aPattern.GetType() == PATTERN_SURFACE) {
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern (NULL);
CGContextSetStrokeColorSpace(cg, patternSpace);
CGColorSpaceRelease(patternSpace);
CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
CGFloat alpha = 1.;
CGContextSetStrokePattern(cg, pattern, &alpha);
CGPatternRelease(pattern);
}
}
void
DrawTargetCG::FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aDrawOptions)
{
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
if (isGradient(aPattern)) {
CGContextClipToRect(cg, RectToCGRect(aRect));
DrawGradient(cg, aPattern);
} else {
SetFillFromPattern(cg, mColorSpace, aPattern);
CGContextFillRect(cg, RectToCGRect(aRect));
}
fixer.Fix(mCg);
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
{
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
CGContextBeginPath(cg);
CGContextMoveToPoint(cg, p1.x, p1.y);
CGContextAddLineToPoint(cg, p2.x, p2.y);
SetStrokeOptions(cg, aStrokeOptions);
if (isGradient(aPattern)) {
CGContextReplacePathWithStrokedPath(cg);
//XXX: should we use EO clip here?
CGContextClip(cg);
DrawGradient(cg, aPattern);
} else {
SetStrokeFromPattern(cg, mColorSpace, aPattern);
CGContextStrokePath(cg);
}
fixer.Fix(mCg);
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aDrawOptions)
{
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
// we don't need to set all of the stroke state because
// it doesn't apply when stroking rects
switch (aStrokeOptions.mLineJoin)
{
case JOIN_BEVEL:
CGContextSetLineJoin(cg, kCGLineJoinBevel);
break;
case JOIN_ROUND:
CGContextSetLineJoin(cg, kCGLineJoinRound);
break;
case JOIN_MITER:
case JOIN_MITER_OR_BEVEL:
CGContextSetLineJoin(cg, kCGLineJoinMiter);
break;
}
CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth);
if (isGradient(aPattern)) {
// There's no CGContextClipStrokeRect so we do it by hand
CGContextBeginPath(cg);
CGContextAddRect(cg, RectToCGRect(aRect));
CGContextReplacePathWithStrokedPath(cg);
//XXX: should we use EO clip here?
CGContextClip(cg);
DrawGradient(cg, aPattern);
} else {
SetStrokeFromPattern(cg, mColorSpace, aPattern);
CGContextStrokeRect(cg, RectToCGRect(aRect));
}
fixer.Fix(mCg);
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::ClearRect(const Rect &aRect)
{
CGContextSaveGState(mCg);
CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
CGContextClearRect(mCg, RectToCGRect(aRect));
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
{
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
CGContextBeginPath(cg);
assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
const PathCG *cgPath = static_cast<const PathCG*>(aPath);
CGContextAddPath(cg, cgPath->GetPath());
SetStrokeOptions(cg, aStrokeOptions);
if (isGradient(aPattern)) {
CGContextReplacePathWithStrokedPath(cg);
//XXX: should we use EO clip here?
CGContextClip(cg);
DrawGradient(cg, aPattern);
} else {
CGContextBeginPath(cg);
// XXX: we could put fill mode into the path fill rule if we wanted
const PathCG *cgPath = static_cast<const PathCG*>(aPath);
CGContextAddPath(cg, cgPath->GetPath());
SetStrokeFromPattern(cg, mColorSpace, aPattern);
CGContextStrokePath(cg);
}
fixer.Fix(mCg);
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aDrawOptions)
{
assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
CGContextSaveGState(mCg);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
if (isGradient(aPattern)) {
// XXX: we should be able to avoid the extra SaveState that PushClip does
PushClip(aPath);
DrawGradient(cg, aPattern);
PopClip();
} else {
CGContextBeginPath(cg);
// XXX: we could put fill mode into the path fill rule if we wanted
const PathCG *cgPath = static_cast<const PathCG*>(aPath);
CGContextAddPath(cg, cgPath->GetPath());
SetFillFromPattern(cg, mColorSpace, aPattern);
if (cgPath->GetFillRule() == FILL_EVEN_ODD)
CGContextEOFillPath(cg);
else
CGContextFillPath(cg);
}
fixer.Fix(mCg);
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions)
{
assert(aBuffer.mNumGlyphs);
CGContextSaveGState(mCg);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
ScaledFontMac* cgFont = static_cast<ScaledFontMac*>(aFont);
CGContextSetFont(cg, cgFont->mFont);
CGContextSetFontSize(cg, cgFont->mSize);
//XXX: we should use a stack vector here when we have a class like that
std::vector<CGGlyph> glyphs;
std::vector<CGPoint> positions;
glyphs.resize(aBuffer.mNumGlyphs);
positions.resize(aBuffer.mNumGlyphs);
CGFloat xprev = aBuffer.mGlyphs[0].mPosition.x;
CGFloat yprev = aBuffer.mGlyphs[0].mPosition.y;
CGContextSetTextPosition(cg, xprev, yprev);
// Handle the flip
CGAffineTransform matrix = CGAffineTransformMakeScale(1, -1);//CGAffineTransformMake(1, 0, 0, -1, 0, -mSize.height);
// "Note that the text matrix is not a part of the graphics state"
CGContextSetTextMatrix(cg, matrix);
for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
glyphs[i] = aBuffer.mGlyphs[i].mIndex;
// XXX: CGPointMake might not be inlined
positions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
-aBuffer.mGlyphs[i].mPosition.y);
}
//XXX: CGContextShowGlyphsAtPositions is 10.5+ for older versions use CGContextShowGlyphsWithAdvances
if (isGradient(aPattern)) {
CGContextSetTextDrawingMode(cg, kCGTextClip);
CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), aBuffer.mNumGlyphs);
DrawGradient(cg, aPattern);
} else {
//XXX: with CoreGraphics we can stroke text directly instead of going
// through GetPath. It would be nice to add support for using that
CGContextSetTextDrawingMode(cg, kCGTextFill);
SetFillFromPattern(cg, mColorSpace, aPattern);
CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), aBuffer.mNumGlyphs);
}
fixer.Fix(mCg);
CGContextRestoreGState(cg);
}
extern "C" {
void
CGContextResetClip(CGContextRef);
};
void
DrawTargetCG::CopySurface(SourceSurface *aSurface,
const IntRect& aSourceRect,
const IntPoint &aDestination)
{
CGImageRef image;
CGImageRef subimage = NULL;
if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
/* we have two options here:
* - create a subimage -- this is slower
* - fancy things with clip and different dest rects */
{
subimage = CGImageCreateWithImageInRect(image, IntRectToCGRect(aSourceRect));
image = subimage;
}
// XXX: it might be more efficient for us to do the copy directly if we have access to the bits
CGContextSaveGState(mCg);
// CopySurface ignores the clip, so we need to use private API to temporarily reset it
CGContextResetClip(mCg);
CGContextSetBlendMode(mCg, kCGBlendModeCopy);
CGContextScaleCTM(mCg, 1, -1);
CGRect flippedRect = CGRectMake(aDestination.x, -(aDestination.y + aSourceRect.height),
aSourceRect.width, aSourceRect.height);
CGContextDrawImage(mCg, flippedRect, image);
CGContextRestoreGState(mCg);
CGImageRelease(subimage);
}
}
void
DrawTargetCG::FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions)
DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator)
{
//XXX: it would be nice to hang a CGColor off of the pattern here
if (aPattern.GetType() == COLOR) {
Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
//XXX: the m prefixes are painful here
CGContextSetRGBFillColor(mCg, color.mR, color.mG, color.mB, color.mA);
}
CGImageRef image;
CGImageRef subimage = NULL;
if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
CGContextSetBlendMode(mCg, ToBlendMode(aOptions.mCompositionOp));
CGContextFillRect(mCg, RectToCGRect(aRect));
IntSize size = aSurface->GetSize();
CGContextSaveGState(mCg);
//XXX do we need to do the fixup here?
CGContextSetBlendMode(mCg, ToBlendMode(aOperator));
CGContextScaleCTM(mCg, 1, -1);
CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + size.height),
size.width, size.height);
CGColorRef color = ColorToCGColor(mColorSpace, aColor);
CGSize offset = {aOffset.x, -aOffset.y};
// CoreGraphics needs twice sigma as it's amount of blur
CGContextSetShadowWithColor(mCg, offset, 2*aSigma, color);
CGColorRelease(color);
CGContextDrawImage(mCg, flippedRect, image);
CGContextRestoreGState(mCg);
CGImageRelease(subimage);
}
}
bool
DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
{
// XXX: we should come up with some consistent semantics for dealing
// with zero area drawtargets
if (aSize.width == 0 || aSize.height == 0) {
mColorSpace = NULL;
mCg = NULL;
return false;
}
//XXX: handle SurfaceFormat
//XXX: we'd be better off reusing the Colorspace across draw targets
mColorSpace = CGColorSpaceCreateDeviceRGB();
mSize = aSize;
mCg = cgContext;
mData = NULL;
assert(mCg);
// CGContext's default to have the origin at the bottom left
// so flip it to the top left
CGContextTranslateCTM(mCg, 0, mSize.height);
CGContextScaleCTM(mCg, 1, -1);
//XXX: set correct format
mFormat = FORMAT_B8G8R8A8;
return true;
}
bool
DrawTargetCG::Init(const IntSize &aSize)
DrawTargetCG::Init(const IntSize &aSize, SurfaceFormat &)
{
CGColorSpaceRef cgColorspace;
cgColorspace = CGColorSpaceCreateDeviceRGB();
// XXX: we should come up with some consistent semantics for dealing
// with zero area drawtargets
if (aSize.width == 0 || aSize.height == 0) {
mColorSpace = NULL;
mCg = NULL;
return false;
}
//XXX: handle SurfaceFormat
//XXX: we'd be better off reusing the Colorspace across draw targets
mColorSpace = CGColorSpaceCreateDeviceRGB();
mSize = aSize;
int bitsPerComponent = 8;
int stride = mSize.width;
int stride = mSize.width*4;
CGBitmapInfo bitinfo;
bitinfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
// XXX: mWidth is ugly
mCg = CGBitmapContextCreate (NULL,
mSize.width,
mSize.height,
bitsPerComponent,
stride,
cgColorspace,
bitinfo);
// XXX: currently we allocate ourselves so that we can easily return a gfxImageSurface
// we might not need to later if once we don't need to support gfxImageSurface
//XXX: currently Init implicitly clears, that can often be a waste of time
// XXX: leaked
mData = calloc(mSize.height * stride, 1);
// XXX: what should we do if this fails?
mCg = CGBitmapContextCreate (mData,
mSize.width,
mSize.height,
bitsPerComponent,
stride,
mColorSpace,
bitinfo);
CGColorSpaceRelease (cgColorspace);
assert(mCg);
// CGContext's default to have the origin at the bottom left
// so flip it to the top left
CGContextTranslateCTM(mCg, 0, mSize.height);
CGContextScaleCTM(mCg, 1, -1);
//XXX: set correct format
mFormat = FORMAT_B8G8R8A8;
return true;
}
TemporaryRef<PathBuilder>
DrawTargetCG::CreatePathBuilder(FillRule aFillRule) const
{
RefPtr<PathBuilderCG> pb = new PathBuilderCG(aFillRule);
return pb;
}
void*
DrawTargetCG::GetNativeSurface(NativeSurfaceType aType)
{
if (aType == NATIVE_SURFACE_CGCONTEXT) {
return mCg;
} else {
return NULL;
}
}
void
DrawTargetCG::Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aDrawOptions)
{
CGContextSaveGState(mCg);
if (isGradient(aMask)) {
assert(0);
} else {
if (aMask.GetType() == PATTERN_COLOR) {
DrawOptions drawOptions(aDrawOptions);
const Color& color = static_cast<const ColorPattern&>(aMask).mColor;
drawOptions.mAlpha *= color.a;
assert(0);
// XXX: we need to get a rect that when transformed covers the entire surface
//Rect
//FillRect(rect, aSource, drawOptions);
} else if (aMask.GetType() == PATTERN_SURFACE) {
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aMask);
CGImageRef mask = static_cast<SourceSurfaceCG*>(pat.mSurface.get())->GetImage();
Rect rect(0,0, CGImageGetWidth(mask), CGImageGetHeight(mask));
// XXX: probably we need to do some flipping of the image or something
CGContextClipToMask(mCg, RectToCGRect(rect), mask);
FillRect(rect, aSource, aDrawOptions);
}
}
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::PushClipRect(const Rect &aRect)
{
CGContextSaveGState(mCg);
CGContextClipToRect(mCg, RectToCGRect(aRect));
}
void
DrawTargetCG::PushClip(const Path *aPath)
{
CGContextSaveGState(mCg);
CGContextBeginPath(mCg);
assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
const PathCG *cgPath = static_cast<const PathCG*>(aPath);
// Weirdly, CoreGraphics clips empty paths as all shown
// but emtpy rects as all clipped. We detect this situation and
// workaround it appropriately
if (CGPathIsEmpty(cgPath->GetPath())) {
// XXX: should we return here?
CGContextClipToRect(mCg, CGRectZero);
}
CGContextAddPath(mCg, cgPath->GetPath());
if (cgPath->GetFillRule() == FILL_EVEN_ODD)
CGContextEOClip(mCg);
else
CGContextClip(mCg);
}
void
DrawTargetCG::PopClip()
{
CGContextRestoreGState(mCg);
}
}
}

View File

@ -35,50 +35,155 @@
*
* ***** END LICENSE BLOCK ***** */
#pragma once
#include <ApplicationServices/ApplicationServices.h>
#include "2D.h"
#include "Rect.h"
#include "PathCG.h"
namespace mozilla {
namespace gfx {
static inline CGAffineTransform
GfxMatrixToCGAffineTransform(Matrix m)
{
CGAffineTransform t;
t.a = m._11;
t.b = m._12;
t.c = m._21;
t.d = m._22;
t.tx = m._31;
t.ty = m._32;
return t;
}
static inline Rect
CGRectToRect(CGRect rect)
{
return Rect(rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height);
}
static inline void
SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions)
{
switch (aStrokeOptions.mLineCap)
{
case CAP_BUTT:
CGContextSetLineCap(cg, kCGLineCapButt);
break;
case CAP_ROUND:
CGContextSetLineCap(cg, kCGLineCapRound);
break;
case CAP_SQUARE:
CGContextSetLineCap(cg, kCGLineCapSquare);
break;
}
switch (aStrokeOptions.mLineJoin)
{
case JOIN_BEVEL:
CGContextSetLineJoin(cg, kCGLineJoinBevel);
break;
case JOIN_ROUND:
CGContextSetLineJoin(cg, kCGLineJoinRound);
break;
case JOIN_MITER:
case JOIN_MITER_OR_BEVEL:
CGContextSetLineJoin(cg, kCGLineJoinMiter);
break;
}
CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth);
CGContextSetMiterLimit(cg, aStrokeOptions.mMiterLimit);
// XXX: rename mDashLength to dashLength
if (aStrokeOptions.mDashLength > 1) {
// we use a regular array instead of a std::vector here because we don't want to leak the <vector> include
CGFloat *dashes = new CGFloat[aStrokeOptions.mDashLength];
for (size_t i=0; i<aStrokeOptions.mDashLength; i++) {
dashes[i] = aStrokeOptions.mDashPattern[i];
}
CGContextSetLineDash(cg, aStrokeOptions.mDashOffset, dashes, aStrokeOptions.mDashLength);
delete[] dashes;
}
}
class DrawTargetCG : public DrawTarget
{
public:
DrawTargetCG();
virtual ~DrawTargetCG();
virtual BackendType GetType() const { return COREGRAPHICS; }
virtual BackendType GetType() const { return BACKEND_COREGRAPHICS; }
virtual TemporaryRef<SourceSurface> Snapshot();
virtual void DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawOptions &aOptions = DrawOptions(),
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions());
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
const DrawOptions &aOptions = DrawOptions());
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions());
bool Init(const IntSize &aSize);
//XXX: why do we take a reference to SurfaceFormat?
bool Init(const IntSize &aSize, SurfaceFormat&);
bool Init(CGContextRef cgContext, const IntSize &aSize);
virtual void Flush() {}
virtual void DrawSurfaceWithShadow(SourceSurface *, const Point &, const Color &, const Point &, Float, CompositionOp);
virtual void ClearRect(const Rect &);
virtual void CopySurface(SourceSurface *, const IntRect&, const IntPoint&);
virtual void StrokeRect(const Rect &, const Pattern &, const StrokeOptions&, const DrawOptions&);
virtual void StrokeLine(const Point &, const Point &, const Pattern &, const StrokeOptions &, const DrawOptions &);
virtual void Stroke(const Path *, const Pattern &, const StrokeOptions &, const DrawOptions &);
virtual void Fill(const Path *, const Pattern &, const DrawOptions &);
virtual void FillGlyphs(ScaledFont *, const GlyphBuffer&, const Pattern &, const DrawOptions &);
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions = DrawOptions());
virtual void PushClip(const Path *);
virtual void PushClipRect(const Rect &aRect);
virtual void PopClip();
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromNativeSurface(const NativeSurface&) const { return NULL;}
virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &, SurfaceFormat) const;
virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule) const;
virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *, uint32_t,
ExtendMode aExtendMode = EXTEND_CLAMP) const;
virtual void *GetNativeSurface(NativeSurfaceType);
virtual IntSize GetSize() { return mSize; }
/* This is for creating good compatible surfaces */
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat) const;
virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
CGContextRef GetCGContext() {
return mCg;
}
private:
bool InitCGRenderTarget();
IntSize mSize;
CGColorSpaceRef mColorSpace;
CGContextRef mCg;
void *mData;
SurfaceFormat mFormat;
};
}

View File

@ -37,7 +37,7 @@
#include "DrawTargetSkia.h"
#include "SourceSurfaceSkia.h"
#include "ScaledFontSkia.h"
#include "ScaledFontBase.h"
#include "skia/SkDevice.h"
#include "skia/SkTypeface.h"
#include "skia/SkGradientShader.h"
@ -58,6 +58,7 @@ namespace gfx {
SkColor ColorToSkColor(const Color &color, Float aAlpha)
{
//XXX: do a better job converting to int
return SkColorSetARGB(color.a*aAlpha*255.0, color.r*255.0, color.g*255.0, color.b*255.0);
}
@ -520,10 +521,10 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
MarkChanged();
ScaledFontSkia* skiaFont = static_cast<ScaledFontSkia*>(aFont);
ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
paint.mPaint.setTypeface(skiaFont->mTypeface);
paint.mPaint.setTypeface(skiaFont->GetSkTypeface());
paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);

View File

@ -44,13 +44,20 @@
#ifdef USE_SKIA
#include "DrawTargetSkia.h"
#ifdef XP_MACOSX
#include "ScaledFontMac.h"
#include "ScaledFontBase.h"
#endif
#ifdef WIN32
#include "ScaledFontWin.h"
#endif
#include "ScaledFontSkia.h"
#ifdef XP_MACOSX
#include "ScaledFontMac.h"
#endif
#ifdef XP_MACOSX
#include "DrawTargetCG.h"
#endif
#ifdef WIN32
@ -90,7 +97,7 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor
}
break;
}
#endif
#elif defined XP_MACOSX || defined ANDROID
#ifdef USE_SKIA
case BACKEND_SKIA:
{
@ -101,6 +108,18 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor
}
break;
}
#endif
#ifdef XP_MACOSX
case BACKEND_COREGRAPHICS:
{
RefPtr<DrawTargetCG> newTarget;
newTarget = new DrawTargetCG();
if (newTarget->Init(aSize, aFormat)) {
return newTarget;
}
break;
}
#endif
#endif
default:
gfxDebug() << "Invalid draw target type specified.";
@ -122,13 +141,13 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz
return new ScaledFontDWrite(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
}
#endif
#ifdef USE_SKIA
#ifdef XP_MACOSX
case NATIVE_FONT_MAC_FONT_FACE:
{
return new ScaledFontMac(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
}
#endif
#ifdef USE_SKIA
#ifdef WIN32
case NATIVE_FONT_GDI_FONT_FACE:
{
@ -137,7 +156,7 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz
#endif
case NATIVE_FONT_SKIA_FONT_FACE:
{
return new ScaledFontSkia(static_cast<gfxFont*>(aNativeFont.mFont), aSize);
return new ScaledFontBase(static_cast<gfxFont*>(aNativeFont.mFont), aSize);
}
#endif
case NATIVE_FONT_CAIRO_FONT_FACE:

View File

@ -72,8 +72,16 @@ CPPSRCS = \
SourceSurfaceCairo.cpp \
PathCairo.cpp \
Blur.cpp \
ScaledFontBase.cpp \
$(NULL)
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
SourceSurfaceCG.cpp \
DrawTargetCG.cpp \
PathCG.cpp \
$(NULL)
endif
DEFINES += -DMOZ_GFX -DUSE_CAIRO
@ -82,7 +90,6 @@ CPPSRCS += \
SourceSurfaceSkia.cpp \
DrawTargetSkia.cpp \
PathSkia.cpp \
ScaledFontSkia.cpp \
$(NULL)
DEFINES += -DUSE_SKIA

273
gfx/2d/PathCG.cpp Normal file
View File

@ -0,0 +1,273 @@
/* -*- 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):
* Bas Schouten <bschouten@mozilla.com>
*
* 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 "PathCG.h"
#include <math.h>
#include "DrawTargetCG.h"
#include "Logging.h"
namespace mozilla {
namespace gfx {
PathBuilderCG::~PathBuilderCG()
{
CGPathRelease(mCGPath);
}
void
PathBuilderCG::MoveTo(const Point &aPoint)
{
CGPathMoveToPoint(mCGPath, NULL, aPoint.x, aPoint.y);
}
void
PathBuilderCG::LineTo(const Point &aPoint)
{
if (CGPathIsEmpty(mCGPath))
MoveTo(aPoint);
else
CGPathAddLineToPoint(mCGPath, NULL, aPoint.x, aPoint.y);
}
void
PathBuilderCG::BezierTo(const Point &aCP1,
const Point &aCP2,
const Point &aCP3)
{
if (CGPathIsEmpty(mCGPath))
MoveTo(aCP1);
else
CGPathAddCurveToPoint(mCGPath, NULL,
aCP1.x, aCP1.y,
aCP2.x, aCP2.y,
aCP3.x, aCP3.y);
}
void
PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
const Point &aCP2)
{
if (CGPathIsEmpty(mCGPath))
MoveTo(aCP1);
else
CGPathAddQuadCurveToPoint(mCGPath, NULL,
aCP1.x, aCP1.y,
aCP2.x, aCP2.y);
}
void
PathBuilderCG::Close()
{
if (!CGPathIsEmpty(mCGPath))
CGPathCloseSubpath(mCGPath);
}
void
PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
Float aEndAngle, bool aAntiClockwise)
{
}
Point
PathBuilderCG::CurrentPoint() const
{
CGPoint pt = CGPathGetCurrentPoint(mCGPath);
Point ret(pt.x, pt.y);
return ret;
}
void
PathBuilderCG::EnsureActive(const Point &aPoint)
{
}
TemporaryRef<Path>
PathBuilderCG::Finish()
{
RefPtr<PathCG> path = new PathCG(mCGPath, mFillRule);
return path;
}
TemporaryRef<PathBuilder>
PathCG::CopyToBuilder(FillRule aFillRule) const
{
CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
RefPtr<PathBuilderCG> builder = new PathBuilderCG(path, aFillRule);
return builder;
}
TemporaryRef<PathBuilder>
PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
{
// 10.7 adds CGPathCreateMutableCopyByTransformingPath it might be faster than doing
// this by hand
struct TransformApplier {
CGMutablePathRef path;
CGAffineTransform transform;
static void
TranformCGPathApplierFunc(void *vinfo, const CGPathElement *element)
{
TransformApplier *info = reinterpret_cast<TransformApplier*>(vinfo);
switch (element->type) {
case kCGPathElementMoveToPoint:
{
CGPoint pt = element->points[0];
CGPathMoveToPoint(info->path, &info->transform, pt.x, pt.y);
break;
}
case kCGPathElementAddLineToPoint:
{
CGPoint pt = element->points[0];
CGPathAddLineToPoint(info->path, &info->transform, pt.x, pt.y);
break;
}
case kCGPathElementAddQuadCurveToPoint:
{
CGPoint pt = element->points[0];
CGPoint cpt = element->points[1];
CGPathAddQuadCurveToPoint(info->path, &info->transform, cpt.x, cpt.y, pt.x, pt.y);
break;
}
case kCGPathElementAddCurveToPoint:
{
CGPoint pt = element->points[0];
CGPoint cpt1 = element->points[1];
CGPoint cpt2 = element->points[2];
CGPathAddCurveToPoint(info->path, &info->transform, cpt1.x, cpt1.y, cpt2.x, cpt2.y, pt.x, pt.y);
break;
}
case kCGPathElementCloseSubpath:
{
CGPathCloseSubpath(info->path);
break;
}
}
}
};
TransformApplier ta;
ta.path = CGPathCreateMutable();
ta.transform = GfxMatrixToCGAffineTransform(aTransform);
CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
RefPtr<PathBuilderCG> builder = new PathBuilderCG(ta.path, aFillRule);
return builder;
}
bool
PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
{
Matrix inverse = aTransform;
inverse.Invert();
Point transformedPoint = inverse*aPoint;
// We could probably drop the input transform and just transform the point at the caller?
CGPoint point = {transformedPoint.x, transformedPoint.y};
// The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5
// so we transform aPoint ourselves.
return CGPathContainsPoint(mPath, NULL, point, mFillRule == FILL_EVEN_ODD);
}
static size_t
PutBytesNull(void *info, const void *buffer, size_t count)
{
return count;
}
/* The idea of a scratch context comes from WebKit */
static CGContextRef
CreateScratchContext()
{
CGDataConsumerCallbacks callbacks = {PutBytesNull, NULL};
CGDataConsumerRef consumer = CGDataConsumerCreate(NULL, &callbacks);
CGContextRef cg = CGPDFContextCreate(consumer, NULL, NULL);
CGDataConsumerRelease(consumer);
return cg;
}
static CGContextRef
ScratchContext()
{
static CGContextRef cg = CreateScratchContext();
return cg;
}
//XXX: what should these functions return for an empty path?
// currently they return CGRectNull {inf,inf, 0, 0}
Rect
PathCG::GetBounds(const Matrix &aTransform) const
{
//XXX: are these bounds tight enough
Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
//XXX: curretnly this returns the bounds of the transformed bounds
// this is strictly looser than the bounds of the transformed path
return aTransform.TransformBounds(bounds);
}
Rect
PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform) const
{
// 10.7 has CGPathCreateCopyByStrokingPath which we could use
// instead of this scratch context business
CGContextRef cg = ScratchContext();
CGContextSaveGState(cg);
CGContextBeginPath(cg);
CGContextAddPath(cg, mPath);
SetStrokeOptions(cg, aStrokeOptions);
CGContextReplacePathWithStrokedPath(cg);
Rect bounds = CGRectToRect(CGContextGetPathBoundingBox(cg));
CGContextRestoreGState(cg);
return aTransform.TransformBounds(bounds);
}
}
}

133
gfx/2d/PathCG.h Normal file
View File

@ -0,0 +1,133 @@
/* -*- 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):
* Bas Schouten <bschouten@mozilla.com>
*
* 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_PATHCG_H_
#define MOZILLA_GFX_PATHCG_H_
#include <ApplicationServices/ApplicationServices.h>
#include "2D.h"
namespace mozilla {
namespace gfx {
class PathCG;
class PathBuilderCG : public PathBuilder
{
public:
// absorbs a reference of aPath
PathBuilderCG(CGMutablePathRef aPath, FillRule aFillRule)
: mFigureActive(false)
, mFillRule(aFillRule)
{
mCGPath = aPath;
}
PathBuilderCG(FillRule aFillRule)
: mFigureActive(false)
, mFillRule(aFillRule)
{
mCGPath = CGPathCreateMutable();
}
virtual ~PathBuilderCG();
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();
private:
friend class PathCG;
void EnsureActive(const Point &aPoint);
CGMutablePathRef mCGPath;
bool mFigureActive;
Point mCurrentPoint;
Point mBeginPoint;
FillRule mFillRule;
};
class PathCG : public Path
{
public:
PathCG(CGMutablePathRef aPath, FillRule aFillRule)
: mPath(aPath)
, mFillRule(aFillRule)
{
CGPathRetain(mPath);
}
virtual ~PathCG() { CGPathRelease(mPath); }
virtual BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; }
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; }
CGMutablePathRef GetPath() const { return mPath; }
private:
friend class DrawTargetCG;
CGMutablePathRef mPath;
bool mEndedActive;
Point mEndPoint;
FillRule mFillRule;
};
}
}
#endif

View File

@ -35,10 +35,12 @@
*
* ***** END LICENSE BLOCK ***** */
#include "ScaledFontSkia.h"
#include "ScaledFontBase.h"
#ifdef USE_SKIA
#include "PathSkia.h"
#include "skia/SkPaint.h"
#include "skia/SkPath.h"
#endif
#include <vector>
#include <cmath>
using namespace std;
@ -46,7 +48,7 @@ using namespace std;
namespace mozilla {
namespace gfx {
#ifdef USE_SKIA
static SkTypeface::Style gfxFontStyleToSkia(const gfxFontStyle* aStyle)
{
if (aStyle->style == NS_FONT_STYLE_ITALIC) {
@ -61,49 +63,57 @@ static SkTypeface::Style gfxFontStyleToSkia(const gfxFontStyle* aStyle)
return SkTypeface::kNormal;
}
ScaledFontSkia::ScaledFontSkia(gfxFont* aFont, Float aSize)
ScaledFontBase::ScaledFontBase(gfxFont* aFont, Float aSize)
: mSize(aSize)
{
NS_LossyConvertUTF16toASCII name(aFont->GetName());
mTypeface = SkTypeface::CreateFromName(name.get(), gfxFontStyleToSkia(aFont->GetStyle()));
}
#endif
ScaledFontSkia::ScaledFontSkia(Float aSize)
ScaledFontBase::~ScaledFontBase()
{
#ifdef USE_SKIA
SkSafeUnref(mTypeface);
#endif
}
ScaledFontBase::ScaledFontBase(Float aSize)
: mSize(aSize)
{
#ifdef USE_SKIA
mTypeface = NULL;
#endif
}
ScaledFontSkia::~ScaledFontSkia()
{
SkSafeUnref(mTypeface);
}
TemporaryRef<Path>
ScaledFontSkia::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
{
if (aTarget->GetType() != BACKEND_SKIA) {
return NULL;
#ifdef USE_SKIA
if (aTarget->GetType() == BACKEND_SKIA) {
SkPaint paint;
paint.setTypeface(GetSkTypeface());
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setTextSize(SkFloatToScalar(mSize));
std::vector<uint16_t> indices;
std::vector<SkPoint> offsets;
indices.resize(aBuffer.mNumGlyphs);
offsets.resize(aBuffer.mNumGlyphs);
for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
indices[i] = aBuffer.mGlyphs[i].mIndex;
offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
}
SkPath path;
paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
return new PathSkia(path, FILL_WINDING);
}
SkPaint paint;
paint.setTypeface(mTypeface);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setTextSize(SkFloatToScalar(mSize));
std::vector<uint16_t> indices;
std::vector<SkPoint> offsets;
indices.resize(aBuffer.mNumGlyphs);
offsets.resize(aBuffer.mNumGlyphs);
for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
indices[i] = aBuffer.mGlyphs[i].mIndex;
offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
}
SkPath path;
paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
return new PathSkia(path, FILL_WINDING);
#endif
return NULL;
}
}

View File

@ -35,36 +35,41 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef MOZILLA_GFX_SCALEDFONTSKIA_H_
#define MOZILLA_GFX_SCALEDFONTSKIA_H_
#ifndef MOZILLA_GFX_SCALEDFONTBASE_H_
#define MOZILLA_GFX_SCALEDFONTBASE_H_
#include "2D.h"
#ifdef USE_SKIA
#include "skia/SkTypeface.h"
#endif
class gfxFont;
namespace mozilla {
namespace gfx {
class ScaledFontSkia : public ScaledFont
class ScaledFontBase : public ScaledFont
{
public:
ScaledFontSkia(gfxFont* aFont, Float aSize);
ScaledFontSkia(Float aSize);
virtual ~ScaledFontSkia();
virtual FontType GetType() const { return FONT_SKIA; }
ScaledFontBase(Float aSize);
virtual ~ScaledFontBase();
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
#ifdef USE_SKIA
ScaledFontBase(gfxFont* aFont, Float aSize);
virtual SkTypeface* GetSkTypeface() { return mTypeface; }
virtual FontType GetType() const { return FONT_SKIA; }
#endif
protected:
friend class DrawTargetSkia;
#ifdef USE_SKIA
SkTypeface* mTypeface;
#endif
Float mSize;
};
}
}
#endif /* MOZILLA_GFX_SCALEDFONTSKIA_H_ */
#endif /* MOZILLA_GFX_SCALEDFONTBASE_H_ */

View File

@ -36,25 +36,76 @@
* ***** END LICENSE BLOCK ***** */
#include "ScaledFontMac.h"
#ifdef USE_SKIA
#include "PathSkia.h"
#include "skia/SkPaint.h"
#include "skia/SkPath.h"
#include "skia/SkTypeface_mac.h"
#endif
#include "DrawTargetCG.h"
#include <vector>
// prototype for private API
extern "C" {
CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph);
};
namespace mozilla {
namespace gfx {
ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize)
: ScaledFontSkia(aSize)
: ScaledFontBase(aSize)
{
mFontFace = CTFontCreateWithGraphicsFont(aFont, aSize, NULL, NULL);
mTypeface = SkCreateTypefaceFromCTFont(mFontFace);
// XXX: should we be taking a reference
mFont = CGFontRetain(aFont);
}
ScaledFontMac::~ScaledFontMac()
{
CFRelease(mFontFace);
CGFontRelease(mFont);
}
#ifdef USE_SKIA
SkTypeface* ScaledFontMac::GetSkTypeface()
{
if (!mTypeface) {
CTFontRef fontFace = CTFontCreateWithGraphicsFont(mFont, mSize, NULL, NULL);
mTypeface = SkCreateTypefaceFromCTFont(fontFace);
CFRelease(fontFace);
}
return mTypeface;
}
#endif
// private API here are the public options on OS X
// CTFontCreatePathForGlyph
// ATSUGlyphGetCubicPaths
// we've used this in cairo sucessfully for some time.
// Note: cairo dlsyms it. We could do that but maybe it's
// safe just to use?
TemporaryRef<Path>
ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
{
if (aTarget->GetType() == BACKEND_COREGRAPHICS) {
CGMutablePathRef path = CGPathCreateMutable();
for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
// XXX: we could probably fold both of these transforms together to avoid extra work
CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
aBuffer.mGlyphs[i].mPosition.x,
aBuffer.mGlyphs[i].mPosition.y);
CGPathAddPath(path, &matrix, glyphPath);
CGPathRelease(glyphPath);
}
return new PathCG(path, FILL_WINDING);
} else {
return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
}
}
}

View File

@ -38,25 +38,28 @@
#ifndef MOZILLA_GFX_SCALEDFONTMAC_H_
#define MOZILLA_GFX_SCALEDFONTMAC_H_
#include "ScaledFontSkia.h"
#import <ApplicationServices/ApplicationServices.h>
#include "2D.h"
#include "ScaledFontBase.h"
namespace mozilla {
namespace gfx {
class ScaledFontMac : public ScaledFontSkia
class ScaledFontMac : public ScaledFontBase
{
public:
ScaledFontMac(CGFontRef aFont, Float aSize);
virtual ~ScaledFontMac();
virtual FontType GetType() const { return FONT_MAC; }
#ifdef USE_SKIA
virtual SkTypeface* GetSkTypeface();
#endif
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
private:
friend class DrawTargetSkia;
CTFontRef mFontFace;
friend class DrawTargetCG;
CGFontRef mFont;
};
}

View File

@ -36,18 +36,32 @@
* ***** END LICENSE BLOCK ***** */
#include "ScaledFontWin.h"
#include "ScaeldFontBase.h"
#ifdef USE_SKIA
#include "skia/SkTypeface_win.h"
#endif
namespace mozilla {
namespace gfx {
ScaledFontWin::ScaledFontWin(gfxGDIFont* aFont, Float aSize)
: ScaledFontSkia(aSize)
: ScaledFontBase(aSize)
{
LOGFONT lf;
GetObject(aFont->GetHFONT(), sizeof(LOGFONT), &lf);
mTypeface = SkCreateTypefaceFromLOGFONT(lf);
}
#ifdef USE_SKIA
SkTypeface* ScaledFontWin::GetSkTypeface()
{
if (!mTypeface) {
mTypeface = SkCreateTypefaceFromLOGFONT(lf);
}
return mTypeface;
}
#endif
}
}

View File

@ -38,21 +38,25 @@
#ifndef MOZILLA_GFX_SCALEDFONTWIN_H_
#define MOZILLA_GFX_SCALEDFONTWIN_H_
#include "ScaledFontSkia.h"
#include "ScaledFontBase.h"
#include "gfxGDIFont.h"
namespace mozilla {
namespace gfx {
class ScaledFontWin : public ScaledFontSkia
class ScaledFontWin : public ScaledFontBase
{
public:
ScaledFontWin(gfxGDIFont* aFont, Float aSize);
virtual FontType GetType() const { return FONT_GDI; }
#ifdef USE_SKIA
virtual SkTypeface* GetSkTypeface();
#endif
private:
#ifdef USE_SKIA
friend class DrawTargetSkia;
#endif
};
}

View File

@ -40,9 +40,6 @@
namespace mozilla {
namespace gfx {
SourceSurfaceCG::SourceSurfaceCG()
{
}
SourceSurfaceCG::~SourceSurfaceCG()
{
@ -53,8 +50,8 @@ IntSize
SourceSurfaceCG::GetSize() const
{
IntSize size;
size.width = CGImageGetHeight(mImage);
size.height = CGImageGetWidth(mImage);
size.width = CGImageGetWidth(mImage);
size.height = CGImageGetHeight(mImage);
return size;
}
@ -67,10 +64,13 @@ SourceSurfaceCG::GetFormat() const
TemporaryRef<DataSourceSurface>
SourceSurfaceCG::GetDataSurface()
{
return NULL;
//XXX: we should be more disciplined about who takes a reference and where
CGImageRetain(mImage);
RefPtr<DataSourceSurfaceCG> dataSurf =
new DataSourceSurfaceCG(mImage);
return dataSurf;
}
static void releaseCallback(void *info, const void *data, size_t size) {
free(info);
}
@ -88,22 +88,24 @@ SourceSurfaceCG::InitFromData(unsigned char *aData,
int bitsPerComponent = 0;
int bitsPerPixel = 0;
assert(aSize.width >= 0 && aSize.height >= 0);
switch (aFormat) {
case B8G8R8A8:
case FORMAT_B8G8R8A8:
colorSpace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
case B8G8R8X8:
case FORMAT_B8G8R8X8:
colorSpace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
case A8:
case FORMAT_A8:
// XXX: why don't we set a colorspace here?
bitsPerComponent = 8;
bitsPerPixel = 8;
@ -119,7 +121,7 @@ SourceSurfaceCG::InitFromData(unsigned char *aData,
aSize.height * aStride,
releaseCallback);
if (aFormat == A8) {
if (aFormat == FORMAT_A8) {
CGFloat decode[] = {1.0, 0.0};
mImage = CGImageMaskCreate (aSize.width, aSize.height,
bitsPerComponent,
@ -145,12 +147,173 @@ SourceSurfaceCG::InitFromData(unsigned char *aData,
CGDataProviderRelease(dataProvider);
CGColorSpaceRelease (colorSpace);
if (mImage) {
return false;
return mImage != NULL;
}
DataSourceSurfaceCG::~DataSourceSurfaceCG()
{
CGImageRelease(mImage);
free(CGBitmapContextGetData(mCg));
CGContextRelease(mCg);
}
IntSize
DataSourceSurfaceCG::GetSize() const
{
IntSize size;
size.width = CGImageGetWidth(mImage);
size.height = CGImageGetHeight(mImage);
return size;
}
bool
DataSourceSurfaceCG::InitFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat)
{
//XXX: we should avoid creating this colorspace everytime
CGColorSpaceRef colorSpace = NULL;
CGBitmapInfo bitinfo = 0;
CGDataProviderRef dataProvider = NULL;
int bitsPerComponent = 0;
int bitsPerPixel = 0;
switch (aFormat) {
case FORMAT_B8G8R8A8:
colorSpace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
case FORMAT_B8G8R8X8:
colorSpace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
case FORMAT_A8:
// XXX: why don't we set a colorspace here?
bitsPerComponent = 8;
bitsPerPixel = 8;
};
void *data = malloc(aStride * aSize.height);
memcpy(data, aData, aStride * aSize.height);
//mFormat = aFormat;
dataProvider = CGDataProviderCreateWithData (data,
data,
aSize.height * aStride,
releaseCallback);
if (aFormat == FORMAT_A8) {
CGFloat decode[] = {1.0, 0.0};
mImage = CGImageMaskCreate (aSize.width, aSize.height,
bitsPerComponent,
bitsPerPixel,
aStride,
dataProvider,
decode,
true);
} else {
mImage = CGImageCreate (aSize.width, aSize.height,
bitsPerComponent,
bitsPerPixel,
aStride,
colorSpace,
bitinfo,
dataProvider,
NULL,
true,
kCGRenderingIntentDefault);
}
return true;
CGDataProviderRelease(dataProvider);
CGColorSpaceRelease (colorSpace);
return mImage;
}
CGContextRef CreateBitmapContextForImage(CGImageRef image)
{
CGColorSpaceRef colorSpace;
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
int bitmapBytesPerRow = (width * 4);
int bitmapByteCount = (bitmapBytesPerRow * height);
void *data = calloc(bitmapByteCount, 1);
//XXX: which color space should we be using here?
colorSpace = CGColorSpaceCreateDeviceRGB();
assert(colorSpace);
// we'd like to pass NULL as the first parameter
// to let Quartz manage this memory for us. However,
// on 10.5 and older CGBitmapContextGetData will return
// NULL instead of the associated buffer so we need
// to manage it ourselves.
CGContextRef cg = CGBitmapContextCreate(data,
width,
height,
8,
bitmapBytesPerRow,
colorSpace,
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
assert(cg);
CGColorSpaceRelease(colorSpace);
return cg;
}
DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage)
{
mImage = aImage;
mCg = CreateBitmapContextForImage(aImage);
if (mCg == NULL) {
// error creating context
return;
}
// Get image width, height. We'll use the entire image.
CGFloat w = CGImageGetWidth(aImage);
CGFloat h = CGImageGetHeight(aImage);
CGRect rect = {{0,0},{w,h}};
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(mCg, rect, aImage);
// Now we can get a pointer to the image data associated with the bitmap
// context.
mData = CGBitmapContextGetData(mCg);
assert(mData);
}
unsigned char *
DataSourceSurfaceCG::GetData()
{
// See http://developer.apple.com/library/mac/#qa/qa1509/_index.html
// the following only works on 10.5+, the Q&A above suggests a method
// that can be used for earlier versions
//CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
//unsigned char *dataPtr = CFDataGetBytePtr(data);
//CFDataRelease(data);
// unfortunately the the method above only works for read-only access and
// we need read-write for DataSourceSurfaces
return (unsigned char*)mData;
}
}
}

View File

@ -47,10 +47,11 @@ namespace gfx {
class SourceSurfaceCG : public SourceSurface
{
public:
SourceSurfaceCG();
SourceSurfaceCG() {}
SourceSurfaceCG(CGImageRef aImage) : mImage(aImage) {}
~SourceSurfaceCG();
virtual SurfaceType GetType() const { return COREGRAPHICS_IMAGE; }
virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_IMAGE; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
@ -71,5 +72,38 @@ private:
SurfaceFormat mFormat;
};
class DataSourceSurfaceCG : public DataSourceSurface
{
public:
DataSourceSurfaceCG() {}
DataSourceSurfaceCG(CGImageRef aImage);
~DataSourceSurfaceCG();
virtual SurfaceType GetType() const { return SURFACE_DATA; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; }
CGImageRef GetImage() { return mImage; }
bool InitFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat);
virtual unsigned char *GetData();
virtual int32_t Stride() { return CGImageGetBytesPerRow(mImage); }
private:
CGContextRef mCg;
CGImageRef mImage;
//XXX: we don't need to store mData we can just get it from the CGContext
void *mData;
/* It might be better to just use the bitmap info from the CGImageRef to
* deduce the format to save space in SourceSurfaceCG,
* for now we just store it in mFormat */
};
}
}

View File

@ -80,13 +80,15 @@ enum FontType
FONT_GDI,
FONT_MAC,
FONT_SKIA,
FONT_CAIRO
FONT_CAIRO,
FONT_COREGRAPHICS
};
enum NativeSurfaceType
{
NATIVE_SURFACE_D3D10_TEXTURE,
NATIVE_SURFACE_CAIRO_SURFACE
NATIVE_SURFACE_CAIRO_SURFACE,
NATIVE_SURFACE_CGCONTEXT
};
enum NativeFontType

View File

@ -683,7 +683,7 @@ struct gfxTextRange {
* completely, with all its words, and avoid the cost of aging the words
* individually. That only happens with longer-lived fonts.
*/
class THEBES_API gfxFontCache : public nsExpirationTracker<gfxFont,3> {
class THEBES_API gfxFontCache MOZ_FINAL : public nsExpirationTracker<gfxFont,3> {
public:
enum {
FONT_TIMEOUT_SECONDS = 10,

View File

@ -57,6 +57,7 @@
#include "qcms.h"
#include <dlfcn.h>
#include "mozilla/gfx/2D.h"
using namespace mozilla;
using namespace mozilla::gfx;
@ -131,7 +132,7 @@ gfxPlatformMac::CreateOffscreenSurface(const gfxIntSize& size,
NS_IF_ADDREF(newSurface);
return newSurface;
}
already_AddRefed<gfxASurface>
gfxPlatformMac::OptimizeImage(gfxImageSurface *aSurface,
gfxASurface::gfxImageFormat format)
@ -162,7 +163,7 @@ gfxPlatformMac::GetScaledFontForFont(gfxFont *aFont)
bool
gfxPlatformMac::SupportsAzure(BackendType& aBackend)
{
aBackend = BACKEND_SKIA;
aBackend = BACKEND_COREGRAPHICS;
return true;
}
@ -298,6 +299,26 @@ gfxPlatformMac::ReadAntiAliasingThreshold()
return threshold;
}
already_AddRefed<gfxASurface>
gfxPlatformMac::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
{
if (aTarget->GetType() == BACKEND_COREGRAPHICS) {
CGContextRef cg = static_cast<CGContextRef>(aTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT));
//XXX: it would be nice to have an implicit conversion from IntSize to gfxIntSize
IntSize intSize = aTarget->GetSize();
gfxIntSize size(intSize.width, intSize.height);
nsRefPtr<gfxASurface> surf =
new gfxQuartzSurface(cg, size);
return surf.forget();
}
return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
}
qcms_profile *
gfxPlatformMac::GetPlatformCMSOutputProfile()
{

View File

@ -50,6 +50,7 @@
#define MAC_OS_X_MAJOR_VERSION_MASK 0xFFFFFFF0U
class gfxTextRun;
class mozilla::gfx::DrawTarget;
class THEBES_API gfxPlatformMac : public gfxPlatform {
public:
@ -104,6 +105,8 @@ public:
// lower threshold on font anti-aliasing
PRUint32 GetAntiAliasingThreshold() { return mFontAntiAliasingThreshold; }
virtual already_AddRefed<gfxASurface>
GetThebesSurfaceForDrawTarget(mozilla::gfx::DrawTarget *aTarget);
private:
virtual qcms_profile* GetPlatformCMSOutputProfile();

View File

@ -519,6 +519,7 @@ gfxWindowsPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
nsRefPtr<gfxASurface> surf =
new gfxD2DSurface(texture, ContentForFormat(aTarget->GetFormat()));
// shouldn't this hold a reference?
surf->SetData(&kDrawTarget, aTarget, NULL);
return surf.forget();