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.
8 #include "base/logging.h"
9 #include "base/debug/alias.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"
19 // PlatformBitmapPixelRef is an SkPixelRef that, on Windows, is backed by an
21 class SK_API PlatformBitmapPixelRef
: public SkPixelRef
{
23 PlatformBitmapPixelRef(const SkImageInfo
& info
, HBITMAP bitmap_handle
,
25 virtual ~PlatformBitmapPixelRef();
27 SK_DECLARE_UNFLATTENABLE_OBJECT();
30 virtual void* onLockPixels(SkColorTable
**) SK_OVERRIDE
;
31 virtual void onUnlockPixels() SK_OVERRIDE
;
34 HBITMAP bitmap_handle_
;
38 HBITMAP
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)) {
47 BITMAPINFOHEADER hdr
= {0};
48 hdr
.biSize
= sizeof(BITMAPINFOHEADER
);
50 hdr
.biHeight
= -height
; // minus means top-down bitmap
53 hdr
.biCompression
= BI_RGB
; // no compression
55 hdr
.biXPelsPerMeter
= 1;
56 hdr
.biYPelsPerMeter
= 1;
58 hdr
.biClrImportant
= 0;
60 HBITMAP hbitmap
= CreateDIBSection(NULL
, reinterpret_cast<BITMAPINFO
*>(&hdr
),
61 0, data
, shared_section
, 0);
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.
66 // Make sure parameters are saved in the minidump.
67 base::debug::Alias(&width
);
68 base::debug::Alias(&height
);
70 int last_error
= GetLastError();
71 base::debug::Alias(&last_error
);
73 int num_gdi_handles
= GetGuiResources(GetCurrentProcess(),
75 if (num_gdi_handles
== 0) {
76 int get_gui_resources_error
= GetLastError();
77 base::debug::Alias(&get_gui_resources_error
);
81 base::debug::Alias(&num_gdi_handles
);
82 const int kLotsOfHandles
= 9990;
83 if (num_gdi_handles
> kLotsOfHandles
)
86 PROCESS_MEMORY_COUNTERS_EX pmc
;
88 if (!GetProcessMemoryInfo(GetCurrentProcess(),
89 reinterpret_cast<PROCESS_MEMORY_COUNTERS
*>(&pmc
),
93 const size_t kLotsOfMemory
= 1500 * 1024 * 1024; // 1.5GB
94 if (pmc
.PagefileUsage
> kLotsOfMemory
)
96 if (pmc
.PrivateUsage
> kLotsOfMemory
)
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
105 HBITMAP small_bitmap
= CreateDIBSection(
106 NULL
, reinterpret_cast<BITMAPINFO
*>(&hdr
),
107 0, &small_data
, shared_section
, 0);
111 if (GetObject(small_bitmap
, sizeof(BITMAP
), &bitmap_data
)) {
112 if (!DeleteObject(small_bitmap
))
115 // No idea what's going on. Die!
121 PlatformBitmapPixelRef::PlatformBitmapPixelRef(const SkImageInfo
& info
,
122 HBITMAP bitmap_handle
,
125 bitmap_handle_(bitmap_handle
),
127 setPreLocked(pixels
, info
.minRowBytes(), NULL
);
130 PlatformBitmapPixelRef::~PlatformBitmapPixelRef() {
132 DeleteObject(bitmap_handle_
);
135 void* PlatformBitmapPixelRef::onLockPixels(SkColorTable
** color_table
) {
140 void PlatformBitmapPixelRef::onUnlockPixels() {
149 HDC
BitmapPlatformDevice::GetBitmapDC() {
151 hdc_
= CreateCompatibleDC(NULL
);
153 old_hbitmap_
= static_cast<HBITMAP
>(SelectObject(hdc_
, hbitmap_
));
160 void BitmapPlatformDevice::ReleaseBitmapDC() {
162 SelectObject(hdc_
, old_hbitmap_
);
168 bool BitmapPlatformDevice::IsBitmapDCCreated()
174 void BitmapPlatformDevice::SetMatrixClip(
175 const SkMatrix
& transform
,
176 const SkRegion
& region
) {
177 transform_
= transform
;
178 clip_region_
= region
;
179 config_dirty_
= true;
182 void BitmapPlatformDevice::LoadConfig() {
183 if (!config_dirty_
|| !hdc_
)
184 return; // Nothing to do.
185 config_dirty_
= false;
188 LoadTransformToDC(hdc_
, transform_
);
189 LoadClippingRegionToDC(hdc_
, clip_region_
, transform_
);
192 // We use this static factory function instead of the regular constructor so
193 // that we can create the pixel data before calling the constructor. This is
194 // required so that we can call the base class' constructor with the pixel
196 BitmapPlatformDevice
* BitmapPlatformDevice::Create(
200 HANDLE shared_section
) {
203 HBITMAP hbitmap
= CreateHBitmap(width
, height
, is_opaque
, shared_section
,
208 const SkImageInfo info
= {
211 kPMColor_SkColorType
,
212 is_opaque
? kOpaque_SkAlphaType
: kPremul_SkAlphaType
215 bitmap
.setConfig(info
);
216 RefPtr
<SkPixelRef
> pixel_ref
= AdoptRef(new PlatformBitmapPixelRef(info
,
219 bitmap
.setPixelRef(pixel_ref
.get());
222 // If we were given data, then don't clobber it!
223 if (!shared_section
&& is_opaque
)
224 // To aid in finding bugs, we set the background color to something
225 // obviously wrong so it will be noticable when it is not cleared
226 bitmap
.eraseARGB(255, 0, 255, 128); // bright bluish green
229 // The device object will take ownership of the HBITMAP. The initial refcount
230 // of the data object will be 1, which is what the constructor expects.
231 return new BitmapPlatformDevice(hbitmap
, bitmap
);
235 BitmapPlatformDevice
* BitmapPlatformDevice::Create(int width
, int height
,
237 return Create(width
, height
, is_opaque
, NULL
);
241 BitmapPlatformDevice
* BitmapPlatformDevice::CreateAndClear(int width
,
244 BitmapPlatformDevice
* device
= BitmapPlatformDevice::Create(width
, height
,
246 if (device
&& !is_opaque
)
247 device
->accessBitmap(true).eraseARGB(0, 0, 0, 0);
251 // The device will own the HBITMAP, which corresponds to also owning the pixel
252 // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
253 BitmapPlatformDevice::BitmapPlatformDevice(
255 const SkBitmap
& bitmap
)
256 : SkBitmapDevice(bitmap
),
260 config_dirty_(true), // Want to load the config next time.
261 transform_(SkMatrix::I()) {
262 // The data object is already ref'ed for us by create().
263 SkDEBUGCODE(begin_paint_count_
= 0);
264 SetPlatformDevice(this, this);
265 // Initialize the clip region to the entire bitmap.
267 if (GetObject(hbitmap_
, sizeof(BITMAP
), &bitmap_data
)) {
269 rect
.set(0, 0, bitmap_data
.bmWidth
, bitmap_data
.bmHeight
);
270 clip_region_
= SkRegion(rect
);
274 BitmapPlatformDevice::~BitmapPlatformDevice() {
275 SkASSERT(begin_paint_count_
== 0);
280 HDC
BitmapPlatformDevice::BeginPlatformPaint() {
281 SkDEBUGCODE(begin_paint_count_
++);
282 return GetBitmapDC();
285 void BitmapPlatformDevice::EndPlatformPaint() {
286 SkASSERT(begin_paint_count_
--);
287 PlatformDevice::EndPlatformPaint();
290 void BitmapPlatformDevice::setMatrixClip(const SkMatrix
& transform
,
291 const SkRegion
& region
,
292 const SkClipStack
&) {
293 SetMatrixClip(transform
, region
);
296 void BitmapPlatformDevice::DrawToNativeContext(HDC dc
, int x
, int y
,
297 const RECT
* src_rect
) {
298 bool created_dc
= !IsBitmapDCCreated();
299 HDC source_dc
= BeginPlatformPaint();
304 temp_rect
.right
= width();
306 temp_rect
.bottom
= height();
307 src_rect
= &temp_rect
;
310 int copy_width
= src_rect
->right
- src_rect
->left
;
311 int copy_height
= src_rect
->bottom
- src_rect
->top
;
313 // We need to reset the translation for our bitmap or (0,0) won't be in the
314 // upper left anymore
318 LoadTransformToDC(source_dc
, identity
);
330 SkASSERT(copy_width
!= 0 && copy_height
!= 0);
331 BLENDFUNCTION blend_function
= {AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
344 LoadTransformToDC(source_dc
, transform_
);
351 const SkBitmap
& BitmapPlatformDevice::onAccessBitmap() {
352 // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
353 // operation has occurred on our DC.
354 if (IsBitmapDCCreated())
356 return SkBitmapDevice::onAccessBitmap();
359 SkBaseDevice
* BitmapPlatformDevice::onCreateCompatibleDevice(
360 SkBitmap::Config config
, int width
, int height
, bool isOpaque
, Usage
) {
361 SkASSERT(config
== SkBitmap::kARGB_8888_Config
);
362 return BitmapPlatformDevice::CreateAndClear(width
, height
, isOpaque
);
365 // PlatformCanvas impl
367 SkCanvas
* CreatePlatformCanvas(int width
,
370 HANDLE shared_section
,
371 OnFailureType failureType
) {
372 skia::RefPtr
<SkBaseDevice
> dev
= skia::AdoptRef(
373 BitmapPlatformDevice::Create(width
, height
, is_opaque
, shared_section
));
374 return CreateCanvas(dev
, failureType
);
377 // Port of PlatformBitmap to win
379 PlatformBitmap::~PlatformBitmap() {
382 SelectObject(surface_
, reinterpret_cast<HGDIOBJ
>(platform_extra_
));
387 bool PlatformBitmap::Allocate(int width
, int height
, bool is_opaque
) {
389 HBITMAP hbitmap
= CreateHBitmap(width
, height
, is_opaque
, 0, &data
);
393 surface_
= CreateCompatibleDC(NULL
);
394 InitializeDC(surface_
);
395 // When the memory DC is created, its display surface is exactly one
396 // monochrome pixel wide and one monochrome pixel high. Save this object
397 // off, we'll restore it just before deleting the memory DC.
398 HGDIOBJ stock_bitmap
= SelectObject(surface_
, hbitmap
);
399 platform_extra_
= reinterpret_cast<intptr_t>(stock_bitmap
);
401 const SkImageInfo info
= {
404 kPMColor_SkColorType
,
405 is_opaque
? kOpaque_SkAlphaType
: kPremul_SkAlphaType
407 bitmap_
.setConfig(info
);
408 // PlatformBitmapPixelRef takes ownership of |hbitmap|.
409 RefPtr
<SkPixelRef
> pixel_ref
= AdoptRef(new PlatformBitmapPixelRef(info
,
412 bitmap_
.setPixelRef(pixel_ref
.get());
413 bitmap_
.lockPixels();