// 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 "skia/ext/bitmap_platform_device_win.h" #include "skia/ext/bitmap_platform_device_data.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); 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 { BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData( HBITMAP hbitmap) : bitmap_context_(hbitmap), hdc_(NULL), config_dirty_(true), // Want to load the config next time. transform_(SkMatrix::I()) { // Initialize the clip region to the entire bitmap. BITMAP bitmap_data; if (GetObject(bitmap_context_, sizeof(BITMAP), &bitmap_data)) { SkIRect rect; rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight); clip_region_ = SkRegion(rect); } } BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() { if (hdc_) ReleaseBitmapDC(); // this will free the bitmap data as well as the bitmap handle DeleteObject(bitmap_context_); } HDC BitmapPlatformDevice::BitmapPlatformDeviceData::GetBitmapDC() { if (!hdc_) { hdc_ = CreateCompatibleDC(NULL); InitializeDC(hdc_); HGDIOBJ old_bitmap = SelectObject(hdc_, bitmap_context_); // When the memory DC is created, its display surface is exactly one // monochrome pixel wide and one monochrome pixel high. Since we select our // own bitmap, we must delete the previous one. DeleteObject(old_bitmap); } LoadConfig(); return hdc_; } void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC() { SkASSERT(hdc_); DeleteDC(hdc_); hdc_ = NULL; } bool BitmapPlatformDevice::BitmapPlatformDeviceData::IsBitmapDCCreated() const { return hdc_ != NULL; } void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip( const SkMatrix& transform, const SkRegion& region) { transform_ = transform; clip_region_ = region; config_dirty_ = true; } void BitmapPlatformDevice::BitmapPlatformDeviceData::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); bitmap.setPixels(data); bitmap.setIsOpaque(is_opaque); #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( skia::AdoptRef(new BitmapPlatformDeviceData(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 SkDevice's bitmap. BitmapPlatformDevice::BitmapPlatformDevice( const skia::RefPtr& data, const SkBitmap& bitmap) : SkDevice(bitmap), data_(data) { // The data object is already ref'ed for us by create(). SkDEBUGCODE(begin_paint_count_ = 0); SetPlatformDevice(this, this); } BitmapPlatformDevice::~BitmapPlatformDevice() { SkASSERT(begin_paint_count_ == 0); } HDC BitmapPlatformDevice::BeginPlatformPaint() { SkDEBUGCODE(begin_paint_count_++); return data_->GetBitmapDC(); } void BitmapPlatformDevice::EndPlatformPaint() { SkASSERT(begin_paint_count_--); PlatformDevice::EndPlatformPaint(); } void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform, const SkRegion& region, const SkClipStack&) { data_->SetMatrixClip(transform, region); } void BitmapPlatformDevice::DrawToNativeContext(HDC dc, int x, int y, const RECT* src_rect) { bool created_dc = !data_->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, data_->transform()); EndPlatformPaint(); if (created_dc) data_->ReleaseBitmapDC(); } const SkBitmap& BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) { // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI // operation has occurred on our DC. if (data_->IsBitmapDCCreated()) GdiFlush(); return *bitmap; } SkDevice* 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); // PlatformBitmapPixelRef takes ownership of |hbitmap|. bitmap_.setPixelRef( skia::AdoptRef(new PlatformBitmapPixelRef(hbitmap, data)).get()); bitmap_.setIsOpaque(is_opaque); bitmap_.lockPixels(); return true; } } // namespace skia