#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "skia/ext/skia_utils_win.h"
#include <windows.h>
#include <stddef.h>
#include "base/check_op.h"
#include "base/debug/gdi_debug_util_win.h"
#include "base/numerics/checked_math.h"
#include "base/win/scoped_hdc.h"
#include "base/win/scoped_hglobal.h"
#include "skia/ext/legacy_display_globals.h"
#include "skia/ext/skia_utils_base.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkTypes.h"
namespace {
static_assert(offsetof(RECT, left) == offsetof(SkIRect, fLeft), "o1");
static_assert(offsetof(RECT, top) == offsetof(SkIRect, fTop), "o2");
static_assert(offsetof(RECT, right) == offsetof(SkIRect, fRight), "o3");
static_assert(offsetof(RECT, bottom) == offsetof(SkIRect, fBottom), "o4");
static_assert(sizeof(RECT().left) == sizeof(SkIRect().fLeft), "o5");
static_assert(sizeof(RECT().top) == sizeof(SkIRect().fTop), "o6");
static_assert(sizeof(RECT().right) == sizeof(SkIRect().fRight), "o7");
static_assert(sizeof(RECT().bottom) == sizeof(SkIRect().fBottom), "o8");
static_assert(sizeof(RECT) == sizeof(SkIRect), "o9");
void CreateBitmapHeaderWithColorDepth(LONG width,
LONG height,
WORD color_depth,
BITMAPINFOHEADER* hdr) {
hdr->biSize = sizeof(BITMAPINFOHEADER);
hdr->biWidth = width;
hdr->biHeight = -height;
hdr->biPlanes = 1;
hdr->biBitCount = color_depth;
hdr->biCompression = BI_RGB;
hdr->biSizeImage = 0;
hdr->biXPelsPerMeter = 1;
hdr->biYPelsPerMeter = 1;
hdr->biClrUsed = 0;
hdr->biClrImportant = 0;
}
void CreateBitmapV5HeaderForARGB8888(LONG width,
LONG height,
LONG image_size,
BITMAPV5HEADER* hdr) {
memset(hdr, 0, sizeof(BITMAPV5HEADER));
hdr->bV5Size = sizeof(BITMAPV5HEADER);
hdr->bV5Width = width;
hdr->bV5Height = height;
hdr->bV5Planes = 1;
hdr->bV5BitCount = 32;
hdr->bV5Compression = BI_RGB;
hdr->bV5AlphaMask = 0xff000000;
hdr->bV5CSType = LCS_WINDOWS_COLOR_SPACE;
hdr->bV5Intent = LCS_GM_IMAGES;
hdr->bV5ClrUsed = 0;
hdr->bV5ClrImportant = 0;
hdr->bV5ProfileData = 0;
}
}
namespace skia {
POINT SkPointToPOINT(const SkPoint& point) {
POINT win_point = {
SkScalarRoundToInt(point.fX), SkScalarRoundToInt(point.fY)
};
return win_point;
}
SkRect RECTToSkRect(const RECT& rect) {
SkRect sk_rect = { SkIntToScalar(rect.left), SkIntToScalar(rect.top),
SkIntToScalar(rect.right), SkIntToScalar(rect.bottom) };
return sk_rect;
}
SkColor COLORREFToSkColor(COLORREF color) {
#ifndef _MSC_VER
return SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color));
#else
return 0xFF000000u | (_byteswap_ulong(color) >> 8);
#endif
}
COLORREF SkColorToCOLORREF(SkColor color) {
#ifndef _MSC_VER
return RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
#else
return (_byteswap_ulong(color) >> 8);
#endif
}
void InitializeDC(HDC context) {
BOOL res = SetGraphicsMode(context, GM_ADVANCED);
SkASSERT(res != 0);
res = SetStretchBltMode(context, HALFTONE);
SkASSERT(res != 0);
res = SetBrushOrgEx(context, 0, 0, NULL);
SkASSERT(res != 0);
res = SetArcDirection(context, AD_CLOCKWISE);
SkASSERT(res != 0);
res = SetBkColor(context, RGB(255, 255, 255));
SkASSERT(res != CLR_INVALID);
res = SetTextColor(context, RGB(0, 0, 0));
SkASSERT(res != CLR_INVALID);
res = SetDCBrushColor(context, RGB(255, 255, 255));
SkASSERT(res != CLR_INVALID);
res = SetDCPenColor(context, RGB(0, 0, 0));
SkASSERT(res != CLR_INVALID);
res = SetBkMode(context, OPAQUE);
SkASSERT(res != 0);
res = SetROP2(context, R2_COPYPEN);
SkASSERT(res != 0);
}
void LoadTransformToDC(HDC dc, const SkMatrix& matrix) {
XFORM xf;
xf.eM11 = matrix[SkMatrix::kMScaleX];
xf.eM21 = matrix[SkMatrix::kMSkewX];
xf.eDx = matrix[SkMatrix::kMTransX];
xf.eM12 = matrix[SkMatrix::kMSkewY];
xf.eM22 = matrix[SkMatrix::kMScaleY];
xf.eDy = matrix[SkMatrix::kMTransY];
SetWorldTransform(dc, &xf);
}
void CopyHDC(HDC source, HDC destination, int x, int y, bool is_opaque,
const RECT& src_rect, const SkMatrix& transform) {
int copy_width = src_rect.right - src_rect.left;
int copy_height = src_rect.bottom - src_rect.top;
SkMatrix identity;
identity.reset();
LoadTransformToDC(source, identity);
if (is_opaque) {
BitBlt(destination,
x,
y,
copy_width,
copy_height,
source,
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(destination,
x,
y,
copy_width,
copy_height,
source,
src_rect.left,
src_rect.top,
copy_width,
copy_height,
blend_function);
}
LoadTransformToDC(source, transform);
}
SkImageInfo PrepareAllocation(HDC context, BITMAP* backing) {
HBITMAP backing_handle =
static_cast<HBITMAP>(GetCurrentObject(context, OBJ_BITMAP));
const size_t backing_size = sizeof *backing;
return (GetObject(backing_handle, backing_size, backing) == backing_size)
? SkImageInfo::MakeN32Premul(backing->bmWidth, backing->bmHeight)
: SkImageInfo();
}
sk_sp<SkSurface> MapPlatformSurface(HDC context) {
BITMAP backing;
const SkImageInfo size(PrepareAllocation(context, &backing));
SkSurfaceProps props = skia::LegacyDisplayGlobals::GetSkSurfaceProps();
return size.isEmpty() ? nullptr
: SkSurfaces::WrapPixels(size, backing.bmBits,
backing.bmWidthBytes, &props);
}
SkBitmap MapPlatformBitmap(HDC context) {
BITMAP backing;
const SkImageInfo size(PrepareAllocation(context, &backing));
SkBitmap bitmap;
if (!size.isEmpty())
bitmap.installPixels(size, backing.bmBits, size.minRowBytes());
return bitmap;
}
void CreateBitmapHeaderForN32SkBitmap(const SkBitmap& bitmap,
BITMAPINFOHEADER* hdr) {
CHECK_EQ(bitmap.colorType(), kN32_SkColorType);
CHECK_EQ(4, bitmap.info().bytesPerPixel());
CHECK_EQ(bitmap.rowBytes(), bitmap.width() * static_cast<size_t>(4));
CreateBitmapHeaderWithColorDepth(bitmap.width(), bitmap.height(), 32, hdr);
}
HGLOBAL CreateHGlobalForByteArray(
const std::vector<unsigned char>& byte_array) {
HGLOBAL hglobal = ::GlobalAlloc(GHND, byte_array.size());
if (!hglobal) {
return nullptr;
}
base::win::ScopedHGlobal<uint8_t*> global_mem(hglobal);
if (!global_mem.data()) {
::GlobalFree(hglobal);
return nullptr;
}
memcpy(global_mem.data(), byte_array.data(), byte_array.size());
return hglobal;
}
HGLOBAL CreateDIBV5ImageDataFromN32SkBitmap(const SkBitmap& bitmap) {
CHECK_EQ(bitmap.colorType(), kN32_SkColorType);
CHECK_EQ(4, bitmap.info().bytesPerPixel());
CHECK_EQ(bitmap.rowBytes(), bitmap.width() * static_cast<size_t>(4));
int width = bitmap.width();
int height = bitmap.height();
size_t bytes;
constexpr size_t bpp = 4;
if (!base::CheckMul(height, base::CheckMul(width, bpp)).AssignIfValid(&bytes))
return nullptr;
HGLOBAL hglobal = ::GlobalAlloc(GHND, sizeof(BITMAPV5HEADER) + bytes);
if (hglobal == nullptr)
return nullptr;
base::win::ScopedHGlobal<BITMAPV5HEADER*> header(hglobal);
if (!header.data()) {
::GlobalFree(hglobal);
return nullptr;
}
CreateBitmapV5HeaderForARGB8888(width, height, bytes, header.data());
auto* dst_pixels =
reinterpret_cast<uint8_t*>(header.data()) + sizeof(BITMAPV5HEADER);
SkImageInfo infoSRGB = bitmap.info()
.makeColorSpace(SkColorSpace::MakeSRGB())
.makeWH(bitmap.width(), 1);
const size_t row_bytes = bitmap.rowBytes();
for (size_t line = 0; line < height; line++) {
size_t flipped_line_index = height - 1 - line;
auto* current_dst = dst_pixels + (row_bytes * flipped_line_index);
bool success = bitmap.readPixels(infoSRGB, current_dst, row_bytes, 0, line);
DCHECK(success);
}
return hglobal;
}
base::win::ScopedGDIObject<HBITMAP> CreateHBitmapFromN32SkBitmap(
const SkBitmap& bitmap) {
BITMAPINFOHEADER header;
CreateBitmapHeaderForN32SkBitmap(bitmap, &header);
int width = bitmap.width();
int height = bitmap.height();
size_t bytes;
const size_t bpp = 4;
if (!base::CheckMul(height, base::CheckMul(width, bpp)).AssignIfValid(&bytes))
return {};
void* bits;
HBITMAP hbitmap;
{
base::win::ScopedGetDC screen_dc(nullptr);
hbitmap =
CreateDIBSection(screen_dc, reinterpret_cast<BITMAPINFO*>(&header),
DIB_RGB_COLORS, &bits, nullptr, 0);
}
if (hbitmap) {
memcpy(bits, bitmap.getPixels(), bytes);
} else {
base::debug::CollectGDIUsageAndDie(&header, nullptr);
}
return base::win::ScopedGDIObject<HBITMAP>(hbitmap);
}
void CreateBitmapHeaderForXRGB888(int width,
int height,
BITMAPINFOHEADER* hdr) {
CreateBitmapHeaderWithColorDepth(width, height, 32, hdr);
}
base::win::ScopedGDIObject<HBITMAP> CreateHBitmapXRGB8888(int width,
int height,
HANDLE shared_section,
void** data) {
if ((width == 0) || (height == 0)) {
width = 1;
height = 1;
}
BITMAPINFOHEADER hdr = {0};
CreateBitmapHeaderWithColorDepth(width, height, 32, &hdr);
HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
0, data, shared_section, 0);
if (!hbitmap)
base::debug::CollectGDIUsageAndDie(&hdr, shared_section);
return base::win::ScopedGDIObject<HBITMAP>(hbitmap);
}
}