This source file includes following definitions.
- CreateDevice
- FillBitmapInfoHeader
- create
- previous_pen_
- BeginPlatformPaint
- drawPaint
- drawPoints
- drawRect
- drawRRect
- drawPath
- drawBitmapRect
- drawBitmap
- drawSprite
- gdiCanHandleText
- fUseGDI
- useGDI
- getAscent
- getTextOutOptions
- SetSkiaEnsureTypefaceCharactersAccessible
- EnsureTypefaceCharactersAccessible
- EnsureExtTextOut
- drawText
- size_utf8
- size_utf16
- size_glyphid
- drawPosText
- drawTextOnPath
- drawVertices
- drawDevice
- ApplyPaint
- setMatrixClip
- DrawToNativeContext
- LoadClipRegion
- onCreateCompatibleDevice
- onCreateDevice
- CreateBrush
- CreatePen
- Cleanup
- SelectObject
- CreateBrush
- CreatePen
- InternalDrawBitmap
#include "skia/ext/vector_platform_device_emf_win.h"
#include <windows.h>
#include "base/logging.h"
#include "base/strings/string16.h"
#include "skia/ext/bitmap_platform_device.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkFontHost.h"
#include "third_party/skia/include/core/SkPathEffect.h"
#include "third_party/skia/include/core/SkTemplates.h"
#include "third_party/skia/include/core/SkUtils.h"
#include "third_party/skia/include/ports/SkTypeface_win.h"
namespace skia {
#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
SkBaseDevice* VectorPlatformDeviceEmf::CreateDevice(
int width, int height, bool is_opaque, HANDLE shared_section) {
if (!is_opaque) {
return BitmapPlatformDevice::Create(width, height, is_opaque,
shared_section);
}
SkASSERT(shared_section);
SkBaseDevice* device = VectorPlatformDeviceEmf::create(
reinterpret_cast<HDC>(shared_section), width, height);
return device;
}
static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) {
hdr->biSize = sizeof(BITMAPINFOHEADER);
hdr->biWidth = width;
hdr->biHeight = -height;
hdr->biPlanes = 1;
hdr->biBitCount = 32;
hdr->biCompression = BI_RGB;
hdr->biSizeImage = 0;
hdr->biXPelsPerMeter = 1;
hdr->biYPelsPerMeter = 1;
hdr->biClrUsed = 0;
hdr->biClrImportant = 0;
}
SkBaseDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
InitializeDC(dc);
SkBitmap bitmap;
HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
bool succeeded = false;
if (selected_bitmap != NULL) {
BITMAP bitmap_data;
if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
sizeof(BITMAP)) {
if (width == bitmap_data.bmWidth &&
height == bitmap_data.bmHeight) {
bitmap.setConfig(SkBitmap::kARGB_8888_Config,
bitmap_data.bmWidth,
bitmap_data.bmHeight,
bitmap_data.bmWidthBytes);
bitmap.setPixels(bitmap_data.bmBits);
succeeded = true;
}
}
}
if (!succeeded)
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
return new VectorPlatformDeviceEmf(dc, bitmap);
}
VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
: SkBitmapDevice(bitmap),
hdc_(dc),
previous_brush_(NULL),
previous_pen_(NULL) {
transform_.reset();
SetPlatformDevice(this, this);
}
VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
SkASSERT(previous_brush_ == NULL);
SkASSERT(previous_pen_ == NULL);
}
HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
return hdc_;
}
void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
const SkPaint& paint) {
SkRect rect;
rect.fLeft = 0;
rect.fTop = 0;
rect.fRight = SkIntToScalar(width() + 1);
rect.fBottom = SkIntToScalar(height() + 1);
drawRect(draw, rect, paint);
}
void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
SkCanvas::PointMode mode,
size_t count,
const SkPoint pts[],
const SkPaint& paint) {
if (!count)
return;
if (mode == SkCanvas::kPoints_PointMode) {
SkASSERT(false);
return;
}
SkPaint tmp_paint(paint);
tmp_paint.setStyle(SkPaint::kStroke_Style);
SkPath path;
switch (mode) {
case SkCanvas::kLines_PointMode:
if (count % 2) {
SkASSERT(false);
return;
}
for (size_t i = 0; i < count / 2; ++i) {
path.moveTo(pts[2 * i]);
path.lineTo(pts[2 * i + 1]);
}
break;
case SkCanvas::kPolygon_PointMode:
path.moveTo(pts[0]);
for (size_t i = 1; i < count; ++i) {
path.lineTo(pts[i]);
}
break;
default:
SkASSERT(false);
return;
}
drawPath(draw, path, tmp_paint);
}
void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
const SkRect& rect,
const SkPaint& paint) {
CHECK_FOR_NODRAW_ANNOTATION(paint);
if (paint.getPathEffect()) {
SkPath path_orginal;
path_orginal.addRect(rect);
SkPath path_modified;
paint.getFillPath(path_orginal, &path_modified);
SkPaint paint_no_effet(paint);
paint_no_effet.setPathEffect(NULL);
drawPath(draw, path_modified, paint_no_effet);
return;
}
if (!ApplyPaint(paint)) {
return;
}
HDC dc = BeginPlatformPaint();
if (!Rectangle(dc, SkScalarRoundToInt(rect.fLeft),
SkScalarRoundToInt(rect.fTop),
SkScalarRoundToInt(rect.fRight),
SkScalarRoundToInt(rect.fBottom))) {
SkASSERT(false);
}
EndPlatformPaint();
Cleanup();
}
void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr,
const SkPaint& paint) {
SkPath path;
path.addRRect(rr);
this->drawPath(draw, path, paint, NULL, true);
}
void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
const SkPath& path,
const SkPaint& paint,
const SkMatrix* prePathMatrix,
bool pathIsMutable) {
CHECK_FOR_NODRAW_ANNOTATION(paint);
if (paint.getPathEffect()) {
SkPath path_modified;
paint.getFillPath(path, &path_modified);
SkPaint paint_no_effet(paint);
paint_no_effet.setPathEffect(NULL);
drawPath(draw, path_modified, paint_no_effet);
return;
}
if (!ApplyPaint(paint)) {
return;
}
HDC dc = BeginPlatformPaint();
if (PlatformDevice::LoadPathToDC(dc, path)) {
switch (paint.getStyle()) {
case SkPaint::kFill_Style: {
BOOL res = StrokeAndFillPath(dc);
SkASSERT(res != 0);
break;
}
case SkPaint::kStroke_Style: {
BOOL res = StrokePath(dc);
SkASSERT(res != 0);
break;
}
case SkPaint::kStrokeAndFill_Style: {
BOOL res = StrokeAndFillPath(dc);
SkASSERT(res != 0);
break;
}
default:
SkASSERT(false);
break;
}
}
EndPlatformPaint();
Cleanup();
}
void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
const SkBitmap& bitmap,
const SkRect* src,
const SkRect& dst,
const SkPaint& paint,
SkCanvas::DrawBitmapRectFlags flags) {
SkMatrix matrix;
SkRect bitmapBounds, tmpSrc, tmpDst;
SkBitmap tmpBitmap;
bitmapBounds.isetWH(bitmap.width(), bitmap.height());
if (src) {
tmpSrc = *src;
} else {
tmpSrc = bitmapBounds;
}
matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
const SkBitmap* bitmapPtr = &bitmap;
if (src) {
if (!bitmapBounds.contains(*src)) {
if (!tmpSrc.intersect(bitmapBounds)) {
return;
}
matrix.mapRect(&tmpDst, tmpSrc);
}
SkIRect srcIR;
tmpSrc.roundOut(&srcIR);
if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
return;
}
bitmapPtr = &tmpBitmap;
SkScalar dx = 0, dy = 0;
if (srcIR.fLeft > 0) {
dx = SkIntToScalar(srcIR.fLeft);
}
if (srcIR.fTop > 0) {
dy = SkIntToScalar(srcIR.fTop);
}
if (dx || dy) {
matrix.preTranslate(dx, dy);
}
}
this->drawBitmap(draw, *bitmapPtr, matrix, paint);
}
void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw,
const SkBitmap& bitmap,
const SkMatrix& matrix,
const SkPaint& paint) {
SkMatrix actual_transform(transform_);
actual_transform.preConcat(matrix);
LoadTransformToDC(hdc_, actual_transform);
InternalDrawBitmap(bitmap, 0, 0, paint);
LoadTransformToDC(hdc_, transform_);
}
void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
const SkBitmap& bitmap,
int x, int y,
const SkPaint& paint) {
SkMatrix identity;
identity.reset();
LoadTransformToDC(hdc_, identity);
InternalDrawBitmap(bitmap, x, y, paint);
LoadTransformToDC(hdc_, transform_);
}
static bool gdiCanHandleText(const SkPaint& paint) {
return !paint.getShader() &&
!paint.getPathEffect() &&
(SkPaint::kFill_Style == paint.getStyle()) &&
(255 == paint.getAlpha());
}
class SkGDIFontSetup {
public:
SkGDIFontSetup() :
fHDC(NULL),
fNewFont(NULL),
fSavedFont(NULL),
fSavedTextColor(0),
fUseGDI(false) {
SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
}
~SkGDIFontSetup();
bool useGDI(HDC hdc, const SkPaint&);
private:
HDC fHDC;
HFONT fNewFont;
HFONT fSavedFont;
COLORREF fSavedTextColor;
bool fUseGDI;
SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
};
bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
SkASSERT(!fUseGDIHasBeenCalled);
SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
fUseGDI = gdiCanHandleText(paint);
if (fUseGDI) {
fSavedTextColor = GetTextColor(hdc);
SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
LOGFONT lf;
SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
lf.lfHeight = -SkScalarRoundToInt(paint.getTextSize());
fNewFont = CreateFontIndirect(&lf);
fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
fHDC = hdc;
}
return fUseGDI;
}
SkGDIFontSetup::~SkGDIFontSetup() {
if (fUseGDI) {
::SelectObject(fHDC, fSavedFont);
::DeleteObject(fNewFont);
SetTextColor(fHDC, fSavedTextColor);
}
}
static SkScalar getAscent(const SkPaint& paint) {
SkPaint::FontMetrics fm;
paint.getFontMetrics(&fm);
return fm.fAscent;
}
static UINT getTextOutOptions(const SkPaint& paint) {
if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
return ETO_GLYPH_INDEX;
} else {
SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
return 0;
}
}
static SkiaEnsureTypefaceCharactersAccessible
g_skia_ensure_typeface_characters_accessible = NULL;
SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
SkiaEnsureTypefaceCharactersAccessible func) {
SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL);
g_skia_ensure_typeface_characters_accessible = func;
}
void EnsureTypefaceCharactersAccessible(
const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) {
LOGFONT lf;
SkLOGFONTFromTypeface(&typeface, &lf);
g_skia_ensure_typeface_characters_accessible(lf, text, text_length);
}
bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect,
LPCWSTR text, unsigned int characters, const int * lpDx,
SkTypeface* const typeface) {
bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
if (!success) {
if (typeface) {
EnsureTypefaceCharactersAccessible(*typeface,
text,
characters);
success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
if (!success) {
LOGFONT lf;
SkLOGFONTFromTypeface(typeface, &lf);
VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
<< " FaceName = " << lf.lfFaceName
<< " and characters: " << base::string16(text, characters);
}
} else {
VLOG(1) << "ExtTextOut FAILED for default FaceName "
<< " and characters: " << base::string16(text, characters);
}
}
return success;
}
void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
const void* text,
size_t byteLength,
SkScalar x,
SkScalar y,
const SkPaint& paint) {
SkGDIFontSetup setup;
bool useDrawPath = true;
if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
&& setup.useGDI(hdc_, paint)) {
UINT options = getTextOutOptions(paint);
UINT count = byteLength >> 1;
useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRoundToInt(x),
SkScalarRoundToInt(y + getAscent(paint)), options, 0,
reinterpret_cast<const wchar_t*>(text), count, NULL,
paint.getTypeface());
}
if (useDrawPath) {
SkPath path;
paint.getTextPath(text, byteLength, x, y, &path);
drawPath(draw, path, paint);
}
}
static size_t size_utf8(const char* text) {
return SkUTF8_CountUTF8Bytes(text);
}
static size_t size_utf16(const char* text) {
uint16_t c = *reinterpret_cast<const uint16_t*>(text);
return SkUTF16_IsHighSurrogate(c) ? 4 : 2;
}
static size_t size_glyphid(const char* text) {
return 2;
}
void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
const void* text,
size_t len,
const SkScalar pos[],
SkScalar constY,
int scalarsPerPos,
const SkPaint& paint) {
SkGDIFontSetup setup;
bool useDrawText = true;
if (2 == scalarsPerPos
&& SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
&& setup.useGDI(hdc_, paint)) {
int startX = SkScalarRoundToInt(pos[0]);
int startY = SkScalarRoundToInt(pos[1] + getAscent(paint));
const int count = len >> 1;
SkAutoSTMalloc<64, INT> storage(count);
INT* advances = storage.get();
for (int i = 0; i < count - 1; ++i) {
advances[i] = SkScalarRoundToInt(pos[2] - pos[0]);
pos += 2;
}
useDrawText = !EnsureExtTextOut(hdc_, startX, startY,
getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text),
count, advances, paint.getTypeface());
}
if (useDrawText) {
size_t (*bytesPerCodePoint)(const char*);
switch (paint.getTextEncoding()) {
case SkPaint::kUTF8_TextEncoding:
bytesPerCodePoint = size_utf8;
break;
case SkPaint::kUTF16_TextEncoding:
bytesPerCodePoint = size_utf16;
break;
default:
SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
bytesPerCodePoint = size_glyphid;
break;
}
const char* curr = reinterpret_cast<const char*>(text);
const char* stop = curr + len;
while (curr < stop) {
SkScalar y = (1 == scalarsPerPos) ? constY : pos[1];
size_t bytes = bytesPerCodePoint(curr);
drawText(draw, curr, bytes, pos[0], y, paint);
curr += bytes;
pos += scalarsPerPos;
}
}
}
void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
const void* text,
size_t len,
const SkPath& path,
const SkMatrix* matrix,
const SkPaint& paint) {
SkASSERT(false);
}
void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
SkCanvas::VertexMode vmode,
int vertexCount,
const SkPoint vertices[],
const SkPoint texs[],
const SkColor colors[],
SkXfermode* xmode,
const uint16_t indices[],
int indexCount,
const SkPaint& paint) {
SkASSERT(false);
}
void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
SkBaseDevice* device,
int x,
int y,
const SkPaint& paint) {
drawSprite(draw, device->accessBitmap(false), x, y, paint);
}
bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
SkPaint::Style style = paint.getStyle();
if (!paint.getAlpha())
style = (SkPaint::Style) SkPaint::kStyleCount;
switch (style) {
case SkPaint::kFill_Style:
if (!CreateBrush(true, paint) ||
!CreatePen(false, paint))
return false;
break;
case SkPaint::kStroke_Style:
if (!CreateBrush(false, paint) ||
!CreatePen(true, paint))
return false;
break;
case SkPaint::kStrokeAndFill_Style:
if (!CreateBrush(true, paint) ||
!CreatePen(true, paint))
return false;
break;
default:
if (!CreateBrush(false, paint) ||
!CreatePen(false, paint))
return false;
break;
}
SkASSERT(!paint.getPathEffect());
SkASSERT(!paint.getRasterizer());
return true;
}
void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
const SkRegion& region,
const SkClipStack&) {
transform_ = transform;
LoadTransformToDC(hdc_, transform_);
clip_region_ = region;
if (!clip_region_.isEmpty())
LoadClipRegion();
}
void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
const RECT* src_rect) {
SkASSERT(false);
}
void VectorPlatformDeviceEmf::LoadClipRegion() {
SkMatrix t;
t.reset();
LoadClippingRegionToDC(hdc_, clip_region_, t);
}
#ifdef SK_SUPPORT_LEGACY_COMPATIBLEDEVICE_CONFIG
SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
SkBitmap::Config config, int width, int height, bool isOpaque,
Usage ) {
SkASSERT(config == SkBitmap::kARGB_8888_Config);
return VectorPlatformDeviceEmf::CreateDevice(width, height, isOpaque, NULL);
}
#endif
SkBaseDevice* VectorPlatformDeviceEmf::onCreateDevice(const SkImageInfo& info,
Usage ) {
SkASSERT(info.colorType() == kPMColor_SkColorType);
return VectorPlatformDeviceEmf::CreateDevice(
info.width(), info.height(), info.isOpaque(), NULL);
}
bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
SkASSERT(previous_brush_ == NULL);
if (!use_brush) {
if (0 == SetBkMode(hdc_, TRANSPARENT)) {
SkASSERT(false);
return false;
}
previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
return previous_brush_ != NULL;
}
if (0 == SetBkMode(hdc_, OPAQUE)) {
SkASSERT(false);
return false;
}
previous_brush_ = SelectObject(CreateSolidBrush(color));
return previous_brush_ != NULL;
}
bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
COLORREF color,
int stroke_width,
float stroke_miter,
DWORD pen_style) {
SkASSERT(previous_pen_ == NULL);
if (!use_pen) {
previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
return previous_pen_ != NULL;
}
if (stroke_width == 0) {
previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
return previous_pen_ != NULL;
}
LOGBRUSH brush;
brush.lbStyle = BS_SOLID;
brush.lbColor = color;
brush.lbHatch = 0;
HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
SkASSERT(pen != NULL);
previous_pen_ = SelectObject(pen);
if (previous_pen_ == NULL)
return false;
if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
SkASSERT(false);
return false;
}
return true;
}
void VectorPlatformDeviceEmf::Cleanup() {
if (previous_brush_) {
HGDIOBJ result = SelectObject(previous_brush_);
previous_brush_ = NULL;
if (result) {
BOOL res = DeleteObject(result);
SkASSERT(res != 0);
}
}
if (previous_pen_) {
HGDIOBJ result = SelectObject(previous_pen_);
previous_pen_ = NULL;
if (result) {
BOOL res = DeleteObject(result);
SkASSERT(res != 0);
}
}
AbortPath(hdc_);
}
HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
HGDIOBJ result = ::SelectObject(hdc_, object);
SkASSERT(result != HGDI_ERROR);
if (result == HGDI_ERROR)
return NULL;
return result;
}
bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
const SkPaint& paint) {
if (paint.getAlpha() == 0) {
use_brush = false;
}
return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
}
bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
if (paint.getAlpha() == 0) {
use_pen = false;
}
DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
switch (paint.getStrokeJoin()) {
case SkPaint::kMiter_Join:
pen_style |= PS_JOIN_MITER;
break;
case SkPaint::kRound_Join:
pen_style |= PS_JOIN_ROUND;
break;
case SkPaint::kBevel_Join:
pen_style |= PS_JOIN_BEVEL;
break;
default:
SkASSERT(false);
break;
}
switch (paint.getStrokeCap()) {
case SkPaint::kButt_Cap:
pen_style |= PS_ENDCAP_FLAT;
break;
case SkPaint::kRound_Cap:
pen_style |= PS_ENDCAP_ROUND;
break;
case SkPaint::kSquare_Cap:
pen_style |= PS_ENDCAP_SQUARE;
break;
default:
SkASSERT(false);
break;
}
return CreatePen(use_pen,
SkColorToCOLORREF(paint.getColor()),
SkScalarRoundToInt(paint.getStrokeWidth()),
paint.getStrokeMiter(),
pen_style);
}
void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
int x, int y,
const SkPaint& paint) {
unsigned char alpha = paint.getAlpha();
if (alpha == 0)
return;
bool is_translucent;
if (alpha != 255) {
SkPaint tmp_paint(paint);
tmp_paint.setAlpha(255);
if (!ApplyPaint(tmp_paint))
return;
is_translucent = true;
} else {
if (!ApplyPaint(paint))
return;
is_translucent = false;
}
int src_size_x = bitmap.width();
int src_size_y = bitmap.height();
if (!src_size_x || !src_size_y)
return;
BITMAPV4HEADER bitmap_header;
memset(&bitmap_header, 0, sizeof(BITMAPV4HEADER));
FillBitmapInfoHeader(src_size_x, src_size_y,
reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header));
bitmap_header.bV4Size = sizeof(BITMAPV4HEADER);
bitmap_header.bV4RedMask = 0x00ff0000;
bitmap_header.bV4GreenMask = 0x0000ff00;
bitmap_header.bV4BlueMask = 0x000000ff;
bitmap_header.bV4AlphaMask = 0xff000000;
SkAutoLockPixels lock(bitmap);
SkASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
if (pixels == NULL) {
SkASSERT(false);
return;
}
if (!is_translucent) {
int row_length = bitmap.rowBytesAsPixels();
for (int y2 = 0; y2 < src_size_y; ++y2) {
for (int x2 = 0; x2 < src_size_x; ++x2) {
if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
is_translucent = true;
y2 = src_size_y;
break;
}
}
}
}
HDC dc = BeginPlatformPaint();
BITMAPINFOHEADER hdr;
FillBitmapInfoHeader(src_size_x, src_size_y, &hdr);
if (is_translucent) {
HDC bitmap_dc = ::CreateCompatibleDC(dc);
void* bits = NULL;
HBITMAP hbitmap = ::CreateDIBSection(
bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
DIB_RGB_COLORS, &bits, NULL, 0);
unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth;
for (int row = 0; row < bitmap.height(); ++row) {
int dest_offset = row * dest_row_size;
int src_offset = row * bitmap.rowBytesAsPixels();
memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size);
}
SkASSERT(hbitmap);
HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
DWORD previous_mode = GetStretchBltMode(dc);
BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
SkASSERT(result);
BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
result = GdiAlphaBlend(dc,
x, y,
src_size_x, src_size_y,
bitmap_dc,
0, 0,
src_size_x, src_size_y,
blend_function);
SkASSERT(result);
result = SetStretchBltMode(dc, previous_mode);
SkASSERT(result);
::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
DeleteObject(hbitmap);
DeleteDC(bitmap_dc);
} else {
int nCopied = StretchDIBits(dc,
x, y,
src_size_x, src_size_y,
0, 0,
src_size_x, src_size_y,
pixels,
reinterpret_cast<const BITMAPINFO*>(&hdr),
DIB_RGB_COLORS,
SRCCOPY);
}
EndPlatformPaint();
Cleanup();
}
}