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