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/debug/gdi_debug_util_win.h"
9#include "base/logging.h"
10#include "skia/ext/bitmap_platform_device_win.h"
11#include "skia/ext/platform_canvas.h"
12#include "third_party/skia/include/core/SkMatrix.h"
13#include "third_party/skia/include/core/SkRefCnt.h"
14#include "third_party/skia/include/core/SkRegion.h"
15#include "third_party/skia/include/core/SkUtils.h"
16
17namespace {
18
19HBITMAP CreateHBitmap(int width, int height, bool is_opaque,
20                             HANDLE shared_section, void** data) {
21  // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
22  // just create a minimal bitmap
23  if ((width == 0) || (height == 0)) {
24    width = 1;
25    height = 1;
26  }
27
28  BITMAPINFOHEADER hdr = {0};
29  hdr.biSize = sizeof(BITMAPINFOHEADER);
30  hdr.biWidth = width;
31  hdr.biHeight = -height;  // minus means top-down bitmap
32  hdr.biPlanes = 1;
33  hdr.biBitCount = 32;
34  hdr.biCompression = BI_RGB;  // no compression
35  hdr.biSizeImage = 0;
36  hdr.biXPelsPerMeter = 1;
37  hdr.biYPelsPerMeter = 1;
38  hdr.biClrUsed = 0;
39  hdr.biClrImportant = 0;
40
41  HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
42                                     0, data, shared_section, 0);
43
44#if !defined(_WIN64)
45  // If this call fails, we're gonna crash hard. Try to get some useful
46  // information out before we crash for post-mortem analysis.
47  if (!hbitmap)
48    base::debug::GDIBitmapAllocFailure(&hdr, shared_section);
49#endif
50
51  return hbitmap;
52}
53
54}  // namespace
55
56namespace skia {
57
58HDC BitmapPlatformDevice::GetBitmapDC() {
59  if (!hdc_) {
60    hdc_ = CreateCompatibleDC(NULL);
61    InitializeDC(hdc_);
62    old_hbitmap_ = static_cast<HBITMAP>(SelectObject(hdc_, hbitmap_));
63  }
64
65  LoadConfig();
66  return hdc_;
67}
68
69void BitmapPlatformDevice::ReleaseBitmapDC() {
70  SkASSERT(hdc_);
71  SelectObject(hdc_, old_hbitmap_);
72  DeleteDC(hdc_);
73  hdc_ = NULL;
74  old_hbitmap_ = NULL;
75}
76
77bool BitmapPlatformDevice::IsBitmapDCCreated()
78    const {
79  return hdc_ != NULL;
80}
81
82
83void BitmapPlatformDevice::SetMatrixClip(
84    const SkMatrix& transform,
85    const SkRegion& region) {
86  transform_ = transform;
87  clip_region_ = region;
88  config_dirty_ = true;
89}
90
91void BitmapPlatformDevice::LoadConfig() {
92  if (!config_dirty_ || !hdc_)
93    return;  // Nothing to do.
94  config_dirty_ = false;
95
96  // Transform.
97  LoadTransformToDC(hdc_, transform_);
98  LoadClippingRegionToDC(hdc_, clip_region_, transform_);
99}
100
101static void DeleteHBitmapCallback(void* addr, void* context) {
102  DeleteObject(static_cast<HBITMAP>(context));
103}
104
105static bool InstallHBitmapPixels(SkBitmap* bitmap, int width, int height,
106                                 bool is_opaque, void* data, HBITMAP hbitmap) {
107  const SkAlphaType at = is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
108  const SkImageInfo info = SkImageInfo::MakeN32(width, height, at);
109  const size_t rowBytes = info.minRowBytes();
110  SkColorTable* color_table = NULL;
111  return bitmap->installPixels(info, data, rowBytes, color_table,
112                               DeleteHBitmapCallback, hbitmap);
113}
114
115// We use this static factory function instead of the regular constructor so
116// that we can create the pixel data before calling the constructor. This is
117// required so that we can call the base class' constructor with the pixel
118// data.
119BitmapPlatformDevice* BitmapPlatformDevice::Create(
120    int width,
121    int height,
122    bool is_opaque,
123    HANDLE shared_section) {
124
125  void* data;
126  HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section,
127                                  &data);
128  if (!hbitmap)
129    return NULL;
130
131  SkBitmap bitmap;
132  if (!InstallHBitmapPixels(&bitmap, width, height, is_opaque, data, hbitmap))
133    return NULL;
134
135#ifndef NDEBUG
136  // If we were given data, then don't clobber it!
137  if (!shared_section && is_opaque)
138    // To aid in finding bugs, we set the background color to something
139    // obviously wrong so it will be noticable when it is not cleared
140    bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
141#endif
142
143  // The device object will take ownership of the HBITMAP. The initial refcount
144  // of the data object will be 1, which is what the constructor expects.
145  return new BitmapPlatformDevice(hbitmap, bitmap);
146}
147
148// static
149BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
150                                                   bool is_opaque) {
151  return Create(width, height, is_opaque, NULL);
152}
153
154// static
155BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
156                                                           int height,
157                                                           bool is_opaque) {
158  BitmapPlatformDevice* device = BitmapPlatformDevice::Create(width, height,
159                                                              is_opaque);
160  if (device && !is_opaque)
161    device->clear(0);
162  return device;
163}
164
165// The device will own the HBITMAP, which corresponds to also owning the pixel
166// data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
167BitmapPlatformDevice::BitmapPlatformDevice(
168    HBITMAP hbitmap,
169    const SkBitmap& bitmap)
170    : SkBitmapDevice(bitmap),
171      hbitmap_(hbitmap),
172      old_hbitmap_(NULL),
173      hdc_(NULL),
174      config_dirty_(true),  // Want to load the config next time.
175      transform_(SkMatrix::I()) {
176  // The data object is already ref'ed for us by create().
177  SkDEBUGCODE(begin_paint_count_ = 0);
178  SetPlatformDevice(this, this);
179  // Initialize the clip region to the entire bitmap.
180  BITMAP bitmap_data;
181  if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) {
182    SkIRect rect;
183    rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
184    clip_region_ = SkRegion(rect);
185  }
186}
187
188BitmapPlatformDevice::~BitmapPlatformDevice() {
189  SkASSERT(begin_paint_count_ == 0);
190  if (hdc_)
191    ReleaseBitmapDC();
192}
193
194HDC BitmapPlatformDevice::BeginPlatformPaint() {
195  SkDEBUGCODE(begin_paint_count_++);
196  return GetBitmapDC();
197}
198
199void BitmapPlatformDevice::EndPlatformPaint() {
200  SkASSERT(begin_paint_count_--);
201  PlatformDevice::EndPlatformPaint();
202}
203
204void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
205                                         const SkRegion& region,
206                                         const SkClipStack&) {
207  SetMatrixClip(transform, region);
208}
209
210void BitmapPlatformDevice::DrawToNativeContext(HDC dc, int x, int y,
211                                               const RECT* src_rect) {
212  bool created_dc = !IsBitmapDCCreated();
213  HDC source_dc = BeginPlatformPaint();
214
215  RECT temp_rect;
216  if (!src_rect) {
217    temp_rect.left = 0;
218    temp_rect.right = width();
219    temp_rect.top = 0;
220    temp_rect.bottom = height();
221    src_rect = &temp_rect;
222  }
223
224  int copy_width = src_rect->right - src_rect->left;
225  int copy_height = src_rect->bottom - src_rect->top;
226
227  // We need to reset the translation for our bitmap or (0,0) won't be in the
228  // upper left anymore
229  SkMatrix identity;
230  identity.reset();
231
232  LoadTransformToDC(source_dc, identity);
233  if (isOpaque()) {
234    BitBlt(dc,
235           x,
236           y,
237           copy_width,
238           copy_height,
239           source_dc,
240           src_rect->left,
241           src_rect->top,
242           SRCCOPY);
243  } else {
244    SkASSERT(copy_width != 0 && copy_height != 0);
245    BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
246    GdiAlphaBlend(dc,
247                  x,
248                  y,
249                  copy_width,
250                  copy_height,
251                  source_dc,
252                  src_rect->left,
253                  src_rect->top,
254                  copy_width,
255                  copy_height,
256                  blend_function);
257  }
258  LoadTransformToDC(source_dc, transform_);
259
260  EndPlatformPaint();
261  if (created_dc)
262    ReleaseBitmapDC();
263}
264
265const SkBitmap& BitmapPlatformDevice::onAccessBitmap() {
266  // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
267  // operation has occurred on our DC.
268  if (IsBitmapDCCreated())
269    GdiFlush();
270  return SkBitmapDevice::onAccessBitmap();
271}
272
273SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info,
274                                                   Usage /*usage*/) {
275  SkASSERT(info.colorType() == kPMColor_SkColorType);
276  return BitmapPlatformDevice::CreateAndClear(info.width(), info.height(),
277                                              info.isOpaque());
278}
279
280// PlatformCanvas impl
281
282SkCanvas* CreatePlatformCanvas(int width,
283                               int height,
284                               bool is_opaque,
285                               HANDLE shared_section,
286                               OnFailureType failureType) {
287  skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
288      BitmapPlatformDevice::Create(width, height, is_opaque, shared_section));
289  return CreateCanvas(dev, failureType);
290}
291
292// Port of PlatformBitmap to win
293
294PlatformBitmap::~PlatformBitmap() {
295  if (surface_) {
296    if (platform_extra_)
297      SelectObject(surface_, reinterpret_cast<HGDIOBJ>(platform_extra_));
298    DeleteDC(surface_);
299  }
300}
301
302bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
303  void* data;
304  HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, 0, &data);
305  if (!hbitmap)
306    return false;
307
308  surface_ = CreateCompatibleDC(NULL);
309  InitializeDC(surface_);
310  // When the memory DC is created, its display surface is exactly one
311  // monochrome pixel wide and one monochrome pixel high. Save this object
312  // off, we'll restore it just before deleting the memory DC.
313  HGDIOBJ stock_bitmap = SelectObject(surface_, hbitmap);
314  platform_extra_ = reinterpret_cast<intptr_t>(stock_bitmap);
315
316  if (!InstallHBitmapPixels(&bitmap_, width, height, is_opaque, data, hbitmap))
317    return false;
318  bitmap_.lockPixels();
319
320  return true;
321}
322
323}  // namespace skia
324