From b1c640a9564c70a51a89b44a29f6c5922001aa42 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sun, 17 Jan 2021 13:58:25 -0500 Subject: Docs: Start outlining options for highdpi resource handling --- Documentation/HighDPI.md | 151 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/Documentation/HighDPI.md b/Documentation/HighDPI.md index 8c946b9dd1..b8f19c0828 100644 --- a/Documentation/HighDPI.md +++ b/Documentation/HighDPI.md @@ -23,6 +23,157 @@ Desired end state - Jury's still out if logical coordinates should stay ints. Probably, but it means mouse cursor etc only have point resolution, not pixel resolution - We should have something that can store a collection of (lazily-loaded?) bitmaps and fonts that each represent a single image / font at different scale levels, and at paint time the right representation is picked for the current scale +Resource loading +---------------- + +Resources such as icons, cursors, bitmap fonts are scale-dependent: In HighDPI modes, different resources need to be loaded. + +### Art direction + +A 2x resource should look like a 1x resource, just with less jagged edges. A horizontal or vertical line that's 1 pixel wide in 1x should be 2 pixels wide in 2x. + +A good guideline for black-and-white images: start with a 1x bitmap, resize it to 200% using nearest-neighbor filtering, and then move black pixels around to smooth diagonal edges -- but the number of black pixels shouldn't change relative to the 200% nearest-neighbor-resampled image. + +While a 1x 32x32 bitmap and a 2x 16x16 bitmap both have 32x32 pixels, they don't have to look the same: The 2x 16x16 should look exactly like the corresonding 1x 16x16, just with smoother edges. The 1x 32x32 pixel resource could instead pick a different crop. As a concrete example, the 1x 7x10 ladybug emoji image currently just has the ladybug's shell (for space reasons), and so should the 2x version of that emoji. On the other hand, if we add a higher-res 1x 14x20 ladybug emoji at some point, we might want show the ladybug's legs on it, instead of just a smoother rendition of just the shell. (The 2x version of that 14x20 emoji would then have legs and shell in less jagged.) + +### Directory structure + +currently: + + res/ + cursors/ + arrowx2y2.png + ... + emoji/ (currently all 7x10 px) + U+1F346.png + ... + fonts/ + CsillaRegular10.font + ... + graphics/ + brand-banner.png + ... + icons/ + 16x16/ + small app icons, small filetype icons, toolbar icons, window buttons, ... + 32x32/ + large app icons, large filetype icons, message box icons + various per-app folders with in-app UI images (XXX: maybe move into "apps" subdir?) + themes/ + Coffee/ + 16x16/ + custom window buttons + (more themes) + ... + wallpapers/ + desktop wallpapers + +Every one of these could grow a 2x variant (and if we do more scale factors later, even more variants). + +Possible new structures: + +1. Have "1x", "2x" folders right inside res and then mirror folder structures inside them: + + res/ + 1x/ + cursors/ + 16x16/ + emoji/ + ... + 2x/ + cursors/ + 16x16/ + emoji/ + ... + +2. Instead of having the 1x/2x fork at the root, have it at each leaf: + + res/ + cursors/ + 1x/ + arrowx2y2.png + ... + 2x/ + arrowx2y2.png + ... + emoji/ + 1/ + U+1F346.png + ... + 2/ + U+1F346.png + ... + ... + +3. Use filename suffixes instead of directories (similar to macOS): + + res/ + cursors/ + arrowx2y2.png + arrowx2y2@2x.png + ... + emoji/ + U+1F346.png + U+1F346@2x.png + ... + +4. Use suffixes on directory instead of subdirectory: + + res/ + cursors/ + arrowx2y2.png + ... + cursors-2x/ + arrowx2y2.png + ... + +Root-level split makes it easy to see which scale factors exist and is subjectively aesthetically pleasing. + +Filename suffixes make it easy to see which icons don't have high-res versions (but in return clutter up an icon directory), and it makes it easy to get the intrinsic scale factor of a bitmap (just need to look at the image's basename, not at any directory). + +Android has additional modifiers in additon to scale factors in its resource system (UI language, light/dark mode, screen size in addition to resolution, etc). If we ever add more factors to the resource system, a suffix-based system would probably extend more nicely than a nesting-based one. + +In the end probably doesn't matter all that much which version to pick. + +### Resource loading strategy tradeoffs + +- eagerly load one scale, reload at new scale on scale factor change events + - needs explicit code + - random code in LibGfx currently loads icons, and scale factor change events would be more a LibGUI level concept, not clear how to plumb the event to there + + memory efficient -- only have one copy of each resource in memory + + easy to understand: Bitmap stays Bitmap, Font stays Font, no need for collections + +- have BitmapCollection that stores high-res and low-res path and load lazily when needed + - need to do synchronous disk access at first paint on UI thread + - or load compressed data at each scale eagerly and decompress lazily. still a blocking decode on UI thread then, and needs more memory -- 2x compressed resources in memory even if they might never be needed + + puts complexity in framework, app doesn't have to care + + can transparently paint UI at both 1x and 2x into different backbuffers (eg for multiple screens that have different scale factors) + +- eagerly load both and use the right one at paint time + - similar to GUI::Icon + - 400% memory overhead in 1x mode (but most icons are small) + + conceptually easy to understand, but still need some collection class + + puts (less) complexity in framework, app doesn't have to care + + can transparently paint UI at both 1x and 2x into different backbuffers (eg for multiple screens that have different scale factors) + +### Resource loading API + +Currently: + + auto app_icon = GUI::Icon::default_icon("app-playground"); + +or + + s_unfilled_circle_bitmap = Bitmap::load_from_file("/res/icons/serenity/unfilled-radio-circle.png"); + +or + + header.set_font(Gfx::Font::load_from_file("/res/fonts/PebbletonBold14.font")); + +Going forward: + + FIXME (depends on loading strategy decison a bit?) + Implementation plan ------------------- -- cgit v1.2.3