Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / skia / ext / skia_utils_mac.mm
index 6a624a8..dd8f560 100644 (file)
@@ -27,13 +27,7 @@ SkBitmap NSImageOrNSImageRepToSkBitmapWithColorSpace(
   DCHECK((image != 0) ^ (image_rep != 0));
 
   SkBitmap bitmap;
-  bitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                   size.width,
-                   size.height,
-                   0,
-                   is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
-
-  if (!bitmap.allocPixels())
+  if (!bitmap.tryAllocN32Pixels(size.width, size.height, is_opaque))
     return bitmap;  // Return |bitmap| which should respond true to isNull().
 
 
@@ -273,81 +267,111 @@ NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) {
 
 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas)
     : canvas_(canvas),
-      cgContext_(0) {
+      userClipRectSpecified_(false),
+      cgContext_(0),
+      bitmapScaleFactor_(1),
+      useDeviceBits_(false),
+      bitmapIsDummy_(false) {
+}
+
+SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas,
+                             const SkIRect& userClipRect,
+                             SkScalar bitmapScaleFactor)
+    : canvas_(canvas),
+      userClipRectSpecified_(true),
+      cgContext_(0),
+      bitmapScaleFactor_(bitmapScaleFactor),
+      useDeviceBits_(false),
+      bitmapIsDummy_(false) {
+  canvas_->save();
+  canvas_->clipRect(SkRect::MakeFromIRect(userClipRect));
 }
 
 SkiaBitLocker::~SkiaBitLocker() {
   releaseIfNeeded();
+  if (userClipRectSpecified_)
+    canvas_->restore();
 }
 
