📄 platwin.cxx
字号:
if (paletteOld) {
// Fonts are not deleted as they are owned by a Palette object
::SelectPalette(reinterpret_cast<HDC>(hdc),
reinterpret_cast<HPALETTE>(paletteOld), TRUE);
paletteOld = 0;
}
if (hdcOwned) {
::DeleteDC(reinterpret_cast<HDC>(hdc));
hdc = 0;
hdcOwned = false;
}
}
bool SurfaceImpl::Initialised() {
return hdc != 0;
}
void SurfaceImpl::Init(WindowID) {
Release();
hdc = ::CreateCompatibleDC(NULL);
hdcOwned = true;
::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);
}
void SurfaceImpl::Init(SurfaceID sid, WindowID) {
Release();
hdc = reinterpret_cast<HDC>(sid);
::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);
}
void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID) {
Release();
hdc = ::CreateCompatibleDC(static_cast<SurfaceImpl *>(surface_)->hdc);
hdcOwned = true;
bitmap = ::CreateCompatibleBitmap(static_cast<SurfaceImpl *>(surface_)->hdc, width, height);
bitmapOld = static_cast<HBITMAP>(::SelectObject(hdc, bitmap));
::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);
}
void SurfaceImpl::PenColour(ColourAllocated fore) {
if (pen) {
::SelectObject(hdc, penOld);
::DeleteObject(pen);
pen = 0;
penOld = 0;
}
pen = ::CreatePen(0,1,fore.AsLong());
penOld = static_cast<HPEN>(::SelectObject(reinterpret_cast<HDC>(hdc), pen));
}
void SurfaceImpl::BrushColor(ColourAllocated back) {
if (brush) {
::SelectObject(hdc, brushOld);
::DeleteObject(brush);
brush = 0;
brushOld = 0;
}
// Only ever want pure, non-dithered brushes
ColourAllocated colourNearest = ::GetNearestColor(hdc, back.AsLong());
brush = ::CreateSolidBrush(colourNearest.AsLong());
brushOld = static_cast<HBRUSH>(::SelectObject(hdc, brush));
}
void SurfaceImpl::SetFont(Font &font_) {
if (font_.GetID() != font) {
if (fontOld) {
::SelectObject(hdc, font_.GetID());
} else {
fontOld = static_cast<HFONT>(::SelectObject(hdc, font_.GetID()));
}
font = reinterpret_cast<HFONT>(font_.GetID());
}
}
int SurfaceImpl::LogPixelsY() {
return ::GetDeviceCaps(hdc, LOGPIXELSY);
}
int SurfaceImpl::DeviceHeightFont(int points) {
return ::MulDiv(points, LogPixelsY(), 72);
}
void SurfaceImpl::MoveTo(int x_, int y_) {
::MoveToEx(hdc, x_, y_, 0);
}
void SurfaceImpl::LineTo(int x_, int y_) {
::LineTo(hdc, x_, y_);
}
void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back) {
PenColour(fore);
BrushColor(back);
::Polygon(hdc, reinterpret_cast<POINT *>(pts), npts);
}
void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
PenColour(fore);
BrushColor(back);
::Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
}
void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
// Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
// There is no need to allocate a brush either.
RECT rcw = RectFromPRectangle(rc);
::SetBkColor(hdc, back.AsLong());
::ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rcw, TEXT(""), 0, NULL);
}
void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
HBRUSH br;
if (static_cast<SurfaceImpl &>(surfacePattern).bitmap)
br = ::CreatePatternBrush(static_cast<SurfaceImpl &>(surfacePattern).bitmap);
else // Something is wrong so display in red
br = ::CreateSolidBrush(RGB(0xff, 0, 0));
RECT rcw = RectFromPRectangle(rc);
::FillRect(hdc, &rcw, br);
::DeleteObject(br);
}
void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
PenColour(fore);
BrushColor(back);
::RoundRect(hdc,
rc.left + 1, rc.top,
rc.right - 1, rc.bottom,
8, 8);
}
// Plot a point into a DWORD buffer symetrically to all 4 qudrants
static void AllFour(DWORD *pixels, int width, int height, int x, int y, DWORD val) {
pixels[y*width+x] = val;
pixels[y*width+width-1-x] = val;
pixels[(height-1-y)*width+x] = val;
pixels[(height-1-y)*width+width-1-x] = val;
}
#ifndef AC_SRC_OVER
#define AC_SRC_OVER 0x00
#endif
#ifndef AC_SRC_ALPHA
#define AC_SRC_ALPHA 0x01
#endif
void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
ColourAllocated outline, int alphaOutline, int /* flags*/ ) {
if (AlphaBlendFn) {
HDC hMemDC = ::CreateCompatibleDC(reinterpret_cast<HDC>(hdc));
int width = rc.Width();
int height = rc.Height();
// Ensure not distorted too much by corners when small
cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2);
BITMAPINFO bpih = {sizeof(BITMAPINFOHEADER), width, height, 1, 32, BI_RGB, 0, 0, 0, 0, 0};
void *image = 0;
HBITMAP hbmMem = CreateDIBSection(reinterpret_cast<HDC>(hMemDC), &bpih,
DIB_RGB_COLORS, &image, NULL, 0);
HBITMAP hbmOld = SelectBitmap(hMemDC, hbmMem);
byte pixVal[4] = {0};
DWORD valEmpty = *(reinterpret_cast<DWORD *>(pixVal));
pixVal[0] = static_cast<byte>(GetBValue(fill.AsLong()) * alphaFill / 255);
pixVal[1] = static_cast<byte>(GetGValue(fill.AsLong()) * alphaFill / 255);
pixVal[2] = static_cast<byte>(GetRValue(fill.AsLong()) * alphaFill / 255);
pixVal[3] = static_cast<byte>(alphaFill);
DWORD valFill = *(reinterpret_cast<DWORD *>(pixVal));
pixVal[0] = static_cast<byte>(GetBValue(outline.AsLong()) * alphaOutline / 255);
pixVal[1] = static_cast<byte>(GetGValue(outline.AsLong()) * alphaOutline / 255);
pixVal[2] = static_cast<byte>(GetRValue(outline.AsLong()) * alphaOutline / 255);
pixVal[3] = static_cast<byte>(alphaOutline);
DWORD valOutline = *(reinterpret_cast<DWORD *>(pixVal));
DWORD *pixels = reinterpret_cast<DWORD *>(image);
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
if ((x==0) || (x==width-1) || (y == 0) || (y == height-1)) {
pixels[y*width+x] = valOutline;
} else {
pixels[y*width+x] = valFill;
}
}
}
for (int c=0;c<cornerSize; c++) {
for (int x=0;x<c+1; x++) {
AllFour(pixels, width, height, x, c-x, valEmpty);
}
}
for (int x=1;x<cornerSize; x++) {
AllFour(pixels, width, height, x, cornerSize-x, valOutline);
}
BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
AlphaBlendFn(reinterpret_cast<HDC>(hdc), rc.left, rc.top, width, height, hMemDC, 0, 0, width, height, merge);
SelectBitmap(hMemDC, hbmOld);
::DeleteObject(hbmMem);
::DeleteObject(hMemDC);
} else {
RectangleDraw(rc, outline, fill);
}
}
void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
PenColour(fore);
BrushColor(back);
::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);
}
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
::BitBlt(hdc,
rc.left, rc.top, rc.Width(), rc.Height(),
static_cast<SurfaceImpl &>(surfaceSource).hdc, from.x, from.y, SRCCOPY);
}
const int MAX_US_LEN = 10000;
void SurfaceImpl::DrawTextCommon(PRectangle rc, Font &font_, int ybase, const char *s, int len, UINT fuOptions) {
SetFont(font_);
RECT rcw = RectFromPRectangle(rc);
SIZE sz={0,0};
int pos = 0;
int x = rc.left;
// Text drawing may fail if the text is too big.
// If it does fail, slice up into segments and draw each segment.
const int maxSegmentLength = 0x200;
if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) {
// Use ANSI calls
int lenDraw = Platform::Minimum(len, maxLenText);
if (!::ExtTextOutA(hdc, x, ybase, fuOptions, &rcw, s, lenDraw, NULL)) {
while (lenDraw > pos) {
int seglen = Platform::Minimum(maxSegmentLength, lenDraw - pos);
if (!::ExtTextOutA(hdc, x, ybase, fuOptions, &rcw, s+pos, seglen, NULL)) {
PLATFORM_ASSERT(false);
return;
}
::GetTextExtentPoint32A(hdc, s+pos, seglen, &sz);
x += sz.cx;
pos += seglen;
}
}
} else {
// Use Unicode calls
wchar_t tbuf[MAX_US_LEN];
int tlen;
if (unicodeMode) {
tlen = UCS2FromUTF8(s, len, tbuf, MAX_US_LEN);
} else {
// Support Asian string display in 9x English
tlen = ::MultiByteToWideChar(codePage, 0, s, len, NULL, 0);
if (tlen > MAX_US_LEN)
tlen = MAX_US_LEN;
::MultiByteToWideChar(codePage, 0, s, len, tbuf, tlen);
}
if (!::ExtTextOutW(hdc, x, ybase, fuOptions, &rcw, tbuf, tlen, NULL)) {
while (tlen > pos) {
int seglen = Platform::Minimum(maxSegmentLength, tlen - pos);
if (!::ExtTextOutW(hdc, x, ybase, fuOptions, &rcw, tbuf+pos, seglen, NULL)) {
PLATFORM_ASSERT(false);
return;
}
::GetTextExtentPoint32W(hdc, tbuf+pos, seglen, &sz);
x += sz.cx;
pos += seglen;
}
}
}
}
void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
ColourAllocated fore, ColourAllocated back) {
::SetTextColor(hdc, fore.AsLong());
::SetBkColor(hdc, back.AsLong());
DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE);
}
void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
ColourAllocated fore, ColourAllocated back) {
::SetTextColor(hdc, fore.AsLong());
::SetBkColor(hdc, back.AsLong());
DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE | ETO_CLIPPED);
}
void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
ColourAllocated fore) {
// Avoid drawing spaces in transparent mode
for (int i=0;i<len;i++) {
if (s[i] != ' ') {
::SetTextColor(hdc, fore.AsLong());
::SetBkMode(hdc, TRANSPARENT);
DrawTextCommon(rc, font_, ybase, s, len, 0);
::SetBkMode(hdc, OPAQUE);
return;
}
}
}
int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
SetFont(font_);
SIZE sz={0,0};
if (unicodeMode) {
wchar_t tbuf[MAX_US_LEN];
int tlen = UCS2FromUTF8(s, len, tbuf, MAX_US_LEN);
::GetTextExtentPoint32W(hdc, tbuf, tlen, &sz);
} else if (IsNT() || (codePage==0) || win9xACPSame) {
::GetTextExtentPoint32A(hdc, s, Platform::Minimum(len, maxLenText), &sz);
} else {
// Support Asian string display in 9x English
wchar_t tbuf[MAX_US_LEN];
int tlen = ::MultiByteToWideChar(codePage, 0, s, len, NULL, 0);
::MultiByteToWideChar(codePage, 0, s, len, tbuf, tlen);
::GetTextExtentPoint32W(hdc, tbuf, tlen, &sz);
}
return sz.cx;
}
void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
SetFont(font_);
SIZE sz={0,0};
int fit = 0;
if (unicodeMode) {
wchar_t tbuf[MAX_US_LEN];
int tlen = UCS2FromUTF8(s, len, tbuf, MAX_US_LEN);
int poses[MAX_US_LEN];
fit = tlen;
if (!::GetTextExtentExPointW(hdc, tbuf, tlen, maxWidthMeasure, &fit, poses, &sz)) {
// Likely to have failed because on Windows 9x where function not available
// So measure the character widths by measuring each initial substring
// Turns a linear operation into a qudratic but seems fast enough on test files
for (int widthSS=0; widthSS < tlen; widthSS++) {
::GetTextExtentPoint32W(hdc, tbuf, widthSS+1, &sz);
poses[widthSS] = sz.cx;
}
}
// Map the widths given for UCS-2 characters back onto the UTF-8 input string
int ui=0;
const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
int i=0;
while (ui<fit) {
unsigned char uch = us[i];
positions[i++] = poses[ui];
if (uch >= 0x80) {
if (uch < (0x80 + 0x40 + 0x20)) {
positions[i++] = poses[ui];
} else {
positions[i++] = poses[ui];
positions[i++] = poses[ui];
}
}
ui++;
}
int lastPos = 0;
if (i > 0)
lastPos = positions[i-1];
while (i<len) {
positions[i++] = lastPos;
}
} else if (IsNT() || (codePage==0) || win9xACPSame) {
if (!::GetTextExtentExPoint(hdc, s, Platform::Minimum(len, maxLenText),
maxWidthMeasure, &fit, positions, &sz)) {
// Eeek - a NULL DC or other foolishness could cause this.
// The least we can do is set the positions to zero!
memset(positions, 0, len * sizeof(*positions));
} else if (fit < len) {
// For some reason, such as an incomplete DBCS character
// Not all the positions are filled in so make them equal to end.
for (int i=fit;i<len;i++)
positions[i] = positions[fit-1];
}
} else {
// Support Asian string display in 9x English
wchar_t tbuf[MAX_US_LEN];
int tlen = ::MultiByteToWideChar(codePage, 0, s, len, NULL, 0);
::MultiByteToWideChar(codePage, 0, s, len, tbuf, tlen);
int poses[MAX_US_LEN];
for (int widthSS=0; widthSS<tlen; widthSS++) {
::GetTextExtentPoint32W(hdc, tbuf, widthSS+1, &sz);
poses[widthSS] = sz.cx;
}
int ui = 0;
for (int i=0;i<len;) {
if (::IsDBCSLeadByteEx(codePage, s[i])) {
positions[i] = poses[ui];
positions[i+1] = poses[ui];
i += 2;
} else {
positions[i] = poses[ui];
i++;
}
ui++;
}
}
}
int SurfaceImpl::WidthChar(Font &font_, char ch) {
SetFont(font_);
SIZE sz;
::GetTextExtentPoint32(hdc, &ch, 1, &sz);
return sz.cx;
}
int SurfaceImpl::Ascent(Font &font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return tm.tmAscent;
}
int SurfaceImpl::Descent(Font &font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return tm.tmDescent;
}
int SurfaceImpl::InternalLeading(Font &font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return tm.tmInternalLeading;
}
int SurfaceImpl::ExternalLeading(Font &font_) {
SetFont(font_);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
return tm.tmExternalLeading;
}
int SurfaceImpl::Height(Font &font_) {
SetFont(font_);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -