Home | History | Annotate | Download | only in ext
      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
      3 // found in the LICENSE file.
      4 
      5 #include "skia/ext/bitmap_platform_device_mac.h"
      6 
      7 #import <ApplicationServices/ApplicationServices.h>
      8 #include <time.h>
      9 
     10 #include "base/mac/mac_util.h"
     11 #include "base/memory/ref_counted.h"
     12 #include "skia/ext/bitmap_platform_device.h"
     13 #include "skia/ext/platform_canvas.h"
     14 #include "skia/ext/skia_utils_mac.h"
     15 #include "third_party/skia/include/core/SkMatrix.h"
     16 #include "third_party/skia/include/core/SkRegion.h"
     17 #include "third_party/skia/include/core/SkTypes.h"
     18 #include "third_party/skia/include/core/SkUtils.h"
     19 
     20 namespace skia {
     21 
     22 namespace {
     23 
     24 static CGContextRef CGContextForData(void* data, int width, int height) {
     25 #define HAS_ARGB_SHIFTS(a, r, g, b) \
     26             (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
     27              && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
     28 #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
     29   // Allocate a bitmap context with 4 components per pixel (BGRA).  Apple
     30   // recommends these flags for improved CG performance.
     31 
     32   // CGBitmapContextCreate returns NULL if width/height are 0. However, our
     33   // callers expect to get a canvas back (which they later resize/reallocate)
     34   // so we pin the dimensions here.
     35   width = SkMax32(1, width);
     36   height = SkMax32(1, height);
     37   CGContextRef context =
     38       CGBitmapContextCreate(data, width, height, 8, width * 4,
     39                             base::mac::GetSystemColorSpace(),
     40                             kCGImageAlphaPremultipliedFirst |
     41                                 kCGBitmapByteOrder32Host);
     42 #else
     43 #error We require that Skia's and CoreGraphics's recommended \
     44        image memory layout match.
     45 #endif
     46 #undef HAS_ARGB_SHIFTS
     47 
     48   if (!context)
     49     return NULL;
     50 
     51   // Change the coordinate system to match WebCore's
     52   CGContextTranslateCTM(context, 0, height);
     53   CGContextScaleCTM(context, 1.0, -1.0);
     54 
     55   return context;
     56 }
     57 
     58 }  // namespace
     59 
     60 void BitmapPlatformDevice::ReleaseBitmapContext() {
     61   SkASSERT(bitmap_context_);
     62   CGContextRelease(bitmap_context_);
     63   bitmap_context_ = NULL;
     64 }
     65 
     66 void BitmapPlatformDevice::SetMatrixClip(
     67     const SkMatrix& transform,
     68     const SkRegion& region) {
     69   transform_ = transform;
     70   clip_region_ = region;
     71   config_dirty_ = true;
     72 }
     73 
     74 void BitmapPlatformDevice::LoadConfig() {
     75   if (!config_dirty_ || !bitmap_context_)
     76     return;  // Nothing to do.
     77   config_dirty_ = false;
     78 
     79   // We must restore and then save the state of the graphics context since the
     80   // calls to Load the clipping region to the context are strictly cummulative,
     81   // i.e., you can't replace a clip rect, other than with a save/restore.
     82   // But this implies that no other changes to the state are done elsewhere.
     83   // If we ever get to need to change this, then we must replace the clip rect
     84   // calls in LoadClippingRegionToCGContext() with an image mask instead.
     85   CGContextRestoreGState(bitmap_context_);
     86   CGContextSaveGState(bitmap_context_);
     87   LoadTransformToCGContext(bitmap_context_, transform_);
     88   LoadClippingRegionToCGContext(bitmap_context_, clip_region_, transform_);
     89 }
     90 
     91 
     92 // We use this static factory function instead of the regular constructor so
     93 // that we can create the pixel data before calling the constructor. This is
     94 // required so that we can call the base class' constructor with the pixel
     95 // data.
     96 BitmapPlatformDevice* BitmapPlatformDevice::Create(CGContextRef context,
     97                                                    int width,
     98                                                    int height,
     99                                                    bool is_opaque) {
    100   if (RasterDeviceTooBigToAllocate(width, height))
    101     return NULL;
    102 
    103   SkBitmap bitmap;
    104   // TODO: verify that the CG Context's pixels will have tight rowbytes or pass in the correct
    105   // rowbytes for the case when context != NULL.
    106   bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0,
    107                    is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
    108 
    109   void* data;
    110   if (context) {
    111     data = CGBitmapContextGetData(context);
    112     bitmap.setPixels(data);
    113   } else {
    114     if (!bitmap.allocPixels())
    115       return NULL;
    116     data = bitmap.getPixels();
    117   }
    118 
    119   // If we were given data, then don't clobber it!
    120 #ifndef NDEBUG
    121   if (!context && is_opaque) {
    122     // To aid in finding bugs, we set the background color to something
    123     // obviously wrong so it will be noticable when it is not cleared
    124     bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
    125   }
    126 #endif
    127 
    128   if (!context) {
    129     context = CGContextForData(data, width, height);
    130     if (!context)
    131       return NULL;
    132   } else
    133     CGContextRetain(context);
    134 
    135   BitmapPlatformDevice* rv = new BitmapPlatformDevice(context, bitmap);
    136 
    137   // The device object took ownership of the graphics context with its own
    138   // CGContextRetain call.
    139   CGContextRelease(context);
    140 
    141   return rv;
    142 }
    143 
    144 BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
    145                                                            int height,
    146                                                            bool is_opaque) {
    147   BitmapPlatformDevice* device = Create(NULL, width, height, is_opaque);
    148   if (!is_opaque)
    149     device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
    150   return device;
    151 }
    152 
    153 BitmapPlatformDevice* BitmapPlatformDevice::CreateWithData(uint8_t* data,
    154                                                            int width,
    155                                                            int height,
    156                                                            bool is_opaque) {
    157   CGContextRef context = NULL;
    158   if (data)
    159     context = CGContextForData(data, width, height);
    160 
    161   BitmapPlatformDevice* rv = Create(context, width, height, is_opaque);
    162 
    163   // The device object took ownership of the graphics context with its own
    164   // CGContextRetain call.
    165   if (context)
    166     CGContextRelease(context);
    167 
    168   return rv;
    169 }
    170 
    171 // The device will own the bitmap, which corresponds to also owning the pixel
    172 // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
    173 BitmapPlatformDevice::BitmapPlatformDevice(
    174     CGContextRef context, const SkBitmap& bitmap)
    175     : SkBitmapDevice(bitmap),
    176       bitmap_context_(context),
    177       config_dirty_(true),  // Want to load the config next time.
    178       transform_(SkMatrix::I()) {
    179   SetPlatformDevice(this, this);
    180   SkASSERT(bitmap_context_);
    181   // Initialize the clip region to the entire bitmap.
    182 
    183   SkIRect rect;
    184   rect.set(0, 0,
    185            CGBitmapContextGetWidth(bitmap_context_),
    186            CGBitmapContextGetHeight(bitmap_context_));
    187   clip_region_ = SkRegion(rect);
    188   CGContextRetain(bitmap_context_);
    189   // We must save the state once so that we can use the restore/save trick
    190   // in LoadConfig().
    191   CGContextSaveGState(bitmap_context_);
    192 }
    193 
    194 BitmapPlatformDevice::~BitmapPlatformDevice() {
    195   if (bitmap_context_)
    196     CGContextRelease(bitmap_context_);
    197 }
    198 
    199 CGContextRef BitmapPlatformDevice::GetBitmapContext() {
    200   LoadConfig();
    201   return bitmap_context_;
    202 }
    203 
    204 void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
    205                                          const SkRegion& region,
    206                                          const SkClipStack&) {
    207   SetMatrixClip(transform, region);
    208 }
    209 
    210 void BitmapPlatformDevice::DrawToNativeContext(CGContextRef context, int x,
    211                                                int y, const CGRect* src_rect) {
    212   bool created_dc = false;
    213   if (!bitmap_context_) {
    214     created_dc = true;
    215     GetBitmapContext();
    216   }
    217 
    218   // this should not make a copy of the bits, since we're not doing
    219   // anything to trigger copy on write
    220   CGImageRef image = CGBitmapContextCreateImage(bitmap_context_);
    221   CGRect bounds;
    222   bounds.origin.x = x;
    223   bounds.origin.y = y;
    224   if (src_rect) {
    225     bounds.size.width = src_rect->size.width;
    226     bounds.size.height = src_rect->size.height;
    227     CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect);
    228     CGContextDrawImage(context, bounds, sub_image);
    229     CGImageRelease(sub_image);
    230   } else {
    231     bounds.size.width = width();
    232     bounds.size.height = height();
    233     CGContextDrawImage(context, bounds, image);
    234   }
    235   CGImageRelease(image);
    236 
    237   if (created_dc)
    238     ReleaseBitmapContext();
    239 }
    240 
    241 SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
    242     SkBitmap::Config config, int width, int height, bool isOpaque,
    243     Usage /*usage*/) {
    244   SkASSERT(config == SkBitmap::kARGB_8888_Config);
    245   SkBaseDevice* bitmap_device = BitmapPlatformDevice::CreateAndClear(width,
    246                                                                      height,
    247                                                                      isOpaque);
    248   return bitmap_device;
    249 }
    250 
    251 // PlatformCanvas impl
    252 
    253 SkCanvas* CreatePlatformCanvas(CGContextRef ctx, int width, int height,
    254                                bool is_opaque, OnFailureType failureType) {
    255   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
    256       BitmapPlatformDevice::Create(ctx, width, height, is_opaque));
    257   return CreateCanvas(dev, failureType);
    258 }
    259 
    260 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
    261                                uint8_t* data, OnFailureType failureType) {
    262   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
    263       BitmapPlatformDevice::CreateWithData(data, width, height, is_opaque));
    264   return CreateCanvas(dev, failureType);
    265 }
    266 
    267 // Port of PlatformBitmap to mac
    268 
    269 PlatformBitmap::~PlatformBitmap() {
    270   if (surface_)
    271     CGContextRelease(surface_);
    272 }
    273 
    274 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
    275   if (RasterDeviceTooBigToAllocate(width, height))
    276     return false;
    277 
    278   bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, width * 4,
    279                     is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
    280   if (!bitmap_.allocPixels())
    281       return false;
    282 
    283   if (!is_opaque)
    284     bitmap_.eraseColor(0);
    285 
    286   surface_ = CGContextForData(bitmap_.getPixels(), bitmap_.width(),
    287                               bitmap_.height());
    288   return true;
    289 }
    290 
    291 }  // namespace skia
    292