mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
329 lines
9.8 KiB
C++
329 lines
9.8 KiB
C++
// 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 <windows.h>
|
|
#include <psapi.h>
|
|
|
|
#include "base/debug/gdi_debug_util_win.h"
|
|
#include "base/logging.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 {
|
|
|
|
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<BITMAPINFO*>(&hdr),
|
|
0, data, shared_section, 0);
|
|
|
|
#if !defined(_WIN64)
|
|
// 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)
|
|
base::debug::GDIBitmapAllocFailure(&hdr, shared_section);
|
|
#endif
|
|
|
|
return hbitmap;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace skia {
|
|
|
|
void DrawToNativeContext(SkCanvas* canvas, HDC hdc, int x, int y,
|
|
const RECT* src_rect) {
|
|
PlatformDevice* platform_device = GetPlatformDevice(GetTopDevice(*canvas));
|
|
if (platform_device)
|
|
platform_device->DrawToHDC(hdc, x, y, src_rect);
|
|
}
|
|
|
|
void PlatformDevice::DrawToHDC(HDC, int x, int y, const RECT* src_rect) {}
|
|
|
|
HDC BitmapPlatformDevice::GetBitmapDC() {
|
|
if (!hdc_) {
|
|
hdc_ = CreateCompatibleDC(NULL);
|
|
InitializeDC(hdc_);
|
|
old_hbitmap_ = static_cast<HBITMAP>(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_);
|
|
}
|
|
|
|
static void DeleteHBitmapCallback(void* addr, void* context) {
|
|
DeleteObject(static_cast<HBITMAP>(context));
|
|
}
|
|
|
|
static bool InstallHBitmapPixels(SkBitmap* bitmap, int width, int height,
|
|
bool is_opaque, void* data, HBITMAP hbitmap) {
|
|
const SkAlphaType at = is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
|
|
const SkImageInfo info = SkImageInfo::MakeN32(width, height, at);
|
|
const size_t rowBytes = info.minRowBytes();
|
|
SkColorTable* color_table = NULL;
|
|
return bitmap->installPixels(info, data, rowBytes, color_table,
|
|
DeleteHBitmapCallback, hbitmap);
|
|
}
|
|
|
|
// 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,
|
|
bool do_clear) {
|
|
|
|
void* data;
|
|
HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section,
|
|
&data);
|
|
if (!hbitmap)
|
|
return NULL;
|
|
|
|
SkBitmap bitmap;
|
|
if (!InstallHBitmapPixels(&bitmap, width, height, is_opaque, data, hbitmap))
|
|
return NULL;
|
|
|
|
if (do_clear)
|
|
bitmap.eraseColor(0);
|
|
|
|
#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) {
|
|
const HANDLE shared_section = NULL;
|
|
const bool do_clear = false;
|
|
return Create(width, height, is_opaque, shared_section, do_clear);
|
|
}
|
|
|
|
// 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::DrawToHDC(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::onCreateDevice(const CreateInfo& cinfo,
|
|
const SkPaint*) {
|
|
const SkImageInfo& info = cinfo.fInfo;
|
|
const bool do_clear = !info.isOpaque();
|
|
SkASSERT(info.colorType() == kN32_SkColorType);
|
|
return Create(info.width(), info.height(), info.isOpaque(), NULL, do_clear);
|
|
}
|
|
|
|
// PlatformCanvas impl
|
|
|
|
SkCanvas* CreatePlatformCanvas(int width,
|
|
int height,
|
|
bool is_opaque,
|
|
HANDLE shared_section,
|
|
OnFailureType failureType) {
|
|
skia::RefPtr<SkBaseDevice> 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<HGDIOBJ>(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<intptr_t>(stock_bitmap);
|
|
|
|
if (!InstallHBitmapPixels(&bitmap_, width, height, is_opaque, data, hbitmap))
|
|
return false;
|
|
bitmap_.lockPixels();
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace skia
|