Merge lp:~widelands-dev/widelands/fh1-multitexture into lp:widelands
- fh1-multitexture
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 8369 |
Proposed branch: | lp:~widelands-dev/widelands/fh1-multitexture |
Merge into: | lp:widelands |
Diff against target: |
3418 lines (+1178/-714) 64 files modified
src/editor/ui_menus/main_menu_map_options.cc (+1/-3) src/editor/ui_menus/main_menu_random_map.cc (+1/-3) src/editor/ui_menus/tool_set_terrain_options_menu.cc (+3/-3) src/graphic/CMakeLists.txt (+0/-9) src/graphic/animation.cc (+2/-1) src/graphic/build_texture_atlas.cc (+0/-4) src/graphic/font_handler1.cc (+49/-87) src/graphic/font_handler1.h (+7/-13) src/graphic/graphic.h (+4/-0) src/graphic/minimap_renderer.cc (+2/-1) src/graphic/playercolor.cc (+2/-2) src/graphic/rendertarget.cc (+18/-6) src/graphic/rendertarget.h (+8/-2) src/graphic/text/CMakeLists.txt (+4/-0) src/graphic/text/rendered_text.cc (+266/-0) src/graphic/text/rendered_text.h (+174/-0) src/graphic/text/rt_render.cc (+260/-132) src/graphic/text/rt_render.h (+6/-7) src/graphic/text/sdl_ttf_font.cc (+10/-9) src/graphic/text/sdl_ttf_font.h (+5/-3) src/graphic/text/test/CMakeLists.txt (+0/-1) src/graphic/text/test/render.cc (+1/-1) src/graphic/text/test/render_richtext.cc (+7/-2) src/graphic/text/texture_cache.h (+39/-0) src/graphic/text/transient_cache.h (+106/-29) src/graphic/text_layout.cc (+16/-8) src/graphic/text_layout.h (+9/-9) src/graphic/texture.cc (+1/-1) src/graphic/texture_atlas.cc (+2/-2) src/graphic/texture_cache.cc (+0/-87) src/graphic/wordwrap.cc (+10/-14) src/graphic/wordwrap.h (+1/-1) src/logic/editor_game_base.h (+2/-2) src/logic/map_objects/map_object.cc (+11/-14) src/network/network.h (+1/-1) src/ui_basic/button.cc (+4/-7) src/ui_basic/checkbox.cc (+8/-8) src/ui_basic/checkbox.h (+1/-1) src/ui_basic/editbox.cc (+10/-23) src/ui_basic/listselect.cc (+10/-27) src/ui_basic/messagebox.cc (+2/-2) src/ui_basic/multilineeditbox.cc (+1/-1) src/ui_basic/multilinetextarea.cc (+10/-23) src/ui_basic/panel.cc (+7/-6) src/ui_basic/panel.h (+3/-0) src/ui_basic/progressbar.cc (+2/-3) src/ui_basic/progresswindow.cc (+3/-5) src/ui_basic/slider.cc (+6/-17) src/ui_basic/table.cc (+18/-36) src/ui_basic/tabpanel.cc (+17/-18) src/ui_basic/tabpanel.h (+2/-4) src/ui_basic/textarea.cc (+3/-6) src/ui_basic/textarea.h (+1/-1) src/ui_basic/window.cc (+2/-3) src/ui_fsmenu/loadgame.cc (+1/-11) src/wui/chatoverlay.cc (+5/-7) src/wui/game_tips.cc (+9/-12) src/wui/interactive_base.cc (+7/-10) src/wui/interactive_base.h (+2/-2) src/wui/interactive_gamebase.cc (+2/-4) src/wui/mapdetails.cc (+1/-3) src/wui/maptable.cc (+1/-1) src/wui/plot_area.cc (+8/-11) src/wui/waresdisplay.cc (+4/-5) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/fh1-multitexture |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Klaus Halfmann | code review, compile, test | Approve | |
kaputtnik (community) | testing | Approve | |
GunChleoc | Needs Resubmitting | ||
Review via email: mp+323903@code.launchpad.net |
Commit message
The new font renderer now creates a set of textures that will be blitted separately by the new class RenderedText. This avoids issues with texture sizes exceeding the maximum size supported by graphics drivers when MultilineTextareas get arbitrarily long.
- Fixed positioning and scaling for census & statistics.
- Removed size restriction from FullscreenMenuL
- Got rid of BiDi spaghetti code in Table and Listselect
- As a side effect, this fixes the lag in Multilineeditbox with large texts and reduces the amount of needed cache memory.
Also, refactored the following classes and functions:
- text_height()
- Tooltips
- Game tips
- TabPanel
Description of the change
Notes:
1. There are 2 NOCOMs in this branch for testing purposes - need to be removed before merging.
2. Code review: Please pay special attention to memory management.
3. Testers: Pay attention to scrolling and alignment issues and possible wonky background colors. The messages sent by buildings have a row of dead trees in the background - this is for testing background images and will be removed before the merge.
Known issue: A line of text can start with a blank space. This issue is already in trunk and unrelated to this branch.
bunnybot (widelandsofficial) wrote : | # |
kaputtnik (franku) wrote : | # |
Got a compiler warning:
/home/kaputtnik
const int maximum_size = kMinimumSizeFor
And some crashes, but i am not sure if they are related to this branch, because trunk crashes also sometimes without a reason. One crash in this branch shows in console:
SaveHandler:
Autosave: save took 660 ms
pure virtual method called
terminate called without an active exception
Abgebrochen (Speicherabzug geschrieben
Will try to get a backtrace...
kaputtnik (franku) wrote : | # |
In the message box strings in the title column are not cut if they exceed the column width. Played the Warfare tutorial and the message 'Enemy defeated' is only cut at the image from the Type-column, not where the Title-column ends.
No crash so far... every time running with gdb i get no crash :-D
GunChleoc (gunchleoc) wrote : | # |
Fixed the cropping, so this is now ready for review again.
kaputtnik (franku) wrote : | # |
Got a crash:
Thread 1 "widelands" received signal SIGSEGV, Segmentation fault.
0x0000000000cfe5fd in RenderTarget::blit (this=0x1c03930, dst=..., image=0x803d538, blend_mode=
align=
151 UI::correct_
(gdb) bt
#0 0x0000000000cfe5fd in RenderTarget::blit (this=0x1c03930, dst=..., image=0x803d538, blend_mode=
align=
#1 0x0000000000d4a5b1 in UI::RenderedTex
cropmode=
at /home/kaputtnik
#2 0x0000000000d4a75e in UI::RenderedTex
at /home/kaputtnik
#3 0x0000000000fac3b9 in InteractiveGame
at /home/kaputtnik
#4 0x0000000000effd10 in UI::Panel:
at /home/kaputtnik
#5 0x0000000000effe7d in UI::Panel::do_draw (this=0x9c77ce0, dst=...)
at /home/kaputtnik
#6 0x0000000000efeafe in UI::Panel::do_run (this=0x9c77ce0)
at /home/kaputtnik
#7 0x0000000000c578a0 in UI::Panel:
at /home/kaputtnik
#8 0x0000000000d6b046 in Widelands:
script_
at /home/kaputtnik
#9 0x0000000000d6a4a0 in Widelands:
at /home/kaputtnik
#10 0x0000000000c52396 in WLApplication:
at /home/kaputtnik
#11 0x0000000000c513ee in WLApplication:
at /home/kaputtnik
#12 0x0000000000c50
kaputtnik (franku) wrote : | # |
Got another crash in RenderTarget:
Those bugs appear after playing a while. Could not say if i played an hour or longer using mostly 3x speed or 5x speed in fullscreen mode (initial resolution 1440x900).
Klaus Halfmann (klaus-halfmann) wrote : | # |
I think that render_cache_ need a bit more attention:
* explian what this actually maps
* what is the cachek poliy (e.g. when will object be removed?)
* why do you use an (additional) std::unique_ptr if the make already makes this unique?
* if its not small it deserverves some presizing.
Some comments inline, but I need more time to read this ....
GunChleoc (gunchleoc) wrote : | # |
Thanks for taking on this review :)
* explian what this actually maps
It keeps rendered text so that we won't have to render it again - rendering is expensive, especially for complex stuff like the help windows. And some text images are reused a lot, e.g. the census and statistics strings.
* what is the cachek poliy (e.g. when will object be removed?)
Just like with the main image cache and the animation cache, objects are never removed. I guess we could have a flush function if the memory consumption should get too big and that could be called at strategic points, e.g. when switching between fullscreen menu and game.
* why do you use an (additional) std::unique_ptr if the make already makes this unique?
I don't know what you mean. Are you misreading make_pair as make_unique here?
* if its not small it deserverves some presizing.
You mean as is already done in texture_cache_(new TextureCache(
I have also added comments to your in-line comments.
GunChleoc (gunchleoc) wrote : | # |
@kaputtnik: Yes, that looks like the same crash.
GunChleoc (gunchleoc) wrote : | # |
I have now looked at the code for the TextureCache (which I didn't write) and it does indeed automatically drop the oldest entries to ensure that its size is not exceeded.
I have also added some asserts (to help track down the crash), and made the nullptr comparisons explicit (maybe this will make it go away).
Klaus Halfmann (klaus-halfmann) wrote : | # |
Gun: please add the comments in the code,
the next reader will not find it here :-)
What I did not get about the cache is:
For every Text (and width) it stores the Image to render it.
So If I open all dialogs in every language one after the other,
the cache will contain all the Images? Will this include
user generated strings as well? Such szenarios could make
the cache explode.
In general such cahes should me somewhat elastic:
* user LRU to drop old, rarely used entries
* reduze size when memory is getting low
(e.g. malloc starts to retun null)
Do you have some debugoutput about the size consumptions?
Is there some tescase that switches through all lanaguages
and open all dialogs?
P.S. I now got a newer, faster mac, but I needed today to migrate everything ...
Klaus Halfmann (klaus-halfmann) wrote : | # |
On tip about int <-> flot conversion:
As of modern processsors this is quiten expensive,
as this involves the otherwise decupled int and float computing unita.
e.g. Rectf(0.0f, 0.0f, width, height) would be better.
In this particular case the compiler should optimize this away of cause.
lets go on reading the code.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Checked the lower 50% of the chnages now, still not complete.
The code reads much better (some exceptions inline).
please comment what happens with scaling and in the chat window.
will try to play now with different scalings and check lots of dialogs
(english and german only).
Will the font cache be active inside a Gmae only of for the liftime of
the Programm?
kaputtnik (franku) wrote : | # |
Crashed again (r8200) after playing a while. Just followed an expedition ship exploring the world. Opened windows: Stock and Expedition. Unfortunately no asserts:
Autosave: save took 1591 ms
TrainingSite:
TrainingSite:
TrainingSite:
TrainingSite:
Thread 1 "widelands" received signal SIGSEGV, Segmentation fault.
0x0000000007abea00 in ?? ()
(gdb) bt
#0 0x0000000007abea00 in ?? ()
#1 0x0000000000cff86f in RenderTarget::blit (this=0x78078b0, dst=..., image=0x7abea18, blend_mode=
align=
#2 0x0000000000d4b86f in UI::RenderedTex
cropmode=
at /home/kaputtnik
#3 0x0000000000d4ba1c in UI::RenderedTex
at /home/kaputtnik
#4 0x0000000000f1f907 in UI::Window:
at /home/kaputtnik
#5 0x0000000000f010f9 in UI::Panel::do_draw (this=0x7d0b9e0, dst=...)
at /home/kaputtnik
#6 0x0000000000f01004 in UI::Panel:
at /home/kaputtnik
#7 0x0000000000f0119d in UI::Panel::do_draw (this=0x2331ee0, dst=...)
at /home/kaputtnik
#8 0x0000000000effe1e in UI::Panel::do_run (this=0x2331ee0)
at /home/kaputtnik
#9 0x0000000000c58af0 in UI::Panel:
GunChleoc (gunchleoc) wrote : | # |
If you click on "Show diff comments" for a post, the diff will switch to the appropriate revision so you can see them.
Answers to your comments inline - I added all important comments to the code as well. I will also replace the 0's with 0.f for the Rectf calls.
I think the caching model is generally broken and also the cause of the cache. The textures are kept in a cache that can overflow, so the RenderedText has references to objects that have been destroyed by the TextureCache. I think that I need to do the following 2 things:
- Turn the TextureCache into a templated object por mimic the code, so we will have an upper size limit for both the small textures within a text and the while RenderedText, and maximum reuse of computed textures at the same time,
- Replace unique_ptr with shared_ptr. This way, when an texture is removed from the cache, it won't be destroyed if a RenderedText is still using it. Furthermore, some UI elements prerender their texts and keep them for their lifetime, so those objects need to share ownership as well. I think the only reason that this particular problem hasn't blown up in our faces yet is that the texture cache never overflows in Build 19.
GunChleoc (gunchleoc) wrote : | # |
OK, this went faster than expected. We now have a templated class TransientCache that hands out shared_ptrs to everyone. It's a bit heavy in the header, but there was no obvious split into header/cc file because of the templating.
I have also added some log output when the cache overflows. I haven't seen a log message yet, but that might change once we move more stuff to the new renderer. The cache size can be tweaked then.
GunChleoc (gunchleoc) wrote : | # |
Added some more small tweaks, and this is hopefully done now. Let's get a new diff.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2237. State: passed. Details: https:/
Appveyor build 2072. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
OK, compile your code but will now go down to kTextureCacheSize = 5 << 20
or even further untill I find some logs and/or the cache flutters.
Now lets start reading again ...
Klaus Halfmann (klaus-halfmann) wrote : | # |
Checked the code from the top to about 50%, some comments inline.
Did not find the logging, yet.
wil now play a bit with the reduced cache size.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Ahh, now I see what the cache ist used for:
TransientCache: Dropping 2 bytes, new size 4094. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 2 bytes, new size 4092. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 2 bytes, new size 4090. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 2 bytes, new size 4088. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 2 bytes, new size 4086. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 1 bytes, new size 4085. Hash: 317<rt><p align=left><font face=sans size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 2 bytes, new size 4083. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 2 bytes, new size 4081. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 2 bytes, new size 4079. Hash: 0<rt><p align=left><font face=condensed size=14 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 4176 bytes, new size 5230440. Hash: ttf:DejaVu/
TransientCache: Dropping 4 bytes, new size 4091. Hash: 0<rt><p align=left><font face=sans size=13 bold=1 shadow=1 color=ffff00>
TransientCache: Dropping 3744 bytes, new size 5236416. Hash: ttf:DejaVu/
So this is the gametime, the fps display and such.
Is this wise?
Do you have a chache hit ratio computed somewhere?
Me will ponder this the next days :-)
kaputtnik (franku) wrote : | # |
This looks really good now :-)
Played an hour or so but no crash anymore. The output of 'new size' is a bit annoying, i guess it switches between Kilobytes and Bytes (?):
TransientCache: Dropping 11 bytes, new size 4082. [...]
TransientCache: Dropping 4032 bytes, new size 31452972. [...]
[...]
TransientCache: Dropping 4 bytes, new size 4092.
But it never exceeds the size of 4093 (as far as i can see).
There are a lot of such outputs if you just move the mouse around, also moving the mouse on black areas. Don't know if this much processor time consuming. Maybe here is a chance of optimization?
kaputtnik (franku) wrote : | # |
Ah, forgotton:
There is still the compiler warning about the unused variable 'maximum_size' as decribed in my first post here.
GunChleoc (gunchleoc) wrote : | # |
I have now removed all the temporary testing stuff, so the compiler warning and the "TransientCache: Dropping" output should be gone now.
I have only commented out the log output in "graphic/
From my testing, the current cache sizes allow for loading 22 maps in the map selection screen before they start dropping entries. In-game dropping of textures only started after quite a lot of text was generated by mousing over things. Typing 500+ characters in a Multilineeditbox did not trigger texture dropping.
Klaus Halfmann (klaus-halfmann) wrote : | # |
I got some compile warning we should not neglect:
fh1-multitextur
destructor [-Wnon-
in general when subclassing and using a parent pointer the correct Destructor will not be called.
fh1-multitextur
[
struct FullscreenMenuL
^
fh1-multitextur
class FullscreenMenuL
fh1-multitextur
friend struct Deserializer;
^
fh1-multitextur
class Deserializer;
Are there functions limited to float only?
fh1-multitextur
scale = std::round(2 * (scale > 1.f ? std::sqrt(scale) : std::pow(scale, 2))) / 2;
No idea about these, SirVer may tell us?
fh1-multitextur
fh1-multitextur
Sorry no time to check more code tonight, will just play that branch a bit ....
GunChleoc (gunchleoc) wrote : | # |
Thanks for the review! I have hopefully fixed the compiler warnings now - we'll just have to wait what clang says.
I also addressed your points from a few posts up and added some more documentation to the RendererText header file - I hope it will make the code a bit more clear. Yes, it's a bit complicated due to the cropping involved.
2 notes:
1. CropMode::kVertical doesn't exist because CropMode:
2. Re. better replace true / false by kBackGroundColorSet and !kBackGroundCol
GunChleoc (gunchleoc) wrote : | # |
> Do you have a chache hit ratio computed somewhere?
No, I just used the log output to experiment a bit to find an acceptable value. Since we have 2 caches now, I made one really big while testing the other and vice versa.
BTW the gametime being kicked out is perfectly OK, since it counts up, so the old gametime texture definitely isn't needed any more. The cache also bumps entries when they are accessed, so those textures that have gone the longest without being used are the first to be removed when it overflows.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Thx, the warning are gone, we still have some, but I will addres them in the next branch,
so we get get this in.
Will it be possible to exclude some Texts/Textures from Caching?
e.g. the current game time or any other text we know will change the next time we draw it.
will not have time for more code rewiew till perhpas the weekend.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Now I got a crash after playing for perhaps 10 Minutes?
Thread 0 Crashed:: Dispatch queue: com.apple.
0 widelands 0x00000001042abba5 Widelands:
1 widelands 0x00000001042bc8c2 Widelands:
2 widelands 0x00000001042becad Widelands:
3 widelands 0x0000000103bb77d9 Widelands:
4 widelands 0x0000000103bb72cb Widelands:
5 widelands 0x0000000103b5cb51 Widelands:
Uhm.. perhpas I can reproduce that one?
Klaus Halfmann (klaus-halfmann) wrote : | # |
playes another on > 1h, no Problems. so far for determistic machines.
GunChleoc (gunchleoc) wrote : | # |
The newest crash looks like it's coming from WareInstance:
GunChleoc (gunchleoc) wrote : | # |
> Will it be possible to exclude some Texts/Textures from Caching?
I think that would be overkill. This would mean carrying a parameter through quite a lot of functions until it reaches the point where it's checked for, for very little gain.
Let's say our cache looks like this:
A - B - C - D
Then we need B again, this will make the cache like like this:
A - C - D - B
Then let's say it's full and we add something new, the cache will now look like this:
C - D - B - E
D - B - E - F
So, the frequently-used B has not been dropped yet, although C was originally generated after B was generated.
I think this is efficient enough for reuse.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Will we need a resubmit fro travis to kick in?
Looks I now have time to do a full review and some Debugging.
Klaus Halfmann (klaus-halfmann) wrote : | # |
OK for me now, we should get this in and try to find those nasty crashes in trunk.
I still do not get the whole story, though. Would like to see bunnybot again.
Will continue with that net-boost-asio next.
GunChleoc (gunchleoc) wrote : | # |
As long as both Travis and Appveyor continue to be green, bunnybot will not post here again. SirVer stopped that, because it was too spammy.
Bunnybot will refuse to merge though if Travis and Appyeyor aren't both green.
Thanks for the testing and review! I know this was a complicated one.
@bunnybot merge
Preview Diff
1 | === modified file 'src/editor/ui_menus/main_menu_map_options.cc' |
2 | --- src/editor/ui_menus/main_menu_map_options.cc 2017-05-13 11:25:24 +0000 |
3 | +++ src/editor/ui_menus/main_menu_map_options.cc 2017-05-23 21:33:09 +0000 |
4 | @@ -46,9 +46,7 @@ |
5 | : UI::Window(&parent, "map_options", 0, 0, 350, parent.get_inner_h() - 80, _("Map Options")), |
6 | padding_(4), |
7 | indent_(10), |
8 | - labelh_( |
9 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + |
10 | - 4), |
11 | + labelh_(text_height() + 4), |
12 | checkbox_space_(25), |
13 | butw_((get_inner_w() - 3 * padding_) / 2), |
14 | max_w_(get_inner_w() - 2 * padding_), |
15 | |
16 | === modified file 'src/editor/ui_menus/main_menu_random_map.cc' |
17 | --- src/editor/ui_menus/main_menu_random_map.cc 2017-05-13 11:25:24 +0000 |
18 | +++ src/editor/ui_menus/main_menu_random_map.cc 2017-05-23 21:33:09 +0000 |
19 | @@ -52,9 +52,7 @@ |
20 | // UI elements |
21 | margin_(4), |
22 | box_width_(get_inner_w() - 2 * margin_), |
23 | - label_height_( |
24 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + |
25 | - 2), |
26 | + label_height_(text_height() + 2), |
27 | box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_), |
28 | // Size |
29 | width_(&box_, |
30 | |
31 | === modified file 'src/editor/ui_menus/tool_set_terrain_options_menu.cc' |
32 | --- src/editor/ui_menus/tool_set_terrain_options_menu.cc 2017-05-13 11:25:24 +0000 |
33 | +++ src/editor/ui_menus/tool_set_terrain_options_menu.cc 2017-05-23 21:33:09 +0000 |
34 | @@ -53,8 +53,8 @@ |
35 | // Blit the main terrain image |
36 | const Image& terrain_texture = terrain_descr.get_texture(0); |
37 | Texture* texture = new Texture(terrain_texture.width(), terrain_texture.height()); |
38 | - texture->blit(Rectf(0, 0, terrain_texture.width(), terrain_texture.height()), terrain_texture, |
39 | - Rectf(0, 0, terrain_texture.width(), terrain_texture.height()), 1., |
40 | + texture->blit(Rectf(0.f, 0.f, terrain_texture.width(), terrain_texture.height()), terrain_texture, |
41 | + Rectf(0.f, 0.f, terrain_texture.width(), terrain_texture.height()), 1., |
42 | BlendMode::UseAlpha); |
43 | Vector2i pt(1, terrain_texture.height() - kSmallPicSize - 1); |
44 | |
45 | @@ -66,7 +66,7 @@ |
46 | |
47 | texture->blit(Rectf(pt.x, pt.y, terrain_type.icon->width(), terrain_type.icon->height()), |
48 | *terrain_type.icon, |
49 | - Rectf(0, 0, terrain_type.icon->width(), terrain_type.icon->height()), 1., |
50 | + Rectf(0.f, 0.f, terrain_type.icon->width(), terrain_type.icon->height()), 1., |
51 | BlendMode::UseAlpha); |
52 | pt.x += kSmallPicSize + 1; |
53 | } |
54 | |
55 | === modified file 'src/graphic/CMakeLists.txt' |
56 | --- src/graphic/CMakeLists.txt 2017-04-26 17:11:43 +0000 |
57 | +++ src/graphic/CMakeLists.txt 2017-05-23 21:33:09 +0000 |
58 | @@ -109,8 +109,6 @@ |
59 | surface.h |
60 | texture.cc |
61 | texture.h |
62 | - texture_cache.cc |
63 | - texture_cache.h |
64 | USES_SDL2 |
65 | DEPENDS |
66 | base_exceptions |
67 | @@ -268,16 +266,9 @@ |
68 | font_handler1.cc |
69 | font_handler1.h |
70 | DEPENDS |
71 | - base_exceptions |
72 | - base_geometry |
73 | - base_log |
74 | base_macros |
75 | - graphic |
76 | - graphic_align |
77 | graphic_image_cache |
78 | - graphic_surface |
79 | graphic_text |
80 | - io_filesystem |
81 | ) |
82 | |
83 | wl_library(graphic_text_constants |
84 | |
85 | === modified file 'src/graphic/animation.cc' |
86 | --- src/graphic/animation.cc 2017-05-19 06:05:40 +0000 |
87 | +++ src/graphic/animation.cc 2017-05-23 21:33:09 +0000 |
88 | @@ -227,7 +227,8 @@ |
89 | const int w = image->width(); |
90 | const int h = image->height(); |
91 | Texture* rv = new Texture(w / scale_, h / scale_); |
92 | - rv->blit(Rectf(0, 0, w / scale_, h / scale_), *image, Rectf(0, 0, w, h), 1., BlendMode::Copy); |
93 | + rv->blit( |
94 | + Rectf(0.f, 0.f, w / scale_, h / scale_), *image, Rectf(0.f, 0.f, w, h), 1., BlendMode::Copy); |
95 | return rv; |
96 | } |
97 | |
98 | |
99 | === modified file 'src/graphic/build_texture_atlas.cc' |
100 | --- src/graphic/build_texture_atlas.cc 2017-01-25 18:55:59 +0000 |
101 | +++ src/graphic/build_texture_atlas.cc 2017-05-23 21:33:09 +0000 |
102 | @@ -39,10 +39,6 @@ |
103 | // threshold, but not background pictures. |
104 | constexpr int kMaxAreaForTextureAtlas = 240 * 240; |
105 | |
106 | -// A graphics card must at least support this size for texture for Widelands to |
107 | -// run. |
108 | -constexpr int kMinimumSizeForTextures = 2048; |
109 | - |
110 | // Returns true if 'filename' ends with an image extension. |
111 | bool is_image(const std::string& filename) { |
112 | return boost::ends_with(filename, ".png") || boost::ends_with(filename, ".jpg"); |
113 | |
114 | === modified file 'src/graphic/font_handler1.cc' |
115 | --- src/graphic/font_handler1.cc 2017-03-25 15:32:49 +0000 |
116 | +++ src/graphic/font_handler1.cc 2017-05-23 21:33:09 +0000 |
117 | @@ -19,88 +19,34 @@ |
118 | |
119 | #include "graphic/font_handler1.h" |
120 | |
121 | -#include <functional> |
122 | #include <memory> |
123 | |
124 | #include <boost/lexical_cast.hpp> |
125 | -#include <boost/utility.hpp> |
126 | |
127 | -#include "base/log.h" |
128 | -#include "base/wexception.h" |
129 | -#include "graphic/graphic.h" |
130 | -#include "graphic/image.h" |
131 | -#include "graphic/image_cache.h" |
132 | -#include "graphic/rendertarget.h" |
133 | -#include "graphic/text/rt_errors.h" |
134 | #include "graphic/text/rt_render.h" |
135 | -#include "graphic/text/sdl_ttf_font.h" |
136 | -#include "graphic/texture.h" |
137 | -#include "graphic/texture_cache.h" |
138 | -#include "io/filesystem/filesystem.h" |
139 | - |
140 | -using namespace std; |
141 | -using namespace boost; |
142 | +#include "graphic/text/texture_cache.h" |
143 | |
144 | namespace { |
145 | |
146 | -// The size of the richtext surface cache in bytes. All work that the richtext |
147 | -// renderer does is / cached in this cache until it overflows. The idea is that |
148 | -// this is big enough to cache the text that is used on a typical screen - so |
149 | -// that we do not need to lay out text every frame. Last benchmarked at r7712, |
150 | -// 30 MB was enough to cache texts for many frames (> 1000), while it is |
151 | -// quickly overflowing in the map selection menu. |
152 | -// This might need reevaluation is the new font handler is used for more stuff. |
153 | -const uint32_t RICHTEXT_TEXTURE_CACHE = 30 << 20; // shifting converts to MB |
154 | - |
155 | -// An Image implementation that recreates a rich text texture when needed on |
156 | -// the fly. It is meant to be saved into the ImageCache. |
157 | -class RTImage : public Image { |
158 | -public: |
159 | - RTImage(const string& ghash, |
160 | - TextureCache* texture_cache, |
161 | - std::function<RT::Renderer*()> get_renderer, |
162 | - const string& text, |
163 | - int gwidth) |
164 | - : hash_(ghash), |
165 | - text_(text), |
166 | - width_(gwidth), |
167 | - get_renderer_(get_renderer), |
168 | - texture_cache_(texture_cache) { |
169 | - } |
170 | - virtual ~RTImage() { |
171 | - } |
172 | - |
173 | - // Implements Image. |
174 | - int width() const override { |
175 | - return texture()->width(); |
176 | - } |
177 | - int height() const override { |
178 | - return texture()->height(); |
179 | - } |
180 | - |
181 | - const BlitData& blit_data() const override { |
182 | - return texture()->blit_data(); |
183 | - } |
184 | - |
185 | -private: |
186 | - Texture* texture() const { |
187 | - Texture* surf = texture_cache_->get(hash_); |
188 | - if (surf != nullptr) { |
189 | - return surf; |
190 | - } |
191 | - return texture_cache_->insert( |
192 | - hash_, std::unique_ptr<Texture>(get_renderer_()->render(text_, width_))); |
193 | - } |
194 | - |
195 | - const string hash_; |
196 | - const string text_; |
197 | - int width_; |
198 | - std::function<RT::Renderer*()> get_renderer_; |
199 | - |
200 | - // Nothing owned. |
201 | - TextureCache* const texture_cache_; |
202 | -}; |
203 | -} |
204 | +// The CacheSize constexpr values control the cache sizes for our transient caches. |
205 | +// The idea is that they are big enough to cache the text that is used on a typical screen - so |
206 | +// that we do not need to lay out text every frame. |
207 | + |
208 | +// TODO(GunChleoc): Re-evaulate the cache sizes once the in-game help and the campaigns have been |
209 | +// converted to this font renderer. |
210 | +// The current cache sizes allow for loading 22 maps in the map selection screen before they start |
211 | +// dropping entries. In-game dropping of textures only started after quite a lot of text was |
212 | +// generated by mousing over things. Typing 500+ characters in a Multilineeditbox did not trigger |
213 | +// texture dropping. |
214 | + |
215 | +// The size of the richtext surface cache in bytes. |
216 | +constexpr uint32_t kTextureCacheSize = 3 << 20; // shifting by 20 converts to MB |
217 | + |
218 | +// The maximum number of RenderedRects. It's all pointers or combinations of basic data types, so |
219 | +// the size requirement is pretty constant. Therefore, simply counting them is sufficient. |
220 | +// We estimate that the member variables of each RenderedRect take up ca. 13 * 32 bytes. |
221 | +constexpr uint32_t kRenderCacheSize = 8 * 1024; |
222 | +} // namespace |
223 | |
224 | namespace UI { |
225 | |
226 | @@ -108,9 +54,24 @@ |
227 | // the ImageCache, so repeated calls to render with the same arguments should not |
228 | // be a problem. |
229 | class FontHandler1 : public IFontHandler1 { |
230 | +private: |
231 | + // A transient cache for the generated rendered texts |
232 | + class RenderCache : public TransientCache<RenderedText> { |
233 | + public: |
234 | + RenderCache(uint32_t max_number_of_rects) |
235 | + : TransientCache<RenderedText>(max_number_of_rects) { |
236 | + } |
237 | + |
238 | + std::shared_ptr<const RenderedText> insert(const std::string& hash, |
239 | + std::shared_ptr<const RenderedText> entry) override { |
240 | + return TransientCache<RenderedText>::insert(hash, entry, entry->rects.size()); |
241 | + } |
242 | + }; |
243 | + |
244 | public: |
245 | FontHandler1(ImageCache* image_cache, const std::string& locale) |
246 | - : texture_cache_(new TextureCache(RICHTEXT_TEXTURE_CACHE)), |
247 | + : texture_cache_(new TextureCache(kTextureCacheSize)), |
248 | + render_cache_(new RenderCache(kRenderCacheSize)), |
249 | fontsets_(), |
250 | fontset_(fontsets_.get_fontset(locale)), |
251 | rt_renderer_(new RT::Renderer(image_cache, texture_cache_.get(), fontsets_)), |
252 | @@ -119,17 +80,16 @@ |
253 | virtual ~FontHandler1() { |
254 | } |
255 | |
256 | - const Image* render(const string& text, uint16_t w = 0) override { |
257 | - const string hash = boost::lexical_cast<string>(w) + text; |
258 | - |
259 | - if (image_cache_->has(hash)) |
260 | - return image_cache_->get(hash); |
261 | - |
262 | - std::unique_ptr<RTImage> image( |
263 | - new RTImage(hash, texture_cache_.get(), [this] { return rt_renderer_.get(); }, text, w)); |
264 | - image->width(); // force the rich text to get rendered in case there is an exception thrown. |
265 | - |
266 | - return image_cache_->insert(hash, std::move(image)); |
267 | + // This will render the 'text' with a width restriction of 'w'. If 'w' == 0, no restriction is |
268 | + // applied. |
269 | + std::shared_ptr<const UI::RenderedText> render(const std::string& text, |
270 | + uint16_t w = 0) override { |
271 | + const std::string hash = boost::lexical_cast<std::string>(w) + text; |
272 | + std::shared_ptr<const RenderedText> rendered_text = render_cache_->get(hash); |
273 | + if (rendered_text.get() == nullptr) { |
274 | + rendered_text = render_cache_->insert(hash, rt_renderer_->render(text, w)); |
275 | + } |
276 | + return rendered_text; |
277 | } |
278 | |
279 | UI::FontSet const* fontset() const override { |
280 | @@ -138,12 +98,14 @@ |
281 | |
282 | void reinitialize_fontset(const std::string& locale) override { |
283 | fontset_ = fontsets_.get_fontset(locale); |
284 | - texture_cache_.get()->flush(); |
285 | + texture_cache_->flush(); |
286 | + render_cache_->flush(); |
287 | rt_renderer_.reset(new RT::Renderer(image_cache_, texture_cache_.get(), fontsets_)); |
288 | } |
289 | |
290 | private: |
291 | std::unique_ptr<TextureCache> texture_cache_; |
292 | + std::unique_ptr<RenderCache> render_cache_; |
293 | UI::FontSets fontsets_; // All fontsets |
294 | UI::FontSet const* fontset_; // The currently active FontSet |
295 | std::unique_ptr<RT::Renderer> rt_renderer_; |
296 | |
297 | === modified file 'src/graphic/font_handler1.h' |
298 | --- src/graphic/font_handler1.h 2017-02-28 20:07:07 +0000 |
299 | +++ src/graphic/font_handler1.h 2017-05-23 21:33:09 +0000 |
300 | @@ -22,16 +22,11 @@ |
301 | #define WL_GRAPHIC_FONT_HANDLER1_H |
302 | |
303 | #include <memory> |
304 | -#include <string> |
305 | |
306 | #include "base/macros.h" |
307 | -#include "base/vector.h" |
308 | -#include "graphic/align.h" |
309 | +#include "graphic/image_cache.h" |
310 | #include "graphic/text/font_set.h" |
311 | - |
312 | -class FileSystem; |
313 | -class Image; |
314 | -class ImageCache; |
315 | +#include "graphic/text/rendered_text.h" |
316 | |
317 | namespace UI { |
318 | |
319 | @@ -44,11 +39,10 @@ |
320 | virtual ~IFontHandler1() { |
321 | } |
322 | |
323 | - /* |
324 | - * Renders the given text into an image. The image is cached and therefore |
325 | - * ownership remains with this class. Will throw on error. |
326 | - */ |
327 | - virtual const Image* render(const std::string& text, uint16_t w = 0) = 0; |
328 | + /// Renders the given text into a set of images. The images are cached in a transient cache, |
329 | + /// so we share the ownership. Will throw on error. |
330 | + virtual std::shared_ptr<const UI::RenderedText> render(const std::string& text, |
331 | + uint16_t w = 0) = 0; |
332 | |
333 | /// Returns the font handler's current FontSet |
334 | virtual UI::FontSet const* fontset() const = 0; |
335 | @@ -61,7 +55,7 @@ |
336 | DISALLOW_COPY_AND_ASSIGN(IFontHandler1); |
337 | }; |
338 | |
339 | -// Create a new FontHandler1. |
340 | +/// Create a new FontHandler1. |
341 | IFontHandler1* create_fonthandler(ImageCache* image_cache, const std::string& locale); |
342 | |
343 | extern IFontHandler1* g_fh1; |
344 | |
345 | === modified file 'src/graphic/graphic.h' |
346 | --- src/graphic/graphic.h 2017-01-25 18:55:59 +0000 |
347 | +++ src/graphic/graphic.h 2017-05-23 21:33:09 +0000 |
348 | @@ -33,6 +33,10 @@ |
349 | class Screen; |
350 | class StreamWrite; |
351 | |
352 | +// A graphics card must at least support this size for texture for Widelands to |
353 | +// run. |
354 | +constexpr int kMinimumSizeForTextures = 2048; |
355 | + |
356 | // Will be send whenever the resolution changes. |
357 | struct GraphicResolutionChanged { |
358 | CAN_BE_SENT_AS_NOTE(NoteId::GraphicResolutionChanged) |
359 | |
360 | === modified file 'src/graphic/minimap_renderer.cc' |
361 | --- src/graphic/minimap_renderer.cc 2017-05-13 11:25:24 +0000 |
362 | +++ src/graphic/minimap_renderer.cc 2017-05-23 21:33:09 +0000 |
363 | @@ -237,7 +237,8 @@ |
364 | |
365 | std::unique_ptr<Texture> texture(new Texture(map_w, map_h)); |
366 | |
367 | - texture->fill_rect(Rectf(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255)); |
368 | + texture->fill_rect( |
369 | + Rectf(0.f, 0.f, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255)); |
370 | |
371 | // Center the view on the middle of the 'view_area'. |
372 | const bool zoom = layers & MiniMapLayer::Zoom2; |
373 | |
374 | === modified file 'src/graphic/playercolor.cc' |
375 | --- src/graphic/playercolor.cc 2017-05-15 11:36:22 +0000 |
376 | +++ src/graphic/playercolor.cc 2017-05-23 21:33:09 +0000 |
377 | @@ -49,8 +49,8 @@ |
378 | const int w = image->width(); |
379 | const int h = image->height(); |
380 | Texture* pc_image = new Texture(w, h); |
381 | - pc_image->fill_rect(Rectf(0, 0, w, h), RGBAColor(0, 0, 0, 0)); |
382 | - pc_image->blit_blended(Rectf(0, 0, w, h), *image, *color_mask, Rectf(0, 0, w, h), clr); |
383 | + pc_image->fill_rect(Rectf(0.f, 0.f, w, h), RGBAColor(0, 0, 0, 0)); |
384 | + pc_image->blit_blended(Rectf(0.f, 0.f, w, h), *image, *color_mask, Rectf(0.f, 0.f, w, h), clr); |
385 | g_gr->images().insert(hash, std::unique_ptr<const Texture>(std::move(pc_image))); |
386 | assert(g_gr->images().has(hash)); |
387 | return g_gr->images().get(hash); |
388 | |
389 | === modified file 'src/graphic/rendertarget.cc' |
390 | --- src/graphic/rendertarget.cc 2017-05-13 18:48:26 +0000 |
391 | +++ src/graphic/rendertarget.cc 2017-05-23 21:33:09 +0000 |
392 | @@ -23,6 +23,7 @@ |
393 | #include "graphic/animation.h" |
394 | #include "graphic/graphic.h" |
395 | #include "graphic/surface.h" |
396 | +#include "graphic/text_layout.h" |
397 | |
398 | /** |
399 | * Build a render target for the given surface. |
400 | @@ -142,9 +143,16 @@ |
401 | * |
402 | * This blit function copies the pixels to the destination surface. |
403 | */ |
404 | -void RenderTarget::blit(const Vector2i& dst, const Image* image, BlendMode blend_mode) { |
405 | - Rectf source_rect(Vector2i::zero(), image->width(), image->height()); |
406 | - Rectf destination_rect(dst.x, dst.y, source_rect.w, source_rect.h); |
407 | +void RenderTarget::blit(const Vector2i& dst, |
408 | + const Image* image, |
409 | + BlendMode blend_mode, |
410 | + UI::Align align) { |
411 | + assert(image != nullptr); |
412 | + Vector2i destination_point(dst); |
413 | + UI::correct_for_align(align, image->width(), &destination_point); |
414 | + |
415 | + Rectf source_rect(0.f, 0.f, image->width(), image->height()); |
416 | + Rectf destination_rect(destination_point.x, destination_point.y, source_rect.w, source_rect.h); |
417 | |
418 | if (to_surface_geometry(&destination_rect, &source_rect)) { |
419 | // I seem to remember seeing 1. a lot in blitting calls. |
420 | @@ -155,9 +163,13 @@ |
421 | |
422 | void RenderTarget::blit_monochrome(const Vector2i& dst, |
423 | const Image* image, |
424 | - const RGBAColor& blend_mode) { |
425 | - Rectf source_rect(Vector2i::zero(), image->width(), image->height()); |
426 | - Rectf destination_rect(dst.x, dst.y, source_rect.w, source_rect.h); |
427 | + const RGBAColor& blend_mode, |
428 | + UI::Align align) { |
429 | + Vector2i destination_point(dst); |
430 | + UI::correct_for_align(align, image->width(), &destination_point); |
431 | + |
432 | + Rectf source_rect(0.f, 0.f, image->width(), image->height()); |
433 | + Rectf destination_rect(destination_point.x, destination_point.y, source_rect.w, source_rect.h); |
434 | |
435 | if (to_surface_geometry(&destination_rect, &source_rect)) { |
436 | surface_->blit_monochrome(destination_rect, *image, source_rect, blend_mode); |
437 | |
438 | === modified file 'src/graphic/rendertarget.h' |
439 | --- src/graphic/rendertarget.h 2017-05-13 18:48:26 +0000 |
440 | +++ src/graphic/rendertarget.h 2017-05-23 21:33:09 +0000 |
441 | @@ -62,10 +62,16 @@ |
442 | void fill_rect(const Recti&, const RGBAColor&, BlendMode blend_mode = BlendMode::Copy); |
443 | void brighten_rect(const Recti&, int32_t factor); |
444 | |
445 | - void blit(const Vector2i& dst, const Image* image, BlendMode blend_mode = BlendMode::UseAlpha); |
446 | + void blit(const Vector2i& dst, |
447 | + const Image* image, |
448 | + BlendMode blend_mode = BlendMode::UseAlpha, |
449 | + UI::Align = UI::Align::kLeft); |
450 | |
451 | // Like blit. See MonochromeBlitProgram for details. |
452 | - void blit_monochrome(const Vector2i& dst, const Image* image, const RGBAColor& blend_mode); |
453 | + void blit_monochrome(const Vector2i& dst, |
454 | + const Image* image, |
455 | + const RGBAColor& blend_mode, |
456 | + UI::Align = UI::Align::kLeft); |
457 | |
458 | void blitrect(const Vector2i& dst, |
459 | const Image* image, |
460 | |
461 | === modified file 'src/graphic/text/CMakeLists.txt' |
462 | --- src/graphic/text/CMakeLists.txt 2017-03-29 12:36:20 +0000 |
463 | +++ src/graphic/text/CMakeLists.txt 2017-05-23 21:33:09 +0000 |
464 | @@ -14,10 +14,14 @@ |
465 | rt_parse.h |
466 | rt_render.cc |
467 | rt_render.h |
468 | + rendered_text.cc |
469 | + rendered_text.h |
470 | sdl_ttf_font.cc |
471 | sdl_ttf_font.h |
472 | textstream.cc |
473 | textstream.h |
474 | + texture_cache.h |
475 | + transient_cache.h |
476 | USES_SDL2 |
477 | USES_SDL2_TTF |
478 | USES_ICU |
479 | |
480 | === added file 'src/graphic/text/rendered_text.cc' |
481 | --- src/graphic/text/rendered_text.cc 1970-01-01 00:00:00 +0000 |
482 | +++ src/graphic/text/rendered_text.cc 2017-05-23 21:33:09 +0000 |
483 | @@ -0,0 +1,266 @@ |
484 | +/* |
485 | + * Copyright (C) 2017 by the Widelands Development Team |
486 | + * |
487 | + * This program is free software; you can redistribute it and/or |
488 | + * modify it under the terms of the GNU General Public License |
489 | + * as published by the Free Software Foundation; either version 2 |
490 | + * of the License, or (at your option) any later version. |
491 | + * |
492 | + * This program is distributed in the hope that it will be useful, |
493 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
494 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
495 | + * GNU General Public License for more details. |
496 | + * |
497 | + * You should have received a copy of the GNU General Public License |
498 | + * along with this program; if not, write to the Free Software |
499 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
500 | + * |
501 | + */ |
502 | + |
503 | +#include "graphic/text/rendered_text.h" |
504 | + |
505 | +#include <memory> |
506 | + |
507 | +#include "graphic/graphic.h" |
508 | +#include "graphic/text_layout.h" |
509 | + |
510 | +namespace UI { |
511 | +// RenderedRect |
512 | +RenderedRect::RenderedRect(const Recti& init_rect, |
513 | + std::shared_ptr<const Image> init_image, |
514 | + bool visited, |
515 | + const RGBColor& color, |
516 | + bool is_background_color_set, |
517 | + DrawMode init_mode) |
518 | + : rect_(init_rect), |
519 | + transient_image_(init_image), |
520 | + permanent_image_(nullptr), |
521 | + visited_(visited), |
522 | + background_color_(color), |
523 | + is_background_color_set_(is_background_color_set), |
524 | + mode_(init_mode) { |
525 | +} |
526 | +RenderedRect::RenderedRect(const Recti& init_rect, |
527 | + const Image* init_image, |
528 | + bool visited, |
529 | + const RGBColor& color, |
530 | + bool is_background_color_set, |
531 | + DrawMode init_mode) |
532 | + : rect_(init_rect), |
533 | + transient_image_(nullptr), |
534 | + permanent_image_(init_image), |
535 | + visited_(visited), |
536 | + background_color_(color), |
537 | + is_background_color_set_(is_background_color_set), |
538 | + mode_(init_mode) { |
539 | +} |
540 | + |
541 | +RenderedRect::RenderedRect(const Recti& init_rect, const Image* init_image) |
542 | + : RenderedRect(init_rect, init_image, false, RGBColor(0, 0, 0), false, DrawMode::kTile) { |
543 | +} |
544 | +RenderedRect::RenderedRect(const Recti& init_rect, const RGBColor& color) |
545 | + : RenderedRect(init_rect, nullptr, false, color, true, DrawMode::kTile) { |
546 | +} |
547 | +RenderedRect::RenderedRect(std::shared_ptr<const Image> init_image) |
548 | + : RenderedRect(Recti(0, 0, init_image->width(), init_image->height()), |
549 | + init_image, |
550 | + false, |
551 | + RGBColor(0, 0, 0), |
552 | + false, |
553 | + DrawMode::kBlit) { |
554 | +} |
555 | +RenderedRect::RenderedRect(const Image* init_image) |
556 | + : RenderedRect(Recti(0, 0, init_image->width(), init_image->height()), |
557 | + init_image, |
558 | + false, |
559 | + RGBColor(0, 0, 0), |
560 | + false, |
561 | + DrawMode::kBlit) { |
562 | +} |
563 | + |
564 | +const Image* RenderedRect::image() const { |
565 | + assert(permanent_image_ == nullptr || transient_image_.get() == nullptr); |
566 | + return permanent_image_ == nullptr ? transient_image_.get() : permanent_image_; |
567 | +} |
568 | + |
569 | +int RenderedRect::x() const { |
570 | + return rect_.x; |
571 | +} |
572 | + |
573 | +int RenderedRect::y() const { |
574 | + return rect_.y; |
575 | +} |
576 | + |
577 | +int RenderedRect::width() const { |
578 | + return rect_.w; |
579 | +} |
580 | +int RenderedRect::height() const { |
581 | + return rect_.h; |
582 | +} |
583 | + |
584 | +void RenderedRect::set_origin(const Vector2i& new_origin) { |
585 | + rect_.x = new_origin.x; |
586 | + rect_.y = new_origin.y; |
587 | +} |
588 | +void RenderedRect::set_visited() { |
589 | + visited_ = true; |
590 | +} |
591 | +bool RenderedRect::was_visited() const { |
592 | + return visited_; |
593 | +} |
594 | + |
595 | +bool RenderedRect::has_background_color() const { |
596 | + return is_background_color_set_; |
597 | +} |
598 | +const RGBColor& RenderedRect::background_color() const { |
599 | + return background_color_; |
600 | +} |
601 | + |
602 | +RenderedRect::DrawMode RenderedRect::mode() const { |
603 | + return mode_; |
604 | +} |
605 | + |
606 | +// RenderedText |
607 | +int RenderedText::width() const { |
608 | + int result = 0; |
609 | + for (const auto& rect : rects) { |
610 | + result = std::max(result, rect->x() + rect->width()); |
611 | + } |
612 | + return result; |
613 | +} |
614 | +int RenderedText::height() const { |
615 | + int result = 0; |
616 | + for (const auto& rect : rects) { |
617 | + result = std::max(result, rect->y() + rect->height()); |
618 | + } |
619 | + return result; |
620 | +} |
621 | + |
622 | +void RenderedText::draw(RenderTarget& dst, |
623 | + const Vector2i& position, |
624 | + const Recti& region, |
625 | + Align align, |
626 | + CropMode cropmode) const { |
627 | + |
628 | + // Un-const the position and adjust for alignment according to region width |
629 | + Vector2i aligned_pos(position.x, position.y); |
630 | + |
631 | + // For cropping images that don't fit |
632 | + int offset_x = 0; |
633 | + if (cropmode == CropMode::kSelf) { |
634 | + UI::correct_for_align(align, width(), &aligned_pos); |
635 | + if (align != UI::Align::kLeft) { |
636 | + for (const auto& rect : rects) { |
637 | + offset_x = std::min(region.w - rect->width(), offset_x); |
638 | + } |
639 | + if (align == UI::Align::kCenter) { |
640 | + offset_x /= 2; |
641 | + } |
642 | + } |
643 | + } else { |
644 | + aligned_pos.x -= region.x; |
645 | + aligned_pos.y -= region.y; |
646 | + UI::correct_for_align(align, region.w, &aligned_pos); |
647 | + } |
648 | + |
649 | + // Blit the rects |
650 | + for (const auto& rect : rects) { |
651 | + blit_rect(dst, offset_x, aligned_pos, *rect.get(), region, align, cropmode); |
652 | + } |
653 | +} |
654 | + |
655 | +void RenderedText::blit_rect(RenderTarget& dst, |
656 | + int offset_x, |
657 | + const Vector2i& aligned_position, |
658 | + const RenderedRect& rect, |
659 | + const Recti& region, |
660 | + Align align, |
661 | + CropMode cropmode) const { |
662 | + const Vector2i blit_point(aligned_position.x + rect.x(), aligned_position.y + rect.y()); |
663 | + |
664 | + // Draw Solid background Color |
665 | + if (rect.has_background_color()) { |
666 | +#ifndef NDEBUG |
667 | + const int maximum_size = kMinimumSizeForTextures; |
668 | +#else |
669 | + const int maximum_size = g_gr->max_texture_size(); |
670 | +#endif |
671 | + const int tile_width = std::min(maximum_size, rect.width()); |
672 | + const int tile_height = std::min(maximum_size, rect.height()); |
673 | + for (int tile_x = blit_point.x; tile_x + tile_width <= blit_point.x + rect.width(); |
674 | + tile_x += tile_width) { |
675 | + for (int tile_y = blit_point.y; tile_y + tile_height <= blit_point.y + rect.height(); |
676 | + tile_y += tile_height) { |
677 | + dst.fill_rect(Recti(tile_x, tile_y, tile_width, tile_height), rect.background_color()); |
678 | + } |
679 | + } |
680 | + } |
681 | + |
682 | + if (rect.image() != nullptr) { |
683 | + switch (rect.mode()) { |
684 | + // Draw a foreground texture |
685 | + case RenderedRect::DrawMode::kBlit: { |
686 | + switch (cropmode) { |
687 | + case CropMode::kRenderTarget: |
688 | + // dst will handle any cropping |
689 | + dst.blit(blit_point, rect.image()); |
690 | + break; |
691 | + case CropMode::kSelf: |
692 | + blit_cropped(dst, offset_x, aligned_position, blit_point, rect, region, align); |
693 | + } |
694 | + } break; |
695 | + // Draw a background image (tiling) |
696 | + case RenderedRect::DrawMode::kTile: |
697 | + dst.tile(Recti(blit_point, rect.width(), rect.height()), rect.image(), Vector2i::zero()); |
698 | + break; |
699 | + } |
700 | + } |
701 | +} |
702 | + |
703 | +void RenderedText::draw(RenderTarget& dst, const Vector2i& position, UI::Align align) const { |
704 | + draw(dst, position, Recti(0, 0, width(), height()), align); |
705 | +} |
706 | + |
707 | +// Crop horizontally if it doesn't fit |
708 | +void RenderedText::blit_cropped(RenderTarget& dst, |
709 | + int offset_x, |
710 | + const Vector2i& position, |
711 | + const Vector2i& blit_point, |
712 | + const RenderedRect& rect, |
713 | + const Recti& region, |
714 | + Align align) const { |
715 | + |
716 | + int blit_width = rect.width(); |
717 | + int cropped_left = 0; |
718 | + if (align != UI::Align::kLeft) { |
719 | + if (rect.x() + rect.width() + offset_x <= region.x) { |
720 | + // Falls off the left-hand side |
721 | + return; |
722 | + } |
723 | + if (rect.x() + offset_x < 0) { |
724 | + // Needs cropping |
725 | + blit_width = rect.width() + offset_x + rect.x() - region.x; |
726 | + cropped_left = rect.width() - blit_width; |
727 | + } |
728 | + } |
729 | + |
730 | + if (align != UI::Align::kRight) { |
731 | + if (rect.x() + rect.width() - offset_x > region.w - region.x) { |
732 | + blit_width = region.w - rect.x() - offset_x; |
733 | + } |
734 | + } |
735 | + |
736 | + // Don't blit tiny or negative width |
737 | + if (blit_width < 3) { |
738 | + return; |
739 | + } |
740 | + |
741 | + dst.blitrect( |
742 | + Vector2i(cropped_left > 0 ? |
743 | + position.x + region.x - (align == UI::Align::kRight ? region.w : region.w / 2) : |
744 | + blit_point.x, |
745 | + blit_point.y), |
746 | + rect.image(), Recti(cropped_left > 0 ? cropped_left : 0, region.y, blit_width, region.h)); |
747 | +} |
748 | + |
749 | +} // namespace UI |
750 | |
751 | === added file 'src/graphic/text/rendered_text.h' |
752 | --- src/graphic/text/rendered_text.h 1970-01-01 00:00:00 +0000 |
753 | +++ src/graphic/text/rendered_text.h 2017-05-23 21:33:09 +0000 |
754 | @@ -0,0 +1,174 @@ |
755 | +/* |
756 | + * Copyright (C) 2017 by the Widelands Development Team |
757 | + * |
758 | + * This program is free software; you can redistribute it and/or |
759 | + * modify it under the terms of the GNU General Public License |
760 | + * as published by the Free Software Foundation; either version 2 |
761 | + * of the License, or (at your option) any later version. |
762 | + * |
763 | + * This program is distributed in the hope that it will be useful, |
764 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
765 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
766 | + * GNU General Public License for more details. |
767 | + * |
768 | + * You should have received a copy of the GNU General Public License |
769 | + * along with this program; if not, write to the Free Software |
770 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
771 | + * |
772 | + */ |
773 | + |
774 | +#ifndef WL_GRAPHIC_TEXT_RENDERED_TEXT_H |
775 | +#define WL_GRAPHIC_TEXT_RENDERED_TEXT_H |
776 | + |
777 | +#include <memory> |
778 | +#include <vector> |
779 | + |
780 | +#include "base/rect.h" |
781 | +#include "base/vector.h" |
782 | +#include "graphic/image.h" |
783 | +#include "graphic/rendertarget.h" |
784 | +#include "graphic/texture.h" |
785 | + |
786 | +namespace UI { |
787 | + |
788 | +/// A rectangle that contains blitting information for rendered text. |
789 | +class RenderedRect { |
790 | +public: |
791 | + /// Whether the RenderedRect's image should be blitted once or tiled |
792 | + enum class DrawMode { |
793 | + kBlit, // The image texture is considered a foreground image and blitted as is |
794 | + kTile // The image texture is considered a background image and is tiled to fill the rect |
795 | + }; |
796 | + |
797 | +private: |
798 | + // The image is managed by a transient cache |
799 | + RenderedRect(const Recti& init_rect, |
800 | + std::shared_ptr<const Image> init_image, |
801 | + bool visited, |
802 | + const RGBColor& color, |
803 | + bool is_background_color_set, |
804 | + DrawMode init_mode); |
805 | + // The image is managed by a pernament cache |
806 | + RenderedRect(const Recti& init_rect, |
807 | + const Image* init_image, |
808 | + bool visited, |
809 | + const RGBColor& color, |
810 | + bool is_background_color_set, |
811 | + DrawMode init_mode); |
812 | + |
813 | +public: |
814 | + /// RenderedRect will contain a background image that should be tiled |
815 | + RenderedRect(const Recti& init_rect, const Image* init_image); |
816 | + |
817 | + /// RenderedRect will contain a background color that should be tiled |
818 | + RenderedRect(const Recti& init_rect, const RGBColor& color); |
819 | + |
820 | + /// RenderedRect will contain a normal image that is managed by a transient cache. |
821 | + /// Use this if the image is managed by an instance of TextureCache. |
822 | + RenderedRect(std::shared_ptr<const Image> init_image); |
823 | + |
824 | + /// RenderedRect will contain a normal image that is managed by a permanent cache. |
825 | + /// Use this if the image is managed by g_gr->images(). |
826 | + RenderedRect(const Image* init_image); |
827 | + ~RenderedRect() { |
828 | + } |
829 | + |
830 | + /// An image to be blitted. Can be nullptr. |
831 | + const Image* image() const; |
832 | + |
833 | + /// The x position of the rectangle |
834 | + int x() const; |
835 | + /// The y position of the rectangle |
836 | + int y() const; |
837 | + /// The width of the rectangle |
838 | + int width() const; |
839 | + /// The height of the rectangle |
840 | + int height() const; |
841 | + |
842 | + /// Change x and y position of the rectangle. |
843 | + void set_origin(const Vector2i& new_origin); |
844 | + |
845 | + /// Set that this rectangle was already visited by the font renderer. Needed by the font renderer |
846 | + /// for correct positioning. |
847 | + void set_visited(); |
848 | + /// Whether this rectangle was already visited by the font renderer |
849 | + bool was_visited() const; |
850 | + |
851 | + /// Whether this rectangle contains a background color |
852 | + bool has_background_color() const; |
853 | + /// This rectangle's background color |
854 | + const RGBColor& background_color() const; |
855 | + |
856 | + /// Whether the RenderedRect's image should be blitted once or tiled |
857 | + DrawMode mode() const; |
858 | + |
859 | +private: |
860 | + Recti rect_; |
861 | + // We have 2 image objects depending on the caching situation - only use one of them at the same |
862 | + // time. |
863 | + std::shared_ptr<const Image> transient_image_; // Shared ownership, managed by a transient cache |
864 | + const Image* permanent_image_; // Not owned, managed by a permanent cache |
865 | + bool visited_; |
866 | + const RGBColor background_color_; |
867 | + const bool is_background_color_set_; |
868 | + const DrawMode mode_; |
869 | +}; |
870 | + |
871 | +struct RenderedText { |
872 | + /// RenderedRects that can be drawn on screen |
873 | + std::vector<std::unique_ptr<RenderedRect>> rects; |
874 | + |
875 | + /// The width occupied by all rects in pixels. |
876 | + int width() const; |
877 | + /// The height occupied by all rects in pixels. |
878 | + int height() const; |
879 | + |
880 | + enum class CropMode { |
881 | + // The RenderTarget will handle all cropping. Use this for scrollable elements or when you |
882 | + // don't expect any cropping. |
883 | + kRenderTarget, |
884 | + // The draw() method will handle horizontal cropping. Use this for table entries. |
885 | + kSelf |
886 | + }; |
887 | + |
888 | + /// Draw the rects. 'position', 'region' and 'align' are used to control the overall drawing |
889 | + /// position and cropping. |
890 | + /// For 'cropmode', use kRenderTarget if you wish the text to fill the whole RenderTarget, e.g. |
891 | + /// for scrolling panels. Use kHorizontal for horizontal cropping in smaller elements, e.g. table |
892 | + /// cells. |
893 | + void draw(RenderTarget& dst, |
894 | + const Vector2i& position, |
895 | + const Recti& region, |
896 | + UI::Align align = UI::Align::kLeft, |
897 | + CropMode cropmode = CropMode::kRenderTarget) const; |
898 | + |
899 | + /// Draw the rects without cropping. 'position' and 'align' are used to control the overall |
900 | + /// drawing position |
901 | + void draw(RenderTarget& dst, const Vector2i& position, UI::Align align = UI::Align::kLeft) const; |
902 | + |
903 | +private: |
904 | + /// Helper function for draw(). Blits the rect's background color and images. The rect will be |
905 | + /// positioned according to 'aligned_position' and cropped according to 'region'. 'offxet_x' and |
906 | + /// 'align' are used by the cropping algorithm when we use CropMode::kSelf mode. |
907 | + void blit_rect(RenderTarget& dst, |
908 | + int offset_x, |
909 | + const Vector2i& aligned_position, |
910 | + const RenderedRect& rect, |
911 | + const Recti& region, |
912 | + Align align, |
913 | + CropMode cropmode) const; |
914 | + |
915 | + /// Helper function for CropMode::kSelf. It only does horizontal cropping since the RenderTarget |
916 | + /// itself still seems to take care of vertical stuff for us in tables. |
917 | + void blit_cropped(RenderTarget& dst, |
918 | + int offset_x, |
919 | + const Vector2i& position, |
920 | + const Vector2i& blit_point, |
921 | + const RenderedRect& rect, |
922 | + const Recti& region, |
923 | + Align align) const; |
924 | +}; |
925 | + |
926 | +} // namespace UI |
927 | + |
928 | +#endif // end of include guard: WL_GRAPHIC_TEXT_RENDERED_TEXT_H |
929 | |
930 | === modified file 'src/graphic/text/rt_render.cc' |
931 | --- src/graphic/text/rt_render.cc 2017-05-03 07:24:06 +0000 |
932 | +++ src/graphic/text/rt_render.cc 2017-05-23 21:33:09 +0000 |
933 | @@ -43,6 +43,7 @@ |
934 | #include "graphic/text/bidi.h" |
935 | #include "graphic/text/font_io.h" |
936 | #include "graphic/text/font_set.h" |
937 | +#include "graphic/text/rendered_text.h" |
938 | #include "graphic/text/rt_parse.h" |
939 | #include "graphic/text/sdl_ttf_font.h" |
940 | #include "graphic/text/textstream.h" |
941 | @@ -52,7 +53,7 @@ |
942 | #include "io/filesystem/layered_filesystem.h" |
943 | |
944 | using namespace std; |
945 | - |
946 | +// TODO(GunChleoc): text line can start with space text node when it's within a div. |
947 | namespace RT { |
948 | |
949 | static const uint16_t INFINITE_WIDTH = 65535; // 2^16-1 |
950 | @@ -227,15 +228,20 @@ |
951 | virtual ~RenderNode() { |
952 | } |
953 | |
954 | - virtual uint16_t width() = 0; |
955 | - virtual uint16_t height() = 0; |
956 | - virtual uint16_t hotspot_y() = 0; |
957 | - virtual Texture* render(TextureCache* texture_cache) = 0; |
958 | - |
959 | - virtual bool is_non_mandatory_space() { |
960 | + virtual uint16_t width() const = 0; |
961 | + virtual uint16_t height() const = 0; |
962 | + virtual uint16_t hotspot_y() const = 0; |
963 | + virtual UI::RenderedText* render(TextureCache* texture_cache) = 0; |
964 | + |
965 | + // TODO(GunChleoc): Remove this function once conversion is finished and well tested. |
966 | + virtual std::string debug_info() const = 0; |
967 | + |
968 | + // If a node is a non-mandatory space, it can be removed as a leading/trailing space |
969 | + // by the positioning algorithm. |
970 | + virtual bool is_non_mandatory_space() const { |
971 | return false; |
972 | } |
973 | - virtual bool is_expanding() { |
974 | + virtual bool is_expanding() const { |
975 | return false; |
976 | } |
977 | virtual void set_w(uint16_t) { |
978 | @@ -245,19 +251,19 @@ |
979 | return vector<Reference>(); |
980 | } |
981 | |
982 | - Floating get_floating() { |
983 | + Floating get_floating() const { |
984 | return floating_; |
985 | } |
986 | void set_floating(Floating f) { |
987 | floating_ = f; |
988 | } |
989 | - UI::Align halign() { |
990 | + UI::Align halign() const { |
991 | return halign_; |
992 | } |
993 | void set_halign(UI::Align ghalign) { |
994 | halign_ = ghalign; |
995 | } |
996 | - UI::Align valign() { |
997 | + UI::Align valign() const { |
998 | return valign_; |
999 | } |
1000 | void set_valign(UI::Align gvalign) { |
1001 | @@ -269,13 +275,38 @@ |
1002 | void set_y(int32_t ny) { |
1003 | y_ = ny; |
1004 | } |
1005 | - int32_t x() { |
1006 | + int32_t x() const { |
1007 | return x_; |
1008 | } |
1009 | - int32_t y() { |
1010 | + int32_t y() const { |
1011 | return y_; |
1012 | } |
1013 | |
1014 | +protected: |
1015 | + /// Throws a TextureTooBig exception if the given dimensions would be bigger than the graphics |
1016 | + /// can handle |
1017 | + void check_size(int check_w, int check_h) { |
1018 | +// Test for minimum supported size in debug builds. |
1019 | +#ifndef NDEBUG |
1020 | + const int maximum_size = kMinimumSizeForTextures; |
1021 | +#else |
1022 | + const int maximum_size = g_gr->max_texture_size(); |
1023 | +#endif |
1024 | + if (check_w > maximum_size || check_h > maximum_size) { |
1025 | + const std::string error_message = |
1026 | + (boost::format("Texture (%d, %d) too big! Maximum size is %d.") % check_w % check_h % |
1027 | + maximum_size) |
1028 | + .str(); |
1029 | + log("%s\n", error_message.c_str()); |
1030 | + throw TextureTooBig(error_message); |
1031 | + } |
1032 | + } |
1033 | + |
1034 | + /// Check the size for the node's own dimensions |
1035 | + void check_size() { |
1036 | + check_size(width(), height()); |
1037 | + } |
1038 | + |
1039 | private: |
1040 | Floating floating_; |
1041 | UI::Align halign_; |
1042 | @@ -479,13 +510,17 @@ |
1043 | virtual ~TextNode() { |
1044 | } |
1045 | |
1046 | - uint16_t width() override { |
1047 | + std::string debug_info() const override { |
1048 | + return "'" + txt_ + "'"; |
1049 | + } |
1050 | + |
1051 | + uint16_t width() const override { |
1052 | return w_; |
1053 | } |
1054 | - uint16_t height() override { |
1055 | + uint16_t height() const override { |
1056 | return h_ + nodestyle_.spacing; |
1057 | } |
1058 | - uint16_t hotspot_y() override; |
1059 | + uint16_t hotspot_y() const override; |
1060 | const vector<Reference> get_references() override { |
1061 | vector<Reference> rv; |
1062 | if (!nodestyle_.reference.empty()) { |
1063 | @@ -495,7 +530,7 @@ |
1064 | return rv; |
1065 | } |
1066 | |
1067 | - Texture* render(TextureCache* texture_cache) override; |
1068 | + UI::RenderedText* render(TextureCache* texture_cache) override; |
1069 | |
1070 | protected: |
1071 | uint16_t w_, h_; |
1072 | @@ -512,18 +547,20 @@ |
1073 | fontcache_(font), |
1074 | font_(dynamic_cast<SdlTtfFont&>(fontcache_.get_font(&nodestyle_))) { |
1075 | font_.dimensions(txt_, ns.font_style, &w_, &h_); |
1076 | + check_size(); |
1077 | } |
1078 | -uint16_t TextNode::hotspot_y() { |
1079 | +uint16_t TextNode::hotspot_y() const { |
1080 | return font_.ascent(nodestyle_.font_style); |
1081 | } |
1082 | |
1083 | -Texture* TextNode::render(TextureCache* texture_cache) { |
1084 | - const Texture& img = |
1085 | +UI::RenderedText* TextNode::render(TextureCache* texture_cache) { |
1086 | + auto rendered_image = |
1087 | font_.render(txt_, nodestyle_.font_color, nodestyle_.font_style, texture_cache); |
1088 | - Texture* rv = new Texture(img.width(), img.height()); |
1089 | - rv->blit(Rectf(0, 0, img.width(), img.height()), img, Rectf(0, 0, img.width(), img.height()), 1., |
1090 | - BlendMode::Copy); |
1091 | - return rv; |
1092 | + assert(rendered_image.get() != nullptr); |
1093 | + UI::RenderedText* rendered_text = new UI::RenderedText(); |
1094 | + rendered_text->rects.push_back( |
1095 | + std::unique_ptr<UI::RenderedRect>(new UI::RenderedRect(rendered_image))); |
1096 | + return rendered_text; |
1097 | } |
1098 | |
1099 | /* |
1100 | @@ -536,12 +573,18 @@ |
1101 | FontCache& font, NodeStyle& ns, uint16_t w, const string& txt, bool expanding = false) |
1102 | : TextNode(font, ns, txt), is_expanding_(expanding) { |
1103 | w_ = w; |
1104 | + check_size(); |
1105 | } |
1106 | virtual ~FillingTextNode() { |
1107 | } |
1108 | - Texture* render(TextureCache*) override; |
1109 | - |
1110 | - bool is_expanding() override { |
1111 | + |
1112 | + std::string debug_info() const override { |
1113 | + return "ft"; |
1114 | + } |
1115 | + |
1116 | + UI::RenderedText* render(TextureCache*) override; |
1117 | + |
1118 | + bool is_expanding() const override { |
1119 | return is_expanding_; |
1120 | } |
1121 | void set_w(uint16_t w) override { |
1122 | @@ -551,15 +594,29 @@ |
1123 | private: |
1124 | bool is_expanding_; |
1125 | }; |
1126 | -Texture* FillingTextNode::render(TextureCache* texture_cache) { |
1127 | - const Texture& t = |
1128 | - font_.render(txt_, nodestyle_.font_color, nodestyle_.font_style, texture_cache); |
1129 | - Texture* rv = new Texture(w_, h_); |
1130 | - for (uint16_t curx = 0; curx < w_; curx += t.width()) { |
1131 | - Rectf srcrect(0.f, 0.f, min<int>(t.width(), w_ - curx), h_); |
1132 | - rv->blit(Rectf(curx, 0, srcrect.w, srcrect.h), t, srcrect, 1., BlendMode::Copy); |
1133 | +UI::RenderedText* FillingTextNode::render(TextureCache* texture_cache) { |
1134 | + UI::RenderedText* rendered_text = new UI::RenderedText(); |
1135 | + const std::string hash = |
1136 | + (boost::format("rt:fill:%s:%s:%i:%i:%i:%s") % txt_ % nodestyle_.font_color.hex_value() % |
1137 | + nodestyle_.font_style % width() % height() % (is_expanding_ ? "e" : "f")) |
1138 | + .str(); |
1139 | + |
1140 | + std::shared_ptr<const Image> rendered_image = texture_cache->get(hash); |
1141 | + if (rendered_image.get() == nullptr) { |
1142 | + std::shared_ptr<const Image> ttf = |
1143 | + font_.render(txt_, nodestyle_.font_color, nodestyle_.font_style, texture_cache); |
1144 | + auto texture = std::make_shared<Texture>(width(), height()); |
1145 | + for (uint16_t curx = 0; curx < w_; curx += ttf->width()) { |
1146 | + Rectf srcrect(0.f, 0.f, min<int>(ttf->width(), w_ - curx), h_); |
1147 | + texture->blit( |
1148 | + Rectf(curx, 0, srcrect.w, srcrect.h), *ttf.get(), srcrect, 1., BlendMode::Copy); |
1149 | + } |
1150 | + rendered_image = texture_cache->insert(hash, std::move(texture)); |
1151 | } |
1152 | - return rv; |
1153 | + assert(rendered_image.get() != nullptr); |
1154 | + rendered_text->rects.push_back( |
1155 | + std::unique_ptr<UI::RenderedRect>(new UI::RenderedRect(rendered_image))); |
1156 | + return rendered_text; |
1157 | } |
1158 | |
1159 | /* |
1160 | @@ -569,20 +626,34 @@ |
1161 | class WordSpacerNode : public TextNode { |
1162 | public: |
1163 | WordSpacerNode(FontCache& font, NodeStyle& ns) : TextNode(font, ns, " ") { |
1164 | + check_size(); |
1165 | } |
1166 | static void show_spaces(bool t) { |
1167 | show_spaces_ = t; |
1168 | } |
1169 | |
1170 | - Texture* render(TextureCache* texture_cache) override { |
1171 | + std::string debug_info() const override { |
1172 | + return "wsp"; |
1173 | + } |
1174 | + |
1175 | + UI::RenderedText* render(TextureCache* texture_cache) override { |
1176 | if (show_spaces_) { |
1177 | - Texture* rv = new Texture(w_, h_); |
1178 | - rv->fill_rect(Rectf(0, 0, w_, h_), RGBAColor(0xcc, 0, 0, 0xcc)); |
1179 | - return rv; |
1180 | + UI::RenderedText* rendered_text = new UI::RenderedText(); |
1181 | + const std::string hash = (boost::format("rt:wsp:%i:%i") % width() % height()).str(); |
1182 | + std::shared_ptr<const Image> rendered_image = texture_cache->get(hash); |
1183 | + if (rendered_image.get() == nullptr) { |
1184 | + auto texture = std::make_shared<Texture>(width(), height()); |
1185 | + texture->fill_rect(Rectf(0.f, 0.f, w_, h_), RGBAColor(0xcc, 0, 0, 0xcc)); |
1186 | + rendered_image = texture_cache->insert(hash, std::move(texture)); |
1187 | + } |
1188 | + assert(rendered_image.get() != nullptr); |
1189 | + rendered_text->rects.push_back( |
1190 | + std::unique_ptr<UI::RenderedRect>(new UI::RenderedRect(rendered_image))); |
1191 | + return rendered_text; |
1192 | } |
1193 | return TextNode::render(texture_cache); |
1194 | } |
1195 | - bool is_non_mandatory_space() override { |
1196 | + bool is_non_mandatory_space() const override { |
1197 | return true; |
1198 | } |
1199 | |
1200 | @@ -599,19 +670,24 @@ |
1201 | public: |
1202 | NewlineNode(NodeStyle& ns) : RenderNode(ns) { |
1203 | } |
1204 | - uint16_t height() override { |
1205 | + |
1206 | + std::string debug_info() const override { |
1207 | + return "nl"; |
1208 | + } |
1209 | + |
1210 | + uint16_t height() const override { |
1211 | return 0; |
1212 | } |
1213 | - uint16_t width() override { |
1214 | + uint16_t width() const override { |
1215 | return INFINITE_WIDTH; |
1216 | } |
1217 | - uint16_t hotspot_y() override { |
1218 | + uint16_t hotspot_y() const override { |
1219 | return 0; |
1220 | } |
1221 | - Texture* render(TextureCache* /* texture_cache */) override { |
1222 | + UI::RenderedText* render(TextureCache* /* texture_cache */) override { |
1223 | NEVER_HERE(); |
1224 | } |
1225 | - bool is_non_mandatory_space() override { |
1226 | + bool is_non_mandatory_space() const override { |
1227 | return true; |
1228 | } |
1229 | }; |
1230 | @@ -622,52 +698,76 @@ |
1231 | class SpaceNode : public RenderNode { |
1232 | public: |
1233 | SpaceNode(NodeStyle& ns, uint16_t w, uint16_t h = 0, bool expanding = false) |
1234 | - : RenderNode(ns), w_(w), h_(h), background_image_(nullptr), is_expanding_(expanding) { |
1235 | - } |
1236 | - |
1237 | - uint16_t height() override { |
1238 | + : RenderNode(ns), |
1239 | + w_(w), |
1240 | + h_(h), |
1241 | + background_image_(nullptr), |
1242 | + filename_(""), |
1243 | + is_expanding_(expanding) { |
1244 | + check_size(); |
1245 | + } |
1246 | + |
1247 | + std::string debug_info() const override { |
1248 | + return "sp"; |
1249 | + } |
1250 | + |
1251 | + uint16_t height() const override { |
1252 | return h_; |
1253 | } |
1254 | - uint16_t width() override { |
1255 | + uint16_t width() const override { |
1256 | return w_; |
1257 | } |
1258 | - uint16_t hotspot_y() override { |
1259 | + uint16_t hotspot_y() const override { |
1260 | return h_; |
1261 | } |
1262 | - Texture* render(TextureCache* /* texture_cache */) override { |
1263 | - Texture* rv = new Texture(w_, h_); |
1264 | + UI::RenderedText* render(TextureCache* texture_cache) override { |
1265 | + UI::RenderedText* rendered_text = new UI::RenderedText(); |
1266 | + const std::string hash = (boost::format("rt:sp:%s:%i:%i:%s") % filename_ % width() % |
1267 | + height() % (is_expanding_ ? "e" : "f")) |
1268 | + .str(); |
1269 | |
1270 | - // Draw background image (tiling) |
1271 | - if (background_image_) { |
1272 | - Rectf dst; |
1273 | - Rectf srcrect(0, 0, 1, 1); |
1274 | - for (uint16_t curx = 0; curx < w_; curx += background_image_->width()) { |
1275 | - dst.x = curx; |
1276 | - dst.y = 0; |
1277 | - srcrect.w = dst.w = min<int>(background_image_->width(), w_ - curx); |
1278 | - srcrect.h = dst.h = h_; |
1279 | - rv->blit(dst, *background_image_, srcrect, 1., BlendMode::Copy); |
1280 | + std::shared_ptr<const Image> rendered_image = texture_cache->get(hash); |
1281 | + if (rendered_image.get() == nullptr) { |
1282 | + // Draw background image (tiling) |
1283 | + auto texture = std::make_shared<Texture>(width(), height()); |
1284 | + if (background_image_ != nullptr) { |
1285 | + Rectf dst; |
1286 | + Rectf srcrect(0, 0, 1, 1); |
1287 | + for (uint16_t curx = 0; curx < w_; curx += background_image_->width()) { |
1288 | + dst.x = curx; |
1289 | + dst.y = 0; |
1290 | + srcrect.w = dst.w = min<int>(background_image_->width(), w_ - curx); |
1291 | + srcrect.h = dst.h = h_; |
1292 | + texture->blit(dst, *background_image_, srcrect, 1., BlendMode::Copy); |
1293 | + } |
1294 | + } else { |
1295 | + texture->fill_rect(Rectf(0.f, 0.f, w_, h_), RGBAColor(255, 255, 255, 0)); |
1296 | } |
1297 | - } else { |
1298 | - rv->fill_rect(Rectf(0, 0, w_, h_), RGBAColor(255, 255, 255, 0)); |
1299 | + rendered_image = texture_cache->insert(hash, std::move(texture)); |
1300 | } |
1301 | - return rv; |
1302 | + assert(rendered_image.get() != nullptr); |
1303 | + rendered_text->rects.push_back( |
1304 | + std::unique_ptr<UI::RenderedRect>(new UI::RenderedRect(rendered_image))); |
1305 | + return rendered_text; |
1306 | } |
1307 | - bool is_expanding() override { |
1308 | + |
1309 | + bool is_expanding() const override { |
1310 | return is_expanding_; |
1311 | } |
1312 | void set_w(uint16_t w) override { |
1313 | w_ = w; |
1314 | } |
1315 | |
1316 | - void set_background(const Image* s) { |
1317 | + void set_background(const Image* s, const std::string& filename) { |
1318 | background_image_ = s; |
1319 | + filename_ = filename; |
1320 | h_ = s->height(); |
1321 | } |
1322 | |
1323 | private: |
1324 | uint16_t w_, h_; |
1325 | const Image* background_image_; // not owned |
1326 | + std::string filename_; |
1327 | bool is_expanding_; |
1328 | }; |
1329 | |
1330 | @@ -690,13 +790,17 @@ |
1331 | nodes_to_render_.clear(); |
1332 | } |
1333 | |
1334 | - uint16_t width() override { |
1335 | + std::string debug_info() const override { |
1336 | + return "div"; |
1337 | + } |
1338 | + |
1339 | + uint16_t width() const override { |
1340 | return w_ + margin_.left + margin_.right; |
1341 | } |
1342 | - uint16_t height() override { |
1343 | + uint16_t height() const override { |
1344 | return h_ + margin_.top + margin_.bottom; |
1345 | } |
1346 | - uint16_t hotspot_y() override { |
1347 | + uint16_t hotspot_y() const override { |
1348 | return height(); |
1349 | } |
1350 | |
1351 | @@ -704,60 +808,48 @@ |
1352 | return desired_width_; |
1353 | } |
1354 | |
1355 | - Texture* render(TextureCache* texture_cache) override { |
1356 | - if (width() > g_gr->max_texture_size() || height() > g_gr->max_texture_size()) { |
1357 | - const std::string error_message = |
1358 | - (boost::format("Texture (%d, %d) too big! Maximum size is %d.") % width() % height() % |
1359 | - g_gr->max_texture_size()) |
1360 | - .str(); |
1361 | - log("%s\n", error_message.c_str()); |
1362 | - throw TextureTooBig(error_message); |
1363 | - } |
1364 | - Texture* rv = new Texture(width(), height()); |
1365 | - rv->fill_rect(Rectf(0, 0, rv->width(), rv->height()), RGBAColor(255, 255, 255, 0)); |
1366 | + UI::RenderedText* render(TextureCache* texture_cache) override { |
1367 | + UI::RenderedText* rendered_text = new UI::RenderedText(); |
1368 | + // Preserve padding |
1369 | + rendered_text->rects.push_back(std::unique_ptr<UI::RenderedRect>( |
1370 | + new UI::RenderedRect(Recti(0, 0, width(), height()), nullptr))); |
1371 | |
1372 | // Draw Solid background Color |
1373 | - bool set_alpha = true; |
1374 | if (is_background_color_set_) { |
1375 | - rv->fill_rect(Rectf(margin_.left, margin_.top, w_, h_), background_color_); |
1376 | - set_alpha = false; |
1377 | + UI::RenderedRect* bg_rect = |
1378 | + new UI::RenderedRect(Recti(margin_.left, margin_.top, w_, h_), background_color_); |
1379 | + // Size is automatically adjusted in RenderedText while blitting, so no need to call |
1380 | + // check_size() here. |
1381 | + rendered_text->rects.push_back(std::unique_ptr<UI::RenderedRect>(std::move(bg_rect))); |
1382 | } |
1383 | |
1384 | // Draw background image (tiling) |
1385 | - if (background_image_) { |
1386 | - Rectf dst; |
1387 | - Rectf src(0, 0, 0, 0); |
1388 | - |
1389 | - for (uint16_t cury = margin_.top; cury < h_ + margin_.top; |
1390 | - cury += background_image_->height()) { |
1391 | - for (uint16_t curx = margin_.left; curx < w_ + margin_.left; |
1392 | - curx += background_image_->width()) { |
1393 | - dst.x = curx; |
1394 | - dst.y = cury; |
1395 | - src.w = dst.w = min<int>(background_image_->width(), w_ + margin_.left - curx); |
1396 | - src.h = dst.h = min<int>(background_image_->height(), h_ + margin_.top - cury); |
1397 | - rv->blit(dst, *background_image_, src, 1., BlendMode::Copy); |
1398 | + if (background_image_ != nullptr) { |
1399 | + UI::RenderedRect* bg_rect = |
1400 | + new UI::RenderedRect(Recti(margin_.left, margin_.top, w_, h_), background_image_); |
1401 | + check_size(bg_rect->width(), bg_rect->height()); |
1402 | + rendered_text->rects.push_back(std::unique_ptr<UI::RenderedRect>(std::move(bg_rect))); |
1403 | + } |
1404 | + |
1405 | + for (RenderNode* n : nodes_to_render_) { |
1406 | + const auto& renderme = n->render(texture_cache); |
1407 | + for (auto& rendered_rect : renderme->rects) { |
1408 | + if (rendered_rect->was_visited()) { |
1409 | + rendered_rect->set_origin( |
1410 | + Vector2i(x() + rendered_rect->x(), y() + rendered_rect->y() + margin_.top)); |
1411 | + |
1412 | + } else { |
1413 | + rendered_rect->set_origin( |
1414 | + Vector2i(x() + n->x() + margin_.left, y() + n->y() + margin_.top)); |
1415 | + rendered_rect->set_visited(); |
1416 | } |
1417 | - } |
1418 | - set_alpha = false; |
1419 | - } |
1420 | - |
1421 | - for (RenderNode* n : nodes_to_render_) { |
1422 | - Texture* node_texture = n->render(texture_cache); |
1423 | - if (node_texture) { |
1424 | - Rectf dst(n->x() + margin_.left, n->y() + margin_.top, node_texture->width(), |
1425 | - node_texture->height()); |
1426 | - Rectf src(0, 0, node_texture->width(), node_texture->height()); |
1427 | - rv->blit( |
1428 | - dst, *node_texture, src, 1., set_alpha ? BlendMode::Copy : BlendMode::UseAlpha); |
1429 | - delete node_texture; |
1430 | + rendered_text->rects.push_back(std::move(rendered_rect)); |
1431 | } |
1432 | delete n; |
1433 | } |
1434 | - |
1435 | nodes_to_render_.clear(); |
1436 | |
1437 | - return rv; |
1438 | + return rendered_text; |
1439 | } |
1440 | const vector<Reference> get_references() override { |
1441 | return refs_; |
1442 | @@ -806,30 +898,62 @@ |
1443 | : RenderNode(ns), |
1444 | image_(use_playercolor ? playercolor_image(color, image_filename) : |
1445 | g_gr->images().get(image_filename)), |
1446 | - scale_(scale) { |
1447 | - } |
1448 | - |
1449 | - uint16_t width() override { |
1450 | + filename_(image_filename), |
1451 | + scale_(scale), |
1452 | + color_(color), |
1453 | + use_playercolor_(use_playercolor) { |
1454 | + check_size(); |
1455 | + } |
1456 | + |
1457 | + std::string debug_info() const override { |
1458 | + return "img"; |
1459 | + } |
1460 | + |
1461 | + uint16_t width() const override { |
1462 | return scale_ * image_->width(); |
1463 | } |
1464 | - uint16_t height() override { |
1465 | - return scale_ * image_->height(); |
1466 | - } |
1467 | - uint16_t hotspot_y() override { |
1468 | - return scale_ * image_->height(); |
1469 | - } |
1470 | - Texture* render(TextureCache* texture_cache) override; |
1471 | + uint16_t height() const override { |
1472 | + return scale_ * image_->height(); |
1473 | + } |
1474 | + uint16_t hotspot_y() const override { |
1475 | + return scale_ * image_->height(); |
1476 | + } |
1477 | + UI::RenderedText* render(TextureCache* texture_cache) override; |
1478 | |
1479 | private: |
1480 | const Image* image_; |
1481 | + const std::string filename_; |
1482 | const double scale_; |
1483 | + const RGBColor& color_; |
1484 | + bool use_playercolor_; |
1485 | }; |
1486 | |
1487 | -Texture* ImgRenderNode::render(TextureCache* /* texture_cache */) { |
1488 | - Texture* rv = new Texture(width(), height()); |
1489 | - rv->blit(Rectf(0, 0, width(), height()), *image_, Rectf(0, 0, image_->width(), image_->height()), |
1490 | - 1., BlendMode::Copy); |
1491 | - return rv; |
1492 | +UI::RenderedText* ImgRenderNode::render(TextureCache* texture_cache) { |
1493 | + UI::RenderedText* rendered_text = new UI::RenderedText(); |
1494 | + |
1495 | + if (scale_ == 1.0) { |
1496 | + // Image can be used as is, and has already been cached in g_gr->images() |
1497 | + assert(image_ != nullptr); |
1498 | + rendered_text->rects.push_back( |
1499 | + std::unique_ptr<UI::RenderedRect>(new UI::RenderedRect(image_))); |
1500 | + } else { |
1501 | + const std::string hash = (boost::format("rt:img:%s:%s:%i:%i") % filename_ % |
1502 | + (use_playercolor_ ? color_.hex_value() : "") % width() % height()) |
1503 | + .str(); |
1504 | + std::shared_ptr<const Image> rendered_image = texture_cache->get(hash); |
1505 | + if (rendered_image.get() == nullptr) { |
1506 | + auto texture = std::make_shared<Texture>(width(), height()); |
1507 | + texture->blit(Rectf(0.f, 0.f, width(), height()), *image_, |
1508 | + Rectf(0.f, 0.f, image_->width(), image_->height()), 1., BlendMode::Copy); |
1509 | + rendered_image = texture_cache->insert(hash, std::move(texture)); |
1510 | + } |
1511 | + |
1512 | + assert(rendered_image.get() != nullptr); |
1513 | + rendered_text->rects.push_back( |
1514 | + std::unique_ptr<UI::RenderedRect>(new UI::RenderedRect(rendered_image))); |
1515 | + } |
1516 | + |
1517 | + return rendered_text; |
1518 | } |
1519 | // End: Helper Stuff |
1520 | |
1521 | @@ -1136,6 +1260,7 @@ |
1522 | const UI::FontSets& fontsets) |
1523 | : TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets), |
1524 | background_image_(nullptr), |
1525 | + image_filename_(""), |
1526 | space_(0) { |
1527 | } |
1528 | |
1529 | @@ -1151,6 +1276,7 @@ |
1530 | fill_text_ = a["fill"].get_string(); |
1531 | try { |
1532 | background_image_ = image_cache_->get(fill_text_); |
1533 | + image_filename_ = fill_text_; |
1534 | fill_text_ = ""; |
1535 | } catch (ImageNotFound&) { |
1536 | } |
1537 | @@ -1172,7 +1298,7 @@ |
1538 | sn = new SpaceNode(nodestyle_, 0, 0, true); |
1539 | |
1540 | if (background_image_) |
1541 | - sn->set_background(background_image_); |
1542 | + sn->set_background(background_image_, image_filename_); |
1543 | rn = sn; |
1544 | } |
1545 | nodes.push_back(rn); |
1546 | @@ -1181,6 +1307,7 @@ |
1547 | private: |
1548 | string fill_text_; |
1549 | const Image* background_image_; |
1550 | + std::string image_filename_; |
1551 | uint16_t space_; |
1552 | }; |
1553 | |
1554 | @@ -1481,10 +1608,11 @@ |
1555 | return nodes[0]; |
1556 | } |
1557 | |
1558 | -Texture* Renderer::render(const string& text, uint16_t width, const TagSet& allowed_tags) { |
1559 | +std::shared_ptr<const UI::RenderedText> |
1560 | +Renderer::render(const string& text, uint16_t width, const TagSet& allowed_tags) { |
1561 | std::unique_ptr<RenderNode> node(layout_(text, width, allowed_tags)); |
1562 | |
1563 | - return node->render(texture_cache_); |
1564 | + return std::shared_ptr<const UI::RenderedText>(std::move(node->render(texture_cache_))); |
1565 | } |
1566 | |
1567 | IRefMap* |
1568 | |
1569 | === modified file 'src/graphic/text/rt_render.h' |
1570 | --- src/graphic/text/rt_render.h 2017-01-25 18:55:59 +0000 |
1571 | +++ src/graphic/text/rt_render.h 2017-05-23 21:33:09 +0000 |
1572 | @@ -28,11 +28,10 @@ |
1573 | |
1574 | #include "graphic/color.h" |
1575 | #include "graphic/image.h" |
1576 | +#include "graphic/image_cache.h" |
1577 | #include "graphic/text/font_set.h" |
1578 | - |
1579 | -class Texture; |
1580 | -class ImageCache; |
1581 | -class TextureCache; |
1582 | +#include "graphic/text/rendered_text.h" |
1583 | +#include "graphic/text/texture_cache.h" |
1584 | |
1585 | namespace RT { |
1586 | |
1587 | @@ -80,9 +79,9 @@ |
1588 | ~Renderer(); |
1589 | |
1590 | // Render the given string in the given width. Restricts the allowed tags to |
1591 | - // the ones in TagSet. The renderer does not do caching in the TextureCache |
1592 | - // for its individual nodes, but the font render does. |
1593 | - Texture* render(const std::string&, uint16_t width, const TagSet& tagset = TagSet()); |
1594 | + // the ones in TagSet. |
1595 | + std::shared_ptr<const UI::RenderedText> |
1596 | + render(const std::string&, uint16_t width, const TagSet& tagset = TagSet()); |
1597 | |
1598 | // Returns a reference map of the clickable hyperlinks in the image. This |
1599 | // will do no caching and needs to do all layouting, so do not call this too |
1600 | |
1601 | === modified file 'src/graphic/text/sdl_ttf_font.cc' |
1602 | --- src/graphic/text/sdl_ttf_font.cc 2017-01-25 18:55:59 +0000 |
1603 | +++ src/graphic/text/sdl_ttf_font.cc 2017-05-23 21:33:09 +0000 |
1604 | @@ -63,17 +63,18 @@ |
1605 | *gh = h; |
1606 | } |
1607 | |
1608 | -const Texture& SdlTtfFont::render(const std::string& txt, |
1609 | - const RGBColor& clr, |
1610 | - int style, |
1611 | - TextureCache* texture_cache) { |
1612 | +std::shared_ptr<const Image> SdlTtfFont::render(const std::string& txt, |
1613 | + const RGBColor& clr, |
1614 | + int style, |
1615 | + TextureCache* texture_cache) { |
1616 | const std::string hash = |
1617 | - (boost::format("%s:%s:%i:%02x%02x%02x:%i") % font_name_ % ptsize_ % txt % |
1618 | + (boost::format("ttf:%s:%s:%i:%02x%02x%02x:%i") % font_name_ % ptsize_ % txt % |
1619 | static_cast<int>(clr.r) % static_cast<int>(clr.g) % static_cast<int>(clr.b) % style) |
1620 | .str(); |
1621 | - const Texture* rv = texture_cache->get(hash); |
1622 | - if (rv) |
1623 | - return *rv; |
1624 | + std::shared_ptr<const Image> rv = texture_cache->get(hash); |
1625 | + if (rv.get() != nullptr) { |
1626 | + return rv; |
1627 | + } |
1628 | |
1629 | set_style(style); |
1630 | |
1631 | @@ -128,7 +129,7 @@ |
1632 | throw RenderError( |
1633 | (boost::format("Rendering '%s' gave the error: %s") % txt % TTF_GetError()).str()); |
1634 | |
1635 | - return *texture_cache->insert(hash, std::unique_ptr<Texture>(new Texture(text_surface))); |
1636 | + return texture_cache->insert(hash, std::make_shared<Texture>(text_surface)); |
1637 | } |
1638 | |
1639 | uint16_t SdlTtfFont::ascent(int style) const { |
1640 | |
1641 | === modified file 'src/graphic/text/sdl_ttf_font.h' |
1642 | --- src/graphic/text/sdl_ttf_font.h 2017-02-28 20:07:07 +0000 |
1643 | +++ src/graphic/text/sdl_ttf_font.h 2017-05-23 21:33:09 +0000 |
1644 | @@ -25,8 +25,8 @@ |
1645 | |
1646 | #include <SDL_ttf.h> |
1647 | |
1648 | +#include "graphic/text/texture_cache.h" |
1649 | #include "graphic/texture.h" |
1650 | -#include "graphic/texture_cache.h" |
1651 | |
1652 | namespace RT { |
1653 | |
1654 | @@ -51,7 +51,8 @@ |
1655 | } |
1656 | |
1657 | virtual void dimensions(const std::string&, int, uint16_t*, uint16_t*) = 0; |
1658 | - virtual const Texture& render(const std::string&, const RGBColor& clr, int, TextureCache*) = 0; |
1659 | + virtual std::shared_ptr<const Image> |
1660 | + render(const std::string&, const RGBColor& clr, int, TextureCache*) = 0; |
1661 | |
1662 | virtual uint16_t ascent(int) const = 0; |
1663 | virtual TTF_Font* get_ttf_font() const = 0; |
1664 | @@ -64,7 +65,8 @@ |
1665 | virtual ~SdlTtfFont(); |
1666 | |
1667 | void dimensions(const std::string&, int, uint16_t* w, uint16_t* h) override; |
1668 | - const Texture& render(const std::string&, const RGBColor& clr, int, TextureCache*) override; |
1669 | + std::shared_ptr<const Image> |
1670 | + render(const std::string&, const RGBColor& clr, int, TextureCache*) override; |
1671 | uint16_t ascent(int) const override; |
1672 | TTF_Font* get_ttf_font() const override { |
1673 | return font_; |
1674 | |
1675 | === modified file 'src/graphic/text/test/CMakeLists.txt' |
1676 | --- src/graphic/text/test/CMakeLists.txt 2015-10-15 10:46:37 +0000 |
1677 | +++ src/graphic/text/test/CMakeLists.txt 2017-05-23 21:33:09 +0000 |
1678 | @@ -23,7 +23,6 @@ |
1679 | DEPENDS |
1680 | base_i18n |
1681 | graphic_image_cache |
1682 | - graphic_surface |
1683 | graphic_text |
1684 | io_filesystem |
1685 | ) |
1686 | |
1687 | === modified file 'src/graphic/text/test/render.cc' |
1688 | --- src/graphic/text/test/render.cc 2017-01-25 18:55:59 +0000 |
1689 | +++ src/graphic/text/test/render.cc 2017-05-23 21:33:09 +0000 |
1690 | @@ -28,7 +28,7 @@ |
1691 | #include "graphic/image_cache.h" |
1692 | #include "graphic/text/rt_render.h" |
1693 | #include "graphic/text/test/paths.h" |
1694 | -#include "graphic/texture_cache.h" |
1695 | +#include "graphic/text/texture_cache.h" |
1696 | #include "io/filesystem/layered_filesystem.h" |
1697 | |
1698 | StandaloneRenderer::StandaloneRenderer() { |
1699 | |
1700 | === modified file 'src/graphic/text/test/render_richtext.cc' |
1701 | --- src/graphic/text/test/render_richtext.cc 2017-01-25 18:55:59 +0000 |
1702 | +++ src/graphic/text/test/render_richtext.cc 2017-05-23 21:33:09 +0000 |
1703 | @@ -33,6 +33,7 @@ |
1704 | #include "config.h" |
1705 | #include "graphic/graphic.h" |
1706 | #include "graphic/image_io.h" |
1707 | +#include "graphic/rendertarget.h" |
1708 | #include "graphic/text/rt_errors.h" |
1709 | #include "graphic/text/test/render.h" |
1710 | #include "graphic/texture.h" |
1711 | @@ -138,11 +139,15 @@ |
1712 | StandaloneRenderer standalone_renderer; |
1713 | |
1714 | try { |
1715 | - std::unique_ptr<Texture> texture( |
1716 | - standalone_renderer.renderer()->render(txt, w, allowed_tags)); |
1717 | + std::shared_ptr<const UI::RenderedText> rendered_text = |
1718 | + standalone_renderer.renderer()->render(txt, w, allowed_tags); |
1719 | + std::unique_ptr<Texture> texture(new Texture(rendered_text->width(), rendered_text->height())); |
1720 | + std::unique_ptr<RenderTarget> dst(new RenderTarget(texture.get())); |
1721 | + rendered_text->draw(*dst.get(), Vector2i::zero()); |
1722 | |
1723 | std::unique_ptr<FileSystem> fs(&FileSystem::create(".")); |
1724 | std::unique_ptr<StreamWrite> sw(fs->open_stream_write(outname)); |
1725 | + |
1726 | if (!save_to_png(texture.get(), sw.get(), ColorType::RGBA)) { |
1727 | std::cout << "Could not encode PNG." << std::endl; |
1728 | } |
1729 | |
1730 | === added file 'src/graphic/text/texture_cache.h' |
1731 | --- src/graphic/text/texture_cache.h 1970-01-01 00:00:00 +0000 |
1732 | +++ src/graphic/text/texture_cache.h 2017-05-23 21:33:09 +0000 |
1733 | @@ -0,0 +1,39 @@ |
1734 | +/* |
1735 | + * Copyright (C) 2017 by the Widelands Development Team |
1736 | + * |
1737 | + * This program is free software; you can redistribute it and/or |
1738 | + * modify it under the terms of the GNU General Public License |
1739 | + * as published by the Free Software Foundation; either version 2 |
1740 | + * of the License, or (at your option) any later version. |
1741 | + * |
1742 | + * This program is distributed in the hope that it will be useful, |
1743 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1744 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1745 | + * GNU General Public License for more details. |
1746 | + * |
1747 | + * You should have received a copy of the GNU General Public License |
1748 | + * along with this program; if not, write to the Free Software |
1749 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
1750 | + * |
1751 | + */ |
1752 | + |
1753 | +#ifndef WL_GRAPHIC_TEXT_TEXTURE_CACHE_H |
1754 | +#define WL_GRAPHIC_TEXT_TEXTURE_CACHE_H |
1755 | + |
1756 | +#include <memory> |
1757 | + |
1758 | +#include "graphic/image.h" |
1759 | +#include "graphic/text/transient_cache.h" |
1760 | + |
1761 | +class TextureCache : public TransientCache<Image> { |
1762 | +public: |
1763 | + TextureCache(uint32_t max_size_in_bytes) : TransientCache<Image>(max_size_in_bytes) { |
1764 | + } |
1765 | + |
1766 | + std::shared_ptr<const Image> insert(const std::string& hash, |
1767 | + std::shared_ptr<const Image> entry) override { |
1768 | + return TransientCache<Image>::insert(hash, entry, entry->width() * entry->height() * 4); |
1769 | + } |
1770 | +}; |
1771 | + |
1772 | +#endif // end of include guard: WL_GRAPHIC_TEXT_TEXTURE_CACHE_H |
1773 | |
1774 | === renamed file 'src/graphic/texture_cache.h' => 'src/graphic/text/transient_cache.h' |
1775 | --- src/graphic/texture_cache.h 2017-01-25 18:55:59 +0000 |
1776 | +++ src/graphic/text/transient_cache.h 2017-05-23 21:33:09 +0000 |
1777 | @@ -17,8 +17,8 @@ |
1778 | * |
1779 | */ |
1780 | |
1781 | -#ifndef WL_GRAPHIC_TEXTURE_CACHE_H |
1782 | -#define WL_GRAPHIC_TEXTURE_CACHE_H |
1783 | +#ifndef WL_GRAPHIC_TEXT_TRANSIENT_CACHE_H |
1784 | +#define WL_GRAPHIC_TEXT_TRANSIENT_CACHE_H |
1785 | |
1786 | #include <cassert> |
1787 | #include <list> |
1788 | @@ -26,55 +26,132 @@ |
1789 | #include <memory> |
1790 | #include <string> |
1791 | |
1792 | +#include <SDL.h> |
1793 | #include <boost/utility.hpp> |
1794 | |
1795 | #include "base/macros.h" |
1796 | |
1797 | -class Texture; |
1798 | +// The implementation took inspiration from |
1799 | +// https://timday.bitbucket.io/lru.html, but our use case here is a little |
1800 | +// different. |
1801 | |
1802 | -// Caches transient Surfaces, i.e. those that are always free to be deleted |
1803 | -// because they can be regenerated - somebody else must then recreate them when |
1804 | -// they are needed again. |
1805 | -// |
1806 | -// Nothing in Widelands should hold onto a Surface they get from this class, |
1807 | -// instead, they should use it only temporarily and rerequest it whenever they |
1808 | -// need it. |
1809 | -class TextureCache { |
1810 | +/// Caches transient rendered text. The entries will be kept until the memory limit is reached, |
1811 | +/// then the stalest entries will be deleted to make room for new entries. |
1812 | +/// |
1813 | +/// We use shared_ptr so that other objects can hold on to the textures if they need them more |
1814 | +/// permanently. |
1815 | +template <typename T> class TransientCache { |
1816 | public: |
1817 | - // Create a new Cache whichs combined pixels data in all transient surfaces |
1818 | - // are always below the 'max_size_in_bytes'. |
1819 | - TextureCache(uint32_t max_size_in_bytes); |
1820 | - ~TextureCache(); |
1821 | + /// Create a new cache in which the combined data size for all transient entries is always below |
1822 | + /// the 'max_size_in_arbitrary_unit'. |
1823 | + TransientCache(uint32_t max_size_in_arbitrary_unit); |
1824 | + virtual ~TransientCache(); |
1825 | |
1826 | - /// Deletes all surfaces in the cache leaving it as if it were just created. |
1827 | + /// Deletes all entries in the cache, leaving it as if it were just created. |
1828 | void flush(); |
1829 | |
1830 | /// Returns an entry if it is cached, nullptr otherwise. |
1831 | - Texture* get(const std::string& hash); |
1832 | - |
1833 | - // Inserts this entry into the TextureCache. asserts() that there is no |
1834 | - // entry with this hash already cached. Returns the given Surface for |
1835 | - // convenience. If 'transient' is false, this surface will not be deleted |
1836 | - // automatically - use this if surfaces are around for a long time and |
1837 | - // recreation is expensive (i.e. images loaded from disk). |
1838 | - Texture* insert(const std::string& hash, std::unique_ptr<Texture> texture); |
1839 | + std::shared_ptr<const T> get(const std::string& hash); |
1840 | + |
1841 | + |
1842 | + /// Inserts this entry of type T into the cache. Returns the given T for convenience. |
1843 | + /// When overriding this function, calculate the size of 'entry' and then call |
1844 | + /// insert(hash, entry, entry_size_in_size_unit). |
1845 | + virtual std::shared_ptr<const T> insert(const std::string& hash, |
1846 | + std::shared_ptr<const T> entry) = 0; |
1847 | + |
1848 | +protected: |
1849 | + /// Inserts this entry of type T into the cache. asserts() that there is no entry with this hash |
1850 | + /// already cached. Returns the given T for convenience. |
1851 | + std::shared_ptr<const T> insert(const std::string& hash, |
1852 | + std::shared_ptr<const T> entry, |
1853 | + uint32_t entry_size_in_size_unit); |
1854 | |
1855 | private: |
1856 | + /// Drop the oldest entry |
1857 | void drop(); |
1858 | |
1859 | using AccessHistory = std::list<std::string>; |
1860 | struct Entry { |
1861 | - std::unique_ptr<Texture> texture; |
1862 | + std::shared_ptr<const T> entry; |
1863 | + uint32_t size; |
1864 | uint32_t last_access; // Mainly for debugging and analysis. |
1865 | const AccessHistory::iterator list_iterator; |
1866 | }; |
1867 | |
1868 | - uint32_t max_size_in_bytes_; |
1869 | - uint32_t size_in_bytes_; |
1870 | + uint32_t max_size_in_size_unit_; |
1871 | + uint32_t size_in_size_unit_; |
1872 | std::map<std::string, Entry> entries_; |
1873 | AccessHistory access_history_; |
1874 | |
1875 | - DISALLOW_COPY_AND_ASSIGN(TextureCache); |
1876 | + DISALLOW_COPY_AND_ASSIGN(TransientCache); |
1877 | }; |
1878 | |
1879 | -#endif // end of include guard: WL_GRAPHIC_TEXTURE_CACHE_H |
1880 | +// Implementation |
1881 | + |
1882 | +template <typename T> |
1883 | +TransientCache<T>::TransientCache(uint32_t max_size_in_arbitrary_unit) |
1884 | + : max_size_in_size_unit_(max_size_in_arbitrary_unit), size_in_size_unit_(0) { |
1885 | +} |
1886 | +template <typename T> TransientCache<T>::~TransientCache() { |
1887 | + flush(); |
1888 | +} |
1889 | + |
1890 | +template <typename T> void TransientCache<T>::flush() { |
1891 | + access_history_.clear(); |
1892 | + size_in_size_unit_ = 0; |
1893 | + entries_.clear(); |
1894 | +} |
1895 | + |
1896 | +/// Returns an entry if it is cached, nullptr otherwise. |
1897 | +template <typename T> std::shared_ptr<const T> TransientCache<T>::get(const std::string& hash) { |
1898 | + const auto it = entries_.find(hash); |
1899 | + if (it == entries_.end()) { |
1900 | + return std::shared_ptr<const T>(nullptr); |
1901 | + } |
1902 | + |
1903 | + // Move this to the back of the access list to signal that we have used this |
1904 | + // recently and update last access time. |
1905 | + access_history_.splice(access_history_.end(), access_history_, it->second.list_iterator); |
1906 | + it->second.last_access = SDL_GetTicks(); |
1907 | + return it->second.entry; |
1908 | +} |
1909 | + |
1910 | +template <typename T> |
1911 | +std::shared_ptr<const T> TransientCache<T>::insert(const std::string& hash, |
1912 | + std::shared_ptr<const T> entry, |
1913 | + uint32_t entry_size_in_size_unit) { |
1914 | + assert(entries_.find(hash) == entries_.end()); |
1915 | + |
1916 | + while (!entries_.empty() && |
1917 | + size_in_size_unit_ + entry_size_in_size_unit > max_size_in_size_unit_) { |
1918 | + drop(); |
1919 | + } |
1920 | + |
1921 | + // Record hash as most-recently-used. |
1922 | + AccessHistory::iterator it = access_history_.insert(access_history_.end(), hash); |
1923 | + size_in_size_unit_ += entry_size_in_size_unit; |
1924 | + return entries_ |
1925 | + .insert(make_pair(hash, Entry{std::move(entry), entry_size_in_size_unit, SDL_GetTicks(), it})) |
1926 | + .first->second.entry; |
1927 | +} |
1928 | + |
1929 | +template <typename T> void TransientCache<T>::drop() { |
1930 | + assert(!access_history_.empty()); |
1931 | + |
1932 | + // Identify least recently used key |
1933 | + const auto it = entries_.find(access_history_.front()); |
1934 | + assert(it != entries_.end()); |
1935 | + |
1936 | + size_in_size_unit_ -= it->second.size; |
1937 | + // TODO(GunChleoc): Remove the following line once everything is converted to the new font |
1938 | + // renderer and all testing has been done. |
1939 | + // log("TransientCache: Dropping %d bytes, new size %d. Hash: %s\n", it->second.size, |
1940 | + // size_in_size_unit_, it->first.c_str()); |
1941 | + |
1942 | + // Erase both elements to completely purge record |
1943 | + entries_.erase(it); |
1944 | + access_history_.pop_front(); |
1945 | +} |
1946 | + |
1947 | +#endif // end of include guard: WL_GRAPHIC_TEXT_TRANSIENT_CACHE_H |
1948 | |
1949 | === modified file 'src/graphic/text_layout.cc' |
1950 | --- src/graphic/text_layout.cc 2017-05-13 13:14:29 +0000 |
1951 | +++ src/graphic/text_layout.cc 2017-05-23 21:33:09 +0000 |
1952 | @@ -48,14 +48,15 @@ |
1953 | boost::replace_all(*text, "&", "&"); // Must be performed last |
1954 | } |
1955 | |
1956 | -uint32_t text_width(const std::string& text, int ptsize) { |
1957 | +int text_width(const std::string& text, int ptsize) { |
1958 | return UI::g_fh1->render(as_editorfont(text, ptsize - UI::g_fh1->fontset()->size_offset())) |
1959 | ->width(); |
1960 | } |
1961 | |
1962 | -uint32_t text_height(const std::string& text, int ptsize) { |
1963 | - return UI::g_fh1->render(as_editorfont(text.empty() ? "." : text, |
1964 | - ptsize - UI::g_fh1->fontset()->size_offset())) |
1965 | +int text_height(int ptsize, UI::FontSet::Face face) { |
1966 | + return UI::g_fh1 |
1967 | + ->render(as_aligned(UI::g_fh1->fontset()->representative_character(), UI::Align::kLeft, |
1968 | + ptsize - UI::g_fh1->fontset()->size_offset(), RGBColor(0, 0, 0), face)) |
1969 | ->height(); |
1970 | } |
1971 | |
1972 | @@ -162,8 +163,10 @@ |
1973 | .str()); |
1974 | } |
1975 | |
1976 | -const Image* autofit_ui_text(const std::string& text, int width, RGBColor color, int fontsize) { |
1977 | - const Image* result = UI::g_fh1->render(as_uifont(richtext_escape(text), fontsize, color)); |
1978 | +std::shared_ptr<const UI::RenderedText> |
1979 | +autofit_ui_text(const std::string& text, int width, RGBColor color, int fontsize) { |
1980 | + std::shared_ptr<const UI::RenderedText> result = |
1981 | + UI::g_fh1->render(as_uifont(richtext_escape(text), fontsize, color)); |
1982 | if (width > 0) { // Autofit |
1983 | for (; result->width() > width && fontsize >= kMinimumFontSize; --fontsize) { |
1984 | result = UI::g_fh1->render( |
1985 | @@ -179,9 +182,14 @@ |
1986 | * This mirrors the horizontal alignment for RTL languages. |
1987 | * |
1988 | * Do not store this value as it is based on the global font setting. |
1989 | + * |
1990 | + * If 'checkme' is not empty, mirror the alignment if the first 20 characters contain an RTL |
1991 | + * character. Otherwise, mirror if the current fontset is RTL. |
1992 | */ |
1993 | -Align mirror_alignment(Align alignment) { |
1994 | - if (UI::g_fh1->fontset()->is_rtl()) { |
1995 | +Align mirror_alignment(Align alignment, const std::string& checkme) { |
1996 | + bool do_swap_alignment = checkme.empty() ? UI::g_fh1->fontset()->is_rtl() : |
1997 | + i18n::has_rtl_character(checkme.c_str(), 20); |
1998 | + if (do_swap_alignment) { |
1999 | switch (alignment) { |
2000 | case Align::kLeft: |
2001 | alignment = Align::kRight; |
2002 | |
2003 | === modified file 'src/graphic/text_layout.h' |
2004 | --- src/graphic/text_layout.h 2017-05-13 13:14:29 +0000 |
2005 | +++ src/graphic/text_layout.h 2017-05-23 21:33:09 +0000 |
2006 | @@ -24,6 +24,7 @@ |
2007 | |
2008 | #include "graphic/align.h" |
2009 | #include "graphic/color.h" |
2010 | +#include "graphic/font_handler1.h" |
2011 | #include "graphic/image.h" |
2012 | #include "graphic/text/font_set.h" |
2013 | #include "graphic/text_constants.h" |
2014 | @@ -38,14 +39,13 @@ |
2015 | * Returns the exact width of the text rendered as editorfont for the given font size. |
2016 | * This function is inefficient; only call when we need the exact width. |
2017 | */ |
2018 | - |
2019 | -uint32_t text_width(const std::string& text, int ptsize); |
2020 | +int text_width(const std::string& text, int ptsize = UI_FONT_SIZE_SMALL); |
2021 | |
2022 | /** |
2023 | - * Returns the exact height of the text rendered as editorfont for the given font size. |
2024 | + * Returns the exact height of the text rendered for the given font size and face. |
2025 | * This function is inefficient; only call when we need the exact height. |
2026 | */ |
2027 | -uint32_t text_height(const std::string& text, int ptsize); |
2028 | +int text_height(int ptsize = UI_FONT_SIZE_SMALL, UI::FontSet::Face face = UI::FontSet::Face::kSans); |
2029 | |
2030 | /** |
2031 | * Checks it the given string is RichText or not. Does not do validity checking. |
2032 | @@ -95,14 +95,14 @@ |
2033 | * smaller until it fits 'width'. The resulting font size will not go below |
2034 | * 'kMinimumFontSize'. |
2035 | */ |
2036 | -const Image* autofit_ui_text(const std::string& text, |
2037 | - int width = 0, |
2038 | - RGBColor color = UI_FONT_CLR_FG, |
2039 | - int fontsize = UI_FONT_SIZE_SMALL); |
2040 | +std::shared_ptr<const UI::RenderedText> autofit_ui_text(const std::string& text, |
2041 | + int width = 0, |
2042 | + RGBColor color = UI_FONT_CLR_FG, |
2043 | + int fontsize = UI_FONT_SIZE_SMALL); |
2044 | |
2045 | namespace UI { |
2046 | |
2047 | -Align mirror_alignment(Align alignment); |
2048 | +Align mirror_alignment(Align alignment, const std::string& checkme = ""); |
2049 | |
2050 | void center_vertically(uint32_t h, Vector2i* pt); |
2051 | void correct_for_align(Align, uint32_t w, Vector2i* pt); |
2052 | |
2053 | === modified file 'src/graphic/texture.cc' |
2054 | --- src/graphic/texture.cc 2016-10-24 14:07:28 +0000 |
2055 | +++ src/graphic/texture.cc 2017-05-23 21:33:09 +0000 |
2056 | @@ -172,7 +172,7 @@ |
2057 | void Texture::init(uint16_t w, uint16_t h) { |
2058 | blit_data_ = { |
2059 | 0, // initialized below |
2060 | - w, h, Rectf(0, 0, w, h), |
2061 | + w, h, Rectf(0.f, 0.f, w, h), |
2062 | }; |
2063 | if (w * h == 0) { |
2064 | return; |
2065 | |
2066 | === modified file 'src/graphic/texture_atlas.cc' |
2067 | --- src/graphic/texture_atlas.cc 2017-01-25 18:55:59 +0000 |
2068 | +++ src/graphic/texture_atlas.cc 2017-05-23 21:33:09 +0000 |
2069 | @@ -134,13 +134,13 @@ |
2070 | } |
2071 | |
2072 | std::unique_ptr<Texture> texture_atlas(new Texture(root->r.w, root->r.h)); |
2073 | - texture_atlas->fill_rect(Rectf(0, 0, root->r.w, root->r.h), RGBAColor(0, 0, 0, 0)); |
2074 | + texture_atlas->fill_rect(Rectf(0.f, 0.f, root->r.w, root->r.h), RGBAColor(0, 0, 0, 0)); |
2075 | |
2076 | const auto packed_texture_id = texture_atlas->blit_data().texture_id; |
2077 | for (Block& block : packed) { |
2078 | texture_atlas->blit( |
2079 | Rectf(block.node->r.x, block.node->r.y, block.texture->width(), block.texture->height()), |
2080 | - *block.texture, Rectf(0, 0, block.texture->width(), block.texture->height()), 1., |
2081 | + *block.texture, Rectf(0.f, 0.f, block.texture->width(), block.texture->height()), 1., |
2082 | BlendMode::Copy); |
2083 | |
2084 | pack_info->emplace_back(PackedTexture( |
2085 | |
2086 | === removed file 'src/graphic/texture_cache.cc' |
2087 | --- src/graphic/texture_cache.cc 2017-01-25 18:55:59 +0000 |
2088 | +++ src/graphic/texture_cache.cc 1970-01-01 00:00:00 +0000 |
2089 | @@ -1,87 +0,0 @@ |
2090 | -/* |
2091 | - * Copyright (C) 2006-2017 by the Widelands Development Team |
2092 | - * |
2093 | - * This program is free software; you can redistribute it and/or |
2094 | - * modify it under the terms of the GNU General Public License |
2095 | - * as published by the Free Software Foundation; either version 2 |
2096 | - * of the License, or (at your option) any later version. |
2097 | - * |
2098 | - * This program is distributed in the hope that it will be useful, |
2099 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2100 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2101 | - * GNU General Public License for more details. |
2102 | - * |
2103 | - * You should have received a copy of the GNU General Public License |
2104 | - * along with this program; if not, write to the Free Software |
2105 | - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
2106 | - * |
2107 | - */ |
2108 | - |
2109 | -#include "graphic/texture_cache.h" |
2110 | - |
2111 | -#include <memory> |
2112 | - |
2113 | -#include <SDL.h> |
2114 | -#include <stdint.h> |
2115 | - |
2116 | -#include "graphic/texture.h" |
2117 | - |
2118 | -// The implementation took inspiration from |
2119 | -// http://timday.bitbucket.org/lru.html, but our use case here is a little |
2120 | -// different. |
2121 | - |
2122 | -TextureCache::TextureCache(uint32_t max_size_in_bytes) |
2123 | - : max_size_in_bytes_(max_size_in_bytes), size_in_bytes_(0) { |
2124 | -} |
2125 | - |
2126 | -TextureCache::~TextureCache() { |
2127 | - flush(); |
2128 | -} |
2129 | - |
2130 | -void TextureCache::flush() { |
2131 | - entries_.clear(); |
2132 | - access_history_.clear(); |
2133 | - size_in_bytes_ = 0; |
2134 | -} |
2135 | - |
2136 | -Texture* TextureCache::get(const std::string& hash) { |
2137 | - const auto it = entries_.find(hash); |
2138 | - if (it == entries_.end()) |
2139 | - return nullptr; |
2140 | - |
2141 | - // Move this to the back of the access list to signal that we have used this |
2142 | - // recently and update last access time. |
2143 | - access_history_.splice(access_history_.end(), access_history_, it->second.list_iterator); |
2144 | - it->second.last_access = SDL_GetTicks(); |
2145 | - return it->second.texture.get(); |
2146 | -} |
2147 | - |
2148 | -Texture* TextureCache::insert(const std::string& hash, std::unique_ptr<Texture> texture) { |
2149 | - assert(entries_.find(hash) == entries_.end()); |
2150 | - |
2151 | - const uint32_t texture_size = texture->width() * texture->height() * 4; |
2152 | - while (size_in_bytes_ + texture_size > max_size_in_bytes_) { |
2153 | - drop(); |
2154 | - } |
2155 | - |
2156 | - // Record hash as most-recently-used. |
2157 | - AccessHistory::iterator it = access_history_.insert(access_history_.end(), hash); |
2158 | - size_in_bytes_ += texture_size; |
2159 | - return entries_.insert(make_pair(hash, Entry{std::move(texture), SDL_GetTicks(), it})) |
2160 | - .first->second.texture.get(); |
2161 | -} |
2162 | - |
2163 | -void TextureCache::drop() { |
2164 | - assert(!access_history_.empty()); |
2165 | - |
2166 | - // Identify least recently used key |
2167 | - const auto it = entries_.find(access_history_.front()); |
2168 | - assert(it != entries_.end()); |
2169 | - |
2170 | - const uint32_t texture_size = it->second.texture->width() * it->second.texture->height() * 4; |
2171 | - size_in_bytes_ -= texture_size; |
2172 | - |
2173 | - // Erase both elements to completely purge record |
2174 | - entries_.erase(it); |
2175 | - access_history_.pop_front(); |
2176 | -} |
2177 | |
2178 | === modified file 'src/graphic/wordwrap.cc' |
2179 | --- src/graphic/wordwrap.cc 2017-05-13 13:14:29 +0000 |
2180 | +++ src/graphic/wordwrap.cc 2017-05-23 21:33:09 +0000 |
2181 | @@ -119,7 +119,7 @@ |
2182 | } |
2183 | |
2184 | // Optimism: perhaps the entire line fits? |
2185 | - if (text_width(text.substr(line_start, orig_end - line_start), fontsize_) <= |
2186 | + if (uint32_t(text_width(text.substr(line_start, orig_end - line_start), fontsize_)) <= |
2187 | wrapwidth_ - safety_margin) { |
2188 | line_end = orig_end; |
2189 | next_line_start = orig_end + 1; |
2190 | @@ -192,7 +192,8 @@ |
2191 | // Now make sure that it really fits. |
2192 | std::string::size_type test_cutoff = line_start + end * 2 / 3; |
2193 | while ((end > 0) && (static_cast<uint32_t>(line_start + end) > test_cutoff)) { |
2194 | - if (text_width(text.substr(line_start, end), fontsize_) > wrapwidth_ - safety_margin) { |
2195 | + if (uint32_t(text_width(text.substr(line_start, end), fontsize_)) > |
2196 | + wrapwidth_ - safety_margin) { |
2197 | --end; |
2198 | } else { |
2199 | break; |
2200 | @@ -223,7 +224,7 @@ |
2201 | // calc_width_for_wrapping is fast, but it will underestimate the width. |
2202 | // So, we test again with text_width to make sure that the line really fits. |
2203 | return quick_width(i18n::make_ligatures(text.c_str())) <= wrapwidth_ - safety_margin && |
2204 | - text_width(text, fontsize_) <= wrapwidth_ - safety_margin; |
2205 | + uint32_t(text_width(text, fontsize_)) <= wrapwidth_ - safety_margin; |
2206 | } |
2207 | |
2208 | /** |
2209 | @@ -247,12 +248,7 @@ |
2210 | * Compute the total height of the word-wrapped text. |
2211 | */ |
2212 | uint32_t WordWrap::height() const { |
2213 | - uint16_t fontheight = 0; |
2214 | - if (!lines_.empty()) { |
2215 | - fontheight = text_height(lines_[0].text, fontsize_); |
2216 | - } |
2217 | - |
2218 | - return fontheight * (lines_.size()) + 2 * kLineMargin; |
2219 | + return text_height(fontsize_) * (lines_.size()) + 2 * kLineMargin; |
2220 | } |
2221 | |
2222 | /** |
2223 | @@ -305,9 +301,9 @@ |
2224 | |
2225 | Align alignment = mirror_alignment(align); |
2226 | |
2227 | - uint16_t fontheight = text_height(lines_[0].text, fontsize_); |
2228 | + const int fontheight = text_height(fontsize_); |
2229 | for (uint32_t line = 0; line < lines_.size(); ++line, where.y += fontheight) { |
2230 | - if (where.y >= dst.height() || int32_t(where.y + fontheight) <= 0) |
2231 | + if (where.y >= dst.height() || (where.y + fontheight) <= 0) |
2232 | continue; |
2233 | |
2234 | Vector2i point(where.x, where.y); |
2235 | @@ -316,10 +312,10 @@ |
2236 | point.x += wrapwidth_ - kLineMargin; |
2237 | } |
2238 | |
2239 | - const Image* entry_text_im = UI::g_fh1->render( |
2240 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render( |
2241 | as_editorfont(lines_[line].text, fontsize_ - UI::g_fh1->fontset()->size_offset(), color_)); |
2242 | - UI::correct_for_align(alignment, entry_text_im->width(), &point); |
2243 | - dst.blit(point, entry_text_im); |
2244 | + UI::correct_for_align(alignment, rendered_text->width(), &point); |
2245 | + rendered_text->draw(dst, point); |
2246 | |
2247 | if (draw_caret_ && line == caretline) { |
2248 | std::string line_to_caret = lines_[line].text.substr(0, caretpos); |
2249 | |
2250 | === modified file 'src/graphic/wordwrap.h' |
2251 | --- src/graphic/wordwrap.h 2017-04-29 14:57:30 +0000 |
2252 | +++ src/graphic/wordwrap.h 2017-05-23 21:33:09 +0000 |
2253 | @@ -27,8 +27,8 @@ |
2254 | #include "base/vector.h" |
2255 | #include "graphic/align.h" |
2256 | #include "graphic/color.h" |
2257 | +#include "graphic/text/sdl_ttf_font.h" |
2258 | #include "graphic/text_constants.h" |
2259 | -#include "graphic/text/sdl_ttf_font.h" |
2260 | |
2261 | class RenderTarget; |
2262 | |
2263 | |
2264 | === modified file 'src/logic/editor_game_base.h' |
2265 | --- src/logic/editor_game_base.h 2017-02-14 19:59:29 +0000 |
2266 | +++ src/logic/editor_game_base.h 2017-05-23 21:33:09 +0000 |
2267 | @@ -36,7 +36,7 @@ |
2268 | namespace UI { |
2269 | struct ProgressWindow; |
2270 | } |
2271 | -struct FullscreenMenuLaunchGame; |
2272 | +class FullscreenMenuLaunchGame; |
2273 | class InteractiveBase; |
2274 | class InteractiveGameBase; // TODO(GunChleoc): Get rid |
2275 | |
2276 | @@ -78,7 +78,7 @@ |
2277 | class EditorGameBase { |
2278 | public: |
2279 | friend class InteractiveBase; |
2280 | - friend struct FullscreenMenuLaunchGame; |
2281 | + friend class FullscreenMenuLaunchGame; |
2282 | friend struct GameClassPacket; |
2283 | |
2284 | EditorGameBase(LuaInterface* lua); |
2285 | |
2286 | === modified file 'src/logic/map_objects/map_object.cc' |
2287 | --- src/logic/map_objects/map_object.cc 2017-05-13 18:48:26 +0000 |
2288 | +++ src/logic/map_objects/map_object.cc 2017-05-23 21:33:09 +0000 |
2289 | @@ -20,6 +20,7 @@ |
2290 | #include "logic/map_objects/map_object.h" |
2291 | |
2292 | #include <algorithm> |
2293 | +#include <cmath> |
2294 | #include <cstdarg> |
2295 | #include <cstdio> |
2296 | #include <cstring> |
2297 | @@ -463,30 +464,26 @@ |
2298 | } |
2299 | |
2300 | // Rendering text is expensive, so let's just do it for only a few sizes. |
2301 | - scale = std::round(scale); |
2302 | - if (scale == 0.f) { |
2303 | + // The forumla is a bit fancy to avoid too much text overlap. |
2304 | + scale = std::round(2.f * (scale > 1.f ? std::sqrt(scale) : std::pow(scale, 2.f))) / 2.f; |
2305 | + if (scale < 1.f) { |
2306 | return; |
2307 | } |
2308 | const int font_size = scale * UI_FONT_SIZE_SMALL; |
2309 | |
2310 | // We always render this so we can have a stable position for the statistics string. |
2311 | - const Image* rendered_census_info = |
2312 | - UI::g_fh1->render(as_condensed(census, UI::Align::kCenter, font_size), 120); |
2313 | - |
2314 | - const Vector2i base_pos = field_on_dst.cast<int>() - Vector2i(0, 48) * scale; |
2315 | - Vector2i census_pos(base_pos); |
2316 | - UI::correct_for_align(UI::Align::kCenter, rendered_census_info->width(), &census_pos); |
2317 | + std::shared_ptr<const UI::RenderedText> rendered_census = |
2318 | + UI::g_fh1->render(as_condensed(census, UI::Align::kCenter, font_size), 120 * scale); |
2319 | + Vector2i position = field_on_dst.cast<int>() - Vector2i(0, 48) * scale; |
2320 | if (draw_text & TextToDraw::kCensus) { |
2321 | - dst->blit(census_pos, rendered_census_info, BlendMode::UseAlpha); |
2322 | + rendered_census->draw(*dst, position, UI::Align::kCenter); |
2323 | } |
2324 | |
2325 | if (draw_text & TextToDraw::kStatistics && !statictics.empty()) { |
2326 | - Vector2i statistics_pos = |
2327 | - base_pos + Vector2i(0, rendered_census_info->height() / 2 + 10 * scale); |
2328 | - const Image* rendered_statictics = |
2329 | + std::shared_ptr<const UI::RenderedText> rendered_statistics = |
2330 | UI::g_fh1->render(as_condensed(statictics, UI::Align::kCenter, font_size)); |
2331 | - UI::correct_for_align(UI::Align::kCenter, rendered_statictics->width(), &statistics_pos); |
2332 | - dst->blit(statistics_pos, rendered_statictics, BlendMode::UseAlpha); |
2333 | + position.y += rendered_census->height() + text_height(font_size) / 4; |
2334 | + rendered_statistics->draw(*dst, position, UI::Align::kCenter); |
2335 | } |
2336 | } |
2337 | |
2338 | |
2339 | === modified file 'src/network/network.h' |
2340 | --- src/network/network.h 2017-05-11 16:13:34 +0000 |
2341 | +++ src/network/network.h 2017-05-23 21:33:09 +0000 |
2342 | @@ -119,7 +119,7 @@ |
2343 | bool end_of_file() const override; |
2344 | |
2345 | private: |
2346 | - friend struct Deserializer; |
2347 | + friend class Deserializer; |
2348 | std::vector<uint8_t> buffer; |
2349 | size_t index_; |
2350 | }; |
2351 | |
2352 | === modified file 'src/ui_basic/button.cc' |
2353 | --- src/ui_basic/button.cc 2017-05-14 14:40:24 +0000 |
2354 | +++ src/ui_basic/button.cc 2017-05-23 21:33:09 +0000 |
2355 | @@ -58,9 +58,7 @@ |
2356 | clr_down_(229, 161, 2) { |
2357 | // Automatically resize for font height and give it a margin. |
2358 | if (h < 1) { |
2359 | - int new_height = |
2360 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + |
2361 | - 4; |
2362 | + int new_height = text_height() + 4; |
2363 | set_desired_size(w, new_height); |
2364 | set_size(w, new_height); |
2365 | } |
2366 | @@ -207,13 +205,12 @@ |
2367 | |
2368 | } else if (title_.length()) { |
2369 | // Otherwise draw title string centered |
2370 | - const Image* entry_text_im = |
2371 | + std::shared_ptr<const UI::RenderedText> rendered_text = |
2372 | autofit_ui_text(title_, get_inner_w() - 2 * kButtonImageMargin, |
2373 | is_monochrome ? UI_FONT_CLR_DISABLED : UI_FONT_CLR_FG); |
2374 | // Blit on pixel boundary (not float), so that the text is blitted pixel perfect. |
2375 | - dst.blit( |
2376 | - Vector2i((get_w() - entry_text_im->width()) / 2, (get_h() - entry_text_im->height()) / 2), |
2377 | - entry_text_im); |
2378 | + rendered_text->draw(dst, Vector2i((get_w() - rendered_text->width()) / 2, |
2379 | + (get_h() - rendered_text->height()) / 2)); |
2380 | } |
2381 | |
2382 | // draw border |
2383 | |
2384 | === modified file 'src/ui_basic/checkbox.cc' |
2385 | --- src/ui_basic/checkbox.cc 2017-04-22 12:19:21 +0000 |
2386 | +++ src/ui_basic/checkbox.cc 2017-05-23 21:33:09 +0000 |
2387 | @@ -45,8 +45,8 @@ |
2388 | : Panel(parent, p.x, p.y, kStateboxSize, kStateboxSize, tooltip_text), |
2389 | flags_(Is_Enabled), |
2390 | pic_graphics_(pic), |
2391 | - label_text_(""), |
2392 | - rendered_text_(nullptr) { |
2393 | + rendered_text_(nullptr), |
2394 | + label_text_("") { |
2395 | uint16_t w = pic->width(); |
2396 | uint16_t h = pic->height(); |
2397 | set_desired_size(w, h); |
2398 | @@ -63,8 +63,8 @@ |
2399 | : Panel(parent, p.x, p.y, std::max(width, kStateboxSize), kStateboxSize, tooltip_text), |
2400 | flags_(Is_Enabled), |
2401 | pic_graphics_(g_gr->images().get("images/ui_basic/checkbox_light.png")), |
2402 | - label_text_(label_text), |
2403 | - rendered_text_(nullptr) { |
2404 | + rendered_text_(nullptr), |
2405 | + label_text_(label_text) { |
2406 | set_flags(Has_Text, !label_text_.empty()); |
2407 | layout(); |
2408 | } |
2409 | @@ -83,7 +83,7 @@ |
2410 | rendered_text_ = label_text_.empty() ? |
2411 | nullptr : |
2412 | UI::g_fh1->render(as_uifont(label_text_), text_width(get_w(), pic_width)); |
2413 | - if (rendered_text_) { |
2414 | + if (rendered_text_.get()) { |
2415 | w = std::max(rendered_text_->width() + kPadding + pic_width, w); |
2416 | h = std::max(rendered_text_->height(), h); |
2417 | } |
2418 | @@ -144,16 +144,16 @@ |
2419 | } else { |
2420 | static_assert(0 <= kStateboxSize, "assert(0 <= STATEBOX_WIDTH) failed."); |
2421 | static_assert(0 <= kStateboxSize, "assert(0 <= STATEBOX_HEIGHT) failed."); |
2422 | - Vector2i image_anchor(0, 0); |
2423 | + Vector2i image_anchor = Vector2i::zero(); |
2424 | Vector2i text_anchor(kStateboxSize + kPadding, 0); |
2425 | |
2426 | - if (rendered_text_) { |
2427 | + if (rendered_text_.get()) { |
2428 | if (UI::g_fh1->fontset()->is_rtl()) { |
2429 | text_anchor.x = 0; |
2430 | image_anchor.x = rendered_text_->width() + kPadding; |
2431 | image_anchor.y = (get_h() - kStateboxSize) / 2; |
2432 | } |
2433 | - dst.blit(text_anchor, rendered_text_, BlendMode::UseAlpha); |
2434 | + rendered_text_->draw(dst, text_anchor); |
2435 | } |
2436 | |
2437 | dst.blitrect( |
2438 | |
2439 | === modified file 'src/ui_basic/checkbox.h' |
2440 | --- src/ui_basic/checkbox.h 2017-02-12 09:10:57 +0000 |
2441 | +++ src/ui_basic/checkbox.h 2017-05-23 21:33:09 +0000 |
2442 | @@ -90,8 +90,8 @@ |
2443 | flags_ |= flags; |
2444 | } |
2445 | const Image* pic_graphics_; |
2446 | + std::shared_ptr<const UI::RenderedText> rendered_text_; |
2447 | const std::string label_text_; |
2448 | - const Image* rendered_text_; |
2449 | }; |
2450 | |
2451 | /** |
2452 | |
2453 | === modified file 'src/ui_basic/editbox.cc' |
2454 | --- src/ui_basic/editbox.cc 2017-05-13 11:25:24 +0000 |
2455 | +++ src/ui_basic/editbox.cc 2017-05-23 21:33:09 +0000 |
2456 | @@ -80,15 +80,7 @@ |
2457 | int margin_y, |
2458 | const Image* background, |
2459 | int font_size) |
2460 | - : Panel(parent, |
2461 | - x, |
2462 | - y, |
2463 | - w, |
2464 | - h > 0 ? h : |
2465 | - UI::g_fh1->render(as_editorfont(UI::g_fh1->fontset()->representative_character(), |
2466 | - font_size)) |
2467 | - ->height() + |
2468 | - 2 * margin_y), |
2469 | + : Panel(parent, x, y, w, h > 0 ? h : text_height(font_size) + 2 * margin_y), |
2470 | m_(new EditBoxImpl), |
2471 | history_active_(false), |
2472 | history_position_(-1) { |
2473 | @@ -380,15 +372,10 @@ |
2474 | |
2475 | const int max_width = get_w() - 2 * kMarginX; |
2476 | |
2477 | - const Image* entry_text_im = UI::g_fh1->render(as_editorfont(m_->text, m_->fontsize)); |
2478 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(as_editorfont(m_->text, m_->fontsize)); |
2479 | |
2480 | - const int linewidth = entry_text_im->width(); |
2481 | - const int lineheight = |
2482 | - m_->text.empty() ? |
2483 | - UI::g_fh1->render( |
2484 | - as_editorfont(UI::g_fh1->fontset()->representative_character(), m_->fontsize)) |
2485 | - ->height() : |
2486 | - entry_text_im->height(); |
2487 | + const int linewidth = rendered_text->width(); |
2488 | + const int lineheight = m_->text.empty() ? text_height(m_->fontsize) : rendered_text->height(); |
2489 | |
2490 | Vector2i point(kMarginX, get_h() / 2); |
2491 | if (m_->align == UI::Align::kRight) { |
2492 | @@ -405,18 +392,18 @@ |
2493 | // We want this always on, e.g. for mixed language savegame filenames |
2494 | if (i18n::has_rtl_character(m_->text.c_str(), 100)) { // Restrict check for efficiency |
2495 | // TODO(GunChleoc): Arabic: Fix scrolloffset |
2496 | - dst.blitrect(point, entry_text_im, Recti(linewidth - max_width, 0, linewidth, lineheight)); |
2497 | + rendered_text->draw(dst, point, Recti(linewidth - max_width, 0, linewidth, lineheight)); |
2498 | } else { |
2499 | if (m_->align == UI::Align::kRight) { |
2500 | // TODO(GunChleoc): Arabic: Fix scrolloffset |
2501 | - dst.blitrect(point, entry_text_im, |
2502 | - Recti(point.x + m_->scrolloffset + kMarginX, 0, max_width, lineheight)); |
2503 | + rendered_text->draw( |
2504 | + dst, point, Recti(point.x + m_->scrolloffset + kMarginX, 0, max_width, lineheight)); |
2505 | } else { |
2506 | - dst.blitrect(point, entry_text_im, Recti(-m_->scrolloffset, 0, max_width, lineheight)); |
2507 | + rendered_text->draw(dst, point, Recti(-m_->scrolloffset, 0, max_width, lineheight)); |
2508 | } |
2509 | } |
2510 | } else { |
2511 | - dst.blitrect(point, entry_text_im, Recti(0, 0, max_width, lineheight)); |
2512 | + rendered_text->draw(dst, point, Recti(0, 0, max_width, lineheight)); |
2513 | } |
2514 | |
2515 | if (has_focus()) { |
2516 | @@ -425,7 +412,7 @@ |
2517 | // TODO(GunChleoc): Arabic: Fix cursor position for BIDI text. |
2518 | int caret_x = text_width(line_to_caret, m_->fontsize); |
2519 | |
2520 | - const uint16_t fontheight = text_height(m_->text, m_->fontsize); |
2521 | + const uint16_t fontheight = text_height(m_->fontsize); |
2522 | |
2523 | const Image* caret_image = g_gr->images().get("images/ui_basic/caret.png"); |
2524 | Vector2i caretpt = Vector2i::zero(); |
2525 | |
2526 | === modified file 'src/ui_basic/listselect.cc' |
2527 | --- src/ui_basic/listselect.cc 2017-05-14 14:40:24 +0000 |
2528 | +++ src/ui_basic/listselect.cc 2017-05-23 21:33:09 +0000 |
2529 | @@ -53,9 +53,7 @@ |
2530 | const Image* button_background, |
2531 | const ListselectLayout selection_mode) |
2532 | : Panel(parent, x, y, w, h), |
2533 | - lineheight_( |
2534 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + |
2535 | - kMargin), |
2536 | + lineheight_(text_height() + kMargin), |
2537 | scrollbar_(this, get_w() - Scrollbar::kSize, 0, Scrollbar::kSize, h, button_background), |
2538 | scrollpos_(0), |
2539 | selection_(no_selection_index()), |
2540 | @@ -333,10 +331,10 @@ |
2541 | if (selection_mode_ == ListselectLayout::kDropdown) { |
2542 | for (size_t i = 0; i < entry_records_.size(); ++i) { |
2543 | const EntryRecord& er = *entry_records_[i]; |
2544 | - const Image* entry_text_im = UI::g_fh1->render(as_uifont( |
2545 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(as_uifont( |
2546 | richtext_escape(er.name), UI_FONT_SIZE_SMALL, er.use_clr ? er.clr : UI_FONT_CLR_FG)); |
2547 | int picw = max_pic_width_ ? max_pic_width_ + 10 : 0; |
2548 | - int difference = entry_text_im->width() + picw + 8 - get_eff_w(); |
2549 | + int difference = rendered_text->width() + picw + 8 - get_eff_w(); |
2550 | if (difference > 0) { |
2551 | set_size(get_w() + difference, get_h()); |
2552 | } |
2553 | @@ -378,10 +376,10 @@ |
2554 | assert(eff_h < std::numeric_limits<int32_t>::max()); |
2555 | |
2556 | const EntryRecord& er = *entry_records_[idx]; |
2557 | - const Image* entry_text_im = UI::g_fh1->render(as_uifont( |
2558 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(as_uifont( |
2559 | richtext_escape(er.name), UI_FONT_SIZE_SMALL, er.use_clr ? er.clr : UI_FONT_CLR_FG)); |
2560 | |
2561 | - int lineheight = std::max(get_lineheight(), entry_text_im->height()); |
2562 | + int lineheight = std::max(get_lineheight(), rendered_text->height()); |
2563 | |
2564 | // Don't draw over the bottom edge |
2565 | lineheight = std::min(eff_h - y, lineheight); |
2566 | @@ -421,23 +419,22 @@ |
2567 | er.pic); |
2568 | } |
2569 | |
2570 | + // Position the text according to alignment |
2571 | Align alignment = i18n::has_rtl_character(er.name.c_str(), 20) ? Align::kRight : Align::kLeft; |
2572 | if (alignment == UI::Align::kRight) { |
2573 | point.x += maxw - picw; |
2574 | } |
2575 | |
2576 | - UI::correct_for_align(alignment, entry_text_im->width(), &point); |
2577 | - |
2578 | // Shift for image width |
2579 | if (!UI::g_fh1->fontset()->is_rtl()) { |
2580 | point.x += picw; |
2581 | } |
2582 | |
2583 | // Fix vertical position for mixed font heights |
2584 | - if (get_lineheight() > entry_text_im->height()) { |
2585 | - point.y += (lineheight_ - entry_text_im->height()) / 2; |
2586 | + if (get_lineheight() > rendered_text->height()) { |
2587 | + point.y += (lineheight_ - rendered_text->height()) / 2; |
2588 | } else { |
2589 | - point.y -= (entry_text_im->height() - lineheight_) / 2; |
2590 | + point.y -= (rendered_text->height() - lineheight_) / 2; |
2591 | } |
2592 | |
2593 | // Don't draw over the bottom edge |
2594 | @@ -445,21 +442,7 @@ |
2595 | if (lineheight < 0) { |
2596 | break; |
2597 | } |
2598 | - |
2599 | - // Crop to column width while blitting |
2600 | - if ((alignment == UI::Align::kRight) && |
2601 | - (maxw + picw) < static_cast<uint32_t>(entry_text_im->width())) { |
2602 | - // Fix positioning for BiDi languages. |
2603 | - point.x = 0; |
2604 | - |
2605 | - // We want this always on, e.g. for mixed language savegame filenames, or the languages |
2606 | - // list |
2607 | - dst.blitrect(point, entry_text_im, Recti(entry_text_im->width() - maxw + picw, 0, maxw, |
2608 | - entry_text_im->height())); |
2609 | - } else { |
2610 | - dst.blitrect(point, entry_text_im, Recti(0, 0, maxw, lineheight)); |
2611 | - } |
2612 | - |
2613 | + rendered_text->draw(dst, point, Recti(0, 0, maxw, lineheight), alignment, RenderedText::CropMode::kSelf); |
2614 | y += get_lineheight(); |
2615 | ++idx; |
2616 | } |
2617 | |
2618 | === modified file 'src/ui_basic/messagebox.cc' |
2619 | --- src/ui_basic/messagebox.cc 2017-02-23 17:58:25 +0000 |
2620 | +++ src/ui_basic/messagebox.cc 2017-05-23 21:33:09 +0000 |
2621 | @@ -48,14 +48,14 @@ |
2622 | const int margin = 5; |
2623 | int width, height = 0; |
2624 | { |
2625 | - const Image* temp_rendered_text = g_fh1->render(as_uifont(text), maxwidth); |
2626 | + std::shared_ptr<const UI::RenderedText> temp_rendered_text = g_fh1->render(as_uifont(text), maxwidth); |
2627 | width = temp_rendered_text->width(); |
2628 | height = temp_rendered_text->height(); |
2629 | } |
2630 | |
2631 | // Stupid heuristic to avoid excessively long lines |
2632 | if (height < 2 * UI_FONT_SIZE_SMALL) { |
2633 | - const Image* temp_rendered_text = g_fh1->render(as_uifont(text), maxwidth / 2); |
2634 | + std::shared_ptr<const UI::RenderedText> temp_rendered_text = g_fh1->render(as_uifont(text), maxwidth / 2); |
2635 | width = temp_rendered_text->width(); |
2636 | height = temp_rendered_text->height(); |
2637 | } |
2638 | |
2639 | === modified file 'src/ui_basic/multilineeditbox.cc' |
2640 | --- src/ui_basic/multilineeditbox.cc 2017-05-13 11:25:24 +0000 |
2641 | +++ src/ui_basic/multilineeditbox.cc 2017-05-23 21:33:09 +0000 |
2642 | @@ -91,7 +91,7 @@ |
2643 | const Image* button_background) |
2644 | : Panel(parent, x, y, w, h), d_(new Data(*this, button_background)) { |
2645 | d_->background = background; |
2646 | - d_->lineheight = text_height(g_fh1->fontset()->representative_character(), UI_FONT_SIZE_SMALL); |
2647 | + d_->lineheight = text_height(); |
2648 | set_handle_mouse(true); |
2649 | set_can_focus(true); |
2650 | set_thinks(false); |
2651 | |
2652 | === modified file 'src/ui_basic/multilinetextarea.cc' |
2653 | --- src/ui_basic/multilinetextarea.cc 2017-05-13 11:25:24 +0000 |
2654 | +++ src/ui_basic/multilinetextarea.cc 2017-05-23 21:33:09 +0000 |
2655 | @@ -55,10 +55,7 @@ |
2656 | |
2657 | scrollbar_.moved.connect(boost::bind(&MultilineTextarea::scrollpos_changed, this, _1)); |
2658 | |
2659 | - scrollbar_.set_singlestepsize( |
2660 | - UI::g_fh1->render( |
2661 | - as_uifont(UI::g_fh1->fontset()->representative_character(), UI_FONT_SIZE_SMALL)) |
2662 | - ->height()); |
2663 | + scrollbar_.set_singlestepsize(text_height()); |
2664 | scrollbar_.set_steps(1); |
2665 | scrollbar_.set_force_draw(scrollmode_ == ScrollMode::kScrollNormalForced || |
2666 | scrollmode_ == ScrollMode::kScrollLogForced); |
2667 | @@ -88,13 +85,10 @@ |
2668 | for (int i = 0; i < 2; ++i) { |
2669 | if (!is_richtext(text_)) { |
2670 | use_old_renderer_ = false; |
2671 | - const Image* text_im = |
2672 | - UI::g_fh1->render(make_richtext(), get_eff_w() - 2 * RICHTEXT_MARGIN); |
2673 | - height = text_im->height(); |
2674 | + height = UI::g_fh1->render(make_richtext(), get_eff_w() - 2 * RICHTEXT_MARGIN)->height(); |
2675 | } else if (force_new_renderer_) { |
2676 | use_old_renderer_ = false; |
2677 | - const Image* text_im = UI::g_fh1->render(text_, get_eff_w() - 2 * RICHTEXT_MARGIN); |
2678 | - height = text_im->height(); |
2679 | + height = UI::g_fh1->render(text_, get_eff_w() - 2 * RICHTEXT_MARGIN)->height(); |
2680 | } else { |
2681 | use_old_renderer_ = true; |
2682 | rt.set_width(get_eff_w() - 2 * RICHTEXT_MARGIN); |
2683 | @@ -150,19 +144,14 @@ |
2684 | if (use_old_renderer_) { |
2685 | rt.draw(dst, Vector2i(RICHTEXT_MARGIN, RICHTEXT_MARGIN - scrollbar_.get_scrollpos())); |
2686 | } else { |
2687 | - const Image* text_im; |
2688 | - if (!is_richtext(text_)) { |
2689 | - text_im = UI::g_fh1->render(make_richtext(), get_eff_w() - 2 * RICHTEXT_MARGIN); |
2690 | - } else { |
2691 | - text_im = UI::g_fh1->render(text_, get_eff_w() - 2 * RICHTEXT_MARGIN); |
2692 | - } |
2693 | - |
2694 | - uint32_t blit_width = std::min(text_im->width(), static_cast<int>(get_eff_w())); |
2695 | - uint32_t blit_height = std::min(text_im->height(), static_cast<int>(get_inner_h())); |
2696 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render( |
2697 | + is_richtext(text_) ? text_ : make_richtext(), get_eff_w() - 2 * RICHTEXT_MARGIN); |
2698 | + uint32_t blit_width = std::min(rendered_text->width(), static_cast<int>(get_eff_w())); |
2699 | + uint32_t blit_height = std::min(rendered_text->height(), get_inner_h()); |
2700 | |
2701 | if (blit_width > 0 && blit_height > 0) { |
2702 | int anchor = 0; |
2703 | - Align alignment = mirror_alignment(align_); |
2704 | + Align alignment = mirror_alignment(align_, text_); |
2705 | switch (alignment) { |
2706 | case UI::Align::kCenter: |
2707 | anchor = (get_eff_w() - blit_width) / 2; |
2708 | @@ -173,10 +162,8 @@ |
2709 | case UI::Align::kLeft: |
2710 | anchor = RICHTEXT_MARGIN; |
2711 | } |
2712 | - |
2713 | - dst.blitrect(Vector2i(anchor, 0), text_im, |
2714 | - Recti(0, scrollbar_.get_scrollpos(), blit_width, blit_height), |
2715 | - BlendMode::UseAlpha); |
2716 | + rendered_text->draw(dst, Vector2i(anchor, 0), |
2717 | + Recti(0, scrollbar_.get_scrollpos(), blit_width, blit_height)); |
2718 | } |
2719 | } |
2720 | } |
2721 | |
2722 | === modified file 'src/ui_basic/panel.cc' |
2723 | --- src/ui_basic/panel.cc 2017-05-13 11:25:24 +0000 |
2724 | +++ src/ui_basic/panel.cc 2017-05-23 21:33:09 +0000 |
2725 | @@ -1060,13 +1060,14 @@ |
2726 | text_to_render = as_tooltip(text); |
2727 | } |
2728 | |
2729 | - static const uint32_t TIP_WIDTH_MAX = 360; |
2730 | - const Image* rendered_text = g_fh1->render(text_to_render, TIP_WIDTH_MAX); |
2731 | - if (!rendered_text) { |
2732 | + constexpr uint32_t kTipWidthMax = 360; |
2733 | + std::shared_ptr<const UI::RenderedText> rendered_text = g_fh1->render(text_to_render, kTipWidthMax); |
2734 | + if (rendered_text->rects.empty()) { |
2735 | return false; |
2736 | } |
2737 | - uint16_t tip_width = rendered_text->width() + 4; |
2738 | - uint16_t tip_height = rendered_text->height() + 4; |
2739 | + |
2740 | + const uint16_t tip_width = rendered_text->width() + 4; |
2741 | + const uint16_t tip_height = rendered_text->height() + 4; |
2742 | |
2743 | Recti r(WLApplication::get()->get_mouse_position() + Vector2i(2, 32), tip_width, tip_height); |
2744 | const Vector2i tooltip_bottom_right = r.opposite_of_origin(); |
2745 | @@ -1078,7 +1079,7 @@ |
2746 | |
2747 | dst.fill_rect(r, RGBColor(63, 52, 34)); |
2748 | dst.draw_rect(r, RGBColor(0, 0, 0)); |
2749 | - dst.blit(r.origin() + Vector2i(2, 2), rendered_text); |
2750 | + rendered_text->draw(dst, r.origin() + Vector2i(2, 2)); |
2751 | return true; |
2752 | } |
2753 | } |
2754 | |
2755 | === modified file 'src/ui_basic/panel.h' |
2756 | --- src/ui_basic/panel.h 2017-02-27 13:53:04 +0000 |
2757 | +++ src/ui_basic/panel.h 2017-05-23 21:33:09 +0000 |
2758 | @@ -28,7 +28,10 @@ |
2759 | #include <boost/signals2/trackable.hpp> |
2760 | |
2761 | #include "base/macros.h" |
2762 | +#include "base/rect.h" |
2763 | #include "base/vector.h" |
2764 | +#include "graphic/align.h" |
2765 | +#include "graphic/font_handler1.h" |
2766 | |
2767 | class RenderTarget; |
2768 | class Image; |
2769 | |
2770 | === modified file 'src/ui_basic/progressbar.cc' |
2771 | --- src/ui_basic/progressbar.cc 2017-04-22 12:19:21 +0000 |
2772 | +++ src/ui_basic/progressbar.cc 2017-05-23 21:33:09 +0000 |
2773 | @@ -88,10 +88,9 @@ |
2774 | const std::string progress_text = (boost::format("<font color=%s>%u%%</font>") % |
2775 | UI_FONT_CLR_BRIGHT.hex_value() % floor(fraction * 100.f)) |
2776 | .str(); |
2777 | - const Image* rendered_text = UI::g_fh1->render(as_uifont(progress_text)); |
2778 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(as_uifont(progress_text)); |
2779 | Vector2i pos(get_w() / 2, get_h() / 2); |
2780 | - UI::correct_for_align(UI::Align::kCenter, rendered_text->width(), &pos); |
2781 | UI::center_vertically(rendered_text->height(), &pos); |
2782 | - dst.blit(pos, rendered_text, BlendMode::UseAlpha); |
2783 | + rendered_text->draw(dst, pos, UI::Align::kCenter); |
2784 | } |
2785 | } |
2786 | |
2787 | === modified file 'src/ui_basic/progresswindow.cc' |
2788 | --- src/ui_basic/progresswindow.cc 2017-05-13 18:48:26 +0000 |
2789 | +++ src/ui_basic/progresswindow.cc 2017-05-23 21:33:09 +0000 |
2790 | @@ -63,8 +63,7 @@ |
2791 | label_center_.x = get_w() / 2; |
2792 | label_center_.y = get_h() * PROGRESS_LABEL_POSITION_Y / 100; |
2793 | |
2794 | - const uint32_t h = |
2795 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height(); |
2796 | + const uint32_t h = text_height(); |
2797 | |
2798 | label_rectangle_.x = get_w() / 4; |
2799 | label_rectangle_.w = get_w() / 2; |
2800 | @@ -99,11 +98,10 @@ |
2801 | draw(rt); |
2802 | |
2803 | rt.fill_rect(label_rectangle_, PROGRESS_FONT_COLOR_BG); |
2804 | - const Image* rendered_text = |
2805 | + std::shared_ptr<const UI::RenderedText> rendered_text = |
2806 | UI::g_fh1->render(as_uifont(description, UI_FONT_SIZE_SMALL, PROGRESS_FONT_COLOR_FG)); |
2807 | - UI::correct_for_align(UI::Align::kCenter, rendered_text->width(), &label_center_); |
2808 | UI::center_vertically(rendered_text->height(), &label_center_); |
2809 | - rt.blit(label_center_, rendered_text, BlendMode::UseAlpha); |
2810 | + rendered_text->draw(rt, label_center_, UI::Align::kCenter); |
2811 | |
2812 | #ifdef _WIN32 |
2813 | // Pump events to prevent "not responding" on windows |
2814 | |
2815 | === modified file 'src/ui_basic/slider.cc' |
2816 | --- src/ui_basic/slider.cc 2017-04-22 12:19:21 +0000 |
2817 | +++ src/ui_basic/slider.cc 2017-05-23 21:33:09 +0000 |
2818 | @@ -508,12 +508,7 @@ |
2819 | w / (2 * labels_in.size()) - cursor_size / 2, |
2820 | 0, |
2821 | w - (w / labels_in.size()) + cursor_size, |
2822 | - h - |
2823 | - UI::g_fh1->render(as_condensed(UI::g_fh1->fontset()->representative_character(), |
2824 | - UI::Align::kLeft, |
2825 | - UI_FONT_SIZE_SMALL - 2)) |
2826 | - ->height() - |
2827 | - 2, |
2828 | + h - text_height(UI_FONT_SIZE_SMALL - 2, UI::FontSet::Face::kCondensed) - 2, |
2829 | 0, |
2830 | labels_in.size() - 1, |
2831 | value_, |
2832 | @@ -538,11 +533,10 @@ |
2833 | uint32_t gap_n = get_w() / labels.size(); |
2834 | |
2835 | for (uint32_t i = 0; i < labels.size(); i++) { |
2836 | - const Image* rendered_text = |
2837 | + std::shared_ptr<const UI::RenderedText> rendered_text = |
2838 | UI::g_fh1->render(as_condensed(labels[i], UI::Align::kCenter, UI_FONT_SIZE_SMALL - 2)); |
2839 | - Vector2i point(gap_1 + i * gap_n, get_h() - rendered_text->height()); |
2840 | - UI::correct_for_align(UI::Align::kCenter, rendered_text->width(), &point); |
2841 | - dst.blit(point, rendered_text, BlendMode::UseAlpha); |
2842 | + rendered_text->draw( |
2843 | + dst, Vector2i(gap_1 + i * gap_n, get_h() - rendered_text->height()), UI::Align::kCenter); |
2844 | } |
2845 | } |
2846 | |
2847 | @@ -557,13 +551,8 @@ |
2848 | uint32_t h = get_h(); |
2849 | assert(labels.size()); |
2850 | slider.set_pos(Vector2i(w / (2 * labels.size()) - slider.cursor_size_ / 2, 0)); |
2851 | - slider.set_size( |
2852 | - w - (w / labels.size()) + slider.cursor_size_, |
2853 | - h - |
2854 | - UI::g_fh1->render(as_condensed(UI::g_fh1->fontset()->representative_character(), |
2855 | - UI::Align::kLeft, UI_FONT_SIZE_SMALL - 2)) |
2856 | - ->height() + |
2857 | - 2); |
2858 | + slider.set_size(w - (w / labels.size()) + slider.cursor_size_, |
2859 | + h - text_height(UI_FONT_SIZE_SMALL - 2, UI::FontSet::Face::kCondensed) + 2); |
2860 | Panel::layout(); |
2861 | } |
2862 | } |
2863 | |
2864 | === modified file 'src/ui_basic/table.cc' |
2865 | --- src/ui_basic/table.cc 2017-05-14 14:40:24 +0000 |
2866 | +++ src/ui_basic/table.cc 2017-05-23 21:33:09 +0000 |
2867 | @@ -51,11 +51,8 @@ |
2868 | TableRows rowtype) |
2869 | : Panel(parent, x, y, w, h), |
2870 | total_width_(0), |
2871 | - headerheight_( |
2872 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + |
2873 | - 4), |
2874 | - lineheight_( |
2875 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height()), |
2876 | + headerheight_(text_height() + 4), |
2877 | + lineheight_(text_height()), |
2878 | button_background_(button_background), |
2879 | scrollbar_(nullptr), |
2880 | scrollbar_filler_button_( |
2881 | @@ -235,7 +232,7 @@ |
2882 | Columns::size_type const nr_columns = columns_.size(); |
2883 | for (uint32_t i = 0, curx = 0; i < nr_columns; ++i) { |
2884 | const Column& column = columns_[i]; |
2885 | - int const curw = column.width; |
2886 | + const int curw = column.width; |
2887 | Align alignment = mirror_alignment(column.alignment); |
2888 | |
2889 | const Image* entry_picture = er.get_picture(i); |
2890 | @@ -288,52 +285,37 @@ |
2891 | } |
2892 | dst.blit(Vector2i(draw_x, point.y + (lineheight - pich) / 2), entry_picture); |
2893 | } |
2894 | - point.x += picw; |
2895 | + if (alignment != Align::kRight) { |
2896 | + point.x += picw; |
2897 | + } |
2898 | } |
2899 | |
2900 | - ++picw; // A bit of margin between image and text |
2901 | - |
2902 | if (entry_string.empty()) { |
2903 | curx += curw; |
2904 | continue; |
2905 | } |
2906 | - const Image* entry_text_im = UI::g_fh1->render(as_uifont(richtext_escape(entry_string))); |
2907 | - |
2908 | + std::shared_ptr<const UI::RenderedText> rendered_text = |
2909 | + UI::g_fh1->render(as_uifont(richtext_escape(entry_string))); |
2910 | + |
2911 | + // Fix text alignment for BiDi languages if the entry contains an RTL character. We want |
2912 | + // this always on, e.g. for mixed language savegame filenames. |
2913 | + alignment = mirror_alignment(column.alignment, entry_string); |
2914 | + |
2915 | + // Position the text according to alignment |
2916 | switch (alignment) { |
2917 | case UI::Align::kCenter: |
2918 | point.x += (curw - picw) / 2; |
2919 | break; |
2920 | case UI::Align::kRight: |
2921 | - point.x += curw - 2 * picw; |
2922 | + point.x += curw - picw; |
2923 | break; |
2924 | case UI::Align::kLeft: |
2925 | break; |
2926 | } |
2927 | |
2928 | - // Add an offset for rightmost column when the scrollbar is shown. |
2929 | - int text_width = entry_text_im->width(); |
2930 | - if (i == nr_columns - 1 && scrollbar_->is_enabled()) { |
2931 | - text_width = text_width + scrollbar_->get_w(); |
2932 | - } |
2933 | - UI::correct_for_align(alignment, text_width, &point); |
2934 | - |
2935 | - // Crop to column width while blitting |
2936 | - if ((curw + picw) < text_width) { |
2937 | - // Fix positioning for BiDi languages. |
2938 | - if (UI::g_fh1->fontset()->is_rtl()) { |
2939 | - point.x = (alignment == UI::Align::kRight) ? curx : curx + picw; |
2940 | - } |
2941 | - // We want this always on, e.g. for mixed language savegame filenames |
2942 | - if (i18n::has_rtl_character( |
2943 | - entry_string.c_str(), 20)) { // Restrict check for efficiency |
2944 | - dst.blitrect( |
2945 | - point, entry_text_im, Recti(text_width - curw + picw, 0, text_width, lineheight)); |
2946 | - } else { |
2947 | - dst.blitrect(point, entry_text_im, Recti(0, 0, curw - picw, lineheight)); |
2948 | - } |
2949 | - } else { |
2950 | - dst.blitrect(point, entry_text_im, Recti(0, 0, curw - picw, lineheight)); |
2951 | - } |
2952 | + constexpr int kMargin = 1; |
2953 | + rendered_text->draw(dst, point, Recti(kMargin, 0, curw - picw - 2 * kMargin, lineheight), |
2954 | + alignment, RenderedText::CropMode::kSelf); |
2955 | curx += curw; |
2956 | } |
2957 | |
2958 | |
2959 | === modified file 'src/ui_basic/tabpanel.cc' |
2960 | --- src/ui_basic/tabpanel.cc 2017-05-13 11:25:24 +0000 |
2961 | +++ src/ui_basic/tabpanel.cc 2017-05-23 21:33:09 +0000 |
2962 | @@ -47,19 +47,23 @@ |
2963 | Tab::Tab(TabPanel* const tab_parent, |
2964 | size_t const tab_id, |
2965 | int32_t x, |
2966 | - int32_t w, |
2967 | const std::string& name, |
2968 | const std::string& init_title, |
2969 | const Image* init_pic, |
2970 | const std::string& tooltip_text, |
2971 | Panel* const contents) |
2972 | - : NamedPanel(tab_parent, name, x, 0, w, kTabPanelButtonHeight, tooltip_text), |
2973 | + : NamedPanel(tab_parent, name, x, 0, kTabPanelButtonHeight, kTabPanelButtonHeight, tooltip_text), |
2974 | parent(tab_parent), |
2975 | id(tab_id), |
2976 | pic(init_pic), |
2977 | - title(init_title), |
2978 | + rendered_title(nullptr), |
2979 | tooltip(tooltip_text), |
2980 | panel(contents) { |
2981 | + if (!init_title.empty()) { |
2982 | + rendered_title = UI::g_fh1->render(as_uifont(init_title)); |
2983 | + set_size(std::max(kTabPanelButtonHeight, rendered_title->width() + 2 * kTabPanelTextMargin), |
2984 | + kTabPanelButtonHeight); |
2985 | + } |
2986 | } |
2987 | |
2988 | /** |
2989 | @@ -165,9 +169,7 @@ |
2990 | const std::string& title, |
2991 | Panel* const panel, |
2992 | const std::string& tooltip_text) { |
2993 | - const Image* pic = UI::g_fh1->render(as_uifont(title)); |
2994 | - return add_tab(std::max(kTabPanelButtonHeight, pic->width() + 2 * kTabPanelTextMargin), name, |
2995 | - title, pic, tooltip_text, panel); |
2996 | + return add_tab(name, title, nullptr, tooltip_text, panel); |
2997 | } |
2998 | |
2999 | /** |
3000 | @@ -177,12 +179,11 @@ |
3001 | const Image* pic, |
3002 | Panel* const panel, |
3003 | const std::string& tooltip_text) { |
3004 | - return add_tab(kTabPanelButtonHeight, name, "", pic, tooltip_text, panel); |
3005 | + return add_tab(name, "", pic, tooltip_text, panel); |
3006 | } |
3007 | |
3008 | /** Common adding function for textual and pictorial tabs. */ |
3009 | -uint32_t TabPanel::add_tab(int32_t width, |
3010 | - const std::string& name, |
3011 | +uint32_t TabPanel::add_tab(const std::string& name, |
3012 | const std::string& title, |
3013 | const Image* pic, |
3014 | const std::string& tooltip_text, |
3015 | @@ -192,7 +193,7 @@ |
3016 | |
3017 | size_t id = tabs_.size(); |
3018 | int32_t x = id > 0 ? tabs_[id - 1]->get_x() + tabs_[id - 1]->get_w() : 0; |
3019 | - tabs_.push_back(new Tab(this, id, x, width, name, title, pic, tooltip_text, panel)); |
3020 | + tabs_.push_back(new Tab(this, id, x, name, title, pic, tooltip_text, panel)); |
3021 | |
3022 | // Add a margin if there is a border |
3023 | if (border_type_ == TabPanel::Type::kBorder) { |
3024 | @@ -282,10 +283,8 @@ |
3025 | dst.brighten_rect(Recti(x, 0, tab_width, kTabPanelButtonHeight), MOUSE_OVER_BRIGHT_FACTOR); |
3026 | } |
3027 | |
3028 | - assert(tabs_[idx]->pic); |
3029 | - |
3030 | - // If the title is empty, we will assume a pictorial tab |
3031 | - if (tabs_[idx]->title.empty()) { |
3032 | + // If pic is there, we will assume a pictorial tab |
3033 | + if (tabs_[idx]->pic != nullptr) { |
3034 | // Scale the image down if needed, but keep the ratio. |
3035 | constexpr int kMaxImageSize = kTabPanelButtonHeight - 2 * kTabPanelImageMargin; |
3036 | double image_scale = |
3037 | @@ -299,10 +298,10 @@ |
3038 | (kTabPanelButtonHeight - picture_height) / 2, picture_width, picture_height), |
3039 | tabs_[idx]->pic, Recti(0, 0, tabs_[idx]->pic->width(), tabs_[idx]->pic->height()), 1, |
3040 | BlendMode::UseAlpha); |
3041 | - } else { |
3042 | - dst.blit(Vector2i(x + kTabPanelTextMargin, |
3043 | - (kTabPanelButtonHeight - tabs_[idx]->pic->height()) / 2), |
3044 | - tabs_[idx]->pic, BlendMode::UseAlpha); |
3045 | + } else if (tabs_[idx]->rendered_title != nullptr) { |
3046 | + tabs_[idx]->rendered_title->draw( |
3047 | + dst, Vector2i(x + kTabPanelTextMargin, |
3048 | + (kTabPanelButtonHeight - tabs_[idx]->rendered_title->height()) / 2)); |
3049 | } |
3050 | |
3051 | // Draw top part of border |
3052 | |
3053 | === modified file 'src/ui_basic/tabpanel.h' |
3054 | --- src/ui_basic/tabpanel.h 2017-02-12 09:10:57 +0000 |
3055 | +++ src/ui_basic/tabpanel.h 2017-05-23 21:33:09 +0000 |
3056 | @@ -46,7 +46,6 @@ |
3057 | Tab(TabPanel* parent, |
3058 | size_t id, |
3059 | int32_t x, |
3060 | - int32_t w, |
3061 | const std::string& name, |
3062 | const std::string& title, |
3063 | const Image* pic, |
3064 | @@ -68,7 +67,7 @@ |
3065 | uint32_t id; |
3066 | |
3067 | const Image* pic; |
3068 | - std::string title; |
3069 | + std::shared_ptr<const UI::RenderedText> rendered_title; |
3070 | std::string tooltip; |
3071 | Panel* panel; |
3072 | }; |
3073 | @@ -136,8 +135,7 @@ |
3074 | |
3075 | private: |
3076 | // Common adding function for textual and pictorial tabs |
3077 | - uint32_t add_tab(int32_t width, |
3078 | - const std::string& name, |
3079 | + uint32_t add_tab(const std::string& name, |
3080 | const std::string& title, |
3081 | const Image* pic, |
3082 | const std::string& tooltip, |
3083 | |
3084 | === modified file 'src/ui_basic/textarea.cc' |
3085 | --- src/ui_basic/textarea.cc 2017-04-22 10:17:39 +0000 |
3086 | +++ src/ui_basic/textarea.cc 2017-05-23 21:33:09 +0000 |
3087 | @@ -127,8 +127,7 @@ |
3088 | if (!text_.empty()) { |
3089 | Vector2i anchor( |
3090 | (align_ == Align::kCenter) ? get_w() / 2 : (align_ == UI::Align::kRight) ? get_w() : 0, 0); |
3091 | - UI::correct_for_align(align_, rendered_text_->width(), &anchor); |
3092 | - dst.blit(anchor, rendered_text_, BlendMode::UseAlpha); |
3093 | + rendered_text_->draw(dst, anchor, align_); |
3094 | } |
3095 | } |
3096 | |
3097 | @@ -188,14 +187,12 @@ |
3098 | uint32_t w = 0; |
3099 | uint16_t h = 0; |
3100 | |
3101 | - if (rendered_text_) { |
3102 | + if (rendered_text_.get()) { |
3103 | w = fixed_width_ > 0 ? fixed_width_ : rendered_text_->width(); |
3104 | h = rendered_text_->height(); |
3105 | // We want empty textareas to have height |
3106 | if (text_.empty()) { |
3107 | - h = UI::g_fh1->render( |
3108 | - as_uifont(UI::g_fh1->fontset()->representative_character(), fontsize_)) |
3109 | - ->height(); |
3110 | + h = text_height(fontsize_); |
3111 | } |
3112 | } |
3113 | set_desired_size(w, h); |
3114 | |
3115 | === modified file 'src/ui_basic/textarea.h' |
3116 | --- src/ui_basic/textarea.h 2017-02-23 19:38:51 +0000 |
3117 | +++ src/ui_basic/textarea.h 2017-05-23 21:33:09 +0000 |
3118 | @@ -94,7 +94,7 @@ |
3119 | |
3120 | LayoutMode layoutmode_; |
3121 | std::string text_; |
3122 | - const Image* rendered_text_; |
3123 | + std::shared_ptr<const UI::RenderedText> rendered_text_; |
3124 | Align align_; |
3125 | RGBColor color_; |
3126 | int fontsize_; |
3127 | |
3128 | === modified file 'src/ui_basic/window.cc' |
3129 | --- src/ui_basic/window.cc 2017-05-13 11:25:24 +0000 |
3130 | +++ src/ui_basic/window.cc 2017-05-23 21:33:09 +0000 |
3131 | @@ -272,14 +272,13 @@ |
3132 | // draw the title if we have one |
3133 | if (!title_.empty()) { |
3134 | // The title shouldn't be richtext, but we escape it just to make sure. |
3135 | - const Image* text = |
3136 | + std::shared_ptr<const UI::RenderedText> text = |
3137 | autofit_ui_text(richtext_escape(title_), get_inner_w(), UI_FONT_CLR_FG, 13); |
3138 | |
3139 | // Blit on pixel boundary (not float), so that the text is blitted pixel perfect. |
3140 | Vector2i pos(get_lborder() + get_inner_w() / 2, TP_B_PIXMAP_THICKNESS / 2); |
3141 | - UI::correct_for_align(UI::Align::kCenter, text->width(), &pos); |
3142 | UI::center_vertically(text->height(), &pos); |
3143 | - dst.blit(pos, text, BlendMode::UseAlpha); |
3144 | + text->draw(dst, pos, UI::Align::kCenter); |
3145 | } |
3146 | |
3147 | if (!is_minimal_) { |
3148 | |
3149 | === modified file 'src/ui_fsmenu/loadgame.cc' |
3150 | --- src/ui_fsmenu/loadgame.cc 2017-03-05 17:55:29 +0000 |
3151 | +++ src/ui_fsmenu/loadgame.cc 2017-05-23 21:33:09 +0000 |
3152 | @@ -332,19 +332,9 @@ |
3153 | } |
3154 | |
3155 | std::string FullscreenMenuLoadGame::filename_list_string() { |
3156 | - std::set<uint32_t> selections = table_.selections(); |
3157 | boost::format message; |
3158 | - int counter = 0; |
3159 | - for (const uint32_t index : selections) { |
3160 | - ++counter; |
3161 | - // TODO(GunChleoc): We can exceed the texture size for the font renderer, |
3162 | - // so we have to restrict this for now. |
3163 | - if (counter > 50) { |
3164 | - message = boost::format("%s\n%s") % message % "..."; |
3165 | - break; |
3166 | - } |
3167 | + for (const uint32_t index : table_.selections()) { |
3168 | const SavegameData& gamedata = games_data_[table_.get(table_.get_record(index))]; |
3169 | - |
3170 | if (gamedata.errormessage.empty()) { |
3171 | message = |
3172 | boost::format("%s\n%s") % message % |
3173 | |
3174 | === modified file 'src/wui/chatoverlay.cc' |
3175 | --- src/wui/chatoverlay.cc 2017-04-22 12:19:21 +0000 |
3176 | +++ src/wui/chatoverlay.cc 2017-05-23 21:33:09 +0000 |
3177 | @@ -167,15 +167,15 @@ |
3178 | if (!m->havemessages_) |
3179 | return; |
3180 | |
3181 | - const Image* im = nullptr; |
3182 | + std::shared_ptr<const UI::RenderedText> im = std::shared_ptr<const UI::RenderedText>(nullptr); |
3183 | try { |
3184 | im = UI::g_fh1->render(m->all_text_, get_w()); |
3185 | } catch (RT::WidthTooSmall&) { |
3186 | // Oops, maybe one long word? We render again, not limiting the width, but |
3187 | // render everything in one single line. |
3188 | - im = UI::g_fh1->render(m->all_text_, 0); |
3189 | + im = UI::g_fh1->render(m->all_text_); |
3190 | } |
3191 | - assert(im != nullptr); |
3192 | + assert(im.get() != nullptr); |
3193 | |
3194 | // Background |
3195 | const int32_t height = im->height() > get_h() ? get_h() : im->height(); |
3196 | @@ -185,8 +185,6 @@ |
3197 | if (!m->transparent_) { |
3198 | dst.fill_rect(Recti(0, top, width, height), RGBAColor(50, 50, 50, 128), BlendMode::Default); |
3199 | } |
3200 | - int32_t topcrop = im->height() - height; |
3201 | - Recti cropRect(0, topcrop, width, height); |
3202 | - |
3203 | - dst.blitrect(Vector2i(0, top), im, cropRect); |
3204 | + const int topcrop = im->height() - height; |
3205 | + im->draw(dst, Vector2i(0, top), Recti(0, topcrop, width, height)); |
3206 | } |
3207 | |
3208 | === modified file 'src/wui/game_tips.cc' |
3209 | --- src/wui/game_tips.cc 2017-05-13 18:48:26 +0000 |
3210 | +++ src/wui/game_tips.cc 2017-05-23 21:33:09 +0000 |
3211 | @@ -99,20 +99,17 @@ |
3212 | } |
3213 | |
3214 | void GameTips::show_tip(int32_t index) { |
3215 | - // try to load a background |
3216 | + RenderTarget& rt = *g_gr->get_render_target(); |
3217 | + |
3218 | const Image* pic_background = g_gr->images().get(BG_IMAGE); |
3219 | - assert(pic_background); |
3220 | - |
3221 | - RenderTarget& rt = *g_gr->get_render_target(); |
3222 | - |
3223 | - uint16_t w = pic_background->width(); |
3224 | - uint16_t h = pic_background->height(); |
3225 | + const int w = pic_background->width(); |
3226 | + const int h = pic_background->height(); |
3227 | Vector2i pt((g_gr->get_xres() - w) / 2, (g_gr->get_yres() - h) / 2); |
3228 | - Recti tips_area(pt, w, h); |
3229 | rt.blit(pt, pic_background); |
3230 | |
3231 | - const Image* rendered_text = UI::g_fh1->render(as_game_tip(tips_[index].text), tips_area.w); |
3232 | - rt.blit(tips_area.center().cast<int>() - |
3233 | - Vector2i(rendered_text->width() / 2, rendered_text->height() / 2), |
3234 | - rendered_text); |
3235 | + std::shared_ptr<const UI::RenderedText> rendered_text = |
3236 | + UI::g_fh1->render(as_game_tip(tips_[index].text), w); |
3237 | + pt = Vector2i((g_gr->get_xres() - rendered_text->width()) / 2, |
3238 | + (g_gr->get_yres() - rendered_text->height()) / 2); |
3239 | + rendered_text->draw(rt, pt); |
3240 | } |
3241 | |
3242 | === modified file 'src/wui/interactive_base.cc' |
3243 | --- src/wui/interactive_base.cc 2017-05-14 14:40:24 +0000 |
3244 | +++ src/wui/interactive_base.cc 2017-05-23 21:33:09 +0000 |
3245 | @@ -356,8 +356,8 @@ |
3246 | std::string node_text; |
3247 | if (is_game) { |
3248 | const std::string gametime(gametimestring(egbase().get_gametime(), true)); |
3249 | - const std::string gametime_text = as_condensed(gametime); |
3250 | - dst.blit(Vector2i(5, 5), UI::g_fh1->render(gametime_text), BlendMode::UseAlpha); |
3251 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(as_condensed(gametime)); |
3252 | + rendered_text->draw(dst, Vector2i(5, 5)); |
3253 | |
3254 | static boost::format node_format("(%i, %i)"); |
3255 | node_text = as_condensed((node_format % sel_.pos.node.x % sel_.pos.node.y).str()); |
3256 | @@ -366,20 +366,17 @@ |
3257 | const int32_t height = map[sel_.pos.node].get_height(); |
3258 | node_text = as_condensed((node_format % sel_.pos.node.x % sel_.pos.node.y % height).str()); |
3259 | } |
3260 | - |
3261 | - const Image* rendered_text = UI::g_fh1->render(node_text); |
3262 | - Vector2i point(get_w() - 5, get_h() - rendered_text->height() - 5); |
3263 | - UI::correct_for_align(UI::Align::kRight, rendered_text->width(), &point); |
3264 | - dst.blit(point, rendered_text, BlendMode::UseAlpha); |
3265 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(node_text); |
3266 | + rendered_text->draw( |
3267 | + dst, Vector2i(get_w() - 5, get_h() - rendered_text->height() - 5), UI::Align::kRight); |
3268 | } |
3269 | |
3270 | // Blit FPS when playing a game in debug mode. |
3271 | if (get_display_flag(dfDebug) && is_game) { |
3272 | static boost::format fps_format("%5.1f fps (avg: %5.1f fps)"); |
3273 | - const Image* rendered_text = UI::g_fh1->render(as_condensed( |
3274 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(as_condensed( |
3275 | (fps_format % (1000.0 / frametime_) % (1000.0 / (avg_usframetime_ / 1000))).str())); |
3276 | - dst.blit( |
3277 | - Vector2i((get_w() - rendered_text->width()) / 2, 5), rendered_text, BlendMode::UseAlpha); |
3278 | + rendered_text->draw(dst, Vector2i((get_w() - rendered_text->width()) / 2, 5)); |
3279 | } |
3280 | } |
3281 | |
3282 | |
3283 | === modified file 'src/wui/interactive_base.h' |
3284 | --- src/wui/interactive_base.h 2017-03-03 18:13:55 +0000 |
3285 | +++ src/wui/interactive_base.h 2017-05-23 21:33:09 +0000 |
3286 | @@ -29,11 +29,11 @@ |
3287 | #include "logic/map.h" |
3288 | #include "notifications/notifications.h" |
3289 | #include "profile/profile.h" |
3290 | +#include "sound/note_sound.h" |
3291 | +#include "sound/sound_handler.h" |
3292 | #include "ui_basic/box.h" |
3293 | #include "ui_basic/textarea.h" |
3294 | #include "ui_basic/unique_window.h" |
3295 | -#include "sound/note_sound.h" |
3296 | -#include "sound/sound_handler.h" |
3297 | #include "wui/chatoverlay.h" |
3298 | #include "wui/debugconsole.h" |
3299 | #include "wui/edge_overlay_manager.h" |
3300 | |
3301 | === modified file 'src/wui/interactive_gamebase.cc' |
3302 | --- src/wui/interactive_gamebase.cc 2017-04-22 10:17:39 +0000 |
3303 | +++ src/wui/interactive_gamebase.cc 2017-05-23 21:33:09 +0000 |
3304 | @@ -126,10 +126,8 @@ |
3305 | } |
3306 | |
3307 | if (!game_speed.empty()) { |
3308 | - Vector2i point(get_w() - 5, 5); |
3309 | - const Image* rendered_speed = UI::g_fh1->render(game_speed); |
3310 | - UI::correct_for_align(UI::Align::kRight, rendered_speed->width(), &point); |
3311 | - dst.blit(point, rendered_speed, BlendMode::UseAlpha); |
3312 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(game_speed); |
3313 | + rendered_text->draw(dst, Vector2i(get_w() - 5, 5), UI::Align::kRight); |
3314 | } |
3315 | } |
3316 | } |
3317 | |
3318 | === modified file 'src/wui/mapdetails.cc' |
3319 | --- src/wui/mapdetails.cc 2017-02-23 17:58:25 +0000 |
3320 | +++ src/wui/mapdetails.cc 2017-05-23 21:33:09 +0000 |
3321 | @@ -102,9 +102,7 @@ |
3322 | } |
3323 | |
3324 | void MapDetails::layout() { |
3325 | - name_label_.set_size( |
3326 | - get_w() - padding_, |
3327 | - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + 2); |
3328 | + name_label_.set_size(get_w() - padding_, text_height() + 2); |
3329 | |
3330 | // Adjust sizes for show / hide suggested teams |
3331 | if (suggested_teams_box_->is_visible()) { |
3332 | |
3333 | === modified file 'src/wui/maptable.cc' |
3334 | --- src/wui/maptable.cc 2017-03-05 17:55:29 +0000 |
3335 | +++ src/wui/maptable.cc 2017-05-23 21:33:09 +0000 |
3336 | @@ -33,7 +33,7 @@ |
3337 | add_column(35, _("Pl."), _("Number of players"), UI::Align::kCenter); |
3338 | add_column(0, _("Filename"), _("The name of the map or scenario"), UI::Align::kLeft, |
3339 | UI::TableColumnType::kFlexible); |
3340 | - add_column(115, _("Size"), _("The size of the map (Width x Height)")); |
3341 | + add_column(90, _("Size"), _("The size of the map (Width x Height)")); |
3342 | set_sort_column(0); |
3343 | } |
3344 | |
3345 | |
3346 | === modified file 'src/wui/plot_area.cc' |
3347 | --- src/wui/plot_area.cc 2017-05-13 13:14:29 +0000 |
3348 | +++ src/wui/plot_area.cc 2017-05-23 21:33:09 +0000 |
3349 | @@ -178,17 +178,15 @@ |
3350 | const RGBColor& color, |
3351 | const Vector2i& pos, |
3352 | RenderTarget& dst) { |
3353 | - const Image* pic = UI::g_fh1->render(ytick_text_style(value, color)); |
3354 | + std::shared_ptr<const UI::RenderedText> tick = UI::g_fh1->render(ytick_text_style(value, color)); |
3355 | Vector2i point(pos); // Un-const this |
3356 | - UI::correct_for_align(UI::Align::kRight, pic->width(), &point); |
3357 | - UI::center_vertically(pic->height(), &point); |
3358 | - dst.blit(point, pic, BlendMode::UseAlpha); |
3359 | + UI::center_vertically(tick->height(), &point); |
3360 | + tick->draw(dst, point, UI::Align::kRight); |
3361 | } |
3362 | |
3363 | uint32_t calc_plot_x_max_ticks(int32_t plot_width) { |
3364 | // Render a number with 3 digits (maximal length which should appear) |
3365 | - const Image* pic = UI::g_fh1->render(ytick_text_style(" -888 ", kAxisLineColor)); |
3366 | - return plot_width / pic->width(); |
3367 | + return plot_width / UI::g_fh1->render(ytick_text_style(" -888 ", kAxisLineColor))->width(); |
3368 | } |
3369 | |
3370 | int calc_slider_label_width(const std::string& label) { |
3371 | @@ -264,12 +262,11 @@ |
3372 | |
3373 | // The space at the end is intentional to have the tick centered |
3374 | // over the number, not to the left |
3375 | - const Image* xtick = UI::g_fh1->render( |
3376 | + std::shared_ptr<const UI::RenderedText> xtick = UI::g_fh1->render( |
3377 | xtick_text_style((boost::format("-%u ") % (max_x / how_many_ticks * i)).str())); |
3378 | Vector2i pos(posx, inner_h - kSpaceBottom + 10); |
3379 | - UI::correct_for_align(UI::Align::kCenter, xtick->width(), &pos); |
3380 | UI::center_vertically(xtick->height(), &pos); |
3381 | - dst.blit(pos, xtick, BlendMode::UseAlpha); |
3382 | + xtick->draw(dst, pos, UI::Align::kCenter); |
3383 | |
3384 | posx -= sub; |
3385 | } |
3386 | @@ -284,10 +281,10 @@ |
3387 | kAxisLineColor, kAxisLinesWidth); |
3388 | |
3389 | // print the used unit |
3390 | - const Image* xtick = UI::g_fh1->render(xtick_text_style(get_generic_unit_name(unit))); |
3391 | + std::shared_ptr<const UI::RenderedText> xtick = UI::g_fh1->render(xtick_text_style(get_generic_unit_name(unit))); |
3392 | Vector2i pos(2, kSpacing + 2); |
3393 | UI::center_vertically(xtick->height(), &pos); |
3394 | - dst.blit(pos, xtick, BlendMode::UseAlpha); |
3395 | + xtick->draw(dst, pos); |
3396 | } |
3397 | |
3398 | } // namespace |
3399 | |
3400 | === modified file 'src/wui/waresdisplay.cc' |
3401 | --- src/wui/waresdisplay.cc 2017-05-13 13:14:29 +0000 |
3402 | +++ src/wui/waresdisplay.cc 2017-05-23 21:33:09 +0000 |
3403 | @@ -331,11 +331,10 @@ |
3404 | dst.fill_rect(Recti(p + Vector2i(0, WARE_MENU_PIC_HEIGHT), w, WARE_MENU_INFO_SIZE), |
3405 | info_color_for_ware(id)); |
3406 | |
3407 | - const Image* text = UI::g_fh1->render(as_waresinfo(info_for_ware(id))); |
3408 | - if (text) // might be zero when there is no info text. |
3409 | - dst.blit(p + Vector2i(w - text->width() - 1, |
3410 | - WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + 1 - text->height()), |
3411 | - text); |
3412 | + std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh1->render(as_waresinfo(info_for_ware(id))); |
3413 | + rendered_text->draw( |
3414 | + dst, Vector2i(p.x + w - rendered_text->width() - 1, |
3415 | + p.y + WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + 1 - rendered_text->height())); |
3416 | } |
3417 | |
3418 | // Wares highlighting/selecting |
Continuous integration builds have changed state:
Travis build 2172. State: passed. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 231241186. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ fh1_multitextur e-2007.
Appveyor build 2007. State: success. Details: https:/