| // Copyright (c) 2006-2008 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 <windows.h> |
| #include <psapi.h> |
| |
| #include "skia/ext/bitmap_platform_device_win.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 skia { |
| |
| namespace { |
| |
| // Constrains position and size to fit within available_size. If |size| is -1, |
| // all the available_size is used. Returns false if the position is out of |
| // available_size. |
| bool Constrain(int available_size, int* position, int *size) { |
| if (*size < -2) |
| return false; |
| |
| if (*position < 0) { |
| if (*size != -1) |
| *size += *position; |
| *position = 0; |
| } |
| if (*size == 0 || *position >= available_size) |
| return false; |
| |
| if (*size > 0) { |
| int overflow = (*position + *size) - available_size; |
| if (overflow > 0) { |
| *size -= overflow; |
| } |
| } else { |
| // Fill up available size. |
| *size = available_size - *position; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| class BitmapPlatformDevice::BitmapPlatformDeviceData : public SkRefCnt { |
| public: |
| explicit BitmapPlatformDeviceData(HBITMAP hbitmap); |
| |
| // Create/destroy hdc_, which is the memory DC for our bitmap data. |
| HDC GetBitmapDC(); |
| void ReleaseBitmapDC(); |
| bool IsBitmapDCCreated() const; |
| |
| // Sets the transform and clip operations. This will not update the DC, |
| // but will mark the config as dirty. The next call of LoadConfig will |
| // pick up these changes. |
| void SetMatrixClip(const SkMatrix& transform, const SkRegion& region); |
| |
| const SkMatrix& transform() const { |
| return transform_; |
| } |
| |
| protected: |
| // Loads the current transform and clip into the DC. Can be called even when |
| // the DC is NULL (will be a NOP). |
| void LoadConfig(); |
| |
| // Windows bitmap corresponding to our surface. |
| HBITMAP hbitmap_; |
| |
| // Lazily-created DC used to draw into the bitmap, see getBitmapDC. |
| HDC hdc_; |
| |
| // True when there is a transform or clip that has not been set to the DC. |
| // The DC is retrieved for every text operation, and the transform and clip |
| // do not change as much. We can save time by not loading the clip and |
| // transform for every one. |
| bool config_dirty_; |
| |
| // Translation assigned to the DC: we need to keep track of this separately |
| // so it can be updated even if the DC isn't created yet. |
| SkMatrix transform_; |
| |
| // The current clipping |
| SkRegion clip_region_; |
| |
| private: |
| virtual ~BitmapPlatformDeviceData(); |
| |
| // Copy & assign are not supported. |
| BitmapPlatformDeviceData(const BitmapPlatformDeviceData&); |
| BitmapPlatformDeviceData& operator=(const BitmapPlatformDeviceData&); |
| }; |
| |
| BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData( |
| HBITMAP hbitmap) |
| : hbitmap_(hbitmap), |
| hdc_(NULL), |
| config_dirty_(true) { // Want to load the config next time. |
| // 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); |
| } |
| |
| transform_.reset(); |
| } |
| |
| BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() { |
| if (hdc_) |
| ReleaseBitmapDC(); |
| |
| // this will free the bitmap data as well as the bitmap handle |
| DeleteObject(hbitmap_); |
| } |
| |
| HDC BitmapPlatformDevice::BitmapPlatformDeviceData::GetBitmapDC() { |
| if (!hdc_) { |
| hdc_ = CreateCompatibleDC(NULL); |
| InitializeDC(hdc_); |
| HGDIOBJ old_bitmap = SelectObject(hdc_, hbitmap_); |
| // 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. |
| SkMatrix t(transform_); |
| LoadTransformToDC(hdc_, t); |
| LoadClippingRegionToDC(hdc_, clip_region_, t); |
| } |
| |
| // 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( |
| HDC screen_dc, |
| int width, |
| int height, |
| bool is_opaque, |
| HANDLE shared_section) { |
| SkBitmap bitmap; |
| |
| // 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; |
| |
| void* data = NULL; |
| HBITMAP hbitmap = CreateDIBSection(screen_dc, |
| reinterpret_cast<BITMAPINFO*>(&hdr), 0, |
| &data, |
| shared_section, 0); |
| if (!hbitmap) { |
| return NULL; |
| } |
| |
| bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); |
| bitmap.setPixels(data); |
| bitmap.setIsOpaque(is_opaque); |
| |
| if (is_opaque) { |
| #ifndef NDEBUG |
| // 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 |
| } else { |
| bitmap.eraseARGB(0, 0, 0, 0); |
| } |
| |
| // 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(new BitmapPlatformDeviceData(hbitmap), |
| bitmap); |
| } |
| |
| // static |
| BitmapPlatformDevice* BitmapPlatformDevice::create(int width, |
| int height, |
| bool is_opaque, |
| HANDLE shared_section) { |
| HDC screen_dc = GetDC(NULL); |
| BitmapPlatformDevice* device = BitmapPlatformDevice::create( |
| screen_dc, width, height, is_opaque, shared_section); |
| ReleaseDC(NULL, screen_dc); |
| 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( |
| BitmapPlatformDeviceData* data, |
| const SkBitmap& bitmap) |
| : PlatformDevice(bitmap), |
| data_(data) { |
| // The data object is already ref'ed for us by create(). |
| } |
| |
| // The copy constructor just adds another reference to the underlying data. |
| // We use a const cast since the default Skia definitions don't define the |
| // proper constedness that we expect (accessBitmap should really be const). |
| BitmapPlatformDevice::BitmapPlatformDevice( |
| const BitmapPlatformDevice& other) |
| : PlatformDevice( |
| const_cast<BitmapPlatformDevice&>(other).accessBitmap(true)), |
| data_(other.data_) { |
| data_->ref(); |
| } |
| |
| BitmapPlatformDevice::~BitmapPlatformDevice() { |
| data_->unref(); |
| } |
| |
| BitmapPlatformDevice& BitmapPlatformDevice::operator=( |
| const BitmapPlatformDevice& other) { |
| data_ = other.data_; |
| data_->ref(); |
| return *this; |
| } |
| |
| HDC BitmapPlatformDevice::getBitmapDC() { |
| return data_->GetBitmapDC(); |
| } |
| |
| void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform, |
| const SkRegion& region) { |
| data_->SetMatrixClip(transform, region); |
| } |
| |
| void BitmapPlatformDevice::drawToHDC(HDC dc, int x, int y, |
| const RECT* src_rect) { |
| bool created_dc = !data_->IsBitmapDCCreated(); |
| HDC source_dc = getBitmapDC(); |
| |
| 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()); |
| |
| if (created_dc) |
| data_->ReleaseBitmapDC(); |
| } |
| |
| void BitmapPlatformDevice::makeOpaque(int x, int y, int width, int height) { |
| const SkBitmap& bitmap = accessBitmap(true); |
| SkASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); |
| |
| // FIXME(brettw): This is kind of lame, we shouldn't be dealing with |
| // transforms at this level. Probably there should be a PlatformCanvas |
| // function that does the transform (using the actual transform not just the |
| // translation) and calls us with the transformed rect. |
| const SkMatrix& matrix = data_->transform(); |
| int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x; |
| int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y; |
| |
| if (Constrain(bitmap.width(), &bitmap_start_x, &width) && |
| Constrain(bitmap.height(), &bitmap_start_y, &height)) { |
| SkAutoLockPixels lock(bitmap); |
| SkASSERT(bitmap.rowBytes() % sizeof(uint32_t) == 0u); |
| size_t row_words = bitmap.rowBytes() / sizeof(uint32_t); |
| // Set data to the first pixel to be modified. |
| uint32_t* data = bitmap.getAddr32(0, 0) + (bitmap_start_y * row_words) + |
| bitmap_start_x; |
| for (int i = 0; i < height; i++) { |
| for (int j = 0; j < width; j++) |
| data[j] |= (0xFF << SK_A32_SHIFT); |
| data += row_words; |
| } |
| } |
| } |
| |
| // Returns the color value at the specified location. |
| SkColor BitmapPlatformDevice::getColorAt(int x, int y) { |
| const SkBitmap& bitmap = accessBitmap(false); |
| SkAutoLockPixels lock(bitmap); |
| uint32_t* data = bitmap.getAddr32(0, 0); |
| return static_cast<SkColor>(data[x + y * width()]); |
| } |
| |
| void 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(); |
| } |
| |
| } // namespace skia |
| |