Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
GDI.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <algorithm>
7#include <cstdio>
8#include <set>
9
13#include "Stroika/Foundation/Execution/Throw.h"
15
16#include "GDI.h"
17
18using namespace Stroika::Foundation;
20
21using namespace Stroika::Frameworks;
22using namespace Stroika::Frameworks::Led;
23
24#if qStroika_Foundation_Common_Platform_Windows
25// Often included by <Windows.h> automaticly, but sometimes people define NOIME or VC_EXTRALEAN, and then we
26// must include this manaully.
27#include <windows.h>
28
29#include <imm.h>
30#endif
31
32#if qStroika_Foundation_Common_Platform_Windows
33// RTL Imaging flags
34#define qUseUniscribeToImage qUniscribeAvailableWithSDK
35#define qUseFakeTTGetWPlacementToImage 1
36#define qUseGetCharPlacementToImage 1
37#endif
38
39#if qUseUniscribeToImage
40#include <Usp10.h>
41#endif
42
43/*
44 * Short term debugging crap to debug X-Windows font issues.
45 */
46#ifndef qDebugFontDetails
47#define qDebugFontDetails qStroika_Foundation_Debug_AssertionsChecked&& qStroika_FeatureSupported_XWindows
48#endif
49
50// Suggestion from Greg Binkerd [gregb@microsoft.com] about SRX021206603127 - LGP 2003-01-02
51#if qUniscribeAvailableWithSDK
52#define qTryScriptToCPX 0
53//#define qTryScriptToCPX 1
54
55#ifndef qTryToOptimizeLongUNISCRIBEScriptOutCalls
56#define qTryToOptimizeLongUNISCRIBEScriptOutCalls 1
57#endif
58
59#endif
60
61#if qStroika_Foundation_Common_Platform_Windows
62/*
63 * Used to use CreateCompatibleBitmap, but as of SPR#1271 try using a DIBSection (of a compatile depth) instead).
64 * This has no noticable effect on normal drawing, but greatly speeds HilightRectangle () code for some computers.
65 */
66#ifndef qUseDIBSectionForOffscreenBitmap
67#define qUseDIBSectionForOffscreenBitmap 1
68#endif
69#endif
70
71#if qStroika_Foundation_Common_Platform_Windows
72inline bool operator== (PALETTEENTRY lhs, COLORREF rhs)
73{
74 return RGB (lhs.peRed, lhs.peGreen, lhs.peBlue) == rhs;
75}
76#endif
77
78#if !qHaveWindowsDIBDefined
79#ifndef BI_BITFIELDS
80#define BI_BITFIELDS 3
81#endif
82#endif
83
84#if qStroika_Foundation_Common_Platform_Windows
85#ifdef _UNICODE
86const bool kRunning32BitGDI = true; // UNICODE only supported on 32GDI (NT or Win2k or Later)
87#else
88const bool kRunning32BitGDI = ((::GetVersion () & 0x80000000) == 0); // I BELIEVE this is how we can test we are under NT!!!
89#endif // Should be a better way to check for 32bit GDI!!!
90#endif
91
92#if qStroika_Foundation_Common_Platform_Windows
93inline void Win32_GetTextExtentExPoint (HDC hdc, const Led_tChar* str, size_t nChars, int maxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize)
94{
95 Require (nChars < static_cast<size_t> (numeric_limits<int>::max ()));
96 Verify (::GetTextExtentExPointW (hdc, str, static_cast<int> (nChars), maxExtent, lpnFit, alpDx, lpSize));
97}
98inline void Win32_GetTextExtentPoint (HDC hdc, const Led_tChar* str, int nChars, LPSIZE lpSize)
99{
100 Verify (::GetTextExtentPointW (hdc, str, nChars, lpSize));
101}
102inline void Win32_TextOut (HDC hdc, int xStart, int yStart, const Led_tChar* str, int nChars)
103{
104 Verify (::TextOutW (hdc, xStart, yStart, str, nChars));
105}
106#endif
107
108#if qStroika_Foundation_Common_Platform_Windows && qUseUniscribeToImage
109
110const size_t kMaxUNISCRIBECharacters = 30000;
111
112/*
113 * Use LoadLibrary/GetProcAddress instead of direct call to avoid having to link with
114 * Usp10.lib. This avoidance allows us to run on systems that don't it installed.
115 */
116struct UniscribeDLL {
117 UniscribeDLL ()
118 : fDLL (::LoadLibrary (_T ("Usp10.dll")))
119 , fScriptItemize (nullptr)
120 , fScriptShape (nullptr)
121 , fScriptPlace (nullptr)
122 , fScriptStringAnalyse (nullptr)
123 , fScriptStringOut (nullptr)
124 , fScriptStringFree (nullptr)
125 , fScriptStringGetLogicalWidths (nullptr)
126 , fScriptString_pcOutChars (nullptr)
127 , fScriptString_pSize (nullptr)
128 , fScriptStringCPtoX (nullptr)
129 {
130 if (fDLL != nullptr) {
131 fScriptItemize = (HRESULT (WINAPI*) (const WCHAR*, int, int, const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int*)) (
132 ::GetProcAddress (fDLL, "ScriptItemize"));
133 fScriptShape = (HRESULT (WINAPI*) (HDC, SCRIPT_CACHE*, const WCHAR*, int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*,
134 int*)) (::GetProcAddress (fDLL, "ScriptShape"));
135 fScriptPlace = (HRESULT (WINAPI*) (HDC, SCRIPT_CACHE*, const WORD*, int, const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*,
136 GOFFSET*, ABC*)) (::GetProcAddress (fDLL, "ScriptPlace"));
137 fScriptStringAnalyse =
138 (HRESULT (WINAPI*) (HDC, const void*, int, int, int, DWORD, int, SCRIPT_CONTROL*, SCRIPT_STATE*, const int*, SCRIPT_TABDEF*,
139 const BYTE*, SCRIPT_STRING_ANALYSIS*)) (::GetProcAddress (fDLL, "ScriptStringAnalyse"));
140 fScriptStringOut = (HRESULT (WINAPI*) (SCRIPT_STRING_ANALYSIS, int, int, UINT, const RECT*, int, int, BOOL)) (
141 ::GetProcAddress (fDLL, "ScriptStringOut"));
142 fScriptStringFree = (HRESULT (WINAPI*) (SCRIPT_STRING_ANALYSIS*)) (::GetProcAddress (fDLL, "ScriptStringFree"));
143 fScriptStringGetLogicalWidths =
144 (HRESULT (WINAPI*) (SCRIPT_STRING_ANALYSIS, int*)) (::GetProcAddress (fDLL, "ScriptStringGetLogicalWidths"));
145 fScriptString_pcOutChars = (const int*(WINAPI*)(SCRIPT_STRING_ANALYSIS)) (::GetProcAddress (fDLL, "ScriptString_pcOutChars"));
146 fScriptString_pSize = (const SIZE*(WINAPI*)(SCRIPT_STRING_ANALYSIS)) (::GetProcAddress (fDLL, "ScriptString_pSize"));
147 fScriptStringCPtoX =
148 (HRESULT (WINAPI*) (SCRIPT_STRING_ANALYSIS, int, BOOL, int*)) (::GetProcAddress (fDLL, "ScriptStringCPtoX"));
149 }
150 }
151 ~UniscribeDLL ()
152 {
153 if (fDLL != nullptr) {
154 Verify (::FreeLibrary (fDLL));
155 }
156 }
157
158 nonvirtual bool IsAvail () const
159 {
160 return fDLL != nullptr;
161 }
162
163 HRESULT WINAPI ScriptItemize (const WCHAR* pwcInChars, int cInChars, int cMaxItems, const SCRIPT_CONTROL* psControl,
164 const SCRIPT_STATE* psState, SCRIPT_ITEM* pItems, int* pcItems)
165 {
166 if (fScriptItemize == nullptr) {
167 return E_FAIL;
168 }
169 return (*fScriptItemize) (pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pcItems);
170 }
171
172 HRESULT WINAPI ScriptShape (HDC hdc, SCRIPT_CACHE* psc, const WCHAR* pwcChars, int cChars, int cMaxGlyphs, SCRIPT_ANALYSIS* psa,
173 WORD* pwOutGlyphs, WORD* pwLogClust, SCRIPT_VISATTR* psva, int* pcGlyphs)
174 {
175 if (fScriptShape == nullptr) {
176 return E_FAIL;
177 }
178 return (*fScriptShape) (hdc, psc, pwcChars, cChars, cMaxGlyphs, psa, pwOutGlyphs, pwLogClust, psva, pcGlyphs);
179 }
180
181 HRESULT WINAPI ScriptPlace (HDC hdc, SCRIPT_CACHE* psc, const WORD* pwGlyphs, int cGlyphs, const SCRIPT_VISATTR* psva,
182 SCRIPT_ANALYSIS* psa, int* piAdvance, GOFFSET* pGoffset, ABC* pABC)
183 {
184 if (fScriptPlace == nullptr) {
185 return E_FAIL;
186 }
187 return (*fScriptPlace) (hdc, psc, pwGlyphs, cGlyphs, psva, psa, piAdvance, pGoffset, pABC);
188 }
189
190 HRESULT WINAPI ScriptStringAnalyse (HDC hdc, const void* pString, int cString, int cGlyphs, int iCharset, DWORD dwFlags, int iReqWidth,
191 SCRIPT_CONTROL* psControl, SCRIPT_STATE* psState, const int* piDx, SCRIPT_TABDEF* pTabdef,
192 const BYTE* pbInClass, SCRIPT_STRING_ANALYSIS* pssa)
193 {
194 if (fScriptStringAnalyse == nullptr) {
195 return E_FAIL;
196 }
197 return (*fScriptStringAnalyse) (hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth, psControl, psState, piDx, pTabdef, pbInClass, pssa);
198 }
199
200 HRESULT WINAPI ScriptStringOut (SCRIPT_STRING_ANALYSIS ssa, int iX, int iY, UINT uOptions, const RECT* prc, int iMinSel, int iMaxSel, BOOL fDisabled)
201 {
202 if (fScriptStringOut == nullptr) {
203 return E_FAIL;
204 }
205 return (*fScriptStringOut) (ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
206 }
207
208 HRESULT WINAPI ScriptStringFree (SCRIPT_STRING_ANALYSIS* pssa)
209 {
210 if (fScriptStringFree == nullptr) {
211 return E_FAIL;
212 }
213 return (*fScriptStringFree) (pssa);
214 }
215
216 HRESULT WINAPI ScriptStringGetLogicalWidths (SCRIPT_STRING_ANALYSIS ssa, int* piDx)
217 {
218 if (fScriptStringGetLogicalWidths == nullptr) {
219 return E_FAIL;
220 }
221 return (*fScriptStringGetLogicalWidths) (ssa, piDx);
222 }
223
224 const int* WINAPI ScriptString_pcOutChars (SCRIPT_STRING_ANALYSIS ssa)
225 {
226 if (fScriptString_pcOutChars == nullptr) {
227 return nullptr;
228 }
229 return (*fScriptString_pcOutChars) (ssa);
230 }
231
232 const SIZE* WINAPI ScriptString_pSize (SCRIPT_STRING_ANALYSIS ssa)
233 {
234 if (fScriptString_pSize == nullptr) {
235 return nullptr;
236 }
237 return (*fScriptString_pSize) (ssa);
238 }
239
240 HRESULT WINAPI ScriptStringCPtoX (SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
241 {
242 if (fScriptStringCPtoX == nullptr) {
243 return E_FAIL;
244 }
245 return (*fScriptStringCPtoX) (ssa, icp, fTrailing, pX);
246 }
247
248 HINSTANCE fDLL;
249 HRESULT (WINAPI* fScriptItemize)
250 (const WCHAR*, int, int, const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int*);
251 HRESULT (WINAPI* fScriptShape)
252 (HDC, SCRIPT_CACHE*, const WCHAR*, int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*, int*);
253 HRESULT (WINAPI* fScriptPlace)
254 (HDC, SCRIPT_CACHE*, const WORD*, int, const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*, GOFFSET*, ABC*);
255 HRESULT (WINAPI* fScriptStringAnalyse)
256 (HDC, const void*, int, int, int, DWORD, int, SCRIPT_CONTROL*, SCRIPT_STATE*, const int*, SCRIPT_TABDEF*, const BYTE*, SCRIPT_STRING_ANALYSIS*);
257 HRESULT (WINAPI* fScriptStringOut)
258 (SCRIPT_STRING_ANALYSIS, int, int, UINT, const RECT*, int, int, BOOL);
259 HRESULT (WINAPI* fScriptStringFree)
260 (SCRIPT_STRING_ANALYSIS*);
261 HRESULT (WINAPI* fScriptStringGetLogicalWidths)
262 (SCRIPT_STRING_ANALYSIS, int*);
263 const int*(WINAPI* fScriptString_pcOutChars) (SCRIPT_STRING_ANALYSIS);
264 const SIZE*(WINAPI* fScriptString_pSize) (SCRIPT_STRING_ANALYSIS);
265 HRESULT (WINAPI* fScriptStringCPtoX)
266 (SCRIPT_STRING_ANALYSIS, int, BOOL, int*);
267};
268static UniscribeDLL sUniscribeDLL;
269#endif
270
271namespace {
272 inline bool IS_WIN30_DIB (const Led_DIB* dib)
273 {
274 // Logic from MSFT DibLook sample in MSVC.Net 2003
275 RequireNotNull (dib);
276 const BITMAPINFOHEADER& hdr = dib->bmiHeader;
277 return Led_ByteSwapFromWindows (hdr.biSize) == sizeof (BITMAPINFOHEADER);
278 }
279 inline size_t DIBNumColors (const Led_DIB* dib)
280 {
281 // Logic from MSFT DibLook sample in MSVC.Net 2003
282 RequireNotNull (dib);
283 const BITMAPINFOHEADER& hdr = dib->bmiHeader;
284
285 /* If this is a Windows-style DIB, the number of colors in the
286 * color table can be less than the number of bits per pixel
287 * allows for (i.e. lpbi->biClrUsed can be set to some value).
288 * If this is the case, return the appropriate value.
289 */
290 if (IS_WIN30_DIB (dib)) {
291 unsigned long clrUsed = Led_ByteSwapFromWindows (hdr.biClrUsed);
292 if (clrUsed != 0) {
293 return clrUsed;
294 }
295 }
296
297 /* Calculate the number of colors in the color table based on
298 * the number of bits per pixel for the DIB.
299 */
300 unsigned short bitCount = IS_WIN30_DIB (dib) ? Led_ByteSwapFromWindows (hdr.biBitCount)
301 : Led_ByteSwapFromWindows (((const BITMAPCOREHEADER*)dib)->bcBitCount);
302
303 /* return number of colors based on bits per pixel */
304 switch (bitCount) {
305 case 1:
306 return 2;
307 case 4:
308 return 16;
309 case 8:
310 return 256;
311 default:
312 return 0;
313 }
314 }
315}
316
317#if qStroika_Foundation_Common_Platform_Windows
318namespace {
319
320 inline RGBQUAD mkRGBQuad (COLORREF c)
321 {
322 RGBQUAD r;
323 r.rgbBlue = GetBValue (c);
324 r.rgbGreen = GetGValue (c);
325 r.rgbRed = GetRValue (c);
326 r.rgbReserved = 0;
327 return r;
328 }
329 inline void MaybeAddColorRefToTable_ (RGBQUAD colorTable[256], size_t* iP, COLORREF c)
330 {
331 Assert (sizeof (RGBQUAD) == sizeof (COLORREF));
332 COLORREF* ct = reinterpret_cast<COLORREF*> (colorTable); // use COLORREF instead of RGBQUAD cuz same size but COLOREF has op== defined
333 if (find (ct, ct + *iP, c) == ct + *iP and c != RGB (255, 255, 255)) {
334 colorTable[*iP] = mkRGBQuad (c);
335 ++(*iP);
336 }
337 }
338
339 void CreateStandardColorTable (RGBQUAD colorTable[256], COLORREF c1 = RGB (0, 0, 0), COLORREF c2 = RGB (0, 0, 0),
340 COLORREF c3 = RGB (0, 0, 0), COLORREF c4 = RGB (0, 0, 0))
341 {
342 /*
343 * Cannot use ::GetStockObject (DEFAULT_PALETTE) - because - believe it or not - it returns a 20-entry pallete.
344 */
345 (void)::memset (colorTable, 0, sizeof (RGBQUAD) * 256);
346
347 const BYTE kColorSpecVals[] = {0, 32, 64, 128, 192, 255};
348 const size_t kColorSpecValCnt = sizeof (kColorSpecVals) / sizeof (kColorSpecVals[0]);
349
350 size_t i = 0;
351 {
352 /*
353 * Fill in the color table with all X x X x X entries where X varies over the
354 * kColorSpecVals. This gives us a pretty good coverage of colors.
355 */
356 size_t redIdx = 0;
357 size_t greenIdx = 0;
358 size_t blueIdx = 0;
359 for (; i < 256; ++i) {
360 colorTable[i].rgbRed = kColorSpecVals[redIdx];
361 colorTable[i].rgbGreen = kColorSpecVals[greenIdx];
362 colorTable[i].rgbBlue = kColorSpecVals[blueIdx];
363 ++blueIdx;
364 if (blueIdx >= kColorSpecValCnt) {
365 blueIdx = 0;
366 ++greenIdx;
367 if (greenIdx >= kColorSpecValCnt) {
368 greenIdx = 0;
369 ++redIdx;
370 if (redIdx >= kColorSpecValCnt) {
371 Assert (i == kColorSpecValCnt * kColorSpecValCnt * kColorSpecValCnt - 1);
372 break;
373 }
374 }
375 }
376 }
377 }
378 /*
379 * Above algorithm wrote out WHITE at the end (RGB (255,255,255)). We will OVERWRITE that item with other stuff,
380 * (shades of gray) to save WHITE for the very end.
381 */
382
383 --i; // don't count the WHITE color just added. It will be forced to be last.
384 size_t nColorsLeft = 255 - i;
385 Assert (nColorsLeft == 41);
386
387 // Check each color and see if needs to be added. Do this BEFORE removing white so we don't need to check
388 // that specially
389 MaybeAddColorRefToTable_ (colorTable, &i, c1);
390 MaybeAddColorRefToTable_ (colorTable, &i, c2);
391 MaybeAddColorRefToTable_ (colorTable, &i, c3);
392 MaybeAddColorRefToTable_ (colorTable, &i, c4);
393
394 nColorsLeft = 255 - i;
395 Assert (nColorsLeft > 0);
396
397 size_t aveSpace = 255 / nColorsLeft;
398
399 BYTE startAt = static_cast<BYTE> (aveSpace);
400 while (i < 254 and startAt < 255) {
401 COLORREF c = RGB (startAt, startAt, startAt);
402 Assert (sizeof (RGBQUAD) == sizeof (COLORREF));
403 COLORREF* ct = reinterpret_cast<COLORREF*> (colorTable); // use COLORREF instead of RGBQUAD cuz same size but COLOREF has op== defined
404 if (find (ct, ct + i, c) == ct + i) {
405 colorTable[i] = mkRGBQuad (c);
406 ++i;
407 --nColorsLeft;
408 }
409 startAt += static_cast<BYTE> (aveSpace);
410 }
411
412 Assert (nColorsLeft == 255 - i);
413
414 Assert (nColorsLeft < 5); // I'm pretty sure this has to get us under 5 - or pretty close...
415 Assert (nColorsLeft >= 1); // I'm pretty sure this has to get us under 5 - or pretty close...
416
417 for (; i <= 255; ++i) {
418 colorTable[i].rgbRed = static_cast<BYTE> (i);
419 colorTable[i].rgbGreen = static_cast<BYTE> (i);
420 colorTable[i].rgbBlue = static_cast<BYTE> (i);
421 }
422 }
423
424 HPALETTE CreatePaletteForColorTable (const RGBQUAD colorTable[256])
425 {
426 /*
427 * Cannot use ::GetStockObject (DEFAULT_PALETTE) - because - believe it or not - it returns a 20-entry pallete.
428 */
429 Memory::StackBuffer<char> palBuf{sizeof (LOGPALETTE) + sizeof (PALETTEENTRY) * 256};
430 LPLOGPALETTE lplgPal = reinterpret_cast<LPLOGPALETTE> (static_cast<char*> (palBuf));
431
432 lplgPal->palVersion = 0x300;
433 lplgPal->palNumEntries = 256;
434
435 for (size_t i = 0; i <= 255; ++i) {
436 lplgPal->palPalEntry[i].peRed = colorTable[i].rgbRed;
437 lplgPal->palPalEntry[i].peGreen = colorTable[i].rgbGreen;
438 lplgPal->palPalEntry[i].peBlue = colorTable[i].rgbBlue;
439 lplgPal->palPalEntry[i].peFlags = 0;
440 }
441 return ::CreatePalette (lplgPal);
442 }
443
444 HPALETTE CreateStandardPalette (COLORREF c1, COLORREF c2, COLORREF c3, COLORREF c4)
445 {
446 RGBQUAD colorTable[256];
447 CreateStandardColorTable (colorTable, c1, c2, c3, c4);
448 return CreatePaletteForColorTable (colorTable);
449 }
450
451 HBITMAP Create8BitDIBSection (HDC refDC, DWORD dwX, DWORD dwY, const RGBQUAD* colorTable = nullptr, LPBYTE* ppBits = nullptr)
452 {
453 LPBYTE ignored = nullptr;
454 if (ppBits == nullptr) {
455 ppBits = &ignored;
456 }
457
458 const WORD wBits = 8;
459
460 RGBQUAD colorTableBuf[256];
461 if (colorTable == nullptr) {
462 CreateStandardColorTable (colorTableBuf);
463 colorTable = colorTableBuf;
464 }
465
466 // Create the header big enough to contain color table and bitmasks if needed
467 size_t nInfoSize = sizeof (BITMAPINFOHEADER) + sizeof (RGBQUAD) * (1 << wBits);
468 Memory::StackBuffer<char> bmiBuf{nInfoSize};
469 LPBITMAPINFO pbmi = reinterpret_cast<LPBITMAPINFO> (static_cast<char*> (bmiBuf));
470 (void)::memset (pbmi, 0, nInfoSize);
471 pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
472 pbmi->bmiHeader.biWidth = dwX;
473 pbmi->bmiHeader.biHeight = dwY;
474 pbmi->bmiHeader.biPlanes = 1;
475 pbmi->bmiHeader.biBitCount = wBits;
476 pbmi->bmiHeader.biCompression = BI_RGB; // OVERRIDE below for 16 and 32bpp
477
478 for (int i = 0; i < 256; ++i) {
479 pbmi->bmiColors[i].rgbRed = colorTable[i].rgbRed;
480 pbmi->bmiColors[i].rgbGreen = colorTable[i].rgbGreen;
481 pbmi->bmiColors[i].rgbBlue = colorTable[i].rgbBlue;
482 pbmi->bmiColors[i].rgbReserved = 0;
483 }
484 pbmi->bmiHeader.biClrUsed = 256;
485
486 return ::CreateDIBSection (refDC, pbmi, DIB_RGB_COLORS, (void**)ppBits, nullptr, 0);
487 }
488
489 HBITMAP Create16BitDIBSection (HDC refDC, DWORD dwX, DWORD dwY)
490 {
491 // Create the header big enough to contain color table and bitmasks if needed
492 size_t nInfoSize = sizeof (BITMAPINFOHEADER) + 3 * sizeof (DWORD);
493 Memory::StackBuffer<char> bmiBuf{nInfoSize};
494 LPBITMAPINFO pbmi = reinterpret_cast<LPBITMAPINFO> (static_cast<char*> (bmiBuf));
495 (void)::memset (pbmi, 0, nInfoSize);
496 pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
497 pbmi->bmiHeader.biWidth = dwX;
498 pbmi->bmiHeader.biHeight = dwY;
499 pbmi->bmiHeader.biPlanes = 1;
500 pbmi->bmiHeader.biBitCount = 16;
501 {
502 // if it's 16bpp, fill in the masks and OVERRIDE the compression
503 // these are the default masks - you could change them if needed
504 LPDWORD pMasks = (LPDWORD)(pbmi->bmiColors);
505 pMasks[0] = 0x00007c00;
506 pMasks[1] = 0x000003e0;
507 pMasks[2] = 0x0000001f;
508 pbmi->bmiHeader.biCompression = BI_BITFIELDS;
509 }
510 LPBYTE pBits = 0;
511 return ::CreateDIBSection (refDC, pbmi, DIB_RGB_COLORS, (void**)&pBits, nullptr, 0);
512 }
513
514 HBITMAP Create32BitDIBSection (HDC refDC, DWORD dwX, DWORD dwY)
515 {
516 // Create the header big enough to contain color table and bitmasks if needed
517 size_t nInfoSize = sizeof (BITMAPINFOHEADER) + 3 * sizeof (DWORD);
518 Memory::StackBuffer<char> bmiBuf{nInfoSize};
519 LPBITMAPINFO pbmi = reinterpret_cast<LPBITMAPINFO> (static_cast<char*> (bmiBuf));
520 (void)::memset (pbmi, 0, nInfoSize);
521 pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
522 pbmi->bmiHeader.biWidth = dwX;
523 pbmi->bmiHeader.biHeight = dwY;
524 pbmi->bmiHeader.biPlanes = 1;
525 pbmi->bmiHeader.biBitCount = 32;
526 {
527 // if it's 32bpp, fill in the masks and OVERRIDE the compression
528 // these are the default masks - you could change them if needed
529 LPDWORD pMasks = (LPDWORD)(pbmi->bmiColors);
530 pMasks[0] = 0x00ff0000;
531 pMasks[1] = 0x0000ff00;
532 pMasks[2] = 0x000000ff;
533 pbmi->bmiHeader.biCompression = BI_BITFIELDS;
534 }
535 LPBYTE pBits;
536 return ::CreateDIBSection (refDC, pbmi, DIB_RGB_COLORS, (void**)&pBits, nullptr, 0);
537 }
538}
539#endif
540
541#if qStroika_Foundation_Common_Platform_Windows && qUseFakeTTGetWPlacementToImage
542static bool Win9x_Workaround_GetCharPlacementFunction (HDC hdc, const wchar_t* srcText, size_t len, wchar_t* glyphImagesOut);
543#endif
544
545#if qStroika_Foundation_Common_Platform_Windows
546/*
547 ********************************************************************************
548 ************************************ Bitmap ********************************
549 ********************************************************************************
550 */
551BOOL Bitmap::CreateCompatibleBitmap (HDC hdc, DistanceType nWidth, DistanceType nHeight)
552{
553 Assert (m_hObject == nullptr);
554 m_hObject = ::CreateCompatibleBitmap (hdc, nWidth, nHeight);
555 fImageSize = Led_Size (nHeight, nWidth);
556 return (m_hObject != nullptr); // return value backward compat hack...
557}
558
559BOOL Bitmap::CreateCompatibleDIBSection (HDC hdc, DistanceType nWidth, DistanceType nHeight)
560{
561 RequireNotNull (hdc);
562 Require (m_hObject == nullptr);
563 int useDepth = 16; // default to DIBSection depth - seems to work pretty well in most cases
564
565 fImageSize = Led_Size (nHeight, nWidth);
566
567 int deviceDepth = ::GetDeviceCaps (hdc, BITSPIXEL) * ::GetDeviceCaps (hdc, PLANES);
568 if (deviceDepth > 16) {
569 useDepth = 32;
570 }
571 else if (deviceDepth <= 8) {
572 useDepth = 8;
573 }
574 switch (useDepth) {
575 case 8:
576 m_hObject = Create8BitDIBSection (hdc, nWidth, nHeight);
577 break;
578 case 16:
579 m_hObject = Create16BitDIBSection (hdc, nWidth, nHeight);
580 break;
581 case 32:
582 m_hObject = Create32BitDIBSection (hdc, nWidth, nHeight);
583 break;
584 default:
585 Assert (false); //NotReached
586 }
587 return m_hObject != nullptr; // return value backward compat hack...
588}
589
590void Bitmap::LoadBitmap (HINSTANCE hInstance, LPCTSTR lpBitmapName)
591{
592 Require (m_hObject == nullptr);
593 m_hObject = ::LoadBitmap (hInstance, lpBitmapName);
594 Execution::ThrowIfNull (m_hObject);
595 {
596 BITMAP bm{};
597 Verify (::GetObject (m_hObject, sizeof (BITMAP), (LPVOID)&bm));
598 fImageSize = Led_Size (bm.bmHeight, bm.bmWidth);
599 }
600}
601#endif
602
603/*
604 ********************************************************************************
605 ************************************** Color ***********************************
606 ********************************************************************************
607 */
608/*
609 * Color name values from page 79 of Web Design in a Nutshell, O'Reilly, table 5-2.
610 */
611const Color Color::kBlack = Color (0, 0, 0);
612const Color Color::kWhite = Color (Color::kColorValueMax, Color::kColorValueMax, Color::kColorValueMax);
613const Color Color::kRed = Color (Color::kColorValueMax, 0, 0);
614const Color Color::kGreen = Color (0, Color::kColorValueMax / 2, 0);
615const Color Color::kBlue = Color (0, 0, Color::kColorValueMax);
616const Color Color::kCyan = Color (0, Color::kColorValueMax, Color::kColorValueMax);
617const Color Color::kMagenta = Color (Color::kColorValueMax, 0, Color::kColorValueMax);
618const Color Color::kYellow = Color (Color::kColorValueMax, Color::kColorValueMax, 0);
619const Color Color::kMaroon = Color (Color::kColorValueMax / 2, 0, 0);
620const Color Color::kOlive = Color (Color::kColorValueMax / 2, Color::kColorValueMax / 2, 0);
621const Color Color::kNavyBlue = Color (0, 0, Color::kColorValueMax / 2);
622const Color Color::kPurple = Color (Color::kColorValueMax / 2, 0, Color::kColorValueMax / 2);
623const Color Color::kTeal = Color (0, Color::kColorValueMax / 2, Color::kColorValueMax / 2);
624const Color Color::kGray = Color (Color::kColorValueMax / 2, Color::kColorValueMax / 2, Color::kColorValueMax / 2);
625const Color Color::kSilver = Color (Color::kColorValueMax * 3 / 4, Color::kColorValueMax * 3 / 4, Color::kColorValueMax * 3 / 4);
626const Color Color::kDarkGreen = Color (0, (Color::kColorValueMax / 256) * 100, 0);
627const Color Color::kLimeGreen = Color (0, Color::kColorValueMax, 0);
628const Color Color::kFuchsia = Color::kMagenta; // same according to that table
629const Color Color::kAqua = Color::kCyan; // same according to that table
630
631/*
632 ********************************************************************************
633 ****************************** FontSpecification ***************************
634 ********************************************************************************
635 */
636#if qStroika_FeatureSupported_XWindows
637string FontSpecification::mkOSRep (const string& foundry, const string& family, const string& weight, const string& slant, const string& pointSize)
638{
639 char hRes[1024];
640 (void)::sprintf (hRes, "%d", Globals::Get ().GetMainScreenLogPixelsH ());
641 char vRes[1024];
642 (void)::sprintf (vRes, "%d", Globals::Get ().GetMainScreenLogPixelsV ());
643 string result = "-" + foundry + "-" + family + "-" + weight + "-" + slant + "-*-*-*-" + pointSize + "-" + hRes + "-" + vRes + "-*-*-*-*";
644 return result;
645}
646
647string FontSpecification::GetOSRep () const
648{
649 string foundry = "*";
650 string weight = fBold ? "bold" : "medium";
651 string slant = fItalics ? "i" : "r";
652 char pointSize[1024];
653 (void)::sprintf (pointSize, "%d", GetPointSize () * 10);
654 return mkOSRep (foundry, fFontFamily, weight, slant, pointSize);
655}
656
657void FontSpecification::SetFromOSRep (const string& osRep)
658{
659 SDKString familyName;
660 SDKString fontSize;
661 SDKString fontWeight;
662 SDKString fontSlant;
663 Tablet::ParseFontName (osRep, &familyName, &fontSize, &fontWeight, &fontSlant);
664 SetFontName (familyName);
665 if (fontSlant == "i") {
666 SetStyle_Italic (true);
667 }
668 else if (fontSlant == "r") {
669 SetStyle_Italic (false);
670 }
671 if (fontWeight == "bold") {
672 SetStyle_Bold (true);
673 }
674 else if (fontWeight == "medium") {
675 SetStyle_Bold (false);
676 }
677 int fspPointSize = GetPointSize ();
678 if (::sscanf (fontSize.c_str (), "%d", &fspPointSize) == 1) {
679 fspPointSize /= 10;
680 if (fspPointSize < 5) {
681 fspPointSize = 5;
682 }
683 if (fspPointSize > 100) {
684 fspPointSize = 100;
685 }
686 SetPointSize (fspPointSize);
687 }
688}
689#endif
690
691/*
692@METHOD: FontSpecification::SetFontName
693@DESCRIPTION: <p>See also @'FontSpecification::GetFontName'.</p>
694*/
695void FontSpecification::SetFontName (const SDKString& fontName)
696{
697#if qStroika_Foundation_Common_Platform_Windows
698 Characters::CString::Copy (fFontInfo.lfFaceName, Memory::NEltsOf (fFontInfo.lfFaceName), fontName.c_str ());
699 fFontInfo.lfCharSet = DEFAULT_CHARSET;
700#elif qStroika_FeatureSupported_XWindows
701 fFontFamily = fontName;
702#endif
703}
704
705#if qStroika_Foundation_Common_Platform_Windows
706FontSpecification::FontNameSpecifier::FontNameSpecifier (const Characters::SDKChar* from)
707{
708 Characters::CString::Copy (fName, Memory::NEltsOf (fName), from);
709}
710#endif
711
712void FontSpecification::SetFontNameSpecifier (FontNameSpecifier fontNameSpecifier)
713{
714#if qStroika_Foundation_Common_Platform_Windows
715 Characters::CString::Copy (fFontInfo.lfFaceName, Memory::NEltsOf (fFontInfo.lfFaceName), fontNameSpecifier.fName);
716 fFontInfo.lfCharSet = DEFAULT_CHARSET;
717#elif qStroika_FeatureSupported_XWindows
718 fFontFamily = fontNameSpecifier;
719#endif
720}
721
722#if qStroika_Foundation_Common_Platform_Windows
723struct FontSelectionInfo {
724 FontSelectionInfo (BYTE desiredCharset)
725 : fDesiredCharset (desiredCharset)
726 , fUsesBestCharset (false)
727 , fIsTT (false)
728 , fIsANSI_VAR_Font (false)
729 , fIsGoodBackupCharset (false)
730 , fIsFavoriteForCharset (false)
731 , fIsVariablePitch (false)
732 , fStartsWithAt (false)
733 {
734 memset (&fBestFont, 0, sizeof (fBestFont));
735 }
736 BYTE fDesiredCharset;
737 LOGFONT fBestFont;
738 bool fUsesBestCharset;
739 bool fIsTT;
740 bool fIsANSI_VAR_Font; // a good second choice if Arial not present
741 bool fIsGoodBackupCharset;
742 bool fIsFavoriteForCharset;
743 bool fIsVariablePitch;
744 bool fStartsWithAt;
745 inline int Score () const
746 {
747 int score = 0;
748 if (fUsesBestCharset) {
749 score += 10; // underweight - because (at least with Win2K) - the fCharset field almost always set to zero - so cannot be used reliably
750 }
751 if (fIsTT) {
752 score += 20;
753 }
754 if (fIsANSI_VAR_Font) {
755 score += 4;
756 }
757 if (fIsGoodBackupCharset) {
758 score += 2;
759 }
760 if (fIsFavoriteForCharset) {
761 score += 25;
762 }
763 if (fIsVariablePitch) {
764 //unsure if this is desirable or not - good for US - but not sure about Japan?
765 score += 2;
766 }
767 if (fStartsWithAt) {
768 score -= 25; // we don't really support vertical fonts
769 }
770 return score;
771 }
772};
773static int FAR PASCAL EnumFontCallback (const LOGFONT* lplf, const TEXTMETRIC* /*lpntm*/, DWORD fontType, LPARAM arg)
774{
775 // Score each font choice, and pick the 'best' one. Pick randomly if several are 'best'.
776 RequireNotNull (lplf);
777 FontSelectionInfo& result = *(FontSelectionInfo*)arg;
778
779 static LOGFONT theANSILogFont;
780 if (theANSILogFont.lfFaceName[0] == '\0') {
781 HFONT xxx = HFONT (::GetStockObject (ANSI_VAR_FONT));
782 Verify (::GetObject (xxx, sizeof theANSILogFont, &theANSILogFont));
783 }
784
785 FontSelectionInfo potentialResult = result;
786 memcpy (&potentialResult.fBestFont, lplf, sizeof (LOGFONT));
787
788 if (potentialResult.fDesiredCharset == DEFAULT_CHARSET) {
789 potentialResult.fUsesBestCharset = bool (lplf->lfCharSet == theANSILogFont.lfCharSet);
790 }
791 else {
792 potentialResult.fUsesBestCharset = bool (lplf->lfCharSet == potentialResult.fDesiredCharset);
793 }
794 potentialResult.fIsTT = bool (fontType & TRUETYPE_FONTTYPE);
795 potentialResult.fIsANSI_VAR_Font = ::_tcscmp (lplf->lfFaceName, theANSILogFont.lfFaceName) == 0;
796 potentialResult.fIsGoodBackupCharset = (lplf->lfCharSet == DEFAULT_CHARSET or lplf->lfCharSet == theANSILogFont.lfCharSet);
797 {
798 switch (potentialResult.fDesiredCharset) {
799 case SHIFTJIS_CHARSET: {
800 if (potentialResult.fBestFont.lfFaceName == SDKString{Led_SDK_TCHAROF ("MS P Gothic")} or
801 potentialResult.fBestFont.lfFaceName == SDKString{Led_SDK_TCHAROF ("MS Gothic")} or
802 potentialResult.fBestFont.lfFaceName == SDKString{Led_SDK_TCHAROF ("MS PGothic")}) {
803 potentialResult.fIsFavoriteForCharset = true;
804 }
805 } break;
806 case CHINESEBIG5_CHARSET: {
807 if (potentialResult.fBestFont.lfFaceName == SDKString{Led_SDK_TCHAROF ("MS HEI")} or
808 potentialResult.fBestFont.lfFaceName == SDKString{Led_SDK_TCHAROF ("MingLiU")}) {
809 potentialResult.fIsFavoriteForCharset = true;
810 }
811 } break;
812 }
813 }
814 potentialResult.fIsVariablePitch = lplf->lfPitchAndFamily & VARIABLE_PITCH;
815 potentialResult.fStartsWithAt = lplf->lfFaceName[0] == '@';
816
817 if (potentialResult.Score () > result.Score ()) {
818 result = potentialResult;
819 }
820 return 1;
821}
822#endif
823FontSpecification Led::GetStaticDefaultFont ()
824{
825 // Since this can be called alot, and since its value shouldn't change during the lifetime
826 // of Led running, cache the result (and since on windows - at least - it is expensive to compute)
827 static bool sDefaultFontValid = false;
828 static FontSpecification sDefaultFont;
829 if (not sDefaultFontValid) {
830#if qStroika_Foundation_Common_Platform_Windows
831 sDefaultFont = GetStaticDefaultFont (DEFAULT_CHARSET);
832#elif qStroika_FeatureSupported_XWindows
833 {
834 sDefaultFont.SetFontNameSpecifier ("times");
835 sDefaultFont.SetPointSize (12);
836 }
837#endif
838#if qStroika_Foundation_Common_Platform_Windows
839 sDefaultFont.SetTextColor (Led_GetTextColor ());
840#endif
841 sDefaultFontValid = true;
842 }
843 return (sDefaultFont);
844}
845
846#if qStroika_Foundation_Common_Platform_Windows
847FontSpecification Led::GetStaticDefaultFont (BYTE charSet)
848{
849 FontSpecification defaultFont;
850 //nb: import to go through the intermediary font so we don't set into the
851 // LOGFONT lots of fields which are part of the chosen font but are
852 // extraneous, and then mess up later choices to change to face name.
853 //
854 // Eg., if our default font is BOLD, that will result in a big weight# being part of
855 // the logFont. But then if the user picks a different face name, we don't want it
856 // to stay bold. Maybe BOLD is a bad example cuz that DOES show up in our UI (menu of
857 // font attriobutes). But pick one attribute (width? or escarpment) which doesn't show
858 // up in our choice lists, and yet gets caried along even after you change face names.
859 //
860 // See spr# 0426 for more details.
862 FontSelectionInfo selectedFont (charSet);
863 WindowDC screenDC (nullptr);
864#if defined(STRICT)
865 ::EnumFontFamilies (screenDC.m_hDC, nullptr, EnumFontCallback, reinterpret_cast<LPARAM> (&selectedFont));
866#else
867 ::EnumFontFamilies (screenDC.m_hDC, nullptr, reinterpret_cast<FONTENUMPROC> (EnumFontCallback), reinterpret_cast<LPARAM> (&selectedFont));
868#endif
869 fooo.LightSetOSRep (selectedFont.fBestFont);
870
871 // EnumFontFamilies seems to pick very bad sizes. Not sure why. No biggie. This works
872 // pretty well. LGP 970613
873 {
874 static LOGFONT theANSILogFont;
875 if (theANSILogFont.lfFaceName[0] == '\0') {
876 HFONT xxx = HFONT (::GetStockObject (ANSI_VAR_FONT));
877 Verify (::GetObject (xxx, sizeof theANSILogFont, &theANSILogFont));
878 }
879 fooo.SetPointSize (min (max (FontSpecification (theANSILogFont).GetPointSize (), static_cast<unsigned short> (8)),
880 static_cast<unsigned short> (14)));
881 }
882
883 defaultFont.MergeIn (fooo);
884 defaultFont.SetTextColor (Led_GetTextColor ());
885 return (defaultFont);
886}
887#endif
888
889/*
890@METHOD: Intersection
891@DESCRIPTION: <p>Compute the subset of the two @'IncrementalFontSpecification' arguments where both parts
892 are valid and identical.</p>
893*/
894IncrementalFontSpecification Led::Intersection (const IncrementalFontSpecification& lhs, const IncrementalFontSpecification& rhs)
895{
896 IncrementalFontSpecification result = lhs;
897
898 // FontName Info
899 {
900 if (not lhs.GetFontNameSpecifier_Valid () or not rhs.GetFontNameSpecifier_Valid () or
901 lhs.GetFontNameSpecifier () != rhs.GetFontNameSpecifier ()) {
902 result.InvalidateFontNameSpecifier ();
903 }
904 }
905
906 // Style Info
907 {
908 if (not lhs.GetStyle_Bold_Valid () or not rhs.GetStyle_Bold_Valid () or lhs.GetStyle_Bold () != rhs.GetStyle_Bold ()) {
909 result.InvalidateStyle_Bold ();
910 }
911 }
912 {
913 if (not lhs.GetStyle_Italic_Valid () or not rhs.GetStyle_Italic_Valid () or lhs.GetStyle_Italic () != rhs.GetStyle_Italic ()) {
914 result.InvalidateStyle_Italic ();
915 }
916 }
917 {
918 if (not lhs.GetStyle_Underline_Valid () or not rhs.GetStyle_Underline_Valid () or lhs.GetStyle_Underline () != rhs.GetStyle_Underline ()) {
919 result.InvalidateStyle_Underline ();
920 }
921 }
922 {
923 if (not lhs.GetStyle_SubOrSuperScript_Valid () or not rhs.GetStyle_SubOrSuperScript_Valid () or
924 lhs.GetStyle_SubOrSuperScript () != rhs.GetStyle_SubOrSuperScript ()) {
925 result.InvalidateStyle_SubOrSuperScript ();
926 }
927 }
928#if qStroika_Foundation_Common_Platform_Windows
929 {
930 if (not lhs.GetStyle_Strikeout_Valid () or not rhs.GetStyle_Strikeout_Valid () or lhs.GetStyle_Strikeout () != rhs.GetStyle_Strikeout ()) {
931 result.InvalidateStyle_Strikeout ();
932 }
933 }
934#endif
935
936 // Font Color Info
937 {
938 if (not lhs.GetTextColor_Valid () or not rhs.GetTextColor_Valid () or lhs.GetTextColor () != rhs.GetTextColor ()) {
939 result.InvalidateTextColor ();
940 }
941 }
942
943 // Size Info
944 {
945 // careful - cuz InvalidatePointSizeIncrement and InvalidatePointSize both
946 // invalidate the same thing...
947 // Must check that if ANY specification - its the same on both sides (lhs & rhs)
948 bool needsInval = false;
949 if (lhs.GetPointSizeIncrement_Valid () != rhs.GetPointSizeIncrement_Valid () or
950 (lhs.GetPointSizeIncrement_Valid () and lhs.GetPointSizeIncrement () != rhs.GetPointSizeIncrement ())) {
951 needsInval = true;
952 }
953 if (lhs.GetPointSize_Valid () != rhs.GetPointSize_Valid () or (lhs.GetPointSize_Valid () and lhs.GetPointSize () != rhs.GetPointSize ())) {
954 needsInval = true;
955 }
956 if (needsInval) {
957 result.InvalidatePointSize ();
958 }
959 }
960
961 return result;
962}
963
964#if qStroika_Foundation_Common_Platform_Windows
965/*
966 ********************************************************************************
967 ******************************** Tablet::RecolorHelper *************************
968 ********************************************************************************
969 */
970class Tablet::RecolorHelper {
971public:
972 RecolorHelper (HDC baseHDC, Led_Size size, Color hilightBackColor, Color hilightForeColor, Color oldBackColor, Color oldForeColor);
973 ~RecolorHelper ();
974
975public:
976 static RecolorHelper* CheckCacheAndReconstructIfNeeded (RecolorHelper* _THIS_, HDC baseHDC, Led_Size size, Color hilightBackColor,
977 Color hilightForeColor, Color oldBackColor, Color oldForeColor);
978
979public:
980 nonvirtual void DoRecolor (const Led_Rect& hilightArea);
981
982private:
983 nonvirtual void DoRecolor_SimpleDSTINVERT (const Led_Rect& hilightArea);
984 nonvirtual void DoRecolor_SimplePATINVERT (const Led_Rect& hilightArea);
985
986protected:
987 nonvirtual void DoRecolor_CopyTo8BitManualMungePixAndBack (const Led_Rect& hilightArea);
988
989private:
990 nonvirtual void MakeMappingTable ();
991
992private:
993 uint8_t fMappingTable[256];
994
995private:
996 nonvirtual uint8_t FindClosestColorInColorTable_ (COLORREF c) const;
997
998private:
999 nonvirtual COLORREF MapColor (COLORREF c) const; // use fHilightBackColor etc to map color
1000 nonvirtual COLORREF MapColor (RGBQUAD c) const;
1001
1002private:
1003 HDC fBaseDC;
1004 Led_Size fSize;
1005 RGBQUAD fColorTable[256];
1006 HBITMAP fDibSection;
1007 LPBYTE fDibData;
1008 size_t fDibDataByteCount;
1009 HDC fHMemDC;
1010 HBITMAP fOldBitmap;
1011 COLORREF fHilightBackColor;
1012 COLORREF fHilightForeColor;
1013 COLORREF fOldBackColor;
1014 COLORREF fOldForeColor;
1015};
1016COLORREF Tablet::RecolorHelper::MapColor (COLORREF c) const
1017{
1018 float fIntrp;
1019 fIntrp = (float)(255 - GetRValue (c)) / 256.0f;
1020 BYTE red = static_cast<BYTE> (GetRValue (fHilightBackColor) + fIntrp * GetRValue (fHilightForeColor) - fIntrp * GetRValue (fHilightBackColor));
1021
1022 fIntrp = (float)(255 - GetGValue (c)) / 256.0f;
1023 BYTE green = static_cast<BYTE> (GetGValue (fHilightBackColor) + fIntrp * GetGValue (fHilightForeColor) - fIntrp * GetGValue (fHilightBackColor));
1024
1025 fIntrp = (float)(255 - GetBValue (c)) / 256.0f;
1026 BYTE blue = static_cast<BYTE> (GetBValue (fHilightBackColor) + fIntrp * GetBValue (fHilightForeColor) - fIntrp * GetBValue (fHilightBackColor));
1027 return RGB (red, green, blue);
1028}
1029inline COLORREF Tablet::RecolorHelper::MapColor (RGBQUAD c) const
1030{
1031 return MapColor (RGB (c.rgbRed, c.rgbGreen, c.rgbBlue));
1032}
1033
1034Tablet::RecolorHelper::RecolorHelper (HDC baseHDC, Led_Size size, Color hilightBackColor, Color hilightForeColor, Color oldBackColor, Color oldForeColor)
1035 : fDibData (nullptr)
1036 //fMappingTable ()
1037 , fDibDataByteCount (0)
1038 , fHMemDC (nullptr)
1039 , fBaseDC (baseHDC)
1040 , fSize (size)
1041 , fColorTable ()
1042 , fDibSection (nullptr)
1043 , fOldBitmap (nullptr)
1044 , fHilightBackColor (hilightBackColor.GetOSRep ())
1045 , fHilightForeColor (hilightForeColor.GetOSRep ())
1046 , fOldBackColor (oldBackColor.GetOSRep ())
1047 , fOldForeColor (oldForeColor.GetOSRep ())
1048{
1049 CreateStandardColorTable (fColorTable, fHilightBackColor, fHilightForeColor, fOldBackColor, fOldForeColor);
1050 fDibSection = Create8BitDIBSection (baseHDC, size.h, size.v, fColorTable, &fDibData);
1051 BITMAP bm;
1052 Verify (::GetObject (fDibSection, sizeof (BITMAP), &bm) == sizeof (BITMAP));
1053 fDibDataByteCount = bm.bmWidthBytes * bm.bmHeight;
1054 fHMemDC = ::CreateCompatibleDC (fBaseDC);
1055 fOldBitmap = reinterpret_cast<HBITMAP> (::SelectObject (fHMemDC, fDibSection));
1056 MakeMappingTable ();
1057}
1058
1059Tablet::RecolorHelper::~RecolorHelper ()
1060{
1061 if (fDibSection != nullptr) {
1062 ::SelectObject (fHMemDC, fOldBitmap);
1063 ::DeleteDC (fHMemDC);
1064 ::DeleteObject (fDibSection);
1065 }
1066}
1067
1068Tablet::RecolorHelper* Tablet::RecolorHelper::CheckCacheAndReconstructIfNeeded (RecolorHelper* _THIS_, HDC baseHDC, Led_Size size, Color hilightBackColor,
1069 Color hilightForeColor, Color oldBackColor, Color oldForeColor)
1070{
1071 if (_THIS_ == nullptr or size.h > _THIS_->fSize.h or size.v > _THIS_->fSize.v or baseHDC != _THIS_->fBaseDC or
1072 hilightBackColor.GetOSRep () != _THIS_->fHilightBackColor or hilightForeColor.GetOSRep () != _THIS_->fHilightForeColor or
1073 oldBackColor.GetOSRep () != _THIS_->fOldBackColor or oldForeColor.GetOSRep () != _THIS_->fOldForeColor) {
1074 Led_Size areaSize = size;
1075 if (_THIS_ != nullptr) {
1076 areaSize.v = max (size.v, _THIS_->fSize.v);
1077 areaSize.h = max (size.h, _THIS_->fSize.h);
1078 }
1079 RecolorHelper* tmp = new RecolorHelper (baseHDC, areaSize, hilightBackColor, hilightForeColor, oldBackColor, oldForeColor);
1080 delete _THIS_;
1081 return tmp;
1082 }
1083 return _THIS_;
1084}
1085
1086void Tablet::RecolorHelper::MakeMappingTable ()
1087{
1088 for (size_t i = 0; i < 256; ++i) {
1089 fMappingTable[i] = FindClosestColorInColorTable_ (MapColor (fColorTable[i]));
1090 }
1091}
1092
1093uint8_t Tablet::RecolorHelper::FindClosestColorInColorTable_ (COLORREF c) const
1094{
1095 // walk through the color table and see which color is closest to 'c'
1096 uint8_t closest = 0;
1097 unsigned int closestDistance = 0xffffffff; // big distance
1098 for (size_t i = 0; i < 256; ++i) {
1099 unsigned int thisDist = Distance_Squared (c, RGB (fColorTable[i].rgbRed, fColorTable[i].rgbGreen, fColorTable[i].rgbBlue));
1100 if (thisDist < closestDistance) {
1101 closest = static_cast<uint8_t> (i);
1102 closestDistance = thisDist;
1103 if (closestDistance == 0) {
1104 break; // not needed, but COULD be a slight speed tweek
1105 }
1106 }
1107 }
1108 return closest;
1109}
1110
1111void Tablet::RecolorHelper::DoRecolor (const Led_Rect& hilightArea)
1112{
1113 HPALETTE hPal = nullptr;
1114 HPALETTE hOldPal = nullptr;
1115 bool isPaletteDevice = !!(::GetDeviceCaps (fBaseDC, RASTERCAPS) & RC_PALETTE);
1116 if (isPaletteDevice) {
1117 // if it's a palette device, select and realize a palette
1118 // as a background palette (won't cause a problem is the
1119 // palette was not selected in the foreground in the main app
1120 hPal = CreatePaletteForColorTable (fColorTable);
1122 hOldPal = ::SelectPalette (fBaseDC, hPal, TRUE);
1123 ::RealizePalette (fBaseDC);
1124 }
1125
1126 DoRecolor_CopyTo8BitManualMungePixAndBack (hilightArea);
1127
1128 if (isPaletteDevice) {
1129 if (hOldPal != nullptr) {
1130 ::SelectPalette (fBaseDC, hOldPal, TRUE);
1131 }
1132 ::DeleteObject (hPal);
1133 }
1134}
1135
1136void Tablet::RecolorHelper::DoRecolor_SimpleDSTINVERT (const Led_Rect& hilightArea)
1137{
1138 // Does proper inverse video, but seems to ignore the TextColor/BkColor/Pen/Brush colors.
1139 // Really should fix this to behave like Mac - replacing the background color with the text hilight color.
1140 // See SPR#1271
1141 ::BitBlt (fBaseDC, hilightArea.left, hilightArea.top, hilightArea.GetWidth (), hilightArea.GetHeight (), fBaseDC, hilightArea.left,
1142 hilightArea.top, DSTINVERT);
1143}
1144
1145void Tablet::RecolorHelper::DoRecolor_SimplePATINVERT (const Led_Rect& hilightArea)
1146{
1147 // Attempt at solving SPR#1271. Works decently - producing the right background - but the text is colored YELLOW instead of WHITE - and so
1148 // doesn't look very good (not enough contrast).
1149 Color useColor = Color::kWhite - Color (fHilightBackColor);
1150 HGDIOBJ oldPen = ::SelectObject (fBaseDC, ::GetStockObject (NULL_PEN));
1151 Brush backgroundBrush (useColor.GetOSRep ());
1152 HGDIOBJ oldBrush = ::SelectObject (fBaseDC, backgroundBrush);
1153 ::BitBlt (fBaseDC, hilightArea.left, hilightArea.top, hilightArea.GetWidth (), hilightArea.GetHeight (), fBaseDC, hilightArea.left,
1154 hilightArea.top, PATINVERT);
1155 (void)::SelectObject (fBaseDC, oldPen);
1156 (void)::SelectObject (fBaseDC, oldBrush);
1157}
1158
1159void Tablet::RecolorHelper::DoRecolor_CopyTo8BitManualMungePixAndBack (const Led_Rect& hilightArea)
1160{
1161 // By commenting stuff in and out - I determined that virtuall ALL the time is spent in this first
1162 // BitBlt () - LGP 2003-03-11
1163 // I also found that qUseDIBSectionForOffscreenBitmap made this BitBlt go much faster - to the point of acceptable speed
1164 // LGP - 2003-03-12
1165
1166 // Copy the REAL image into our 8-bit DIBSECTION
1167 ::BitBlt (fHMemDC, 0, 0, hilightArea.GetWidth (), hilightArea.GetHeight (), fBaseDC, hilightArea.left, hilightArea.top, SRCCOPY);
1168
1169 /*
1170 *
1171 * Fiddle the bits:
1172 *
1173 * NB: This code assumes we're pointing at an 8-bit COLOR-LOOKUP-TABLE based Image
1174 *
1175 * Note - this also may be modifying MUCH MORE than is actually needed. It goes all the way to the
1176 * end of the ROW of pixels - but we could be using much less.
1177 *
1178 * In order to remedy that - I tried constructing the DIBSECTION on the fly - instead of caching it. That turned
1179 * out to be quite slow (on DELL Precision Workstation 420 (800mz Dual Processor)). This muning code has never shown
1180 * up as taking significant time. Its all that BitBlt above.
1181 *
1182 * Anyhow - if this does someday look slow - it can easily be fixed. We can just break the loop into two nested loops,
1183 * the outer one over rows, and the inner row loop stopping NOT at the end of the REAL row - but just at the end
1184 * of the subset we are using (easy cuz we always start at 0,0).
1185 */
1186 Verify (::GdiFlush ()); // make sure bits in sync... - not SURE if this is needed?
1187 {
1188 const unsigned char* kMappingTable = fMappingTable;
1189 unsigned char* dataStart = fDibData;
1190 unsigned char* dataEnd = dataStart + fDibDataByteCount;
1191 for (unsigned char* i = dataStart; i < dataEnd; ++i) {
1192 *i = kMappingTable[*i];
1193 }
1194 }
1195
1196 // Copy them back
1197 ::BitBlt (fBaseDC, hilightArea.left, hilightArea.top, hilightArea.GetWidth (), hilightArea.GetHeight (), fHMemDC, 0, 0, SRCCOPY);
1198}
1199#endif
1200
1201#if qStroika_Frameworks_Led_SupportGDI
1202/*
1203 ********************************************************************************
1204 *********************************** Tablet *************************************
1205 ********************************************************************************
1206 */
1207#if qStroika_Foundation_Common_Platform_MacOS
1208Tablet::Tablet (GrafPtr gp)
1209 : fGrafPort (gp)
1210{
1211 RequireNotNull (gp);
1212}
1213#elif qStroika_Foundation_Common_Platform_Windows
1214Tablet::Tablet (HDC hdc, Tablet::OwnDCControl ownsDC)
1215 : m_hDC (hdc)
1216 , fRecolorHelper (nullptr)
1217 , m_hAttribDC (hdc)
1218 , m_bPrinting (false)
1219 , fOwnsDC (ownsDC)
1220 , fLogPixelsV (0)
1221 , fLogPixelsH (0)
1222{
1223}
1224#elif qStroika_FeatureSupported_XWindows
1225Tablet::Tablet (Display* display, Drawable drawable)
1226 : fDrawableOrigin (Led_Point (0, 0))
1227 , fFontCache ()
1228 , fCurDrawLineLoc (Led_Point (0, 0))
1229 , fDisplay (display)
1230 , fDrawable (drawable)
1231 , fGC (nullptr)
1232 , fColormap (0)
1233 , fCachedFontInfo (nullptr)
1234 , fFontMappingCache ()
1235{
1236 int screen = DefaultScreen (display);
1237 fGC = ::XCreateGC (display, drawable, 0, nullptr);
1238 ::XSetForeground (display, fGC, BlackPixel (display, screen));
1239 ::XSetBackground (display, fGC, WhitePixel (display, screen));
1240 XSetGraphicsExposures (display, fGC, true);
1241 XWindowAttributes wa;
1242 (void)::memset (&wa, 0, sizeof (wa));
1243 /*
1244 * Since we don't know for sure the drawable is a window - catch the error and ignore it. Don't let
1245 * XErrorHandler do anything bad.
1246 */
1247 int (*oldErrHandler) (Display*, XErrorEvent*) = ::XSetErrorHandler (IgnoreXErrorHandler);
1248 Status s = ::XGetWindowAttributes (display, drawable, &wa);
1249 ::XSetErrorHandler (oldErrHandler);
1250 if (s != 0 and wa.map_installed) {
1251 fColormap = wa.colormap;
1252 }
1253 else {
1254 fColormap = DefaultColormap (fDisplay, DefaultScreen (fDisplay));
1255 // Assert (false);//???
1256 // make new colormap...call XGetWMColormap ()...
1257 // CALL XSetWindowColormap ().... if not gotten
1258 // .
1259 }
1260}
1261#endif
1262
1263Tablet::~Tablet ()
1264{
1265#if qStroika_Foundation_Common_Platform_Windows
1266 delete fRecolorHelper;
1267 if (m_hDC != nullptr and fOwnsDC == eOwnsDC) {
1268 ::DeleteDC (Detach ());
1269 }
1270#elif qStroika_FeatureSupported_XWindows
1271 ::XFreeGC (fDisplay, fGC);
1272 for (auto i = fFontCache.begin (); i != fFontCache.end (); ++i) {
1273 ::XFreeFont (fDisplay, i->second);
1274 }
1275#endif
1276}
1277
1278/*
1279@METHOD: Tablet::CvtFromTWIPS
1280@DESCRIPTION: <p>Utility routine to convert from TWIPS to logical coordinates (usually pixels).</p>
1281 <p>See also @'Tablet::CvtFromTWIPSV', @'Tablet::CvtFromTWIPSH', @'Tablet::CvtToTWIPSV', @'Tablet::CvtToTWIPSH'.</p>
1282*/
1283Led_Point Tablet::CvtFromTWIPS (TWIPS_Point from) const
1284{
1285 return Led_Point{CvtFromTWIPSV (from.v), CvtFromTWIPSH (from.h)};
1286}
1287
1288/*
1289@METHOD: Tablet::CvtToTWIPS
1290@DESCRIPTION: <p>Utility routine to convert from logical coordinates (usually pixels) to TWIPS.</p>
1291 <p>See also @'Tablet::CvtFromTWIPSV', @'Tablet::CvtFromTWIPSH', @'Tablet::CvtToTWIPSV', @'Tablet::CvtToTWIPSH'.</p>
1292*/
1293TWIPS_Point Tablet::CvtToTWIPS (Led_Point from) const
1294{
1295 return TWIPS_Point{CvtToTWIPSV (from.v), CvtToTWIPSH (from.h)};
1296}
1297
1298/*
1299@METHOD: Tablet::CvtFromTWIPS
1300@DESCRIPTION: <p>Utility routine to convert from TWIPS to logical coordinates (usually pixels).</p>
1301 <p>See also @'Tablet::CvtFromTWIPSV', @'Tablet::CvtFromTWIPSH', @'Tablet::CvtToTWIPSV', @'Tablet::CvtToTWIPSH'.</p>
1302*/
1303Led_Rect Tablet::CvtFromTWIPS (TWIPS_Rect from) const
1304{
1305 return Led_Rect{CvtFromTWIPS (from.GetOrigin ()), Led_Size (CvtFromTWIPS (from.GetSize ()))};
1306}
1307
1308/*
1309@METHOD: Tablet::CvtToTWIPS
1310@DESCRIPTION: <p>Utility routine to convert from logical coordinates (usually pixels) to TWIPS.</p>
1311 <p>See also @'Tablet::CvtFromTWIPSV', @'Tablet::CvtFromTWIPSH', @'Tablet::CvtToTWIPSV', @'Tablet::CvtToTWIPSH'.</p>
1312*/
1313TWIPS_Rect Tablet::CvtToTWIPS (Led_Rect from) const
1314{
1315 return TWIPS_Rect{CvtToTWIPS (from.GetOrigin ()), CvtToTWIPS (Led_Point (from.GetSize ()))};
1316}
1317
1318/*
1319@METHOD: Tablet::ScrollBitsAndInvalRevealed
1320@DESCRIPTION: <p>Scroll the given 'windowRect' by 'scrollVBy localical units. The area of the window exposed by this
1321 action is invalidated (so a later update event will fix it).</p>
1322*/
1323void Tablet::ScrollBitsAndInvalRevealed (const Led_Rect& windowRect, CoordinateType scrollVBy)
1324{
1325#if qStroika_Foundation_Common_Platform_MacOS
1326 Rect qdMoveRect = AsQDRect (windowRect);
1327 RgnHandle updateRgn = ::NewRgn ();
1328 Execution::ThrowIfNull (updateRgn);
1329 SetPort ();
1330 ::ScrollRect (&qdMoveRect, 0, scrollVBy, updateRgn);
1331#if TARGET_CARBON
1332 ::InvalWindowRgn (::GetWindowFromPort (fGrafPort), updateRgn);
1333#else
1334 ::InvalRgn (updateRgn);
1335#endif
1336 ::DisposeRgn (updateRgn);
1337#elif qStroika_Foundation_Common_Platform_Windows
1338 RECT gdiMoveRect = AsRECT (windowRect);
1339 // NB: I used to use ScrollDC (Led 2.1 and earlier). But that code appeared to sometimes leave
1340 // little bits of crufy around. I never understood why. But I assume it was a windows bug.
1341 HWND w = GetWindow ();
1343 ::ScrollWindow (w, 0, scrollVBy, &gdiMoveRect, &gdiMoveRect);
1344#elif qStroika_FeatureSupported_XWindows
1345 if (scrollVBy != 0) {
1346 {
1347 /*
1348 * We cannot do a scrollbits if there are any pending update events. Ideally - we would PREVENT
1349 * this situation by having Led_Gtk_Helper<BASE_INTERACTOR,GTKBASEINFO>::Update_ () work properly.
1350 * But - alas - after a day or two's efforts - I've been unable to get that code working.
1351 * Sigh. Luckily - this seems to prevent any drawing bugs, and only results in an occasional
1352 * drawing (scrolling) slowdown. I guess we can live with that. -- LGP 2001-05-18
1353 */
1354 XEvent e;
1355 if (::XCheckTypedEvent (fDisplay, Expose, &e) or ::XCheckTypedEvent (fDisplay, GraphicsExpose, &e)) {
1356 ::XPutBackEvent (fDisplay, &e);
1357 Execution::ThrowIfNull (nullptr);
1358 }
1359 }
1360 Led_Rect srcMoveRect = windowRect;
1361 Led_Rect exposedRect = windowRect;
1362 if (scrollVBy > 0) {
1363 // moving bits down (up scrollbar button)
1364 srcMoveRect.bottom -= scrollVBy;
1365 exposedRect.bottom = scrollVBy;
1366 }
1367 else {
1368 srcMoveRect.top -= scrollVBy;
1369 exposedRect.top = exposedRect.bottom + scrollVBy;
1370 }
1371 XGCValues prevValues;
1372 const unsigned long kSavedAttrs = GCGraphicsExposures;
1373 (void)::memset (&prevValues, 0, sizeof (prevValues));
1374 ::XGetGCValues (fDisplay, fGC, kSavedAttrs, &prevValues);
1375 ::XSetClipMask (fDisplay, fGC, None);
1376 ::XSetGraphicsExposures (fDisplay, fGC, true);
1377 ::XCopyArea (fDisplay, fDrawable, fDrawable, fGC, srcMoveRect.GetLeft (), srcMoveRect.GetTop (), srcMoveRect.GetWidth (),
1378 srcMoveRect.GetHeight (), srcMoveRect.GetLeft (), srcMoveRect.top + scrollVBy);
1379 ::XChangeGC (fDisplay, fGC, kSavedAttrs, &prevValues);
1380
1381/*
1382 * After the scrollbits - we leave a little rectangle exposed. We must mark that as needing drawing.
1383 */
1384#if 1
1385 XEvent event;
1386 (void)::memset (&event, 0, sizeof (event));
1387 event.type = Expose;
1388 event.xexpose.send_event = true;
1389 event.xexpose.display = fDisplay;
1390 event.xexpose.window = fDrawable;
1391 event.xexpose.x = (int)exposedRect.GetLeft ();
1392 event.xexpose.y = (int)exposedRect.GetTop ();
1393 event.xexpose.width = (int)exposedRect.GetWidth ();
1394 event.xexpose.height = (int)exposedRect.GetHeight ();
1395 event.xexpose.count = 0;
1396 Verify (::XSendEvent (fDisplay, fDrawable, false, ExposureMask, &event) != 0);
1397#else
1398 ::XClearArea (fDisplay, fDrawable, (int)exposedRect.GetLeft (), (int)exposedRect.GetTop (), (unsigned int)exposedRect.GetWidth (),
1399 (unsigned int)exposedRect.GetHeight (), true);
1400#endif
1401 }
1402#else
1403 Assert (false); //NYI
1404#endif
1405}
1406
1407/*
1408@METHOD: Tablet::FrameRegion
1409@DESCRIPTION: <p>Draw the outline of the given region 'r' in color 'c'.</p>
1410*/
1411void Tablet::FrameRegion (const Region& r, const Color& c)
1412{
1413#if qStroika_Foundation_Common_Platform_MacOS
1414 MacPortAndClipRegionEtcSaver saver; // unclear if this is useful/needed?
1415 SetPort ();
1416 PenMode (srcCopy); // ???
1417 GDI_RGBForeColor (c.GetOSRep ());
1418 ::FrameRgn (r.GetOSRep ());
1419#elif qStroika_Foundation_Common_Platform_Windows
1420 Brush brush = Brush (c.GetOSRep ());
1421 (void)::FrameRgn (*this, r, brush, 1, 1);
1422#else
1423 Assert (false); // NYI
1424#endif
1425}
1426
1427/*
1428@METHOD: Tablet::FrameRectangle
1429@DESCRIPTION: <p>Draw the outline of the given rectangle 'r' in color 'c' and with
1430 borderWidth (pen width) 'borderWidth'. This function does NOT use the currently selected
1431 pen or brush, or anything like that. It draws a border just INSIDE the rectangle specified
1432 by 'r'.
1433 </p>
1434*/
1435void Tablet::FrameRectangle (const Led_Rect& r, Color c, DistanceType borderWidth)
1436{
1437 /*
1438 * Almost certainly can implement much more efficiently, but leave like this for now to assure pixel-for-pixel
1439 * equiv across GDIs.
1440 */
1441 Led_Rect topBar = Led_Rect (r.top, r.left, borderWidth, r.GetWidth ());
1442 Led_Rect leftBar = Led_Rect (r.top, r.left, r.GetHeight (), borderWidth);
1443 EraseBackground_SolidHelper (topBar, c); // TOP
1444 EraseBackground_SolidHelper (leftBar, c); // LEFT
1445 EraseBackground_SolidHelper (topBar + Led_Point (r.GetHeight () - borderWidth, 0), c); // BOTTOM
1446 EraseBackground_SolidHelper (leftBar + Led_Point (0, r.GetWidth () - borderWidth), c); // RIGHT
1447}
1448
1449/*
1450@METHOD: Tablet::MeasureText
1451@DESCRIPTION: <p>Measure the widths of the given argument @'Led_tChar's. Assign those widths into
1452 the array 'charLocations'. (EXPLAIN CAREFULLY REQUIREMENTS ABOUT BUFSIZE of charLocations and handling of
1453 zero case and offset rules, handling of tabs, etc)</p>
1454 <p>Note that the resulting measured text must come out in non-descreasing order (there can be zero
1455 character widths, but never negative).</p>
1456*/
1457void Tablet::MeasureText (const FontMetrics& precomputedFontMetrics, const Led_tChar* text, size_t nTChars, DistanceType* charLocations)
1458{
1459 RequireNotNull (text);
1460 RequireNotNull (charLocations);
1461#if qStroika_Foundation_Common_Platform_MacOS
1462 SetPort ();
1463#endif
1464
1465#if qStroika_Foundation_Common_Platform_MacOS
1466 const DistanceType kMaxTextWidthResult = 0x7fff;
1467#elif qStroika_Foundation_Common_Platform_Windows
1468 DistanceType kMaxTextWidthResult = kRunning32BitGDI ? 0x7fffffff : 0x7fff;
1469 if (IsPrinting ()) {
1470 // See SPR#0435
1471 SIZE ve = GetViewportExt ();
1472 SIZE we = GetWindowExt ();
1473 kMaxTextWidthResult = ::MulDiv (kMaxTextWidthResult, we.cx, ve.cx) - 1;
1474 }
1475#elif qStroika_FeatureSupported_XWindows
1476 const DistanceType kMaxTextWidthResult = 0x7fff; //X-TMP-HACK-LGP991213
1477#endif
1478 size_t kMaxChars = kMaxTextWidthResult / precomputedFontMetrics.GetMaxCharacterWidth ();
1479#if qUseUniscribeToImage
1480 if (kMaxChars > kMaxUNISCRIBECharacters) {
1481 kMaxChars = kMaxUNISCRIBECharacters;
1482 }
1483#endif
1484 Assert (kMaxChars > 1);
1485
1486 DistanceType runningCharCount = 0;
1487 for (size_t charsToGo = nTChars; charsToGo > 0;) {
1488 size_t i = nTChars - charsToGo;
1489 Assert (i < nTChars);
1490
1491 while (text[i] == '\t') {
1492 Assert (charsToGo > 0);
1493
1494 charLocations[i] = runningCharCount;
1495 ++i;
1496 if (--charsToGo == 0) {
1497 break;
1498 }
1499 }
1500 if (charsToGo == 0) {
1501 break;
1502 }
1503
1504 size_t charsThisTime = min (charsToGo, kMaxChars);
1505 for (size_t tabIndex = 1; tabIndex < charsThisTime; tabIndex++) {
1506 if (text[i + tabIndex] == '\t') {
1507 charLocations[i + tabIndex] = runningCharCount;
1508 charsThisTime = tabIndex;
1509 break;
1510 }
1511 }
1512
1513#if qStroika_Foundation_Common_Platform_Windows
1514 SIZE size;
1515 Assert (sizeof (int) == sizeof (DistanceType));
1516#if qUseUniscribeToImage
1517 {
1518 if (sUniscribeDLL.IsAvail ()) {
1519 SCRIPT_CONTROL scriptControl;
1520 memset (&scriptControl, 0, sizeof (scriptControl));
1521
1522 SCRIPT_STATE scriptState;
1523 memset (&scriptState, 0, sizeof (scriptState));
1524 // Important to ALLOW ScriptStringAnalyse to REORDER (so don't set this true) cuz otherwise it won't get right measurements
1525 // for arabic font substition (shaping)---LGP 2003-01-02
1526
1527 // MAYBE THIS IS WRONG - AND COVERING UP ANOTHER BUG??? DUNNO? MUST BE CAREFUL ABOUT MIRRORING (SYMSWAP). If done HERE,
1528 // then I must NOT inhibit symswap. I DON'T THINK I CAN DO it here cuz the draw code gets done in RUNS... HMMM
1529 // LGP 2003-01-02...
1530 // scriptState.fOverrideDirection = true; // I THINK This is how I say already in display order
1531 scriptState.fInhibitSymSwap = true;
1532
1533 SCRIPT_STRING_ANALYSIS ssa;
1534 memset (&ssa, 0, sizeof (ssa));
1535
1536 Verify (sUniscribeDLL.ScriptStringAnalyse (m_hAttribDC, &text[i], charsThisTime, 0, -1, SSA_GLYPHS | SSA_FALLBACK, -1,
1537 &scriptControl, &scriptState, nullptr, nullptr, nullptr, &ssa) == S_OK);
1538
1539#if qTryScriptToCPX
1540 for (size_t j = 0; j < charsThisTime; ++j) {
1541 int leadingEdge = 0;
1542 int trailingEdge = 0;
1543 Verify (sUniscribeDLL.ScriptStringCPtoX (ssa, j, false, &leadingEdge) == S_OK);
1544 Verify (sUniscribeDLL.ScriptStringCPtoX (ssa, j, true, &trailingEdge) == S_OK);
1545
1546 int logicalWidth = abs (trailingEdge - leadingEdge); // can be zero-width - but never negative...
1547 if (j == 0) {
1548 charLocations[i + j] = runningCharCount + logicalWidth;
1549 }
1550 else {
1551 charLocations[i + j] = charLocations[i + j - 1] + logicalWidth;
1552 }
1553 }
1554#else
1555 Memory::StackBuffer<int> logicalWidths{charsThisTime};
1556 Verify (sUniscribeDLL.ScriptStringGetLogicalWidths (ssa, logicalWidths) == S_OK);
1557
1558 Assert (charsThisTime > 0);
1559 Assert (logicalWidths[0] >= 0); // can be zero-width - but never negative...
1560 charLocations[i] = runningCharCount + logicalWidths[0];
1561 for (size_t j = 1; j < charsThisTime; ++j) {
1562 Assert (logicalWidths[j] >= 0); // can be zero-width - but never negative...
1563 charLocations[i + j] = charLocations[i + j - 1] + logicalWidths[j];
1564 }
1565#endif
1566 Verify (sUniscribeDLL.ScriptStringFree (&ssa) == S_OK);
1567 goto Succeeded;
1568 }
1569 }
1570#endif
1571
1572 // Default code - if UNISCRIBE not compiled for or not dynamically loaded
1573 Win32_GetTextExtentExPoint (m_hAttribDC, &text[i], charsThisTime, kMaxTextWidthResult, nullptr, (int*)&charLocations[i], &size);
1574 for (size_t j = 0; j < charsThisTime; ++j) {
1575 charLocations[i + j] += runningCharCount;
1576 }
1577
1578#if qUseUniscribeToImage
1579 Succeeded:
1580#endif
1581#elif qStroika_FeatureSupported_XWindows
1582 Execution::ThrowIfNull (fCachedFontInfo);
1583 // Gross hack - sloppy implementation (SLOW). But I'm not sure what in the X SDK allows this to be done faster! -- LGP 2000-09-05
1584 // Actually - not TOO bad since whole computation is done client-side. Seems to be working OK - at least for now - LGP 2001-05-05
1585 for (size_t j = 0; j < charsThisTime; ++j) {
1586 charLocations[i + j] = runningCharCount + ::XTextWidth (const_cast<XFontStruct*> (fCachedFontInfo), &text[i], j + 1);
1587 }
1588#endif
1589
1590 runningCharCount = charLocations[i + charsThisTime - 1];
1591
1592 Assert (charsToGo >= charsThisTime);
1593 charsToGo -= charsThisTime;
1594 }
1595
1596// LGP-991220 - This is generating asserts elsewhere - and seems like such a hack. Not sure why needed. Try getting rid of and see what happens?
1597#if qStroika_Foundation_Common_Platform_Windows && 0
1598 // This gross hack is cuz we do a GetTextExtent() at the end of the
1599 // DrawText for PC, to see how much we drew. This is the only hack
1600 // I could think of to assure we get consistent results (which is very important).
1601 // This REALLY shold be done better - fix may not be here, but in DrawCode below...
1602 // LGP 960509
1603
1604 if (nTChars > 0) {
1605 int lastWidth = (nTChars == 1) ? charLocations[0] : (charLocations[nTChars - 1] - charLocations[nTChars - 2]);
1606 SIZE size;
1607 AssertNotNull (m_hAttribDC);
1608 Win32_GetTextExtentPoint (m_hAttribDC, &text[nTChars - 1], 1, &size);
1609 int sbWidth = size.cx;
1610 if (sbWidth != lastWidth) {
1611 charLocations[nTChars - 1] = ((nTChars == 1) ? 0 : charLocations[nTChars - 2]) + sbWidth;
1612 }
1613 }
1614#endif
1615
1617 // Assure charLocations are in non-decreasing order (OK to have some zero width - but never negative).
1618 DistanceType d = 0;
1619 for (size_t i = 0; i < nTChars; ++i) {
1620 Ensure (d <= charLocations[i]);
1621 d = charLocations[i];
1622 }
1623 }
1624}
1625
1626/*
1627@DESCRIPTION: <p>Draw the given text (@'Led_tChars') to this tablet object, at the given logical coordinates. Use the given
1628 tabstop origin, and tabStopList object to compute where tabs go. Return the amountDrawn (number of pixels used by draw).
1629 (EXPLAIN CAREFULLY REQUIREMENTS ABOUT BUFSIZE of charLocations and handling of
1630 zero case and offset rules, handling of tabs, AND MUCH MORE etc)</p>
1631 <p>Note - for BIDI text - the VIRTUAL (display) text is what should be passed in. Text will be imaged
1632 left to right. This means that for RTL text such as Arabic or Hebrew - it should have already been
1633 re-ordered in the argument passed in, and any mirroring should have already been done. This routine WILL
1634 take care of any contextual shaping required (glyph selection based on context - as with Arabic).</p>
1635*/
1636void Tablet::TabbedTextOut ([[maybe_unused]] const FontMetrics& precomputedFontMetrics, const Led_tChar* text, size_t nBytes,
1637 [[maybe_unused]] TextDirection direction, Led_Point outputAt, CoordinateType hTabOrigin,
1638 const TabStopList& tabStopList, DistanceType* amountDrawn, CoordinateType hScrollOffset)
1639{
1640 DistanceType widthSoFar = 0;
1641 const Led_tChar* textCursor = text;
1642 const Led_tChar* textEnd = text + nBytes;
1643 while (textCursor < textEnd) {
1644 // Skip over tabs
1645 const Led_tChar* nextTabAt = textCursor;
1646 for (; nextTabAt < textEnd;) {
1647 if (*nextTabAt == '\t') {
1648 break;
1649 }
1650 ++nextTabAt;
1651 }
1652
1653// Actually image the characters
1654#if qStroika_Foundation_Common_Platform_Windows
1655 int oldBkMode = SetBkMode (TRANSPARENT);
1656
1657#if qUseUniscribeToImage
1658 {
1659#if qTryToOptimizeLongUNISCRIBEScriptOutCalls
1660 const size_t kMaxCharsToDrawAtATime = 500;
1661#endif
1662 size_t len = nextTabAt - textCursor;
1663 if (len == 0) {
1664 goto Succeeded; // UNISCRIBE barfs on zero-length strings. Nothing todo anyhow...
1665 }
1666 if (sUniscribeDLL.IsAvail ()) {
1667 SCRIPT_CONTROL scriptControl;
1668 memset (&scriptControl, 0, sizeof (scriptControl));
1669
1670 SCRIPT_STATE scriptState;
1671 memset (&scriptState, 0, sizeof (scriptState));
1672 scriptState.fOverrideDirection = true; // I THINK This is how I say already in display order
1673 scriptState.fInhibitSymSwap = true;
1674
1675 const Led_tChar* thisChunkPtr = textCursor;
1676 for (size_t thisChunkLen = len; thisChunkLen > 0;) {
1677 if (thisChunkLen > kMaxUNISCRIBECharacters) {
1678 thisChunkLen = kMaxUNISCRIBECharacters;
1679 }
1680#if qTryToOptimizeLongUNISCRIBEScriptOutCalls
1681 if (thisChunkLen > kMaxCharsToDrawAtATime) {
1682 thisChunkLen = kMaxCharsToDrawAtATime;
1683 }
1684#endif
1685 {
1686 SCRIPT_STRING_ANALYSIS ssa;
1687 memset (&ssa, 0, sizeof (ssa));
1688 if (not SUCCEEDED (sUniscribeDLL.ScriptStringAnalyse (m_hDC, thisChunkPtr, thisChunkLen, 0, -1, SSA_GLYPHS | SSA_FALLBACK,
1689 -1, &scriptControl, &scriptState, nullptr, nullptr, nullptr, &ssa))) {
1690 goto UniscribeFailure; // Can happen - for example - during ColeControl::DrawMetaFile ()
1691 // call - see SPR#1447 - fallback on older draw code...
1692 }
1693 Verify (sUniscribeDLL.ScriptStringOut (ssa, outputAt.h + int (widthSoFar) - hScrollOffset, outputAt.v, 0, nullptr,
1694 0, 0, false) == S_OK);
1695 const SIZE* sizep = sUniscribeDLL.ScriptString_pSize (ssa);
1696 AssertNotNull (sizep);
1697 widthSoFar += sizep->cx;
1698 Verify (sUniscribeDLL.ScriptStringFree (&ssa) == S_OK);
1699 }
1700
1701#if qTryToOptimizeLongUNISCRIBEScriptOutCalls
1702 // only rarely (tune this) - check if we've already drawn past the end of the HDC.
1703 // (not REALLY doing a great/reliable test for that either???
1704 if (len > kMaxCharsToDrawAtATime) {
1705 POINT vpOrg;
1706 Verify (::GetViewportOrgEx (m_hAttribDC, &vpOrg));
1707 POINT wOrg;
1708 Verify (::GetWindowOrgEx (m_hAttribDC, &wOrg));
1709 int deviceWidth = GetDeviceCaps (HORZRES);
1710 POINT x = vpOrg;
1711 x.x += deviceWidth;
1712 Verify (::DPtoLP (m_hAttribDC, &x, 1));
1713 if (x.x < outputAt.h + int (widthSoFar) - hScrollOffset) {
1714 // assume we're done - and can break out...
1715 break;
1716 }
1717 }
1718#endif
1719
1720 thisChunkPtr += thisChunkLen;
1721 thisChunkLen = (textCursor + len - thisChunkPtr); // set length left to go to be end of REAL buf minus new start ptr
1722 // at TOP of loop - it will be trimmed down to kMaxUNISCRIBECharacters
1723 }
1724 goto Succeeded;
1725 }
1726 }
1727 UniscribeFailure:
1728#endif
1729
1730 if (direction == eLeftToRight) {
1731 Win32_TextOut (m_hDC, static_cast<int> (outputAt.h + widthSoFar - hScrollOffset), static_cast<int> (outputAt.v), textCursor,
1732 static_cast<int> (nextTabAt - textCursor));
1733
1734 // Geez! There must be SOME API within Win32 to give me this info (like SetTextAlign (UPDATE_CP))
1735 // without recomputing it. But there doesn't appear to be. So we must recompute!
1736 // LGP 960503
1737
1738 // A SLIGHT optimization is now possible here - we don't need to compute the last GetTextExtent() if amountDrawn
1739 // is nullptr (typically TRUE) - LGP 960521
1740 if (amountDrawn != nullptr or (nextTabAt < textEnd)) {
1741 SIZE size;
1742 Win32_GetTextExtentPoint (m_hAttribDC, textCursor, static_cast<int> (nextTabAt - textCursor), &size);
1743 widthSoFar += size.cx;
1744 }
1745 }
1746 else {
1747 /*
1748 * A bunch of different ways to get the RTL code emitted. Try them each in order (some ifdefed out). When
1749 * one succeeds - just to the succcess label.
1750 */
1751#if qUseGetCharPlacementToImage
1752 {
1753 size_t len = nextTabAt - textCursor;
1754 Memory::StackBuffer<wchar_t> glyphs{len};
1755 GCP_RESULTSW gcpResult;
1756 memset (&gcpResult, 0, sizeof (gcpResult));
1757 gcpResult.lStructSize = sizeof (GCP_RESULTS);
1758 gcpResult.lpGlyphs = glyphs.data ();
1759 gcpResult.nGlyphs = static_cast<UINT> (len);
1760 if (::GetCharacterPlacementW (m_hDC, textCursor, static_cast<int> (len), 0, &gcpResult, GCP_GLYPHSHAPE | GCP_LIGATE) != 0) {
1761 Verify (::ExtTextOutW (m_hDC, outputAt.h + widthSoFar - hScrollOffset, outputAt.v, ETO_GLYPH_INDEX, nullptr,
1762 gcpResult.lpGlyphs, gcpResult.nGlyphs, nullptr));
1763 goto Succeeded_But_Need_To_Adjust_Width;
1764 }
1765 }
1766#endif
1767
1768#if qUseFakeTTGetWPlacementToImage
1769 {
1770 size_t len = nextTabAt - textCursor;
1771 Memory::StackBuffer<wchar_t> glyphs{len};
1772 if (Win9x_Workaround_GetCharPlacementFunction (m_hDC, textCursor, len, glyphs.data ()) != 0) {
1773 Verify (::ExtTextOutW (m_hDC, outputAt.h + widthSoFar - hScrollOffset, outputAt.v, ETO_GLYPH_INDEX, nullptr,
1774 glyphs.data (), static_cast<UINT> (len), nullptr));
1775 goto Succeeded_But_Need_To_Adjust_Width;
1776 }
1777 }
1778#endif
1779
1780 {
1781 size_t len = nextTabAt - textCursor;
1782 // Fallback - if the above fails...
1783 // Displays the text in the right order, but doesn't do contextual shaping (tested on WinXP and WinME) - LGP 2002-12-10
1784 Verify (::ExtTextOutW (m_hDC, outputAt.h + widthSoFar - hScrollOffset, outputAt.v, 0, nullptr, textCursor,
1785 static_cast<UINT> (len), nullptr));
1786 }
1787
1788 Succeeded_But_Need_To_Adjust_Width:
1789 // Geez! There must be SOME API within Win32 to give me this info (like SetTextAlign (UPDATE_CP))
1790 // without recomputing it. But there doesn't appear to be. So we must recompute!
1791 // LGP 960503
1792
1793 // A SLIGHT optimization is now possible here - we don't need to compute the last GetTextExtent() if amountDrawn
1794 // is nullptr (typically TRUE) - LGP 960521
1795 if (amountDrawn != nullptr or (nextTabAt < textEnd)) {
1796 SIZE size;
1797 Win32_GetTextExtentPoint (m_hAttribDC, textCursor, static_cast<int> (nextTabAt - textCursor), &size);
1798 widthSoFar += size.cx;
1799 }
1800 }
1801#if qUseUniscribeToImage
1802 Succeeded:;
1803#endif
1804
1805 (void)SetBkMode (oldBkMode);
1806#elif qStroika_FeatureSupported_XWindows
1807 Led_Point cursor = Led_Point (outputAt.v + precomputedFontMetrics.GetAscent (), outputAt.h - hScrollOffset) - fDrawableOrigin; // ascent - goto baseline...
1808 XTextItem item;
1809 memset (&item, 0, sizeof (item));
1810 item.chars = const_cast<char*> (textCursor);
1811 item.nchars = nextTabAt - textCursor;
1812 item.delta = 0;
1813 item.font = None;
1814 ::XDrawText (fDisplay, fDrawable, fGC, cursor.h + widthSoFar, cursor.v, &item, 1);
1815 Execution::ThrowIfNull (fCachedFontInfo);
1816 widthSoFar += ::XTextWidth (const_cast<XFontStruct*> (fCachedFontInfo), item.chars, item.nchars);
1817#endif
1818
1819 // Now see if nextTab really pointing at a tab (otherwise at end of buffer)
1820 if (nextTabAt < textEnd) {
1821 DistanceType thisTabWidth;
1822 {
1823 DistanceType curOutputAtZeroBased = (outputAt.h - hTabOrigin) + widthSoFar;
1824 DistanceType tabStop = tabStopList.ComputeTabStopAfterPosition (this, curOutputAtZeroBased);
1825 thisTabWidth = tabStop - curOutputAtZeroBased;
1826 Assert (thisTabWidth >= 0);
1827 }
1828
1829 widthSoFar += thisTabWidth;
1830 ++nextTabAt; // since we processed that tab...
1831 }
1832 textCursor = nextTabAt;
1833 }
1834 if (amountDrawn != nullptr) {
1835 *amountDrawn = widthSoFar;
1836 }
1837}
1838
1839void Tablet::SetBackColor (const Color& backColor)
1840{
1841#if qStroika_Foundation_Common_Platform_Windows
1842 SetBkColor (backColor.GetOSRep ());
1843#elif qStroika_FeatureSupported_XWindows
1844 if (backColor == Color::kWhite) {
1845 ::XSetBackground (fDisplay, fGC, WhitePixel (fDisplay, DefaultScreen (fDisplay)));
1846 }
1847 else if (backColor == Color::kBlack) {
1848 ::XSetBackground (fDisplay, fGC, BlackPixel (fDisplay, DefaultScreen (fDisplay)));
1849 }
1850 else {
1851 XColor bgColorDef;
1852 memset (&bgColorDef, 0, sizeof (bgColorDef));
1853 bgColorDef.red = backColor.GetRed ();
1854 bgColorDef.green = backColor.GetGreen ();
1855 bgColorDef.blue = backColor.GetBlue ();
1856 Colormap cmap = DefaultColormap (fDisplay, DefaultScreen (fDisplay));
1857 Status s = XAllocColor (fDisplay, cmap, &bgColorDef);
1858 if (s == 0) {
1859 ::XSetBackground (fDisplay, fGC, WhitePixel (fDisplay, DefaultScreen (fDisplay)));
1860 }
1861 else {
1862 ::XSetBackground (fDisplay, fGC, bgColorDef.pixel);
1863 }
1864 }
1865#endif
1866}
1867
1868void Tablet::SetForeColor (const Color& foreColor)
1869{
1870#if qStroika_Foundation_Common_Platform_Windows
1871 SetTextColor (foreColor.GetOSRep ());
1872#elif qStroika_FeatureSupported_XWindows
1873 if (foreColor == Color::kWhite) {
1874 ::XSetForeground (fDisplay, fGC, WhitePixel (fDisplay, DefaultScreen (fDisplay)));
1875 }
1876 else if (foreColor == Color::kBlack) {
1877 ::XSetForeground (fDisplay, fGC, BlackPixel (fDisplay, DefaultScreen (fDisplay)));
1878 }
1879 else {
1880 XColor fgColorDef;
1881 memset (&fgColorDef, 0, sizeof (fgColorDef));
1882 fgColorDef.red = foreColor.GetRed ();
1883 fgColorDef.green = foreColor.GetGreen ();
1884 fgColorDef.blue = foreColor.GetBlue ();
1885 Colormap cmap = DefaultColormap (fDisplay, DefaultScreen (fDisplay));
1886 Status s = ::XAllocColor (fDisplay, cmap, &fgColorDef);
1887 if (s == 0) {
1888 ::XSetForeground (fDisplay, fGC, BlackPixel (fDisplay, DefaultScreen (fDisplay)));
1889 }
1890 else {
1891 ::XSetForeground (fDisplay, fGC, fgColorDef.pixel);
1892 }
1893 }
1894#endif
1895}
1896
1897/*
1898@METHOD: Tablet::EraseBackground_SolidHelper
1899@DESCRIPTION: <p>EraseBackground_SolidHelper () is simple helper function - usually called from subclasses which OVERRIDE
1900 @'TextImager::EraseBackground'.</p>
1901*/
1902void Tablet::EraseBackground_SolidHelper (const Led_Rect& eraseRect, const Color& eraseColor)
1903{
1904 if (not eraseRect.IsEmpty ()) {
1905#if qStroika_Foundation_Common_Platform_Windows
1906 Led_Rect eraser = eraseRect;
1907 Brush backgroundBrush (eraseColor.GetOSRep ());
1908 GDI_Obj_Selector pen (this, ::GetStockObject (NULL_PEN));
1909 GDI_Obj_Selector brush (this, backgroundBrush);
1910 ++eraser.right; // lovely - windows doesn't count last pixel... See Docs for Rectangle() and rephrase!!!
1911 ++eraser.bottom;
1912 Rectangle (AsRECT (eraser));
1913#elif qStroika_FeatureSupported_XWindows
1914 XGCValues prevValues;
1915 const unsigned long kSavedAttrs = GCForeground;
1916 Colormap cmap = DefaultColormap (fDisplay, 0);
1917 XColor fgColorDef;
1918 memset (&fgColorDef, 0, sizeof (fgColorDef));
1919 fgColorDef.red = eraseColor.GetRed ();
1920 fgColorDef.green = eraseColor.GetGreen ();
1921 fgColorDef.blue = eraseColor.GetBlue ();
1922 Status s = ::XAllocColor (fDisplay, cmap, &fgColorDef);
1923 if (s != 0) {
1924 ::XSetForeground (fDisplay, fGC, fgColorDef.pixel);
1925 }
1926 Led_Rect adjustedEraseRect = eraseRect - fDrawableOrigin;
1927 ::XFillRectangle (fDisplay, fDrawable, fGC, adjustedEraseRect.GetLeft (), adjustedEraseRect.GetTop (),
1928 adjustedEraseRect.GetWidth (), adjustedEraseRect.GetHeight ());
1929 ::XChangeGC (fDisplay, fGC, kSavedAttrs, &prevValues);
1930#endif
1931 }
1932}
1933
1934/*
1935@METHOD: Tablet::HilightArea_SolidHelper
1936@DESCRIPTION: <p>HilightArea_SolidHelper () is simple helper function - usually called from subclasses which OVERRIDE
1937 @'TextImager::HilightArea'.</p>
1938 <p>Note the backColor and foreColor are advisory - and maybe ignored if the GDI better supports (or the
1939 platform UI conventionally calls for) inverting the text via a simple XOR.</p>
1940 <p>Note also that as of Led 3.1b4, there is new code to properly respect the hilight fore/back colors. This code
1941 should generally be fast enough, but may - on some hardware - NOT be fast enough. By setting the text color to black,
1942 the background color to while, and the hilight colors the reverse of this (fore=black/back=white), this code will revert
1943 to the old algorithm, and run much faster.</p>
1944*/
1945void Tablet::HilightArea_SolidHelper (const Led_Rect& hilightArea, [[maybe_unused]] Color hilightBackColor,
1946 [[maybe_unused]] Color hilightForeColor, Color oldBackColor, [[maybe_unused]] Color oldForeColor)
1947{
1948 if (not hilightArea.IsEmpty ()) {
1949#if qStroika_Foundation_Common_Platform_Windows
1950 /*
1951 * SPR#1271 - major reworking using DIB sections etc, to get much better display of hilighted text.
1952 */
1953 if (hilightBackColor.GetOSRep () == Color::kBlack.GetOSRep () and hilightForeColor.GetOSRep () == Color::kWhite.GetOSRep () and
1954 oldBackColor.GetOSRep () == Color::kWhite.GetOSRep () and oldForeColor.GetOSRep () == Color::kBlack.GetOSRep ()) {
1955 // This is much faster (on some/most hardware) than the RecolorHelper algorithms. For this special case of of B&W fore/back/hilight
1956 // colors - this code is MUCH faster as well. So - for people for whom the default algorithm is too slow - they can just specify
1957 // these colors for hilight and back/fore-color, and they'll get the faster hilight.
1958 // See SPR#1271
1959 BitBlt (hilightArea.left, hilightArea.top, hilightArea.GetWidth (), hilightArea.GetHeight (), this, hilightArea.left,
1960 hilightArea.top, DSTINVERT);
1961 }
1962 else {
1963#if 1
1964 fRecolorHelper = RecolorHelper::CheckCacheAndReconstructIfNeeded (fRecolorHelper, m_hDC,
1965 Led_Size (hilightArea.GetHeight (), hilightArea.GetWidth ()),
1966 hilightBackColor, hilightForeColor, oldBackColor, oldForeColor);
1967 fRecolorHelper->DoRecolor (hilightArea);
1968#else
1969 static RecolorHelper* recolorHelper = nullptr;
1970 recolorHelper = RecolorHelper::CheckCacheAndReconstructIfNeeded (recolorHelper, m_hDC,
1971 Led_Size (hilightArea.GetHeight (), hilightArea.GetWidth ()),
1972 hilightBackColor, hilightForeColor, oldBackColor, oldForeColor);
1973 recolorHelper->DoRecolor (hilightArea);
1974#endif
1975 }
1976#elif qStroika_FeatureSupported_XWindows
1977 /*
1978 * Quick and dirty primitive version. Should probably take into account backColor/foreColor args.
1979 * -- LGP 2001-04-30
1980 */
1981 XGCValues prevValues;
1982 const unsigned long kSavedAttrs = GCFunction | GCForeground | GCBackground;
1983 (void)::memset (&prevValues, 0, sizeof (prevValues));
1984 ::XGetGCValues (fDisplay, fGC, kSavedAttrs, &prevValues);
1985 ::XSetFunction (fDisplay, fGC, GXxor);
1986 long whiteP = WhitePixel (fDisplay, DefaultScreen (fDisplay));
1987 long blackP = BlackPixel (fDisplay, DefaultScreen (fDisplay)) ^ whiteP;
1988 ::XSetBackground (fDisplay, fGC, whiteP);
1989 ::XSetForeground (fDisplay, fGC, blackP);
1990 Led_Rect adjustedRect = hilightArea - fDrawableOrigin;
1991 ::XFillRectangle (fDisplay, fDrawable, fGC, adjustedRect.GetLeft (), adjustedRect.GetTop (), adjustedRect.GetWidth (),
1992 adjustedRect.GetHeight ());
1993 ::XChangeGC (fDisplay, fGC, kSavedAttrs, &prevValues);
1994#endif
1995 }
1996}
1997
1998/*
1999@METHOD: Tablet::HilightArea_SolidHelper
2000@DESCRIPTION: <p>HilightArea_SolidHelper () is simple helper function - usually called from subclasses which OVERRIDE
2001 @'TextImager::HilightArea'.</p>
2002 <p>Note the backColor and foreColor are advisory - and maybe ignored if the GDI better supports (or the
2003 platform UI conventionally calls for) inverting the text via a simple XOR.</p>
2004*/
2005void Tablet::HilightArea_SolidHelper (const Region& hilightArea, [[maybe_unused]] Color hilightBackColor, [[maybe_unused]] Color hilightForeColor,
2006 [[maybe_unused]] Color oldBackColor, [[maybe_unused]] Color oldForeColor)
2007{
2008 if (not hilightArea.IsEmpty ()) {
2009#if qStroika_Foundation_Common_Platform_Windows
2010 Assert (false); // probably not hard - bit not totally obvious how todo and since not called yet - ignore for now... LGP 2002-12-03
2011#elif qStroika_FeatureSupported_XWindows
2012 Assert (false); // I have no XWin region implementation yet... LGP 2002-12-03
2013#endif
2014 }
2015}
2016
2017/*
2018@METHOD: Tablet::GetFontMetrics
2019@DESCRIPTION: <p>Retrieve the (@'FontMetrics') associated with the current tablet (based on the last SetFont call).</p>
2020*/
2021FontMetrics Tablet::GetFontMetrics () const
2022{
2023#if qStroika_Foundation_Common_Platform_Windows
2024 RequireNotNull (m_hAttribDC);
2025 TEXTMETRIC tms;
2026 Verify (::GetTextMetrics (m_hAttribDC, &tms) != 0);
2027 return tms;
2028#elif qStroika_FeatureSupported_XWindows
2029 FontMetrics::PlatformSpecific result;
2030 memset (&result, 0, sizeof (result));
2031 Execution::ThrowIfNull (fCachedFontInfo);
2032 result.fAscent = fCachedFontInfo->ascent;
2033 result.fDescent = fCachedFontInfo->descent;
2034 result.fLeading = 0; // NOT SURE WHAT THIS IS in X-terminology. Maybe just not supported in XFonts? - LGP 2001-05-07
2035 result.fMaxCharWidth = fCachedFontInfo->max_bounds.width;
2036 return result;
2037#endif
2038}
2039
2040#if qStroika_FeatureSupported_XWindows
2041void Tablet::SetFont (const FontSpecification& fontSpec)
2042{
2043 /*
2044 * First, see if the XFontStruct* is already cached. If so - all we need todo is (maybe) an XSetFont call.
2045 */
2046 {
2047 map<string, XFontStruct*>::const_iterator i = fFontCache.find (fontSpec.GetOSRep ());
2048 if (i != fFontCache.end ()) {
2049 XFontStruct* newFontStruct = i->second;
2050 if (newFontStruct != fCachedFontInfo) {
2051 fCachedFontInfo = i->second;
2052 AssertNotNull (fCachedFontInfo);
2053 ::XSetFont (fDisplay, fGC, fCachedFontInfo->fid);
2054 }
2055 return;
2056 }
2057 /*
2058 * If a cache miss, then assure cache not too big.
2059 */
2060 if (fFontCache.size () >= kMaxFontCacheSize) {
2061 // remove a random elt
2062 ::XFreeFont (fDisplay, fFontCache.begin ()->second);
2063 fFontCache.erase (fFontCache.begin ());
2064 }
2065 }
2066
2067 /*
2068 * The font is not already cached. We must try to find it (maybe finding the name in the
2069 * fFontMappingCache cache, maybe not.
2070 */
2071 fCachedFontInfo = nullptr;
2072 fCachedFontInfo = ::XLoadQueryFont (fDisplay, fontSpec.GetOSRep ().c_str ());
2073 if (fCachedFontInfo == nullptr) {
2074 /*
2075 * Look and see if the font is in the cache.
2076 */
2077 map<string, string>::const_iterator i = fFontMappingCache.find (fontSpec.GetOSRep ());
2078
2079 string useFontName;
2080 if (i != fFontMappingCache.end ()) {
2081 useFontName = i->second;
2082 Assert (not useFontName.empty ());
2083 }
2084 else {
2085 // try font-matching algorithm...
2086 char pointSize[1024];
2087 (void)::sprintf (pointSize, "%d", fontSpec.GetPointSize () * 10);
2088 const string kMatchAny = "*";
2089 string tryFontRep = fontSpec.mkOSRep (kMatchAny, fontSpec.GetFontNameSpecifier (), kMatchAny, kMatchAny, kMatchAny);
2090 int nFonts = 0;
2091 char** fontList = ::XListFonts (fDisplay, tryFontRep.c_str (), 100000, &nFonts);
2092#if qDebugFontDetails
2093 bool nameMatchFailure = false;
2094#endif
2095 if (fontList == nullptr) {
2096#if qDebugFontDetails
2097 nameMatchFailure = true;
2098#endif
2099 // Try a few name mappings/aliases (apx equal fonts - generalize this!!!)
2100 if (fontSpec.GetFontNameSpecifier () == "Times New Roman") {
2101 tryFontRep = fontSpec.mkOSRep (kMatchAny, "times", kMatchAny, kMatchAny, kMatchAny);
2102 fontList = ::XListFonts (fDisplay, tryFontRep.c_str (), 100000, &nFonts);
2103 }
2104 }
2105 if (fontList == nullptr) {
2106#if qDebugFontDetails
2107 nameMatchFailure = true;
2108#endif
2109 tryFontRep = fontSpec.mkOSRep (kMatchAny, kMatchAny, kMatchAny, kMatchAny, kMatchAny);
2110 fontList = ::XListFonts (fDisplay, tryFontRep.c_str (), 100000, &nFonts);
2111 }
2112 Execution::ThrowIfNull (fontList);
2113 vector<string> vFontList;
2114 {
2115 vFontList.reserve (nFonts);
2116 for (size_t i = 0; i < nFonts; ++i) {
2117 vFontList.push_back (fontList[i]);
2118 }
2119 }
2120 ::XFreeFontNames (fontList);
2121 fontList = nullptr;
2122 string bestMatchingName = BestMatchFont (fontSpec, vFontList);
2123#if qDebugFontDetails
2124 if (nameMatchFailure) {
2125 fprintf (stderr, "Couldn't find fontName '%s'- using BestMatchSpec = '%s'\r\n", fontSpec.GetFontNameSpecifier ().c_str (),
2126 bestMatchingName.c_str ());
2127 }
2128#endif
2129 useFontName = bestMatchingName;
2130 Assert (not useFontName.empty ());
2131#if qDebugFontDetails
2132 fprintf (stderr, "Adding mapping to fFontMappingCache: '%s'- ==> '%s'\r\n", fontSpec.GetOSRep ().c_str (), useFontName.c_str ());
2133#endif
2134 fFontMappingCache.insert (map<string, string>::value_type (fontSpec.GetOSRep (), useFontName));
2135 }
2136 fCachedFontInfo = ::XLoadQueryFont (fDisplay, useFontName.c_str ());
2137 Execution::ThrowIfNull (fCachedFontInfo);
2138 }
2139 fFontCache.insert (map<string, XFontStruct*>::value_type (fontSpec.GetOSRep (), fCachedFontInfo));
2140 AssertNotNull (fCachedFontInfo);
2141 ::XSetFont (fDisplay, fGC, fCachedFontInfo->fid);
2142}
2143
2144void Tablet::SetDrawableOrigin (const Led_Point& origin)
2145{
2146 fDrawableOrigin = origin;
2147}
2148#endif
2149
2150#if qStroika_FeatureSupported_XWindows
2151static bool FontNamesEqual (const string& lhs, const string& rhs)
2152{
2153 if (lhs.length () != rhs.length ()) {
2154 return false;
2155 }
2156 for (size_t i = 0; i < lhs.length (); ++i) {
2157 if (lhs[i] != rhs[i]) {
2158 return false;
2159 }
2160 }
2161 return true;
2162}
2163SDKString Tablet::BestMatchFont (const FontSpecification& fsp, const vector<SDKString>& fontsList)
2164{
2165 SDKString bestAnswer;
2166 float bestScore = 0.0f;
2167 SDKString fspName = fsp.GetFontName ();
2168 int fspPointSize = fsp.GetPointSize ();
2169 SDKString fspWeight = fsp.GetStyle_Bold () ? "bold" : "medium";
2170 SDKString fspItalics = fsp.GetStyle_Italic () ? "i" : "r";
2171 const string kMatchAny = "*";
2172 for (auto i = fontsList.begin (); i != fontsList.end (); ++i) {
2173 SDKString name;
2174 SDKString size;
2175 SDKString weight;
2176 SDKString slant;
2177 ParseFontName (*i, &name, &size, &weight, &slant);
2178 bool rightFontName = (FontNamesEqual (fspName, name));
2179
2180 float thisScore = 1;
2181 if (rightFontName) {
2182 thisScore += 10;
2183 }
2184 int thisPointSize = 0;
2185 if (::sscanf (size.c_str (), "%d", &thisPointSize) == 1) {
2186 int pointSizeDiff = abs (thisPointSize - (fspPointSize * 10));
2187 float scoreAdj = (100.0f - (pointSizeDiff / 10.0f)) / 10.0f;
2188 scoreAdj = max (0.0f, scoreAdj);
2189 thisScore += scoreAdj;
2190 }
2191 if (weight == fspWeight) {
2192 thisScore += 5.0f;
2193 }
2194 if (slant == fspItalics) {
2195 thisScore += 4.0f;
2196 }
2197
2198 if (thisScore > bestScore) {
2199 bestScore = thisScore;
2200 bestAnswer = FontSpecification::mkOSRep (kMatchAny, name, weight, slant, size);
2201 }
2202 }
2203 return bestAnswer;
2204}
2205
2206int Tablet::IgnoreXErrorHandler (Display* /*display*/, XErrorEvent* /*error*/)
2207{
2208 return 0;
2209}
2210
2211void Tablet::ParseFontName (const SDKString& fontName, SDKString* familyName, SDKString* fontSize, SDKString* fontWeight, SDKString* fontSlant)
2212{
2213 RequireNotNull (familyName);
2214 RequireNotNull (fontSize);
2215 RequireNotNull (fontWeight);
2216
2217 SDKString foundry;
2218 SDKString family;
2219 SDKString weight;
2220 SDKString slant;
2221 SDKString setwidth;
2222 SDKString pixels;
2223 SDKString points;
2224 SDKString hRes;
2225 SDKString vRes;
2226 SDKString spacing;
2227 SDKString aveWidth;
2228 SDKString charset;
2229
2230 size_t start = 1;
2231 size_t end = fontName.find ('-', start);
2232 foundry = fontName.substr (start, end - start);
2233
2234 start = end + 1;
2235 end = fontName.find ('-', start);
2236 family = fontName.substr (start, end - start);
2237
2238 start = end + 1;
2239 end = fontName.find ('-', start);
2240 weight = fontName.substr (start, end - start);
2241
2242 start = end + 1;
2243 end = fontName.find ('-', start);
2244 slant = fontName.substr (start, end - start);
2245
2246 start = end + 1;
2247 end = fontName.find ('-', start);
2248 setwidth = fontName.substr (start, end - start);
2249
2250 start = end + 1;
2251 end = fontName.find ('-', start);
2252 string ignored = fontName.substr (start, end - start);
2253
2254 start = end + 1;
2255 end = fontName.find ('-', start);
2256 pixels = fontName.substr (start, end - start);
2257
2258 start = end + 1;
2259 end = fontName.find ('-', start);
2260 points = fontName.substr (start, end - start);
2261
2262 start = end + 1;
2263 end = fontName.find ('-', start);
2264 hRes = fontName.substr (start, end - start);
2265
2266 start = end + 1;
2267 end = fontName.find ('-', start);
2268 vRes = fontName.substr (start, end - start);
2269
2270 *familyName = family;
2271 *fontSize = points;
2272 *fontWeight = weight;
2273 *fontSlant = slant;
2274}
2275#endif
2276
2277/*
2278 ********************************************************************************
2279 ***************************** OffscreenTablet::OT ******************************
2280 ********************************************************************************
2281 */
2282#if qStroika_Foundation_Common_Platform_MacOS
2283OffscreenTablet::OT::OT (GrafPtr gp)
2284 : inherited (gp)
2285{
2286}
2287#elif qStroika_Foundation_Common_Platform_Windows
2288OffscreenTablet::OT::OT (HDC hdc, Tablet::OwnDCControl ownsDC)
2289 : inherited (hdc, ownsDC)
2290{
2291}
2292#elif qStroika_FeatureSupported_XWindows
2293OffscreenTablet::OT::OT (Display* display, Drawable drawable)
2294 : inherited (display, drawable)
2295{
2296}
2297#endif
2298
2299/*
2300 ********************************************************************************
2301 ********************************* OffscreenTablet ******************************
2302 ********************************************************************************
2303 */
2304OffscreenTablet::OffscreenTablet ()
2305 : fOrigTablet (nullptr)
2306 , fOffscreenRect (Led_Rect (0, 0, 0, 0))
2307 , fOffscreenTablet (nullptr)
2308#if qStroika_Foundation_Common_Platform_MacOS
2309 , fOrigDevice (nullptr)
2310 , fOrigPort (nullptr)
2311 , fOffscreenGWorld (nullptr)
2312#elif qStroika_Foundation_Common_Platform_Windows
2313 , fMemDC ()
2314 , fMemoryBitmap ()
2315 , fOldBitmapInDC (nullptr)
2316#elif qStroika_FeatureSupported_XWindows
2317 , fPixmap (0)
2318#endif
2319{
2320}
2321
2322OffscreenTablet::~OffscreenTablet ()
2323{
2324#if qStroika_Foundation_Common_Platform_MacOS
2325 if (fOrigPort != nullptr) {
2326 ::SetGWorld (fOrigPort, fOrigDevice); // restore gworld
2327 }
2328 if (fOffscreenGWorld != nullptr) {
2329 ::DisposeGWorld (fOffscreenGWorld);
2330 }
2331 delete fOffscreenTablet;
2332#elif qStroika_Foundation_Common_Platform_Windows
2333 if (fOldBitmapInDC != nullptr) {
2334 (void)fMemDC.SelectObject (fOldBitmapInDC);
2335 }
2336#elif qStroika_FeatureSupported_XWindows
2337 if (fPixmap != 0) {
2338 ::XFreePixmap (fOrigTablet->fDisplay, fPixmap);
2339 }
2340#endif
2341}
2342
2343/*
2344@METHOD: OffscreenTablet::Setup
2345@DESCRIPTION: <p>Prepare a new offscreen drawing environment given the starting basis 'originalTablet' (typically from a window).</p>
2346 <p>Later call @'OffscreenTablet::PrepareRect' before any actual drawing can be done. This should be called once before
2347 calling @'OffscreenTablet::PrepareRect'.</p>
2348*/
2349void OffscreenTablet::Setup (Tablet* origTablet)
2350{
2351 Require (fOrigTablet == nullptr); // can only call once.
2352 RequireNotNull (origTablet);
2353
2354 fOrigTablet = origTablet;
2355#if qStroika_Foundation_Common_Platform_MacOS
2356 // Save the old gworld info
2357 Assert (fOrigPort == nullptr);
2358 Assert (fOrigDevice == nullptr);
2359 ::GetGWorld (&fOrigPort, &fOrigDevice);
2360
2361 // Create our gworld (may have to cache this if it turns out to be expensive to re-create...
2362 Assert (fOffscreenGWorld == nullptr);
2363 {
2364 Rect bounds = AsQDRect (Led_Rect (0, 0, 1, 1)); // size appropriately on a row-by-row basis below...
2365 OSErr theErr = SafeNewGWorld (&fOffscreenGWorld, 0, &bounds, nullptr, nullptr, noNewDevice | useTempMem);
2366 if (theErr != noErr) {
2367 fOffscreenGWorld = nullptr; // no biggie, we just don't use it...
2368 }
2369 }
2370 if (fOffscreenGWorld != nullptr) {
2371 fOffscreenTablet = new OT (reinterpret_cast<GrafPtr> (fOffscreenGWorld));
2372 }
2373#elif qStroika_Foundation_Common_Platform_Windows
2374 if (fMemDC.CreateCompatibleDC (fOrigTablet)) {
2375 fOffscreenTablet = &fMemDC;
2376 }
2377#elif qStroika_FeatureSupported_XWindows
2378 Assert (fPixmap == 0);
2379// Nothing todo yet - create the pixmap when we know the RowRect.
2380#endif
2381}
2382
2383/*
2384@METHOD: OffscreenTablet::PrepareRect
2385@DESCRIPTION: <p>Prepare the offscreen drawing environment for the given 'currentRowRect'. This can only be safely called
2386 after the call to @'OffscreenTablet::Setup' - but can be called multiple times. Note that calls to this
2387 will typically 'destroy' the bits in the offscreen tablet.</p>
2388*/
2389Tablet* OffscreenTablet::PrepareRect (const Led_Rect& currentRowRect, DistanceType extraToAddToBottomOfRect)
2390{
2391 Tablet* result = fOrigTablet;
2392#if qStroika_Foundation_Common_Platform_MacOS
2393 if (fOffscreenTablet != nullptr) {
2394 fOffscreenRect = currentRowRect;
2395 fOffscreenRect.bottom += extraToAddToBottomOfRect;
2396 Rect bounds = AsQDRect (fOffscreenRect);
2397 ::OffsetRect (&bounds, -bounds.left, -bounds.top);
2398#if TARGET_CARBON
2399 Led_Size curOffscreenGWorldSize;
2400 {
2401 Rect junk;
2402 curOffscreenGWorldSize = AsLedSize (GetRectSize (*::GetPixBounds (::GetPortPixMap (fOffscreenGWorld), &junk)));
2403 }
2404#else
2405 Led_Size curOffscreenGWorldSize = AsLedSize (GetRectSize ((*fOffscreenGWorld->portPixMap)->bounds));
2406#endif
2407 if ((fOffscreenRect.GetSize () == curOffscreenGWorldSize) or SafeUpdateGWorld (&fOffscreenGWorld, 0, &bounds, nullptr, nullptr, 0) >= 0) {
2408 AssertNotNull (::GetGWorldPixMap (fOffscreenGWorld));
2409 if (::LockPixels (::GetGWorldPixMap (fOffscreenGWorld))) {
2410 // UpdateGWorld () can change grafPortPTR!
2411 delete fOffscreenTablet;
2412 fOffscreenTablet = new OT (reinterpret_cast<GrafPtr> (fOffscreenGWorld));
2413 result = fOffscreenTablet;
2414 ::SetGWorld (fOffscreenGWorld, nullptr);
2415 ::SetOrigin (fOffscreenRect.left, fOffscreenRect.top);
2416 goto good;
2417 }
2418 }
2419 bad:
2420 ::SetGWorld (fOrigPort, fOrigDevice); // restore gworld
2421 if (fOffscreenGWorld != nullptr) {
2422 ::DisposeGWorld (fOffscreenGWorld);
2423 fOffscreenGWorld = nullptr;
2424 }
2425 delete fOffscreenTablet;
2426 fOffscreenTablet = nullptr;
2427 good:;
2428 }
2429#elif qStroika_Foundation_Common_Platform_Windows
2430 if (fOffscreenTablet != nullptr) {
2431 fOffscreenRect = currentRowRect;
2432 fOffscreenRect.bottom += extraToAddToBottomOfRect;
2433 // See if we need to re-allocate the bitmap
2434 if (fMemoryBitmap == nullptr or (fOffscreenRect.GetSize () != fMemoryBitmap.GetImageSize ())) {
2435 // deselect our new memory bitmap before changing its size - not sure needed, but lets be paranoid -
2436 // this is Windows after all ... LGP 960513
2437 if (fOldBitmapInDC != nullptr) {
2438 (void)fMemDC.SelectObject (fOldBitmapInDC);
2439 }
2440 fMemoryBitmap.DeleteObject (); // lose previous contents, if any...
2441#if qUseDIBSectionForOffscreenBitmap
2442 if (fMemoryBitmap.CreateCompatibleDIBSection (fOrigTablet->m_hDC, fOffscreenRect.GetWidth (), fOffscreenRect.GetHeight ()) == 0) {
2443 fOffscreenTablet = nullptr; // OK, just don't use...
2444 }
2445#else
2446 if (fMemoryBitmap.CreateCompatibleBitmap (fOrigTablet->m_hDC, fOffscreenRect.GetWidth (), fOffscreenRect.GetHeight ()) == 0) {
2447 fOffscreenTablet = nullptr; // OK, just don't use...
2448 }
2449#endif
2450 if (fOffscreenTablet != nullptr) {
2451 fOldBitmapInDC = fMemDC.SelectObject (fMemoryBitmap);
2452 if (fOldBitmapInDC == nullptr) {
2453 fOffscreenTablet = nullptr; // OK, just don't use...
2454 }
2455 }
2456 }
2457 if (fOffscreenTablet != nullptr) {
2458 result = fOffscreenTablet; // Draw into offscreen bitmap
2459 }
2460 if (fOffscreenTablet != nullptr) {
2461 fMemDC.SetWindowOrg (fOffscreenRect.left, fOffscreenRect.top);
2462 }
2463 }
2464#elif qStroika_FeatureSupported_XWindows
2465 Led_Size pixmapSize = fOffscreenRect.GetSize ();
2466 fOffscreenRect = currentRowRect;
2467 fOffscreenRect.bottom += extraToAddToBottomOfRect;
2468 if (fPixmap == 0 or pixmapSize != fOffscreenRect.GetSize ()) {
2469 // Destroy old pixmap, and create new one
2470 delete fOffscreenTablet;
2471 fOffscreenTablet = nullptr;
2472 if (fPixmap != 0) {
2473 ::XFreePixmap (fOrigTablet->fDisplay, fPixmap);
2474 fPixmap = 0;
2475 }
2476 unsigned int depth = 1; // default - cuz should always be supported
2477 {
2478 // Try to get it from the drawable. Only works (I believe) if the drawable is a window.
2479 XWindowAttributes winAttrs;
2480 (void)::memset (&winAttrs, 0, sizeof (winAttrs));
2481 /*
2482 * Since we don't know for sure the drawable is a window - catch the error and ignore it. Don't let
2483 * XErrorHandler do anything bad.
2484 */
2485 int (*oldErrHandler) (Display*, XErrorEvent*) = ::XSetErrorHandler (Tablet::IgnoreXErrorHandler);
2486 Status s = ::XGetWindowAttributes (fOrigTablet->fDisplay, fOrigTablet->fDrawable, &winAttrs);
2487 ::XSetErrorHandler (oldErrHandler);
2488 if (s == 0) {
2489 // if call failed - no biggie. Just pick the DefaultDepthOfScreen (could have used XListDepths ()?).
2490 depth = ::XDefaultDepthOfScreen (::XScreenOfDisplay (fOrigTablet->fDisplay, DefaultScreen (fOrigTablet->fDisplay)));
2491 }
2492 else {
2493 depth = winAttrs.depth;
2494 }
2495 }
2496 fPixmap = ::XCreatePixmap (fOrigTablet->fDisplay, fOrigTablet->fDrawable, fOffscreenRect.GetWidth (), fOffscreenRect.GetHeight (), depth);
2497 Assert (fPixmap != 0);
2498 try {
2499 fOffscreenTablet = new OT (fOrigTablet->fDisplay, fPixmap);
2500 fOffscreenTablet->fColormap = fOrigTablet->fColormap;
2501 fOffscreenTablet->fFontMappingCache = fOrigTablet->fFontMappingCache;
2502 }
2503 catch (...) {
2504 delete fOffscreenTablet;
2505 fOffscreenTablet = nullptr;
2506 throw;
2507 }
2508 }
2509 if (fOffscreenTablet != nullptr) {
2510 fOffscreenTablet->SetDrawableOrigin (fOffscreenRect.GetTopLeft ());
2511 result = fOffscreenTablet; // Draw into offscreen bitmap
2512 }
2513#endif
2514 return result;
2515}
2516
2517/*
2518@METHOD: OffscreenTablet::BlastBitmapToOrigTablet
2519@DESCRIPTION: <p>Copy the bits which have been saved away into this offscreen tablet back to the original tablet specified in
2520 @'OffscreenTablet::Setup' and to coordinates specified in the last call to @'OffscreenTablet::PrepareRect'.</p>
2521*/
2522void OffscreenTablet::BlastBitmapToOrigTablet ()
2523{
2524 if (fOffscreenTablet != nullptr) {
2525#if qStroika_Foundation_Common_Platform_MacOS
2526 Rect bounds = AsQDRect (fOffscreenRect);
2527 ::SetGWorld (fOrigPort, fOrigDevice); // restore gworld
2528 GDI_RGBForeColor (Color::kBlack.GetOSRep ());
2529 GDI_RGBBackColor (Color::kWhite.GetOSRep ());
2530 GrafPtr tabletGrafPort = *fOffscreenTablet;
2531#if TARGET_CARBON
2532 {
2533 Rect tmp;
2534 ::CopyBits (::GetPortBitMapForCopyBits (tabletGrafPort), ::GetPortBitMapForCopyBits (fOrigPort),
2535 ::GetPortBounds (tabletGrafPort, &tmp), &bounds, srcCopy, nullptr);
2536 }
2537#else
2538 ::CopyBits (&tabletGrafPort->portBits, &((GrafPtr)fOrigPort)->portBits, &tabletGrafPort->portRect, &bounds, srcCopy, nullptr);
2539#endif
2540 ::UnlockPixels (::GetGWorldPixMap (fOffscreenGWorld));
2541#elif qStroika_Foundation_Common_Platform_Windows
2542 Tablet* screenDC = fOrigTablet;
2543 screenDC->BitBlt (fOffscreenRect.left, fOffscreenRect.top, fOffscreenRect.GetWidth (), fOffscreenRect.GetHeight (),
2544 fOffscreenTablet, fOffscreenRect.left, fOffscreenRect.top, SRCCOPY);
2545#elif qStroika_FeatureSupported_XWindows
2546 Assert (fPixmap != 0);
2547 ::XCopyArea (fOrigTablet->fDisplay, fOffscreenTablet->fDrawable, fOrigTablet->fDrawable, fOrigTablet->fGC, 0, 0,
2548 fOffscreenRect.GetWidth (), fOffscreenRect.GetHeight (), (int)fOffscreenRect.GetLeft (), (int)fOffscreenRect.GetTop ());
2549#endif
2550 }
2551}
2552#endif
2553
2554#if qStroika_Frameworks_Led_SupportGDI
2555
2556/*
2557 ********************************************************************************
2558 ********************************* InstalledFonts ***************************
2559 ********************************************************************************
2560 */
2561InstalledFonts::InstalledFonts (
2562#if qStroika_FeatureSupported_XWindows
2563 Display* display,
2564#endif
2565 FilterOptions filterOptions)
2566 : fFilterOptions (filterOptions)
2567 , fFontNames ()
2568{
2569#if qStroika_Foundation_Common_Platform_Windows
2570 LOGFONT lf;
2571 memset (&lf, 0, sizeof (LOGFONT));
2572 lf.lfCharSet = DEFAULT_CHARSET;
2573 WindowDC screenDC (nullptr);
2574 ::EnumFontFamiliesEx (screenDC.m_hDC, &lf, (FONTENUMPROC)FontFamilyAdderProc, reinterpret_cast<LPARAM> (this), 0);
2575 sort (fFontNames.begin (), fFontNames.end ());
2576 vector<SDKString>::iterator rest = unique (fFontNames.begin (), fFontNames.end ());
2577 fFontNames.erase (rest, fFontNames.end ()); // remove the duplicates
2578#elif qStroika_FeatureSupported_XWindows
2579 int fontListSize = 0;
2580 char** fontList = ::XListFonts (display, "*", 200000, &fontListSize);
2581 set<string> fontNames;
2582 for (int i = 0; i < fontListSize; ++i) {
2583 string longFontName = fontList[i];
2584 string tmp = longFontName;
2585 if (tmp.length () > 0 and tmp[0] == '-') {
2586 size_t nextDash = tmp.find ('-', 1);
2587 if (nextDash != string::npos and nextDash > 1) {
2588 tmp = tmp.substr (nextDash + 1);
2589 }
2590 nextDash = tmp.find ('-'); // OK - even if end of string
2591 string fontFamilyName = tmp.substr (0, nextDash);
2592 if (not fontFamilyName.empty ()) {
2593 fontNames.insert (fontFamilyName);
2594 }
2595 }
2596 }
2597 ::XFreeFontNames (fontList);
2598 fontList = nullptr;
2599 fFontNames = vector<string> (fontNames.begin (), fontNames.end ());
2600#else
2601 Assert (false); // NYI for other platforms
2602#endif
2603}
2604
2605#if qStroika_Foundation_Common_Platform_Windows
2606BOOL FAR PASCAL InstalledFonts::FontFamilyAdderProc (ENUMLOGFONTEX* pelf, NEWTEXTMETRICEX* /*lpntm*/, int fontType, LPVOID pThis)
2607{
2608 InstalledFonts* thisP = reinterpret_cast<InstalledFonts*> (pThis);
2609
2610 if (thisP->fFilterOptions & eSkipRasterFonts) {
2611 // don't put in non-printer raster fonts (cuz WordPad doesn't and CFontDialog doesn't appear to -
2612 // LGP 971215)
2613 if (fontType & RASTER_FONTTYPE)
2614 return 1;
2615 }
2616 if (thisP->fFilterOptions & eSkipAtSignFonts) {
2617 if (pelf->elfLogFont.lfFaceName[0] == '@')
2618 return 1;
2619 }
2620 thisP->fFontNames.push_back (pelf->elfLogFont.lfFaceName);
2621 return 1;
2622}
2623#endif
2624#endif
2625
2626#if qStroika_Frameworks_Led_SupportGDI
2627
2628/*
2629 ********************************************************************************
2630 ********************************* Globals *******************************
2631 ********************************************************************************
2632 */
2633
2634Globals* Globals::sThe = nullptr;
2635
2636// Somewhat silly hack so Globals gets destroyed at end of application execution. Helpful for quitting memleak detectors.
2637class Globals::_Global_DESTRUCTOR_ {
2638public:
2639 ~_Global_DESTRUCTOR_ ()
2640 {
2641 delete (Globals::sThe);
2642 Globals::sThe = nullptr;
2643 }
2644} sTheLed_GDIGlobalsDESTRUCTOR_;
2645
2646Globals::Globals ()
2647 : fLogPixelsH (0)
2648 , fLogPixelsV (0)
2649{
2650 InvalidateGlobals ();
2651}
2652
2653void Globals::InvalidateGlobals ()
2654{
2655// From the name, it would appear we invalidated, and re-validate later. But I think this implematnion is a bit
2656// simpler, and should perform fine given its expected usage.
2657#if qStroika_Foundation_Common_Platform_MacOS
2658 fLogPixelsH = 72;
2659 fLogPixelsV = 72;
2660#elif qStroika_Foundation_Common_Platform_Windows
2661 WindowDC screenDC (nullptr);
2662 fLogPixelsH = ::GetDeviceCaps (screenDC, LOGPIXELSX);
2663 fLogPixelsV = ::GetDeviceCaps (screenDC, LOGPIXELSY);
2664#elif qStroika_FeatureSupported_XWindows
2665 /*
2666 * Either 75 or 100??? Not sure which is best
2667 *
2668 * AbiWord has comments (in gr_UnixGraphix.cpp) that though most X-Servers return a resolution of 75, 100 seems to
2669 * look best. I tried on XWinPro 5.1 (a Win32-based X-Server) and on the one that comes with RedHat Linux 6.1, and both
2670 * looked better when I set this to 100. So try that for now...
2671 */
2672 //const int kResToUse = 75;
2673 const int kResToUse = 100;
2674 fLogPixelsH = kResToUse;
2675 fLogPixelsV = kResToUse;
2676#endif
2677}
2678#endif
2679
2680#if qStroika_Frameworks_Led_SupportGDI
2681/*
2682 ********************************************************************************
2683 ************************************ AddRectangleToRegion **********************
2684 ********************************************************************************
2685 */
2686void Led::AddRectangleToRegion (Led_Rect addRect, Region* toRgn)
2687{
2688 RequireNotNull (toRgn);
2689 *toRgn = *toRgn + Region (addRect);
2690}
2691#endif
2692
2693/*
2694 ********************************************************************************
2695 ********************************* Led_GetDIBImageSize **************************
2696 ********************************************************************************
2697 */
2698
2699/*
2700@METHOD: Led_GetDIBImageSize
2701@DESCRIPTION: <p>Return the size in pixels of the given argument DIB</p>
2702*/
2703Led_Size Led::Led_GetDIBImageSize (const Led_DIB* dib)
2704{
2705 RequireNotNull (dib);
2706 Assert (sizeof (BITMAPINFOHEADER) == 40); // just to make sure we have these defined right on other platforms
2707 Assert (sizeof (BITMAPCOREHEADER) == 12); // ''
2708 Assert (sizeof (RGBTRIPLE) == 3); // ''
2709
2710 if (IS_WIN30_DIB (dib)) {
2711 const BITMAPINFOHEADER& hdr = dib->bmiHeader;
2712 return (Led_Size (abs (Led_ByteSwapFromWindows (hdr.biHeight)), abs (Led_ByteSwapFromWindows (hdr.biWidth))));
2713 }
2714 else {
2715 const BITMAPCOREHEADER& hdr = *(reinterpret_cast<const BITMAPCOREHEADER*> (dib));
2716 return (Led_Size (Led_ByteSwapFromWindows (hdr.bcHeight), Led_ByteSwapFromWindows (hdr.bcWidth)));
2717 }
2718}
2719
2720/*
2721 ********************************************************************************
2722 *************************** Led_GetDIBPalletByteCount **************************
2723 ********************************************************************************
2724 */
2725size_t Led::Led_GetDIBPalletByteCount (const Led_DIB* dib)
2726{
2727 RequireNotNull (dib);
2728 /*
2729 * Logic from MSFT DibLook sample in MSVC.Net 2003, plus:
2730 * MSVC.Net 2003 SDK docs mention this case - that:
2731 * BI_BITFIELDS: Specifies that the bitmap is not compressed and that the
2732 * color table consists of three DWORD color masks that specify
2733 * the red, green, and blue components, respectively, of each pixel.
2734 * This is valid when used with 16- and 32-bpp bitmaps
2735 */
2736 if (IS_WIN30_DIB (dib)) {
2737 size_t byteCount = DIBNumColors (dib) * sizeof (RGBQUAD);
2738 const BITMAPINFOHEADER& hdr = dib->bmiHeader;
2739 //unsigned short bitCount = Led_ByteSwapFromWindows (hdr.biBitCount);
2740 if (Led_ByteSwapFromWindows (hdr.biCompression) == BI_BITFIELDS) {
2741#if qStroika_Foundation_Common_Platform_Windows
2742 Assert (sizeof (DWORD) == sizeof (unsigned int));
2743#endif
2744 Assert (4 == sizeof (unsigned int));
2745 byteCount += 3 * sizeof (unsigned int);
2746 }
2747 return byteCount;
2748 }
2749 else {
2750 Assert (sizeof (RGBTRIPLE) == 3); // make sure we have this defined right on each platform
2751 return (DIBNumColors (dib) * sizeof (RGBTRIPLE));
2752 }
2753}
2754
2755/*
2756 ********************************************************************************
2757 ************************* Led_GetDIBImageRowByteCount **************************
2758 ********************************************************************************
2759 */
2760/*
2761@METHOD: Led_GetDIBImageRowByteCount
2762@DESCRIPTION: <p>Return the size in bytes of a single ROW of pixels in the given argument DIB.</p>
2763*/
2764size_t Led::Led_GetDIBImageRowByteCount (const Led_DIB* dib)
2765{
2766 RequireNotNull (dib);
2767 Led_Size imageSize = Led_GetDIBImageSize (dib);
2768 const BITMAPINFOHEADER& hdr = dib->bmiHeader;
2769
2770 unsigned short bitCount = Led_ByteSwapFromWindows (hdr.biBitCount);
2771 return (((imageSize.h * bitCount + 31) & ~31) >> 3);
2772}
2773
2774/*
2775 ********************************************************************************
2776 *************************** Led_GetDIBImageByteCount ***************************
2777 ********************************************************************************
2778 */
2779/*
2780@METHOD: Led_GetDIBImageByteCount
2781@DESCRIPTION: <p>Return the size in bytes of the given argument DIB. DIBs are contiguous chunks of RAM.</p>
2782*/
2783size_t Led::Led_GetDIBImageByteCount (const Led_DIB* dib)
2784{
2785 RequireNotNull (dib);
2786 Led_Size imageSize = Led_GetDIBImageSize (dib);
2787 const BITMAPINFOHEADER& hdr = dib->bmiHeader;
2788 size_t byteCount = Led_ByteSwapFromWindows (hdr.biSize);
2789
2790 byteCount += Led_GetDIBPalletByteCount (dib);
2791
2792 unsigned long imageByteSize = Led_ByteSwapFromWindows (hdr.biSizeImage);
2793 if (imageByteSize == 0) {
2794 unsigned short bitCount = Led_ByteSwapFromWindows (hdr.biBitCount);
2795 // often zero for uncompressed images, so we compute ourselves...
2796 //imageByteSize = imageSize.v * ((imageSize.h * bitCount + 31)/32)*4;
2797 imageByteSize = imageSize.v * (((imageSize.h * bitCount + 31) & ~31) >> 3);
2798 }
2799 byteCount += imageByteSize;
2800 return byteCount;
2801}
2802
2803/*
2804 ********************************************************************************
2805 ********************************* Led_CloneDIB *********************************
2806 ********************************************************************************
2807 */
2808/*
2809@METHOD: Led_CloneDIB
2810@DESCRIPTION: <p>Make a copy of the given @'Led_DIB' object using ::operator new (). Just use normal C++ ::operator delete ()
2811 to destroy the result.</p>
2812*/
2813Led_DIB* Led::Led_CloneDIB (const Led_DIB* dib)
2814{
2815 RequireNotNull (dib);
2816 size_t nBytes = Led_GetDIBImageByteCount (dib);
2817 Led_DIB* newDIB = reinterpret_cast<Led_DIB*> (new char[nBytes]);
2818 (void)::memcpy (newDIB, dib, nBytes);
2819 return newDIB;
2820}
2821
2822/*
2823 ********************************************************************************
2824 ***************************** Led_GetDIBBitsPointer ****************************
2825 ********************************************************************************
2826 */
2827/*
2828@METHOD: Led_GetDIBBitsPointer
2829@DESCRIPTION: <p></p>
2830*/
2831const void* Led::Led_GetDIBBitsPointer (const Led_DIB* dib)
2832{
2833 RequireNotNull (dib);
2834 const BITMAPINFOHEADER& hdr = dib->bmiHeader;
2835 return reinterpret_cast<const char*> (dib) + Led_ByteSwapFromWindows (hdr.biSize) + Led_GetDIBPalletByteCount (dib);
2836}
2837
2838#if qStroika_Foundation_Common_Platform_Windows
2839/*
2840 ********************************************************************************
2841 ****************************** Led_DIBFromHBITMAP ******************************
2842 ********************************************************************************
2843 */
2844Led_DIB* Led::Led_DIBFromHBITMAP (HDC hDC, HBITMAP hbm)
2845{
2846 RequireNotNull (hbm);
2847 BITMAP bm;
2848 Verify (::GetObject (hbm, sizeof (BITMAP), (LPVOID)&bm));
2849
2850 Led_DIB* dibResult = nullptr;
2851 {
2852 BITMAPINFOHEADER bmiHdr;
2853 memset (&bmiHdr, 0, sizeof (bmiHdr));
2854 bmiHdr.biSize = sizeof (BITMAPINFOHEADER);
2855 bmiHdr.biWidth = bm.bmWidth;
2856 bmiHdr.biHeight = bm.bmHeight;
2857 bmiHdr.biPlanes = 1;
2858 bmiHdr.biBitCount = 24;
2859 bmiHdr.biCompression = BI_RGB;
2860 bmiHdr.biSizeImage = ((((bmiHdr.biWidth * bmiHdr.biBitCount) + 31) & ~31) >> 3) * bmiHdr.biHeight;
2861 size_t nBytes = Led_GetDIBImageByteCount (reinterpret_cast<Led_DIB*> (&bmiHdr));
2862 dibResult = reinterpret_cast<Led_DIB*> (new char[nBytes]);
2863 Assert (nBytes > sizeof (BITMAPINFOHEADER));
2865 (void)::memcpy (dibResult, &bmiHdr, sizeof (bmiHdr));
2866 DISABLE_COMPILER_MSC_WARNING_END (6386)
2867 }
2868
2869 [[maybe_unused]] int nScanLinesCopied =
2870 ::GetDIBits (hDC, hbm, 0, dibResult->bmiHeader.biHeight,
2871 reinterpret_cast<char*> (dibResult) + Led_GetDIBPalletByteCount (dibResult) + sizeof (BITMAPINFOHEADER), dibResult, DIB_RGB_COLORS);
2872 Assert (nScanLinesCopied == dibResult->bmiHeader.biHeight);
2873 return dibResult;
2874}
2875#endif
2876
2877#if qStroika_Frameworks_Led_ProvideIMESupport
2878#include <ime.h>
2879
2880/*
2881 ********************************************************************************
2882 ****************************************** IME *********************************
2883 ********************************************************************************
2884 */
2885IME* IME::sThe = nullptr;
2886
2887#ifndef qUseNewIMECode
2888#define qUseNewIMECode 1
2889#endif
2890
2891// Somewhat silly hack so IME gets destroyed at end of application execution. Helpful for quitting memleak detectors.
2892class IME::_Global_DESTRUCTOR_ {
2893public:
2894 ~_Global_DESTRUCTOR_ ()
2895 {
2896 delete (IME::sThe);
2897 IME::sThe = nullptr;
2898 }
2899} sTheIME_DESTRUCTOR_;
2900
2901IME::IME ()
2902 : fSendIMEMessageProc (nullptr)
2903 , fIMEEnableProc (nullptr)
2904 , fImmGetContext (nullptr)
2905 , fImmSetCompositionFont (nullptr)
2906 , fImmReleaseContext (nullptr)
2907 , fImmGetCompositionStringW (nullptr)
2908 , fImmSetCompositionWindow (nullptr)
2909 , fImmSetOpenStatus (nullptr)
2910 , fWinNlsAvailable (false)
2911 , fLastX (-1)
2912 , fLastY (-1)
2913{
2914 Assert (sThe == nullptr);
2915 sThe = this;
2916
2917#ifdef _UNICODE
2918 const char IMEPROCNAME[] = "SendIMEMessageExW";
2919#else
2920 const char IMEPROCNAME[] = "SendIMEMessageExA";
2921#endif
2922 HINSTANCE hNLS = ::GetModuleHandle (_T ("USER32.DLL"));
2923 if (hNLS != nullptr) {
2924 fSendIMEMessageProc = (LRESULT (FAR PASCAL*) (HWND, DWORD))::GetProcAddress (hNLS, IMEPROCNAME);
2925 fIMEEnableProc = (BOOL (FAR PASCAL*) (HWND, BOOL))::GetProcAddress (hNLS, "WINNLSEnableIME");
2926 }
2927 fWinNlsAvailable = fSendIMEMessageProc != nullptr and fIMEEnableProc != nullptr;
2928
2929 HINSTANCE hIMM = ::GetModuleHandle (_T ("IMM32.DLL"));
2930 if (hIMM != nullptr) {
2931#ifdef _UNICODE
2932 constexpr char ImmSetCompositionFontNAME[] = "ImmSetCompositionFontW";
2933#else
2934 constexpr char ImmSetCompositionFontNAME[] = "ImmSetCompositionFontA";
2935#endif
2936 fImmGetContext = (HIMC (FAR PASCAL*) (HWND))::GetProcAddress (hIMM, "ImmGetContext");
2937 fImmSetCompositionFont = (BOOL (FAR PASCAL*) (HIMC, const LOGFONT*))::GetProcAddress (hIMM, ImmSetCompositionFontNAME);
2938 fImmReleaseContext = (BOOL (FAR PASCAL*) (HWND, HIMC))::GetProcAddress (hIMM, "ImmReleaseContext");
2939 fImmGetCompositionStringW = (LONG (FAR PASCAL*) (HIMC, DWORD, LPVOID, DWORD))::GetProcAddress (hIMM, "ImmGetCompositionStringW");
2940 fImmSetCompositionWindow = (BOOL (FAR PASCAL*) (HIMC, const void*))::GetProcAddress (hIMM, "ImmSetCompositionWindow");
2941 fImmSetOpenStatus = (BOOL (FAR PASCAL*) (HIMC, BOOL))::GetProcAddress (hIMM, "ImmSetOpenStatus");
2942 }
2943}
2944
2945void IME::NotifyPosition (HWND hWnd, const SHORT x, const SHORT y)
2946{
2947 if (x != fLastX || y != fLastY) {
2948 UpdatePosition (hWnd, x, y);
2949 }
2950}
2951
2952void IME::NotifyOfFontChange (HWND hWnd, const LOGFONT& lf)
2953{
2954 if (fImmGetContext != nullptr and fImmSetCompositionFont != nullptr and fImmReleaseContext != nullptr) {
2955 HIMC hImc = NULL;
2956 if ((hImc = fImmGetContext (hWnd)) != NULL) {
2957 fImmSetCompositionFont (hImc, &lf);
2958 fImmReleaseContext (hWnd, hImc);
2959 }
2960 }
2961}
2962
2963#if !qUseNewIMECode
2964void IME::SendSimpleMessage (HWND hWnd, UINT fnc, WPARAM wParam)
2965{
2966 if (fSendIMEMessageProc != nullptr) {
2967 HANDLE hime = ::GlobalAlloc (GMEM_MOVEABLE | GMEM_LOWER | GMEM_DDESHARE, (DWORD)sizeof (IMESTRUCT));
2968 LPIMESTRUCT lpime;
2969 if (hime)
2970 lpime = (LPIMESTRUCT)GlobalLock (hime);
2971 else
2972 return;
2973
2974 if (lpime == nullptr) {
2975 GlobalFree (hime);
2976 return;
2977 }
2978 lpime->fnc = fnc;
2979 lpime->wParam = wParam;
2980 fSendIMEMessageProc (hWnd, (LONG)hime);
2981 wParam = lpime->wParam;
2982 ::GlobalUnlock (hime);
2983 ::GlobalFree (hime);
2984 }
2985}
2986#endif
2987
2988void IME::IMEOn (HWND hWnd)
2989{
2990#if qUseNewIMECode
2991 if (fImmGetContext != nullptr and fImmSetOpenStatus != nullptr and fImmReleaseContext != nullptr) {
2992 HIMC hImc = NULL;
2993 if ((hImc = fImmGetContext (hWnd)) != NULL) {
2994 Verify (fImmSetOpenStatus (hImc, true));
2995 fImmReleaseContext (hWnd, hImc);
2996 }
2997 }
2998#else
2999 SendSimpleMessage (hWnd, IME_SETOPEN, 1);
3000#endif
3001}
3002
3003void IME::IMEOff (HWND hWnd)
3004{
3005#if qUseNewIMECode
3006 if (fImmGetContext != nullptr and fImmSetOpenStatus != nullptr and fImmReleaseContext != nullptr) {
3007 HIMC hImc = NULL;
3008 if ((hImc = fImmGetContext (hWnd)) != NULL) {
3009 Verify (fImmSetOpenStatus (hImc, false));
3010 fImmReleaseContext (hWnd, hImc);
3011 }
3012 }
3013#else
3014 SendSimpleMessage (hWnd, IME_SETOPEN, 0);
3015#endif
3016}
3017
3018void IME::UpdatePosition (const HWND hWnd, const SHORT x, const SHORT y)
3019{
3020 if (fSendIMEMessageProc != nullptr) {
3021#if qUseNewIMECode
3022 if (fImmGetContext != nullptr and fImmSetCompositionWindow != nullptr and fImmReleaseContext != nullptr) {
3023 HIMC hImc = NULL;
3024 if ((hImc = fImmGetContext (hWnd)) != NULL) {
3025 COMPOSITIONFORM compForm;
3026 memset (&compForm, 0, sizeof (compForm));
3027 compForm.dwStyle = CFS_FORCE_POSITION;
3028 compForm.ptCurrentPos.x = x;
3029 compForm.ptCurrentPos.y = y;
3030 Verify (fImmSetCompositionWindow (hImc, &compForm));
3031 fImmReleaseContext (hWnd, hImc);
3032 fLastX = x;
3033 fLastY = y;
3034 }
3035 }
3036#else
3037 HANDLE hime = ::GlobalAlloc (GMEM_MOVEABLE | GMEM_LOWER | GMEM_DDESHARE, (DWORD)sizeof (IMESTRUCT));
3038 LPIMESTRUCT lpime = nullptr;
3039 if (hime != nullptr) {
3040 lpime = (LPIMESTRUCT)GlobalLock (hime);
3041 }
3042 else {
3043 return;
3044 }
3045
3046 if (lpime == nullptr) {
3047 return;
3048 }
3049
3050 lpime->fnc = IME_SETCONVERSIONWINDOW; // called IME_MOVECONVERTWINDOW in Win3.1
3051 lpime->wParam = MCW_WINDOW;
3052 lpime->wCount = 0;
3053 lpime->dchSource = 0;
3054 lpime->dchDest = 0;
3055 lpime->lParam1 = MAKELONG (x, y);
3056 lpime->lParam2 = 0L;
3057 lpime->lParam3 = 0L;
3058
3059 // SendIMEMessageProc returns 0 if there is an error, in which case
3060 // the error code is returned in IMESTRUCT.wParam;
3061 short ret = fSendIMEMessageProc (hWnd, (LONG)hime);
3062 ret = ret ? 0 : (short)lpime->wParam;
3063
3064 ::GlobalUnlock (hime);
3065 ::GlobalFree (hime);
3066 if (!ret) {
3067 fLastX = x;
3068 fLastY = y;
3069 }
3070#endif
3071
3072 // Should I redo this taking the LOGFONT as an arg to IME::UpdatePosition ()??? LGP 980714
3073 if (fImmGetContext != nullptr and fImmSetCompositionFont != nullptr and fImmReleaseContext != nullptr) {
3074 HFONT hFont = nullptr;
3075 if ((hFont = (HFONT)::SendMessage (hWnd, WM_GETFONT, 0, 0L)) != nullptr) {
3076 LOGFONT lFont;
3077 if (::GetObject (hFont, sizeof (LOGFONT), &lFont)) {
3078 HIMC hImc = NULL;
3079 if ((hImc = fImmGetContext (hWnd)) != NULL) {
3080 fImmSetCompositionFont (hImc, &lFont);
3081 fImmReleaseContext (hWnd, hImc);
3082 }
3083 }
3084 }
3085 }
3086 }
3087}
3088
3089wstring IME::GetCompositionResultStringW (HWND hWnd)
3090{
3091 wstring result;
3092 if (fImmGetCompositionStringW != nullptr and fImmGetContext != nullptr and fImmReleaseContext != nullptr) {
3093 HIMC hImc = 0;
3094 if ((hImc = fImmGetContext (hWnd)) != 0) {
3095 wchar_t curIMEString[2048];
3096 LONG nChars = fImmGetCompositionStringW (hImc, GCS_RESULTSTR, curIMEString, static_cast<DWORD> (Memory::NEltsOf (curIMEString)));
3097
3098 nChars /= sizeof (wchar_t); // why???? LGP 991214
3099 if (nChars >= 0 and static_cast<size_t> (nChars) < Memory::NEltsOf (curIMEString)) {
3100 curIMEString[nChars] = '\0';
3101 }
3102 else {
3103 curIMEString[0] = '\0';
3104 }
3105 result = curIMEString;
3106 fImmReleaseContext (hWnd, hImc);
3107 }
3108 }
3109 return result;
3110}
3111#endif
3112
3113Led_Rect Led::CenterRectInRect (const Led_Rect& r, const Led_Rect& centerIn)
3114{
3115 CoordinateType xLeft = (centerIn.left + centerIn.right) / 2 - r.GetWidth () / 2;
3116 CoordinateType yTop = (centerIn.top + centerIn.bottom) / 2 - r.GetHeight () / 2;
3117 return Led_Rect (yTop, xLeft, r.GetHeight (), r.GetWidth ());
3118}
3119
3120#if qStroika_Foundation_Common_Platform_Windows
3121void Led::Led_CenterWindowInParent (HWND w)
3122{
3123 Assert (::IsWindow (w));
3124 HWND hWndCenter = ::GetWindow (w, GW_OWNER);
3125 if (hWndCenter == nullptr) {
3126 hWndCenter = ::GetDesktopWindow ();
3127 }
3128 Assert (::IsWindow (hWndCenter));
3129
3130 // get coordinates of the window relative to its parent
3131 RECT rcDlg;
3132 ::GetWindowRect (w, &rcDlg);
3133 RECT rcCenter;
3134 ::GetWindowRect (hWndCenter, &rcCenter);
3135
3136 // find dialog's upper left based on rcCenter
3137 int xLeft = (rcCenter.left + rcCenter.right) / 2 - AsLedRect (rcDlg).GetWidth () / 2;
3138 int yTop = (rcCenter.top + rcCenter.bottom) / 2 - AsLedRect (rcDlg).GetHeight () / 2;
3139
3140 // map screen coordinates to child coordinates
3141 ::SetWindowPos (w, nullptr, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
3142}
3143#endif
3144
3145////////////////////////////// PRIVATE UTILITIES
3146
3147#if qStroika_Foundation_Common_Platform_Windows && qUseFakeTTGetWPlacementToImage
3148
3149///////////////////////////////////////////////////////////////////////////////////
3150////////////// CODE FROM Microsoft Knowledge Base Article - 241020 ///////////////
3151///////////////////////////////////////////////////////////////////////////////////
3152///////// http://support.microsoft.com/default.aspx?scid=kb;en-us;241020 //////////
3153///////////////////////////////////////////////////////////////////////////////////
3154
3155#pragma pack(1) // for byte alignment
3156// We need byte alignment to be structure compatible with the
3157// contents of a TrueType font file
3158
3159// Macros to swap from Big Endian to Little Endian
3160#define SWAPWORD(x) MAKEWORD (HIBYTE (x), LOBYTE (x))
3161#define SWAPLONG(x) MAKELONG (SWAPWORD (HIWORD (x)), SWAPWORD (LOWORD (x)))
3162
3163struct CMAP4 { // From the TrueType Spec. revision 1.66
3164 USHORT format; // Format number is set to 4.
3165 USHORT length; // Length in bytes.
3166 USHORT version; // Version number (starts at 0).
3167 USHORT segCountX2; // 2 x segCount.
3168 USHORT searchRange; // 2 x (2**floor(log2(segCount)))
3169 USHORT entrySelector; // log2(searchRange/2)
3170 USHORT rangeShift; // 2 x segCount - searchRange
3171
3172 USHORT Arrays[1]; // Placeholder symbol for address of arrays following
3173};
3174using LPCMAP4 = CMAP4*;
3175
3176/* CMAP table Data
3177 From the TrueType Spec revision 1.66
3178
3179 USHORT Table Version #
3180 USHORT Number of encoding tables
3181*/
3182#define CMAPHEADERSIZE (sizeof (USHORT) * 2)
3183
3184/* ENCODING entry Data aka CMAPENCODING
3185 From the TrueType Spec revision 1.66
3186
3187 USHORT Platform Id
3188 USHORT Platform Specific Encoding Id
3189 ULONG Byte Offset from beginning of table
3190*/
3191#define ENCODINGSIZE (sizeof (USHORT) * 2 + sizeof (ULONG))
3192
3193struct CMAPENCODING {
3194 USHORT PlatformId;
3195 USHORT EncodingId;
3196 ULONG Offset;
3197};
3198
3199// Macro to pack a TrueType table name into a DWORD
3200#define MAKETABLENAME(ch1, ch2, ch3, ch4) ((((DWORD)(ch4)) << 24) | (((DWORD)(ch3)) << 16) | (((DWORD)(ch2)) << 8) | ((DWORD)(ch1)))
3201
3202/* public functions */
3203static USHORT GetTTUnicodeGlyphIndex (HDC hdc, USHORT ch);
3204
3205// DWORD packed four letter table name for each GetFontData()
3206// function call when working with the CMAP TrueType table
3207static DWORD dwCmapName = MAKETABLENAME ('c', 'm', 'a', 'p');
3208
3209static USHORT* GetEndCountArray (LPBYTE pBuff)
3210{
3211 return (USHORT*)(pBuff + 7 * sizeof (USHORT)); // Per TT spec
3212}
3213
3214static USHORT* GetStartCountArray (LPBYTE pBuff)
3215{
3216 DWORD segCount = ((LPCMAP4)pBuff)->segCountX2 / 2;
3217 return (USHORT*)(pBuff + 8 * sizeof (USHORT) + // 7 header + 1 reserved USHORT
3218 segCount * sizeof (USHORT)); // Per TT spec
3219}
3220
3221static USHORT* GetIdDeltaArray (LPBYTE pBuff)
3222{
3223 DWORD segCount = ((LPCMAP4)pBuff)->segCountX2 / 2;
3224 return (USHORT*)(pBuff + 8 * sizeof (USHORT) + // 7 header + 1 reserved USHORT
3225 segCount * 2 * sizeof (USHORT)); // Per TT spec
3226}
3227
3228static USHORT* GetIdRangeOffsetArray (LPBYTE pBuff)
3229{
3230 DWORD segCount = ((LPCMAP4)pBuff)->segCountX2 / 2;
3231 return (USHORT*)(pBuff + 8 * sizeof (USHORT) + // 7 header + 1 reserved USHORT
3232 segCount * 3 * sizeof (USHORT)); // Per TT spec
3233}
3234
3235static void SwapArrays (LPCMAP4 pFormat4)
3236{
3237 DWORD segCount = pFormat4->segCountX2 / 2; // Per TT Spec
3238 DWORD i;
3239 USHORT *pGlyphId, *pEndOfBuffer, *pstartCount = GetStartCountArray ((LPBYTE)pFormat4), *pidDelta = GetIdDeltaArray ((LPBYTE)pFormat4),
3240 *pidRangeOffset = GetIdRangeOffsetArray ((LPBYTE)pFormat4), *pendCount = GetEndCountArray ((LPBYTE)pFormat4);
3241
3242 // Swap the array elements for Intel.
3243 for (i = 0; i < segCount; ++i) {
3244 pendCount[i] = SWAPWORD (pendCount[i]);
3245 pstartCount[i] = SWAPWORD (pstartCount[i]);
3246 pidDelta[i] = SWAPWORD (pidDelta[i]);
3247 pidRangeOffset[i] = SWAPWORD (pidRangeOffset[i]);
3248 }
3249
3250 // Swap the Glyph Id array
3251 pGlyphId = pidRangeOffset + segCount; // Per TT spec
3252 pEndOfBuffer = (USHORT*)((LPBYTE)pFormat4 + pFormat4->length);
3253 for (; pGlyphId < pEndOfBuffer; ++pGlyphId) {
3254 *pGlyphId = SWAPWORD (*pGlyphId);
3255 }
3256} /* end of function SwapArrays */
3257
3258static BOOL GetFontEncoding (HDC hdc, CMAPENCODING* pEncoding, int iEncoding)
3259/*
3260 Note for this function to work correctly, structures must
3261 have byte alignment.
3262*/
3263{
3264 DWORD dwResult;
3265 BOOL fSuccess = TRUE;
3266
3267 // Get the structure data from the TrueType font
3268 dwResult = GetFontData (hdc, dwCmapName, CMAPHEADERSIZE + ENCODINGSIZE * iEncoding, pEncoding, sizeof (CMAPENCODING));
3269 fSuccess = (dwResult == sizeof (CMAPENCODING));
3270
3271 // swap the Platform Id for Intel
3272 pEncoding->PlatformId = SWAPWORD (pEncoding->PlatformId);
3273
3274 // swap the Specific Id for Intel
3275 pEncoding->EncodingId = SWAPWORD (pEncoding->EncodingId);
3276
3277 // swap the subtable offset for Intel
3278 pEncoding->Offset = SWAPLONG (pEncoding->Offset);
3279
3280 return fSuccess;
3281
3282} /* end of function GetFontEncoding */
3283
3284static BOOL GetFontFormat4Header (HDC hdc, LPCMAP4 pFormat4, DWORD dwOffset)
3285/*
3286 Note for this function to work correctly, structures must
3287 have byte alignment.
3288*/
3289{
3290 BOOL fSuccess = TRUE;
3291 DWORD dwResult;
3292 int i;
3293 USHORT* pField;
3294
3295 // Loop and Alias a writeable pointer to the field of interest
3296 pField = (USHORT*)pFormat4;
3297
3298 for (i = 0; i < 7; ++i) {
3299 // Get the field from the subtable
3300 dwResult = GetFontData (hdc, dwCmapName, dwOffset + sizeof (USHORT) * i, pField, sizeof (USHORT));
3301
3302 // swap it to make it right for Intel.
3303 *pField = SWAPWORD (*pField);
3304 // move on to the next
3305 ++pField;
3306 // accumulate our success
3307 fSuccess = (dwResult == sizeof (USHORT)) && fSuccess;
3308 }
3309
3310 return fSuccess;
3311
3312} /* end of function GetFontFormat4Header */
3313
3314static BOOL GetFontFormat4Subtable (HDC hdc, // DC with TrueType font
3315 LPCMAP4 pFormat4Subtable, // destination buffer
3316 DWORD dwOffset // Offset within font
3317)
3318{
3319 DWORD dwResult;
3320 USHORT length;
3321
3322 // Retrieve the header values in swapped order
3323 if (!GetFontFormat4Header (hdc, pFormat4Subtable, dwOffset)) {
3324 return FALSE;
3325 }
3326
3327 // Get the rest of the table
3328 length = pFormat4Subtable->length - (7 * sizeof (USHORT));
3329 dwResult = GetFontData (hdc, dwCmapName,
3330 dwOffset + 7 * sizeof (USHORT), // pos of arrays
3331 (LPBYTE)pFormat4Subtable->Arrays, // destination
3332 length);
3333
3334 if (dwResult != length) {
3335 // We really shouldn't ever get here
3336 return FALSE;
3337 }
3338
3339 // Swamp the arrays
3340 SwapArrays (pFormat4Subtable);
3341
3342 return TRUE;
3343}
3344
3345static BOOL GetTTUnicodeCoverage (HDC hdc, // DC with TT font
3346 LPCMAP4 pBuffer, // Properly allocated buffer
3347 DWORD cbSize, // Size of properly allocated buffer
3348 DWORD* pcbNeeded // size of buffer needed
3349)
3350/*
3351 if cbSize is to small or zero, or if pBuffer is nullptr the function
3352 will fail and return the required buffer size in *pcbNeeded.
3353
3354 if another error occurs, the function will fail and *pcbNeeded will
3355 be zero.
3356
3357 When the function succeeds, *pcbNeeded contains the number of bytes
3358 copied to pBuffer.
3359*/
3360{
3361 USHORT nEncodings; // # of encoding in the TT font
3362 CMAPENCODING Encoding{}; // The current encoding
3363 DWORD dwResult;
3364 DWORD i,
3365 iUnicode; // The Unicode encoding
3366 CMAP4 Format4; // Unicode subtable format
3367 LPCMAP4 pFormat4Subtable; // Working buffer for subtable
3368
3369 // Get the number of subtables in the CMAP table from the CMAP header
3370 // The # of subtables is the second USHORT in the CMAP table, per the TT Spec.
3371 dwResult = GetFontData (hdc, dwCmapName, sizeof (USHORT), &nEncodings, sizeof (USHORT));
3372 nEncodings = SWAPWORD (nEncodings);
3373
3374 if (dwResult != sizeof (USHORT)) {
3375 // Something is wrong, we probably got GDI_ERROR back
3376 // Probably this means that the Device Context does not have
3377 // a TrueType font selected into it.
3378 return FALSE;
3379 }
3380
3381 // Get the encodings and look for a Unicode Encoding
3382 iUnicode = nEncodings;
3383 for (i = 0; i < nEncodings; ++i) {
3384 // Get the encoding entry for each encoding
3385 if (!GetFontEncoding (hdc, &Encoding, i)) {
3386 *pcbNeeded = 0;
3387 return FALSE;
3388 }
3389
3390 // Take note of the Unicode encoding.
3391 //
3392 // A Unicode encoding per the TrueType specification has a
3393 // Platform Id of 3 and a Platform specific encoding id of 1
3394 // Note that Symbol fonts are supposed to have a Platform Id of 3
3395 // and a specific id of 0. If the TrueType spec. suggestions were
3396 // followed then the Symbol font's Format 4 encoding could also
3397 // be considered Unicode because the mapping would be in the
3398 // Private Use Area of Unicode. We assume this here and allow
3399 // Symbol fonts to be interpreted. If they do not contain a
3400 // Format 4, we bail later. If they do not have a Unicode
3401 // character mapping, we'll get wrong results.
3402 // Code could infer from the coverage whether 3-0 fonts are
3403 // Unicode or not by examining the segments for placement within
3404 // the Private Use Area Subrange.
3405 if (Encoding.PlatformId == 3 && (Encoding.EncodingId == 1 || Encoding.EncodingId == 0)) {
3406 iUnicode = i; // Set the index to the Unicode encoding
3407 }
3408 }
3409
3410 // index out of range means failure to find a Unicode mapping
3411 if (iUnicode >= nEncodings) {
3412 // No Unicode encoding found.
3413 *pcbNeeded = 0;
3414 return FALSE;
3415 }
3416
3417 // Get the header entries(first 7 USHORTs) for the Unicode encoding.
3418 if (!GetFontFormat4Header (hdc, &Format4, Encoding.Offset)) {
3419 *pcbNeeded = 0;
3420 return FALSE;
3421 }
3422
3423 // Check to see if we retrieved a Format 4 table
3424 if (Format4.format != 4) {
3425 // Bad, subtable is not format 4, bail.
3426 // This could happen if the font is corrupt
3427 // It could also happen if there is a new font format we
3428 // don't understand.
3429 *pcbNeeded = 0;
3430 return FALSE;
3431 }
3432
3433 // Figure buffer size and tell caller if buffer to small
3434 *pcbNeeded = Format4.length;
3435 if (*pcbNeeded > cbSize || pBuffer == nullptr) {
3436 // Either test indicates caller needs to know
3437 // the buffer size and the parameters are not setup
3438 // to continue.
3439 return FALSE;
3440 }
3441
3442 // allocate a full working buffer
3443 pFormat4Subtable = (LPCMAP4)malloc (Format4.length);
3444 if (pFormat4Subtable == nullptr) {
3445 // Bad things happening if we can't allocate memory
3446 *pcbNeeded = 0;
3447 return FALSE;
3448 }
3449
3450 // get the entire subtable
3451 if (!GetFontFormat4Subtable (hdc, pFormat4Subtable, Encoding.Offset)) {
3452 // Bad things happening if we can't allocate memory
3453 *pcbNeeded = 0;
3454 return FALSE;
3455 }
3456
3457 // Copy the retrieved table into the buffer
3458 CopyMemory (pBuffer, pFormat4Subtable, pFormat4Subtable->length);
3459
3460 free (pFormat4Subtable);
3461 return TRUE;
3462} /* end of function GetTTUnicodeCoverage */
3463
3464static BOOL FindFormat4Segment (LPCMAP4 pTable, // a valid Format4 subtable buffer
3465 USHORT ch, // Unicode character to search for
3466 USHORT* piSeg // out: index of segment containing ch
3467)
3468/*
3469 if the Unicode character ch is not contained in one of the
3470 segments the function returns FALSE.
3471
3472 if the Unicode character ch is found in a segment, the index
3473 of the segment is placed in*piSeg and the function returns
3474 TRUE.
3475*/
3476{
3477 USHORT i, segCount = pTable->segCountX2 / 2;
3478 USHORT* pendCount = GetEndCountArray ((LPBYTE)pTable);
3479 USHORT* pstartCount = GetStartCountArray ((LPBYTE)pTable);
3480
3481 // Find segment that could contain the Unicode character code
3482 for (i = 0; i < segCount && pendCount[i] < ch; ++i)
3483 ;
3484
3485 // We looked in them all, ch not there
3486 if (i >= segCount)
3487 return FALSE;
3488
3489 // character code not within the range of the segment
3490 if (pstartCount[i] > ch)
3491 return FALSE;
3492
3493 // this segment contains the character code
3494 *piSeg = i;
3495 return TRUE;
3496} /* end of function FindFormat4Segment */
3497
3498static USHORT GetTTUnicodeGlyphIndex (HDC hdc, // DC with a TrueType font selected
3499 USHORT ch // Unicode character to convert to Index
3500)
3501/*
3502 When the TrueType font contains a glyph for ch, the
3503 function returns the glyph index for that character.
3504
3505 If an error occurs, or there is no glyph for ch, the
3506 function will return the missing glyph index of zero.
3507*/
3508{
3509 LPCMAP4 pUnicodeCMapTable;
3510 DWORD dwSize;
3511 USHORT iSegment;
3512 USHORT* idRangeOffset;
3513 USHORT* idDelta;
3514 USHORT* startCount;
3515 USHORT GlyphIndex = 0; // Initialize to missing glyph
3516
3517 // How big a buffer do we need for Unicode CMAP?
3518 GetTTUnicodeCoverage (hdc, nullptr, 0, &dwSize);
3519 pUnicodeCMapTable = (LPCMAP4)malloc (dwSize);
3520 if (!GetTTUnicodeCoverage (hdc, pUnicodeCMapTable, dwSize, &dwSize)) {
3521 // Either no Unicode cmap, or some other error occurred
3522 // like font in DC is not TT.
3523 free (pUnicodeCMapTable);
3524 return 0; // return missing glyph on error
3525 }
3526
3527 // Find the cmap segment that has the character code.
3528 if (!FindFormat4Segment (pUnicodeCMapTable, ch, &iSegment)) {
3529 free (pUnicodeCMapTable);
3530 return 0; // ch not in cmap, return missing glyph
3531 }
3532
3533 // Get pointers to the cmap data
3534 idRangeOffset = GetIdRangeOffsetArray ((LPBYTE)pUnicodeCMapTable);
3535 idDelta = GetIdDeltaArray ((LPBYTE)pUnicodeCMapTable);
3536 startCount = GetStartCountArray ((LPBYTE)pUnicodeCMapTable);
3537
3538 // Per TT spec, if the RangeOffset is zero,
3539 if (idRangeOffset[iSegment] == 0) {
3540 // calculate the glyph index directly
3541 GlyphIndex = (idDelta[iSegment] + ch) % 65536;
3542 }
3543 else {
3544 // otherwise, use the glyph id array to get the index
3545 USHORT idResult; //Intermediate id calc.
3546
3547 idResult = *(idRangeOffset[iSegment] / 2 + (ch - startCount[iSegment]) + &idRangeOffset[iSegment]); // indexing equation from TT spec
3548 if (idResult)
3549 // Per TT spec, nonzero means there is a glyph
3550 GlyphIndex = (idDelta[iSegment] + idResult) % 65536;
3551 else
3552 // otherwise, return the missing glyph
3553 GlyphIndex = 0;
3554 }
3555
3556 free (pUnicodeCMapTable);
3557 return GlyphIndex;
3558} /* end of function GetTTUnicodeGlyphIndex */
3559
3560static bool Win9x_Workaround_GetCharPlacementFunction (HDC hdc, const wchar_t* srcText, size_t len, wchar_t* glyphImagesOut)
3561{
3562 // Should check if really using a true-type font and as the doc "Microsoft Knowledge Base Article - 241020" says:
3563 // This sample code was written for clarity of explanation. It is not optimized for repeated use because
3564 // it allocates and retrieves TrueType tables each time a public function is called. For real applications,
3565 // a good optimization would be to cache the Unicode encoding for the TrueType font file as long as it remained
3566 // in the DC. An application can compare to see whether the font selected into a DC is the same TrueType font file
3567 // by caching and comparing the checksum value of the font file. This checksum is located in the Table Directory
3568 // of the TrueType font file at the beginning of the file and can be retrieved by using the GetFontData function.
3569 // See the TrueType specification's discussion of "The Table Directory" under the Data Types chapter to locate
3570 // the checksum of a font file.
3571 for (size_t i = 0; i < len; ++i) {
3572 glyphImagesOut[i] = GetTTUnicodeGlyphIndex (hdc, srcText[i]);
3573 }
3574 return true;
3575}
3576#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define RequireNotNull(p)
Definition Assertions.h:347
#define Verify(c)
Definition Assertions.h:419
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1049
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
conditional_t< qTargetPlatformSDKUseswchar_t, wchar_t, char > SDKChar
Definition SDKChar.h:71
basic_string< SDKChar > SDKString
Definition SDKString.h:38
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...