mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backout bug 580786 / bug 716639 / bug 692879 / bug 717921 / bug 692879 (2dc5909e63b0:ced751d32df6); a=philor
This commit is contained in:
parent
834bc5acc7
commit
ce301744c0
@ -1165,10 +1165,8 @@ nsCanvasRenderingContext2DAzure::Redraw()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mThebesSurface)
|
||||
mThebesSurface =
|
||||
gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
|
||||
mThebesSurface->MarkDirty();
|
||||
if (mThebesSurface)
|
||||
mThebesSurface->MarkDirty();
|
||||
|
||||
nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
|
||||
|
||||
@ -1197,10 +1195,8 @@ nsCanvasRenderingContext2DAzure::Redraw(const mgfx::Rect &r)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mThebesSurface)
|
||||
mThebesSurface =
|
||||
gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
|
||||
mThebesSurface->MarkDirty();
|
||||
if (mThebesSurface)
|
||||
mThebesSurface->MarkDirty();
|
||||
|
||||
nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
|
||||
|
||||
|
@ -18,23 +18,6 @@ function IsD2DEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
function IsMacOSX10_5orOlder() {
|
||||
var is105orOlder = false;
|
||||
|
||||
if (navigator.platform.indexOf("Mac") == 0) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var version = Components.classes["@mozilla.org/system-info;1"]
|
||||
.getService(Components.interfaces.nsIPropertyBag2)
|
||||
.getProperty("version");
|
||||
// the next line is correct: Mac OS 10.6 corresponds to Darwin version 10 !
|
||||
// Mac OS 10.5 would be Darwin version 9. the |version| string we've got here
|
||||
// is the Darwin version.
|
||||
is105orOlder = (parseFloat(version) < 10.0);
|
||||
}
|
||||
return is105orOlder;
|
||||
}
|
||||
|
||||
|
||||
function IsAzureEnabled() {
|
||||
var enabled = false;
|
||||
|
||||
@ -5795,12 +5778,10 @@ function test_2d_gradient_interpolate_overlap() {
|
||||
var canvas = document.getElementById('c215');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
if (!IsD2DEnabled() && !IsMacOSX10_5orOlder()) {
|
||||
// On D2D the different nature of how gradients
|
||||
if (!IsD2DEnabled()) {
|
||||
// Only run this on non-D2D. On D2D the different nature of how gradients
|
||||
// are drawn makes it so we cannot guarantee these stops are completely
|
||||
// hard.
|
||||
|
||||
// On OS X 10.5 quartz is confused by the overlapping stops: Bug #715235
|
||||
canvas.width = 200;
|
||||
var g = ctx.createLinearGradient(0, 0, 200, 0);
|
||||
g.addColorStop(0, '#f00');
|
||||
@ -5854,15 +5835,12 @@ for (var p = 0; p < ps.length; ++p)
|
||||
}
|
||||
ctx.fillStyle = g;
|
||||
ctx.fillRect(0, 0, 100, 50);
|
||||
isPixel(ctx, 1,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 30,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 40,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 60,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 80,25, 0,255,0,255, 0);
|
||||
|
||||
if (!IsMacOSX10_5orOlder()) {
|
||||
// On OS X 10.5 quartz is confused by the overlapping stops: Bug #715235
|
||||
isPixel(ctx, 1,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 30,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 40,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 60,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 80,25, 0,255,0,255, 0);
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@ -41,7 +41,7 @@
|
||||
#include "Point.h"
|
||||
#include "Rect.h"
|
||||
#include "Matrix.h"
|
||||
#include "UserData.h"
|
||||
|
||||
// This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
|
||||
// outparams using the &-operator. But it will have to do as there's no easy
|
||||
// solution.
|
||||
@ -744,14 +744,7 @@ public:
|
||||
*/
|
||||
virtual void *GetNativeSurface(NativeSurfaceType aType) { return NULL; }
|
||||
|
||||
void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
|
||||
mUserData.Add(key, userData, destroy);
|
||||
}
|
||||
void *GetUserData(UserDataKey *key) {
|
||||
return mUserData.Get(key);
|
||||
}
|
||||
protected:
|
||||
UserData mUserData;
|
||||
Matrix mTransform;
|
||||
bool mTransformDirty : 1;
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Jeff Muizelaar <jmuizelaar@mozilla.com>
|
||||
* 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
|
||||
@ -37,9 +37,6 @@
|
||||
#include "DrawTargetCG.h"
|
||||
#include "SourceSurfaceCG.h"
|
||||
#include "Rect.h"
|
||||
#include "ScaledFontMac.h"
|
||||
#include "Tools.h"
|
||||
#include <vector>
|
||||
|
||||
//CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
|
||||
|
||||
@ -51,11 +48,6 @@ 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;
|
||||
@ -63,40 +55,18 @@ 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;
|
||||
}
|
||||
@ -111,31 +81,12 @@ 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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TemporaryRef<SourceSurface>
|
||||
@ -146,7 +97,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;
|
||||
}
|
||||
|
||||
@ -159,67 +110,16 @@ 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 DrawSurfaceOptions &aSurfOptions,
|
||||
const DrawOptions &aDrawOptions)
|
||||
const DrawOptions &aOptions,
|
||||
const DrawSurfaceOptions &aSurfOptions)
|
||||
{
|
||||
CGImageRef image;
|
||||
CGImageRef subimage = NULL;
|
||||
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));
|
||||
if (aSurface->GetType() == COREGRAPHICS_IMAGE) {
|
||||
image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
|
||||
/* we have two options here:
|
||||
* - create a subimage -- this is slower
|
||||
@ -229,737 +129,56 @@ DrawTargetCG::DrawSurface(SourceSurface *aSurface,
|
||||
image = subimage;
|
||||
}
|
||||
|
||||
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);
|
||||
CGContextDrawImage(mCg, RectToCGRect(aDest), image);
|
||||
|
||||
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)
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
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));
|
||||
//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);
|
||||
}
|
||||
|
||||
fixer.Fix(mCg);
|
||||
CGContextRestoreGState(mCg);
|
||||
CGContextSetBlendMode(mCg, ToBlendMode(aOptions.mCompositionOp));
|
||||
CGContextFillRect(mCg, RectToCGRect(aRect));
|
||||
}
|
||||
|
||||
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::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator)
|
||||
{
|
||||
CGImageRef image;
|
||||
CGImageRef subimage = NULL;
|
||||
if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
|
||||
image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
|
||||
|
||||
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)
|
||||
DrawTargetCG::Init(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, SurfaceFormat &)
|
||||
{
|
||||
// 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();
|
||||
CGColorSpaceRef cgColorspace;
|
||||
cgColorspace = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
mSize = aSize;
|
||||
|
||||
int bitsPerComponent = 8;
|
||||
int stride = mSize.width*4;
|
||||
int stride = mSize.width;
|
||||
|
||||
CGBitmapInfo bitinfo;
|
||||
|
||||
bitinfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
|
||||
|
||||
// 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);
|
||||
// XXX: mWidth is ugly
|
||||
mCg = CGBitmapContextCreate (NULL,
|
||||
mSize.width,
|
||||
mSize.height,
|
||||
bitsPerComponent,
|
||||
stride,
|
||||
cgColorspace,
|
||||
bitinfo);
|
||||
|
||||
|
||||
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;
|
||||
CGColorSpaceRelease (cgColorspace);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -35,155 +35,50 @@
|
||||
*
|
||||
* ***** 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 BACKEND_COREGRAPHICS; }
|
||||
virtual BackendType GetType() const { return COREGRAPHICS; }
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
const DrawOptions &aOptions = DrawOptions(),
|
||||
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions());
|
||||
|
||||
virtual void FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
|
||||
|
||||
//XXX: why do we take a reference to SurfaceFormat?
|
||||
bool Init(const IntSize &aSize, SurfaceFormat&);
|
||||
bool Init(const IntSize &aSize);
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
#include "DrawTargetSkia.h"
|
||||
#include "SourceSurfaceSkia.h"
|
||||
#include "ScaledFontBase.h"
|
||||
#include "ScaledFontSkia.h"
|
||||
#include "skia/SkDevice.h"
|
||||
#include "skia/SkTypeface.h"
|
||||
#include "skia/SkGradientShader.h"
|
||||
@ -58,7 +58,6 @@ 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);
|
||||
}
|
||||
|
||||
@ -521,10 +520,10 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
|
||||
|
||||
MarkChanged();
|
||||
|
||||
ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
|
||||
ScaledFontSkia* skiaFont = static_cast<ScaledFontSkia*>(aFont);
|
||||
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
|
||||
paint.mPaint.setTypeface(skiaFont->GetSkTypeface());
|
||||
paint.mPaint.setTypeface(skiaFont->mTypeface);
|
||||
paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
|
||||
paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
|
||||
|
@ -44,20 +44,13 @@
|
||||
|
||||
#ifdef USE_SKIA
|
||||
#include "DrawTargetSkia.h"
|
||||
#include "ScaledFontBase.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "ScaledFontWin.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "ScaledFontMac.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "DrawTargetCG.h"
|
||||
#ifdef WIN32
|
||||
#include "ScaledFontWin.h"
|
||||
#endif
|
||||
#include "ScaledFontSkia.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@ -97,7 +90,7 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor
|
||||
}
|
||||
break;
|
||||
}
|
||||
#elif defined XP_MACOSX || defined ANDROID
|
||||
#endif
|
||||
#ifdef USE_SKIA
|
||||
case BACKEND_SKIA:
|
||||
{
|
||||
@ -108,18 +101,6 @@ 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.";
|
||||
@ -141,13 +122,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:
|
||||
{
|
||||
@ -156,7 +137,7 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz
|
||||
#endif
|
||||
case NATIVE_FONT_SKIA_FONT_FACE:
|
||||
{
|
||||
return new ScaledFontBase(static_cast<gfxFont*>(aNativeFont.mFont), aSize);
|
||||
return new ScaledFontSkia(static_cast<gfxFont*>(aNativeFont.mFont), aSize);
|
||||
}
|
||||
#endif
|
||||
case NATIVE_FONT_CAIRO_FONT_FACE:
|
||||
|
@ -62,7 +62,6 @@ EXPORTS_mozilla/gfx = \
|
||||
Matrix.h \
|
||||
Rect.h \
|
||||
Types.h \
|
||||
UserData.h \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
@ -73,16 +72,8 @@ 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
|
||||
|
||||
@ -91,6 +82,7 @@ CPPSRCS += \
|
||||
SourceSurfaceSkia.cpp \
|
||||
DrawTargetSkia.cpp \
|
||||
PathSkia.cpp \
|
||||
ScaledFontSkia.cpp \
|
||||
$(NULL)
|
||||
|
||||
DEFINES += -DUSE_SKIA
|
||||
|
@ -1,273 +0,0 @@
|
||||
/* -*- 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
133
gfx/2d/PathCG.h
@ -1,133 +0,0 @@
|
||||
/* -*- 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
|
@ -36,76 +36,25 @@
|
||||
* ***** 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)
|
||||
: ScaledFontBase(aSize)
|
||||
: ScaledFontSkia(aSize)
|
||||
{
|
||||
// XXX: should we be taking a reference
|
||||
mFont = CGFontRetain(aFont);
|
||||
mFontFace = CTFontCreateWithGraphicsFont(aFont, aSize, NULL, NULL);
|
||||
mTypeface = SkCreateTypefaceFromCTFont(mFontFace);
|
||||
}
|
||||
|
||||
ScaledFontMac::~ScaledFontMac()
|
||||
{
|
||||
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);
|
||||
}
|
||||
CFRelease(mFontFace);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,28 +38,25 @@
|
||||
#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 ScaledFontBase
|
||||
class ScaledFontMac : public ScaledFontSkia
|
||||
{
|
||||
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 DrawTargetCG;
|
||||
CGFontRef mFont;
|
||||
friend class DrawTargetSkia;
|
||||
|
||||
CTFontRef mFontFace;
|
||||
};
|
||||
|
||||
}
|
||||
|
72
gfx/2d/ScaledFontBase.cpp → gfx/2d/ScaledFontSkia.cpp
Normal file → Executable file
72
gfx/2d/ScaledFontBase.cpp → gfx/2d/ScaledFontSkia.cpp
Normal file → Executable file
@ -35,12 +35,10 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "ScaledFontBase.h"
|
||||
#ifdef USE_SKIA
|
||||
#include "ScaledFontSkia.h"
|
||||
#include "PathSkia.h"
|
||||
#include "skia/SkPaint.h"
|
||||
#include "skia/SkPath.h"
|
||||
#endif
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
using namespace std;
|
||||
@ -48,7 +46,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) {
|
||||
@ -63,57 +61,49 @@ static SkTypeface::Style gfxFontStyleToSkia(const gfxFontStyle* aStyle)
|
||||
return SkTypeface::kNormal;
|
||||
}
|
||||
|
||||
ScaledFontBase::ScaledFontBase(gfxFont* aFont, Float aSize)
|
||||
ScaledFontSkia::ScaledFontSkia(gfxFont* aFont, Float aSize)
|
||||
: mSize(aSize)
|
||||
{
|
||||
NS_LossyConvertUTF16toASCII name(aFont->GetName());
|
||||
mTypeface = SkTypeface::CreateFromName(name.get(), gfxFontStyleToSkia(aFont->GetStyle()));
|
||||
}
|
||||
#endif
|
||||
|
||||
ScaledFontBase::~ScaledFontBase()
|
||||
{
|
||||
#ifdef USE_SKIA
|
||||
SkSafeUnref(mTypeface);
|
||||
#endif
|
||||
}
|
||||
|
||||
ScaledFontBase::ScaledFontBase(Float aSize)
|
||||
ScaledFontSkia::ScaledFontSkia(Float aSize)
|
||||
: mSize(aSize)
|
||||
{
|
||||
#ifdef USE_SKIA
|
||||
mTypeface = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
ScaledFontSkia::~ScaledFontSkia()
|
||||
{
|
||||
SkSafeUnref(mTypeface);
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
|
||||
ScaledFontSkia::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
|
||||
{
|
||||
#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);
|
||||
if (aTarget->GetType() != BACKEND_SKIA) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
25
gfx/2d/ScaledFontBase.h → gfx/2d/ScaledFontSkia.h
Normal file → Executable file
25
gfx/2d/ScaledFontBase.h → gfx/2d/ScaledFontSkia.h
Normal file → Executable file
@ -35,41 +35,36 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MOZILLA_GFX_SCALEDFONTBASE_H_
|
||||
#define MOZILLA_GFX_SCALEDFONTBASE_H_
|
||||
#ifndef MOZILLA_GFX_SCALEDFONTSKIA_H_
|
||||
#define MOZILLA_GFX_SCALEDFONTSKIA_H_
|
||||
|
||||
#include "2D.h"
|
||||
#ifdef USE_SKIA
|
||||
#include "skia/SkTypeface.h"
|
||||
#endif
|
||||
|
||||
class gfxFont;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class ScaledFontBase : public ScaledFont
|
||||
class ScaledFontSkia : public ScaledFont
|
||||
{
|
||||
public:
|
||||
ScaledFontBase(Float aSize);
|
||||
virtual ~ScaledFontBase();
|
||||
ScaledFontSkia(gfxFont* aFont, Float aSize);
|
||||
ScaledFontSkia(Float aSize);
|
||||
virtual ~ScaledFontSkia();
|
||||
|
||||
virtual FontType GetType() const { return FONT_SKIA; }
|
||||
|
||||
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_SCALEDFONTBASE_H_ */
|
||||
#endif /* MOZILLA_GFX_SCALEDFONTSKIA_H_ */
|
@ -36,32 +36,18 @@
|
||||
* ***** 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)
|
||||
: ScaledFontBase(aSize)
|
||||
: ScaledFontSkia(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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -38,25 +38,21 @@
|
||||
#ifndef MOZILLA_GFX_SCALEDFONTWIN_H_
|
||||
#define MOZILLA_GFX_SCALEDFONTWIN_H_
|
||||
|
||||
#include "ScaledFontBase.h"
|
||||
#include "ScaledFontSkia.h"
|
||||
#include "gfxGDIFont.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class ScaledFontWin : public ScaledFontBase
|
||||
class ScaledFontWin : public ScaledFontSkia
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,9 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
SourceSurfaceCG::SourceSurfaceCG()
|
||||
{
|
||||
}
|
||||
|
||||
SourceSurfaceCG::~SourceSurfaceCG()
|
||||
{
|
||||
@ -50,8 +53,8 @@ IntSize
|
||||
SourceSurfaceCG::GetSize() const
|
||||
{
|
||||
IntSize size;
|
||||
size.width = CGImageGetWidth(mImage);
|
||||
size.height = CGImageGetHeight(mImage);
|
||||
size.width = CGImageGetHeight(mImage);
|
||||
size.height = CGImageGetWidth(mImage);
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -64,13 +67,10 @@ SourceSurfaceCG::GetFormat() const
|
||||
TemporaryRef<DataSourceSurface>
|
||||
SourceSurfaceCG::GetDataSurface()
|
||||
{
|
||||
//XXX: we should be more disciplined about who takes a reference and where
|
||||
CGImageRetain(mImage);
|
||||
RefPtr<DataSourceSurfaceCG> dataSurf =
|
||||
new DataSourceSurfaceCG(mImage);
|
||||
return dataSurf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void releaseCallback(void *info, const void *data, size_t size) {
|
||||
free(info);
|
||||
}
|
||||
@ -88,24 +88,22 @@ SourceSurfaceCG::InitFromData(unsigned char *aData,
|
||||
int bitsPerComponent = 0;
|
||||
int bitsPerPixel = 0;
|
||||
|
||||
assert(aSize.width >= 0 && aSize.height >= 0);
|
||||
|
||||
switch (aFormat) {
|
||||
case FORMAT_B8G8R8A8:
|
||||
case B8G8R8A8:
|
||||
colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
||||
bitsPerComponent = 8;
|
||||
bitsPerPixel = 32;
|
||||
break;
|
||||
|
||||
case FORMAT_B8G8R8X8:
|
||||
case B8G8R8X8:
|
||||
colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
bitsPerComponent = 8;
|
||||
bitsPerPixel = 32;
|
||||
break;
|
||||
|
||||
case FORMAT_A8:
|
||||
case A8:
|
||||
// XXX: why don't we set a colorspace here?
|
||||
bitsPerComponent = 8;
|
||||
bitsPerPixel = 8;
|
||||
@ -121,7 +119,7 @@ SourceSurfaceCG::InitFromData(unsigned char *aData,
|
||||
aSize.height * aStride,
|
||||
releaseCallback);
|
||||
|
||||
if (aFormat == FORMAT_A8) {
|
||||
if (aFormat == A8) {
|
||||
CGFloat decode[] = {1.0, 0.0};
|
||||
mImage = CGImageMaskCreate (aSize.width, aSize.height,
|
||||
bitsPerComponent,
|
||||
@ -147,173 +145,12 @@ SourceSurfaceCG::InitFromData(unsigned char *aData,
|
||||
CGDataProviderRelease(dataProvider);
|
||||
CGColorSpaceRelease (colorSpace);
|
||||
|
||||
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);
|
||||
if (mImage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CGDataProviderRelease(dataProvider);
|
||||
CGColorSpaceRelease (colorSpace);
|
||||
|
||||
return mImage;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -47,11 +47,10 @@ namespace gfx {
|
||||
class SourceSurfaceCG : public SourceSurface
|
||||
{
|
||||
public:
|
||||
SourceSurfaceCG() {}
|
||||
SourceSurfaceCG(CGImageRef aImage) : mImage(aImage) {}
|
||||
SourceSurfaceCG();
|
||||
~SourceSurfaceCG();
|
||||
|
||||
virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_IMAGE; }
|
||||
virtual SurfaceType GetType() const { return COREGRAPHICS_IMAGE; }
|
||||
virtual IntSize GetSize() const;
|
||||
virtual SurfaceFormat GetFormat() const;
|
||||
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
|
||||
@ -72,38 +71,5 @@ 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 */
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -80,15 +80,13 @@ enum FontType
|
||||
FONT_GDI,
|
||||
FONT_MAC,
|
||||
FONT_SKIA,
|
||||
FONT_CAIRO,
|
||||
FONT_COREGRAPHICS
|
||||
FONT_CAIRO
|
||||
};
|
||||
|
||||
enum NativeSurfaceType
|
||||
{
|
||||
NATIVE_SURFACE_D3D10_TEXTURE,
|
||||
NATIVE_SURFACE_CAIRO_SURFACE,
|
||||
NATIVE_SURFACE_CGCONTEXT
|
||||
NATIVE_SURFACE_CAIRO_SURFACE
|
||||
};
|
||||
|
||||
enum NativeFontType
|
||||
|
@ -1,108 +0,0 @@
|
||||
/* -*- Mode: c++; tab-width: 2; 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 (Sub) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* 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
|
||||
* 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 <stdlib.h>
|
||||
#include "mozilla/mozalloc.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
struct UserDataKey {
|
||||
int unused;
|
||||
};
|
||||
|
||||
/* this class is basically a clone of the user data concept from cairo */
|
||||
class UserData
|
||||
{
|
||||
typedef void (*destroyFunc)(void *data);
|
||||
public:
|
||||
UserData() : count(0), entries(NULL) {}
|
||||
|
||||
/* Attaches untyped userData associated with key. destroy is called on destruction */
|
||||
void Add(UserDataKey *key, void *userData, destroyFunc destroy)
|
||||
{
|
||||
// We could keep entries in a std::vector instead of managing it by hand
|
||||
// but that would propagate an stl dependency out which we'd rather not
|
||||
// do (see bug 666609). Plus, the entries array is expect to stay small
|
||||
// so doing a realloc everytime we add a new entry shouldn't be too costly
|
||||
entries = static_cast<Entry*>(moz_xrealloc(entries, sizeof(Entry)*(count+1)));
|
||||
|
||||
entries[count].key = key;
|
||||
entries[count].userData = userData;
|
||||
entries[count].destroy = destroy;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
//XXX: we probably want to add a way to remove Keys
|
||||
|
||||
/* Retrives the userData for the associated key */
|
||||
void *Get(UserDataKey *key)
|
||||
{
|
||||
for (int i=0; i<count; i++) {
|
||||
if (key == entries[i].key) {
|
||||
return entries[i].userData;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
~UserData()
|
||||
{
|
||||
for (int i=0; i<count; i++) {
|
||||
entries[i].destroy(entries[i].userData);
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
const UserDataKey *key;
|
||||
void *userData;
|
||||
destroyFunc destroy;
|
||||
};
|
||||
|
||||
int count;
|
||||
Entry *entries;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 MOZ_FINAL : public nsExpirationTracker<gfxFont,3> {
|
||||
class THEBES_API gfxFontCache : public nsExpirationTracker<gfxFont,3> {
|
||||
public:
|
||||
enum {
|
||||
FONT_TIMEOUT_SECONDS = 10,
|
||||
|
@ -532,26 +532,9 @@ DataSourceSurfaceDestroy(void *dataSourceSurface)
|
||||
static_cast<DataSourceSurface*>(dataSourceSurface)->Release();
|
||||
}
|
||||
|
||||
void DestroyThebesSurface(void *data)
|
||||
{
|
||||
gfxASurface *surface = static_cast<gfxASurface*>(data);
|
||||
surface->Release();
|
||||
}
|
||||
|
||||
UserDataKey ThebesSurfaceKey;
|
||||
|
||||
// The semantics of this function are sort of weird. We snapshot the first
|
||||
// time and then return the snapshotted surface for the lifetime of the
|
||||
// draw target
|
||||
already_AddRefed<gfxASurface>
|
||||
gfxPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
|
||||
{
|
||||
void *surface = aTarget->GetUserData(&ThebesSurfaceKey);
|
||||
if (surface) {
|
||||
nsRefPtr<gfxASurface> surf = static_cast<gfxASurface*>(surface);
|
||||
return surf.forget();
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> source = aTarget->Snapshot();
|
||||
RefPtr<DataSourceSurface> data = source->GetDataSurface();
|
||||
|
||||
@ -562,18 +545,12 @@ gfxPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
|
||||
IntSize size = data->GetSize();
|
||||
gfxASurface::gfxImageFormat format = gfxASurface::FormatFromContent(ContentForFormat(data->GetFormat()));
|
||||
|
||||
nsRefPtr<gfxImageSurface> surf =
|
||||
nsRefPtr<gfxImageSurface> image =
|
||||
new gfxImageSurface(data->GetData(), gfxIntSize(size.width, size.height),
|
||||
data->Stride(), format);
|
||||
|
||||
surf->SetData(&kDrawSourceSurface, data.forget().drop(), DataSourceSurfaceDestroy);
|
||||
|
||||
// add a reference to be held by the drawTarget
|
||||
// careful, the reference graph is getting complicated here
|
||||
surf->AddRef();
|
||||
aTarget->AddUserData(&ThebesSurfaceKey, surf.get(), DestroyThebesSurface);
|
||||
|
||||
return surf.forget();
|
||||
image->SetData(&kDrawSourceSurface, data.forget().drop(), DataSourceSurfaceDestroy);
|
||||
return image.forget();
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget>
|
||||
|
@ -71,9 +71,6 @@ class gfxTextRun;
|
||||
class nsIURI;
|
||||
class nsIAtom;
|
||||
|
||||
extern mozilla::gfx::UserDataKey ThebesSurfaceKey;
|
||||
void DestroyThebesSurface(void *data);
|
||||
|
||||
extern cairo_user_data_key_t kDrawTarget;
|
||||
|
||||
// pref lang id's for font prefs
|
||||
|
@ -57,7 +57,6 @@
|
||||
#include "qcms.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
@ -132,7 +131,7 @@ gfxPlatformMac::CreateOffscreenSurface(const gfxIntSize& size,
|
||||
NS_IF_ADDREF(newSurface);
|
||||
return newSurface;
|
||||
}
|
||||
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
gfxPlatformMac::OptimizeImage(gfxImageSurface *aSurface,
|
||||
gfxASurface::gfxImageFormat format)
|
||||
@ -163,7 +162,7 @@ gfxPlatformMac::GetScaledFontForFont(gfxFont *aFont)
|
||||
bool
|
||||
gfxPlatformMac::SupportsAzure(BackendType& aBackend)
|
||||
{
|
||||
aBackend = BACKEND_COREGRAPHICS;
|
||||
aBackend = BACKEND_SKIA;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -299,36 +298,6 @@ gfxPlatformMac::ReadAntiAliasingThreshold()
|
||||
return threshold;
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
gfxPlatformMac::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
|
||||
{
|
||||
if (aTarget->GetType() == BACKEND_COREGRAPHICS) {
|
||||
void *surface = aTarget->GetUserData(&ThebesSurfaceKey);
|
||||
if (surface) {
|
||||
nsRefPtr<gfxASurface> surf = static_cast<gfxQuartzSurface*>(surface);
|
||||
return surf.forget();
|
||||
} else {
|
||||
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);
|
||||
|
||||
// add a reference to be held by the drawTarget
|
||||
surf->AddRef();
|
||||
aTarget->AddUserData(&ThebesSurfaceKey, surf.get(), DestroyThebesSurface);
|
||||
|
||||
return surf.forget();
|
||||
}
|
||||
}
|
||||
|
||||
return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
|
||||
}
|
||||
|
||||
|
||||
qcms_profile *
|
||||
gfxPlatformMac::GetPlatformCMSOutputProfile()
|
||||
{
|
||||
|
@ -50,7 +50,6 @@
|
||||
#define MAC_OS_X_MAJOR_VERSION_MASK 0xFFFFFFF0U
|
||||
|
||||
class gfxTextRun;
|
||||
class mozilla::gfx::DrawTarget;
|
||||
|
||||
class THEBES_API gfxPlatformMac : public gfxPlatform {
|
||||
public:
|
||||
@ -105,8 +104,6 @@ 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();
|
||||
|
||||
|
@ -81,26 +81,6 @@ gfxQuartzSurface::gfxQuartzSurface(CGContextRef context,
|
||||
unsigned int width = static_cast<unsigned int>(mSize.width);
|
||||
unsigned int height = static_cast<unsigned int>(mSize.height);
|
||||
|
||||
cairo_surface_t *surf =
|
||||
cairo_quartz_surface_create_for_cg_context(context,
|
||||
width, height);
|
||||
|
||||
CGContextRetain(mCGContext);
|
||||
|
||||
Init(surf);
|
||||
}
|
||||
|
||||
gfxQuartzSurface::gfxQuartzSurface(CGContextRef context,
|
||||
const gfxIntSize& size,
|
||||
bool aForPrinting)
|
||||
: mCGContext(context), mSize(size), mForPrinting(aForPrinting)
|
||||
{
|
||||
if (!CheckSurfaceSize(size))
|
||||
MakeInvalid();
|
||||
|
||||
unsigned int width = static_cast<unsigned int>(mSize.width);
|
||||
unsigned int height = static_cast<unsigned int>(mSize.height);
|
||||
|
||||
cairo_surface_t *surf =
|
||||
cairo_quartz_surface_create_for_cg_context(context,
|
||||
width, height);
|
||||
|
@ -50,7 +50,6 @@ class THEBES_API gfxQuartzSurface : public gfxASurface {
|
||||
public:
|
||||
gfxQuartzSurface(const gfxSize& size, gfxImageFormat format, bool aForPrinting = false);
|
||||
gfxQuartzSurface(CGContextRef context, const gfxSize& size, bool aForPrinting = false);
|
||||
gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false);
|
||||
gfxQuartzSurface(cairo_surface_t *csurf, bool aForPrinting = false);
|
||||
gfxQuartzSurface(unsigned char *data, const gfxSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
|
||||
|
||||
|
@ -507,35 +507,21 @@ gfxWindowsPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
if (aTarget->GetType() == BACKEND_DIRECT2D) {
|
||||
void *surface = aTarget->GetUserData(&ThebesSurfaceKey);
|
||||
if (surface) {
|
||||
nsRefPtr<gfxASurface> surf = static_cast<gfxASurface*>(surface);
|
||||
return surf.forget();
|
||||
} else {
|
||||
RefPtr<ID3D10Texture2D> texture =
|
||||
static_cast<ID3D10Texture2D*>(aTarget->GetNativeSurface(NATIVE_SURFACE_D3D10_TEXTURE));
|
||||
RefPtr<ID3D10Texture2D> texture =
|
||||
static_cast<ID3D10Texture2D*>(aTarget->GetNativeSurface(NATIVE_SURFACE_D3D10_TEXTURE));
|
||||
|
||||
if (!texture) {
|
||||
return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
|
||||
}
|
||||
|
||||
aTarget->Flush();
|
||||
|
||||
nsRefPtr<gfxASurface> surf =
|
||||
new gfxD2DSurface(texture, ContentForFormat(aTarget->GetFormat()));
|
||||
|
||||
// add a reference to be held by the drawTarget
|
||||
surf->AddRef();
|
||||
aTarget->AddUserData(&ThebesSurfaceKey, surf.get(), DestroyThebesSurface);
|
||||
/* "It might be worth it to clear cairo surfaces associated with a drawtarget.
|
||||
The strong reference means for example for D2D that cairo's scratch surface
|
||||
will be kept alive (well after a user being done) and consume extra VRAM.
|
||||
We can deal with this in a follow-up though." */
|
||||
|
||||
// shouldn't this hold a reference?
|
||||
surf->SetData(&kDrawTarget, aTarget, NULL);
|
||||
return surf.forget();
|
||||
if (!texture) {
|
||||
return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
|
||||
}
|
||||
|
||||
aTarget->Flush();
|
||||
|
||||
nsRefPtr<gfxASurface> surf =
|
||||
new gfxD2DSurface(texture, ContentForFormat(aTarget->GetFormat()));
|
||||
|
||||
surf->SetData(&kDrawTarget, aTarget, NULL);
|
||||
|
||||
return surf.forget();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -47,11 +47,8 @@ fails-if(Android) != text-font-lang.html text-font-lang-notref.html
|
||||
|
||||
== strokeText-path.html strokeText-path-ref.html
|
||||
|
||||
# azure quartz uses CGDrawLinearGradient instead of DrawShading
|
||||
# so we have less control over degenerate behaviour as tested by this
|
||||
# test
|
||||
fails-if(azureQuartz) == linear-gradient-1a.html linear-gradient-1-ref.html
|
||||
|
||||
# gradient off-by-one, fails on windows and linux
|
||||
== linear-gradient-1a.html linear-gradient-1-ref.html
|
||||
fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == linear-gradient-1b.html linear-gradient-1-ref.html
|
||||
|
||||
== zero-dimensions.html zero-dimensions-ref.html
|
||||
|
@ -1,9 +1,9 @@
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-1a.html linear-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-1b.html linear-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-keywords-1a.html linear-keywords-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-keywords-1b.html linear-keywords-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-percent.html linear-percent-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-mix.html linear-mix-ref.html
|
||||
fails-if(Android) == linear-1a.html linear-1-ref.html
|
||||
fails-if(Android) == linear-1b.html linear-1-ref.html
|
||||
fails-if(Android) == linear-keywords-1a.html linear-keywords-1-ref.html
|
||||
fails-if(Android) == linear-keywords-1b.html linear-keywords-1-ref.html
|
||||
fails-if(Android) == linear-percent.html linear-percent-ref.html
|
||||
fails-if(Android) == linear-mix.html linear-mix-ref.html
|
||||
== linear-diagonal-1a.html linear-diagonal-1-ref.html
|
||||
== linear-diagonal-1b.html linear-diagonal-1-ref.html
|
||||
== linear-diagonal-1c.html linear-diagonal-1-ref.html
|
||||
@ -36,20 +36,20 @@ fails-if(d2d) == linear-repeat-1g.html linear-repeat-1-ref.html # bug 582236
|
||||
== linear-stops-1d.html linear-stops-1-ref.html
|
||||
== linear-stops-1e.html linear-stops-1-ref.html
|
||||
== linear-stops-1f.html linear-stops-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1a.html linear-vertical-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1b.html linear-vertical-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1c.html linear-vertical-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1d.html linear-vertical-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1e.html linear-vertical-1-ref.html
|
||||
fails-if(Android) == linear-vertical-1a.html linear-vertical-1-ref.html
|
||||
fails-if(Android) == linear-vertical-1b.html linear-vertical-1-ref.html
|
||||
fails-if(Android) == linear-vertical-1c.html linear-vertical-1-ref.html
|
||||
fails-if(Android) == linear-vertical-1d.html linear-vertical-1-ref.html
|
||||
fails-if(Android) == linear-vertical-1e.html linear-vertical-1-ref.html
|
||||
== linear-viewport.html linear-viewport-ref.html
|
||||
== linear-zero-length-1a.html linear-zero-length-1-ref.html
|
||||
== linear-zero-length-1b.html linear-zero-length-1-ref.html
|
||||
== linear-zero-length-1c.html linear-zero-length-1-ref.html
|
||||
== nostops.html about:blank
|
||||
== onestop.html about:blank
|
||||
fuzzy-if(azureQuartz) fails-if(Android) random-if(d2d) == radial-1a.html radial-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == radial-2a.html radial-2-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == radial-2b.html radial-2-ref.html
|
||||
fails-if(Android) random-if(d2d) == radial-1a.html radial-1-ref.html
|
||||
fails-if(Android) == radial-2a.html radial-2-ref.html
|
||||
fails-if(Android) == radial-2b.html radial-2-ref.html
|
||||
== radial-position-1a.html radial-position-1-ref.html
|
||||
== radial-shape-closest-corner-1a.html radial-shape-closest-corner-1-ref.html
|
||||
== radial-shape-closest-side-1a.html radial-shape-closest-side-1-ref.html
|
||||
@ -77,23 +77,23 @@ fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == twostops-1c.html twos
|
||||
== twostops-1g.html twostops-1-ref.html
|
||||
|
||||
# from http://www.xanthir.com/:4bhipd by way of http://a-ja.net/newgrad.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1a.html aja-linear-1-ref.html
|
||||
fails-if(Android) == aja-linear-1a.html aja-linear-1-ref.html
|
||||
fails-if(!d2d) == aja-linear-1b.html aja-linear-1-ref.html # bug 526694
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1c.html aja-linear-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1d.html aja-linear-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1e.html aja-linear-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1f.html aja-linear-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1g.html aja-linear-1-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-2a.html aja-linear-2-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-2b.html aja-linear-2-ref.html
|
||||
fails-if(Android) == aja-linear-1c.html aja-linear-1-ref.html
|
||||
fails-if(Android) == aja-linear-1d.html aja-linear-1-ref.html
|
||||
fails-if(Android) == aja-linear-1e.html aja-linear-1-ref.html
|
||||
fails-if(Android) == aja-linear-1f.html aja-linear-1-ref.html
|
||||
fails-if(Android) == aja-linear-1g.html aja-linear-1-ref.html
|
||||
fails-if(Android) == aja-linear-2a.html aja-linear-2-ref.html
|
||||
fails-if(Android) == aja-linear-2b.html aja-linear-2-ref.html
|
||||
fails == aja-linear-2c.html aja-linear-2-ref.html # bug 522607
|
||||
fails-if(!d2d) == aja-linear-2d.html aja-linear-2-ref.html # bug 526694
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-3a.html aja-linear-3-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-3b.html aja-linear-3-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-4a.html aja-linear-4-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-4b.html aja-linear-4-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-5a.html aja-linear-5-ref.html
|
||||
fuzzy-if(azureQuartz) fails-if(Android) fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
|
||||
fails-if(Android) == aja-linear-3a.html aja-linear-3-ref.html
|
||||
fails-if(Android) == aja-linear-3b.html aja-linear-3-ref.html
|
||||
fails-if(Android) == aja-linear-4a.html aja-linear-4-ref.html
|
||||
fails-if(Android) == aja-linear-4b.html aja-linear-4-ref.html
|
||||
fails-if(Android) == aja-linear-5a.html aja-linear-5-ref.html
|
||||
fails-if(Android) fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
|
||||
fails == aja-linear-6b.html aja-linear-6-ref.html # bug 522607
|
||||
== height-dependence-1.html height-dependence-1-ref.html
|
||||
fails-if(cocoaWidget) == height-dependence-2.html height-dependence-2-ref.html # bug 535007
|
||||
|
@ -1,6 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div style="background: #ff00ff; width: 500px; height: 500px;"></div>
|
||||
</body>
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div style="background: #ff01ff; width: 500px; height: 500px;"></div>
|
||||
</body>
|
||||
</html>
|
@ -129,10 +129,3 @@ pref(font.default.x-western,"sans-serif") == font-sans-serif.html font-default.h
|
||||
pref(font.default.x-western,"sans-serif") != font-serif.html font-default.html
|
||||
fails pref(font.default.x-western,true) == font-serif.html font-default.html
|
||||
fails pref(font.default.x-western,0) == font-serif.html font-default.html
|
||||
# reftest syntax: fuzzy
|
||||
fuzzy == fuzzy.html fuzzy-ref.html
|
||||
fuzzy != too-fuzzy.html fuzzy-ref.html
|
||||
fuzzy-if(true) == fuzzy.html fuzzy-ref.html
|
||||
fuzzy-if(false) == fuzzy-ref.html fuzzy-ref.html
|
||||
# When using 565 fuzzy.html and fuzzy-ref.html will compare as equal
|
||||
fails fuzzy-if(false) random-if(Android) == fuzzy.html fuzzy-ref.html
|
||||
|
@ -1,6 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div style="background: #ff04ff; width: 500px; height: 500px;"></div>
|
||||
</body>
|
||||
</html>
|
@ -39,7 +39,7 @@ fails == canvas-drawImage-scale-2a.html canvas-drawImage-scale-2-ref.html # XXX
|
||||
fails == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html # XXX all edges fuzzy
|
||||
|
||||
== canvas-drawImage-slice-1a.html lime100x100-ref.html
|
||||
fails-if(!azureQuartz) == canvas-drawImage-slice-1b.html lime100x100-ref.html # XXX all edges fuzzy
|
||||
fails == canvas-drawImage-slice-1b.html lime100x100-ref.html # XXX all edges fuzzy
|
||||
|
||||
== canvas-drawImage-origin-clean-1.html lime100x100-ref.html
|
||||
|
||||
|
@ -106,14 +106,6 @@ must be one of the following:
|
||||
fast on a 32-bit system but inordinately slow on a
|
||||
64-bit system).
|
||||
|
||||
fuzzy This allows a test to pass if the pixel value differences
|
||||
are <= 2. It can also be used with '!=' to ensure that the
|
||||
difference is greater than 2.
|
||||
|
||||
fuzzy-if(condition) If the condition is met, the test is treated as if
|
||||
'fuzzy' had been specified. This is useful if there
|
||||
are differences on particular platforms.
|
||||
|
||||
require-or(cond1&&cond2&&...,fallback)
|
||||
Require some particular setup be performed or environmental
|
||||
condition(s) made true (eg setting debug mode) before the test
|
||||
|
@ -39,7 +39,7 @@
|
||||
import sys, os.path, re
|
||||
|
||||
commentRE = re.compile(r"\s+#")
|
||||
conditionsRE = re.compile(r"^(fails|needs-focus|random|skip|asserts|fuzzy)")
|
||||
conditionsRE = re.compile(r"^(fails|needs-focus|random|skip|asserts)")
|
||||
httpRE = re.compile(r"HTTP\((\.\.(\/\.\.)*)\)")
|
||||
protocolRE = re.compile(r"^\w+:")
|
||||
|
||||
|
@ -149,7 +149,6 @@ const EXPECTED_PASS = 0;
|
||||
const EXPECTED_FAIL = 1;
|
||||
const EXPECTED_RANDOM = 2;
|
||||
const EXPECTED_DEATH = 3; // test must be skipped to avoid e.g. crash/hang
|
||||
const EXPECTED_FUZZY = 4;
|
||||
|
||||
// types of preference value we might want to set for a specific test
|
||||
const PREF_BOOLEAN = 0;
|
||||
@ -346,9 +345,13 @@ function InitAndStartRefTests()
|
||||
gThisChunk = 0;
|
||||
}
|
||||
|
||||
gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
|
||||
if (!gWindowUtils || !gWindowUtils.compareCanvases)
|
||||
throw "nsIDOMWindowUtils inteface missing";
|
||||
try {
|
||||
gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
|
||||
if (gWindowUtils && !gWindowUtils.compareCanvases)
|
||||
gWindowUtils = null;
|
||||
} catch (e) {
|
||||
gWindowUtils = null;
|
||||
}
|
||||
|
||||
gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService);
|
||||
gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2);
|
||||
@ -509,20 +512,18 @@ function BuildConditionSandbox(aURL) {
|
||||
} catch(e) {
|
||||
sandbox.xulRuntime.XPCOMABI = "";
|
||||
}
|
||||
|
||||
|
||||
var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
|
||||
|
||||
try {
|
||||
sandbox.d2d = gfxInfo.D2DEnabled;
|
||||
} catch (e) {
|
||||
sandbox.d2d = false;
|
||||
// nsIGfxInfo is currently only implemented on Windows
|
||||
sandbox.d2d = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo).D2DEnabled;
|
||||
} catch(e) {
|
||||
sandbox.d2d = false;
|
||||
}
|
||||
sandbox.azureQuartz = gfxInfo.getInfo().AzureBackend == "quartz";
|
||||
|
||||
sandbox.layersGPUAccelerated =
|
||||
gWindowUtils.layerManagerType != "Basic";
|
||||
gWindowUtils && gWindowUtils.layerManagerType != "Basic";
|
||||
sandbox.layersOpenGL =
|
||||
gWindowUtils.layerManagerType == "OpenGL";
|
||||
gWindowUtils && gWindowUtils.layerManagerType == "OpenGL";
|
||||
|
||||
// Shortcuts for widget toolkits.
|
||||
sandbox.Android = xr.OS == "Android";
|
||||
@ -689,16 +690,16 @@ function ReadManifest(aURL, inherited_status)
|
||||
var slow = false;
|
||||
var prefSettings = [];
|
||||
|
||||
while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|fuzzy)/)) {
|
||||
while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref)/)) {
|
||||
var item = items.shift();
|
||||
var stat;
|
||||
var cond;
|
||||
var m = item.match(/^(fails|random|skip|silentfail|fuzzy)-if(\(.*\))$/);
|
||||
var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
|
||||
if (m) {
|
||||
stat = m[1];
|
||||
// Note: m[2] contains the parentheses, and we want them.
|
||||
cond = Components.utils.evalInSandbox(m[2], sandbox);
|
||||
} else if (item.match(/^(fails|random|skip|fuzzy)$/)) {
|
||||
} else if (item.match(/^(fails|random|skip)$/)) {
|
||||
stat = item;
|
||||
cond = true;
|
||||
} else if (item == "needs-focus") {
|
||||
@ -780,8 +781,6 @@ function ReadManifest(aURL, inherited_status)
|
||||
expected_status = EXPECTED_RANDOM;
|
||||
} else if (stat == "skip") {
|
||||
expected_status = EXPECTED_DEATH;
|
||||
} else if (stat == "fuzzy") {
|
||||
expected_status = EXPECTED_FUZZY;
|
||||
} else if (stat == "silentfail") {
|
||||
allow_silent_fail = true;
|
||||
}
|
||||
@ -1301,8 +1300,6 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
|
||||
true: {s: "TEST-PASS" + randomMsg , n: "Random"},
|
||||
false: {s: "TEST-KNOWN-FAIL" + randomMsg, n: "Random"}
|
||||
};
|
||||
outputs[EXPECTED_FUZZY] = outputs[EXPECTED_PASS];
|
||||
|
||||
var output;
|
||||
|
||||
if (gURLs[0].type == TYPE_LOAD) {
|
||||
@ -1405,25 +1402,21 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
|
||||
var differences;
|
||||
// whether the two renderings match:
|
||||
var equal;
|
||||
var maxDifference = {};
|
||||
|
||||
differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, maxDifference);
|
||||
equal = (differences == 0);
|
||||
|
||||
// what is expected on this platform (PASS, FAIL, or RANDOM)
|
||||
var expected = gURLs[0].expected;
|
||||
|
||||
if (maxDifference.value > 0 && maxDifference.value <= 2) {
|
||||
if (equal) {
|
||||
throw "Inconsistent result from compareCanvases.";
|
||||
}
|
||||
equal = expected == EXPECTED_FUZZY;
|
||||
gDumpLog("REFTEST fuzzy match\n");
|
||||
if (gWindowUtils) {
|
||||
differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, {});
|
||||
equal = (differences == 0);
|
||||
} else {
|
||||
differences = -1;
|
||||
var k1 = gCanvas1.toDataURL();
|
||||
var k2 = gCanvas2.toDataURL();
|
||||
equal = (k1 == k2);
|
||||
}
|
||||
|
||||
// whether the comparison result matches what is in the manifest
|
||||
var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL));
|
||||
|
||||
// what is expected on this platform (PASS, FAIL, or RANDOM)
|
||||
var expected = gURLs[0].expected;
|
||||
output = outputs[expected][test_passed];
|
||||
|
||||
++gTestResults[output.n];
|
||||
@ -1441,12 +1434,11 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
|
||||
gDumpLog(result + "\n");
|
||||
|
||||
if (!test_passed && expected == EXPECTED_PASS ||
|
||||
!test_passed && expected == EXPECTED_FUZZY ||
|
||||
test_passed && expected == EXPECTED_FAIL) {
|
||||
if (!equal) {
|
||||
gDumpLog("REFTEST IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n");
|
||||
gDumpLog("REFTEST IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n");
|
||||
gDumpLog("REFTEST number of differing pixels: " + differences + " max difference: " + maxDifference.value + "\n");
|
||||
gDumpLog("REFTEST number of differing pixels: " + differences + "\n");
|
||||
} else {
|
||||
gDumpLog("REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n");
|
||||
}
|
||||
|
@ -230,10 +230,6 @@ pref("gfx.font_rendering.directwrite.use_gdi_table_loading", true);
|
||||
|
||||
#ifdef XP_WIN
|
||||
pref("gfx.canvas.azure.enabled", true);
|
||||
#else
|
||||
#ifdef XP_MACOSX
|
||||
pref("gfx.canvas.azure.enabled", true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
pref("accessibility.browsewithcaret", false);
|
||||
|
Loading…
Reference in New Issue
Block a user