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