// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "base/logging.h" #include "base/debug/alias.h" #include "skia/ext/bitmap_platform_device_win.h" #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkUtils.h" namespace { // PlatformBitmapPixelRef is an SkPixelRef that, on Windows, is backed by an // HBITMAP. class SK_API PlatformBitmapPixelRef : public SkPixelRef { public: PlatformBitmapPixelRef(HBITMAP bitmap_handle, void* pixels); virtual ~PlatformBitmapPixelRef(); SK_DECLARE_UNFLATTENABLE_OBJECT(); protected: virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE; virtual void onUnlockPixels() SK_OVERRIDE; private: HBITMAP bitmap_handle_; void* pixels_; }; HBITMAP CreateHBitmap(int width, int height, bool is_opaque, HANDLE shared_section, void** data) { // CreateDIBSection appears to get unhappy if we create an empty bitmap, so // just create a minimal bitmap if ((width == 0) || (height == 0)) { width = 1; height = 1; } BITMAPINFOHEADER hdr = {0}; hdr.biSize = sizeof(BITMAPINFOHEADER); hdr.biWidth = width; hdr.biHeight = -height; // minus means top-down bitmap hdr.biPlanes = 1; hdr.biBitCount = 32; hdr.biCompression = BI_RGB; // no compression hdr.biSizeImage = 0; hdr.biXPelsPerMeter = 1; hdr.biYPelsPerMeter = 1; hdr.biClrUsed = 0; hdr.biClrImportant = 0; HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast(&hdr), 0, data, shared_section, 0); // If this call fails, we're gonna crash hard. Try to get some useful // information out before we crash for post-mortem analysis. if (!hbitmap) { // Make sure parameters are saved in the minidump. base::debug::Alias(&width); base::debug::Alias(&height); int last_error = GetLastError(); base::debug::Alias(&last_error); int num_gdi_handles = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); if (num_gdi_handles == 0) { int get_gui_resources_error = GetLastError(); base::debug::Alias(&get_gui_resources_error); CHECK(false); } base::debug::Alias(&num_gdi_handles); const int kLotsOfHandles = 9990; if (num_gdi_handles > kLotsOfHandles) CHECK(false); PROCESS_MEMORY_COUNTERS_EX pmc; pmc.cb = sizeof(pmc); if (!GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc))) { CHECK(false); } const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB if (pmc.PagefileUsage > kLotsOfMemory) CHECK(false); if (pmc.PrivateUsage > kLotsOfMemory) CHECK(false); // Huh, that's weird. We don't have crazy handle count, we don't have // ridiculous memory usage. Try to allocate a small bitmap and see if that // fails too. hdr.biWidth = 5; hdr.biHeight = 5; void* small_data; HBITMAP small_bitmap = CreateDIBSection( NULL, reinterpret_cast(&hdr), 0, &small_data, shared_section, 0); if (!small_bitmap) CHECK(false); BITMAP bitmap_data; if (GetObject(small_bitmap, sizeof(BITMAP), &bitmap_data)) { if (!DeleteObject(small_bitmap)) CHECK(false); } // No idea what's going on. Die! CHECK(false); } return hbitmap; } PlatformBitmapPixelRef::PlatformBitmapPixelRef(HBITMAP bitmap_handle, void* pixels) : bitmap_handle_(bitmap_handle), pixels_(pixels) { setPreLocked(pixels, NULL); } PlatformBitmapPixelRef::~PlatformBitmapPixelRef() { if (bitmap_handle_) DeleteObject(bitmap_handle_); } void* PlatformBitmapPixelRef::onLockPixels(SkColorTable** color_table) { *color_table = NULL; return pixels_; } void PlatformBitmapPixelRef::onUnlockPixels() { // Nothing to do. return; } } // namespace namespace skia { HDC BitmapPlatformDevice::GetBitmapDC() { if (!hdc_) { hdc_ = CreateCompatibleDC(NULL); InitializeDC(hdc_); old_hbitmap_ = static_cast(SelectObject(hdc_, hbitmap_)); } LoadConfig(); return hdc_; } void BitmapPlatformDevice::ReleaseBitmapDC() { SkASSERT(hdc_); SelectObject(hdc_, old_hbitmap_); DeleteDC(hdc_); hdc_ = NULL; old_hbitmap_ = NULL; } bool BitmapPlatformDevice::IsBitmapDCCreated() const { return hdc_ != NULL; } void BitmapPlatformDevice::SetMatrixClip( const SkMatrix& transform, const SkRegion& region) { transform_ = transform; clip_region_ = region; config_dirty_ = true; } void BitmapPlatformDevice::LoadConfig() { if (!config_dirty_ || !hdc_) return; // Nothing to do. config_dirty_ = false; // Transform. LoadTransformToDC(hdc_, transform_); LoadClippingRegionToDC(hdc_, clip_region_, transform_); } // We use this static factory function instead of the regular constructor so // that we can create the pixel data before calling the constructor. This is // required so that we can call the base class' constructor with the pixel // data. BitmapPlatformDevice* BitmapPlatformDevice::Create( int width, int height, bool is_opaque, HANDLE shared_section) { void* data; HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section, &data); if (!hbitmap) return NULL; SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); RefPtr pixel_ref = AdoptRef(new PlatformBitmapPixelRef(hbitmap, data)); bitmap.setPixelRef(pixel_ref.get()); #ifndef NDEBUG // If we were given data, then don't clobber it! if (!shared_section && is_opaque) // To aid in finding bugs, we set the background color to something // obviously wrong so it will be noticable when it is not cleared bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green #endif // The device object will take ownership of the HBITMAP. The initial refcount // of the data object will be 1, which is what the constructor expects. return new BitmapPlatformDevice(hbitmap, bitmap); } // static BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height, bool is_opaque) { return Create(width, height, is_opaque, NULL); } // static BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width, int height, bool is_opaque) { BitmapPlatformDevice* device = BitmapPlatformDevice::Create(width, height, is_opaque); if (device && !is_opaque) device->accessBitmap(true).eraseARGB(0, 0, 0, 0); return device; } // The device will own the HBITMAP, which corresponds to also owning the pixel // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap. BitmapPlatformDevice::BitmapPlatformDevice( HBITMAP hbitmap, const SkBitmap& bitmap) : SkBitmapDevice(bitmap), hbitmap_(hbitmap), old_hbitmap_(NULL), hdc_(NULL), config_dirty_(true), // Want to load the config next time. transform_(SkMatrix::I()) { // The data object is already ref'ed for us by create(). SkDEBUGCODE(begin_paint_count_ = 0); SetPlatformDevice(this, this); // Initialize the clip region to the entire bitmap. BITMAP bitmap_data; if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) { SkIRect rect; rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight); clip_region_ = SkRegion(rect); } } BitmapPlatformDevice::~BitmapPlatformDevice() { SkASSERT(begin_paint_count_ == 0); if (hdc_) ReleaseBitmapDC(); } HDC BitmapPlatformDevice::BeginPlatformPaint() { SkDEBUGCODE(begin_paint_count_++); return GetBitmapDC(); } void BitmapPlatformDevice::EndPlatformPaint() { SkASSERT(begin_paint_count_--); PlatformDevice::EndPlatformPaint(); } void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform, const SkRegion& region, const SkClipStack&) { SetMatrixClip(transform, region); } void BitmapPlatformDevice::DrawToNativeContext(HDC dc, int x, int y, const RECT* src_rect) { bool created_dc = !IsBitmapDCCreated(); HDC source_dc = BeginPlatformPaint(); RECT temp_rect; if (!src_rect) { temp_rect.left = 0; temp_rect.right = width(); temp_rect.top = 0; temp_rect.bottom = height(); src_rect = &temp_rect; } int copy_width = src_rect->right - src_rect->left; int copy_height = src_rect->bottom - src_rect->top; // We need to reset the translation for our bitmap or (0,0) won't be in the // upper left anymore SkMatrix identity; identity.reset(); LoadTransformToDC(source_dc, identity); if (isOpaque()) { BitBlt(dc, x, y, copy_width, copy_height, source_dc, src_rect->left, src_rect->top, SRCCOPY); } else { SkASSERT(copy_width != 0 && copy_height != 0); BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; GdiAlphaBlend(dc, x, y, copy_width, copy_height, source_dc, src_rect->left, src_rect->top, copy_width, copy_height, blend_function); } LoadTransformToDC(source_dc, transform_); EndPlatformPaint(); if (created_dc) ReleaseBitmapDC(); } const SkBitmap& BitmapPlatformDevice::onAccessBitmap() { // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI // operation has occurred on our DC. if (IsBitmapDCCreated()) GdiFlush(); return SkBitmapDevice::onAccessBitmap(); } SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice( SkBitmap::Config config, int width, int height, bool isOpaque, Usage) { SkASSERT(config == SkBitmap::kARGB_8888_Config); return BitmapPlatformDevice::CreateAndClear(width, height, isOpaque); } // PlatformCanvas impl SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque, HANDLE shared_section, OnFailureType failureType) { skia::RefPtr dev = skia::AdoptRef( BitmapPlatformDevice::Create(width, height, is_opaque, shared_section)); return CreateCanvas(dev, failureType); } // Port of PlatformBitmap to win PlatformBitmap::~PlatformBitmap() { if (surface_) { if (platform_extra_) SelectObject(surface_, reinterpret_cast(platform_extra_)); DeleteDC(surface_); } } bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) { void* data; HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, 0, &data); if (!hbitmap) return false; surface_ = CreateCompatibleDC(NULL); InitializeDC(surface_); // When the memory DC is created, its display surface is exactly one // monochrome pixel wide and one monochrome pixel high. Save this object // off, we'll restore it just before deleting the memory DC. HGDIOBJ stock_bitmap = SelectObject(surface_, hbitmap); platform_extra_ = reinterpret_cast(stock_bitmap); bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); // PlatformBitmapPixelRef takes ownership of |hbitmap|. RefPtr pixel_ref = AdoptRef(new PlatformBitmapPixelRef(hbitmap, data)); bitmap_.setPixelRef(pixel_ref.get()); bitmap_.lockPixels(); return true; } } // namespace skia