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 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/mac/scoped_cftyperef.h" | 10 #include "base/mac/scoped_cftyperef.h" |
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 } | 260 } |
261 | 261 |
262 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { | 262 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { |
263 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( | 263 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( |
264 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); | 264 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); |
265 return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); | 265 return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); |
266 } | 266 } |
267 | 267 |
268 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas) | 268 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas) |
269 : canvas_(canvas), | 269 : canvas_(canvas), |
270 cgContext_(0) { | 270 userClipRectSpecified_(false), |
| 271 cgContext_(0), |
| 272 useDeviceBits_(false), |
| 273 bitmapIsDummy_(false) { |
| 274 } |
| 275 |
| 276 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas, const SkIRect& userClipRect) |
| 277 : canvas_(canvas), |
| 278 userClipRectSpecified_(true), |
| 279 cgContext_(0), |
| 280 useDeviceBits_(false), |
| 281 bitmapIsDummy_(false) { |
| 282 canvas_->save(); |
| 283 canvas_->clipRect(SkRect::MakeFromIRect(userClipRect)); |
271 } | 284 } |
272 | 285 |
273 SkiaBitLocker::~SkiaBitLocker() { | 286 SkiaBitLocker::~SkiaBitLocker() { |
274 releaseIfNeeded(); | 287 releaseIfNeeded(); |
| 288 if (userClipRectSpecified_) |
| 289 canvas_->restore(); |
| 290 } |
| 291 |
| 292 SkIRect SkiaBitLocker::computeDirtyRect() { |
| 293 // If the user specified a clip region, assume that it was tight and that the |
| 294 // dirty rect is approximately the whole bitmap. |
| 295 if (userClipRectSpecified_) |
| 296 return SkIRect::MakeWH(bitmap_.width(), bitmap_.height()); |
| 297 |
| 298 // Find the bits that were drawn to. |
| 299 SkAutoLockPixels lockedPixels(bitmap_); |
| 300 const uint32_t* pixelBase |
| 301 = reinterpret_cast<uint32_t*>(bitmap_.getPixels()); |
| 302 int rowPixels = bitmap_.rowBytesAsPixels(); |
| 303 int width = bitmap_.width(); |
| 304 int height = bitmap_.height(); |
| 305 SkIRect bounds; |
| 306 bounds.fTop = 0; |
| 307 int x; |
| 308 int y = -1; |
| 309 const uint32_t* pixels = pixelBase; |
| 310 while (++y < height) { |
| 311 for (x = 0; x < width; ++x) { |
| 312 if (pixels[x]) { |
| 313 bounds.fTop = y; |
| 314 goto foundTop; |
| 315 } |
| 316 } |
| 317 pixels += rowPixels; |
| 318 } |
| 319 foundTop: |
| 320 bounds.fBottom = height; |
| 321 y = height; |
| 322 pixels = pixelBase + rowPixels * (y - 1); |
| 323 while (--y > bounds.fTop) { |
| 324 for (x = 0; x < width; ++x) { |
| 325 if (pixels[x]) { |
| 326 bounds.fBottom = y + 1; |
| 327 goto foundBottom; |
| 328 } |
| 329 } |
| 330 pixels -= rowPixels; |
| 331 } |
| 332 foundBottom: |
| 333 bounds.fLeft = 0; |
| 334 x = -1; |
| 335 while (++x < width) { |
| 336 pixels = pixelBase + rowPixels * bounds.fTop; |
| 337 for (y = bounds.fTop; y < bounds.fBottom; ++y) { |
| 338 if (pixels[x]) { |
| 339 bounds.fLeft = x; |
| 340 goto foundLeft; |
| 341 } |
| 342 pixels += rowPixels; |
| 343 } |
| 344 } |
| 345 foundLeft: |
| 346 bounds.fRight = width; |
| 347 x = width; |
| 348 while (--x > bounds.fLeft) { |
| 349 pixels = pixelBase + rowPixels * bounds.fTop; |
| 350 for (y = bounds.fTop; y < bounds.fBottom; ++y) { |
| 351 if (pixels[x]) { |
| 352 bounds.fRight = x + 1; |
| 353 goto foundRight; |
| 354 } |
| 355 pixels += rowPixels; |
| 356 } |
| 357 } |
| 358 foundRight: |
| 359 return bounds; |
275 } | 360 } |
276 | 361 |
277 // This must be called to balance calls to cgContext | 362 // This must be called to balance calls to cgContext |
278 void SkiaBitLocker::releaseIfNeeded() { | 363 void SkiaBitLocker::releaseIfNeeded() { |
279 if (!cgContext_) | 364 if (!cgContext_) |
280 return; | 365 return; |
281 if (useDeviceBits_) { | 366 if (useDeviceBits_) { |
282 bitmap_.unlockPixels(); | 367 bitmap_.unlockPixels(); |
283 } else { | 368 } else if (!bitmapIsDummy_) { |
284 // Find the bits that were drawn to. | 369 // Find the bits that were drawn to. |
285 SkAutoLockPixels lockedPixels(bitmap_); | 370 SkIRect bounds = computeDirtyRect(); |
286 const uint32_t* pixelBase | |
287 = reinterpret_cast<uint32_t*>(bitmap_.getPixels()); | |
288 int rowPixels = bitmap_.rowBytesAsPixels(); | |
289 int width = bitmap_.width(); | |
290 int height = bitmap_.height(); | |
291 SkIRect bounds; | |
292 bounds.fTop = 0; | |
293 int x; | |
294 int y = -1; | |
295 const uint32_t* pixels = pixelBase; | |
296 while (++y < height) { | |
297 for (x = 0; x < width; ++x) { | |
298 if (pixels[x]) { | |
299 bounds.fTop = y; | |
300 goto foundTop; | |
301 } | |
302 } | |
303 pixels += rowPixels; | |
304 } | |
305 foundTop: | |
306 bounds.fBottom = height; | |
307 y = height; | |
308 pixels = pixelBase + rowPixels * (y - 1); | |
309 while (--y > bounds.fTop) { | |
310 for (x = 0; x < width; ++x) { | |
311 if (pixels[x]) { | |
312 bounds.fBottom = y + 1; | |
313 goto foundBottom; | |
314 } | |
315 } | |
316 pixels -= rowPixels; | |
317 } | |
318 foundBottom: | |
319 bounds.fLeft = 0; | |
320 x = -1; | |
321 while (++x < width) { | |
322 pixels = pixelBase + rowPixels * bounds.fTop; | |
323 for (y = bounds.fTop; y < bounds.fBottom; ++y) { | |
324 if (pixels[x]) { | |
325 bounds.fLeft = x; | |
326 goto foundLeft; | |
327 } | |
328 pixels += rowPixels; | |
329 } | |
330 } | |
331 foundLeft: | |
332 bounds.fRight = width; | |
333 x = width; | |
334 while (--x > bounds.fLeft) { | |
335 pixels = pixelBase + rowPixels * bounds.fTop; | |
336 for (y = bounds.fTop; y < bounds.fBottom; ++y) { | |
337 if (pixels[x]) { | |
338 bounds.fRight = x + 1; | |
339 goto foundRight; | |
340 } | |
341 pixels += rowPixels; | |
342 } | |
343 } | |
344 foundRight: | |
345 SkBitmap subset; | 371 SkBitmap subset; |
346 if (!bitmap_.extractSubset(&subset, bounds)) { | 372 if (!bitmap_.extractSubset(&subset, bounds)) { |
347 return; | 373 return; |
348 } | 374 } |
349 // Neutralize the global matrix by concatenating the inverse. In the | 375 // Neutralize the global matrix by concatenating the inverse. In the |
350 // future, Skia may provide some mechanism to set the device portion of | 376 // future, Skia may provide some mechanism to set the device portion of |
351 // the matrix to identity without clobbering any hosting matrix (e.g., the | 377 // the matrix to identity without clobbering any hosting matrix (e.g., the |
352 // picture's matrix). | 378 // picture's matrix). |
353 const SkMatrix& skMatrix = canvas_->getTotalMatrix(); | 379 const SkMatrix& skMatrix = canvas_->getTotalMatrix(); |
354 SkMatrix inverse; | 380 SkMatrix inverse; |
355 if (!skMatrix.invert(&inverse)) | 381 if (!skMatrix.invert(&inverse)) |
356 return; | 382 return; |
357 canvas_->save(); | 383 canvas_->save(); |
358 canvas_->concat(inverse); | 384 canvas_->concat(inverse); |
359 canvas_->drawBitmap(subset, bounds.x() + bitmapOffset_.x(), | 385 canvas_->drawBitmap(subset, bounds.x() + bitmapOffset_.x(), |
360 bounds.y() + bitmapOffset_.y()); | 386 bounds.y() + bitmapOffset_.y()); |
361 canvas_->restore(); | 387 canvas_->restore(); |
362 } | 388 } |
363 CGContextRelease(cgContext_); | 389 CGContextRelease(cgContext_); |
364 cgContext_ = 0; | 390 cgContext_ = 0; |
| 391 useDeviceBits_ = false; |
| 392 bitmapIsDummy_ = false; |
365 } | 393 } |
366 | 394 |
367 CGContextRef SkiaBitLocker::cgContext() { | 395 CGContextRef SkiaBitLocker::cgContext() { |
368 SkIRect clip_bounds; | 396 SkIRect clip_bounds; |
369 if (!canvas_->getClipDeviceBounds(&clip_bounds)) | 397 if (!canvas_->getClipDeviceBounds(&clip_bounds)) { |
370 return 0; // the clip is empty, nothing to draw | 398 // If the clip is empty, then there is nothing to draw. The caller may |
| 399 // attempt to draw (to-be-clipped) results, so ensure there is a dummy |
| 400 // non-NULL CGContext to use. |
| 401 bitmapIsDummy_ = true; |
| 402 clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); |
| 403 } |
371 | 404 |
372 SkBaseDevice* device = canvas_->getTopDevice(); | 405 SkBaseDevice* device = canvas_->getTopDevice(); |
373 DCHECK(device); | 406 DCHECK(device); |
374 if (!device) | 407 if (!device) |
375 return 0; | 408 return 0; |
376 | 409 |
377 releaseIfNeeded(); // This flushes any prior bitmap use | 410 releaseIfNeeded(); // This flushes any prior bitmap use |
378 | 411 |
379 // remember the top/left, in case we need to compose this later | 412 // remember the top/left, in case we need to compose this later |
380 bitmapOffset_.set(clip_bounds.x(), clip_bounds.y()); | 413 bitmapOffset_.set(clip_bounds.x(), clip_bounds.y()); |
381 | 414 |
382 // Now make clip_bounds be relative to the current layer/device | 415 // Now make clip_bounds be relative to the current layer/device |
383 clip_bounds.offset(-device->getOrigin()); | 416 clip_bounds.offset(-device->getOrigin()); |
384 | 417 |
385 const SkBitmap& deviceBits = device->accessBitmap(true); | 418 const SkBitmap& deviceBits = device->accessBitmap(true); |
386 | 419 |
387 // Only draw directly if we have pixels, and we're only rect-clipped. | 420 // Only draw directly if we have pixels, and we're only rect-clipped. |
388 // If not, we allocate an offscreen and draw into that, relying on the | 421 // If not, we allocate an offscreen and draw into that, relying on the |
389 // compositing step to apply skia's clip. | 422 // compositing step to apply skia's clip. |
390 useDeviceBits_ = deviceBits.getPixels() && canvas_->isClipRect(); | 423 useDeviceBits_ = deviceBits.getPixels() && |
| 424 canvas_->isClipRect() && |
| 425 !bitmapIsDummy_; |
391 if (useDeviceBits_) { | 426 if (useDeviceBits_) { |
392 if (!deviceBits.extractSubset(&bitmap_, clip_bounds)) | 427 bool result = deviceBits.extractSubset(&bitmap_, clip_bounds); |
| 428 DCHECK(result); |
| 429 if (!result) |
393 return 0; | 430 return 0; |
394 bitmap_.lockPixels(); | 431 bitmap_.lockPixels(); |
395 } else { | 432 } else { |
396 if (!bitmap_.allocN32Pixels(clip_bounds.width(), clip_bounds.height())) | 433 bool result = bitmap_.allocN32Pixels( |
| 434 clip_bounds.width(), clip_bounds.height()); |
| 435 DCHECK(result); |
| 436 if (!result) |
397 return 0; | 437 return 0; |
398 bitmap_.eraseColor(0); | 438 bitmap_.eraseColor(0); |
399 } | 439 } |
400 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( | 440 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( |
401 CGColorSpaceCreateDeviceRGB()); | 441 CGColorSpaceCreateDeviceRGB()); |
402 cgContext_ = CGBitmapContextCreate(bitmap_.getPixels(), bitmap_.width(), | 442 cgContext_ = CGBitmapContextCreate(bitmap_.getPixels(), bitmap_.width(), |
403 bitmap_.height(), 8, bitmap_.rowBytes(), colorSpace, | 443 bitmap_.height(), 8, bitmap_.rowBytes(), colorSpace, |
404 kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); | 444 kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); |
| 445 DCHECK(cgContext_); |
405 | 446 |
406 SkMatrix matrix = canvas_->getTotalMatrix(); | 447 SkMatrix matrix = canvas_->getTotalMatrix(); |
407 matrix.postTranslate(-SkIntToScalar(bitmapOffset_.x()), | 448 matrix.postTranslate(-SkIntToScalar(bitmapOffset_.x()), |
408 -SkIntToScalar(bitmapOffset_.y())); | 449 -SkIntToScalar(bitmapOffset_.y())); |
409 matrix.postScale(1, -1); | 450 matrix.postScale(1, -1); |
410 matrix.postTranslate(0, SkIntToScalar(bitmap_.height())); | 451 matrix.postTranslate(0, SkIntToScalar(bitmap_.height())); |
411 | 452 |
412 CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix)); | 453 CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix)); |
413 | 454 |
414 return cgContext_; | 455 return cgContext_; |
415 } | 456 } |
416 | 457 |
| 458 bool SkiaBitLocker::hasEmptyClipRegion() const { |
| 459 return canvas_->isClipEmpty(); |
| 460 } |
| 461 |
417 } // namespace gfx | 462 } // namespace gfx |
OLD | NEW |