-// This must be called to balance calls to cgContext
-void SkiaBitLocker::releaseIfNeeded() {
-  if (!cgContext_)
-    return;
-  if (useDeviceBits_) {
-    bitmap_.unlockPixels();
-  } else {
-    // Find the bits that were drawn to.
-    SkAutoLockPixels lockedPixels(bitmap_);
-    const uint32_t* pixelBase
-        = reinterpret_cast<uint32_t*>(bitmap_.getPixels());
-    int rowPixels = bitmap_.rowBytesAsPixels();
-    int width = bitmap_.width();
-    int height = bitmap_.height();
-    SkIRect bounds;
-    bounds.fTop = 0;
-    int x;
-    int y = -1;
-    const uint32_t* pixels = pixelBase;
-    while (++y < height) {
-      for (x = 0; x < width; ++x) {
-        if (pixels[x]) {
-          bounds.fTop = y;
-          goto foundTop;
-        }
+SkIRect SkiaBitLocker::computeDirtyRect() {
+  // If the user specified a clip region, assume that it was tight and that the
+  // dirty rect is approximately the whole bitmap.
+  if (userClipRectSpecified_)
+    return SkIRect::MakeWH(bitmap_.width(), bitmap_.height());
+
+  // Find the bits that were drawn to.
+  SkAutoLockPixels lockedPixels(bitmap_);
+  const uint32_t* pixelBase
+      = reinterpret_cast<uint32_t*>(bitmap_.getPixels());
+  int rowPixels = bitmap_.rowBytesAsPixels();
+  int width = bitmap_.width();
+  int height = bitmap_.height();
+  SkIRect bounds;
+  bounds.fTop = 0;
+  int x;
+  int y = -1;
+  const uint32_t* pixels = pixelBase;
+  while (++y < height) {
+    for (x = 0; x < width; ++x) {
+      if (pixels[x]) {
+        bounds.fTop = y;
+        goto foundTop;
       }
-      pixels += rowPixels;
     }
+    pixels += rowPixels;
+  }
 foundTop:
-    bounds.fBottom = height;
-    y = height;
-    pixels = pixelBase + rowPixels * (y - 1);
-    while (--y > bounds.fTop) {
-      for (x = 0; x < width; ++x) {
-        if (pixels[x]) {
-          bounds.fBottom = y + 1;
-          goto foundBottom;
-        }
+  bounds.fBottom = height;
+  y = height;
+  pixels = pixelBase + rowPixels * (y - 1);
+  while (--y > bounds.fTop) {
+    for (x = 0; x < width; ++x) {
+      if (pixels[x]) {
+        bounds.fBottom = y + 1;
+        goto foundBottom;
       }
-      pixels -= rowPixels;
     }
+    pixels -= rowPixels;
+  }
 foundBottom:
-    bounds.fLeft = 0;
-    x = -1;
-    while (++x < width) {
-      pixels = pixelBase + rowPixels * bounds.fTop;
-      for (y = bounds.fTop; y < bounds.fBottom; ++y) {
-        if (pixels[x]) {
-          bounds.fLeft = x;
-          goto foundLeft;
-        }
-        pixels += rowPixels;
+  bounds.fLeft = 0;
+  x = -1;
+  while (++x < width) {
+    pixels = pixelBase + rowPixels * bounds.fTop;
+    for (y = bounds.fTop; y < bounds.fBottom; ++y) {
+      if (pixels[x]) {
+        bounds.fLeft = x;
+        goto foundLeft;
       }
+      pixels += rowPixels;
     }
+  }
 foundLeft:
-    bounds.fRight = width;
-    x = width;
-    while (--x > bounds.fLeft) {
-      pixels = pixelBase + rowPixels * bounds.fTop;
-      for (y = bounds.fTop; y < bounds.fBottom; ++y) {
-        if (pixels[x]) {
-          bounds.fRight = x + 1;
-          goto foundRight;
-        }
-        pixels += rowPixels;
+  bounds.fRight = width;
+  x = width;
+  while (--x > bounds.fLeft) {
+    pixels = pixelBase + rowPixels * bounds.fTop;
+    for (y = bounds.fTop; y < bounds.fBottom; ++y) {
+      if (pixels[x]) {
+        bounds.fRight = x + 1;
+        goto foundRight;
       }
+      pixels += rowPixels;
     }
+  }
 foundRight:
+  return bounds;
+}
+
+// This must be called to balance calls to cgContext
+void SkiaBitLocker::releaseIfNeeded() {
+  if (!cgContext_)
+    return;
+  if (useDeviceBits_) {
+    bitmap_.unlockPixels();
+  } else if (!bitmapIsDummy_) {
+    // Find the bits that were drawn to.
+    SkIRect bounds = computeDirtyRect();
     SkBitmap subset;
     if (!bitmap_.extractSubset(&subset, bounds)) {
         return;
@@ -362,28 +386,62 @@ foundRight:
       return;
     canvas_->save();
     canvas_->concat(inverse);
-    canvas_->drawBitmap(subset, bounds.fLeft, bounds.fTop);
+    canvas_->translate(bounds.x() + bitmapOffset_.x(),
+                       bounds.y() + bitmapOffset_.y());
+    canvas_->scale(1.f / bitmapScaleFactor_, 1.f / bitmapScaleFactor_);
+    canvas_->drawBitmap(subset, 0, 0);
     canvas_->restore();
   }
   CGContextRelease(cgContext_);
   cgContext_ = 0;
+  useDeviceBits_ = false;
+  bitmapIsDummy_ = false;
 }
 
 CGContextRef SkiaBitLocker::cgContext() {
+  SkIRect clip_bounds;
+  if (!canvas_->getClipDeviceBounds(&clip_bounds)) {
+    // If the clip is empty, then there is nothing to draw. The caller may
+    // attempt to draw (to-be-clipped) results, so ensure there is a dummy
+    // non-NULL CGContext to use.
+    bitmapIsDummy_ = true;
+    clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1);
+  }
+
   SkBaseDevice* device = canvas_->getTopDevice();
   DCHECK(device);
   if (!device)
     return 0;
+
   releaseIfNeeded(); // This flushes any prior bitmap use
+
+  // remember the top/left, in case we need to compose this later
+  bitmapOffset_.set(clip_bounds.x(), clip_bounds.y());
+
+  // Now make clip_bounds be relative to the current layer/device
+  clip_bounds.offset(-device->getOrigin());
+
   const SkBitmap& deviceBits = device->accessBitmap(true);
-  useDeviceBits_ = deviceBits.getPixels();
+
+  // Only draw directly if we have pixels, and we're only rect-clipped.
+  // If not, we allocate an offscreen and draw into that, relying on the
+  // compositing step to apply skia's clip.
+  useDeviceBits_ = deviceBits.getPixels() &&
+                   canvas_->isClipRect() &&
+                   !bitmapIsDummy_;
   if (useDeviceBits_) {
-    bitmap_ = deviceBits;
+    bool result = deviceBits.extractSubset(&bitmap_, clip_bounds);
+    DCHECK(result);
+    if (!result)
+      return 0;
     bitmap_.lockPixels();
   } else {
-    bitmap_.setConfig(
-        SkBitmap::kARGB_8888_Config, deviceBits.width(), deviceBits.height());
-    bitmap_.allocPixels();
+    bool result = bitmap_.tryAllocN32Pixels(
+        SkScalarCeilToInt(bitmapScaleFactor_ * clip_bounds.width()),
+        SkScalarCeilToInt(bitmapScaleFactor_ * clip_bounds.height()));
+    DCHECK(result);
+    if (!result)
+      return 0;
     bitmap_.eraseColor(0);
   }
   base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace(
@@ -391,47 +449,21 @@ CGContextRef SkiaBitLocker::cgContext() {
   cgContext_ = CGBitmapContextCreate(bitmap_.getPixels(), bitmap_.width(),
     bitmap_.height(), 8, bitmap_.rowBytes(), colorSpace, 
     kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
+  DCHECK(cgContext_);
 
-  // Apply device matrix.
-  CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1);
-  contentsTransform = CGAffineTransformTranslate(contentsTransform, 0,
-      -device->height());
-  CGContextConcatCTM(cgContext_, contentsTransform);
-
-  const SkIPoint& pt = device->getOrigin();
-  // Skip applying the clip when not writing directly to device.
-  // They're applied in the offscreen case when the bitmap is drawn.
-  if (useDeviceBits_) {
-      // Apply clip in device coordinates.
-      CGMutablePathRef clipPath = CGPathCreateMutable();
-      const SkRegion& clipRgn = canvas_->getTotalClip();
-      if (clipRgn.isEmpty()) {
-        // CoreGraphics does not consider a newly created path to be empty.
-        // Explicitly set it to empty so the subsequent drawing is clipped out.
-        // It would be better to make the CGContext hidden if there was a CG
-        // call that does that.
-        CGPathAddRect(clipPath, 0, CGRectMake(0, 0, 0, 0));
-      }
-      SkRegion::Iterator iter(clipRgn);
-      const SkIPoint& pt = device->getOrigin();
-      for (; !iter.done(); iter.next()) {
-        SkIRect skRect = iter.rect();
-        skRect.offset(-pt);
-        CGRect cgRect = SkIRectToCGRect(skRect);
-        CGPathAddRect(clipPath, 0, cgRect);
-      }
-      CGContextAddPath(cgContext_, clipPath);
-      CGContextClip(cgContext_);
-      CGPathRelease(clipPath);
-  }
+  SkMatrix matrix = canvas_->getTotalMatrix();
+  matrix.postTranslate(-SkIntToScalar(bitmapOffset_.x()),
+                       -SkIntToScalar(bitmapOffset_.y()));
+  matrix.postScale(bitmapScaleFactor_, -bitmapScaleFactor_);
+  matrix.postTranslate(0, SkIntToScalar(bitmap_.height()));
 
-  // Apply content matrix.
-  SkMatrix skMatrix = canvas_->getTotalMatrix();
-  skMatrix.postTranslate(-SkIntToScalar(pt.fX), -SkIntToScalar(pt.fY));
-  CGAffineTransform affine = SkMatrixToCGAffineTransform(skMatrix);
-  CGContextConcatCTM(cgContext_, affine);
+  CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix));
   
   return cgContext_;
 }
 
+bool SkiaBitLocker::hasEmptyClipRegion() const {
+  return canvas_->isClipEmpty();
+}
+
 }  // namespace gfx