aboutsummaryrefslogtreecommitdiffhomepage
path: root/win32/PlatWin.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'win32/PlatWin.cxx')
-rw-r--r--win32/PlatWin.cxx293
1 files changed, 259 insertions, 34 deletions
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx
index 26a351871..3f93a96fd 100644
--- a/win32/PlatWin.cxx
+++ b/win32/PlatWin.cxx
@@ -506,6 +506,14 @@ public:
void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;
XYPOSITION WidthText(const Font *font_, std::string_view text) override;
+
+ void DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
+ void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
+ void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
+ void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
+ void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override;
+ XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override;
+
XYPOSITION Ascent(const Font *font_) override;
XYPOSITION Descent(const Font *font_) override;
XYPOSITION InternalLeading(const Font *font_) override;
@@ -1003,23 +1011,11 @@ void SurfaceGDI::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITIO
}
}
-XYPOSITION SurfaceGDI::WidthText(const Font *font_, std::string_view text) {
- SetFont(font_);
- SIZE sz={0,0};
- if (!unicodeMode) {
- ::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz);
- } else {
- const TextWide tbuf(text, unicodeMode, codePage);
- ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
- }
- return static_cast<XYPOSITION>(sz.cx);
-}
-
void SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {
// Zero positions to avoid random behaviour on failure.
std::fill(positions, positions + text.length(), 0.0f);
SetFont(font_);
- SIZE sz={0,0};
+ SIZE sz = { 0,0 };
int fit = 0;
int i = 0;
const int len = static_cast<int>(text.length());
@@ -1037,7 +1033,7 @@ void SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSIT
if (byteCount == 4) { // Non-BMP
ui++;
}
- for (unsigned int bytePos=0; (bytePos<byteCount) && (i<len); bytePos++) {
+ for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) {
positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);
}
}
@@ -1047,14 +1043,102 @@ void SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSIT
// Eeek - a NULL DC or other foolishness could cause this.
return;
}
- while (i<fit) {
+ while (i < fit) {
positions[i] = static_cast<XYPOSITION>(poses.buffer[i]);
i++;
}
}
// If any positions not filled in then use the last position for them
const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;
- std::fill(positions+i, positions + text.length(), lastPos);
+ std::fill(positions + i, positions + text.length(), lastPos);
+}
+
+XYPOSITION SurfaceGDI::WidthText(const Font *font_, std::string_view text) {
+ SetFont(font_);
+ SIZE sz = { 0,0 };
+ if (!unicodeMode) {
+ ::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz);
+ } else {
+ const TextWide tbuf(text, unicodeMode, codePage);
+ ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
+ }
+ return static_cast<XYPOSITION>(sz.cx);
+}
+
+void SurfaceGDI::DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
+ SetFont(font_);
+ const RECT rcw = RectFromPRectangle(rc);
+ const int x = static_cast<int>(rc.left);
+ const int yBaseInt = static_cast<int>(ybase);
+
+ const TextWide tbuf(text, true);
+ ::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr);
+}
+
+void SurfaceGDI::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
+ ColourDesired fore, ColourDesired back) {
+ ::SetTextColor(hdc, fore.AsInteger());
+ ::SetBkColor(hdc, back.AsInteger());
+ DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE);
+}
+
+void SurfaceGDI::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
+ ColourDesired fore, ColourDesired back) {
+ ::SetTextColor(hdc, fore.AsInteger());
+ ::SetBkColor(hdc, back.AsInteger());
+ DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
+}
+
+void SurfaceGDI::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
+ ColourDesired fore) {
+ // Avoid drawing spaces in transparent mode
+ for (const char ch : text) {
+ if (ch != ' ') {
+ ::SetTextColor(hdc, fore.AsInteger());
+ ::SetBkMode(hdc, TRANSPARENT);
+ DrawTextCommonUTF8(rc, font_, ybase, text, 0);
+ ::SetBkMode(hdc, OPAQUE);
+ return;
+ }
+ }
+}
+
+void SurfaceGDI::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) {
+ // Zero positions to avoid random behaviour on failure.
+ std::fill(positions, positions + text.length(), 0.0f);
+ SetFont(font_);
+ SIZE sz = { 0,0 };
+ int fit = 0;
+ int i = 0;
+ const int len = static_cast<int>(text.length());
+ const TextWide tbuf(text, true);
+ TextPositionsI poses(tbuf.tlen);
+ if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {
+ // Failure
+ return;
+ }
+ // Map the widths given for UTF-16 characters back onto the UTF-8 input string
+ for (int ui = 0; ui < fit; ui++) {
+ const unsigned char uch = text[i];
+ const unsigned int byteCount = UTF8BytesOfLead[uch];
+ if (byteCount == 4) { // Non-BMP
+ ui++;
+ }
+ for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) {
+ positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);
+ }
+ }
+ // If any positions not filled in then use the last position for them
+ const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;
+ std::fill(positions + i, positions + text.length(), lastPos);
+}
+
+XYPOSITION SurfaceGDI::WidthTextUTF8(const Font *font_, std::string_view text) {
+ SetFont(font_);
+ SIZE sz = { 0,0 };
+ const TextWide tbuf(text, true);
+ ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
+ return static_cast<XYPOSITION>(sz.cx);
}
XYPOSITION SurfaceGDI::Ascent(const Font *font_) {
@@ -1208,6 +1292,14 @@ public:
void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;
XYPOSITION WidthText(const Font *font_, std::string_view text) override;
+
+ void DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
+ void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
+ void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
+ void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
+ void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override;
+ XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override;
+
XYPOSITION Ascent(const Font *font_) override;
XYPOSITION Descent(const Font *font_) override;
XYPOSITION InternalLeading(const Font *font_) override;
@@ -2104,24 +2196,6 @@ void SurfaceD2D::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITIO
}
}
-XYPOSITION SurfaceD2D::WidthText(const Font *font_, std::string_view text) {
- FLOAT width = 1.0;
- SetFont(font_);
- const TextWide tbuf(text, unicodeMode, codePageText);
- if (pIDWriteFactory && pTextFormat) {
- // Create a layout
- IDWriteTextLayout *pTextLayout = nullptr;
- const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout);
- if (SUCCEEDED(hr) && pTextLayout) {
- DWRITE_TEXT_METRICS textMetrics;
- if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))
- width = textMetrics.widthIncludingTrailingWhitespace;
- ReleaseUnknown(pTextLayout);
- }
- }
- return width;
-}
-
void SurfaceD2D::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {
SetFont(font_);
if (!pIDWriteFactory || !pTextFormat) {
@@ -2203,6 +2277,157 @@ void SurfaceD2D::MeasureWidths(const Font *font_, std::string_view text, XYPOSIT
}
}
+XYPOSITION SurfaceD2D::WidthText(const Font *font_, std::string_view text) {
+ FLOAT width = 1.0;
+ SetFont(font_);
+ const TextWide tbuf(text, unicodeMode, codePageText);
+ if (pIDWriteFactory && pTextFormat) {
+ // Create a layout
+ IDWriteTextLayout *pTextLayout = nullptr;
+ const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout);
+ if (SUCCEEDED(hr) && pTextLayout) {
+ DWRITE_TEXT_METRICS textMetrics;
+ if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))
+ width = textMetrics.widthIncludingTrailingWhitespace;
+ ReleaseUnknown(pTextLayout);
+ }
+ }
+ return width;
+}
+
+void SurfaceD2D::DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
+ SetFont(font_);
+
+ // Use Unicode calls
+ const TextWide tbuf(text, true);
+ if (pRenderTarget && pTextFormat && pBrush) {
+ if (fuOptions & ETO_CLIPPED) {
+ D2D1_RECT_F rcClip = { rc.left, rc.top, rc.right, rc.bottom };
+ pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);
+ }
+
+ // Explicitly creating a text layout appears a little faster
+ IDWriteTextLayout *pTextLayout = nullptr;
+ const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat,
+ rc.Width(), rc.Height(), &pTextLayout);
+ if (SUCCEEDED(hr)) {
+ D2D1_POINT_2F origin = {rc.left, ybase-yAscent};
+ pRenderTarget->DrawTextLayout(origin, pTextLayout, pBrush, d2dDrawTextOptions);
+ ReleaseUnknown(pTextLayout);
+ }
+
+ if (fuOptions & ETO_CLIPPED) {
+ pRenderTarget->PopAxisAlignedClip();
+ }
+ }
+}
+
+void SurfaceD2D::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
+ ColourDesired fore, ColourDesired back) {
+ if (pRenderTarget) {
+ FillRectangle(rc, back);
+ D2DPenColour(fore);
+ DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE);
+ }
+}
+
+void SurfaceD2D::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
+ ColourDesired fore, ColourDesired back) {
+ if (pRenderTarget) {
+ FillRectangle(rc, back);
+ D2DPenColour(fore);
+ DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
+ }
+}
+
+void SurfaceD2D::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,
+ ColourDesired fore) {
+ // Avoid drawing spaces in transparent mode
+ for (const char ch : text) {
+ if (ch != ' ') {
+ if (pRenderTarget) {
+ D2DPenColour(fore);
+ DrawTextCommonUTF8(rc, font_, ybase, text, 0);
+ }
+ return;
+ }
+ }
+}
+
+void SurfaceD2D::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) {
+ SetFont(font_);
+ if (!pIDWriteFactory || !pTextFormat) {
+ // SetFont failed or no access to DirectWrite so give up.
+ return;
+ }
+ const TextWide tbuf(text, true);
+ TextPositions poses(tbuf.tlen);
+ // Initialize poses for safety.
+ std::fill(poses.buffer, poses.buffer + tbuf.tlen, 0.0f);
+ // Create a layout
+ IDWriteTextLayout *pTextLayout = nullptr;
+ const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 10000.0, 1000.0, &pTextLayout);
+ if (!SUCCEEDED(hrCreate)) {
+ return;
+ }
+ constexpr int clusters = stackBufferLength;
+ DWRITE_CLUSTER_METRICS clusterMetrics[clusters];
+ UINT32 count = 0;
+ const HRESULT hrGetCluster = pTextLayout->GetClusterMetrics(clusterMetrics, clusters, &count);
+ ReleaseUnknown(pTextLayout);
+ if (!SUCCEEDED(hrGetCluster)) {
+ return;
+ }
+ // A cluster may be more than one WCHAR, such as for "ffi" which is a ligature in the Candara font
+ FLOAT position = 0.0f;
+ size_t ti = 0;
+ for (size_t ci = 0; ci < count; ci++) {
+ for (size_t inCluster = 0; inCluster < clusterMetrics[ci].length; inCluster++) {
+ poses.buffer[ti++] = position + clusterMetrics[ci].width * (inCluster + 1) / clusterMetrics[ci].length;
+ }
+ position += clusterMetrics[ci].width;
+ }
+ PLATFORM_ASSERT(ti == static_cast<size_t>(tbuf.tlen));
+ // Map the widths given for UTF-16 characters back onto the UTF-8 input string
+ int ui = 0;
+ size_t i = 0;
+ while (ui < tbuf.tlen) {
+ const unsigned char uch = text[i];
+ const unsigned int byteCount = UTF8BytesOfLead[uch];
+ if (byteCount == 4) { // Non-BMP
+ ui++;
+ }
+ for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < text.length()); bytePos++) {
+ positions[i++] = poses.buffer[ui];
+ }
+ ui++;
+ }
+ XYPOSITION lastPos = 0.0f;
+ if (i > 0)
+ lastPos = positions[i - 1];
+ while (i < text.length()) {
+ positions[i++] = lastPos;
+ }
+}
+
+XYPOSITION SurfaceD2D::WidthTextUTF8(const Font * font_, std::string_view text) {
+ FLOAT width = 1.0;
+ SetFont(font_);
+ const TextWide tbuf(text, true);
+ if (pIDWriteFactory && pTextFormat) {
+ // Create a layout
+ IDWriteTextLayout *pTextLayout = nullptr;
+ const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout);
+ if (SUCCEEDED(hr)) {
+ DWRITE_TEXT_METRICS textMetrics;
+ if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))
+ width = textMetrics.widthIncludingTrailingWhitespace;
+ ReleaseUnknown(pTextLayout);
+ }
+ }
+ return width;
+}
+
XYPOSITION SurfaceD2D::Ascent(const Font *font_) {
SetFont(font_);
return std::ceil(yAscent);