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