blob: 9a1dd5a6525f4b3eadce912644e216218c42dc24 [file] [log] [blame]
[email protected]3b63f8f42011-03-28 01:54:151// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]247e2472008-08-20 19:34:304
[email protected]98abd85552008-11-21 20:32:455#include "skia/ext/bitmap_platform_device_mac.h"
[email protected]2eda1562008-11-19 19:16:116
[email protected]247e2472008-08-20 19:34:307#include <time.h>
8
[email protected]0378bf42011-01-01 18:20:149#include "base/mac/mac_util.h"
[email protected]3b63f8f42011-03-28 01:54:1510#include "base/memory/ref_counted.h"
[email protected]4618ddb2010-08-31 18:40:5511#include "skia/ext/bitmap_platform_device_data.h"
[email protected]24a14e82008-12-03 20:26:5112#include "skia/ext/skia_utils_mac.h"
[email protected]c43c6682009-05-19 14:51:4413#include "third_party/skia/include/core/SkMatrix.h"
14#include "third_party/skia/include/core/SkRegion.h"
15#include "third_party/skia/include/core/SkTypes.h"
16#include "third_party/skia/include/core/SkUtils.h"
[email protected]247e2472008-08-20 19:34:3017
[email protected]21f527e2008-12-17 23:29:4018namespace skia {
[email protected]247e2472008-08-20 19:34:3019
20namespace {
21
[email protected]18ca68b2009-06-15 19:05:4422static CGContextRef CGContextForData(void* data, int width, int height) {
[email protected]8860e4f52009-06-25 01:01:5223#define HAS_ARGB_SHIFTS(a, r, g, b) \
24 (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
25 && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
26#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
[email protected]18ca68b2009-06-15 19:05:4427 // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
28 // recommends these flags for improved CG performance.
29 CGContextRef context =
30 CGBitmapContextCreate(data, width, height, 8, width * 4,
[email protected]0378bf42011-01-01 18:20:1431 base::mac::GetSystemColorSpace(),
[email protected]18ca68b2009-06-15 19:05:4432 kCGImageAlphaPremultipliedFirst |
33 kCGBitmapByteOrder32Host);
[email protected]8860e4f52009-06-25 01:01:5234#else
35#error We require that Skia's and CoreGraphics's recommended \
36 image memory layout match.
37#endif
38#undef HAS_ARGB_SHIFTS
[email protected]18ca68b2009-06-15 19:05:4439
40 if (!context)
41 return NULL;
42
43 // Change the coordinate system to match WebCore's
44 CGContextTranslateCTM(context, 0, height);
45 CGContextScaleCTM(context, 1.0, -1.0);
46
47 return context;
48}
49
[email protected]21f527e2008-12-17 23:29:4050} // namespace
[email protected]247e2472008-08-20 19:34:3051
[email protected]f7567982011-01-08 05:02:3052SkDevice* BitmapPlatformDeviceFactory::newDevice(SkCanvas* ignored,
53 SkBitmap::Config config,
[email protected]56b9875e2010-10-27 16:56:5754 int width, int height,
55 bool isOpaque,
56 bool isForLayer) {
[email protected]9ffeb6672010-10-15 22:27:2557 SkASSERT(config == SkBitmap::kARGB_8888_Config);
58 return BitmapPlatformDevice::Create(NULL, width, height, isOpaque);
59}
60
[email protected]bd63dc92009-06-14 15:14:5361BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
[email protected]247e2472008-08-20 19:34:3062 CGContextRef bitmap)
63 : bitmap_context_(bitmap),
[email protected]247e2472008-08-20 19:34:3064 config_dirty_(true) { // Want to load the config next time.
[email protected]87080c62009-01-14 16:58:5565 SkASSERT(bitmap_context_);
[email protected]21f527e2008-12-17 23:29:4066 // Initialize the clip region to the entire bitmap.
67
[email protected]247e2472008-08-20 19:34:3068 SkIRect rect;
69 rect.set(0, 0,
70 CGBitmapContextGetWidth(bitmap_context_),
71 CGBitmapContextGetHeight(bitmap_context_));
[email protected]6e9d0ce2008-09-29 23:40:0272 clip_region_ = SkRegion(rect);
73 transform_.reset();
[email protected]247e2472008-08-20 19:34:3074 CGContextRetain(bitmap_context_);
[email protected]87ef84842009-06-10 21:41:5475 // We must save the state once so that we can use the restore/save trick
76 // in LoadConfig().
77 CGContextSaveGState(bitmap_context_);
[email protected]247e2472008-08-20 19:34:3078}
79
[email protected]4618ddb2010-08-31 18:40:5580BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
81 if (bitmap_context_)
82 CGContextRelease(bitmap_context_);
83}
84
[email protected]bd63dc92009-06-14 15:14:5385void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
[email protected]6e9d0ce2008-09-29 23:40:0286 const SkMatrix& transform,
[email protected]247e2472008-08-20 19:34:3087 const SkRegion& region) {
[email protected]6e9d0ce2008-09-29 23:40:0288 transform_ = transform;
[email protected]247e2472008-08-20 19:34:3089 clip_region_ = region;
90 config_dirty_ = true;
91}
92
[email protected]bd63dc92009-06-14 15:14:5393void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
[email protected]247e2472008-08-20 19:34:3094 if (!config_dirty_ || !bitmap_context_)
95 return; // Nothing to do.
96 config_dirty_ = false;
97
[email protected]87ef84842009-06-10 21:41:5498 // We must restore and then save the state of the graphics context since the
99 // calls to Load the clipping region to the context are strictly cummulative,
100 // i.e., you can't replace a clip rect, other than with a save/restore.
101 // But this implies that no other changes to the state are done elsewhere.
102 // If we ever get to need to change this, then we must replace the clip rect
103 // calls in LoadClippingRegionToCGContext() with an image mask instead.
104 CGContextRestoreGState(bitmap_context_);
105 CGContextSaveGState(bitmap_context_);
[email protected]bff545e62010-07-16 18:49:11106 LoadTransformToCGContext(bitmap_context_, transform_);
107 LoadClippingRegionToCGContext(bitmap_context_, clip_region_, transform_);
[email protected]247e2472008-08-20 19:34:30108}
109
110
111// We use this static factory function instead of the regular constructor so
112// that we can create the pixel data before calling the constructor. This is
113// required so that we can call the base class' constructor with the pixel
114// data.
[email protected]23e891ac2009-06-15 20:04:03115BitmapPlatformDevice* BitmapPlatformDevice::Create(CGContextRef context,
116 int width,
117 int height,
118 bool is_opaque) {
[email protected]247e2472008-08-20 19:34:30119 SkBitmap bitmap;
120 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
[email protected]61e978a2009-03-07 03:45:14121 if (bitmap.allocPixels() != true)
122 return NULL;
[email protected]18ca68b2009-06-15 19:05:44123
124 void* data = NULL;
125 if (context) {
126 data = CGBitmapContextGetData(context);
127 bitmap.setPixels(data);
128 } else {
129 data = bitmap.getPixels();
[email protected]21f527e2008-12-17 23:29:40130
[email protected]4fb66842009-12-04 21:41:00131 // Note: The Windows implementation clears the Bitmap later on.
132 // This bears mentioning since removal of this line makes the
133 // unit tests only fail periodically (or when MallocPreScribble is set).
134 bitmap.eraseARGB(0, 0, 0, 0);
135 }
[email protected]21f527e2008-12-17 23:29:40136
[email protected]247e2472008-08-20 19:34:30137 bitmap.setIsOpaque(is_opaque);
138
[email protected]4fb66842009-12-04 21:41:00139 // If we were given data, then don't clobber it!
[email protected]247e2472008-08-20 19:34:30140#ifndef NDEBUG
[email protected]4fb66842009-12-04 21:41:00141 if (!context && is_opaque) {
[email protected]247e2472008-08-20 19:34:30142 // To aid in finding bugs, we set the background color to something
143 // obviously wrong so it will be noticable when it is not cleared
144 bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green
[email protected]18ca68b2009-06-15 19:05:44145 }
[email protected]247e2472008-08-20 19:34:30146#endif
[email protected]21f527e2008-12-17 23:29:40147
[email protected]18ca68b2009-06-15 19:05:44148 if (!context)
149 context = CGContextForData(data, width, height);
[email protected]60406bc2009-06-15 23:49:18150 else
151 CGContextRetain(context);
[email protected]247e2472008-08-20 19:34:30152
[email protected]60406bc2009-06-15 23:49:18153 BitmapPlatformDevice* rv = new BitmapPlatformDevice(
[email protected]bd63dc92009-06-14 15:14:53154 new BitmapPlatformDeviceData(context), bitmap);
[email protected]60406bc2009-06-15 23:49:18155
156 // The device object took ownership of the graphics context with its own
157 // CGContextRetain call.
158 CGContextRelease(context);
159
160 return rv;
[email protected]247e2472008-08-20 19:34:30161}
162
[email protected]18ca68b2009-06-15 19:05:44163BitmapPlatformDevice* BitmapPlatformDevice::CreateWithData(uint8_t* data,
164 int width,
165 int height,
166 bool is_opaque) {
167 CGContextRef context = NULL;
168 if (data)
169 context = CGContextForData(data, width, height);
170
[email protected]60406bc2009-06-15 23:49:18171 BitmapPlatformDevice* rv = Create(context, width, height, is_opaque);
172
173 // The device object took ownership of the graphics context with its own
174 // CGContextRetain call.
175 if (context)
176 CGContextRelease(context);
177
178 return rv;
[email protected]18ca68b2009-06-15 19:05:44179}
180
[email protected]247e2472008-08-20 19:34:30181// The device will own the bitmap, which corresponds to also owning the pixel
182// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
[email protected]bd63dc92009-06-14 15:14:53183BitmapPlatformDevice::BitmapPlatformDevice(
184 BitmapPlatformDeviceData* data, const SkBitmap& bitmap)
185 : PlatformDevice(bitmap),
[email protected]247e2472008-08-20 19:34:30186 data_(data) {
187}
188
189// The copy constructor just adds another reference to the underlying data.
190// We use a const cast since the default Skia definitions don't define the
191// proper constedness that we expect (accessBitmap should really be const).
[email protected]bd63dc92009-06-14 15:14:53192BitmapPlatformDevice::BitmapPlatformDevice(
193 const BitmapPlatformDevice& other)
194 : PlatformDevice(
195 const_cast<BitmapPlatformDevice&>(other).accessBitmap(true)),
[email protected]247e2472008-08-20 19:34:30196 data_(other.data_) {
[email protected]87080c62009-01-14 16:58:55197 data_->ref();
[email protected]247e2472008-08-20 19:34:30198}
199
[email protected]bd63dc92009-06-14 15:14:53200BitmapPlatformDevice::~BitmapPlatformDevice() {
[email protected]87080c62009-01-14 16:58:55201 data_->unref();
[email protected]247e2472008-08-20 19:34:30202}
203
[email protected]1d20cdf2011-02-16 18:09:28204SkDeviceFactory* BitmapPlatformDevice::getDeviceFactory() {
205 return SkNEW(BitmapPlatformDeviceFactory);
206}
207
[email protected]bd63dc92009-06-14 15:14:53208BitmapPlatformDevice& BitmapPlatformDevice::operator=(
209 const BitmapPlatformDevice& other) {
[email protected]247e2472008-08-20 19:34:30210 data_ = other.data_;
[email protected]87080c62009-01-14 16:58:55211 data_->ref();
[email protected]247e2472008-08-20 19:34:30212 return *this;
213}
214
[email protected]bd63dc92009-06-14 15:14:53215CGContextRef BitmapPlatformDevice::GetBitmapContext() {
[email protected]4618ddb2010-08-31 18:40:55216 data_->LoadConfig();
217 return data_->bitmap_context();
[email protected]247e2472008-08-20 19:34:30218}
219
[email protected]bd63dc92009-06-14 15:14:53220void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
[email protected]ab47c752011-02-28 15:21:12221 const SkRegion& region,
222 const SkClipStack&) {
[email protected]6e9d0ce2008-09-29 23:40:02223 data_->SetMatrixClip(transform, region);
[email protected]247e2472008-08-20 19:34:30224}
225
[email protected]bd63dc92009-06-14 15:14:53226void BitmapPlatformDevice::DrawToContext(CGContextRef context, int x, int y,
227 const CGRect* src_rect) {
[email protected]247e2472008-08-20 19:34:30228 bool created_dc = false;
[email protected]4618ddb2010-08-31 18:40:55229 if (!data_->bitmap_context()) {
[email protected]247e2472008-08-20 19:34:30230 created_dc = true;
231 GetBitmapContext();
232 }
233
234 // this should not make a copy of the bits, since we're not doing
235 // anything to trigger copy on write
[email protected]4618ddb2010-08-31 18:40:55236 CGImageRef image = CGBitmapContextCreateImage(data_->bitmap_context());
[email protected]247e2472008-08-20 19:34:30237 CGRect bounds;
[email protected]6c0006dc2009-07-23 00:11:09238 bounds.origin.x = x;
239 bounds.origin.y = y;
[email protected]247e2472008-08-20 19:34:30240 if (src_rect) {
[email protected]6c0006dc2009-07-23 00:11:09241 bounds.size.width = src_rect->size.width;
242 bounds.size.height = src_rect->size.height;
[email protected]247e2472008-08-20 19:34:30243 CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect);
244 CGContextDrawImage(context, bounds, sub_image);
245 CGImageRelease(sub_image);
246 } else {
[email protected]247e2472008-08-20 19:34:30247 bounds.size.width = width();
248 bounds.size.height = height();
249 CGContextDrawImage(context, bounds, image);
250 }
251 CGImageRelease(image);
252
253 if (created_dc)
254 data_->ReleaseBitmapContext();
255}
[email protected]21f527e2008-12-17 23:29:40256
[email protected]1d20cdf2011-02-16 18:09:28257bool BitmapPlatformDevice::IsVectorial() {
258 return false;
259}
260
[email protected]6e9d0ce2008-09-29 23:40:02261// Returns the color value at the specified location.
[email protected]bd63dc92009-06-14 15:14:53262SkColor BitmapPlatformDevice::getColorAt(int x, int y) {
[email protected]6e9d0ce2008-09-29 23:40:02263 const SkBitmap& bitmap = accessBitmap(true);
264 SkAutoLockPixels lock(bitmap);
265 uint32_t* data = bitmap.getAddr32(0, 0);
266 return static_cast<SkColor>(data[x + y * width()]);
267}
268
[email protected]bd63dc92009-06-14 15:14:53269void BitmapPlatformDevice::onAccessBitmap(SkBitmap*) {
[email protected]6e9d0ce2008-09-29 23:40:02270 // Not needed in CoreGraphics
271}
[email protected]247e2472008-08-20 19:34:30272
[email protected]21f527e2008-12-17 23:29:40273} // namespace skia