Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(34)

Side by Side Diff: webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceWin.cpp

Issue 11568: Move skia extensions from the port to skia/ext. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 "BitmapPlatformDeviceWin.h"
6
7 #include "base/gfx/gdi_util.h"
8 #include "base/logging.h"
9 #include "SkMatrix.h"
10 #include "SkRegion.h"
11 #include "SkUtils.h"
12
13 namespace gfx {
14
15 // When Windows draws text, is sets the fourth byte (which Skia uses for alpha)
16 // to zero. This means that if we try compositing with text that Windows has
17 // drawn, we get invalid color values (if the alpha is 0, the other channels
18 // should be 0 since Skia uses premultiplied colors) and strange results.
19 //
20 // HTML rendering only requires one bit of transparency. When you ask for a
21 // semitransparent div, the div itself is drawn in another layer as completely
22 // opaque, and then composited onto the lower layer with a transfer function.
23 // The only place an alpha channel is needed is to track what has been drawn
24 // and what has not been drawn.
25 //
26 // Therefore, when we allocate a new device, we fill it with this special
27 // color. Because Skia uses premultiplied colors, any color where the alpha
28 // channel is smaller than any component is impossible, so we know that no
29 // legitimate drawing will produce this color. We use 1 as the alpha value
30 // because 0 is produced when Windows draws text (even though it should be
31 // opaque).
32 //
33 // When a layer is done and we want to render it to a lower layer, we use
34 // fixupAlphaBeforeCompositing. This replaces all 0 alpha channels with
35 // opaque (to fix the text problem), and replaces this magic color value
36 // with transparency. The result is something that can be correctly
37 // composited. However, once this has been done, no more can be drawn to
38 // the layer because fixing the alphas *again* will result in incorrect
39 // values.
40 static const uint32_t kMagicTransparencyColor = 0x01FFFEFD;
41
42 namespace {
43
44 // Constrains position and size to fit within available_size. If |size| is -1,
45 // all the available_size is used. Returns false if the position is out of
46 // available_size.
47 bool Constrain(int available_size, int* position, int *size) {
48 if (*size < -2)
49 return false;
50
51 if (*position < 0) {
52 if (*size != -1)
53 *size += *position;
54 *position = 0;
55 }
56 if (*size == 0 || *position >= available_size)
57 return false;
58
59 if (*size > 0) {
60 int overflow = (*position + *size) - available_size;
61 if (overflow > 0) {
62 *size -= overflow;
63 }
64 } else {
65 // Fill up available size.
66 *size = available_size - *position;
67 }
68 return true;
69 }
70
71 // If the pixel value is 0, it gets set to kMagicTransparencyColor.
72 void PrepareAlphaForGDI(uint32_t* pixel) {
73 if (*pixel == 0) {
74 *pixel = kMagicTransparencyColor;
75 }
76 }
77
78 // If the pixel value is kMagicTransparencyColor, it gets set to 0. Otherwise
79 // if the alpha is 0, the alpha is set to 255.
80 void PostProcessAlphaForGDI(uint32_t* pixel) {
81 if (*pixel == kMagicTransparencyColor) {
82 *pixel = 0;
83 } else if ((*pixel & 0xFF000000) == 0) {
84 *pixel |= 0xFF000000;
85 }
86 }
87
88 // Sets the opacity of the specified value to 0xFF.
89 void MakeOpaqueAlphaAdjuster(uint32_t* pixel) {
90 *pixel |= 0xFF000000;
91 }
92
93 // See the declaration of kMagicTransparencyColor at the top of the file.
94 void FixupAlphaBeforeCompositing(uint32_t* pixel) {
95 if (*pixel == kMagicTransparencyColor)
96 *pixel = 0;
97 else
98 *pixel |= 0xFF000000;
99 }
100
101 } // namespace
102
103 class BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData
104 : public base::RefCounted<BitmapPlatformDeviceWinData> {
105 public:
106 explicit BitmapPlatformDeviceWinData(HBITMAP hbitmap);
107
108 // Create/destroy hdc_, which is the memory DC for our bitmap data.
109 HDC GetBitmapDC();
110 void ReleaseBitmapDC();
111 bool IsBitmapDCCreated() const;
112
113 // Sets the transform and clip operations. This will not update the DC,
114 // but will mark the config as dirty. The next call of LoadConfig will
115 // pick up these changes.
116 void SetMatrixClip(const SkMatrix& transform, const SkRegion& region);
117
118 const SkMatrix& transform() const {
119 return transform_;
120 }
121
122 protected:
123 // Loads the current transform and clip into the DC. Can be called even when
124 // the DC is NULL (will be a NOP).
125 void LoadConfig();
126
127 // Windows bitmap corresponding to our surface.
128 HBITMAP hbitmap_;
129
130 // Lazily-created DC used to draw into the bitmap, see getBitmapDC.
131 HDC hdc_;
132
133 // True when there is a transform or clip that has not been set to the DC.
134 // The DC is retrieved for every text operation, and the transform and clip
135 // do not change as much. We can save time by not loading the clip and
136 // transform for every one.
137 bool config_dirty_;
138
139 // Translation assigned to the DC: we need to keep track of this separately
140 // so it can be updated even if the DC isn't created yet.
141 SkMatrix transform_;
142
143 // The current clipping
144 SkRegion clip_region_;
145
146 private:
147 friend class base::RefCounted<BitmapPlatformDeviceWinData>;
148 ~BitmapPlatformDeviceWinData();
149
150 DISALLOW_EVIL_CONSTRUCTORS(BitmapPlatformDeviceWinData);
151 };
152
153 BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::BitmapPlatformDeviceWinDat a(
154 HBITMAP hbitmap)
155 : hbitmap_(hbitmap),
156 hdc_(NULL),
157 config_dirty_(true) { // Want to load the config next time.
158 // Initialize the clip region to the entire bitmap.
159 BITMAP bitmap_data;
160 if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) {
161 SkIRect rect;
162 rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
163 clip_region_ = SkRegion(rect);
164 }
165
166 transform_.reset();
167 }
168
169 BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::~BitmapPlatformDeviceWinDa ta() {
170 if (hdc_)
171 ReleaseBitmapDC();
172
173 // this will free the bitmap data as well as the bitmap handle
174 DeleteObject(hbitmap_);
175 }
176
177 HDC BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::GetBitmapDC() {
178 if (!hdc_) {
179 hdc_ = CreateCompatibleDC(NULL);
180 InitializeDC(hdc_);
181 HGDIOBJ old_bitmap = SelectObject(hdc_, hbitmap_);
182 // When the memory DC is created, its display surface is exactly one
183 // monochrome pixel wide and one monochrome pixel high. Since we select our
184 // own bitmap, we must delete the previous one.
185 DeleteObject(old_bitmap);
186 }
187
188 LoadConfig();
189 return hdc_;
190 }
191
192 void BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::ReleaseBitmapDC() {
193 DCHECK(hdc_);
194 DeleteDC(hdc_);
195 hdc_ = NULL;
196 }
197
198 bool BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::IsBitmapDCCreated()
199 const {
200 return hdc_ != NULL;
201 }
202
203
204 void BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::SetMatrixClip(
205 const SkMatrix& transform,
206 const SkRegion& region) {
207 transform_ = transform;
208 clip_region_ = region;
209 config_dirty_ = true;
210 }
211
212 void BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::LoadConfig() {
213 if (!config_dirty_ || !hdc_)
214 return; // Nothing to do.
215 config_dirty_ = false;
216
217 // Transform.
218 SkMatrix t(transform_);
219 LoadTransformToDC(hdc_, t);
220 // We don't use transform_ for the clipping region since the translation is
221 // already applied to offset_x_ and offset_y_.
222 t.reset();
223 LoadClippingRegionToDC(hdc_, clip_region_, t);
224 }
225
226 // We use this static factory function instead of the regular constructor so
227 // that we can create the pixel data before calling the constructor. This is
228 // required so that we can call the base class' constructor with the pixel
229 // data.
230 BitmapPlatformDeviceWin* BitmapPlatformDeviceWin::create(HDC screen_dc,
231 int width,
232 int height,
233 bool is_opaque,
234 HANDLE shared_section) {
235 SkBitmap bitmap;
236
237 // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
238 // just create a minimal bitmap
239 if ((width == 0) || (height == 0)) {
240 width = 1;
241 height = 1;
242 }
243
244 BITMAPINFOHEADER hdr = {0};
245 CreateBitmapHeader(width, height, &hdr);
246
247 void* data = NULL;
248 HBITMAP hbitmap = CreateDIBSection(screen_dc,
249 reinterpret_cast<BITMAPINFO*>(&hdr), 0,
250 &data,
251 shared_section, 0);
252
253 // If we run out of GDI objects or some other error occurs, we won't get a
254 // bitmap here. This will cause us to crash later because the data pointer is
255 // NULL. To make sure that we can assign blame for those crashes to this code,
256 // we deliberately crash here, even in release mode.
257 if (!hbitmap) {
258 DWORD error = GetLastError();
259 LOG(ERROR) << "CreateDIBSection Failed. Error: " << error << "\n";
260 return NULL;
261 }
262
263 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
264 bitmap.setPixels(data);
265 bitmap.setIsOpaque(is_opaque);
266
267 if (is_opaque) {
268 #ifndef NDEBUG
269 // To aid in finding bugs, we set the background color to something
270 // obviously wrong so it will be noticable when it is not cleared
271 bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green
272 #endif
273 } else {
274 // A transparent layer is requested: fill with our magic "transparent"
275 // color, see the declaration of kMagicTransparencyColor above
276 sk_memset32(static_cast<uint32_t*>(data), kMagicTransparencyColor,
277 width * height);
278 }
279
280 // The device object will take ownership of the HBITMAP.
281 return new BitmapPlatformDeviceWin(new BitmapPlatformDeviceWinData(hbitmap),
282 bitmap);
283 }
284
285 // The device will own the HBITMAP, which corresponds to also owning the pixel
286 // data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
287 BitmapPlatformDeviceWin::BitmapPlatformDeviceWin(
288 BitmapPlatformDeviceWinData* data,
289 const SkBitmap& bitmap) : PlatformDeviceWin(bitmap), data_(data) {
290 }
291
292 // The copy constructor just adds another reference to the underlying data.
293 // We use a const cast since the default Skia definitions don't define the
294 // proper constedness that we expect (accessBitmap should really be const).
295 BitmapPlatformDeviceWin::BitmapPlatformDeviceWin(
296 const BitmapPlatformDeviceWin& other)
297 : PlatformDeviceWin(
298 const_cast<BitmapPlatformDeviceWin&>(other).accessBitmap(true)),
299 data_(other.data_) {
300 }
301
302 BitmapPlatformDeviceWin::~BitmapPlatformDeviceWin() {
303 }
304
305 BitmapPlatformDeviceWin& BitmapPlatformDeviceWin::operator=(
306 const BitmapPlatformDeviceWin& other) {
307 data_ = other.data_;
308 return *this;
309 }
310
311 HDC BitmapPlatformDeviceWin::getBitmapDC() {
312 return data_->GetBitmapDC();
313 }
314
315 void BitmapPlatformDeviceWin::setMatrixClip(const SkMatrix& transform,
316 const SkRegion& region) {
317 data_->SetMatrixClip(transform, region);
318 }
319
320 void BitmapPlatformDeviceWin::drawToHDC(HDC dc, int x, int y,
321 const RECT* src_rect) {
322 bool created_dc = !data_->IsBitmapDCCreated();
323 HDC source_dc = getBitmapDC();
324
325 RECT temp_rect;
326 if (!src_rect) {
327 temp_rect.left = 0;
328 temp_rect.right = width();
329 temp_rect.top = 0;
330 temp_rect.bottom = height();
331 src_rect = &temp_rect;
332 }
333
334 int copy_width = src_rect->right - src_rect->left;
335 int copy_height = src_rect->bottom - src_rect->top;
336
337 // We need to reset the translation for our bitmap or (0,0) won't be in the
338 // upper left anymore
339 SkMatrix identity;
340 identity.reset();
341
342 LoadTransformToDC(source_dc, identity);
343 if (isOpaque()) {
344 BitBlt(dc,
345 x,
346 y,
347 copy_width,
348 copy_height,
349 source_dc,
350 src_rect->left,
351 src_rect->top,
352 SRCCOPY);
353 } else {
354 DCHECK(copy_width != 0 && copy_height != 0);
355 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
356 GdiAlphaBlend(dc,
357 x,
358 y,
359 copy_width,
360 copy_height,
361 source_dc,
362 src_rect->left,
363 src_rect->top,
364 copy_width,
365 copy_height,
366 blend_function);
367 }
368 LoadTransformToDC(source_dc, data_->transform());
369
370 if (created_dc)
371 data_->ReleaseBitmapDC();
372 }
373
374 void BitmapPlatformDeviceWin::prepareForGDI(int x, int y, int width,
375 int height) {
376 processPixels<PrepareAlphaForGDI>(x, y, width, height);
377 }
378
379 void BitmapPlatformDeviceWin::postProcessGDI(int x, int y, int width,
380 int height) {
381 processPixels<PostProcessAlphaForGDI>(x, y, width, height);
382 }
383
384 void BitmapPlatformDeviceWin::makeOpaque(int x, int y, int width, int height) {
385 processPixels<MakeOpaqueAlphaAdjuster>(x, y, width, height);
386 }
387
388 void BitmapPlatformDeviceWin::fixupAlphaBeforeCompositing() {
389 const SkBitmap& bitmap = accessBitmap(true);
390 SkAutoLockPixels lock(bitmap);
391 uint32_t* data = bitmap.getAddr32(0, 0);
392
393 size_t words = bitmap.rowBytes() / sizeof(uint32_t) * bitmap.height();
394 for (size_t i = 0; i < words; i++) {
395 if (data[i] == kMagicTransparencyColor)
396 data[i] = 0;
397 else
398 data[i] |= 0xFF000000;
399 }
400 }
401
402 // Returns the color value at the specified location.
403 SkColor BitmapPlatformDeviceWin::getColorAt(int x, int y) {
404 const SkBitmap& bitmap = accessBitmap(false);
405 SkAutoLockPixels lock(bitmap);
406 uint32_t* data = bitmap.getAddr32(0, 0);
407 return static_cast<SkColor>(data[x + y * width()]);
408 }
409
410 void BitmapPlatformDeviceWin::onAccessBitmap(SkBitmap* bitmap) {
411 // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
412 // operation has occurred on our DC.
413 if (data_->IsBitmapDCCreated())
414 GdiFlush();
415 }
416
417 template<BitmapPlatformDeviceWin::adjustAlpha adjustor>
418 void BitmapPlatformDeviceWin::processPixels(int x,
419 int y,
420 int width,
421 int height) {
422 const SkBitmap& bitmap = accessBitmap(true);
423 DCHECK_EQ(bitmap.config(), SkBitmap::kARGB_8888_Config);
424 const SkMatrix& matrix = data_->transform();
425 int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x;
426 int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y;
427
428 if (Constrain(bitmap.width(), &bitmap_start_x, &width) &&
429 Constrain(bitmap.height(), &bitmap_start_y, &height)) {
430 SkAutoLockPixels lock(bitmap);
431 DCHECK_EQ(bitmap.rowBytes() % sizeof(uint32_t), 0u);
432 size_t row_words = bitmap.rowBytes() / sizeof(uint32_t);
433 // Set data to the first pixel to be modified.
434 uint32_t* data = bitmap.getAddr32(0, 0) + (bitmap_start_y * row_words) +
435 bitmap_start_x;
436 for (int i = 0; i < height; i++) {
437 for (int j = 0; j < width; j++) {
438 adjustor(data + j);
439 }
440 data += row_words;
441 }
442 }
443 }
444
445 } // namespace gfx
446
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698