From 6659c6fb24e05bbaaaedc422a51f47a2d3b901bb Mon Sep 17 00:00:00 2001 From: Dmitry Sapozhnikov <11535558+o-sdn-o@users.noreply.github.com> Date: Fri, 19 Apr 2024 22:39:43 +0500 Subject: [PATCH] #571 WIP: Revise title rendering (shadows + fix overbright) --- src/netxs/desktopio/canvas.hpp | 19 ++++ src/netxs/desktopio/geometry.hpp | 1 + src/netxs/desktopio/gui.hpp | 163 ++++++++++++++----------------- src/netxs/desktopio/intmath.hpp | 2 +- 4 files changed, 95 insertions(+), 90 deletions(-) diff --git a/src/netxs/desktopio/canvas.hpp b/src/netxs/desktopio/canvas.hpp index 152d7981bb..9deb5632c3 100644 --- a/src/netxs/desktopio/canvas.hpp +++ b/src/netxs/desktopio/canvas.hpp @@ -2962,7 +2962,10 @@ namespace netxs digest++; } }; +} +namespace netxs::misc +{ template void boxblur(auto& image, si32 r, T&& cache = {}, P shade = {}) { @@ -2998,4 +3001,20 @@ namespace netxs d_width, Ratio, s_point, d_point, shade); } + void contour(auto& image) + { + static auto shadows_cache = std::vector{}; + static auto boxblur_cache = std::vector{}; + auto r = image.area(); + auto v = r.size.x * r.size.y; + boxblur_cache.resize(v); + shadows_cache.resize(v); + auto shadows_image = netxs::raster, rect>{ shadows_cache, r }; + shadows_image.step(-dot_11); + netxs::onbody(image, shadows_image, [](auto& src, auto& dst){ dst = src ? 255.f * 3.f : 0.f; }); + shadows_image.step(dot_11); + shadows_image.clip(r); + netxs::misc::boxblur<2>(shadows_image, 1, boxblur_cache); + netxs::oncopy(image, shadows_image, [](auto& src, auto& dst){ src.chan.a = src ? 0xFF : (byte)std::clamp(dst, 0.f, 255.f); }); + } } \ No newline at end of file diff --git a/src/netxs/desktopio/geometry.hpp b/src/netxs/desktopio/geometry.hpp index 5293634509..47f53b458c 100644 --- a/src/netxs/desktopio/geometry.hpp +++ b/src/netxs/desktopio/geometry.hpp @@ -211,6 +211,7 @@ namespace netxs twod map (twod p) const { return p - coor; } rect shift (twod p) const { return { coor + p, size }; } auto& shift_itself (twod p) { coor += p; return *this; } + auto& moveto (twod p) { coor = p; return *this; } rect operator | (rect r) const { return unite(r, *this); } auto& operator += (rect r) { coor += r.coor; size += r.size; return *this; } auto& operator -= (rect r) { coor -= r.coor; size -= r.size; return *this; } diff --git a/src/netxs/desktopio/gui.hpp b/src/netxs/desktopio/gui.hpp index c79b3482e0..739b74f692 100644 --- a/src/netxs/desktopio/gui.hpp +++ b/src/netxs/desktopio/gui.hpp @@ -25,7 +25,7 @@ namespace netxs::gui auto fx_white = [](auto& c){ c = 0x5F'23'23'23; }; auto fx_black = [](auto& c){ c = 0x3F'00'00'00; }; - struct w32window : IDWriteTextRenderer + struct w32surface : IDWriteTextRenderer { using bits = netxs::raster, rect>; using dwrt = IDWriteBitmapRenderTarget*; @@ -34,11 +34,13 @@ namespace netxs::gui { IDWriteFactory2* pDWriteFactory{}; IDWriteGdiInterop* pGdiInterop{}; - IDWriteRenderingParams* pRenderingParams{}; + IDWriteRenderingParams* pNaturalRendering{}; + IDWriteRenderingParams* pAliasedRendering{}; IDWriteTextFormat* pTextFormat{}; void reset() { - pRenderingParams->Release(); + pNaturalRendering->Release(); + pAliasedRendering->Release(); pTextFormat->Release(); pGdiInterop->Release(); pDWriteFactory->Release(); @@ -47,8 +49,6 @@ namespace netxs::gui HDC hdc; argb fgc; - argb shadow_color; - twod shadow_shift; HWND hWnd; bool sync; rect prev; @@ -58,9 +58,9 @@ namespace netxs::gui gcfg conf; dwrt surf; - w32window(w32window const&) = default; - w32window(w32window&&) = default; - w32window(gcfg context, HWND hWnd) + w32surface(w32surface const&) = default; + w32surface(w32surface&&) = default; + w32surface(gcfg context, HWND hWnd) : hWnd{ hWnd }, sync{ faux }, prev{ .coor = dot_mx, .size = dot_11 }, @@ -80,31 +80,35 @@ namespace netxs::gui { if (surf) surf->Release(); } - auto canvas() + auto canvas(bool wipe = faux) { if (area && hdc) { if (area.size != size) { auto hr = surf->Resize(area.size.x, area.size.y); - if (hr == S_OK) size = area.size; - else log("bitmap resizing error: ", utf::to_hex(hr)); + if (hr == S_OK) + { + //wipe = faux; + size = area.size; + } + else log("bitmap resizing error: ", utf::to_hex(hr)); } sync = faux; auto pv = BITMAP{}; if (sizeof(BITMAP) == ::GetObjectW(::GetCurrentObject(hdc, OBJ_BITMAP), sizeof(BITMAP), &pv)) { - return bits{ std::span{ (argb*)pv.bmBits, (sz_t)size.x * size.y }, rect{ area.coor, size }}; + auto sz = (sz_t)size.x * size.y; + if (wipe) std::memset(pv.bmBits, 0, sz * sizeof(argb)); + return bits{ std::span{ (argb*)pv.bmBits, sz }, rect{ area.coor, size }}; } } return bits{}; } - auto textout(rect dest, argb color, wiew txt, argb shadow_clr = {}, twod shadow_off = {}) + auto textout(rect dest, argb color, wiew txt) { if (!dest || !hdc) return; fgc = color; - shadow_color = shadow_clr; - shadow_shift = shadow_off; auto pTextLayout = (IDWriteTextLayout*)nullptr; auto textLength = (ui32)txt.size(); ok2(conf.pDWriteFactory->CreateTextLayout(txt.data(), @@ -118,8 +122,6 @@ namespace netxs::gui auto dtm = DWRITE_TEXT_METRICS{}; pTextLayout->GetMetrics(&dtm); auto minHeight = dtm.height; - //auto minWidth = dtm.widthIncludingTrailingWhitespace; - //log("mh ", minHeight, " dest.size.y ", dest.size.y); dest.coor.y -= (si32)minHeight; dest.size.y = -dest.size.y; } @@ -127,9 +129,7 @@ namespace netxs::gui { auto min_width = fp32{}; pTextLayout->DetermineMinWidth(&min_width); - auto mw = (si32)min_width; - //log("mw ", mw, " dest.size.x ", dest.size.x); - dest.coor.x -= mw + (shadow_clr ? shadow_off.x * 3 : 0); + dest.coor.x -= (si32)min_width; dest.size.x = -dest.size.x; } //pTextLayout->SetUnderline(true, DWRITE_TEXT_RANGE{ 0, textLength }); @@ -137,7 +137,7 @@ namespace netxs::gui ok2(pTextLayout->Draw(hdc, this, (fp32)dest.coor.x, (fp32)dest.coor.y)); pTextLayout->Release(); } - void display() + void present() { if (sync || !hdc) return; //netxs::misc::fill(canvas(), [](argb& c){ c.pma(); }); @@ -168,31 +168,6 @@ namespace netxs::gui IFACEMETHOD(DrawGlyphRun)(void* /*clientDrawingContext*/, fp32 baselineOriginX, fp32 baselineOriginY, DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun, DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, IUnknown* /*clientDrawingEffect*/) { auto dirtyRect = RECT{}; - if (shadow_color) //todo draw shadow by the resulting bitmap instead of glyph run - { - auto cache = vrgb{}; - auto clip = rect{}; - auto dest = canvas(); - dest.move(dot_00); - auto test = 0xff; - for (auto i = 0; i < 3 ; i++) //todo optimize, just apply gamma - { - surf->DrawGlyphRun(baselineOriginX + shadow_shift.x, - baselineOriginY + shadow_shift.y, - measuringMode, - glyphRun, - conf.pRenderingParams, - test, - &dirtyRect); - clip = rect{{ dirtyRect.left, dirtyRect.top }, { dirtyRect.right - dirtyRect.left, dirtyRect.bottom - dirtyRect.top }}; - clip += dent{ 1,1,1,1 } * 2; - clip.trimby(dest.area()); - dest.clip(clip); - netxs::boxblur<1>(dest, 1, cache); - } - auto d = shadow_color; - netxs::onrect(dest, clip, [d](auto& c){ c.chan.a = c.chan.r; c.chan.r = d.chan.r; c.chan.g = d.chan.g; c.chan.b = d.chan.b; }); - } auto para_layers = (IDWriteColorGlyphRunEnumerator*)nullptr; auto hr = conf.pDWriteFactory->TranslateColorGlyphRun(baselineOriginX, baselineOriginY, glyphRun, glyphRunDescription, measuringMode, nullptr, 0, ¶_layers); if (para_layers) //todo cache color glyphs @@ -209,7 +184,7 @@ namespace netxs::gui hr = surf->DrawGlyphRun(s.baselineOriginX, s.baselineOriginY, measuringMode, &s.glyphRun, - conf.pRenderingParams, + conf.pNaturalRendering, // Emojis are broken without antialiasing. argb::swap_rb(s.paletteIndex == -1 ? fgc.token : color.token), &dirtyRect); } @@ -222,7 +197,8 @@ namespace netxs::gui baselineOriginY, measuringMode, glyphRun, - conf.pRenderingParams, + //conf.pNaturalRendering, + conf.pAliasedRendering, argb::swap_rb(fgc.token), &dirtyRect); } @@ -278,8 +254,8 @@ namespace netxs::gui template struct w32renderer { - using gcfg = w32window::gcfg; - using wins = std::vector; + using gcfg = w32surface::gcfg; + using wins = std::vector; gcfg conf; bool initialized; @@ -338,7 +314,9 @@ namespace netxs::gui fontSize, L"en-en", &conf.pTextFormat)); - ok2(conf.pDWriteFactory->CreateRenderingParams(&conf.pRenderingParams)); + //ok2(conf.pDWriteFactory->CreateRenderingParams(&conf.pAliasedRendering)); + ok2(conf.pDWriteFactory->CreateCustomRenderingParams(1.f/*no gamma*/, 0.f/*nocontrast*/, 0.f/*grayscale*/, DWRITE_PIXEL_GEOMETRY_FLAT, DWRITE_RENDERING_MODE_ALIASED, &conf.pAliasedRendering)); + ok2(conf.pDWriteFactory->CreateCustomRenderingParams(2.2f/*sRGB gamma*/, 0.f/*nocontrast*/, 0.5f/*cleartype*/, DWRITE_PIXEL_GEOMETRY_FLAT, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, &conf.pNaturalRendering)); pFontFamily->Release(); pFont->Release(); pFamilyNames->Release(); @@ -416,7 +394,7 @@ namespace netxs::gui } }; - auto header_text = L"Windows Command Prompt - C:\\Windows\\System32\\"s; + auto header_text = L"Windows Command Prompt - 😎 - C:\\Windows\\System32\\"s; auto footer_text = L"4/4000 80:25"s; auto header_para = ui::para{ utf::to_utf(header_text) }; auto footer_para = ui::para{ utf::to_utf(footer_text) }; @@ -504,6 +482,7 @@ namespace netxs::gui si32 header; si32 footer; si32 client; + static constexpr auto shadow_dent = dent{ 1,1,1,1 } * 3; constexpr explicit operator bool () const { return initialized; } ~window() @@ -530,8 +509,11 @@ namespace netxs::gui if (!engine) return; layers[shadow].area = { inner_rect.coor - shadow_rastr.over / 2, inner_rect.size + shadow_rastr.over }; layers[client].area = inner_rect + outer_dent; - layers[header].area = rect{ inner_rect.coor, { inner_rect.size.x, -cell_size.y * ((header_para.size().x + grid_size.x - 1)/ grid_size.x) }}.normalize_itself(); - layers[footer].area = rect{{ inner_rect.coor.x, inner_rect.coor.y + inner_rect.size.y }, { inner_rect.size.x, cell_size.y * ((footer_para.size().x + grid_size.x - 1)/ grid_size.x) }}; + + layers[header].area = rect{ inner_rect.coor, { inner_rect.size.x, -cell_size.y * ((header_para.size().x + grid_size.x - 1)/ grid_size.x) }}.normalize_itself() + shadow_dent; + layers[footer].area = rect{{ inner_rect.coor.x, inner_rect.coor.y + inner_rect.size.y }, { inner_rect.size.x, cell_size.y * ((footer_para.size().x + grid_size.x - 1)/ grid_size.x) }} + shadow_dent; + layers[header].area.coor.y -= shadow_dent.b; + //layers[footer].area.coor.y += shadow_dent.t; //dx3d specific //dx3d = [&] @@ -560,8 +542,10 @@ namespace netxs::gui layers[client].area.coor += coor_delta; inner_rect.coor += coor_delta; //todo unify - layers[header].area = rect{ inner_rect.coor, { inner_rect.size.x, -cell_size.y * ((header_para.size().x + grid_size.x - 1) / grid_size.x) }}.normalize_itself(); - layers[footer].area = rect{{ inner_rect.coor.x, inner_rect.coor.y + inner_rect.size.y }, { inner_rect.size.x, cell_size.y * ((footer_para.size().x + grid_size.x - 1) / grid_size.x) }}; + layers[header].area = rect{ inner_rect.coor, { inner_rect.size.x, -cell_size.y * ((header_para.size().x + grid_size.x - 1)/ grid_size.x) }}.normalize_itself() + shadow_dent; + layers[footer].area = rect{{ inner_rect.coor.x, inner_rect.coor.y + inner_rect.size.y }, { inner_rect.size.x, cell_size.y * ((footer_para.size().x + grid_size.x - 1)/ grid_size.x) }} + shadow_dent; + layers[header].area.coor.y -= shadow_dent.b; + //layers[footer].area.coor.y += shadow_dent.t; tasks += task::moved; } @@ -571,8 +555,10 @@ namespace netxs::gui layers[shadow].area.size += size_delta; inner_rect.size += size_delta; //todo unify - layers[header].area = rect{ inner_rect.coor, { inner_rect.size.x, -cell_size.y * ((header_para.size().x + grid_size.x - 1)/ grid_size.x) }}.normalize_itself(); - layers[footer].area = rect{{ inner_rect.coor.x, inner_rect.coor.y + inner_rect.size.y }, { inner_rect.size.x, cell_size.y * ((footer_para.size().x + grid_size.x - 1)/ grid_size.x) }}; + layers[header].area = rect{ inner_rect.coor, { inner_rect.size.x, -cell_size.y * ((header_para.size().x + grid_size.x - 1)/ grid_size.x) }}.normalize_itself() + shadow_dent; + layers[footer].area = rect{{ inner_rect.coor.x, inner_rect.coor.y + inner_rect.size.y }, { inner_rect.size.x, cell_size.y * ((footer_para.size().x + grid_size.x - 1)/ grid_size.x) }} + shadow_dent; + layers[header].area.coor.y -= shadow_dent.b; + //layers[footer].area.coor.y += shadow_dent.t; tasks += task::sized; } @@ -625,8 +611,9 @@ namespace netxs::gui } } } - void draw_grid(auto canvas) + void draw_grid() { + auto canvas = layers[client].canvas(); auto c = rect{{}, cell_size }; auto lt = dent{ 1, 0, 1, 0 }; auto rb = dent{ 0, 1, 0, 1 }; @@ -667,14 +654,17 @@ namespace netxs::gui //netxs::onrect(canvas, c3, fx_pure_wt); canvas.step(inner_rect.coor); + auto region = rect{ .coor = grip_size + cell_size * dot_01, .size = inner_rect.size }; + layers[client].textout(region, tint::cyanlt, L"vtm GUI frontend is currently under development. You can try it on any versions/editions of Windows platforms starting from Windows 8.1 (with colored emoji!), including Windows Server Core. 😀😬😁😂😃😄😅😆 👌🐞😎👪. Press Esc or Right click to close."s); } bool hit_grips() { auto hit = !grip.zoomon && (grip.seized || (hovered && !inner_rect.hittest(mouse_coord) && layers[client].area.hittest(mouse_coord))); return hit; } - void draw_grips(auto canvas) + void draw_grips() { + auto canvas = layers[client].canvas(); auto fx_trans2 = [](auto& c){ c = 0x01'00'00'00; }; auto fx_white2 = [](auto& c){ c = 0x5F'23'23'23; }; auto fx_black2 = [](auto& c){ c = 0x3F'00'00'00; }; @@ -689,11 +679,23 @@ namespace netxs::gui //log("grips ", side_x, " ", side_y); } } - void draw_shadow(auto canvas) //todo draw shadow per cell + void draw_shadow() //todo draw shadow per cell { + auto canvas = layers[shadow].canvas(); canvas.move(-shadow_rastr.over / 2); //todo unify shadow_rastr.render(canvas, inner_rect.size); } + void draw_titles() + { + auto header_dest = layers[header].canvas(true); + auto footer_dest = layers[footer].canvas(true); + auto h = header_dest.area().moveto(dot_00).rotate({ 1, -1 }) - shadow_dent; + auto f = footer_dest.area().moveto(dot_00).rotate({ -1, 1 }) - shadow_dent; + layers[header].textout(h, 0xFF'ff'ff'ff, header_text); + layers[footer].textout(f, 0xFF'ff'ff'ff, footer_text); + netxs::misc::contour(header_dest); + netxs::misc::contour(footer_dest); + } void redraw() { auto mods = tasks; @@ -712,34 +714,17 @@ namespace netxs::gui } else if (mods) { - if (mods(task::sized | task::inner)) - { - draw_grid(layers[client].canvas()); // 0.600 ms - auto region = rect{ .coor = grip_size + cell_size * dot_01, .size = inner_rect.size }; - layers[client].textout(region, tint::cyanlt, L"vtm GUI frontend is currently under development. You can try it on any versions/editions of Windows platforms starting from Windows 8.1 (with colored emoji!), including Windows Server Core. 😀😬😁😂😃😄😅😆 👌🐞😎👪. Press Esc or Right click to close."s); - } - if (mods(task::sized | task::grips)) draw_grips(layers[client].canvas()); // 0.150 ms - if (mods(task::sized )) draw_shadow(layers[shadow].canvas()); // 0.300 ms - if (mods(task::sized | task::title)) - { - auto header_dest = layers[header].canvas(); - auto footer_dest = layers[footer].canvas(); - netxs::misc::fill(header_dest, header_dest.area(), [](auto& c){ c = 0; }); - netxs::misc::fill(footer_dest, footer_dest.area(), [](auto& c){ c = 0; }); - auto h = layers[header].area; - auto f = layers[footer].area; - h.coor = {}; - f.coor = {}; - layers[header].textout(h.rotate({ 1, -1 }), tint::purewhite, header_text, tint::pureblack, dot_11); - layers[footer].textout(f.rotate({ -1, 1 }), tint::purewhite, footer_text, tint::pureblack, dot_11); - } - if (hovered/*mouse test*/) - { - auto fx_green = [](auto& c){ c = 0x7F'00'3f'00; }; - auto cursor = rect{ mouse_coord - (mouse_coord - layers[client].area.coor) % cell_size, cell_size }; - netxs::onrect(layers[client].canvas(), cursor, fx_green); - } - for (auto& w : layers) w.display(); + if (mods(task::sized | task::inner)) draw_grid(); // 0.600 ms + if (mods(task::sized | task::grips)) draw_grips(); // 0.150 ms + if (mods(task::sized )) draw_shadow(); // 0.300 ms + if (mods(task::sized | task::title)) draw_titles(); + //if (hovered/*mouse test*/) + //{ + // auto fx_green = [](auto& c){ c = 0x7F'00'3f'00; }; + // auto cursor = rect{ mouse_coord - (mouse_coord - layers[client].area.coor) % cell_size, cell_size }; + // netxs::onrect(layers[client].canvas(), cursor, fx_green); + //} + for (auto& w : layers) w.present(); //log("full update"); } } diff --git a/src/netxs/desktopio/intmath.hpp b/src/netxs/desktopio/intmath.hpp index 73822c6d3a..90bc3b829e 100644 --- a/src/netxs/desktopio/intmath.hpp +++ b/src/netxs/desktopio/intmath.hpp @@ -1127,7 +1127,7 @@ namespace netxs auto width = r1 + r; auto debug = [&]([[maybe_unused]] auto& accum) { - if constexpr (debugmode) + if constexpr (!std::is_same_v, fp32> && debugmode) { auto n = accum / count; if (n > 255) throw;