support full-width/composite characters and true color palette in terminal (#1386)
[far2l.git] / WinPort / src / Backend / WX / Paint.cpp
blobd6f47725ce6590c4582a5f5583ca424c7eae7223
1 #include "Backend.h"
2 #include "wxWinTranslations.h"
3 #include <wx/fontdlg.h>
4 #include <wx/fontenum.h>
5 #include <wx/textfile.h>
6 #include <wx/graphics.h>
7 #include "Paint.h"
8 #include "PathHelpers.h"
9 #include "WinPort.h"
10 #include <utils.h>
12 #define COLOR_ATTRIBUTES ( FOREGROUND_INTENSITY | BACKGROUND_INTENSITY | \
13 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | \
14 BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE )
16 #define DYNAMIC_FONTS
18 #ifdef __APPLE__
19 # define DEFAULT_FONT_SIZE 20
20 #else
21 # define DEFAULT_FONT_SIZE 16
22 #endif
24 /////////////////////////////////////////////////////////////////////////////////
25 static const char *g_known_good_fonts[] = { "Ubuntu", "Terminus", "DejaVu",
26 "Liberation", "Droid", "Monospace", "PT Mono", "Menlo",
27 nullptr};
30 class FixedFontLookup : wxFontEnumerator
32 wxString _any, _known_good;
33 virtual bool OnFacename(const wxString &face_name)
35 _any = face_name;
36 for (const char **p = g_known_good_fonts; *p; ++p) {
37 if (face_name.find(*p)!=wxString::npos) {
38 _known_good = face_name;
42 /* unfortunatelly following code gives nothing interesting
43 wxFont f(wxFontInfo(DEFAULT_FONT_SIZE).Underlined().FaceName(face_name));
44 if (f.IsOk()) {
45 fprintf(stderr, "FONT family %u encoding %u face_name='%ls' \n",
46 (unsigned int)f.GetFamily(), (unsigned int)f.GetEncoding(), static_cast<const wchar_t*>(face_name.wc_str()));
47 } else {
48 fprintf(stderr, "BAD FONT: face_name='%ls'\n", static_cast<const wchar_t*>(face_name.wc_str()));
49 } */
50 return true;
52 public:
54 wxString Query()
56 _any.Empty();
57 _known_good.Empty();
58 EnumerateFacenames(wxFONTENCODING_SYSTEM, true);
59 fprintf(stderr, "FixedFontLookup: _any='%ls' _known_good='%ls'\n",
60 static_cast<const wchar_t*>(_any.wc_str()),
61 static_cast<const wchar_t*>(_known_good.wc_str()));
62 return _known_good.IsEmpty() ? _any : _known_good;
66 static bool LoadFontFromSettings(wxFont& font)
68 const std::string &path = InMyConfig("font");
69 wxTextFile file(path);
70 if (file.Exists() && file.Open()) {
71 for (wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine()) {
72 font.SetNativeFontInfo(str);
73 if (font.IsOk()) {
74 printf("LoadFontFromSettings: used %ls\n",
75 static_cast<const wchar_t*>(str.wc_str()));
76 return true;
81 return false;
84 static bool ChooseFontAndSaveToSettings(wxWindow *parent, wxFont& font)
86 font = wxGetFontFromUser(parent, font);
87 if (font.IsOk()) {
88 const std::string &path = InMyConfig("font");
89 unlink(path.c_str());
90 wxTextFile file;
91 file.Create(path);
93 file.InsertLine(font.GetNativeFontInfoDesc(), 0);
94 file.Write();
95 return true;
98 return false;
101 static void InitializeFont(wxWindow *parent, wxFont& font)
103 if (LoadFontFromSettings(font))
104 return;
107 for (;;) {
108 FixedFontLookup ffl;
109 wxString fixed_font = ffl.Query();
110 if (!fixed_font.empty()) {
111 font = wxFont(DEFAULT_FONT_SIZE, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, fixed_font);
113 if (fixed_font.empty() || !font.IsOk())
114 font = wxFont(wxSystemSettings::GetFont(wxSYS_ANSI_FIXED_FONT));
115 #if defined(__WXOSX__) && !wxCHECK_VERSION(3, 1, 0)
116 return;//older (not sure what exactly version) wxwidgets crashes in wxGetFontFromUser under OSX, this allows at least to start
117 #else
118 if (ChooseFontAndSaveToSettings(parent, font))
119 return;
120 #endif
124 ConsolePaintContext::ConsolePaintContext(wxWindow *window) :
125 _window(window), _font_width(12), _font_height(16), _font_descent(0), _font_thickness(2),
126 _buffered_paint(false), _sharp(false)
128 _char_fit_cache.checked.resize(0xffff);
129 _char_fit_cache.result.resize(0xffff);
131 _window->SetBackgroundColour(*wxBLACK);
132 wxFont font;
133 InitializeFont(_window, font);
134 SetFont(font);
139 class FontSizeInspector
141 wxBitmap _bitmap;
142 wxMemoryDC _dc;
144 int _max_width, _prev_width;
145 int _max_height, _prev_height;
146 int _max_descent;
147 bool _unstable_size, _fractional_size;
149 void InspectChar(const wchar_t c)
151 wchar_t wz[2] = { c, 0};
152 wxCoord width = 0, height = 0, descent = 0;
153 _dc.GetTextExtent(wz, &width, &height, &descent);
155 if (_max_width < width) _max_width = width;
156 if (_max_height < height) _max_height = height;
157 if (_max_descent < descent) _max_descent = descent;
159 if ( _prev_width != width ) {
160 if (_prev_width!=-1)
161 _unstable_size = true;
162 _prev_width = width;
164 if ( _prev_height != height ) {
165 if (_prev_height!=-1) _unstable_size = true;
166 _prev_height = height;
170 void DetectFractionalSize(const wchar_t *chars)
172 // If font is non-monospaced there is no sense to detect if widths are fractional
173 if (_unstable_size) return;
174 _fractional_size = _dc.GetTextExtent(chars).GetWidth() != (int)(_max_width * wcslen(chars));
177 public:
178 FontSizeInspector(wxFont& font)
179 : _bitmap(48, 48, wxBITMAP_SCREEN_DEPTH),
180 _max_width(4), _prev_width(-1),
181 _max_height(6), _prev_height(-1),
182 _unstable_size(false), _fractional_size(false)
184 _dc.SelectObject(_bitmap);
185 _dc.SetFont(font);
188 void InspectChars(const wchar_t *chars)
190 for(const wchar_t *s = chars; *s; ++s)
191 InspectChar(*s);
192 #if defined(__WXOSX__)
193 // There are font rendering artifacts on MacOS if buffering is enabled and font size differs from 10, 15, 20;
194 // E.g. if font size = 13, one char in a string has width 9px (GetTextExtent returns 9), but total string width
195 // is less than N*9px, because internally one char could have fractional width.
196 // We need to disable buffering for certain font sizes as done for non-monospaced ("unstable size") fonts.
197 DetectFractionalSize(chars);
198 #endif
201 bool IsUnstableSize() const { return _unstable_size; }
202 bool IsFractionalSize() const { return _fractional_size; }
203 int GetMaxWidth() const { return _max_width; }
204 int GetMaxHeight() const { return _max_height; }
205 int GetMaxDescent() const { return _max_descent; }
210 void ConsolePaintContext::SetFont(wxFont font)
212 FontSizeInspector fsi(font);
213 fsi.InspectChars(L" 1234567890-=!@#$%^&*()_+qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?");
214 //fsi.InspectChars(L"QWERTYUIOPASDFGHJKL");
216 bool is_unstable = fsi.IsUnstableSize();
217 bool is_fractional = fsi.IsFractionalSize();
218 _font_width = fsi.GetMaxWidth();
219 _font_height = fsi.GetMaxHeight();
220 _font_descent = fsi.GetMaxDescent();
221 //font_height+= _font_height/4;
223 _font_thickness = (_font_width > 8) ? _font_width / 8 : 1;
224 switch (font.GetWeight()) {
225 case wxFONTWEIGHT_LIGHT:
226 if (_font_thickness > 1) {
227 --_font_thickness;
229 break;
231 case wxFONTWEIGHT_BOLD:
232 ++_font_thickness;
233 break;
235 case wxFONTWEIGHT_NORMAL:
236 default:
240 fprintf(stderr, "Font %u x %u . %u: '%ls' - %s\n", _font_width, _font_height, _font_thickness,
241 static_cast<const wchar_t*>(font.GetFaceName().wc_str()),
242 font.IsFixedWidth() ?
243 (is_unstable ?
244 "monospaced unstable" :
245 (is_fractional ?
246 "monospaced stable (fractional)" :
247 "monospaced stable (integer)")) :
248 "not monospaced");
250 struct stat s{};
252 _custom_draw_enabled = stat(InMyConfig("nocustomdraw").c_str(), &s) != 0;
253 _buffered_paint = false;
255 if (font.IsFixedWidth() && !is_unstable && !is_fractional) {
256 if (stat(InMyConfig("nobuffering").c_str(), &s) != 0)
257 _buffered_paint = true;
260 _fonts.clear();
261 _fonts.push_back(font);
264 void ConsolePaintContext::ShowFontDialog()
266 wxFont font;
267 if (!_fonts.empty()) {
268 font = _fonts.front();
269 if (!ChooseFontAndSaveToSettings(_window, font))
270 return;
272 } else
273 InitializeFont(_window, font);
275 SetFont(font);
278 uint8_t ConsolePaintContext::CharFitTest(wxPaintDC &dc, const wchar_t *wcz)
280 #ifdef DYNAMIC_FONTS
281 const bool cacheable = (size_t(wcz[0]) <= _char_fit_cache.checked.size() && wcz[1] == 0);
282 if (cacheable && _char_fit_cache.checked[ size_t(wcz[0]) - 1 ]) {
283 return _char_fit_cache.result[ size_t(wcz[0]) - 1 ];
286 uint8_t font_index = 0;
287 _cft_tmp = wcz;
288 wxCoord w, h = _font_height, d = _font_descent;
289 dc.GetTextExtent(_cft_tmp, &w, &h, &d);
290 for (uint8_t try_index = 1; try_index != 0xff && (unsigned)h > _font_height + std::max(0, int(d) - int(_font_descent)); ++try_index) {
292 if (try_index >= _fonts.size()) {
293 wxFont smallest = _fonts.back();
294 wxSize px_size = smallest.GetPixelSize();
295 if (px_size.GetHeight() <= 4) {
296 break;
298 px_size.SetHeight(px_size.GetHeight() - 1);
299 px_size.SetWidth(0);
300 smallest.SetPixelSize(px_size);
301 _fonts.emplace_back(smallest);
303 assert(try_index < _fonts.size());
304 dc.SetFont(_fonts[try_index]);
305 dc.GetTextExtent(_cft_tmp, &w, &h, &d);
306 font_index = try_index;
308 if (font_index != 0) {
309 // fprintf(stderr, "Changed[%d] point size = %u -> %u for '%ls'\n",
310 // font_index, _fonts[0].GetPointSize(), _fonts[font_index].GetPointSize(), wcz);
311 ApplyFont(dc);
314 if (cacheable) {
315 _char_fit_cache.result[ size_t(wcz[0]) - 1 ] = font_index;
316 _char_fit_cache.checked[ size_t(wcz[0]) - 1 ] = true;
319 return font_index;
321 #else
322 return 0;
324 #endif
327 void ConsolePaintContext::ApplyFont(wxPaintDC &dc, uint8_t index)
329 if (index < _fonts.size())
330 dc.SetFont(_fonts[index]);
333 void ConsolePaintContext::OnPaint(SMALL_RECT *qedit)
335 wxPaintDC dc(_window);
336 #if wxUSE_GRAPHICS_CONTEXT
337 wxGraphicsContext* gctx = dc.GetGraphicsContext();
338 if (gctx) {
339 if (_sharp) {
340 gctx->SetInterpolationQuality(wxINTERPOLATION_FAST);
341 gctx->SetAntialiasMode(wxANTIALIAS_NONE);
342 } else {
343 gctx->SetInterpolationQuality(wxINTERPOLATION_DEFAULT);
344 gctx->SetAntialiasMode(wxANTIALIAS_DEFAULT);
347 #endif
348 unsigned int cw, ch; g_winport_con_out->GetSize(cw, ch);
349 if (cw > MAXSHORT) cw = MAXSHORT;
350 if (ch > MAXSHORT) ch = MAXSHORT;
352 wxRegion rgn = _window->GetUpdateRegion();
353 wxRect box = rgn.GetBox();
354 SMALL_RECT area = {SHORT(box.GetLeft() / _font_width), SHORT(box.GetTop() / _font_height),
355 SHORT(box.GetRight() / _font_width), SHORT(box.GetBottom() / _font_height)};
357 if (area.Left < 0 ) area.Left = 0;
358 if (area.Top < 0 ) area.Top = 0;
359 if ((unsigned)area.Right >= cw) area.Right = cw - 1;
360 if ((unsigned)area.Bottom >= ch) area.Bottom = ch - 1;
361 if (area.Right < area.Left || area.Bottom < area.Top) return;
363 wxString tmp;
364 _line.resize(cw);
365 ApplyFont(dc);
367 _cursor_props.Update();
369 ConsolePainter painter(this, dc, _buffer, _cursor_props);
370 for (unsigned int cy = (unsigned)area.Top; cy <= (unsigned)area.Bottom; ++cy) {
371 wxRegionContain lc = rgn.Contains(0, cy * _font_height, cw * _font_width, _font_height);
373 if (lc == wxOutRegion) {
374 continue;
377 const CHAR_INFO *line;
379 // dont keep console output locked for a long time to avoid output slowdown
380 IConsoleOutput::DirectLineAccess dla(g_winport_con_out, cy);
381 line = dla.Line();
382 unsigned int cur_cw = line ? dla.Width() : 0;
383 if (cur_cw < cw) {
384 memcpy(&_line[0], line, cur_cw * sizeof(*line));
385 memset(&_line[cur_cw], 0, (cw - cur_cw) * sizeof(*line));
386 } else {
387 memcpy(&_line[0], line, cw * sizeof(*line));
389 line = &_line[0];
392 painter.LineBegin(cy);
393 wchar_t tmp_wcz[2] = {0, 0};
394 DWORD64 attributes = line->Attributes;
395 const unsigned int cx_begin = (area.Left > 0 && !line[area.Left].Char.UnicodeChar) ? area.Left - 1 : area.Left;
396 const unsigned int cx_end = std::min(cw, (unsigned)area.Right + 1);
397 for (unsigned int cx = cx_begin; cx < cx_end; ++cx) {
398 if (!line[cx].Char.UnicodeChar) {
399 painter.LineFlush(cx + 1);
400 continue;
402 const wchar_t *pwcz;
403 if (UNLIKELY(CI_USING_COMPOSITE_CHAR(line[cx]))) {
404 pwcz = WINPORT(CompositeCharLookup)(line[cx].Char.UnicodeChar);
405 } else {
406 tmp_wcz[0] = line[cx].Char.UnicodeChar ? wchar_t(line[cx].Char.UnicodeChar) : L' ';
407 pwcz = tmp_wcz;
410 attributes = line[cx].Attributes;
411 if (qedit && cx >= (unsigned)qedit->Left && cx <= (unsigned)qedit->Right
412 && cy >= (unsigned)qedit->Top && cy <= (unsigned)qedit->Bottom) {
413 attributes^= COLOR_ATTRIBUTES;
414 if (attributes & FOREGROUND_TRUECOLOR) {
415 attributes^= 0x000000ffffff0000;
417 if (attributes & BACKGROUND_TRUECOLOR) {
418 attributes^= 0xffffff0000000000;
421 const int nx = (cx + 1 < cw && !line[cx + 1].Char.UnicodeChar) ? 2 : 1;
422 painter.NextChar(cx, attributes, pwcz, nx);
424 painter.LineFlush(area.Right + 1);
429 void ConsolePaintContext::RefreshArea( const SMALL_RECT &area )
431 wxRect rc;
432 rc.SetLeft(((int)area.Left) * _font_width);
433 rc.SetRight(((int)area.Right) * _font_width + _font_width - 1);
434 rc.SetTop(((int)area.Top) * _font_height);
435 rc.SetBottom(((int)area.Bottom) * _font_height + _font_height - 1);
436 _window->Refresh(false, &rc);
440 void ConsolePaintContext::BlinkCursor()
442 if (_cursor_props.Blink()) {
443 SMALL_RECT area = {
444 _cursor_props.pos.X, _cursor_props.pos.Y,
445 _cursor_props.pos.X, _cursor_props.pos.Y
447 CHAR_INFO ci{};
448 if (g_winport_con_out->Read(ci, _cursor_props.pos)) {
449 if (!ci.Char.UnicodeChar && area.Left > 0) {
450 --area.Left;
451 } else if (CI_FULL_WIDTH_CHAR(ci)) {
452 ++area.Right;
455 RefreshArea(area);
459 void ConsolePaintContext::SetSharp(bool sharp)
461 if (_sharp != sharp) {
462 _sharp = sharp;
463 _window->Refresh();
467 bool ConsolePaintContext::IsSharpSupported()
469 #if wxUSE_GRAPHICS_CONTEXT
470 return true;
471 #else
472 return false;
473 #endif
476 wxBrush &ConsolePaintContext::GetBrush(const WinPortRGB &clr)
478 auto it = _color2brush.find(clr);
479 if (it != _color2brush.end()) {
480 return it->second;
483 return _color2brush.emplace(clr, wxColour(clr.r, clr.g, clr.b)).first->second;
486 /////////////////////////////
488 bool CursorProps::Blink()
490 bool prev_blink_state = blink_state;
491 blink_state = !blink_state;
492 Update();
493 return (blink_state != prev_blink_state);
496 void CursorProps::Update()
498 pos = g_winport_con_out->GetCursor(height, visible);
499 if (prev_pos.X != pos.X || prev_pos.Y != pos.Y) {
500 prev_pos = pos;
501 blink_state = true;
505 //////////////////////
507 ConsolePainter::ConsolePainter(ConsolePaintContext *context, wxPaintDC &dc, wxString &buffer, CursorProps &cursor_props) :
508 _context(context), _dc(dc), _buffer(buffer), _cursor_props(cursor_props),
509 _start_cx((unsigned int)-1), _start_back_cx((unsigned int)-1), _prev_fit_font_index(0), _prev_underlined(false)
511 _dc.SetPen(context->GetTransparentPen());
512 _dc.SetBackgroundMode(wxPENSTYLE_TRANSPARENT);
513 _buffer.Empty();
517 void ConsolePainter::SetFillColor(const WinPortRGB &clr)
519 if (_brush_clr.Change(clr)) {
520 wxBrush &brush = _context->GetBrush(clr);
521 _dc.SetBrush(brush);
522 _dc.SetBackground(brush);
526 void ConsolePainter::PrepareBackground(unsigned int cx, const WinPortRGB &clr, unsigned int nx)
528 const bool cursor_here = (_cursor_props.visible && _cursor_props.blink_state
529 && cx == (unsigned int)_cursor_props.pos.X
530 && _start_cy == (unsigned int)_cursor_props.pos.Y);
532 if (!cursor_here && _start_back_cx != (unsigned int)-1 && _clr_back == clr)
533 return;
535 FlushBackground(cx + nx - 1);
537 if (!cursor_here) {
538 _clr_back = clr;
539 _start_back_cx = cx;
540 return;
543 _start_back_cx = (unsigned int)-1;
545 const unsigned int x = cx * _context->FontWidth();
546 unsigned int h = (_context->FontHeight() * _cursor_props.height) / 100;
547 if (h==0) h = 1;
548 unsigned int fill_height = _context->FontHeight() - h;
549 if (fill_height > _context->FontHeight()) fill_height = _context->FontHeight();
550 WinPortRGB clr_xored(clr.r ^ 0xff, clr.g ^ 0xff, clr.b ^ 0xff);
551 SetFillColor(clr_xored);
552 _dc.DrawRectangle(x, _start_y + fill_height, _context->FontWidth() * nx, h);
554 if (fill_height) {
555 SetFillColor(clr);
556 _dc.DrawRectangle(x, _start_y, _context->FontWidth() * nx, fill_height);
561 void ConsolePainter::FlushBackground(unsigned int cx_end)
563 if (_start_back_cx != ((unsigned int)-1)) {
564 SetFillColor(_clr_back);
565 _dc.DrawRectangle(_start_back_cx * _context->FontWidth(), _start_y,
566 (cx_end - _start_back_cx) * _context->FontWidth(), _context->FontHeight());
567 _start_back_cx = ((unsigned int)-1);
571 void ConsolePainter::FlushText(unsigned int cx_end)
573 if (!_buffer.empty()) {
574 _dc.SetTextForeground(wxColour(_clr_text.r, _clr_text.g, _clr_text.b));
575 _dc.DrawText(_buffer, _start_cx * _context->FontWidth(), _start_y);
576 _buffer.Empty();
578 FlushUnderline(cx_end);
579 _start_cx = (unsigned int)-1;
580 _prev_fit_font_index = 0;
583 void ConsolePainter::FlushUnderline(unsigned int cx_end)
585 if (_prev_underlined) {
586 _dc.SetPen(wxColour(_clr_text.r, _clr_text.g, _clr_text.b));
587 _dc.DrawLine(_start_cx * _context->FontWidth(), _start_y + _context->FontHeight() - 1,
588 cx_end * _context->FontWidth(), _start_y + _context->FontHeight() - 1);
589 _dc.SetPen(_context->GetTransparentPen());
590 _prev_underlined = false;
594 static inline unsigned char CalcFadeColor(unsigned char bg, unsigned char fg)
596 unsigned short out = fg;
597 out*= 2;
598 out+= bg;
599 out/= 3;
600 return (out > 0xff) ? 0xff : (unsigned char)out;
603 static inline unsigned char CalcExtraFadeColor(unsigned char bg, unsigned char fg)
605 unsigned short out = bg;
606 out+= fg;
607 out/= 2;
608 return (out > 0xff) ? 0xff : (unsigned char)out;
611 // #define DEBUG_FADED_EDGES
613 struct WXCustomDrawCharPainter : WXCustomDrawChar::Painter
615 ConsolePainter &_painter;
616 const WinPortRGB &_clr_text;
617 const WinPortRGB &_clr_back;
619 inline WXCustomDrawCharPainter(ConsolePainter &painter, const WinPortRGB &clr_text, const WinPortRGB &clr_back)
620 : _painter(painter), _clr_text(clr_text), _clr_back(clr_back)
622 fw = (wxCoord)_painter._context->FontWidth();
623 fh = (wxCoord)_painter._context->FontHeight(),
624 thickness = (wxCoord)_painter._context->FontThickness();
625 _painter.SetFillColor(clr_text);
628 inline bool MayDrawFadedEdgesImpl()
630 return (fw > 7 && fh > 7 && !_painter._context->IsSharp());
633 inline void SetColorFadedImpl()
635 #ifndef DEBUG_FADED_EDGES
636 WinPortRGB clr_fade(CalcFadeColor(_clr_back.r, _clr_text.r),
637 CalcFadeColor(_clr_back.g, _clr_text.g), CalcFadeColor(_clr_back.b, _clr_text.b));
638 #else
639 WinPortRGB clr_fade(0xff, 0, 0);
640 #endif
641 _painter.SetFillColor(clr_fade);
644 inline void SetColorExtraFadedImpl()
646 #ifndef DEBUG_FADED_EDGES
647 WinPortRGB clr_fade(CalcExtraFadeColor(_clr_back.r, _clr_text.r),
648 CalcExtraFadeColor(_clr_back.g, _clr_text.g), CalcExtraFadeColor(_clr_back.b, _clr_text.b));
649 #else
650 WinPortRGB clr_fade(0, 0xff, 0);
651 #endif
652 _painter.SetFillColor(clr_fade);
656 inline void FillRectangleImpl(wxCoord left, wxCoord top, wxCoord right, wxCoord bottom)
658 _painter._dc.DrawRectangle(left, top, right + 1 - left , bottom + 1 - top);
662 // this code little bit wacky just to avoid virtual methods overhead
663 bool WXCustomDrawChar::Painter::MayDrawFadedEdges()
665 return ((WXCustomDrawCharPainter *)this)->MayDrawFadedEdgesImpl();
668 void WXCustomDrawChar::Painter::SetColorFaded()
670 ((WXCustomDrawCharPainter *)this)->SetColorFadedImpl();
673 void WXCustomDrawChar::Painter::SetColorExtraFaded()
675 ((WXCustomDrawCharPainter *)this)->SetColorExtraFadedImpl();
678 void WXCustomDrawChar::Painter::FillRectangle(wxCoord left, wxCoord top, wxCoord right, wxCoord bottom)
680 ((WXCustomDrawCharPainter *)this)->FillRectangleImpl(left, top, right, bottom);
683 void WXCustomDrawChar::Painter::FillPixel(wxCoord left, wxCoord top)
685 ((WXCustomDrawCharPainter *)this)->FillRectangleImpl(left, top, left, top);
689 void ConsolePainter::NextChar(unsigned int cx, DWORD64 attributes, const wchar_t *wcz, unsigned int nx)
691 WXCustomDrawChar::DrawT custom_draw = nullptr;
693 if (!wcz[0] || (!wcz[1] && (wcz[0] == L' ' || !WCHAR_IS_VALID(wcz[0]) || (_context->IsCustomDrawEnabled()
694 && (custom_draw = WXCustomDrawChar::Get(wcz[0])) != nullptr)))) {
695 if (!_buffer.empty())
696 FlushBackground(cx + nx - 1);
697 FlushText(cx + nx - 1);
700 const WinPortRGB &clr_back = ConsoleBackground2RGB(attributes);
701 PrepareBackground(cx, clr_back, nx);
703 if (!wcz[0] || (!wcz[1] && (wcz[0] == L' ' || !WCHAR_IS_VALID(wcz[0])))) {
704 return;
707 const WinPortRGB &clr_text = ConsoleForeground2RGB(attributes);
709 if (custom_draw) {
710 FlushBackground(cx + nx);
711 WXCustomDrawCharPainter cdp(*this, clr_text, clr_back);
712 custom_draw(cdp, _start_y, cx);
713 FlushUnderline(cx);
714 _start_cx = (unsigned int)-1;
715 _prev_fit_font_index = 0;
716 return;
719 uint8_t fit_font_index = _context->CharFitTest(_dc, wcz);
720 const bool underlined = (attributes & COMMON_LVB_UNDERSCORE) != 0;
722 if (fit_font_index == _prev_fit_font_index && _prev_underlined == underlined
723 && _start_cx != (unsigned int)-1 && _clr_text == clr_text && _context->IsPaintBuffered()) {
724 _buffer+= wcz;
725 return;
728 FlushBackground(cx + nx);
729 FlushText(cx);
731 _prev_fit_font_index = fit_font_index;
732 _prev_underlined = underlined;
734 _start_cx = cx;
735 _buffer = wcz;
736 _clr_text = clr_text;
738 if (fit_font_index != 0 && fit_font_index != 0xff) {
739 _context->ApplyFont(_dc, fit_font_index);
740 FlushText(cx + nx);
741 _context->ApplyFont(_dc);