OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 <time.h> | |
6 | |
7 #include "SkMatrix.h" | |
8 #include "SkRegion.h" | |
9 #include "SkUtils.h" | |
10 | |
11 #include "base/gfx/bitmap_platform_device_mac.h" | |
12 #include "base/gfx/skia_utils_mac.h" | |
13 #include "base/logging.h" | |
14 | |
15 namespace gfx { | |
16 | |
17 namespace { | |
18 | |
19 // Constrains position and size to fit within available_size. If |size| is -1, | |
20 // all the |available_size| is used. Returns false if the position is out of | |
21 // |available_size|. | |
22 bool Constrain(int available_size, int* position, int *size) { | |
23 if (*size < -2) | |
24 return false; | |
25 | |
26 if (*position < 0) { | |
27 if (*size != -1) | |
28 *size += *position; | |
29 *position = 0; | |
30 } | |
31 if (*size == 0 || *position >= available_size) | |
32 return false; | |
33 | |
34 if (*size > 0) { | |
35 int overflow = (*position + *size) - available_size; | |
36 if (overflow > 0) { | |
37 *size -= overflow; | |
38 } | |
39 } else { | |
40 // Fill up available size. | |
41 *size = available_size - *position; | |
42 } | |
43 return true; | |
44 } | |
45 | |
46 } // namespace | |
47 | |
48 class BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData | |
49 : public base::RefCounted<BitmapPlatformDeviceMacData> { | |
50 public: | |
51 explicit BitmapPlatformDeviceMacData(CGContextRef bitmap); | |
52 | |
53 // Create/destroy CoreGraphics context for our bitmap data. | |
54 CGContextRef GetBitmapContext() { | |
55 LoadConfig(); | |
56 return bitmap_context_; | |
57 } | |
58 | |
59 void ReleaseBitmapContext() { | |
60 DCHECK(bitmap_context_); | |
61 CGContextRelease(bitmap_context_); | |
62 bitmap_context_ = NULL; | |
63 } | |
64 | |
65 // Sets the transform and clip operations. This will not update the CGContext, | |
66 // but will mark the config as dirty. The next call of LoadConfig will | |
67 // pick up these changes. | |
68 void SetMatrixClip(const SkMatrix& transform, const SkRegion& region); | |
69 | |
70 // Loads the current transform and clip into the DC. Can be called even when | |
71 // |bitmap_context_| is NULL (will be a NOP). | |
72 void LoadConfig(); | |
73 | |
74 // Lazily-created graphics context used to draw into the bitmap. | |
75 CGContextRef bitmap_context_; | |
76 | |
77 // True when there is a transform or clip that has not been set to the | |
78 // CGContext. The CGContext is retrieved for every text operation, and the | |
79 // transform and clip do not change as much. We can save time by not loading | |
80 // the clip and transform for every one. | |
81 bool config_dirty_; | |
82 | |
83 // Translation assigned to the CGContext: we need to keep track of this | |
84 // separately so it can be updated even if the CGContext isn't created yet. | |
85 SkMatrix transform_; | |
86 | |
87 // The current clipping | |
88 SkRegion clip_region_; | |
89 | |
90 private: | |
91 friend class base::RefCounted<BitmapPlatformDeviceMacData>; | |
92 ~BitmapPlatformDeviceMacData() { | |
93 if (bitmap_context_) | |
94 CGContextRelease(bitmap_context_); | |
95 } | |
96 | |
97 DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDeviceMacData); | |
98 }; | |
99 | |
100 BitmapPlatformDeviceMac::\ | |
101 BitmapPlatformDeviceMacData::BitmapPlatformDeviceMacData( | |
102 CGContextRef bitmap) | |
103 : bitmap_context_(bitmap), | |
104 config_dirty_(true) { // Want to load the config next time. | |
105 DCHECK(bitmap_context_); | |
106 // Initialize the clip region to the entire bitmap. | |
107 | |
108 SkIRect rect; | |
109 rect.set(0, 0, | |
110 CGBitmapContextGetWidth(bitmap_context_), | |
111 CGBitmapContextGetHeight(bitmap_context_)); | |
112 clip_region_ = SkRegion(rect); | |
113 transform_.reset(); | |
114 CGContextRetain(bitmap_context_); | |
115 } | |
116 | |
117 void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::SetMatrixClip( | |
118 const SkMatrix& transform, | |
119 const SkRegion& region) { | |
120 transform_ = transform; | |
121 clip_region_ = region; | |
122 config_dirty_ = true; | |
123 } | |
124 | |
125 void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::LoadConfig() { | |
126 if (!config_dirty_ || !bitmap_context_) | |
127 return; // Nothing to do. | |
128 config_dirty_ = false; | |
129 | |
130 // Transform. | |
131 SkMatrix t(transform_); | |
132 LoadTransformToCGContext(bitmap_context_, t); | |
133 t.setTranslateX(-t.getTranslateX()); | |
134 t.setTranslateY(-t.getTranslateY()); | |
135 LoadClippingRegionToCGContext(bitmap_context_, clip_region_, t); | |
136 } | |
137 | |
138 | |
139 // We use this static factory function instead of the regular constructor so | |
140 // that we can create the pixel data before calling the constructor. This is | |
141 // required so that we can call the base class' constructor with the pixel | |
142 // data. | |
143 BitmapPlatformDeviceMac* BitmapPlatformDeviceMac::Create(CGContextRef context, | |
144 int width, | |
145 int height, | |
146 bool is_opaque) { | |
147 void* data = malloc(height * width * 4); | |
148 if (!data) return NULL; | |
149 | |
150 SkBitmap bitmap; | |
151 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
152 bitmap.setPixels(data); | |
153 | |
154 // Note: The Windows implementation clears the Bitmap later on. | |
155 // This bears mentioning since removal of this line makes the | |
156 // unit tests only fail periodically (or when MallocPreScribble is set). | |
157 bitmap.eraseARGB(0, 0, 0, 0); | |
158 | |
159 bitmap.setIsOpaque(is_opaque); | |
160 | |
161 if (is_opaque) { | |
162 #ifndef NDEBUG | |
163 // To aid in finding bugs, we set the background color to something | |
164 // obviously wrong so it will be noticable when it is not cleared | |
165 bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green | |
166 #endif | |
167 } | |
168 | |
169 CGColorSpaceRef color_space = | |
170 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); | |
171 // allocate a bitmap context with 4 components per pixel (RGBA): | |
172 CGContextRef bitmap_context = | |
173 CGBitmapContextCreate(data, width, height, 8, width*4, | |
174 color_space, kCGImageAlphaPremultipliedLast); | |
175 | |
176 // Change the coordinate system to match WebCore's | |
177 CGContextTranslateCTM(bitmap_context, 0, height); | |
178 CGContextScaleCTM(bitmap_context, 1.0, -1.0); | |
179 CGColorSpaceRelease(color_space); | |
180 | |
181 // The device object will take ownership of the graphics context. | |
182 return new BitmapPlatformDeviceMac( | |
183 new BitmapPlatformDeviceMacData(bitmap_context), bitmap); | |
184 } | |
185 | |
186 // The device will own the bitmap, which corresponds to also owning the pixel | |
187 // data. Therefore, we do not transfer ownership to the SkDevice's bitmap. | |
188 BitmapPlatformDeviceMac::BitmapPlatformDeviceMac( | |
189 BitmapPlatformDeviceMacData* data, const SkBitmap& bitmap) | |
190 : PlatformDeviceMac(bitmap), | |
191 data_(data) { | |
192 } | |
193 | |
194 // The copy constructor just adds another reference to the underlying data. | |
195 // We use a const cast since the default Skia definitions don't define the | |
196 // proper constedness that we expect (accessBitmap should really be const). | |
197 BitmapPlatformDeviceMac::BitmapPlatformDeviceMac( | |
198 const BitmapPlatformDeviceMac& other) | |
199 : PlatformDeviceMac( | |
200 const_cast<BitmapPlatformDeviceMac&>(other).accessBitmap(true)), | |
201 data_(other.data_) { | |
202 } | |
203 | |
204 BitmapPlatformDeviceMac::~BitmapPlatformDeviceMac() { | |
205 } | |
206 | |
207 BitmapPlatformDeviceMac& BitmapPlatformDeviceMac::operator=( | |
208 const BitmapPlatformDeviceMac& other) { | |
209 data_ = other.data_; | |
210 return *this; | |
211 } | |
212 | |
213 CGContextRef BitmapPlatformDeviceMac::GetBitmapContext() { | |
214 return data_->GetBitmapContext(); | |
215 } | |
216 | |
217 void BitmapPlatformDeviceMac::setMatrixClip(const SkMatrix& transform, | |
218 const SkRegion& region) { | |
219 data_->SetMatrixClip(transform, region); | |
220 } | |
221 | |
222 void BitmapPlatformDeviceMac::DrawToContext(CGContextRef context, int x, int y, | |
223 const CGRect* src_rect) { | |
224 bool created_dc = false; | |
225 if (!data_->bitmap_context_) { | |
226 created_dc = true; | |
227 GetBitmapContext(); | |
228 } | |
229 | |
230 // this should not make a copy of the bits, since we're not doing | |
231 // anything to trigger copy on write | |
232 CGImageRef image = CGBitmapContextCreateImage(data_->bitmap_context_); | |
233 CGRect bounds; | |
234 if (src_rect) { | |
235 bounds = *src_rect; | |
236 bounds.origin.x = x; | |
237 bounds.origin.y = y; | |
238 CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect); | |
239 CGContextDrawImage(context, bounds, sub_image); | |
240 CGImageRelease(sub_image); | |
241 } else { | |
242 bounds.origin.x = 0; | |
243 bounds.origin.y = 0; | |
244 bounds.size.width = width(); | |
245 bounds.size.height = height(); | |
246 CGContextDrawImage(context, bounds, image); | |
247 } | |
248 CGImageRelease(image); | |
249 | |
250 if (created_dc) | |
251 data_->ReleaseBitmapContext(); | |
252 } | |
253 | |
254 // Returns the color value at the specified location. | |
255 SkColor BitmapPlatformDeviceMac::getColorAt(int x, int y) { | |
256 const SkBitmap& bitmap = accessBitmap(true); | |
257 SkAutoLockPixels lock(bitmap); | |
258 uint32_t* data = bitmap.getAddr32(0, 0); | |
259 return static_cast<SkColor>(data[x + y * width()]); | |
260 } | |
261 | |
262 void BitmapPlatformDeviceMac::onAccessBitmap(SkBitmap*) { | |
263 // Not needed in CoreGraphics | |
264 } | |
265 | |
266 void BitmapPlatformDeviceMac::processPixels(int x, int y, | |
267 int width, int height, | |
268 adjustAlpha adjustor) { | |
269 const SkBitmap& bitmap = accessBitmap(true); | |
270 SkMatrix& matrix = data_->transform_; | |
271 int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x; | |
272 int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y; | |
273 | |
274 SkAutoLockPixels lock(bitmap); | |
275 if (Constrain(bitmap.width(), &bitmap_start_x, &width) && | |
276 Constrain(bitmap.height(), &bitmap_start_y, &height)) { | |
277 uint32_t* data = bitmap.getAddr32(0, 0); | |
278 size_t row_words = bitmap.rowBytes() / 4; | |
279 for (int i = 0; i < height; i++) { | |
280 size_t offset = (i + bitmap_start_y) * row_words + bitmap_start_x; | |
281 for (int j = 0; j < width; j++) { | |
282 adjustor(data + offset + j); | |
283 } | |
284 } | |
285 } | |
286 } | |
287 | |
288 } // namespace gfx | |
289 | |
OLD | NEW |