| OLD | NEW | 
|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "skia/ext/skia_utils_mac.h" | 5 #include "skia/ext/skia_utils_mac.h" | 
| 6 | 6 | 
| 7 #import <AppKit/AppKit.h> | 7 #import <AppKit/AppKit.h> | 
| 8 #include <stdint.h> | 8 #include <stdint.h> | 
| 9 | 9 | 
| 10 #include <memory> | 10 #include <memory> | 
| 11 | 11 | 
| 12 #include "base/logging.h" | 12 #include "base/logging.h" | 
| 13 #include "base/mac/scoped_cftyperef.h" | 13 #include "base/mac/scoped_cftyperef.h" | 
| 14 #include "base/mac/scoped_nsobject.h" | 14 #include "base/mac/scoped_nsobject.h" | 
| 15 #include "skia/ext/bitmap_platform_device_mac.h" |  | 
| 16 #include "skia/ext/platform_canvas.h" | 15 #include "skia/ext/platform_canvas.h" | 
| 17 #include "third_party/skia/include/utils/mac/SkCGUtils.h" | 16 #include "third_party/skia/include/utils/mac/SkCGUtils.h" | 
| 18 | 17 | 
| 19 namespace { | 18 namespace { | 
| 20 | 19 | 
| 21 // Draws an NSImage or an NSImageRep with a given size into a SkBitmap. | 20 // Draws an NSImage or an NSImageRep with a given size into a SkBitmap. | 
| 22 SkBitmap NSImageOrNSImageRepToSkBitmapWithColorSpace( | 21 SkBitmap NSImageOrNSImageRepToSkBitmapWithColorSpace( | 
| 23     NSImage* image, | 22     NSImage* image, | 
| 24     NSImageRep* image_rep, | 23     NSImageRep* image_rep, | 
| 25     NSSize size, | 24     NSSize size, | 
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 184                                 count:4]; | 183                                 count:4]; | 
| 185 } | 184 } | 
| 186 | 185 | 
| 187 SkBitmap CGImageToSkBitmap(CGImageRef image) { | 186 SkBitmap CGImageToSkBitmap(CGImageRef image) { | 
| 188   if (!image) | 187   if (!image) | 
| 189     return SkBitmap(); | 188     return SkBitmap(); | 
| 190 | 189 | 
| 191   int width = CGImageGetWidth(image); | 190   int width = CGImageGetWidth(image); | 
| 192   int height = CGImageGetHeight(image); | 191   int height = CGImageGetHeight(image); | 
| 193 | 192 | 
| 194   sk_sp<skia::BitmapPlatformDevice> device( | 193   sk_sp<SkCanvas> canvas(skia::CreatePlatformCanvas( | 
| 195       skia::BitmapPlatformDevice::Create(NULL, width, height, false)); | 194       nullptr, width, height, false, RETURN_NULL_ON_FAILURE)); | 
| 196 |  | 
| 197   sk_sp<SkCanvas> canvas(skia::CreateCanvas(device, RETURN_NULL_ON_FAILURE)); |  | 
| 198   ScopedPlatformPaint p(canvas.get()); | 195   ScopedPlatformPaint p(canvas.get()); | 
| 199   CGContextRef context = p.GetPlatformSurface(); | 196   CGContextRef context = p.GetPlatformSurface(); | 
| 200 | 197 | 
| 201   // We need to invert the y-axis of the canvas so that Core Graphics drawing | 198   // We need to invert the y-axis of the canvas so that Core Graphics drawing | 
| 202   // happens right-side up. Skia has an upper-left origin and CG has a lower- | 199   // happens right-side up. Skia has an upper-left origin and CG has a lower- | 
| 203   // left one. | 200   // left one. | 
| 204   CGContextScaleCTM(context, 1.0, -1.0); | 201   CGContextScaleCTM(context, 1.0, -1.0); | 
| 205   CGContextTranslateCTM(context, 0, -height); | 202   CGContextTranslateCTM(context, 0, -height); | 
| 206 | 203 | 
| 207   // We want to copy transparent pixels from |image|, instead of blending it | 204   // We want to copy transparent pixels from |image|, instead of blending it | 
| 208   // onto uninitialized pixels. | 205   // onto uninitialized pixels. | 
| 209   CGContextSetBlendMode(context, kCGBlendModeCopy); | 206   CGContextSetBlendMode(context, kCGBlendModeCopy); | 
| 210 | 207 | 
| 211   CGRect rect = CGRectMake(0, 0, width, height); | 208   CGRect rect = CGRectMake(0, 0, width, height); | 
| 212   CGContextDrawImage(context, rect, image); | 209   CGContextDrawImage(context, rect, image); | 
| 213 | 210 | 
| 214   // Because |device| will be cleaned up and will take its pixels with it, we | 211   return skia::ReadPixels(canvas.get()); | 
| 215   // copy it to the stack and return it. |  | 
| 216   SkBitmap bitmap = device->accessBitmap(false); |  | 
| 217 |  | 
| 218   return bitmap; |  | 
| 219 } | 212 } | 
| 220 | 213 | 
| 221 SkBitmap NSImageToSkBitmapWithColorSpace( | 214 SkBitmap NSImageToSkBitmapWithColorSpace( | 
| 222     NSImage* image, bool is_opaque, CGColorSpaceRef color_space) { | 215     NSImage* image, bool is_opaque, CGColorSpaceRef color_space) { | 
| 223   return NSImageOrNSImageRepToSkBitmapWithColorSpace( | 216   return NSImageOrNSImageRepToSkBitmapWithColorSpace( | 
| 224       image, nil, [image size], is_opaque, color_space); | 217       image, nil, [image size], is_opaque, color_space); | 
| 225 } | 218 } | 
| 226 | 219 | 
| 227 SkBitmap NSImageRepToSkBitmapWithColorSpace(NSImageRep* image_rep, | 220 SkBitmap NSImageRepToSkBitmapWithColorSpace(NSImageRep* image_rep, | 
| 228                                             NSSize size, | 221                                             NSSize size, | 
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 262   [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())]; | 255   [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())]; | 
| 263   return [image.release() autorelease]; | 256   return [image.release() autorelease]; | 
| 264 } | 257 } | 
| 265 | 258 | 
| 266 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { | 259 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { | 
| 267   base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( | 260   base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( | 
| 268       CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); | 261       CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); | 
| 269   return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); | 262   return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); | 
| 270 } | 263 } | 
| 271 | 264 | 
| 272 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas) |  | 
| 273     : canvas_(canvas), |  | 
| 274       userClipRectSpecified_(false), |  | 
| 275       cgContext_(0), |  | 
| 276       bitmapScaleFactor_(1), |  | 
| 277       useDeviceBits_(false), |  | 
| 278       bitmapIsDummy_(false) { |  | 
| 279 } |  | 
| 280 |  | 
| 281 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas, | 265 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas, | 
| 282                              const SkIRect& userClipRect, | 266                              const SkIRect& userClipRect, | 
| 283                              SkScalar bitmapScaleFactor) | 267                              SkScalar bitmapScaleFactor) | 
| 284     : canvas_(canvas), | 268     : canvas_(canvas), | 
| 285       userClipRectSpecified_(true), |  | 
| 286       cgContext_(0), | 269       cgContext_(0), | 
| 287       bitmapScaleFactor_(bitmapScaleFactor), | 270       bitmapScaleFactor_(bitmapScaleFactor), | 
| 288       useDeviceBits_(false), | 271       useDeviceBits_(false), | 
| 289       bitmapIsDummy_(false) { | 272       bitmapIsDummy_(false) { | 
| 290   canvas_->save(); | 273   canvas_->save(); | 
| 291   canvas_->clipRect(SkRect::MakeFromIRect(userClipRect)); | 274   canvas_->clipRect(SkRect::MakeFromIRect(userClipRect)); | 
| 292 } | 275 } | 
| 293 | 276 | 
| 294 SkiaBitLocker::~SkiaBitLocker() { | 277 SkiaBitLocker::~SkiaBitLocker() { | 
| 295   releaseIfNeeded(); | 278   releaseIfNeeded(); | 
| 296   if (userClipRectSpecified_) | 279   canvas_->restore(); | 
| 297     canvas_->restore(); |  | 
| 298 } | 280 } | 
| 299 | 281 | 
| 300 SkIRect SkiaBitLocker::computeDirtyRect() { | 282 SkIRect SkiaBitLocker::computeDirtyRect() { | 
| 301   // If the user specified a clip region, assume that it was tight and that the | 283   // If the user specified a clip region, assume that it was tight and that the | 
| 302   // dirty rect is approximately the whole bitmap. | 284   // dirty rect is approximately the whole bitmap. | 
| 303   if (userClipRectSpecified_) | 285   return SkIRect::MakeWH(offscreen_.width(), offscreen_.height()); | 
| 304     return SkIRect::MakeWH(offscreen_.width(), offscreen_.height()); |  | 
| 305 |  | 
| 306   // Find the bits that were drawn to. |  | 
| 307   SkAutoLockPixels lockedPixels(offscreen_); |  | 
| 308   const uint32_t* pixelBase |  | 
| 309       = reinterpret_cast<uint32_t*>(offscreen_.getPixels()); |  | 
| 310   int rowPixels = offscreen_.rowBytesAsPixels(); |  | 
| 311   int width = offscreen_.width(); |  | 
| 312   int height = offscreen_.height(); |  | 
| 313   SkIRect bounds; |  | 
| 314   bounds.fTop = 0; |  | 
| 315   int x; |  | 
| 316   int y = -1; |  | 
| 317   const uint32_t* pixels = pixelBase; |  | 
| 318   while (++y < height) { |  | 
| 319     for (x = 0; x < width; ++x) { |  | 
| 320       if (pixels[x]) { |  | 
| 321         bounds.fTop = y; |  | 
| 322         goto foundTop; |  | 
| 323       } |  | 
| 324     } |  | 
| 325     pixels += rowPixels; |  | 
| 326   } |  | 
| 327 foundTop: |  | 
| 328   bounds.fBottom = height; |  | 
| 329   y = height; |  | 
| 330   pixels = pixelBase + rowPixels * (y - 1); |  | 
| 331   while (--y > bounds.fTop) { |  | 
| 332     for (x = 0; x < width; ++x) { |  | 
| 333       if (pixels[x]) { |  | 
| 334         bounds.fBottom = y + 1; |  | 
| 335         goto foundBottom; |  | 
| 336       } |  | 
| 337     } |  | 
| 338     pixels -= rowPixels; |  | 
| 339   } |  | 
| 340 foundBottom: |  | 
| 341   bounds.fLeft = 0; |  | 
| 342   x = -1; |  | 
| 343   while (++x < width) { |  | 
| 344     pixels = pixelBase + rowPixels * bounds.fTop; |  | 
| 345     for (y = bounds.fTop; y < bounds.fBottom; ++y) { |  | 
| 346       if (pixels[x]) { |  | 
| 347         bounds.fLeft = x; |  | 
| 348         goto foundLeft; |  | 
| 349       } |  | 
| 350       pixels += rowPixels; |  | 
| 351     } |  | 
| 352   } |  | 
| 353 foundLeft: |  | 
| 354   bounds.fRight = width; |  | 
| 355   x = width; |  | 
| 356   while (--x > bounds.fLeft) { |  | 
| 357     pixels = pixelBase + rowPixels * bounds.fTop; |  | 
| 358     for (y = bounds.fTop; y < bounds.fBottom; ++y) { |  | 
| 359       if (pixels[x]) { |  | 
| 360         bounds.fRight = x + 1; |  | 
| 361         goto foundRight; |  | 
| 362       } |  | 
| 363       pixels += rowPixels; |  | 
| 364     } |  | 
| 365   } |  | 
| 366 foundRight: |  | 
| 367   return bounds; |  | 
| 368 } | 286 } | 
| 369 | 287 | 
| 370 // This must be called to balance calls to cgContext | 288 // This must be called to balance calls to cgContext | 
| 371 void SkiaBitLocker::releaseIfNeeded() { | 289 void SkiaBitLocker::releaseIfNeeded() { | 
| 372   if (!cgContext_) | 290   if (!cgContext_) | 
| 373     return; | 291     return; | 
| 374   if (!useDeviceBits_ && !bitmapIsDummy_) { | 292   if (!useDeviceBits_ && !bitmapIsDummy_) { | 
| 375     // Find the bits that were drawn to. | 293     // Find the bits that were drawn to. | 
| 376     SkIRect bounds = computeDirtyRect(); | 294     SkIRect bounds = computeDirtyRect(); | 
| 377     SkBitmap subset; | 295     SkBitmap subset; | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 396 CGContextRef SkiaBitLocker::cgContext() { | 314 CGContextRef SkiaBitLocker::cgContext() { | 
| 397   SkIRect clip_bounds; | 315   SkIRect clip_bounds; | 
| 398   if (!canvas_->getClipDeviceBounds(&clip_bounds)) { | 316   if (!canvas_->getClipDeviceBounds(&clip_bounds)) { | 
| 399     // If the clip is empty, then there is nothing to draw. The caller may | 317     // If the clip is empty, then there is nothing to draw. The caller may | 
| 400     // attempt to draw (to-be-clipped) results, so ensure there is a dummy | 318     // attempt to draw (to-be-clipped) results, so ensure there is a dummy | 
| 401     // non-NULL CGContext to use. | 319     // non-NULL CGContext to use. | 
| 402     bitmapIsDummy_ = true; | 320     bitmapIsDummy_ = true; | 
| 403     clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); | 321     clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); | 
| 404   } | 322   } | 
| 405 | 323 | 
| 406   SkBaseDevice* device = canvas_->getTopDevice(); |  | 
| 407   DCHECK(device); |  | 
| 408   if (!device) |  | 
| 409     return 0; |  | 
| 410 |  | 
| 411   releaseIfNeeded(); // This flushes any prior bitmap use | 324   releaseIfNeeded(); // This flushes any prior bitmap use | 
| 412 | 325 | 
| 413   // remember the top/left, in case we need to compose this later | 326   // remember the top/left, in case we need to compose this later | 
| 414   bitmapOffset_.set(clip_bounds.x(), clip_bounds.y()); | 327   bitmapOffset_.set(clip_bounds.x(), clip_bounds.y()); | 
| 415 | 328 | 
| 416   // Now make clip_bounds be relative to the current layer/device | 329   // Now make clip_bounds be relative to the current layer/device | 
| 417   clip_bounds.offset(-device->getOrigin()); | 330   if (!bitmapIsDummy_) { | 
|  | 331     canvas_->temporary_internal_describeTopLayer(nullptr, &clip_bounds); | 
|  | 332   } | 
| 418 | 333 | 
| 419   SkPixmap devicePixels; | 334   SkPixmap devicePixels; | 
| 420   skia::GetWritablePixels(canvas_, &devicePixels); | 335   skia::GetWritablePixels(canvas_, &devicePixels); | 
| 421 | 336 | 
| 422   // Only draw directly if we have pixels, and we're only rect-clipped. | 337   // Only draw directly if we have pixels, and we're only rect-clipped. | 
| 423   // If not, we allocate an offscreen and draw into that, relying on the | 338   // If not, we allocate an offscreen and draw into that, relying on the | 
| 424   // compositing step to apply skia's clip. | 339   // compositing step to apply skia's clip. | 
| 425   useDeviceBits_ = devicePixels.addr() && | 340   useDeviceBits_ = devicePixels.addr() && | 
| 426                    canvas_->isClipRect() && | 341                    canvas_->isClipRect() && | 
| 427                    !bitmapIsDummy_; | 342                    !bitmapIsDummy_; | 
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 463   CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix)); | 378   CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix)); | 
| 464 | 379 | 
| 465   return cgContext_; | 380   return cgContext_; | 
| 466 } | 381 } | 
| 467 | 382 | 
| 468 bool SkiaBitLocker::hasEmptyClipRegion() const { | 383 bool SkiaBitLocker::hasEmptyClipRegion() const { | 
| 469   return canvas_->isClipEmpty(); | 384   return canvas_->isClipEmpty(); | 
| 470 } | 385 } | 
| 471 | 386 | 
| 472 }  // namespace skia | 387 }  // namespace skia | 
| OLD | NEW | 
|---|