bitmap_platform_device_win.cc revision f2477e01787aa58f445919b809d89e252beef54f
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 <windows.h>
6#include <psapi.h>
7
8#include "base/logging.h"
9#include "base/debug/alias.h"
10#include "skia/ext/bitmap_platform_device_win.h"
11#include "skia/ext/bitmap_platform_device_data.h"
12#include "skia/ext/platform_canvas.h"
13#include "third_party/skia/include/core/SkMatrix.h"
14#include "third_party/skia/include/core/SkRefCnt.h"
15#include "third_party/skia/include/core/SkRegion.h"
16#include "third_party/skia/include/core/SkUtils.h"
17
18namespace {
19
20// PlatformBitmapPixelRef is an SkPixelRef that, on Windows, is backed by an
21// HBITMAP.
22class SK_API PlatformBitmapPixelRef : public SkPixelRef {
23 public:
24  PlatformBitmapPixelRef(HBITMAP bitmap_handle, void* pixels);
25  virtual ~PlatformBitmapPixelRef();
26
27  SK_DECLARE_UNFLATTENABLE_OBJECT();
28
29 protected:
30  virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
31  virtual void onUnlockPixels() SK_OVERRIDE;
32
33 private:
34  HBITMAP bitmap_handle_;
35  void* pixels_;
36};
37
38HBITMAP CreateHBitmap(int width, int height, bool is_opaque,
39                             HANDLE shared_section, void** data) {
40  // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
41  // just create a minimal bitmap
42  if ((width == 0) || (height == 0)) {
43    width = 1;
44    height = 1;
45  }
46
47  BITMAPINFOHEADER hdr = {0};
48  hdr.biSize = sizeof(BITMAPINFOHEADER);
49  hdr.biWidth = width;
50  hdr.biHeight = -height;  // minus means top-down bitmap
51  hdr.biPlanes = 1;
52  hdr.biBitCount = 32;
53  hdr.biCompression = BI_RGB;  // no compression
54  hdr.biSizeImage = 0;
55  hdr.biXPelsPerMeter = 1;
56  hdr.biYPelsPerMeter = 1;
57  hdr.biClrUsed = 0;
58  hdr.biClrImportant = 0;
59
60  HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
61                                     0, data, shared_section, 0);
62
63  // If this call fails, we're gonna crash hard. Try to get some useful
64  // information out before we crash for post-mortem analysis.
65  if (!hbitmap) {
66    // Make sure parameters are saved in the minidump.
67    base::debug::Alias(&width);
68    base::debug::Alias(&height);
69
70    int last_error = GetLastError();
71    base::debug::Alias(&last_error);
72
73    int num_gdi_handles = GetGuiResources(GetCurrentProcess(),
74                                          GR_GDIOBJECTS);
75    if (num_gdi_handles == 0) {
76      int get_gui_resources_error = GetLastError();
77      base::debug::Alias(&get_gui_resources_error);
78      CHECK(false);
79    }
80
81    base::debug::Alias(&num_gdi_handles);
82    const int kLotsOfHandles = 9990;
83    if (num_gdi_handles > kLotsOfHandles)
84      CHECK(false);
85
86    PROCESS_MEMORY_COUNTERS_EX pmc;
87    pmc.cb = sizeof(pmc);
88    if (!GetProcessMemoryInfo(GetCurrentProcess(),
89                              reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
90                              sizeof(pmc))) {
91      CHECK(false);
92    }
93    const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
94    if (pmc.PagefileUsage > kLotsOfMemory)
95      CHECK(false);
96    if (pmc.PrivateUsage > kLotsOfMemory)
97      CHECK(false);
98
99    // Huh, that's weird.  We don't have crazy handle count, we don't have
100    // ridiculous memory usage.  Try to allocate a small bitmap and see if that
101    // fails too.
102    hdr.biWidth = 5;
103    hdr.biHeight = 5;
104    void* small_data;
105    HBITMAP small_bitmap = CreateDIBSection(
106        NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
107        0, &small_data, shared_section, 0);
108    if (!small_bitmap)
109      CHECK(false);
110    BITMAP bitmap_data;
111    if (GetObject(small_bitmap, sizeof(BITMAP), &bitmap_data)) {
112      if (!DeleteObject(small_bitmap))
113        CHECK(false);
114    }
115    // No idea what's going on. Die!
116    CHECK(false);
117  }
118  return hbitmap;
119}
120
121PlatformBitmapPixelRef::PlatformBitmapPixelRef(HBITMAP bitmap_handle,
122                                               void* pixels)
123    : bitmap_handle_(bitmap_handle),
124      pixels_(pixels) {
125  setPreLocked(pixels, NULL);
126}
127
128PlatformBitmapPixelRef::~PlatformBitmapPixelRef() {
129  if (bitmap_handle_)
130    DeleteObject(bitmap_handle_);
131}
132
133void* PlatformBitmapPixelRef::onLockPixels(SkColorTable** color_table) {
134  *color_table = NULL;
135  return pixels_;
136}
137
138void PlatformBitmapPixelRef::onUnlockPixels() {
139  // Nothing to do.
140  return;
141}
142
143}  // namespace
144
145namespace skia {
146
147BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
148    HBITMAP hbitmap)
149    : bitmap_context_(hbitmap),
150      hdc_(NULL),
151      config_dirty_(true),  // Want to load the config next time.
152      transform_(SkMatrix::I()) {
153  // Initialize the clip region to the entire bitmap.
154  BITMAP bitmap_data;
155  if (GetObject(bitmap_context_, sizeof(BITMAP), &bitmap_data)) {
156    SkIRect rect;
157    rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
158    clip_region_ = SkRegion(rect);
159  }
160}
161
162BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
163  if (hdc_)
164    ReleaseBitmapDC();
165
166  // this will free the bitmap data as well as the bitmap handle
167  DeleteObject(bitmap_context_);
168}
169
170HDC BitmapPlatformDevice::BitmapPlatformDeviceData::GetBitmapDC() {
171  if (!hdc_) {
172    hdc_ = CreateCompatibleDC(NULL);
173    InitializeDC(hdc_);
174    HGDIOBJ old_bitmap = SelectObject(hdc_, bitmap_context_);
175    // When the memory DC is created, its display surface is exactly one
176    // monochrome pixel wide and one monochrome pixel high. Since we select our
177    // own bitmap, we must delete the previous one.
178    DeleteObject(old_bitmap);
179  }
180
181  LoadConfig();
182  return hdc_;
183}
184
185void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC() {
186  SkASSERT(hdc_);
187  DeleteDC(hdc_);
188  hdc_ = NULL;
189}
190
191bool BitmapPlatformDevice::BitmapPlatformDeviceData::IsBitmapDCCreated()
192    const {
193  return hdc_ != NULL;
194}
195
196
197void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
198    const SkMatrix& transform,
199    const SkRegion& region) {
200  transform_ = transform;
201  clip_region_ = region;
202  config_dirty_ = true;
203}
204
205void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
206  if (!config_dirty_ || !hdc_)
207    return;  // Nothing to do.
208  config_dirty_ = false;
209
210  // Transform.
211  LoadTransformToDC(hdc_, transform_);
212  LoadClippingRegionToDC(hdc_, clip_region_, transform_);
213}
214
215// We use this static factory function instead of the regular constructor so
216// that we can create the pixel data before calling the constructor. This is
217// required so that we can call the base class' constructor with the pixel
218// data.
219BitmapPlatformDevice* BitmapPlatformDevice::Create(
220    int width,
221    int height,
222    bool is_opaque,
223    HANDLE shared_section) {
224
225  void* data;
226  HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section,
227                                  &data);
228  if (!hbitmap)
229    return NULL;
230
231  SkBitmap bitmap;
232  bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0,
233                   is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
234  bitmap.setPixels(data);
235
236#ifndef NDEBUG
237  // If we were given data, then don't clobber it!
238  if (!shared_section && is_opaque)
239    // To aid in finding bugs, we set the background color to something
240    // obviously wrong so it will be noticable when it is not cleared
241    bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
242#endif
243
244  // The device object will take ownership of the HBITMAP. The initial refcount
245  // of the data object will be 1, which is what the constructor expects.
246  return new BitmapPlatformDevice(
247      skia::AdoptRef(new BitmapPlatformDeviceData(hbitmap)), bitmap);
248}
249
250// static
251BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
252                                                   bool is_opaque) {
253  return Create(width, height, is_opaque, NULL);
254}
255
256// static
257BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
258                                                           int height,
259                                                           bool is_opaque) {
260  BitmapPlatformDevice* device = BitmapPlatformDevice::Create(width, height,
261                                                              is_opaque);
262  if (device && !is_opaque)
263    device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
264  return device;
265}
266
267// The device will own the HBITMAP, which corresponds to also owning the pixel
268// data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
269BitmapPlatformDevice::BitmapPlatformDevice(
270    const skia::RefPtr<BitmapPlatformDeviceData>& data,
271    const SkBitmap& bitmap)
272    : SkBitmapDevice(bitmap),
273      data_(data) {
274  // The data object is already ref'ed for us by create().
275  SkDEBUGCODE(begin_paint_count_ = 0);
276  SetPlatformDevice(this, this);
277}
278
279BitmapPlatformDevice::~BitmapPlatformDevice() {
280  SkASSERT(begin_paint_count_ == 0);
281}
282
283HDC BitmapPlatformDevice::BeginPlatformPaint() {
284  SkDEBUGCODE(begin_paint_count_++);
285  return data_->GetBitmapDC();
286}
287
288void BitmapPlatformDevice::EndPlatformPaint() {
289  SkASSERT(begin_paint_count_--);
290  PlatformDevice::EndPlatformPaint();
291}
292
293void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
294                                         const SkRegion& region,
295                                         const SkClipStack&) {
296  data_->SetMatrixClip(transform, region);
297}
298
299void BitmapPlatformDevice::DrawToNativeContext(HDC dc, int x, int y,
300                                               const RECT* src_rect) {
301  bool created_dc = !data_->IsBitmapDCCreated();
302  HDC source_dc = BeginPlatformPaint();
303
304  RECT temp_rect;
305  if (!src_rect) {
306    temp_rect.left = 0;
307    temp_rect.right = width();
308    temp_rect.top = 0;
309    temp_rect.bottom = height();
310    src_rect = &temp_rect;
311  }
312
313  int copy_width = src_rect->right - src_rect->left;
314  int copy_height = src_rect->bottom - src_rect->top;
315
316  // We need to reset the translation for our bitmap or (0,0) won't be in the
317  // upper left anymore
318  SkMatrix identity;
319  identity.reset();
320
321  LoadTransformToDC(source_dc, identity);
322  if (isOpaque()) {
323    BitBlt(dc,
324           x,
325           y,
326           copy_width,
327           copy_height,
328           source_dc,
329           src_rect->left,
330           src_rect->top,
331           SRCCOPY);
332  } else {
333    SkASSERT(copy_width != 0 && copy_height != 0);
334    BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
335    GdiAlphaBlend(dc,
336                  x,
337                  y,
338                  copy_width,
339                  copy_height,
340                  source_dc,
341                  src_rect->left,
342                  src_rect->top,
343                  copy_width,
344                  copy_height,
345                  blend_function);
346  }
347  LoadTransformToDC(source_dc, data_->transform());
348
349  EndPlatformPaint();
350  if (created_dc)
351    data_->ReleaseBitmapDC();
352}
353
354const SkBitmap& BitmapPlatformDevice::onAccessBitmap() {
355  // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
356  // operation has occurred on our DC.
357  if (data_->IsBitmapDCCreated())
358    GdiFlush();
359  return SkBitmapDevice::onAccessBitmap();
360}
361
362SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
363    SkBitmap::Config config, int width, int height, bool isOpaque, Usage) {
364  SkASSERT(config == SkBitmap::kARGB_8888_Config);
365  return BitmapPlatformDevice::CreateAndClear(width, height, isOpaque);
366}
367
368// PlatformCanvas impl
369
370SkCanvas* CreatePlatformCanvas(int width,
371                               int height,
372                               bool is_opaque,
373                               HANDLE shared_section,
374                               OnFailureType failureType) {
375  skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
376      BitmapPlatformDevice::Create(width, height, is_opaque, shared_section));
377  return CreateCanvas(dev, failureType);
378}
379
380// Port of PlatformBitmap to win
381
382PlatformBitmap::~PlatformBitmap() {
383  if (surface_) {
384    if (platform_extra_)
385      SelectObject(surface_, reinterpret_cast<HGDIOBJ>(platform_extra_));
386    DeleteDC(surface_);
387  }
388}
389
390bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
391  void* data;
392  HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, 0, &data);
393  if (!hbitmap)
394    return false;
395
396  surface_ = CreateCompatibleDC(NULL);
397  InitializeDC(surface_);
398  // When the memory DC is created, its display surface is exactly one
399  // monochrome pixel wide and one monochrome pixel high. Save this object
400  // off, we'll restore it just before deleting the memory DC.
401  HGDIOBJ stock_bitmap = SelectObject(surface_, hbitmap);
402  platform_extra_ = reinterpret_cast<intptr_t>(stock_bitmap);
403
404  bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0,
405                    is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
406  // PlatformBitmapPixelRef takes ownership of |hbitmap|.
407  bitmap_.setPixelRef(
408      skia::AdoptRef(new PlatformBitmapPixelRef(hbitmap, data)).get());
409  bitmap_.lockPixels();
410
411  return true;
412}
413
414}  // namespace skia
415