gfx/2d/DrawTargetCG.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/2d/DrawTargetCG.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1609 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +#include "BorrowedContext.h"
     1.9 +#include "DataSurfaceHelpers.h"
    1.10 +#include "DrawTargetCG.h"
    1.11 +#include "Logging.h"
    1.12 +#include "SourceSurfaceCG.h"
    1.13 +#include "Rect.h"
    1.14 +#include "ScaledFontMac.h"
    1.15 +#include "Tools.h"
    1.16 +#include <vector>
    1.17 +#include <algorithm>
    1.18 +#include "MacIOSurface.h"
    1.19 +#include "FilterNodeSoftware.h"
    1.20 +#include "mozilla/Assertions.h"
    1.21 +#include "mozilla/Types.h" // for decltype
    1.22 +#include "mozilla/FloatingPoint.h"
    1.23 +
    1.24 +using namespace std;
    1.25 +
    1.26 +//CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
    1.27 +
    1.28 +// A private API that Cairo has been using for a long time
    1.29 +CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
    1.30 +
    1.31 +namespace mozilla {
    1.32 +namespace gfx {
    1.33 +
    1.34 +static CGRect RectToCGRect(Rect r)
    1.35 +{
    1.36 +  return CGRectMake(r.x, r.y, r.width, r.height);
    1.37 +}
    1.38 +
    1.39 +CGBlendMode ToBlendMode(CompositionOp op)
    1.40 +{
    1.41 +  CGBlendMode mode;
    1.42 +  switch (op) {
    1.43 +    case CompositionOp::OP_OVER:
    1.44 +      mode = kCGBlendModeNormal;
    1.45 +      break;
    1.46 +    case CompositionOp::OP_ADD:
    1.47 +      mode = kCGBlendModePlusLighter;
    1.48 +      break;
    1.49 +    case CompositionOp::OP_ATOP:
    1.50 +      mode = kCGBlendModeSourceAtop;
    1.51 +      break;
    1.52 +    case CompositionOp::OP_OUT:
    1.53 +      mode = kCGBlendModeSourceOut;
    1.54 +      break;
    1.55 +    case CompositionOp::OP_IN:
    1.56 +      mode = kCGBlendModeSourceIn;
    1.57 +      break;
    1.58 +    case CompositionOp::OP_SOURCE:
    1.59 +      mode = kCGBlendModeCopy;
    1.60 +      break;
    1.61 +    case CompositionOp::OP_DEST_IN:
    1.62 +      mode = kCGBlendModeDestinationIn;
    1.63 +      break;
    1.64 +    case CompositionOp::OP_DEST_OUT:
    1.65 +      mode = kCGBlendModeDestinationOut;
    1.66 +      break;
    1.67 +    case CompositionOp::OP_DEST_OVER:
    1.68 +      mode = kCGBlendModeDestinationOver;
    1.69 +      break;
    1.70 +    case CompositionOp::OP_DEST_ATOP:
    1.71 +      mode = kCGBlendModeDestinationAtop;
    1.72 +      break;
    1.73 +    case CompositionOp::OP_XOR:
    1.74 +      mode = kCGBlendModeXOR;
    1.75 +      break;
    1.76 +    case CompositionOp::OP_MULTIPLY:
    1.77 +      mode = kCGBlendModeMultiply;
    1.78 +      break;
    1.79 +    case CompositionOp::OP_SCREEN:
    1.80 +      mode = kCGBlendModeScreen;
    1.81 +      break;
    1.82 +    case CompositionOp::OP_OVERLAY:
    1.83 +      mode = kCGBlendModeOverlay;
    1.84 +      break;
    1.85 +    case CompositionOp::OP_DARKEN:
    1.86 +      mode = kCGBlendModeDarken;
    1.87 +      break;
    1.88 +    case CompositionOp::OP_LIGHTEN:
    1.89 +      mode = kCGBlendModeLighten;
    1.90 +      break;
    1.91 +    case CompositionOp::OP_COLOR_DODGE:
    1.92 +      mode = kCGBlendModeColorDodge;
    1.93 +      break;
    1.94 +    case CompositionOp::OP_COLOR_BURN:
    1.95 +      mode = kCGBlendModeColorBurn;
    1.96 +      break;
    1.97 +    case CompositionOp::OP_HARD_LIGHT:
    1.98 +      mode = kCGBlendModeHardLight;
    1.99 +      break;
   1.100 +    case CompositionOp::OP_SOFT_LIGHT:
   1.101 +      mode = kCGBlendModeSoftLight;
   1.102 +      break;
   1.103 +    case CompositionOp::OP_DIFFERENCE:
   1.104 +      mode = kCGBlendModeDifference;
   1.105 +      break;
   1.106 +    case CompositionOp::OP_EXCLUSION:
   1.107 +      mode = kCGBlendModeExclusion;
   1.108 +      break;
   1.109 +    case CompositionOp::OP_HUE:
   1.110 +      mode = kCGBlendModeHue;
   1.111 +      break;
   1.112 +    case CompositionOp::OP_SATURATION:
   1.113 +      mode = kCGBlendModeSaturation;
   1.114 +      break;
   1.115 +    case CompositionOp::OP_COLOR:
   1.116 +      mode = kCGBlendModeColor;
   1.117 +      break;
   1.118 +    case CompositionOp::OP_LUMINOSITY:
   1.119 +      mode = kCGBlendModeLuminosity;
   1.120 +      break;
   1.121 +      /*
   1.122 +    case OP_CLEAR:
   1.123 +      mode = kCGBlendModeClear;
   1.124 +      break;*/
   1.125 +    default:
   1.126 +      mode = kCGBlendModeNormal;
   1.127 +  }
   1.128 +  return mode;
   1.129 +}
   1.130 +
   1.131 +static CGInterpolationQuality
   1.132 +InterpolationQualityFromFilter(Filter aFilter)
   1.133 +{
   1.134 +  switch (aFilter) {
   1.135 +    default:
   1.136 +    case Filter::LINEAR:
   1.137 +      return kCGInterpolationLow;
   1.138 +    case Filter::POINT:
   1.139 +      return kCGInterpolationNone;
   1.140 +    case Filter::GOOD:
   1.141 +      return kCGInterpolationDefault;
   1.142 +  }
   1.143 +}
   1.144 +
   1.145 +
   1.146 +DrawTargetCG::DrawTargetCG() : mCg(nullptr), mSnapshot(nullptr)
   1.147 +{
   1.148 +}
   1.149 +
   1.150 +DrawTargetCG::~DrawTargetCG()
   1.151 +{
   1.152 +  MarkChanged();
   1.153 +
   1.154 +  // We need to conditionally release these because Init can fail without initializing these.
   1.155 +  if (mColorSpace)
   1.156 +    CGColorSpaceRelease(mColorSpace);
   1.157 +  if (mCg)
   1.158 +    CGContextRelease(mCg);
   1.159 +}
   1.160 +
   1.161 +BackendType
   1.162 +DrawTargetCG::GetType() const
   1.163 +{
   1.164 +  // It may be worth spliting Bitmap and IOSurface DrawTarget
   1.165 +  // into seperate classes.
   1.166 +  if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
   1.167 +    return BackendType::COREGRAPHICS_ACCELERATED;
   1.168 +  } else {
   1.169 +    return BackendType::COREGRAPHICS;
   1.170 +  }
   1.171 +}
   1.172 +
   1.173 +TemporaryRef<SourceSurface>
   1.174 +DrawTargetCG::Snapshot()
   1.175 +{
   1.176 +  if (!mSnapshot) {
   1.177 +    if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
   1.178 +      return new SourceSurfaceCGIOSurfaceContext(this);
   1.179 +    } else {
   1.180 +      mSnapshot = new SourceSurfaceCGBitmapContext(this);
   1.181 +    }
   1.182 +  }
   1.183 +
   1.184 +  return mSnapshot;
   1.185 +}
   1.186 +
   1.187 +TemporaryRef<DrawTarget>
   1.188 +DrawTargetCG::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
   1.189 +{
   1.190 +  // XXX: in thebes we use CGLayers to do this kind of thing. It probably makes sense
   1.191 +  // to add that in somehow, but at a higher level
   1.192 +  RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
   1.193 +  if (newTarget->Init(GetType(), aSize, aFormat)) {
   1.194 +    return newTarget;
   1.195 +  } else {
   1.196 +    return nullptr;
   1.197 +  }
   1.198 +}
   1.199 +
   1.200 +TemporaryRef<SourceSurface>
   1.201 +DrawTargetCG::CreateSourceSurfaceFromData(unsigned char *aData,
   1.202 +                                           const IntSize &aSize,
   1.203 +                                           int32_t aStride,
   1.204 +                                           SurfaceFormat aFormat) const
   1.205 +{
   1.206 +  RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG();
   1.207 +
   1.208 + if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
   1.209 +    return nullptr;
   1.210 +  }
   1.211 +
   1.212 +  return newSurf;
   1.213 +}
   1.214 +
   1.215 +// This function returns a retained CGImage that needs to be released after
   1.216 +// use. The reason for this is that we want to either reuse an existing CGImage
   1.217 +// or create a new one.
   1.218 +static CGImageRef
   1.219 +GetRetainedImageFromSourceSurface(SourceSurface *aSurface)
   1.220 +{
   1.221 +  if (aSurface->GetType() == SurfaceType::COREGRAPHICS_IMAGE)
   1.222 +    return CGImageRetain(static_cast<SourceSurfaceCG*>(aSurface)->GetImage());
   1.223 +  else if (aSurface->GetType() == SurfaceType::COREGRAPHICS_CGCONTEXT)
   1.224 +    return CGImageRetain(static_cast<SourceSurfaceCGContext*>(aSurface)->GetImage());
   1.225 +
   1.226 +  if (aSurface->GetType() == SurfaceType::DATA) {
   1.227 +    DataSourceSurface* dataSource = static_cast<DataSourceSurface*>(aSurface);
   1.228 +    return CreateCGImage(nullptr, dataSource->GetData(), dataSource->GetSize(),
   1.229 +                         dataSource->Stride(), dataSource->GetFormat());
   1.230 +  }
   1.231 +
   1.232 +  MOZ_CRASH("unsupported source surface");
   1.233 +}
   1.234 +
   1.235 +TemporaryRef<SourceSurface>
   1.236 +DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const
   1.237 +{
   1.238 +  if (aSurface->GetType() == SurfaceType::COREGRAPHICS_IMAGE ||
   1.239 +      aSurface->GetType() == SurfaceType::COREGRAPHICS_CGCONTEXT) {
   1.240 +    return aSurface;
   1.241 +  }
   1.242 +  return aSurface->GetDataSurface();
   1.243 +}
   1.244 +
   1.245 +class UnboundnessFixer
   1.246 +{
   1.247 +    CGRect mClipBounds;
   1.248 +    CGLayerRef mLayer;
   1.249 +    CGContextRef mCg;
   1.250 +  public:
   1.251 +    UnboundnessFixer() : mCg(nullptr) {}
   1.252 +
   1.253 +    CGContextRef Check(CGContextRef baseCg, CompositionOp blend, const Rect* maskBounds = nullptr)
   1.254 +    {
   1.255 +      if (!IsOperatorBoundByMask(blend)) {
   1.256 +        mClipBounds = CGContextGetClipBoundingBox(baseCg);
   1.257 +        // If we're entirely clipped out or if the drawing operation covers the entire clip then
   1.258 +        // we don't need to create a temporary surface.
   1.259 +        if (CGRectIsEmpty(mClipBounds) ||
   1.260 +            (maskBounds && maskBounds->Contains(CGRectToRect(mClipBounds)))) {
   1.261 +          return baseCg;
   1.262 +        }
   1.263 +
   1.264 +        // TransparencyLayers aren't blended using the blend mode so
   1.265 +        // we are forced to use CGLayers
   1.266 +
   1.267 +        //XXX: The size here is in default user space units, of the layer relative to the graphics context.
   1.268 +        // is the clip bounds still correct if, for example, we have a scale applied to the context?
   1.269 +        mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, nullptr);
   1.270 +        mCg = CGLayerGetContext(mLayer);
   1.271 +        // CGContext's default to have the origin at the bottom left
   1.272 +        // so flip it to the top left and adjust for the origin
   1.273 +        // of the layer
   1.274 +        CGContextTranslateCTM(mCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height);
   1.275 +        CGContextScaleCTM(mCg, 1, -1);
   1.276 +
   1.277 +        return mCg;
   1.278 +      } else {
   1.279 +        return baseCg;
   1.280 +      }
   1.281 +    }
   1.282 +
   1.283 +    void Fix(CGContextRef baseCg)
   1.284 +    {
   1.285 +        if (mCg) {
   1.286 +            CGContextTranslateCTM(baseCg, 0, mClipBounds.size.height);
   1.287 +            CGContextScaleCTM(baseCg, 1, -1);
   1.288 +            mClipBounds.origin.y *= -1;
   1.289 +            CGContextDrawLayerAtPoint(baseCg, mClipBounds.origin, mLayer);
   1.290 +            CGContextRelease(mCg);
   1.291 +        }
   1.292 +    }
   1.293 +};
   1.294 +
   1.295 +void
   1.296 +DrawTargetCG::DrawSurface(SourceSurface *aSurface,
   1.297 +                           const Rect &aDest,
   1.298 +                           const Rect &aSource,
   1.299 +                           const DrawSurfaceOptions &aSurfOptions,
   1.300 +                           const DrawOptions &aDrawOptions)
   1.301 +{
   1.302 +  MarkChanged();
   1.303 +
   1.304 +  CGContextSaveGState(mCg);
   1.305 +
   1.306 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   1.307 +  UnboundnessFixer fixer;
   1.308 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aDest);
   1.309 +  CGContextSetAlpha(cg, aDrawOptions.mAlpha);
   1.310 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   1.311 +
   1.312 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
   1.313 +
   1.314 +  CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(aSurfOptions.mFilter));
   1.315 +
   1.316 +  CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
   1.317 +
   1.318 +  if (aSurfOptions.mFilter == Filter::POINT) {
   1.319 +    CGImageRef subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
   1.320 +    CGImageRelease(image);
   1.321 +
   1.322 +    CGContextScaleCTM(cg, 1, -1);
   1.323 +
   1.324 +    CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height),
   1.325 +                                    aDest.width, aDest.height);
   1.326 +
   1.327 +    CGContextDrawImage(cg, flippedRect, subimage);
   1.328 +    CGImageRelease(subimage);
   1.329 +  } else {
   1.330 +    CGRect destRect = CGRectMake(aDest.x, aDest.y, aDest.width, aDest.height);
   1.331 +    CGContextClipToRect(cg, destRect);
   1.332 +
   1.333 +    float xScale = aSource.width / aDest.width;
   1.334 +    float yScale = aSource.height / aDest.height;
   1.335 +    CGContextTranslateCTM(cg, aDest.x - aSource.x / xScale, aDest.y - aSource.y / yScale);
   1.336 +
   1.337 +    CGRect adjustedDestRect = CGRectMake(0, 0, CGImageGetWidth(image) / xScale,
   1.338 +                                         CGImageGetHeight(image) / yScale);
   1.339 +
   1.340 +    CGContextTranslateCTM(cg, 0, CGRectGetHeight(adjustedDestRect));
   1.341 +    CGContextScaleCTM(cg, 1, -1);
   1.342 +
   1.343 +    CGContextDrawImage(cg, adjustedDestRect, image);
   1.344 +    CGImageRelease(image);
   1.345 +  }
   1.346 +
   1.347 +  fixer.Fix(mCg);
   1.348 +
   1.349 +  CGContextRestoreGState(mCg);
   1.350 +}
   1.351 +
   1.352 +TemporaryRef<FilterNode>
   1.353 +DrawTargetCG::CreateFilter(FilterType aType)
   1.354 +{
   1.355 +  return FilterNodeSoftware::Create(aType);
   1.356 +}
   1.357 +
   1.358 +void
   1.359 +DrawTargetCG::DrawFilter(FilterNode *aNode,
   1.360 +                         const Rect &aSourceRect,
   1.361 +                         const Point &aDestPoint,
   1.362 +                         const DrawOptions &aOptions)
   1.363 +{
   1.364 +  FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
   1.365 +  filter->Draw(this, aSourceRect, aDestPoint, aOptions);
   1.366 +}
   1.367 +
   1.368 +static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
   1.369 +{
   1.370 +  CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
   1.371 +  return CGColorCreate(aColorSpace, components);
   1.372 +}
   1.373 +
   1.374 +class GradientStopsCG : public GradientStops
   1.375 +{
   1.376 +  public:
   1.377 +  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCG)
   1.378 +  //XXX: The skia backend uses a vector and passes in aNumStops. It should do better
   1.379 +  GradientStopsCG(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode)
   1.380 +  {
   1.381 +    mExtend = aExtendMode;
   1.382 +    if (aExtendMode == ExtendMode::CLAMP) {
   1.383 +      //XXX: do the stops need to be in any particular order?
   1.384 +      // what should we do about the color space here? we certainly shouldn't be
   1.385 +      // recreating it all the time
   1.386 +      std::vector<CGFloat> colors;
   1.387 +      std::vector<CGFloat> offsets;
   1.388 +      colors.reserve(aNumStops*4);
   1.389 +      offsets.reserve(aNumStops);
   1.390 +
   1.391 +      for (uint32_t i = 0; i < aNumStops; i++) {
   1.392 +        colors.push_back(aStops[i].color.r);
   1.393 +        colors.push_back(aStops[i].color.g);
   1.394 +        colors.push_back(aStops[i].color.b);
   1.395 +        colors.push_back(aStops[i].color.a);
   1.396 +
   1.397 +        offsets.push_back(aStops[i].offset);
   1.398 +      }
   1.399 +
   1.400 +      CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
   1.401 +      mGradient = CGGradientCreateWithColorComponents(colorSpace,
   1.402 +                                                      &colors.front(),
   1.403 +                                                      &offsets.front(),
   1.404 +                                                      aNumStops);
   1.405 +      CGColorSpaceRelease(colorSpace);
   1.406 +    } else {
   1.407 +      mGradient = nullptr;
   1.408 +      mStops.reserve(aNumStops);
   1.409 +      for (uint32_t i = 0; i < aNumStops; i++) {
   1.410 +        mStops.push_back(aStops[i]);
   1.411 +      }
   1.412 +    }
   1.413 +
   1.414 +  }
   1.415 +  virtual ~GradientStopsCG() {
   1.416 +    if (mGradient)
   1.417 +        CGGradientRelease(mGradient);
   1.418 +  }
   1.419 +  // Will always report BackendType::COREGRAPHICS, but it is compatible
   1.420 +  // with BackendType::COREGRAPHICS_ACCELERATED
   1.421 +  BackendType GetBackendType() const { return BackendType::COREGRAPHICS; }
   1.422 +  // XXX this should be a union
   1.423 +  CGGradientRef mGradient;
   1.424 +  std::vector<GradientStop> mStops;
   1.425 +  ExtendMode mExtend;
   1.426 +};
   1.427 +
   1.428 +TemporaryRef<GradientStops>
   1.429 +DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
   1.430 +                                  ExtendMode aExtendMode) const
   1.431 +{
   1.432 +  return new GradientStopsCG(aStops, aNumStops, aExtendMode);
   1.433 +}
   1.434 +
   1.435 +static void
   1.436 +UpdateLinearParametersToIncludePoint(double *min_t, double *max_t,
   1.437 +                                     CGPoint *start,
   1.438 +                                     double dx, double dy,
   1.439 +                                     double x, double y)
   1.440 +{
   1.441 +  MOZ_ASSERT(IsFinite(x) && IsFinite(y));
   1.442 +
   1.443 +  /**
   1.444 +   * Compute a parameter t such that a line perpendicular to the (dx,dy)
   1.445 +   * vector, passing through (start->x + dx*t, start->y + dy*t), also
   1.446 +   * passes through (x,y).
   1.447 +   *
   1.448 +   * Let px = x - start->x, py = y - start->y.
   1.449 +   * t is given by
   1.450 +   *   (px - dx*t)*dx + (py - dy*t)*dy = 0
   1.451 +   *
   1.452 +   * Solving for t we get
   1.453 +   *   numerator = dx*px + dy*py
   1.454 +   *   denominator = dx^2 + dy^2
   1.455 +   *   t = numerator/denominator
   1.456 +   *
   1.457 +   * In CalculateRepeatingGradientParams we know the length of (dx,dy)
   1.458 +   * is not zero. (This is checked in DrawLinearRepeatingGradient.)
   1.459 +   */
   1.460 +  double px = x - start->x;
   1.461 +  double py = y - start->y;
   1.462 +  double numerator = dx * px + dy * py;
   1.463 +  double denominator = dx * dx + dy * dy;
   1.464 +  double t = numerator / denominator;
   1.465 +
   1.466 +  if (*min_t > t) {
   1.467 +    *min_t = t;
   1.468 +  }
   1.469 +  if (*max_t < t) {
   1.470 +    *max_t = t;
   1.471 +  }
   1.472 +}
   1.473 +
   1.474 +/**
   1.475 + * Repeat the gradient line such that lines extended perpendicular to the
   1.476 + * gradient line at both start and end would completely enclose the drawing
   1.477 + * extents.
   1.478 + */
   1.479 +static void
   1.480 +CalculateRepeatingGradientParams(CGPoint *aStart, CGPoint *aEnd,
   1.481 +                                 CGRect aExtents, int *aRepeatCount)
   1.482 +{
   1.483 +  double t_min = INFINITY;
   1.484 +  double t_max = -INFINITY;
   1.485 +  double dx = aEnd->x - aStart->x;
   1.486 +  double dy = aEnd->y - aStart->y;
   1.487 +
   1.488 +  double bounds_x1 = aExtents.origin.x;
   1.489 +  double bounds_y1 = aExtents.origin.y;
   1.490 +  double bounds_x2 = aExtents.origin.x + aExtents.size.width;
   1.491 +  double bounds_y2 = aExtents.origin.y + aExtents.size.height;
   1.492 +
   1.493 +  UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
   1.494 +                                       bounds_x1, bounds_y1);
   1.495 +  UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
   1.496 +                                       bounds_x2, bounds_y1);
   1.497 +  UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
   1.498 +                                       bounds_x2, bounds_y2);
   1.499 +  UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
   1.500 +                                       bounds_x1, bounds_y2);
   1.501 +
   1.502 +  MOZ_ASSERT(!isinf(t_min) && !isinf(t_max),
   1.503 +             "The first call to UpdateLinearParametersToIncludePoint should have made t_min and t_max non-infinite.");
   1.504 +
   1.505 +  // Move t_min and t_max to the nearest usable integer to try to avoid
   1.506 +  // subtle variations due to numerical instability, especially accidentally
   1.507 +  // cutting off a pixel. Extending the gradient repetitions is always safe.
   1.508 +  t_min = floor (t_min);
   1.509 +  t_max = ceil (t_max);
   1.510 +  aEnd->x = aStart->x + dx * t_max;
   1.511 +  aEnd->y = aStart->y + dy * t_max;
   1.512 +  aStart->x = aStart->x + dx * t_min;
   1.513 +  aStart->y = aStart->y + dy * t_min;
   1.514 +
   1.515 +  *aRepeatCount = t_max - t_min;
   1.516 +}
   1.517 +
   1.518 +static void
   1.519 +DrawLinearRepeatingGradient(CGContextRef cg, const LinearGradientPattern &aPattern, const CGRect &aExtents)
   1.520 +{
   1.521 +  GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get());
   1.522 +  CGPoint startPoint = { aPattern.mBegin.x, aPattern.mBegin.y };
   1.523 +  CGPoint endPoint = { aPattern.mEnd.x, aPattern.mEnd.y };
   1.524 +
   1.525 +  int repeatCount = 1;
   1.526 +  // if we don't have a line then we can't extend it
   1.527 +  if (aPattern.mEnd.x != aPattern.mBegin.x ||
   1.528 +      aPattern.mEnd.y != aPattern.mBegin.y) {
   1.529 +    CalculateRepeatingGradientParams(&startPoint, &endPoint, aExtents,
   1.530 +                                     &repeatCount);
   1.531 +  }
   1.532 +
   1.533 +  double scale = 1./repeatCount;
   1.534 +
   1.535 +  std::vector<CGFloat> colors;
   1.536 +  std::vector<CGFloat> offsets;
   1.537 +  colors.reserve(stops->mStops.size()*repeatCount*4);
   1.538 +  offsets.reserve(stops->mStops.size()*repeatCount);
   1.539 +
   1.540 +  for (int j = 0; j < repeatCount; j++) {
   1.541 +    for (uint32_t i = 0; i < stops->mStops.size(); i++) {
   1.542 +      colors.push_back(stops->mStops[i].color.r);
   1.543 +      colors.push_back(stops->mStops[i].color.g);
   1.544 +      colors.push_back(stops->mStops[i].color.b);
   1.545 +      colors.push_back(stops->mStops[i].color.a);
   1.546 +
   1.547 +      offsets.push_back((stops->mStops[i].offset + j)*scale);
   1.548 +    }
   1.549 +  }
   1.550 +
   1.551 +  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
   1.552 +  CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace,
   1.553 +                                                               &colors.front(),
   1.554 +                                                               &offsets.front(),
   1.555 +                                                               repeatCount*stops->mStops.size());
   1.556 +  CGColorSpaceRelease(colorSpace);
   1.557 +
   1.558 +  CGContextDrawLinearGradient(cg, gradient, startPoint, endPoint,
   1.559 +                              kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
   1.560 +  CGGradientRelease(gradient);
   1.561 +}
   1.562 +
   1.563 +static CGPoint CGRectTopLeft(CGRect a)
   1.564 +{ return a.origin; }
   1.565 +static CGPoint CGRectBottomLeft(CGRect a)
   1.566 +{ return CGPointMake(a.origin.x, a.origin.y + a.size.height); }
   1.567 +static CGPoint CGRectTopRight(CGRect a)
   1.568 +{ return CGPointMake(a.origin.x + a.size.width, a.origin.y); }
   1.569 +static CGPoint CGRectBottomRight(CGRect a)
   1.570 +{ return CGPointMake(a.origin.x + a.size.width, a.origin.y + a.size.height); }
   1.571 +
   1.572 +static CGFloat
   1.573 +CGPointDistance(CGPoint a, CGPoint b)
   1.574 +{
   1.575 +  return hypot(a.x-b.x, a.y-b.y);
   1.576 +}
   1.577 +
   1.578 +static void
   1.579 +DrawRadialRepeatingGradient(CGContextRef cg, const RadialGradientPattern &aPattern, const CGRect &aExtents)
   1.580 +{
   1.581 +  GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get());
   1.582 +  CGPoint startCenter = { aPattern.mCenter1.x, aPattern.mCenter1.y };
   1.583 +  CGFloat startRadius = aPattern.mRadius1;
   1.584 +  CGPoint endCenter   = { aPattern.mCenter2.x, aPattern.mCenter2.y };
   1.585 +  CGFloat endRadius   = aPattern.mRadius2;
   1.586 +
   1.587 +  // find the maximum distance from endCenter to a corner of aExtents
   1.588 +  CGFloat minimumEndRadius = endRadius;
   1.589 +  minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectTopLeft(aExtents)));
   1.590 +  minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectBottomLeft(aExtents)));
   1.591 +  minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectTopRight(aExtents)));
   1.592 +  minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectBottomRight(aExtents)));
   1.593 +
   1.594 +  CGFloat length = endRadius - startRadius;
   1.595 +  int repeatCount = 1;
   1.596 +  while (endRadius < minimumEndRadius) {
   1.597 +    endRadius += length;
   1.598 +    repeatCount++;
   1.599 +  }
   1.600 +
   1.601 +  while (startRadius-length >= 0) {
   1.602 +    startRadius -= length;
   1.603 +    repeatCount++;
   1.604 +  }
   1.605 +
   1.606 +  double scale = 1./repeatCount;
   1.607 +
   1.608 +  std::vector<CGFloat> colors;
   1.609 +  std::vector<CGFloat> offsets;
   1.610 +  colors.reserve(stops->mStops.size()*repeatCount*4);
   1.611 +  offsets.reserve(stops->mStops.size()*repeatCount);
   1.612 +  for (int j = 0; j < repeatCount; j++) {
   1.613 +    for (uint32_t i = 0; i < stops->mStops.size(); i++) {
   1.614 +      colors.push_back(stops->mStops[i].color.r);
   1.615 +      colors.push_back(stops->mStops[i].color.g);
   1.616 +      colors.push_back(stops->mStops[i].color.b);
   1.617 +      colors.push_back(stops->mStops[i].color.a);
   1.618 +
   1.619 +      offsets.push_back((stops->mStops[i].offset + j)*scale);
   1.620 +    }
   1.621 +  }
   1.622 +
   1.623 +  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
   1.624 +  CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace,
   1.625 +                                                               &colors.front(),
   1.626 +                                                               &offsets.front(),
   1.627 +                                                               repeatCount*stops->mStops.size());
   1.628 +  CGColorSpaceRelease(colorSpace);
   1.629 +
   1.630 +  //XXX: are there degenerate radial gradients that we should avoid drawing?
   1.631 +  CGContextDrawRadialGradient(cg, gradient, startCenter, startRadius, endCenter, endRadius,
   1.632 +                              kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
   1.633 +  CGGradientRelease(gradient);
   1.634 +}
   1.635 +
   1.636 +static void
   1.637 +DrawGradient(CGContextRef cg, const Pattern &aPattern, const CGRect &aExtents)
   1.638 +{
   1.639 +  if (CGRectIsEmpty(aExtents)) {
   1.640 +    return;
   1.641 +  }
   1.642 +
   1.643 +  if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
   1.644 +    const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
   1.645 +    GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
   1.646 +    CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix));
   1.647 +    if (stops->mExtend == ExtendMode::CLAMP) {
   1.648 +
   1.649 +      // XXX: we should take the m out of the properties of LinearGradientPatterns
   1.650 +      CGPoint startPoint = { pat.mBegin.x, pat.mBegin.y };
   1.651 +      CGPoint endPoint   = { pat.mEnd.x,   pat.mEnd.y };
   1.652 +
   1.653 +      // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?)
   1.654 +      //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y)
   1.655 +      //  return;
   1.656 +
   1.657 +      CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint,
   1.658 +                                  kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
   1.659 +    } else if (stops->mExtend == ExtendMode::REPEAT) {
   1.660 +      DrawLinearRepeatingGradient(cg, pat, aExtents);
   1.661 +    }
   1.662 +  } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
   1.663 +    const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
   1.664 +    CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix));
   1.665 +    GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
   1.666 +    if (stops->mExtend == ExtendMode::CLAMP) {
   1.667 +
   1.668 +      // XXX: we should take the m out of the properties of RadialGradientPatterns
   1.669 +      CGPoint startCenter = { pat.mCenter1.x, pat.mCenter1.y };
   1.670 +      CGFloat startRadius = pat.mRadius1;
   1.671 +      CGPoint endCenter   = { pat.mCenter2.x, pat.mCenter2.y };
   1.672 +      CGFloat endRadius   = pat.mRadius2;
   1.673 +
   1.674 +      //XXX: are there degenerate radial gradients that we should avoid drawing?
   1.675 +      CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius,
   1.676 +                                  kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
   1.677 +    } else if (stops->mExtend == ExtendMode::REPEAT) {
   1.678 +      DrawRadialRepeatingGradient(cg, pat, aExtents);
   1.679 +    }
   1.680 +  } else {
   1.681 +    assert(0);
   1.682 +  }
   1.683 +
   1.684 +}
   1.685 +
   1.686 +static void
   1.687 +drawPattern(void *info, CGContextRef context)
   1.688 +{
   1.689 +  CGImageRef image = static_cast<CGImageRef>(info);
   1.690 +  CGRect rect = {{0, 0},
   1.691 +    {static_cast<CGFloat>(CGImageGetWidth(image)),
   1.692 +     static_cast<CGFloat>(CGImageGetHeight(image))}};
   1.693 +  CGContextDrawImage(context, rect, image);
   1.694 +}
   1.695 +
   1.696 +static void
   1.697 +releaseInfo(void *info)
   1.698 +{
   1.699 +  CGImageRef image = static_cast<CGImageRef>(info);
   1.700 +  CGImageRelease(image);
   1.701 +}
   1.702 +
   1.703 +CGPatternCallbacks patternCallbacks = {
   1.704 +  0,
   1.705 +  drawPattern,
   1.706 +  releaseInfo
   1.707 +};
   1.708 +
   1.709 +static bool
   1.710 +isGradient(const Pattern &aPattern)
   1.711 +{
   1.712 +  return aPattern.GetType() == PatternType::LINEAR_GRADIENT || aPattern.GetType() == PatternType::RADIAL_GRADIENT;
   1.713 +}
   1.714 +
   1.715 +/* CoreGraphics patterns ignore the userspace transform so
   1.716 + * we need to multiply it in */
   1.717 +static CGPatternRef
   1.718 +CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace)
   1.719 +{
   1.720 +  const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
   1.721 +  // XXX: is .get correct here?
   1.722 +  CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get());
   1.723 +  CGFloat xStep, yStep;
   1.724 +  switch (pat.mExtendMode) {
   1.725 +    case ExtendMode::CLAMP:
   1.726 +      // The 1 << 22 comes from Webkit see Pattern::createPlatformPattern() in PatternCG.cpp for more info
   1.727 +      xStep = static_cast<CGFloat>(1 << 22);
   1.728 +      yStep = static_cast<CGFloat>(1 << 22);
   1.729 +      break;
   1.730 +    case ExtendMode::REFLECT:
   1.731 +      assert(0);
   1.732 +    case ExtendMode::REPEAT:
   1.733 +      xStep = static_cast<CGFloat>(CGImageGetWidth(image));
   1.734 +      yStep = static_cast<CGFloat>(CGImageGetHeight(image));
   1.735 +      // webkit uses wkCGPatternCreateWithImageAndTransform a wrapper around CGPatternCreateWithImage2
   1.736 +      // this is done to avoid pixel-cracking along pattern boundaries
   1.737 +      // (see https://bugs.webkit.org/show_bug.cgi?id=53055)
   1.738 +      // typedef enum {
   1.739 +      //    wkPatternTilingNoDistortion,
   1.740 +      //    wkPatternTilingConstantSpacingMinimalDistortion,
   1.741 +      //    wkPatternTilingConstantSpacing
   1.742 +      // } wkPatternTiling;
   1.743 +      // extern CGPatternRef (*wkCGPatternCreateWithImageAndTransform)(CGImageRef, CGAffineTransform, int);
   1.744 +  }
   1.745 +
   1.746 +  //XXX: We should be using CGContextDrawTiledImage when we can. Even though it
   1.747 +  // creates a pattern, it seems to go down a faster path than using a delegate
   1.748 +  // like we do below
   1.749 +  CGRect bounds = {
   1.750 +    {0, 0,},
   1.751 +    {static_cast<CGFloat>(CGImageGetWidth(image)), static_cast<CGFloat>(CGImageGetHeight(image))}
   1.752 +  };
   1.753 +  CGAffineTransform transform =
   1.754 +      CGAffineTransformConcat(CGAffineTransformConcat(CGAffineTransformMakeScale(1,
   1.755 +                                                                                 -1),
   1.756 +                                                      GfxMatrixToCGAffineTransform(pat.mMatrix)),
   1.757 +                              aUserSpace);
   1.758 +  transform = CGAffineTransformTranslate(transform, 0, -static_cast<float>(CGImageGetHeight(image)));
   1.759 +  return CGPatternCreate(image, bounds, transform, xStep, yStep, kCGPatternTilingConstantSpacing,
   1.760 +                         true, &patternCallbacks);
   1.761 +}
   1.762 +
   1.763 +static void
   1.764 +SetFillFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
   1.765 +{
   1.766 +  assert(!isGradient(aPattern));
   1.767 +  if (aPattern.GetType() == PatternType::COLOR) {
   1.768 +
   1.769 +    const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
   1.770 +    //XXX: we should cache colors
   1.771 +    CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
   1.772 +    CGContextSetFillColorWithColor(cg, cgcolor);
   1.773 +    CGColorRelease(cgcolor);
   1.774 +  } else if (aPattern.GetType() == PatternType::SURFACE) {
   1.775 +
   1.776 +    CGColorSpaceRef patternSpace;
   1.777 +    patternSpace = CGColorSpaceCreatePattern (nullptr);
   1.778 +    CGContextSetFillColorSpace(cg, patternSpace);
   1.779 +    CGColorSpaceRelease(patternSpace);
   1.780 +
   1.781 +    CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
   1.782 +    const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
   1.783 +    CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
   1.784 +    CGFloat alpha = 1.;
   1.785 +    CGContextSetFillPattern(cg, pattern, &alpha);
   1.786 +    CGPatternRelease(pattern);
   1.787 +  }
   1.788 +}
   1.789 +
   1.790 +static void
   1.791 +SetStrokeFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
   1.792 +{
   1.793 +  assert(!isGradient(aPattern));
   1.794 +  if (aPattern.GetType() == PatternType::COLOR) {
   1.795 +    const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
   1.796 +    //XXX: we should cache colors
   1.797 +    CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
   1.798 +    CGContextSetStrokeColorWithColor(cg, cgcolor);
   1.799 +    CGColorRelease(cgcolor);
   1.800 +  } else if (aPattern.GetType() == PatternType::SURFACE) {
   1.801 +    CGColorSpaceRef patternSpace;
   1.802 +    patternSpace = CGColorSpaceCreatePattern (nullptr);
   1.803 +    CGContextSetStrokeColorSpace(cg, patternSpace);
   1.804 +    CGColorSpaceRelease(patternSpace);
   1.805 +
   1.806 +    CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
   1.807 +    const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
   1.808 +    CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
   1.809 +    CGFloat alpha = 1.;
   1.810 +    CGContextSetStrokePattern(cg, pattern, &alpha);
   1.811 +    CGPatternRelease(pattern);
   1.812 +  }
   1.813 +
   1.814 +}
   1.815 +
   1.816 +void
   1.817 +DrawTargetCG::MaskSurface(const Pattern &aSource,
   1.818 +                          SourceSurface *aMask,
   1.819 +                          Point aOffset,
   1.820 +                          const DrawOptions &aDrawOptions)
   1.821 +{
   1.822 +  MarkChanged();
   1.823 +
   1.824 +  CGContextSaveGState(mCg);
   1.825 +
   1.826 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   1.827 +  UnboundnessFixer fixer;
   1.828 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
   1.829 +  CGContextSetAlpha(cg, aDrawOptions.mAlpha);
   1.830 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   1.831 +
   1.832 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
   1.833 +  CGImageRef image = GetRetainedImageFromSourceSurface(aMask);
   1.834 +
   1.835 +  // use a negative-y so that the mask image draws right ways up
   1.836 +  CGContextScaleCTM(cg, 1, -1);
   1.837 +
   1.838 +  IntSize size = aMask->GetSize();
   1.839 +
   1.840 +  CGContextClipToMask(cg, CGRectMake(aOffset.x, -(aOffset.y + size.height), size.width, size.height), image);
   1.841 +
   1.842 +  CGContextScaleCTM(cg, 1, -1);
   1.843 +  if (isGradient(aSource)) {
   1.844 +    // we shouldn't need to clip to an additional rectangle
   1.845 +    // as the cliping to the mask should be sufficient.
   1.846 +    DrawGradient(cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
   1.847 +  } else {
   1.848 +    SetFillFromPattern(cg, mColorSpace, aSource);
   1.849 +    CGContextFillRect(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
   1.850 +  }
   1.851 +
   1.852 +  CGImageRelease(image);
   1.853 +
   1.854 +  fixer.Fix(mCg);
   1.855 +
   1.856 +  CGContextRestoreGState(mCg);
   1.857 +}
   1.858 +
   1.859 +
   1.860 +
   1.861 +void
   1.862 +DrawTargetCG::FillRect(const Rect &aRect,
   1.863 +                        const Pattern &aPattern,
   1.864 +                        const DrawOptions &aDrawOptions)
   1.865 +{
   1.866 +  MarkChanged();
   1.867 +
   1.868 +  CGContextSaveGState(mCg);
   1.869 +
   1.870 +  UnboundnessFixer fixer;
   1.871 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aRect);
   1.872 +  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   1.873 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   1.874 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   1.875 +
   1.876 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
   1.877 +
   1.878 +  if (isGradient(aPattern)) {
   1.879 +    CGContextClipToRect(cg, RectToCGRect(aRect));
   1.880 +    CGRect clipBounds = CGContextGetClipBoundingBox(cg);
   1.881 +    DrawGradient(cg, aPattern, clipBounds);
   1.882 +  } else {
   1.883 +    if (aPattern.GetType() == PatternType::SURFACE && static_cast<const SurfacePattern&>(aPattern).mExtendMode != ExtendMode::REPEAT) {
   1.884 +      // SetFillFromPattern can handle this case but using CGContextDrawImage
   1.885 +      // should give us better performance, better output, smaller PDF and
   1.886 +      // matches what cairo does.
   1.887 +      const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
   1.888 +      CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get());
   1.889 +      CGContextClipToRect(cg, RectToCGRect(aRect));
   1.890 +      CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix));
   1.891 +      CGContextTranslateCTM(cg, 0, CGImageGetHeight(image));
   1.892 +      CGContextScaleCTM(cg, 1, -1);
   1.893 +
   1.894 +      CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image));
   1.895 +
   1.896 +      CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
   1.897 +
   1.898 +      CGContextDrawImage(cg, imageRect, image);
   1.899 +      CGImageRelease(image);
   1.900 +    } else {
   1.901 +      SetFillFromPattern(cg, mColorSpace, aPattern);
   1.902 +      CGContextFillRect(cg, RectToCGRect(aRect));
   1.903 +    }
   1.904 +  }
   1.905 +
   1.906 +  fixer.Fix(mCg);
   1.907 +  CGContextRestoreGState(mCg);
   1.908 +}
   1.909 +
   1.910 +void
   1.911 +DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
   1.912 +{
   1.913 +  if (!std::isfinite(p1.x) ||
   1.914 +      !std::isfinite(p1.y) ||
   1.915 +      !std::isfinite(p2.x) ||
   1.916 +      !std::isfinite(p2.y)) {
   1.917 +    return;
   1.918 +  }
   1.919 +
   1.920 +  MarkChanged();
   1.921 +
   1.922 +  CGContextSaveGState(mCg);
   1.923 +
   1.924 +  UnboundnessFixer fixer;
   1.925 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
   1.926 +  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   1.927 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   1.928 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   1.929 +
   1.930 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
   1.931 +
   1.932 +  CGContextBeginPath(cg);
   1.933 +  CGContextMoveToPoint(cg, p1.x, p1.y);
   1.934 +  CGContextAddLineToPoint(cg, p2.x, p2.y);
   1.935 +
   1.936 +  SetStrokeOptions(cg, aStrokeOptions);
   1.937 +
   1.938 +  if (isGradient(aPattern)) {
   1.939 +    CGContextReplacePathWithStrokedPath(cg);
   1.940 +    CGRect extents = CGContextGetPathBoundingBox(cg);
   1.941 +    //XXX: should we use EO clip here?
   1.942 +    CGContextClip(cg);
   1.943 +    DrawGradient(cg, aPattern, extents);
   1.944 +  } else {
   1.945 +    SetStrokeFromPattern(cg, mColorSpace, aPattern);
   1.946 +    CGContextStrokePath(cg);
   1.947 +  }
   1.948 +
   1.949 +  fixer.Fix(mCg);
   1.950 +  CGContextRestoreGState(mCg);
   1.951 +}
   1.952 +
   1.953 +void
   1.954 +DrawTargetCG::StrokeRect(const Rect &aRect,
   1.955 +                         const Pattern &aPattern,
   1.956 +                         const StrokeOptions &aStrokeOptions,
   1.957 +                         const DrawOptions &aDrawOptions)
   1.958 +{
   1.959 +  if (!aRect.IsFinite()) {
   1.960 +    return;
   1.961 +  }
   1.962 +
   1.963 +  MarkChanged();
   1.964 +
   1.965 +  CGContextSaveGState(mCg);
   1.966 +
   1.967 +  UnboundnessFixer fixer;
   1.968 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
   1.969 +  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   1.970 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   1.971 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   1.972 +
   1.973 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
   1.974 +
   1.975 +  SetStrokeOptions(cg, aStrokeOptions);
   1.976 +
   1.977 +  if (isGradient(aPattern)) {
   1.978 +    // There's no CGContextClipStrokeRect so we do it by hand
   1.979 +    CGContextBeginPath(cg);
   1.980 +    CGContextAddRect(cg, RectToCGRect(aRect));
   1.981 +    CGContextReplacePathWithStrokedPath(cg);
   1.982 +    CGRect extents = CGContextGetPathBoundingBox(cg);
   1.983 +    //XXX: should we use EO clip here?
   1.984 +    CGContextClip(cg);
   1.985 +    DrawGradient(cg, aPattern, extents);
   1.986 +  } else {
   1.987 +    SetStrokeFromPattern(cg, mColorSpace, aPattern);
   1.988 +    CGContextStrokeRect(cg, RectToCGRect(aRect));
   1.989 +  }
   1.990 +
   1.991 +  fixer.Fix(mCg);
   1.992 +  CGContextRestoreGState(mCg);
   1.993 +}
   1.994 +
   1.995 +
   1.996 +void
   1.997 +DrawTargetCG::ClearRect(const Rect &aRect)
   1.998 +{
   1.999 +  MarkChanged();
  1.1000 +
  1.1001 +  CGContextSaveGState(mCg);
  1.1002 +  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
  1.1003 +
  1.1004 +  CGContextClearRect(mCg, RectToCGRect(aRect));
  1.1005 +
  1.1006 +  CGContextRestoreGState(mCg);
  1.1007 +}
  1.1008 +
  1.1009 +void
  1.1010 +DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
  1.1011 +{
  1.1012 +  if (!aPath->GetBounds().IsFinite()) {
  1.1013 +    return;
  1.1014 +  }
  1.1015 +
  1.1016 +  MarkChanged();
  1.1017 +
  1.1018 +  CGContextSaveGState(mCg);
  1.1019 +
  1.1020 +  UnboundnessFixer fixer;
  1.1021 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
  1.1022 +  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
  1.1023 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
  1.1024 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
  1.1025 +
  1.1026 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
  1.1027 +
  1.1028 +
  1.1029 +  CGContextBeginPath(cg);
  1.1030 +
  1.1031 +  assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
  1.1032 +  const PathCG *cgPath = static_cast<const PathCG*>(aPath);
  1.1033 +  CGContextAddPath(cg, cgPath->GetPath());
  1.1034 +
  1.1035 +  SetStrokeOptions(cg, aStrokeOptions);
  1.1036 +
  1.1037 +  if (isGradient(aPattern)) {
  1.1038 +    CGContextReplacePathWithStrokedPath(cg);
  1.1039 +    CGRect extents = CGContextGetPathBoundingBox(cg);
  1.1040 +    //XXX: should we use EO clip here?
  1.1041 +    CGContextClip(cg);
  1.1042 +    DrawGradient(cg, aPattern, extents);
  1.1043 +  } else {
  1.1044 +    // XXX: we could put fill mode into the path fill rule if we wanted
  1.1045 +
  1.1046 +    SetStrokeFromPattern(cg, mColorSpace, aPattern);
  1.1047 +    CGContextStrokePath(cg);
  1.1048 +  }
  1.1049 +
  1.1050 +  fixer.Fix(mCg);
  1.1051 +  CGContextRestoreGState(mCg);
  1.1052 +}
  1.1053 +
  1.1054 +void
  1.1055 +DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aDrawOptions)
  1.1056 +{
  1.1057 +  MarkChanged();
  1.1058 +
  1.1059 +  assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
  1.1060 +
  1.1061 +  CGContextSaveGState(mCg);
  1.1062 +
  1.1063 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
  1.1064 +  UnboundnessFixer fixer;
  1.1065 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
  1.1066 +  CGContextSetAlpha(cg, aDrawOptions.mAlpha);
  1.1067 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
  1.1068 +
  1.1069 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
  1.1070 +
  1.1071 +  CGContextBeginPath(cg);
  1.1072 +  // XXX: we could put fill mode into the path fill rule if we wanted
  1.1073 +  const PathCG *cgPath = static_cast<const PathCG*>(aPath);
  1.1074 +
  1.1075 +  if (isGradient(aPattern)) {
  1.1076 +    // setup a clip to draw the gradient through
  1.1077 +    CGRect extents;
  1.1078 +    if (CGPathIsEmpty(cgPath->GetPath())) {
  1.1079 +      // Adding an empty path will cause us not to clip
  1.1080 +      // so clip everything explicitly
  1.1081 +      CGContextClipToRect(mCg, CGRectZero);
  1.1082 +      extents = CGRectZero;
  1.1083 +    } else {
  1.1084 +      CGContextAddPath(cg, cgPath->GetPath());
  1.1085 +      extents = CGContextGetPathBoundingBox(cg);
  1.1086 +      if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
  1.1087 +        CGContextEOClip(mCg);
  1.1088 +      else
  1.1089 +        CGContextClip(mCg);
  1.1090 +    }
  1.1091 +
  1.1092 +    DrawGradient(cg, aPattern, extents);
  1.1093 +  } else {
  1.1094 +    CGContextAddPath(cg, cgPath->GetPath());
  1.1095 +
  1.1096 +    SetFillFromPattern(cg, mColorSpace, aPattern);
  1.1097 +
  1.1098 +    if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
  1.1099 +      CGContextEOFillPath(cg);
  1.1100 +    else
  1.1101 +      CGContextFillPath(cg);
  1.1102 +  }
  1.1103 +
  1.1104 +  fixer.Fix(mCg);
  1.1105 +  CGContextRestoreGState(mCg);
  1.1106 +}
  1.1107 +
  1.1108 +CGRect ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
  1.1109 +{
  1.1110 +  CGFloat x1, x2, y1, y2;
  1.1111 +  if (count < 1)
  1.1112 +    return CGRectZero;
  1.1113 +
  1.1114 +  x1 = bboxes[0].origin.x + positions[0].x;
  1.1115 +  x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width;
  1.1116 +  y1 = bboxes[0].origin.y + positions[0].y;
  1.1117 +  y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height;
  1.1118 +
  1.1119 +  // accumulate max and minimum coordinates
  1.1120 +  for (int i = 1; i < count; i++) {
  1.1121 +    x1 = min(x1, bboxes[i].origin.x + positions[i].x);
  1.1122 +    y1 = min(y1, bboxes[i].origin.y + positions[i].y);
  1.1123 +    x2 = max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
  1.1124 +    y2 = max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
  1.1125 +  }
  1.1126 +
  1.1127 +  CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
  1.1128 +  return extents;
  1.1129 +}
  1.1130 +
  1.1131 +
  1.1132 +void
  1.1133 +DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions,
  1.1134 +                         const GlyphRenderingOptions*)
  1.1135 +{
  1.1136 +  MarkChanged();
  1.1137 +
  1.1138 +  assert(aBuffer.mNumGlyphs);
  1.1139 +  CGContextSaveGState(mCg);
  1.1140 +
  1.1141 +  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
  1.1142 +  UnboundnessFixer fixer;
  1.1143 +  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
  1.1144 +  CGContextSetAlpha(cg, aDrawOptions.mAlpha);
  1.1145 +  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
  1.1146 +  if (aDrawOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
  1.1147 +    CGContextSetShouldSmoothFonts(cg, aDrawOptions.mAntialiasMode == AntialiasMode::SUBPIXEL);
  1.1148 +  }
  1.1149 +
  1.1150 +  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
  1.1151 +
  1.1152 +  ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
  1.1153 +
  1.1154 +  //XXX: we should use a stack vector here when we have a class like that
  1.1155 +  std::vector<CGGlyph> glyphs;
  1.1156 +  std::vector<CGPoint> positions;
  1.1157 +  glyphs.resize(aBuffer.mNumGlyphs);
  1.1158 +  positions.resize(aBuffer.mNumGlyphs);
  1.1159 +
  1.1160 +  // Handle the flip
  1.1161 +  CGContextScaleCTM(cg, 1, -1);
  1.1162 +  // CGContextSetTextMatrix works differently with kCGTextClip && kCGTextFill
  1.1163 +  // It seems that it transforms the positions with TextFill and not with TextClip
  1.1164 +  // Therefore we'll avoid it. See also:
  1.1165 +  // http://cgit.freedesktop.org/cairo/commit/?id=9c0d761bfcdd28d52c83d74f46dd3c709ae0fa69
  1.1166 +
  1.1167 +  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
  1.1168 +    glyphs[i] = aBuffer.mGlyphs[i].mIndex;
  1.1169 +    // XXX: CGPointMake might not be inlined
  1.1170 +    positions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
  1.1171 +                              -aBuffer.mGlyphs[i].mPosition.y);
  1.1172 +  }
  1.1173 +
  1.1174 +  //XXX: CGContextShowGlyphsAtPositions is 10.5+ for older versions use CGContextShowGlyphsWithAdvances
  1.1175 +  if (isGradient(aPattern)) {
  1.1176 +    CGContextSetTextDrawingMode(cg, kCGTextClip);
  1.1177 +    CGRect extents;
  1.1178 +    if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
  1.1179 +      CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
  1.1180 +      CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation,
  1.1181 +                                      &glyphs.front(), bboxes, aBuffer.mNumGlyphs);
  1.1182 +      extents = ComputeGlyphsExtents(bboxes, &positions.front(), aBuffer.mNumGlyphs, 1.0f);
  1.1183 +      ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(),
  1.1184 +                                         &positions.front(), aBuffer.mNumGlyphs, cg);
  1.1185 +      delete bboxes;
  1.1186 +    } else {
  1.1187 +      CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
  1.1188 +      CGFontGetGlyphBBoxes(macFont->mFont, &glyphs.front(), aBuffer.mNumGlyphs, bboxes);
  1.1189 +      extents = ComputeGlyphsExtents(bboxes, &positions.front(), aBuffer.mNumGlyphs, macFont->mSize);
  1.1190 +
  1.1191 +      CGContextSetFont(cg, macFont->mFont);
  1.1192 +      CGContextSetFontSize(cg, macFont->mSize);
  1.1193 +      CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(),
  1.1194 +                                     aBuffer.mNumGlyphs);
  1.1195 +      delete bboxes;
  1.1196 +    }
  1.1197 +    CGContextScaleCTM(cg, 1, -1);
  1.1198 +    DrawGradient(cg, aPattern, extents);
  1.1199 +  } else {
  1.1200 +    //XXX: with CoreGraphics we can stroke text directly instead of going
  1.1201 +    // through GetPath. It would be nice to add support for using that
  1.1202 +    CGContextSetTextDrawingMode(cg, kCGTextFill);
  1.1203 +    SetFillFromPattern(cg, mColorSpace, aPattern);
  1.1204 +    if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
  1.1205 +      ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(),
  1.1206 +                                         &positions.front(),
  1.1207 +                                         aBuffer.mNumGlyphs, cg);
  1.1208 +    } else {
  1.1209 +      CGContextSetFont(cg, macFont->mFont);
  1.1210 +      CGContextSetFontSize(cg, macFont->mSize);
  1.1211 +      CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(),
  1.1212 +                                     aBuffer.mNumGlyphs);
  1.1213 +    }
  1.1214 +  }
  1.1215 +
  1.1216 +  fixer.Fix(mCg);
  1.1217 +  CGContextRestoreGState(cg);
  1.1218 +}
  1.1219 +
  1.1220 +extern "C" {
  1.1221 +void
  1.1222 +CGContextResetClip(CGContextRef);
  1.1223 +};
  1.1224 +
  1.1225 +void
  1.1226 +DrawTargetCG::CopySurface(SourceSurface *aSurface,
  1.1227 +                          const IntRect& aSourceRect,
  1.1228 +                          const IntPoint &aDestination)
  1.1229 +{
  1.1230 +  MarkChanged();
  1.1231 +
  1.1232 +  if (aSurface->GetType() == SurfaceType::COREGRAPHICS_IMAGE ||
  1.1233 +      aSurface->GetType() == SurfaceType::COREGRAPHICS_CGCONTEXT ||
  1.1234 +      aSurface->GetType() == SurfaceType::DATA) {
  1.1235 +    CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
  1.1236 +
  1.1237 +    // XXX: it might be more efficient for us to do the copy directly if we have access to the bits
  1.1238 +
  1.1239 +    CGContextSaveGState(mCg);
  1.1240 +
  1.1241 +    // CopySurface ignores the clip, so we need to use private API to temporarily reset it
  1.1242 +    CGContextResetClip(mCg);
  1.1243 +    CGRect destRect = CGRectMake(aDestination.x, aDestination.y,
  1.1244 +                                 aSourceRect.width, aSourceRect.height);
  1.1245 +    CGContextClipToRect(mCg, destRect);
  1.1246 +
  1.1247 +    CGContextSetBlendMode(mCg, kCGBlendModeCopy);
  1.1248 +
  1.1249 +    CGContextScaleCTM(mCg, 1, -1);
  1.1250 +
  1.1251 +    CGRect flippedRect = CGRectMake(aDestination.x - aSourceRect.x, -(aDestination.y - aSourceRect.y + double(CGImageGetHeight(image))),
  1.1252 +                                    CGImageGetWidth(image), CGImageGetHeight(image));
  1.1253 +
  1.1254 +    // Quartz seems to copy A8 surfaces incorrectly if we don't initialize them
  1.1255 +    // to transparent first.
  1.1256 +    if (mFormat == SurfaceFormat::A8) {
  1.1257 +      CGContextClearRect(mCg, flippedRect);
  1.1258 +    }
  1.1259 +    CGContextDrawImage(mCg, flippedRect, image);
  1.1260 +
  1.1261 +    CGContextRestoreGState(mCg);
  1.1262 +    CGImageRelease(image);
  1.1263 +  }
  1.1264 +}
  1.1265 +
  1.1266 +void
  1.1267 +DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator)
  1.1268 +{
  1.1269 +  MarkChanged();
  1.1270 +
  1.1271 +  CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
  1.1272 +
  1.1273 +  IntSize size = aSurface->GetSize();
  1.1274 +  CGContextSaveGState(mCg);
  1.1275 +  //XXX do we need to do the fixup here?
  1.1276 +  CGContextSetBlendMode(mCg, ToBlendMode(aOperator));
  1.1277 +
  1.1278 +  CGContextScaleCTM(mCg, 1, -1);
  1.1279 +
  1.1280 +  CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + size.height),
  1.1281 +                                  size.width, size.height);
  1.1282 +
  1.1283 +  CGColorRef color = ColorToCGColor(mColorSpace, aColor);
  1.1284 +  CGSize offset = {aOffset.x, -aOffset.y};
  1.1285 +  // CoreGraphics needs twice sigma as it's amount of blur
  1.1286 +  CGContextSetShadowWithColor(mCg, offset, 2*aSigma, color);
  1.1287 +  CGColorRelease(color);
  1.1288 +
  1.1289 +  CGContextDrawImage(mCg, flippedRect, image);
  1.1290 +
  1.1291 +  CGImageRelease(image);
  1.1292 +  CGContextRestoreGState(mCg);
  1.1293 +
  1.1294 +}
  1.1295 +
  1.1296 +bool
  1.1297 +DrawTargetCG::Init(BackendType aType,
  1.1298 +                   unsigned char* aData,
  1.1299 +                   const IntSize &aSize,
  1.1300 +                   int32_t aStride,
  1.1301 +                   SurfaceFormat aFormat)
  1.1302 +{
  1.1303 +  // XXX: we should come up with some consistent semantics for dealing
  1.1304 +  // with zero area drawtargets
  1.1305 +  if (aSize.width <= 0 || aSize.height <= 0 ||
  1.1306 +      // 32767 is the maximum size supported by cairo
  1.1307 +      // we clamp to that to make it easier to interoperate
  1.1308 +      aSize.width > 32767 || aSize.height > 32767) {
  1.1309 +    gfxWarning() << "Failed to Init() DrawTargetCG because of bad size.";
  1.1310 +    mColorSpace = nullptr;
  1.1311 +    mCg = nullptr;
  1.1312 +    return false;
  1.1313 +  }
  1.1314 +
  1.1315 +  //XXX: handle SurfaceFormat
  1.1316 +
  1.1317 +  //XXX: we'd be better off reusing the Colorspace across draw targets
  1.1318 +  mColorSpace = CGColorSpaceCreateDeviceRGB();
  1.1319 +
  1.1320 +  if (aData == nullptr && aType != BackendType::COREGRAPHICS_ACCELERATED) {
  1.1321 +    // XXX: Currently, Init implicitly clears, that can often be a waste of time
  1.1322 +    size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
  1.1323 +    if (bufLen == 0) {
  1.1324 +      mColorSpace = nullptr;
  1.1325 +      mCg = nullptr;
  1.1326 +      return false;
  1.1327 +    }
  1.1328 +    static_assert(sizeof(decltype(mData[0])) == 1,
  1.1329 +                  "mData.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
  1.1330 +    mData.Realloc(/* actually an object count */ bufLen);
  1.1331 +    aData = static_cast<unsigned char*>(mData);
  1.1332 +    memset(aData, 0, bufLen);
  1.1333 +  }
  1.1334 +
  1.1335 +  mSize = aSize;
  1.1336 +
  1.1337 +  if (aType == BackendType::COREGRAPHICS_ACCELERATED) {
  1.1338 +    RefPtr<MacIOSurface> ioSurface = MacIOSurface::CreateIOSurface(aSize.width, aSize.height);
  1.1339 +    mCg = ioSurface->CreateIOSurfaceContext();
  1.1340 +    // If we don't have the symbol for 'CreateIOSurfaceContext' mCg will be null
  1.1341 +    // and we will fallback to software below
  1.1342 +  }
  1.1343 +
  1.1344 +  mFormat = SurfaceFormat::B8G8R8A8;
  1.1345 +
  1.1346 +  if (!mCg || aType == BackendType::COREGRAPHICS) {
  1.1347 +    int bitsPerComponent = 8;
  1.1348 +
  1.1349 +    CGBitmapInfo bitinfo;
  1.1350 +    if (aFormat == SurfaceFormat::A8) {
  1.1351 +      if (mColorSpace)
  1.1352 +        CGColorSpaceRelease(mColorSpace);
  1.1353 +      mColorSpace = nullptr;
  1.1354 +      bitinfo = kCGImageAlphaOnly;
  1.1355 +      mFormat = SurfaceFormat::A8;
  1.1356 +    } else {
  1.1357 +      bitinfo = kCGBitmapByteOrder32Host;
  1.1358 +      if (aFormat == SurfaceFormat::B8G8R8X8) {
  1.1359 +        bitinfo |= kCGImageAlphaNoneSkipFirst;
  1.1360 +        mFormat = aFormat;
  1.1361 +      } else {
  1.1362 +        bitinfo |= kCGImageAlphaPremultipliedFirst;
  1.1363 +      }
  1.1364 +    }
  1.1365 +    // XXX: what should we do if this fails?
  1.1366 +    mCg = CGBitmapContextCreate (aData,
  1.1367 +                                 mSize.width,
  1.1368 +                                 mSize.height,
  1.1369 +                                 bitsPerComponent,
  1.1370 +                                 aStride,
  1.1371 +                                 mColorSpace,
  1.1372 +                                 bitinfo);
  1.1373 +  }
  1.1374 +
  1.1375 +  assert(mCg);
  1.1376 +  // CGContext's default to have the origin at the bottom left
  1.1377 +  // so flip it to the top left
  1.1378 +  CGContextTranslateCTM(mCg, 0, mSize.height);
  1.1379 +  CGContextScaleCTM(mCg, 1, -1);
  1.1380 +  // See Bug 722164 for performance details
  1.1381 +  // Medium or higher quality lead to expensive interpolation
  1.1382 +  // for canvas we want to use low quality interpolation
  1.1383 +  // to have competitive performance with other canvas
  1.1384 +  // implementation.
  1.1385 +  // XXX: Create input parameter to control interpolation and
  1.1386 +  //      use the default for content.
  1.1387 +  CGContextSetInterpolationQuality(mCg, kCGInterpolationLow);
  1.1388 +
  1.1389 +
  1.1390 +  if (aType == BackendType::COREGRAPHICS_ACCELERATED) {
  1.1391 +    // The bitmap backend uses callac to clear, we can't do that without
  1.1392 +    // reading back the surface. This should trigger something equivilent
  1.1393 +    // to glClear.
  1.1394 +    ClearRect(Rect(0, 0, mSize.width, mSize.height));
  1.1395 +  }
  1.1396 +
  1.1397 +  return true;
  1.1398 +}
  1.1399 +
  1.1400 +void
  1.1401 +DrawTargetCG::Flush()
  1.1402 +{
  1.1403 +  if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
  1.1404 +    CGContextFlush(mCg);
  1.1405 +  }
  1.1406 +}
  1.1407 +
  1.1408 +bool
  1.1409 +DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
  1.1410 +{
  1.1411 +  // XXX: we should come up with some consistent semantics for dealing
  1.1412 +  // with zero area drawtargets
  1.1413 +  if (aSize.width == 0 || aSize.height == 0) {
  1.1414 +    mColorSpace = nullptr;
  1.1415 +    mCg = nullptr;
  1.1416 +    return false;
  1.1417 +  }
  1.1418 +
  1.1419 +  //XXX: handle SurfaceFormat
  1.1420 +
  1.1421 +  //XXX: we'd be better off reusing the Colorspace across draw targets
  1.1422 +  mColorSpace = CGColorSpaceCreateDeviceRGB();
  1.1423 +
  1.1424 +  mSize = aSize;
  1.1425 +
  1.1426 +  mCg = cgContext;
  1.1427 +  CGContextRetain(mCg);
  1.1428 +
  1.1429 +  assert(mCg);
  1.1430 +
  1.1431 +  // CGContext's default to have the origin at the bottom left.
  1.1432 +  // However, currently the only use of this function is to construct a
  1.1433 +  // DrawTargetCG around a CGContextRef from a cairo quartz surface which
  1.1434 +  // already has it's origin adjusted.
  1.1435 +  //
  1.1436 +  // CGContextTranslateCTM(mCg, 0, mSize.height);
  1.1437 +  // CGContextScaleCTM(mCg, 1, -1);
  1.1438 +
  1.1439 +  mFormat = SurfaceFormat::B8G8R8A8;
  1.1440 +  if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) {
  1.1441 +    CGColorSpaceRef colorspace;
  1.1442 +    CGBitmapInfo bitinfo = CGBitmapContextGetBitmapInfo(mCg);
  1.1443 +    colorspace = CGBitmapContextGetColorSpace (mCg);
  1.1444 +    if (CGColorSpaceGetNumberOfComponents(colorspace) == 1) {
  1.1445 +      mFormat = SurfaceFormat::A8;
  1.1446 +    } else if ((bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst) {
  1.1447 +      mFormat = SurfaceFormat::B8G8R8X8;
  1.1448 +    }
  1.1449 +  }
  1.1450 +
  1.1451 +  return true;
  1.1452 +}
  1.1453 +
  1.1454 +bool
  1.1455 +DrawTargetCG::Init(BackendType aType, const IntSize &aSize, SurfaceFormat &aFormat)
  1.1456 +{
  1.1457 +  int32_t stride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat));
  1.1458 +  
  1.1459 +  // Calling Init with aData == nullptr will allocate.
  1.1460 +  return Init(aType, nullptr, aSize, stride, aFormat);
  1.1461 +}
  1.1462 +
  1.1463 +TemporaryRef<PathBuilder>
  1.1464 +DrawTargetCG::CreatePathBuilder(FillRule aFillRule) const
  1.1465 +{
  1.1466 +  RefPtr<PathBuilderCG> pb = new PathBuilderCG(aFillRule);
  1.1467 +  return pb;
  1.1468 +}
  1.1469 +
  1.1470 +void*
  1.1471 +DrawTargetCG::GetNativeSurface(NativeSurfaceType aType)
  1.1472 +{
  1.1473 +  if ((aType == NativeSurfaceType::CGCONTEXT && GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) ||
  1.1474 +      (aType == NativeSurfaceType::CGCONTEXT_ACCELERATED && GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE)) {
  1.1475 +    return mCg;
  1.1476 +  } else {
  1.1477 +    return nullptr;
  1.1478 +  }
  1.1479 +}
  1.1480 +
  1.1481 +void
  1.1482 +DrawTargetCG::Mask(const Pattern &aSource,
  1.1483 +                   const Pattern &aMask,
  1.1484 +                   const DrawOptions &aDrawOptions)
  1.1485 +{
  1.1486 +  MarkChanged();
  1.1487 +
  1.1488 +  CGContextSaveGState(mCg);
  1.1489 +
  1.1490 +  if (isGradient(aMask)) {
  1.1491 +    assert(0);
  1.1492 +  } else {
  1.1493 +    if (aMask.GetType() == PatternType::COLOR) {
  1.1494 +      DrawOptions drawOptions(aDrawOptions);
  1.1495 +      const Color& color = static_cast<const ColorPattern&>(aMask).mColor;
  1.1496 +      drawOptions.mAlpha *= color.a;
  1.1497 +      assert(0);
  1.1498 +      // XXX: we need to get a rect that when transformed covers the entire surface
  1.1499 +      //Rect
  1.1500 +      //FillRect(rect, aSource, drawOptions);
  1.1501 +    } else if (aMask.GetType() == PatternType::SURFACE) {
  1.1502 +      const SurfacePattern& pat = static_cast<const SurfacePattern&>(aMask);
  1.1503 +      CGImageRef mask = GetRetainedImageFromSourceSurface(pat.mSurface.get());
  1.1504 +      Rect rect(0,0, CGImageGetWidth(mask), CGImageGetHeight(mask));
  1.1505 +      // XXX: probably we need to do some flipping of the image or something
  1.1506 +      CGContextClipToMask(mCg, RectToCGRect(rect), mask);
  1.1507 +      FillRect(rect, aSource, aDrawOptions);
  1.1508 +      CGImageRelease(mask);
  1.1509 +    }
  1.1510 +  }
  1.1511 +
  1.1512 +  CGContextRestoreGState(mCg);
  1.1513 +}
  1.1514 +
  1.1515 +void
  1.1516 +DrawTargetCG::PushClipRect(const Rect &aRect)
  1.1517 +{
  1.1518 +  CGContextSaveGState(mCg);
  1.1519 +
  1.1520 +  /* We go through a bit of trouble to temporarilly set the transform
  1.1521 +   * while we add the path */
  1.1522 +  CGAffineTransform previousTransform = CGContextGetCTM(mCg);
  1.1523 +  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
  1.1524 +  CGContextClipToRect(mCg, RectToCGRect(aRect));
  1.1525 +  CGContextSetCTM(mCg, previousTransform);
  1.1526 +}
  1.1527 +
  1.1528 +
  1.1529 +void
  1.1530 +DrawTargetCG::PushClip(const Path *aPath)
  1.1531 +{
  1.1532 +  CGContextSaveGState(mCg);
  1.1533 +
  1.1534 +  CGContextBeginPath(mCg);
  1.1535 +  assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
  1.1536 +
  1.1537 +  const PathCG *cgPath = static_cast<const PathCG*>(aPath);
  1.1538 +
  1.1539 +  // Weirdly, CoreGraphics clips empty paths as all shown
  1.1540 +  // but emtpy rects as all clipped.  We detect this situation and
  1.1541 +  // workaround it appropriately
  1.1542 +  if (CGPathIsEmpty(cgPath->GetPath())) {
  1.1543 +    // XXX: should we return here?
  1.1544 +    CGContextClipToRect(mCg, CGRectZero);
  1.1545 +  }
  1.1546 +
  1.1547 +
  1.1548 +  /* We go through a bit of trouble to temporarilly set the transform
  1.1549 +   * while we add the path. XXX: this could be improved if we keep
  1.1550 +   * the CTM as resident state on the DrawTarget. */
  1.1551 +  CGContextSaveGState(mCg);
  1.1552 +  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
  1.1553 +  CGContextAddPath(mCg, cgPath->GetPath());
  1.1554 +  CGContextRestoreGState(mCg);
  1.1555 +
  1.1556 +  if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
  1.1557 +    CGContextEOClip(mCg);
  1.1558 +  else
  1.1559 +    CGContextClip(mCg);
  1.1560 +}
  1.1561 +
  1.1562 +void
  1.1563 +DrawTargetCG::PopClip()
  1.1564 +{
  1.1565 +  CGContextRestoreGState(mCg);
  1.1566 +}
  1.1567 +
  1.1568 +void
  1.1569 +DrawTargetCG::MarkChanged()
  1.1570 +{
  1.1571 +  if (mSnapshot) {
  1.1572 +    if (mSnapshot->refCount() > 1) {
  1.1573 +      // We only need to worry about snapshots that someone else knows about
  1.1574 +      mSnapshot->DrawTargetWillChange();
  1.1575 +    }
  1.1576 +    mSnapshot = nullptr;
  1.1577 +  }
  1.1578 +}
  1.1579 +
  1.1580 +CGContextRef
  1.1581 +BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
  1.1582 +{
  1.1583 +  if (aDT->GetType() == BackendType::COREGRAPHICS || aDT->GetType() == BackendType::COREGRAPHICS_ACCELERATED) {
  1.1584 +    DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT);
  1.1585 +    cgDT->MarkChanged();
  1.1586 +
  1.1587 +    // swap out the context
  1.1588 +    CGContextRef cg = cgDT->mCg;
  1.1589 +    cgDT->mCg = nullptr;
  1.1590 +
  1.1591 +    // save the state to make it easier for callers to avoid mucking with things
  1.1592 +    CGContextSaveGState(cg);
  1.1593 +
  1.1594 +    CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(cgDT->mTransform));
  1.1595 +
  1.1596 +    return cg;
  1.1597 +  }
  1.1598 +  return nullptr;
  1.1599 +}
  1.1600 +
  1.1601 +void
  1.1602 +BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg)
  1.1603 +{
  1.1604 +  DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT);
  1.1605 +
  1.1606 +  CGContextRestoreGState(cg);
  1.1607 +  cgDT->mCg = cg;
  1.1608 +}
  1.1609 +
  1.1610 +
  1.1611 +}
  1.1612 +}

mercurial