- add sources.
[platform/framework/web/crosswalk.git] / src / skia / ext / bitmap_platform_device_mac.cc
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_data.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 BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
61     CGContextRef bitmap)
62     : bitmap_context_(bitmap),
63       config_dirty_(true),  // Want to load the config next time.
64       transform_(SkMatrix::I()) {
65   SkASSERT(bitmap_context_);
66   // Initialize the clip region to the entire bitmap.
67
68   SkIRect rect;
69   rect.set(0, 0,
70            CGBitmapContextGetWidth(bitmap_context_),
71            CGBitmapContextGetHeight(bitmap_context_));
72   clip_region_ = SkRegion(rect);
73   CGContextRetain(bitmap_context_);
74   // We must save the state once so that we can use the restore/save trick
75   // in LoadConfig().
76   CGContextSaveGState(bitmap_context_);
77 }
78
79 BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
80   if (bitmap_context_)
81     CGContextRelease(bitmap_context_);
82 }
83
84 void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapContext() {
85   SkASSERT(bitmap_context_);
86   CGContextRelease(bitmap_context_);
87   bitmap_context_ = NULL;
88 }
89
90 void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
91     const SkMatrix& transform,
92     const SkRegion& region) {
93   transform_ = transform;
94   clip_region_ = region;
95   config_dirty_ = true;
96 }
97
98 void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
99   if (!config_dirty_ || !bitmap_context_)
100     return;  // Nothing to do.
101   config_dirty_ = false;
102
103   // We must restore and then save the state of the graphics context since the
104   // calls to Load the clipping region to the context are strictly cummulative,
105   // i.e., you can't replace a clip rect, other than with a save/restore.
106   // But this implies that no other changes to the state are done elsewhere.
107   // If we ever get to need to change this, then we must replace the clip rect
108   // calls in LoadClippingRegionToCGContext() with an image mask instead.
109   CGContextRestoreGState(bitmap_context_);
110   CGContextSaveGState(bitmap_context_);
111   LoadTransformToCGContext(bitmap_context_, transform_);
112   LoadClippingRegionToCGContext(bitmap_context_, clip_region_, transform_);
113 }
114
115
116 // We use this static factory function instead of the regular constructor so
117 // that we can create the pixel data before calling the constructor. This is
118 // required so that we can call the base class' constructor with the pixel
119 // data.
120 BitmapPlatformDevice* BitmapPlatformDevice::Create(CGContextRef context,
121                                                    int width,
122                                                    int height,
123                                                    bool is_opaque) {
124   if (RasterDeviceTooBigToAllocate(width, height))
125     return NULL;
126
127   SkBitmap bitmap;
128   // TODO: verify that the CG Context's pixels will have tight rowbytes or pass in the correct
129   // rowbytes for the case when context != NULL.
130   bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0,
131                    is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
132
133   void* data;
134   if (context) {
135     data = CGBitmapContextGetData(context);
136     bitmap.setPixels(data);
137   } else {
138     if (!bitmap.allocPixels())
139       return NULL;
140     data = bitmap.getPixels();
141   }
142
143   // If we were given data, then don't clobber it!
144 #ifndef NDEBUG
145   if (!context && is_opaque) {
146     // To aid in finding bugs, we set the background color to something
147     // obviously wrong so it will be noticable when it is not cleared
148     bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
149   }
150 #endif
151
152   if (!context) {
153     context = CGContextForData(data, width, height);
154     if (!context)
155       return NULL;
156   } else
157     CGContextRetain(context);
158
159   BitmapPlatformDevice* rv = new BitmapPlatformDevice(
160       skia::AdoptRef(new BitmapPlatformDeviceData(context)), bitmap);
161
162   // The device object took ownership of the graphics context with its own
163   // CGContextRetain call.
164   CGContextRelease(context);
165
166   return rv;
167 }
168
169 BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
170                                                            int height,
171                                                            bool is_opaque) {
172   BitmapPlatformDevice* device = Create(NULL, width, height, is_opaque);
173   if (!is_opaque)
174     device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
175   return device;
176 }
177
178 BitmapPlatformDevice* BitmapPlatformDevice::CreateWithData(uint8_t* data,
179                                                            int width,
180                                                            int height,
181                                                            bool is_opaque) {
182   CGContextRef context = NULL;
183   if (data)
184     context = CGContextForData(data, width, height);
185
186   BitmapPlatformDevice* rv = Create(context, width, height, is_opaque);
187
188   // The device object took ownership of the graphics context with its own
189   // CGContextRetain call.
190   if (context)
191     CGContextRelease(context);
192
193   return rv;
194 }
195
196 // The device will own the bitmap, which corresponds to also owning the pixel
197 // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
198 BitmapPlatformDevice::BitmapPlatformDevice(
199     const skia::RefPtr<BitmapPlatformDeviceData>& data, const SkBitmap& bitmap)
200     : SkBitmapDevice(bitmap),
201       data_(data) {
202   SetPlatformDevice(this, this);
203 }
204
205 BitmapPlatformDevice::~BitmapPlatformDevice() {
206 }
207
208 CGContextRef BitmapPlatformDevice::GetBitmapContext() {
209   data_->LoadConfig();
210   return data_->bitmap_context();
211 }
212
213 void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
214                                          const SkRegion& region,
215                                          const SkClipStack&) {
216   data_->SetMatrixClip(transform, region);
217 }
218
219 void BitmapPlatformDevice::DrawToNativeContext(CGContextRef context, int x,
220                                                int y, const CGRect* src_rect) {
221   bool created_dc = false;
222   if (!data_->bitmap_context()) {
223     created_dc = true;
224     GetBitmapContext();
225   }
226
227   // this should not make a copy of the bits, since we're not doing
228   // anything to trigger copy on write
229   CGImageRef image = CGBitmapContextCreateImage(data_->bitmap_context());
230   CGRect bounds;
231   bounds.origin.x = x;
232   bounds.origin.y = y;
233   if (src_rect) {
234     bounds.size.width = src_rect->size.width;
235     bounds.size.height = src_rect->size.height;
236     CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect);
237     CGContextDrawImage(context, bounds, sub_image);
238     CGImageRelease(sub_image);
239   } else {
240     bounds.size.width = width();
241     bounds.size.height = height();
242     CGContextDrawImage(context, bounds, image);
243   }
244   CGImageRelease(image);
245
246   if (created_dc)
247     data_->ReleaseBitmapContext();
248 }
249
250 SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
251     SkBitmap::Config config, int width, int height, bool isOpaque,
252     Usage /*usage*/) {
253   SkASSERT(config == SkBitmap::kARGB_8888_Config);
254   SkBaseDevice* bitmap_device = BitmapPlatformDevice::CreateAndClear(width, 
255                                                                      height,
256                                                                      isOpaque);
257   return bitmap_device;
258 }
259
260 // PlatformCanvas impl
261
262 SkCanvas* CreatePlatformCanvas(CGContextRef ctx, int width, int height,
263                                bool is_opaque, OnFailureType failureType) {
264   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
265       BitmapPlatformDevice::Create(ctx, width, height, is_opaque));
266   return CreateCanvas(dev, failureType);
267 }
268
269 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
270                                uint8_t* data, OnFailureType failureType) {
271   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
272       BitmapPlatformDevice::CreateWithData(data, width, height, is_opaque));
273   return CreateCanvas(dev, failureType);
274 }
275
276 // Port of PlatformBitmap to mac
277
278 PlatformBitmap::~PlatformBitmap() {
279   if (surface_)
280     CGContextRelease(surface_);
281 }
282
283 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
284   if (RasterDeviceTooBigToAllocate(width, height))
285     return false;
286     
287   bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, width * 4,
288                     is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
289   if (!bitmap_.allocPixels())
290       return false;
291
292   if (!is_opaque)
293     bitmap_.eraseColor(0);
294
295   surface_ = CGContextForData(bitmap_.getPixels(), bitmap_.width(),
296                               bitmap_.height());
297   return true;
298 }
299
300 }  // namespace skia