[Scummvm-git-logs] scummvm master -> 448bb2d77ef4e49aaaa4ad5298ab07fa1737115a

dreammaster noreply at scummvm.org
Tue Mar 15 04:52:55 UTC 2022


This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
399a89825f AGS: New auto outline supports AA TTFs in 32- and 16-bit games
641ade11fa AGS: Fixed crash in auto outline in case of empty text argument
fd0e16ffc3 AGS: implemented stencil cache per Font for automatic outlining
8b8bc2f32f AGS: support reading font extension "v360_fonts"
255a739eca AGS: Encapsulate game object draw caches in draw.cpp
2d995bbe07 AGS: Simplified use of ReplaceBitmapWithSupportedFormat()
448bb2d77e AGS: Moved "LucasFan" font hack to game_init, enable for all systems


Commit: 399a89825f4c2be0d3693924bb37c90d32849a89
    https://github.com/scummvm/scummvm/commit/399a89825f4c2be0d3693924bb37c90d32849a89
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-14T21:04:07-07:00

Commit Message:
AGS: New auto outline supports AA TTFs in 32- and 16-bit games

>From upstream cae84d689019313cad49b6dca7e916866b90e49e

Changed paths:
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/gfx/blender.cpp
    engines/ags/engine/gfx/blender.h
    engines/ags/shared/gfx/allegro_bitmap.cpp
    engines/ags/shared/gfx/allegro_bitmap.h


diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index a41fe099027..77e70cfdef0 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -43,6 +43,7 @@
 #include "ags/engine/ac/system.h"
 #include "ags/engine/ac/top_bar_settings.h"
 #include "ags/engine/debugging/debug_log.h"
+#include "ags/engine/gfx/blender.h"
 #include "ags/shared/gui/gui_button.h"
 #include "ags/shared/gui/gui_main.h"
 #include "ags/engine/main/game_run.h"
@@ -452,31 +453,46 @@ bool ShouldAntiAliasText() {
 }
 
 void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *texx, int &xxp, int &yyp) {
-	using AGS::Shared::kBitmap_Transparency;
-
-	int thickness = _GP(game).fonts.at(font).AutoOutlineThickness;
-	auto style = _GP(game).fonts.at(font).AutoOutlineStyle;
+	int const thickness = _GP(game).fonts.at(font).AutoOutlineThickness;
+	auto const style = _GP(game).fonts.at(font).AutoOutlineStyle;
 	if (thickness <= 0)
 		return;
 
+	// 16-bit games should use 32-bit stencils to keep anti-aliasing working
+	// because 16-bit blending works correctly if there's an actual color
+	// on the destination bitmap (and our intermediate bitmaps are transparent).
+	int const  ds_cd = ds->GetColorDepth();
+	bool const antialias = ds_cd >= 16 && _GP(game).options[OPT_ANTIALIASFONTS] != 0 && !is_bitmap_font(font);
+	int const  stencil_cd = antialias ? 32 : ds_cd;
+	if (antialias) // This is to make sure TTFs render proper alpha channel in 16-bit games too
+		color |= makeacol32(0, 0, 0, 0xff);
+
 	size_t const t_width = wgettextwidth(texx, font);
 	size_t const t_height = wgettextheight(texx, font);
-	if (t_width == 0 || t_height == 0)
-		return;
-
-	Bitmap *outline_stencil =
-		CreateTransparentBitmap(t_width, t_height + 2 * thickness, ds->GetColorDepth());
-	Bitmap *texx_stencil =
-		CreateTransparentBitmap(t_width, t_height, ds->GetColorDepth());
-	if (!outline_stencil || !texx_stencil)
+	Bitmap texx_stencil, outline_stencil;
+	texx_stencil.CreateTransparent(t_width, t_height, stencil_cd);
+	outline_stencil.CreateTransparent(t_width, t_height + 2 * thickness, stencil_cd);
+	if (outline_stencil.IsNull() || texx_stencil.IsNull())
 		return;
-	wouttextxy(texx_stencil, 0, 0, font, color, texx);
+	wouttextxy(&texx_stencil, 0, 0, font, color, texx);
+
+	// Anti-aliased TTFs require to be alpha-blended, not blit,
+	// or the alpha values will be plain copied and final image will be broken.
+	void(Bitmap:: * pfn_drawstencil)(Bitmap * src, int dst_x, int dst_y);
+	if (antialias) { // NOTE: we must set out blender AFTER wouttextxy, or it will be overidden
+		set_argb2any_blender();
+		pfn_drawstencil = &Bitmap::TransBlendBlt;
+	} else {
+		pfn_drawstencil = &Bitmap::MaskedBlit;
+	}
 
 	// move start of text so that the outline doesn't drop off the bitmap
 	xxp += thickness;
 	int const outline_y = yyp;
 	yyp += thickness;
 
+	// What we do here: first we paint text onto outline_stencil offsetting vertically;
+	// then we paint resulting outline_stencil onto final dest offsetting horizontally.
 	int largest_y_diff_reached_so_far = -1;
 	for (int x_diff = thickness; x_diff >= 0; x_diff--) {
 		// Integer arithmetics: In the following, we use terms k*(k + 1) to account for rounding.
@@ -485,24 +501,21 @@ void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *
 		if (FontInfo::kRounded == style)
 			y_term_limit -= x_diff * x_diff;
 
-		// extend the outline stencil to the top and bottom
+		// Extend the outline stencil to the top and bottom
 		for (int y_diff = largest_y_diff_reached_so_far + 1;
 			y_diff <= thickness && y_diff * y_diff <= y_term_limit;
 			y_diff++) {
-			outline_stencil->Blit(texx_stencil, 0, thickness - y_diff, kBitmap_Transparency);
+			(outline_stencil.*pfn_drawstencil)(&texx_stencil, 0, thickness - y_diff);
 			if (y_diff > 0)
-				outline_stencil->Blit(texx_stencil, 0, thickness + y_diff, kBitmap_Transparency);
+				(outline_stencil.*pfn_drawstencil)(&texx_stencil, 0, thickness + y_diff);
 			largest_y_diff_reached_so_far = y_diff;
 		}
 
-		// stamp the outline stencil to the left and right of the text
-		ds->Blit(outline_stencil, xxp - x_diff, outline_y, kBitmap_Transparency);
+		// Stamp the outline stencil to the left and right of the text
+		(ds->*pfn_drawstencil)(&outline_stencil, xxp - x_diff, outline_y);
 		if (x_diff > 0)
-			ds->Blit(outline_stencil, xxp + x_diff, outline_y, kBitmap_Transparency);
+			(ds->*pfn_drawstencil)(&outline_stencil, xxp + x_diff, outline_y);
 	}
-
-	delete texx_stencil;
-	delete outline_stencil;
 }
 
 // Draw an outline if requested, then draw the text on top 
diff --git a/engines/ags/engine/gfx/blender.cpp b/engines/ags/engine/gfx/blender.cpp
index fb15d581499..949060695e1 100644
--- a/engines/ags/engine/gfx/blender.cpp
+++ b/engines/ags/engine/gfx/blender.cpp
@@ -43,4 +43,9 @@ void set_opaque_alpha_blender() {
 	set_blender_mode(kOpaqueBlenderMode, 0, 0, 0, 0);
 }
 
+void set_argb2any_blender() {
+	// TODO: Properly implement this new mode
+	set_blender_mode(kArgbToArgbBlender, 0, 0, 0, 0xff);
+}
+
 } // namespace AGS3
diff --git a/engines/ags/engine/gfx/blender.h b/engines/ags/engine/gfx/blender.h
index 747186bf6e8..fd949fabf75 100644
--- a/engines/ags/engine/gfx/blender.h
+++ b/engines/ags/engine/gfx/blender.h
@@ -50,6 +50,8 @@ void set_my_trans_blender(int r, int g, int b, int a);
 void set_additive_alpha_blender();
 // Opaque alpha blender plain copies src over, applying opaque alpha value.
 void set_opaque_alpha_blender();
+// Sets argb2argb for 32-bit mode, and provides appropriate funcs for blending 32-bit onto 15/16/24-bit destination
+void set_argb2any_blender();
 
 } // namespace AGS3
 
diff --git a/engines/ags/shared/gfx/allegro_bitmap.cpp b/engines/ags/shared/gfx/allegro_bitmap.cpp
index c8552d07673..5d934da340d 100644
--- a/engines/ags/shared/gfx/allegro_bitmap.cpp
+++ b/engines/ags/shared/gfx/allegro_bitmap.cpp
@@ -198,6 +198,10 @@ void Bitmap::Blit(Bitmap *src, int src_x, int src_y, int dst_x, int dst_y, int w
 	}
 }
 
+void Bitmap::MaskedBlit(Bitmap *src, int dst_x, int dst_y) {
+	draw_sprite(_alBitmap, src->_alBitmap, dst_x, dst_y);
+}
+
 void Bitmap::StretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask) {
 	BITMAP *al_src_bmp = src->_alBitmap;
 	// WARNING: For some evil reason Allegro expects dest and src bitmaps in different order for blit and draw_sprite
diff --git a/engines/ags/shared/gfx/allegro_bitmap.h b/engines/ags/shared/gfx/allegro_bitmap.h
index f067b04ff1b..b6bb3d9919b 100644
--- a/engines/ags/shared/gfx/allegro_bitmap.h
+++ b/engines/ags/shared/gfx/allegro_bitmap.h
@@ -176,7 +176,9 @@ public:
 	// Draw other bitmap over current one
 	void    Blit(Bitmap *src, int dst_x = 0, int dst_y = 0, BitmapMaskOption mask = kBitmap_Copy);
 	void    Blit(Bitmap *src, int src_x, int src_y, int dst_x, int dst_y, int width, int height, BitmapMaskOption mask = kBitmap_Copy);
-	// Copy other bitmap, stretching or shrinking its size to given values
+	// Draw other bitmap in a masked mode (kBitmap_Transparency)
+	void    MaskedBlit(Bitmap *src, int dst_x, int dst_y);
+	// Draw other bitmap, stretching or shrinking its size to given values
 	void    StretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask = kBitmap_Copy);
 	void    StretchBlt(Bitmap *src, const Rect &src_rc, const Rect &dst_rc, BitmapMaskOption mask = kBitmap_Copy);
 	// Antia-aliased stretch-blit


Commit: 641ade11fa4cc4daa89011dae028190dbd078a9d
    https://github.com/scummvm/scummvm/commit/641ade11fa4cc4daa89011dae028190dbd078a9d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-14T21:04:08-07:00

Commit Message:
AGS: Fixed crash in auto outline in case of empty text argument

>From upstream e3665a3c073e574d3e4fade869b3c0c3e88508e5

Changed paths:
    engines/ags/engine/ac/display.cpp


diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 77e70cfdef0..190e2ecc5e0 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -469,6 +469,9 @@ void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *
 
 	size_t const t_width = wgettextwidth(texx, font);
 	size_t const t_height = wgettextheight(texx, font);
+	if (t_width == 0 || t_height == 0)
+		return;
+
 	Bitmap texx_stencil, outline_stencil;
 	texx_stencil.CreateTransparent(t_width, t_height, stencil_cd);
 	outline_stencil.CreateTransparent(t_width, t_height + 2 * thickness, stencil_cd);


Commit: fd0e16ffc3d348e2e5fe9d89947c8956f7b5e098
    https://github.com/scummvm/scummvm/commit/fd0e16ffc3d348e2e5fe9d89947c8956f7b5e098
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-14T21:04:08-07:00

Commit Message:
AGS: implemented stencil cache per Font for automatic outlining

>From upstream e32c4bf46b5f74b904ac491bb6017ceb8f8e2bbf

Changed paths:
    engines/ags/engine/ac/display.cpp
    engines/ags/shared/font/fonts.cpp
    engines/ags/shared/font/fonts.h
    engines/ags/shared/gfx/allegro_bitmap.cpp
    engines/ags/shared/gfx/allegro_bitmap.h


diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 190e2ecc5e0..4734132dac1 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -453,8 +453,9 @@ bool ShouldAntiAliasText() {
 }
 
 void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *texx, int &xxp, int &yyp) {
-	int const thickness = _GP(game).fonts.at(font).AutoOutlineThickness;
-	auto const style = _GP(game).fonts.at(font).AutoOutlineStyle;
+	const FontInfo &finfo = get_fontinfo(font);
+	int const thickness = finfo.AutoOutlineThickness;
+	auto const style = finfo.AutoOutlineStyle;
 	if (thickness <= 0)
 		return;
 
@@ -472,12 +473,14 @@ void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *
 	if (t_width == 0 || t_height == 0)
 		return;
 
-	Bitmap texx_stencil, outline_stencil;
-	texx_stencil.CreateTransparent(t_width, t_height, stencil_cd);
-	outline_stencil.CreateTransparent(t_width, t_height + 2 * thickness, stencil_cd);
-	if (outline_stencil.IsNull() || texx_stencil.IsNull())
-		return;
-	wouttextxy(&texx_stencil, 0, 0, font, color, texx);
+	// Prepare stencils
+	Bitmap *texx_stencil, *outline_stencil;
+	alloc_font_outline_buffers(font, &texx_stencil, &outline_stencil,
+		t_width, t_height, stencil_cd);
+	texx_stencil->ClearTransparent();
+	outline_stencil->ClearTransparent();
+	// Ready text stencil
+	wouttextxy(texx_stencil, 0, 0, font, color, texx);
 
 	// Anti-aliased TTFs require to be alpha-blended, not blit,
 	// or the alpha values will be plain copied and final image will be broken.
@@ -508,16 +511,16 @@ void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *
 		for (int y_diff = largest_y_diff_reached_so_far + 1;
 			y_diff <= thickness && y_diff * y_diff <= y_term_limit;
 			y_diff++) {
-			(outline_stencil.*pfn_drawstencil)(&texx_stencil, 0, thickness - y_diff);
+			(outline_stencil->*pfn_drawstencil)(texx_stencil, 0, thickness - y_diff);
 			if (y_diff > 0)
-				(outline_stencil.*pfn_drawstencil)(&texx_stencil, 0, thickness + y_diff);
+				(outline_stencil->*pfn_drawstencil)(texx_stencil, 0, thickness + y_diff);
 			largest_y_diff_reached_so_far = y_diff;
 		}
 
 		// Stamp the outline stencil to the left and right of the text
-		(ds->*pfn_drawstencil)(&outline_stencil, xxp - x_diff, outline_y);
+		(ds->*pfn_drawstencil)(outline_stencil, xxp - x_diff, outline_y);
 		if (x_diff > 0)
-			(ds->*pfn_drawstencil)(&outline_stencil, xxp + x_diff, outline_y);
+			(ds->*pfn_drawstencil)(outline_stencil, xxp + x_diff, outline_y);
 	}
 }
 
diff --git a/engines/ags/shared/font/fonts.cpp b/engines/ags/shared/font/fonts.cpp
index bc385ad262a..5451f57b140 100644
--- a/engines/ags/shared/font/fonts.cpp
+++ b/engines/ags/shared/font/fonts.cpp
@@ -37,17 +37,6 @@ namespace AGS3 {
 
 using namespace AGS::Shared;
 
-namespace AGS {
-namespace Shared {
-
-Font::Font()
-	: Renderer(nullptr)
-	, Renderer2(nullptr) {
-}
-
-} // namespace Shared
-} // namespace AGS
-
 FontInfo::FontInfo()
 	: Flags(0)
 	, SizePt(0)
@@ -346,6 +335,12 @@ void set_fontinfo(size_t fontNumber, const FontInfo &finfo) {
 		_GP(fonts)[fontNumber].Info = finfo;
 }
 
+FontInfo get_fontinfo(size_t font_number) {
+	if (font_number < _GP(fonts).size())
+		return _GP(fonts)[font_number].Info;
+	return FontInfo();
+}
+
 // Loads a font from disk
 bool wloadfont_size(size_t fontNumber, const FontInfo &font_info) {
 	if (_GP(fonts).size() <= fontNumber)
@@ -386,10 +381,40 @@ void wgtprintf(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t
 	wouttextxy(ds, xxx, yyy, fontNumber, text_color, tbuffer);
 }
 
+void alloc_font_outline_buffers(size_t font_number,
+	Bitmap **text_stencil, Bitmap **outline_stencil,
+	int text_width, int text_height, int color_depth) {
+	if (font_number >= _GP(fonts).size())
+		return;
+	Font &f = _GP(fonts)[font_number];
+	const int thick = 2 * f.Info.AutoOutlineThickness;
+	if (f.TextStencil.IsNull() || (f.TextStencil.GetColorDepth() != color_depth) ||
+		(f.TextStencil.GetWidth() < text_width) || (f.TextStencil.GetHeight() < text_height)) {
+		int sw = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetWidth();
+		int sh = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetHeight();
+		sw = std::max(text_width, sw);
+		sh = std::max(text_height, sh);
+		f.TextStencil.Create(sw, sh, color_depth);
+		f.OutlineStencil.Create(sw, sh + thick, color_depth);
+		f.TextStencilSub.CreateSubBitmap(&f.TextStencil, RectWH(Size(text_width, text_height)));
+		f.OutlineStencilSub.CreateSubBitmap(&f.OutlineStencil, RectWH(Size(text_width, text_height + thick)));
+	} else {
+		f.TextStencilSub.ResizeSubBitmap(text_width, text_height);
+		f.OutlineStencilSub.ResizeSubBitmap(text_width, text_height + thick);
+	}
+	*text_stencil = &f.TextStencilSub;
+	*outline_stencil = &f.OutlineStencilSub;
+}
+
 void wfreefont(size_t fontNumber) {
 	if (fontNumber >= _GP(fonts).size())
 		return;
 
+	_GP(fonts)[fontNumber].TextStencilSub.Destroy();
+	_GP(fonts)[fontNumber].OutlineStencilSub.Destroy();
+	_GP(fonts)[fontNumber].TextStencil.Destroy();
+	_GP(fonts)[fontNumber].OutlineStencil.Destroy();
+
 	if (_GP(fonts)[fontNumber].Renderer != nullptr)
 		_GP(fonts)[fontNumber].Renderer->FreeMemory(fontNumber);
 
diff --git a/engines/ags/shared/font/fonts.h b/engines/ags/shared/font/fonts.h
index 67d11513ed8..2c6bb129438 100644
--- a/engines/ags/shared/font/fonts.h
+++ b/engines/ags/shared/font/fonts.h
@@ -50,7 +50,7 @@ struct Font {
 	Bitmap TextStencil, TextStencilSub;
 	Bitmap OutlineStencil, OutlineStencilSub;
 
-	Font();
+	Font() {}
 };
 
 } // namespace Shared
diff --git a/engines/ags/shared/gfx/allegro_bitmap.cpp b/engines/ags/shared/gfx/allegro_bitmap.cpp
index 5d934da340d..3ffd00e7a9f 100644
--- a/engines/ags/shared/gfx/allegro_bitmap.cpp
+++ b/engines/ags/shared/gfx/allegro_bitmap.cpp
@@ -90,6 +90,16 @@ bool Bitmap::CreateSubBitmap(Bitmap *src, const Rect &rc) {
 	return _alBitmap != nullptr;
 }
 
+bool Bitmap::ResizeSubBitmap(int width, int height) {
+	if (!isSubBitmap())
+		return false;
+	// TODO: can't clamp to parent size, because subs do not keep parent ref;
+	// might require amending allegro bitmap struct
+	_alBitmap->w = _alBitmap->cr = width;
+	_alBitmap->h = _alBitmap->cb = height;
+	return true;
+}
+
 bool Bitmap::CreateCopy(Bitmap *src, int color_depth) {
 	if (Create(src->_alBitmap->w, src->_alBitmap->h, color_depth ? color_depth : bitmap_color_depth(src->_alBitmap))) {
 		blit(src->_alBitmap, _alBitmap, 0, 0, 0, 0, _alBitmap->w, _alBitmap->h);
diff --git a/engines/ags/shared/gfx/allegro_bitmap.h b/engines/ags/shared/gfx/allegro_bitmap.h
index b6bb3d9919b..18fab261b19 100644
--- a/engines/ags/shared/gfx/allegro_bitmap.h
+++ b/engines/ags/shared/gfx/allegro_bitmap.h
@@ -57,6 +57,8 @@ public:
 	bool    CreateTransparent(int width, int height, int color_depth = 0);
 	// Allow this object to share existing bitmap data
 	bool    CreateSubBitmap(Bitmap *src, const Rect &rc);
+	// Resizes existing sub-bitmap within the borders of its parent
+	bool    ResizeSubBitmap(int width, int height);
 	// Create a copy of given bitmap
 	bool    CreateCopy(Bitmap *src, int color_depth = 0);
 	// TODO: a temporary solution for plugin support


Commit: 8b8bc2f32f048fff6f148f4953b6b3555931ba3c
    https://github.com/scummvm/scummvm/commit/8b8bc2f32f048fff6f148f4953b6b3555931ba3c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-14T21:04:08-07:00

Commit Message:
AGS: support reading font extension "v360_fonts"

>From upstream 668430c4d21e6c39ae4c1c3a7e5be2c3a0ad7062

Changed paths:
    engines/ags/shared/ac/game_version.h
    engines/ags/shared/game/main_game_file.cpp


diff --git a/engines/ags/shared/ac/game_version.h b/engines/ags/shared/ac/game_version.h
index b6e1cc1336e..37acb875118 100644
--- a/engines/ags/shared/ac/game_version.h
+++ b/engines/ags/shared/ac/game_version.h
@@ -113,6 +113,10 @@ Font custom line spacing.
 Sprites have "real" resolution. Expanded FontInfo data format.
 Option to allow legacy relative asset resolutions.
 
+3.6.0 :
+Format value is defined as AGS version represented as NN,NN,NN,NN.
+Fonts have adjustable outline
+
 */
 
 enum GameDataVersion {
@@ -146,7 +150,7 @@ enum GameDataVersion {
 	kGameVersion_341_2 = 49,
 	kGameVersion_350 = 50,
 	kGameVersion_360 = 3060000,
-	kGameVersion_Current = kGameVersion_350
+	kGameVersion_Current = kGameVersion_360
 };
 
 } // namespace AGS3
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index 1a3d8ca2d4c..dcbc0a1d33e 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -716,6 +716,21 @@ HError GameDataExtReader::ReadBlock(int block_id, const String &ext_id,
     // {
     //     // read new gui properties
     // }
+	if (ext_id.CompareNoCase("v360_fonts") == 0) {
+		for (int i = 0; i < _ents.Game.numfonts; ++i) {
+			// adjustable font outlines
+			_ents.Game.fonts[i].AutoOutlineThickness = _in->ReadInt32();
+			_ents.Game.fonts[i].AutoOutlineStyle =
+				static_cast<enum FontInfo::AutoOutlineStyle>(_in->ReadInt32());
+			// reserved
+			_in->ReadInt32();
+			_in->ReadInt32();
+			_in->ReadInt32();
+			_in->ReadInt32();
+		}
+		return HError::None();
+	}
+
     return new MainGameFileError(kMGFErr_ExtUnknown, String::FromFormat("Type: %s", ext_id.GetCStr()));
 }
 


Commit: 255a739eca896ed8f71d69eace676514a1305979
    https://github.com/scummvm/scummvm/commit/255a739eca896ed8f71d69eace676514a1305979
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-14T21:04:09-07:00

Commit Message:
AGS: Encapsulate game object draw caches in draw.cpp

>From upstream e04ebc0039580faf017ceb0acbeeab975bf44fcc

Changed paths:
    engines/ags/engine/ac/character.cpp
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/global_object.cpp
    engines/ags/engine/ac/gui.cpp
    engines/ags/engine/ac/gui.h
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/character.cpp b/engines/ags/engine/ac/character.cpp
index 0a925bc0227..705639a4778 100644
--- a/engines/ags/engine/ac/character.cpp
+++ b/engines/ags/engine/ac/character.cpp
@@ -2068,11 +2068,12 @@ void CheckViewFrameForCharacter(CharacterInfo *chi) {
 
 Bitmap *GetCharacterImage(int charid, int *isFlipped) {
 	if (!_G(gfxDriver)->HasAcceleratedTransform()) {
-		if (_G(actsps)[charid + MAX_ROOM_OBJECTS] != nullptr) {
-			// the actsps image is pre-flipped, so no longer register the image as such
+		Bitmap *actsp = get_cached_character_image(charid);
+		if (actsp) {
+			// the cached image is pre-flipped, so no longer register the image as such
 			if (isFlipped)
 				*isFlipped = 0;
-			return _G(actsps)[charid + MAX_ROOM_OBJECTS];
+			return actsp;
 		}
 	}
 	CharacterInfo *chin = &_GP(game).chars[charid];
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 7685dcbe16b..f72ed968335 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -150,7 +150,7 @@ Bitmap *convert_32_to_32bgr(Bitmap *tempbl) {
 // could copy them to texture without additional changes.
 // AGS own OpenGL renderer tries to sync its behavior with the former one.
 //
-// TODO: make gfxDriver->GetCompatibleBitmapFormat describe all necessary
+// TODO: make _G(gfxDriver)->GetCompatibleBitmapFormat describe all necessary
 // conversions, so that we did not have to guess.
 //
 Bitmap *AdjustBitmapForUseWithDisplayMode(Bitmap *bitmap, bool has_alpha) {
@@ -397,11 +397,69 @@ void dispose_draw_method() {
 	destroy_blank_image();
 }
 
+void init_game_drawdata() {
+	for (int i = 0; i < MAX_ROOM_OBJECTS; ++i)
+		_G(objcache)[i].image = nullptr;
+
+	size_t actsps_num = _GP(game).numcharacters + MAX_ROOM_OBJECTS;
+	_GP(actsps).resize(actsps_num);
+	_GP(actspsbmp).resize(actsps_num);
+	_GP(actspswb).resize(actsps_num);
+	_GP(actspswbbmp).resize(actsps_num);
+	_GP(actspswbcache).resize(actsps_num);
+	_GP(guibg).resize(_GP(game).numgui);
+	_GP(guibgbmp).resize(_GP(game).numgui);
+}
+
+void dispose_game_drawdata() {
+	clear_drawobj_cache();
+
+	_GP(actsps).clear();
+	_GP(actspsbmp).clear();
+	_GP(actspswb).clear();
+	_GP(actspswbbmp).clear();
+	_GP(actspswbcache).clear();
+	_GP(guibg).clear();
+}
+
 void dispose_room_drawdata() {
 	_GP(CameraDrawData).clear();
 	dispose_invalid_regions(true);
 }
 
+void clear_drawobj_cache() {
+	// clear the object cache
+	for (int i = 0; i < MAX_ROOM_OBJECTS; ++i) {
+		delete _G(objcache)[i].image;
+		_G(objcache)[i].image = nullptr;
+	}
+
+	// cleanup Character + Room object textures
+	for (int i = 0; i < MAX_ROOM_OBJECTS + _GP(game).numcharacters; ++i) {
+		delete _GP(actsps)[i];
+		_GP(actsps)[i] = nullptr;
+		if (_GP(actspsbmp)[i] != nullptr)
+			_G(gfxDriver)->DestroyDDB(_GP(actspsbmp)[i]);
+		_GP(actspsbmp)[i] = nullptr;
+
+		delete _GP(actspswb)[i];
+		_GP(actspswb)[i] = nullptr;
+		if (_GP(actspswbbmp)[i] != nullptr)
+			_G(gfxDriver)->DestroyDDB(_GP(actspswbbmp)[i]);
+		_GP(actspswbbmp)[i] = nullptr;
+		_GP(actspswbcache)[i].valid = 0;
+	}
+
+	// cleanup GUI backgrounds
+	for (int i = 0; i < _GP(game).numgui; ++i) {
+		delete _GP(guibg)[i];
+		_GP(guibg)[i] = nullptr;
+		if (_GP(guibgbmp)[i])
+			_G(gfxDriver)->DestroyDDB(_GP(guibgbmp)[i]);
+		_GP(guibgbmp)[i] = nullptr;
+	}
+}
+
 void on_mainviewport_changed() {
 	if (!_G(gfxDriver)->RequiresFullRedrawEachFrame()) {
 		const auto &view = _GP(play).GetMainViewport();
@@ -705,7 +763,7 @@ IDriverDependantBitmap *recycle_ddb_bitmap(IDriverDependantBitmap *bimp, Bitmap
 }
 
 void invalidate_cached_walkbehinds() {
-	memset(&_G(actspswbcache)[0], 0, sizeof(CachedActSpsData) * _G(actSpsCount));
+	memset(&_GP(actspswbcache)[0], 0, sizeof(CachedActSpsData) * _GP(actspswbcache).size());
 }
 
 // sort_out_walk_behinds: modifies the supplied sprite by overwriting parts
@@ -823,26 +881,26 @@ void sort_out_char_sprite_walk_behind(int actspsIndex, int xx, int yy, int basel
 	if (_G(noWalkBehindsAtAll))
 		return;
 
-	if ((!_G(actspswbcache)[actspsIndex].valid) ||
-	        (_G(actspswbcache)[actspsIndex].xWas != xx) ||
-	        (_G(actspswbcache)[actspsIndex].yWas != yy) ||
-	        (_G(actspswbcache)[actspsIndex].baselineWas != basel)) {
-		_G(actspswb)[actspsIndex] = recycle_bitmap(_G(actspswb)[actspsIndex], _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth(), width, height, true);
-		Bitmap *wbSprite = _G(actspswb)[actspsIndex];
-
-		_G(actspswbcache)[actspsIndex].isWalkBehindHere = sort_out_walk_behinds(wbSprite, xx, yy, basel, _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic.get(), _G(actsps)[actspsIndex], zoom);
-		_G(actspswbcache)[actspsIndex].xWas = xx;
-		_G(actspswbcache)[actspsIndex].yWas = yy;
-		_G(actspswbcache)[actspsIndex].baselineWas = basel;
-		_G(actspswbcache)[actspsIndex].valid = 1;
-
-		if (_G(actspswbcache)[actspsIndex].isWalkBehindHere) {
-			_G(actspswbbmp)[actspsIndex] = recycle_ddb_bitmap(_G(actspswbbmp)[actspsIndex], _G(actspswb)[actspsIndex], false);
+	if ((!_GP(actspswbcache)[actspsIndex].valid) ||
+	        (_GP(actspswbcache)[actspsIndex].xWas != xx) ||
+	        (_GP(actspswbcache)[actspsIndex].yWas != yy) ||
+	        (_GP(actspswbcache)[actspsIndex].baselineWas != basel)) {
+		_GP(actspswb)[actspsIndex] = recycle_bitmap(_GP(actspswb)[actspsIndex], _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth(), width, height, true);
+		Bitmap *wbSprite = _GP(actspswb)[actspsIndex];
+
+		_GP(actspswbcache)[actspsIndex].isWalkBehindHere = sort_out_walk_behinds(wbSprite, xx, yy, basel, _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic.get(), _GP(actsps)[actspsIndex], zoom);
+		_GP(actspswbcache)[actspsIndex].xWas = xx;
+		_GP(actspswbcache)[actspsIndex].yWas = yy;
+		_GP(actspswbcache)[actspsIndex].baselineWas = basel;
+		_GP(actspswbcache)[actspsIndex].valid = 1;
+
+		if (_GP(actspswbcache)[actspsIndex].isWalkBehindHere) {
+			_GP(actspswbbmp)[actspsIndex] = recycle_ddb_bitmap(_GP(actspswbbmp)[actspsIndex], _GP(actspswb)[actspsIndex], false);
 		}
 	}
 
-	if (_G(actspswbcache)[actspsIndex].isWalkBehindHere) {
-		add_to_sprite_list(_G(actspswbbmp)[actspsIndex], xx, yy, basel, 0, -1, true);
+	if (_GP(actspswbcache)[actspsIndex].isWalkBehindHere) {
+		add_to_sprite_list(_GP(actspswbbmp)[actspsIndex], xx, yy, basel, 0, -1, true);
 	}
 }
 
@@ -987,6 +1045,18 @@ Bitmap *recycle_bitmap(Bitmap *bimp, int coldep, int wid, int hit, bool make_tra
 	return bimp;
 }
 
+// Allocates texture for the GUI
+void recreate_guibg_image(GUIMain *tehgui) {
+	int ifn = tehgui->ID;
+	delete _GP(guibg)[ifn];
+	_GP(guibg)[ifn] = BitmapHelper::CreateBitmap(tehgui->Width, tehgui->Height, _GP(game).GetColorDepth());
+	_GP(guibg)[ifn] = ReplaceBitmapWithSupportedFormat(_GP(guibg)[ifn]);
+
+	if (_GP(guibgbmp)[ifn] != nullptr) {
+		_G(gfxDriver)->DestroyDDB(_GP(guibgbmp)[ifn]);
+		_GP(guibgbmp)[ifn] = nullptr;
+	}
+}
 
 // Get the local tint at the specified X & Y co-ordinates, based on
 // room regions and SetAmbientTint
@@ -1089,7 +1159,7 @@ void apply_tint_or_light(int actspsindex, int light_level,
 	}
 
 // we can only do tint/light if the colour depths match
-	if (_GP(game).GetColorDepth() == _G(actsps)[actspsindex]->GetColorDepth()) {
+	if (_GP(game).GetColorDepth() == _GP(actsps)[actspsindex]->GetColorDepth()) {
 		Bitmap *oldwas;
 		// if the caller supplied a source bitmap, ->Blit from it
 		// (used as a speed optimisation where possible)
@@ -1097,10 +1167,10 @@ void apply_tint_or_light(int actspsindex, int light_level,
 			oldwas = blitFrom;
 		// otherwise, make a new target bmp
 		else {
-			oldwas = _G(actsps)[actspsindex];
-			_G(actsps)[actspsindex] = BitmapHelper::CreateBitmap(oldwas->GetWidth(), oldwas->GetHeight(), coldept);
+			oldwas = _GP(actsps)[actspsindex];
+			_GP(actsps)[actspsindex] = BitmapHelper::CreateBitmap(oldwas->GetWidth(), oldwas->GetHeight(), coldept);
 		}
-		Bitmap *active_spr = _G(actsps)[actspsindex];
+		Bitmap *active_spr = _GP(actsps)[actspsindex];
 
 		if (tint_amount) {
 			// It is an RGB tint
@@ -1134,13 +1204,13 @@ void apply_tint_or_light(int actspsindex, int light_level,
 	} else if (blitFrom) {
 		// sprite colour depth != game colour depth, so don't try and tint
 		// but we do need to do something, so copy the source
-		Bitmap *active_spr = _G(actsps)[actspsindex];
+		Bitmap *active_spr = _GP(actsps)[actspsindex];
 		active_spr->Blit(blitFrom, 0, 0, 0, 0, active_spr->GetWidth(), active_spr->GetHeight());
 	}
 
 }
 
-// Draws the specified 'sppic' sprite onto actsps[useindx] at the
+// Draws the specified 'sppic' sprite onto _GP(actsps)[useindx] at the
 // specified width and height, and flips the sprite if necessary.
 // Returns 1 if something was drawn to actsps; returns 0 if no
 // scaling or stretching was required, in which case nothing was done
@@ -1151,8 +1221,8 @@ int scale_and_flip_sprite(int useindx, int coldept, int zoom_level,
 	int actsps_used = 1;
 
 	// create and blank out the new sprite
-	_G(actsps)[useindx] = recycle_bitmap(_G(actsps)[useindx], coldept, newwidth, newheight, true);
-	Bitmap *active_spr = _G(actsps)[useindx];
+	_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], coldept, newwidth, newheight, true);
+	Bitmap *active_spr = _GP(actsps)[useindx];
 
 	if (zoom_level != 100) {
 		// Scaled character
@@ -1167,7 +1237,7 @@ int scale_and_flip_sprite(int useindx, int coldept, int zoom_level,
 
 		if (isMirrored) {
 			Bitmap *tempspr = BitmapHelper::CreateBitmap(newwidth, newheight, coldept);
-			tempspr->Fill(_G(actsps)[useindx]->GetMaskColor());
+			tempspr->Fill(_GP(actsps)[useindx]->GetMaskColor());
 			if ((IS_ANTIALIAS_SPRITES) && ((_GP(game).SpriteInfos[sppic].Flags & SPF_ALPHACHANNEL) == 0))
 				tempspr->AAStretchBlt(_GP(spriteset)[sppic], RectWH(0, 0, newwidth, newheight), Shared::kBitmap_Transparency);
 			else
@@ -1188,17 +1258,17 @@ int scale_and_flip_sprite(int useindx, int coldept, int zoom_level,
 		aa_mode |= AA_HFLIP;
 
 		aa_set_mode(aa_mode);
-		->AAStretchBlt(_G(actsps)[useindx],_GP(spriteset)[sppic],0,0,newwidth,newheight);
+		->AAStretchBlt(_GP(actsps)[useindx],_GP(spriteset)[sppic],0,0,newwidth,newheight);
 		}
 		else if (isMirrored) {
 		Bitmap *tempspr = BitmapHelper::CreateBitmap_ (coldept, newwidth, newheight);
-		->Clear (tempspr, ->GetMaskColor(_G(actsps)[useindx]));
+		->Clear (tempspr, ->GetMaskColor(_GP(actsps)[useindx]));
 		->StretchBlt (tempspr, _GP(spriteset)[sppic], 0, 0, newwidth, newheight);
-		->FlipBlt(Shared::kBitmap_HFlip, (_G(actsps)[useindx], tempspr, 0, 0);
+		->FlipBlt(Shared::kBitmap_HFlip, (_GP(actsps)[useindx], tempspr, 0, 0);
 		wfreeblock (tempspr);
 		}
 		else
-		->StretchBlt(_G(actsps)[useindx],_GP(spriteset)[sppic],0,0,newwidth,newheight);
+		->StretchBlt(_GP(actsps)[useindx],_GP(spriteset)[sppic],0,0,newwidth,newheight);
 		*/
 		if (_G(in_new_room))
 			unselect_palette();
@@ -1212,7 +1282,7 @@ int scale_and_flip_sprite(int useindx, int coldept, int zoom_level,
 			active_spr->FlipBlt(_GP(spriteset)[sppic], 0, 0, Shared::kBitmap_HFlip);
 		else
 			actsps_used = 0;
-		//->Blit (_GP(spriteset)[sppic], _G(actsps)[useindx], 0, 0, 0, 0, _G(actsps)[useindx]->GetWidth(), _G(actsps)[useindx]->GetHeight());
+		//->Blit (_GP(spriteset)[sppic], _GP(actsps)[useindx], 0, 0, 0, 0, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
 	}
 
 	return actsps_used;
@@ -1220,7 +1290,7 @@ int scale_and_flip_sprite(int useindx, int coldept, int zoom_level,
 
 
 
-// create the actsps[aa] image with the object drawn correctly
+// create the _GP(actsps)[aa] image with the object drawn correctly
 // returns 1 if nothing at all has changed and actsps is still
 // intact from last time; 0 otherwise
 int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysUseSoftware) {
@@ -1300,7 +1370,7 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	        (_G(walkBehindMethod) != DrawOverCharSprite) &&
 	        (_G(objcache)[aa].image != nullptr) &&
 	        (_G(objcache)[aa].sppic == _G(objs)[aa].num) &&
-	        (_G(actsps)[useindx] != nullptr)) {
+	        (_GP(actsps)[useindx] != nullptr)) {
 		// HW acceleration
 		_G(objcache)[aa].tintamntwas = tint_level;
 		_G(objcache)[aa].tintredwas = tint_red;
@@ -1333,17 +1403,17 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	        (_G(objcache)[aa].mirroredWas == isMirrored)) {
 		// the image is the same, we can use it cached!
 		if ((_G(walkBehindMethod) != DrawOverCharSprite) &&
-		        (_G(actsps)[useindx] != nullptr))
+		        (_GP(actsps)[useindx] != nullptr))
 			return 1;
 		// Check if the X & Y co-ords are the same, too -- if so, there
 		// is scope for further optimisations
 		if ((_G(objcache)[aa].xwas == _G(objs)[aa].x) &&
 		        (_G(objcache)[aa].ywas == _G(objs)[aa].y) &&
-		        (_G(actsps)[useindx] != nullptr) &&
+		        (_GP(actsps)[useindx] != nullptr) &&
 		        (_G(walk_behind_baselines_changed) == 0))
 			return 1;
-		_G(actsps)[useindx] = recycle_bitmap(_G(actsps)[useindx], coldept, sprwidth, sprheight);
-		_G(actsps)[useindx]->Blit(_G(objcache)[aa].image, 0, 0, 0, 0, _G(objcache)[aa].image->GetWidth(), _G(objcache)[aa].image->GetHeight());
+		_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], coldept, sprwidth, sprheight);
+		_GP(actsps)[useindx]->Blit(_G(objcache)[aa].image, 0, 0, 0, 0, _G(objcache)[aa].image->GetWidth(), _G(objcache)[aa].image->GetHeight());
 		return 0;
 	}
 
@@ -1356,7 +1426,7 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 		                                   _G(objs)[aa].num, sprwidth, sprheight, isMirrored);
 	} else {
 		// ensure actsps exists
-		_G(actsps)[useindx] = recycle_bitmap(_G(actsps)[useindx], coldept, _GP(game).SpriteInfos[_G(objs)[aa].num].Width, _GP(game).SpriteInfos[_G(objs)[aa].num].Height);
+		_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], coldept, _GP(game).SpriteInfos[_G(objs)[aa].num].Width, _GP(game).SpriteInfos[_G(objs)[aa].num].Height);
 	}
 
 	// direct read from source bitmap, where possible
@@ -1371,13 +1441,13 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 		                    tint_green, tint_blue, tint_light, coldept,
 		                    comeFrom);
 	} else if (!actspsUsed) {
-		_G(actsps)[useindx]->Blit(_GP(spriteset)[_G(objs)[aa].num], 0, 0, 0, 0, _GP(game).SpriteInfos[_G(objs)[aa].num].Width, _GP(game).SpriteInfos[_G(objs)[aa].num].Height);
+		_GP(actsps)[useindx]->Blit(_GP(spriteset)[_G(objs)[aa].num], 0, 0, 0, 0, _GP(game).SpriteInfos[_G(objs)[aa].num].Width, _GP(game).SpriteInfos[_G(objs)[aa].num].Height);
 	}
 
 	// Re-use the bitmap if it's the same size
 	_G(objcache)[aa].image = recycle_bitmap(_G(objcache)[aa].image, coldept, sprwidth, sprheight);
 	// Create the cached image and store it
-	_G(objcache)[aa].image->Blit(_G(actsps)[useindx], 0, 0, 0, 0, sprwidth, sprheight);
+	_G(objcache)[aa].image->Blit(_GP(actsps)[useindx], 0, 0, 0, 0, sprwidth, sprheight);
 	_G(objcache)[aa].sppic = _G(objs)[aa].num;
 	_G(objcache)[aa].tintamntwas = tint_level;
 	_G(objcache)[aa].tintredwas = tint_red;
@@ -1424,36 +1494,36 @@ void prepare_objects_for_drawing() {
 		} else if (_G(walkBehindMethod) == DrawAsSeparateCharSprite) {
 			sort_out_char_sprite_walk_behind(useindx, atxp, atyp, usebasel, _G(objs)[aa].zoom, _G(objs)[aa].last_width, _G(objs)[aa].last_height);
 		} else if ((!actspsIntact) && (_G(walkBehindMethod) == DrawOverCharSprite)) {
-			sort_out_walk_behinds(_G(actsps)[useindx], atxp, atyp, usebasel);
+			sort_out_walk_behinds(_GP(actsps)[useindx], atxp, atyp, usebasel);
 		}
 
-		if ((!actspsIntact) || (_G(actspsbmp)[useindx] == nullptr)) {
+		if ((!actspsIntact) || (_GP(actspsbmp)[useindx] == nullptr)) {
 			bool hasAlpha = (_GP(game).SpriteInfos[_G(objs)[aa].num].Flags & SPF_ALPHACHANNEL) != 0;
 
-			if (_G(actspsbmp)[useindx] != nullptr)
-				_G(gfxDriver)->DestroyDDB(_G(actspsbmp)[useindx]);
-			_G(actspsbmp)[useindx] = _G(gfxDriver)->CreateDDBFromBitmap(_G(actsps)[useindx], hasAlpha);
+			if (_GP(actspsbmp)[useindx] != nullptr)
+				_G(gfxDriver)->DestroyDDB(_GP(actspsbmp)[useindx]);
+			_GP(actspsbmp)[useindx] = _G(gfxDriver)->CreateDDBFromBitmap(_GP(actsps)[useindx], hasAlpha);
 		}
 
 		if (_G(gfxDriver)->HasAcceleratedTransform()) {
-			_G(actspsbmp)[useindx]->SetFlippedLeftRight(_G(objcache)[aa].mirroredWas != 0);
-			_G(actspsbmp)[useindx]->SetStretch(_G(objs)[aa].last_width, _G(objs)[aa].last_height);
-			_G(actspsbmp)[useindx]->SetTint(_G(objcache)[aa].tintredwas, _G(objcache)[aa].tintgrnwas, _G(objcache)[aa].tintbluwas, (_G(objcache)[aa].tintamntwas * 256) / 100);
+			_GP(actspsbmp)[useindx]->SetFlippedLeftRight(_G(objcache)[aa].mirroredWas != 0);
+			_GP(actspsbmp)[useindx]->SetStretch(_G(objs)[aa].last_width, _G(objs)[aa].last_height);
+			_GP(actspsbmp)[useindx]->SetTint(_G(objcache)[aa].tintredwas, _G(objcache)[aa].tintgrnwas, _G(objcache)[aa].tintbluwas, (_G(objcache)[aa].tintamntwas * 256) / 100);
 
 			if (_G(objcache)[aa].tintamntwas > 0) {
 				if (_G(objcache)[aa].tintlightwas == 0)  // luminance of 0 -- pass 1 to enable
-					_G(actspsbmp)[useindx]->SetLightLevel(1);
+					_GP(actspsbmp)[useindx]->SetLightLevel(1);
 				else if (_G(objcache)[aa].tintlightwas < 250)
-					_G(actspsbmp)[useindx]->SetLightLevel(_G(objcache)[aa].tintlightwas);
+					_GP(actspsbmp)[useindx]->SetLightLevel(_G(objcache)[aa].tintlightwas);
 				else
-					_G(actspsbmp)[useindx]->SetLightLevel(0);
+					_GP(actspsbmp)[useindx]->SetLightLevel(0);
 			} else if (_G(objcache)[aa].lightlevwas != 0)
-				_G(actspsbmp)[useindx]->SetLightLevel((_G(objcache)[aa].lightlevwas * 25) / 10 + 256);
+				_GP(actspsbmp)[useindx]->SetLightLevel((_G(objcache)[aa].lightlevwas * 25) / 10 + 256);
 			else
-				_G(actspsbmp)[useindx]->SetLightLevel(0);
+				_GP(actspsbmp)[useindx]->SetLightLevel(0);
 		}
 
-		add_to_sprite_list(_G(actspsbmp)[useindx], atxp, atyp, usebasel, _G(objs)[aa].transparent, _G(objs)[aa].num);
+		add_to_sprite_list(_GP(actspsbmp)[useindx], atxp, atyp, usebasel, _G(objs)[aa].transparent, _G(objs)[aa].num);
 	}
 }
 
@@ -1608,8 +1678,8 @@ void prepare_characters_for_drawing() {
 		        (_G(charcache)[aa].tintlightwas == tint_light) &&
 		        (_G(charcache)[aa].lightlevwas == light_level)) {
 			if (_G(walkBehindMethod) == DrawOverCharSprite) {
-				_G(actsps)[useindx] = recycle_bitmap(_G(actsps)[useindx], _G(charcache)[aa].image->GetColorDepth(), _G(charcache)[aa].image->GetWidth(), _G(charcache)[aa].image->GetHeight());
-				_G(actsps)[useindx]->Blit(_G(charcache)[aa].image, 0, 0, 0, 0, _G(actsps)[useindx]->GetWidth(), _G(actsps)[useindx]->GetHeight());
+				_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], _G(charcache)[aa].image->GetColorDepth(), _G(charcache)[aa].image->GetWidth(), _G(charcache)[aa].image->GetHeight());
+				_GP(actsps)[useindx]->Blit(_G(charcache)[aa].image, 0, 0, 0, 0, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
 			} else {
 				usingCachedImage = true;
 			}
@@ -1659,7 +1729,7 @@ void prepare_characters_for_drawing() {
 		// If cache needs to be re-drawn
 		if (!_G(charcache)[aa].inUse) {
 
-			// create the base sprite in actsps[useindx], which will
+			// create the base sprite in _GP(actsps)[useindx], which will
 			// be scaled and/or flipped, as appropriate
 			int actspsUsed = 0;
 			if (!_G(gfxDriver)->HasAcceleratedTransform()) {
@@ -1668,7 +1738,7 @@ void prepare_characters_for_drawing() {
 				                 newwidth, newheight, isMirrored);
 			} else {
 				// ensure actsps exists
-				_G(actsps)[useindx] = recycle_bitmap(_G(actsps)[useindx], coldept, _GP(game).SpriteInfos[sppic].Width, _GP(game).SpriteInfos[sppic].Height);
+				_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], coldept, _GP(game).SpriteInfos[sppic].Width, _GP(game).SpriteInfos[sppic].Height);
 			}
 
 			_G(our_eip) = 335;
@@ -1686,14 +1756,14 @@ void prepare_characters_for_drawing() {
 				                    comeFrom);
 			} else if (!actspsUsed) {
 				// no scaling, flipping or tinting was done, so just blit it normally
-				_G(actsps)[useindx]->Blit(_GP(spriteset)[sppic], 0, 0, 0, 0, _G(actsps)[useindx]->GetWidth(), _G(actsps)[useindx]->GetHeight());
+				_GP(actsps)[useindx]->Blit(_GP(spriteset)[sppic], 0, 0, 0, 0, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
 			}
 
 			// update the character cache with the new image
 			_G(charcache)[aa].inUse = 1;
-			//_G(charcache)[aa].image = BitmapHelper::CreateBitmap_ (coldept, _G(actsps)[useindx]->GetWidth(), _G(actsps)[useindx]->GetHeight());
-			_G(charcache)[aa].image = recycle_bitmap(_G(charcache)[aa].image, coldept, _G(actsps)[useindx]->GetWidth(), _G(actsps)[useindx]->GetHeight());
-			_G(charcache)[aa].image->Blit(_G(actsps)[useindx], 0, 0, 0, 0, _G(actsps)[useindx]->GetWidth(), _G(actsps)[useindx]->GetHeight());
+			//_G(charcache)[aa].image = BitmapHelper::CreateBitmap_ (coldept, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
+			_G(charcache)[aa].image = recycle_bitmap(_G(charcache)[aa].image, coldept, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
+			_G(charcache)[aa].image->Blit(_GP(actsps)[useindx], 0, 0, 0, 0, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
 
 		} // end if !cache.inUse
 
@@ -1712,31 +1782,31 @@ void prepare_characters_for_drawing() {
 		} else if (_G(walkBehindMethod) == DrawAsSeparateCharSprite) {
 			sort_out_char_sprite_walk_behind(useindx, bgX, bgY, usebasel, _G(charextra)[aa].zoom, newwidth, newheight);
 		} else if (_G(walkBehindMethod) == DrawOverCharSprite) {
-			sort_out_walk_behinds(_G(actsps)[useindx], bgX, bgY, usebasel);
+			sort_out_walk_behinds(_GP(actsps)[useindx], bgX, bgY, usebasel);
 		}
 
-		if ((!usingCachedImage) || (_G(actspsbmp)[useindx] == nullptr)) {
+		if ((!usingCachedImage) || (_GP(actspsbmp)[useindx] == nullptr)) {
 			bool hasAlpha = (_GP(game).SpriteInfos[sppic].Flags & SPF_ALPHACHANNEL) != 0;
 
-			_G(actspsbmp)[useindx] = recycle_ddb_bitmap(_G(actspsbmp)[useindx], _G(actsps)[useindx], hasAlpha);
+			_GP(actspsbmp)[useindx] = recycle_ddb_bitmap(_GP(actspsbmp)[useindx], _GP(actsps)[useindx], hasAlpha);
 		}
 
 		if (_G(gfxDriver)->HasAcceleratedTransform()) {
-			_G(actspsbmp)[useindx]->SetStretch(newwidth, newheight);
-			_G(actspsbmp)[useindx]->SetFlippedLeftRight(isMirrored != 0);
-			_G(actspsbmp)[useindx]->SetTint(tint_red, tint_green, tint_blue, (tint_amount * 256) / 100);
+			_GP(actspsbmp)[useindx]->SetStretch(newwidth, newheight);
+			_GP(actspsbmp)[useindx]->SetFlippedLeftRight(isMirrored != 0);
+			_GP(actspsbmp)[useindx]->SetTint(tint_red, tint_green, tint_blue, (tint_amount * 256) / 100);
 
 			if (tint_amount != 0) {
 				if (tint_light == 0) // tint with 0 luminance, pass as 1 instead
-					_G(actspsbmp)[useindx]->SetLightLevel(1);
+					_GP(actspsbmp)[useindx]->SetLightLevel(1);
 				else if (tint_light < 250)
-					_G(actspsbmp)[useindx]->SetLightLevel(tint_light);
+					_GP(actspsbmp)[useindx]->SetLightLevel(tint_light);
 				else
-					_G(actspsbmp)[useindx]->SetLightLevel(0);
+					_GP(actspsbmp)[useindx]->SetLightLevel(0);
 			} else if (light_level != 0)
-				_G(actspsbmp)[useindx]->SetLightLevel((light_level * 25) / 10 + 256);
+				_GP(actspsbmp)[useindx]->SetLightLevel((light_level * 25) / 10 + 256);
 			else
-				_G(actspsbmp)[useindx]->SetLightLevel(0);
+				_GP(actspsbmp)[useindx]->SetLightLevel(0);
 
 		}
 
@@ -1745,10 +1815,17 @@ void prepare_characters_for_drawing() {
 		chin->actx = atxp;
 		chin->acty = atyp;
 
-		add_to_sprite_list(_G(actspsbmp)[useindx], bgX, bgY, usebasel, chin->transparency, sppic);
+		add_to_sprite_list(_GP(actspsbmp)[useindx], bgX, bgY, usebasel, chin->transparency, sppic);
 	}
 }
 
+Shared::Bitmap *get_cached_character_image(int charid) {
+	return _GP(actsps)[charid + MAX_ROOM_OBJECTS];
+}
+
+Shared::Bitmap *get_cached_object_image(int objid) {
+	return _GP(actsps)[objid];
+}
 
 // Compiles a list of room sprites (characters, objects, background)
 void prepare_room_sprites() {
@@ -1917,14 +1994,15 @@ void draw_gui_and_overlays() {
 				if (_GP(guis)[aa].Transparency == 255) continue;
 
 				_GP(guis)[aa].ClearChanged();
-				if (_G(guibg)[aa] == nullptr)
+				if (_GP(guibg)[aa] == nullptr ||
+					_GP(guibg)[aa]->GetSize() != Size(_GP(guis)[aa].Width, _GP(guis)[aa].Height))
 					recreate_guibg_image(&_GP(guis)[aa]);
 
 				_G(eip_guinum) = aa;
 				_G(our_eip) = 370;
-				_G(guibg)[aa]->ClearTransparent();
+				_GP(guibg)[aa]->ClearTransparent();
 				_G(our_eip) = 372;
-				_GP(guis)[aa].DrawAt(_G(guibg)[aa], 0, 0);
+				_GP(guis)[aa].DrawAt(_GP(guibg)[aa], 0, 0);
 				_G(our_eip) = 373;
 
 				bool isAlpha = false;
@@ -1933,14 +2011,14 @@ void draw_gui_and_overlays() {
 
 					if ((_GP(game).options[OPT_NEWGUIALPHA] == kGuiAlphaRender_Legacy) && (_GP(guis)[aa].BgImage > 0)) {
 						// old-style (pre-3.0.2) GUI alpha rendering
-						repair_alpha_channel(_G(guibg)[aa], _GP(spriteset)[_GP(guis)[aa].BgImage]);
+						repair_alpha_channel(_GP(guibg)[aa], _GP(spriteset)[_GP(guis)[aa].BgImage]);
 					}
 				}
 
-				if (_G(guibgbmp)[aa] != nullptr) {
-					_G(gfxDriver)->UpdateDDBFromBitmap(_G(guibgbmp)[aa], _G(guibg)[aa], isAlpha);
+				if (_GP(guibgbmp)[aa] != nullptr) {
+					_G(gfxDriver)->UpdateDDBFromBitmap(_GP(guibgbmp)[aa], _GP(guibg)[aa], isAlpha);
 				} else {
-					_G(guibgbmp)[aa] = _G(gfxDriver)->CreateDDBFromBitmap(_G(guibg)[aa], isAlpha);
+					_GP(guibgbmp)[aa] = _G(gfxDriver)->CreateDDBFromBitmap(_GP(guibg)[aa], isAlpha);
 				}
 				_G(our_eip) = 374;
 			}
@@ -1958,7 +2036,7 @@ void draw_gui_and_overlays() {
 			        (_GP(guis)[aa].PopupStyle != kGUIPopupNoAutoRemove))
 				continue;
 
-			add_thing_to_draw(_G(guibgbmp)[aa], _GP(guis)[aa].X, _GP(guis)[aa].Y, _GP(guis)[aa].Transparency, _GP(guis)[aa].HasAlphaChannel());
+			add_thing_to_draw(_GP(guibgbmp)[aa], _GP(guis)[aa].X, _GP(guis)[aa].Y, _GP(guis)[aa].Transparency, _GP(guis)[aa].HasAlphaChannel());
 
 			// only poll if the interface is enabled (mouseovers should not
 			// work while in Wait state)
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 455757b604c..00ec67e08ec 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -73,12 +73,18 @@ class Camera;
 
 // Initializes drawing methods and optimisation
 void init_draw_method();
+// Initializes global game drawing resources
+void init_game_drawdata();
 // Initializes drawing resources upon entering new room
 void init_room_drawdata();
 // Disposes resources related to the current drawing methods
 void dispose_draw_method();
+// Disposes global game drawing resources
+void dispose_game_drawdata();
 // Disposes any temporary resources on leaving current room
 void dispose_room_drawdata();
+// Releases all the cached textures of game objects
+void clear_drawobj_cache();
 // Updates drawing settings depending on main viewport's size and position on screen
 void on_mainviewport_changed();
 // Notifies that a new room viewport was created
@@ -106,6 +112,7 @@ void invalidate_rect(int x1, int y1, int x2, int y2, bool in_room);
 
 void mark_current_background_dirty();
 void invalidate_cached_walkbehinds();
+
 // Avoid freeing and reallocating the memory if possible
 Shared::Bitmap *recycle_bitmap(Shared::Bitmap *bimp, int coldep, int wid, int hit, bool make_transparent = false);
 Engine::IDriverDependantBitmap *recycle_ddb_bitmap(Engine::IDriverDependantBitmap *bimp, Shared::Bitmap *source, bool hasAlpha = false, bool opaque = false);
@@ -117,6 +124,9 @@ void construct_game_scene(bool full_redraw = false);
 void construct_game_screen_overlay(bool draw_mouse = true);
 // Construct engine overlay with debugging tools (fps, console)
 void construct_engine_overlay();
+// Clears black game borders in legacy letterbox mode
+void clear_letterbox_borders();
+
 void add_to_sprite_list(Engine::IDriverDependantBitmap *spp, int xx, int yy, int baseline, int trans, int sprNum, bool isWalkBehind = false);
 void tint_image(Shared::Bitmap *g, Shared::Bitmap *source, int red, int grn, int blu, int light_level, int luminance = 255);
 void draw_sprite_support_alpha(Shared::Bitmap *ds, bool ds_has_alpha, int xpos, int ypos, Shared::Bitmap *image, bool src_has_alpha,
@@ -136,7 +146,10 @@ void putpixel_compensate(Shared::Bitmap *g, int xx, int yy, int col);
 // returns 1 if nothing at all has changed and actsps is still
 // intact from last time; 0 otherwise
 int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysUseSoftware);
-void clear_letterbox_borders();
+// Returns a cached character image prepared for the render
+Shared::Bitmap *get_cached_character_image(int charid);
+// Returns a cached object image prepared for the render
+Shared::Bitmap *get_cached_object_image(int objid);
 
 void draw_and_invalidate_text(Shared::Bitmap *ds, int x1, int y1, int font, color_t text_color, const char *text);
 
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index e2b20d0f966..d6ebdf22709 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -400,11 +400,8 @@ void unload_game_file() {
 	_GP(characterScriptObjNames).clear();
 	free(_G(charextra));
 	free(_G(mls));
-	free(_G(actsps));
-	free(_G(actspsbmp));
-	free(_G(actspswb));
-	free(_G(actspswbbmp));
-	free(_G(actspswbcache));
+
+	dispose_game_drawdata();
 
 	if ((_G(gameinst) != nullptr) && (_G(gameinst)->pc != 0)) {
 		quit("Error: unload_game called while script still running");
@@ -472,13 +469,7 @@ void unload_game_file() {
 	delete[] _G(scrDialog);
 	_G(scrDialog) = nullptr;
 
-	for (int i = 0; i < _GP(game).numgui; ++i) {
-		free(_G(guibg)[i]);
-		_G(guibg)[i] = nullptr;
-	}
-
 	_GP(guiScriptObjNames).clear();
-	free(_G(guibg));
 	_GP(guis).clear();
 	free(_G(scrGui));
 
diff --git a/engines/ags/engine/ac/global_object.cpp b/engines/ags/engine/ac/global_object.cpp
index af6f9a7b973..9d227077700 100644
--- a/engines/ags/engine/ac/global_object.cpp
+++ b/engines/ags/engine/ac/global_object.cpp
@@ -277,17 +277,16 @@ void MergeObject(int obn) {
 	int theHeight;
 
 	construct_object_gfx(obn, nullptr, &theHeight, true);
+	Bitmap *actsp = get_cached_object_image(obn);
 
-	//Bitmap *oldabuf = graphics->bmp;
-	//abuf = _GP(thisroom).BgFrames.Graphic[_GP(play).bg_frame];
 	PBitmap bg_frame = _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic;
-	if (bg_frame->GetColorDepth() != _G(actsps)[obn]->GetColorDepth())
+	if (bg_frame->GetColorDepth() != actsp->GetColorDepth())
 		quit("!MergeObject: unable to merge object due to color depth differences");
 
 	int xpos = data_to_game_coord(_G(objs)[obn].x);
 	int ypos = (data_to_game_coord(_G(objs)[obn].y) - theHeight);
 
-	draw_sprite_support_alpha(bg_frame.get(), false, xpos, ypos, _G(actsps)[obn], (_GP(game).SpriteInfos[_G(objs)[obn].num].Flags & SPF_ALPHACHANNEL) != 0);
+	draw_sprite_support_alpha(bg_frame.get(), false, xpos, ypos, actsp, (_GP(game).SpriteInfos[_G(objs)[obn].num].Flags & SPF_ALPHACHANNEL) != 0);
 	invalidate_screen();
 	mark_current_background_dirty();
 
@@ -522,15 +521,14 @@ void GetObjectPropertyText(int item, const char *property, char *bufer) {
 
 Bitmap *GetObjectImage(int obj, int *isFlipped) {
 	if (!_G(gfxDriver)->HasAcceleratedTransform()) {
-		if (_G(actsps)[obj] != nullptr) {
-			// the _G(actsps) image is pre-flipped, so no longer register the image as such
+		if (_GP(actsps)[obj] != nullptr) {
+			// the actsps image is pre-flipped, so no longer register the image as such
 			if (isFlipped)
 				*isFlipped = 0;
 
-			return _G(actsps)[obj];
+			return _GP(actsps)[obj];
 		}
 	}
-
 	return _GP(spriteset)[_G(objs)[obj].num];
 }
 
diff --git a/engines/ags/engine/ac/gui.cpp b/engines/ags/engine/ac/gui.cpp
index f2b48785fa6..6e011ac4729 100644
--- a/engines/ags/engine/ac/gui.cpp
+++ b/engines/ags/engine/ac/gui.cpp
@@ -125,8 +125,6 @@ void GUI_SetSize(ScriptGUI *sgui, int widd, int hitt) {
 	tehgui->Width = widd;
 	tehgui->Height = hitt;
 
-	recreate_guibg_image(tehgui);
-
 	tehgui->MarkChanged();
 }
 
@@ -549,20 +547,6 @@ int adjust_y_for_guis(int yy) {
 	return yy;
 }
 
-void recreate_guibg_image(GUIMain *tehgui) {
-	int ifn = tehgui->ID;
-	delete _G(guibg)[ifn];
-	_G(guibg)[ifn] = BitmapHelper::CreateBitmap(tehgui->Width, tehgui->Height, _GP(game).GetColorDepth());
-	if (_G(guibg)[ifn] == nullptr)
-		quit("SetGUISize: internal error: unable to reallocate gui cache");
-	_G(guibg)[ifn] = ReplaceBitmapWithSupportedFormat(_G(guibg)[ifn]);
-
-	if (_G(guibgbmp)[ifn] != nullptr) {
-		_G(gfxDriver)->DestroyDDB(_G(guibgbmp)[ifn]);
-		_G(guibgbmp)[ifn] = nullptr;
-	}
-}
-
 int gui_get_interactable(int x, int y) {
 	if ((_GP(game).options[OPT_DISABLEOFF] == 3) && (_G(all_buttons_disabled) > 0))
 		return -1;
diff --git a/engines/ags/engine/ac/gui.h b/engines/ags/engine/ac/gui.h
index 2aa2992b1b1..41d848634ca 100644
--- a/engines/ags/engine/ac/gui.h
+++ b/engines/ags/engine/ac/gui.h
@@ -78,7 +78,6 @@ int     convert_gui_disabled_style(int oldStyle);
 void    update_gui_disabled_status();
 int     adjust_x_for_guis(int xx, int yy);
 int     adjust_y_for_guis(int yy);
-void    recreate_guibg_image(GUIMain *tehgui);
 
 int     gui_get_interactable(int x, int y);
 int     gui_on_mouse_move();
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 0b7be963f7d..7e80bc5ad0f 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -302,31 +302,10 @@ void unload_old_room() {
 
 	croom_ptr_clear();
 
-	// clear the object cache
-	for (ff = 0; ff < MAX_ROOM_OBJECTS; ff++) {
-		delete _G(objcache)[ff].image;
-		_G(objcache)[ff].image = nullptr;
-	}
-	// clear the _G(actsps) buffers to save memory, since the
+	// clear the _GP(actsps) buffers to save memory, since the
 	// objects/characters involved probably aren't on the
 	// new screen. this also ensures all cached data is flushed
-	for (ff = 0; ff < MAX_ROOM_OBJECTS + _GP(game).numcharacters; ff++) {
-		delete _G(actsps)[ff];
-		_G(actsps)[ff] = nullptr;
-
-		if (_G(actspsbmp)[ff] != nullptr)
-			_G(gfxDriver)->DestroyDDB(_G(actspsbmp)[ff]);
-		_G(actspsbmp)[ff] = nullptr;
-
-		delete _G(actspswb)[ff];
-		_G(actspswb)[ff] = nullptr;
-
-		if (_G(actspswbbmp)[ff] != nullptr)
-			_G(gfxDriver)->DestroyDDB(_G(actspswbbmp)[ff]);
-		_G(actspswbbmp)[ff] = nullptr;
-
-		_G(actspswbcache)[ff].valid = 0;
-	}
+	clear_drawobj_cache();
 
 	// if Hide Player Character was ticked, restore it to visible
 	if (_GP(play).temporarily_turned_off_character >= 0) {
@@ -950,15 +929,6 @@ void new_room(int newnum, CharacterInfo *forchar) {
 		// Delete all cached sprites
 		_GP(spriteset).DisposeAll();
 
-		// Delete all gui background images
-		for (int i = 0; i < _GP(game).numgui; i++) {
-			delete _G(guibg)[i];
-			_G(guibg)[i] = nullptr;
-
-			if (_G(guibgbmp)[i])
-				_G(gfxDriver)->DestroyDDB(_G(guibgbmp)[i]);
-			_G(guibgbmp)[i] = nullptr;
-		}
 		GUI::MarkAllGUIForUpdate();
 	}
 
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 4017d596ec3..3ba652ab5f5 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -354,12 +354,8 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 	_G(charextra) = (CharacterExtras *)calloc(_GP(game).numcharacters, sizeof(CharacterExtras));
 	_G(charcache) = (CharacterCache *)calloc(1, sizeof(CharacterCache) * _GP(game).numcharacters + 5);
 	_G(mls) = (MoveList *)calloc(_GP(game).numcharacters + MAX_ROOM_OBJECTS + 1, sizeof(MoveList));
-	_G(actSpsCount) = _GP(game).numcharacters + MAX_ROOM_OBJECTS + 2;
-	_G(actsps) = (Bitmap **)calloc(_G(actSpsCount), sizeof(Bitmap *));
-	_G(actspsbmp) = (IDriverDependantBitmap **)calloc(_G(actSpsCount), sizeof(IDriverDependantBitmap *));
-	_G(actspswb) = (Bitmap **)calloc(_G(actSpsCount), sizeof(Bitmap *));
-	_G(actspswbbmp) = (IDriverDependantBitmap **)calloc(_G(actSpsCount), sizeof(IDriverDependantBitmap *));
-	_G(actspswbcache) = (CachedActSpsData *)calloc(_G(actSpsCount), sizeof(CachedActSpsData));
+	init_game_drawdata();
+
 	_GP(play).charProps.resize(_GP(game).numcharacters);
 	_G(old_dialog_scripts) = ents.OldDialogScripts;
 	_G(old_speech_lines) = ents.OldSpeechLines;
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index c8ed7b98f95..ccb9f296fe3 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -344,15 +344,8 @@ void DoBeforeRestore(PreservedParams &pp) {
 		}
 	}
 
-	// cleanup GUI backgrounds
-	for (int i = 0; i < _GP(game).numgui; ++i) {
-		delete _G(guibg)[i];
-		_G(guibg)[i] = nullptr;
-
-		if (_G(guibgbmp)[i])
-			_G(gfxDriver)->DestroyDDB(_G(guibgbmp)[i]);
-		_G(guibgbmp)[i] = nullptr;
-	}
+    // Cleanup drawn caches
+    clear_drawobj_cache();
 
 	// preserve script data sizes and cleanup scripts
 	pp.GlScDataSize = _G(gameinst)->globaldatasize;
@@ -608,11 +601,6 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	}
 	update_directional_sound_vol();
 
-	for (int i = 0; i < _GP(game).numgui; ++i) {
-		_G(guibg)[i] = BitmapHelper::CreateBitmap(_GP(guis)[i].Width, _GP(guis)[i].Height, _GP(game).GetColorDepth());
-		_G(guibg)[i] = ReplaceBitmapWithSupportedFormat(_G(guibg)[i]);
-	}
-
 	recreate_overlay_ddbs();
 
 	GUI::MarkAllGUIForUpdate();
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index c76e468c8d5..1dca064cbce 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -577,9 +577,6 @@ void engine_init_game_settings() {
 
 	int ee;
 
-	for (ee = 0; ee < MAX_ROOM_OBJECTS + _GP(game).numcharacters; ee++)
-		_G(actsps)[ee] = nullptr;
-
 	for (ee = 0; ee < 256; ee++) {
 		if (_GP(game).paluses[ee] != PAL_BACKGROUND)
 			_G(palette)[ee] = _GP(game).defpal[ee];
@@ -602,9 +599,6 @@ void engine_init_game_settings() {
 	if (_G(playerchar)->view >= 0)
 		precache_view(_G(playerchar)->view);
 
-	for (ee = 0; ee < MAX_ROOM_OBJECTS; ee++)
-		_G(objcache)[ee].image = nullptr;
-
 	/*  dummygui.guiId = -1;
 	dummyguicontrol.guin = -1;
 	dummyguicontrol.objn = -1;*/
@@ -644,13 +638,6 @@ void engine_init_game_settings() {
 		_G(charextra)[ee].slow_move_counter = 0;
 		_G(charextra)[ee].animwait = 0;
 	}
-	// multiply up gui positions
-	_G(guibg) = (Bitmap **)malloc(sizeof(Bitmap *) * _GP(game).numgui);
-	_G(guibgbmp) = (IDriverDependantBitmap **)malloc(sizeof(IDriverDependantBitmap *) * _GP(game).numgui);
-	for (ee = 0; ee < _GP(game).numgui; ee++) {
-		_G(guibg)[ee] = nullptr;
-		_G(guibgbmp)[ee] = nullptr;
-	}
 
 	_G(our_eip) = -5;
 	for (ee = 0; ee < _GP(game).numinvitems; ee++) {
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index d603b142ca1..613366bda3b 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -173,6 +173,15 @@ Globals::Globals() {
 	_dynamicallyCreatedSurfaces = new AGS::Shared::Bitmap *[MAX_DYNAMIC_SURFACES];
 	Common::fill(_dynamicallyCreatedSurfaces, _dynamicallyCreatedSurfaces +
 	             MAX_DYNAMIC_SURFACES, (AGS::Shared::Bitmap *)nullptr);
+
+	_actsps = new std::vector<Shared::Bitmap *>();
+	_actspsbmp = new std::vector<Engine::IDriverDependantBitmap *>();
+	_actspswb = new	std::vector<Shared::Bitmap *>();
+	_actspswbbmp = new std::vector<Engine::IDriverDependantBitmap *>();
+	_actspswbcache = new std::vector<CachedActSpsData>();
+	_guibg = new std::vector<Shared::Bitmap *>();
+	_guibgbmp = new std::vector<Engine::IDriverDependantBitmap *>();
+
 	_maincoltable = new COLOR_MAP();
 	_palette = new color[256];
 	for (int i = 0; i < PALETTE_COUNT; ++i)
@@ -413,6 +422,13 @@ Globals::~Globals() {
 	delete _CameraDrawData;
 	delete _sprlist;
 	delete _thingsToDrawList;
+	delete _actsps;
+	delete _actspsbmp;
+	delete _actspswb;
+	delete _actspswbbmp;
+	delete _actspswbcache;
+	delete _guibg;
+	delete _guibgbmp;
 	delete[] _dynamicallyCreatedSurfaces;
 	delete[] _palette;
 	delete _maincoltable;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 29e14c52bca..18ed2a1d4ee 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -569,18 +569,19 @@ public:
 
 	// actsps is used for temporary storage of the bitamp image
 	// of the latest version of the sprite
-	int _actSpsCount = 0;
-	AGS::Shared::Bitmap **_actsps = nullptr;
-	AGS::Engine::IDriverDependantBitmap **_actspsbmp = nullptr;
+	std::vector<Shared::Bitmap *> *_actsps;
+	std::vector<Engine::IDriverDependantBitmap *> *_actspsbmp;
 	// temporary cache of walk-behind for this actsps image
-	AGS::Shared::Bitmap **_actspswb = nullptr;
-	AGS::Engine::IDriverDependantBitmap **_actspswbbmp = nullptr;
-	CachedActSpsData *_actspswbcache = nullptr;
+	std::vector<Shared::Bitmap *> *_actspswb;
+	std::vector<Engine::IDriverDependantBitmap *> *_actspswbbmp;
+	std::vector<CachedActSpsData> *_actspswbcache;
+	// GUI surfaces
+	std::vector<Shared::Bitmap *> *_guibg;
+	std::vector<Engine::IDriverDependantBitmap *> *_guibgbmp;
+
 	bool _current_background_is_dirty = false;
 	// Room background sprite
 	AGS::Engine::IDriverDependantBitmap *_roomBackgroundBmp = nullptr;
-	AGS::Shared::Bitmap **_guibg = nullptr;
-	AGS::Engine::IDriverDependantBitmap **_guibgbmp = nullptr;
 	AGS::Shared::Bitmap *_debugConsoleBuffer = nullptr;
 	// whether there are currently remnants of a DisplaySpeech
 	bool _screen_is_dirty = false;


Commit: 2d995bbe074cd1badd3cfef05f727de52ec74fa0
    https://github.com/scummvm/scummvm/commit/2d995bbe074cd1badd3cfef05f727de52ec74fa0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-14T21:33:38-07:00

Commit Message:
AGS: Simplified use of ReplaceBitmapWithSupportedFormat()

>From upstream 2132ba833b9d4508ed3f81a65f531b88f6087cb5

Changed paths:
    engines/ags/engine/ac/dialog.cpp
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/engine/ac/dynamic_sprite.cpp
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/global_dynamic_sprite.cpp
    engines/ags/engine/ac/screen.cpp
    engines/ags/engine/gui/gui_dialog.cpp


diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index 98c181a14dc..0e262a32300 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -766,8 +766,8 @@ void DialogOptions::Redraw() {
 
 	update_polled_stuff_if_runtime();
 
-	subBitmap = recycle_bitmap(subBitmap, tempScrn->GetColorDepth(), dirtywidth, dirtyheight);
-	subBitmap = ReplaceBitmapWithSupportedFormat(subBitmap);
+	subBitmap = recycle_bitmap(subBitmap,
+		_G(gfxDriver)->GetCompatibleBitmapFormat(tempScrn->GetColorDepth()), dirtywidth, dirtyheight);
 
 	update_polled_stuff_if_runtime();
 
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index f72ed968335..e0177b19098 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -155,8 +155,14 @@ Bitmap *convert_32_to_32bgr(Bitmap *tempbl) {
 //
 Bitmap *AdjustBitmapForUseWithDisplayMode(Bitmap *bitmap, bool has_alpha) {
 	const int bmp_col_depth = bitmap->GetColorDepth();
-	// const int sys_col_depth = System_GetColorDepth();
+	const int sys_col_depth = System_GetColorDepth();
 	const int game_col_depth = _GP(game).GetColorDepth();
+	const int compat_col_depth = _G(gfxDriver)->GetCompatibleBitmapFormat(game_col_depth);
+
+	const bool must_switch_palette = bitmap->GetColorDepth() == 8 && game_col_depth > 8;
+	if (must_switch_palette)
+		select_palette(_G(palette));
+
 	Bitmap *new_bitmap = bitmap;
 
 	//
@@ -184,54 +190,48 @@ Bitmap *AdjustBitmapForUseWithDisplayMode(Bitmap *bitmap, bool has_alpha) {
 	}
 	// In 32-bit game hicolor bitmaps must be converted to the true color
 	else if (game_col_depth == 32 && (bmp_col_depth > 8 && bmp_col_depth <= 16)) {
-		new_bitmap = BitmapHelper::CreateBitmapCopy(bitmap, game_col_depth);
+		new_bitmap = BitmapHelper::CreateBitmapCopy(bitmap, compat_col_depth);
 	}
 	// In non-32-bit game truecolor bitmaps must be downgraded
 	else if (game_col_depth <= 16 && bmp_col_depth > 16) {
 		if (has_alpha) // if has valid alpha channel, convert it to regular transparency mask
 			new_bitmap = remove_alpha_channel(bitmap);
 		else // else simply convert bitmap
-			new_bitmap = BitmapHelper::CreateBitmapCopy(bitmap, game_col_depth);
+			new_bitmap = BitmapHelper::CreateBitmapCopy(bitmap, compat_col_depth);
 	}
 	// Special case when we must convert 16-bit RGB to BGR
 	else if (_G(convert_16bit_bgr) == 1 && bmp_col_depth == 16) {
 		new_bitmap = convert_16_to_16bgr(bitmap);
 	}
+
+	// Finally, if we did not create a new copy already, - convert to driver compatible format
+	if ((new_bitmap == bitmap) && (bmp_col_depth != compat_col_depth))
+		new_bitmap = GfxUtil::ConvertBitmap(bitmap, compat_col_depth);
+
+	if (must_switch_palette)
+		unselect_palette();
+
 	return new_bitmap;
 }
 
+Bitmap *CreateCompatBitmap(int width, int height, int col_depth) {
+	return new Bitmap(width, height,
+		_G(gfxDriver)->GetCompatibleBitmapFormat(col_depth == 0 ? _GP(game).GetColorDepth() : col_depth));
+}
+
 Bitmap *ReplaceBitmapWithSupportedFormat(Bitmap *bitmap) {
-	Bitmap *new_bitmap = GfxUtil::ConvertBitmap(bitmap, _G(gfxDriver)->GetCompatibleBitmapFormat(bitmap->GetColorDepth()));
-	if (new_bitmap != bitmap)
-		delete bitmap;
-	return new_bitmap;
+	return GfxUtil::ConvertBitmap(bitmap, _G(gfxDriver)->GetCompatibleBitmapFormat(bitmap->GetColorDepth()));
 }
 
 Bitmap *PrepareSpriteForUse(Bitmap *bitmap, bool has_alpha) {
-	bool must_switch_palette = bitmap->GetColorDepth() == 8 && _GP(game).GetColorDepth() > 8;
-	if (must_switch_palette)
-		select_palette(_G(palette));
-
 	Bitmap *new_bitmap = AdjustBitmapForUseWithDisplayMode(bitmap, has_alpha);
 	if (new_bitmap != bitmap)
 		delete bitmap;
-	new_bitmap = ReplaceBitmapWithSupportedFormat(new_bitmap);
-
-	if (must_switch_palette)
-		unselect_palette();
 	return new_bitmap;
 }
 
 PBitmap PrepareSpriteForUse(PBitmap bitmap, bool has_alpha) {
-	bool must_switch_palette = bitmap->GetColorDepth() == 8 && System_GetColorDepth() > 8;
-	if (must_switch_palette)
-		select_palette(_G(palette));
-
 	Bitmap *new_bitmap = AdjustBitmapForUseWithDisplayMode(bitmap.get(), has_alpha);
-	new_bitmap = ReplaceBitmapWithSupportedFormat(new_bitmap);
-
-	if (must_switch_palette)
-		unselect_palette();
 	return new_bitmap == bitmap.get() ? bitmap : PBitmap(new_bitmap); // if bitmap is same, don't create new smart ptr!
 }
 
@@ -349,8 +349,7 @@ void create_blank_image(int coldepth) {
 	// so it's the most likey place for a crash
 	//try
 	//{
-	Bitmap *blank = BitmapHelper::CreateBitmap(16, 16, coldepth);
-	blank = ReplaceBitmapWithSupportedFormat(blank);
+	Bitmap *blank = CreateCompatBitmap(16, 16, coldepth);
 	blank->Clear();
 	_G(blankImage) = _G(gfxDriver)->CreateDDBFromBitmap(blank, false, true);
 	_G(blankSidebarImage) = _G(gfxDriver)->CreateDDBFromBitmap(blank, false, true);
@@ -1049,8 +1048,7 @@ Bitmap *recycle_bitmap(Bitmap *bimp, int coldep, int wid, int hit, bool make_tra
 void recreate_guibg_image(GUIMain *tehgui) {
 	int ifn = tehgui->ID;
 	delete _GP(guibg)[ifn];
-	_GP(guibg)[ifn] = BitmapHelper::CreateBitmap(tehgui->Width, tehgui->Height, _GP(game).GetColorDepth());
-	_GP(guibg)[ifn] = ReplaceBitmapWithSupportedFormat(_GP(guibg)[ifn]);
+	_GP(guibg)[ifn] = CreateCompatBitmap(tehgui->Width, tehgui->Height);
 
 	if (_GP(guibgbmp)[ifn] != nullptr) {
 		_G(gfxDriver)->DestroyDDB(_GP(guibgbmp)[ifn]);
@@ -1917,8 +1915,7 @@ void draw_fps(const Rect &viewport) {
 	static Bitmap *fpsDisplay = nullptr;
 	const int font = FONT_NORMAL;
 	if (fpsDisplay == nullptr) {
-		fpsDisplay = BitmapHelper::CreateBitmap(viewport.GetWidth(), (getfontheight_outlined(font) + get_fixed_pixel_size(5)), _GP(game).GetColorDepth());
-		fpsDisplay = ReplaceBitmapWithSupportedFormat(fpsDisplay);
+		fpsDisplay = CreateCompatBitmap(viewport.GetWidth(), (getfontheight_outlined(font) + get_fixed_pixel_size(5)), _GP(game).GetColorDepth());
 	}
 	fpsDisplay->ClearTransparent();
 
@@ -2274,8 +2271,7 @@ void construct_engine_overlay() {
 		int barheight = getheightoflines(font, DEBUG_CONSOLE_NUMLINES - 1) + 4;
 
 		if (_G(debugConsoleBuffer) == nullptr) {
-			_G(debugConsoleBuffer) = BitmapHelper::CreateBitmap(viewport.GetWidth(), barheight, _GP(game).GetColorDepth());
-			_G(debugConsoleBuffer) = ReplaceBitmapWithSupportedFormat(_G(debugConsoleBuffer));
+			_G(debugConsoleBuffer) = CreateCompatBitmap(viewport.GetWidth(), barheight);
 		}
 
 		color_t draw_color = _G(debugConsoleBuffer)->GetCompatibleColor(15);
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 00ec67e08ec..9a5bccce96d 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -178,11 +178,11 @@ extern AGS_INLINE int game_to_ctx_data_size(int size, bool hires_ctx);
 // This function converts game coordinates coming from script to the actual game resolution.
 extern AGS_INLINE void defgame_to_finalgame_coords(int &x, int &y);
 
-// Checks if the bitmap needs to be converted and **deletes original** if a new bitmap
-// had to be created (by default).
-// TODO: this helper function was meant to remove bitmap deletion from the GraphicsDriver's
-// implementations while keeping code changes to minimum. The proper solution would probably
-// be to use shared pointers when storing Bitmaps, or make Bitmap reference-counted object.
+// Creates bitmap of a format compatible with the gfxdriver;
+// if col_depth is 0, uses game's native color depth.
+Shared::Bitmap *CreateCompatBitmap(int width, int height, int col_depth = 0);
+// Checks if the bitmap is compatible with the gfxdriver;
+// returns same bitmap or its copy of a compatible format.
 Shared::Bitmap *ReplaceBitmapWithSupportedFormat(Shared::Bitmap *bitmap);
 // Checks if the bitmap needs any kind of adjustments before it may be used
 // in AGS sprite operations. Also handles number of certain special cases
diff --git a/engines/ags/engine/ac/dynamic_sprite.cpp b/engines/ags/engine/ac/dynamic_sprite.cpp
index 6e4f68eb0db..f64067144d7 100644
--- a/engines/ags/engine/ac/dynamic_sprite.cpp
+++ b/engines/ags/engine/ac/dynamic_sprite.cpp
@@ -322,7 +322,7 @@ ScriptDynamicSprite *DynamicSprite_CreateFromScreenShot(int width, int height) {
 	update_polled_stuff_if_runtime();
 
 	// replace the bitmap in the sprite set
-	add_dynamic_sprite(gotSlot, ReplaceBitmapWithSupportedFormat(newPic));
+	add_dynamic_sprite(gotSlot, newPic);
 	ScriptDynamicSprite *new_spr = new ScriptDynamicSprite(gotSlot);
 	return new_spr;
 }
@@ -385,14 +385,15 @@ ScriptDynamicSprite *DynamicSprite_Create(int width, int height, int alphaChanne
 	if (gotSlot <= 0)
 		return nullptr;
 
-	Bitmap *newPic = BitmapHelper::CreateTransparentBitmap(width, height, _GP(game).GetColorDepth());
+	Bitmap *newPic = CreateCompatBitmap(width, height);
 	if (newPic == nullptr)
 		return nullptr;
 
+	newPic->ClearTransparent();
 	if ((alphaChannel) && (_GP(game).GetColorDepth() < 32))
 		alphaChannel = false;
 
-	add_dynamic_sprite(gotSlot, ReplaceBitmapWithSupportedFormat(newPic), alphaChannel != 0);
+	add_dynamic_sprite(gotSlot, newPic, alphaChannel != 0);
 	ScriptDynamicSprite *new_spr = new ScriptDynamicSprite(gotSlot);
 	return new_spr;
 }
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index d6ebdf22709..0e55ad221d2 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -961,7 +961,7 @@ bool read_savedgame_screenshot(const String &savedgame, int &want_shot) {
 		int slot = _GP(spriteset).GetFreeIndex();
 		if (slot > 0) {
 			// add it into the sprite set
-			add_dynamic_sprite(slot, ReplaceBitmapWithSupportedFormat(desc.UserImage.release()));
+			add_dynamic_sprite(slot, PrepareSpriteForUse(desc.UserImage.release(), false));
 			want_shot = slot;
 		}
 	}
diff --git a/engines/ags/engine/ac/global_dynamic_sprite.cpp b/engines/ags/engine/ac/global_dynamic_sprite.cpp
index 2891916577b..bccc16e00cf 100644
--- a/engines/ags/engine/ac/global_dynamic_sprite.cpp
+++ b/engines/ags/engine/ac/global_dynamic_sprite.cpp
@@ -48,7 +48,7 @@ int LoadImageFile(const char *filename) {
 	if (gotSlot <= 0)
 		return 0;
 
-	add_dynamic_sprite(gotSlot, ReplaceBitmapWithSupportedFormat(loadedFile));
+	add_dynamic_sprite(gotSlot, PrepareSpriteForUse(loadedFile, false));
 
 	return gotSlot;
 }
diff --git a/engines/ags/engine/ac/screen.cpp b/engines/ags/engine/ac/screen.cpp
index 89d01aa4f08..a2fc8de46b1 100644
--- a/engines/ags/engine/ac/screen.cpp
+++ b/engines/ags/engine/ac/screen.cpp
@@ -85,7 +85,6 @@ IDriverDependantBitmap *prepare_screen_for_transition_in() {
 	if (_G(saved_viewport_bitmap) == nullptr)
 		quit("Crossfade: buffer is null attempting transition");
 
-	_G(saved_viewport_bitmap) = ReplaceBitmapWithSupportedFormat(_G(saved_viewport_bitmap));
 	const Rect &viewport = _GP(play).GetMainViewport();
 	if (_G(saved_viewport_bitmap)->GetHeight() < viewport.GetHeight()) {
 		Bitmap *enlargedBuffer = BitmapHelper::CreateBitmap(_G(saved_viewport_bitmap)->GetWidth(), viewport.GetHeight(), _G(saved_viewport_bitmap)->GetColorDepth());
diff --git a/engines/ags/engine/gui/gui_dialog.cpp b/engines/ags/engine/gui/gui_dialog.cpp
index 6ee7eb6458c..665d943096d 100644
--- a/engines/ags/engine/gui/gui_dialog.cpp
+++ b/engines/ags/engine/gui/gui_dialog.cpp
@@ -60,8 +60,7 @@ Bitmap *prepare_gui_screen(int x, int y, int width, int height, bool opaque) {
 	if (_G(windowBuffer)) {
 		_G(windowBuffer) = recycle_bitmap(_G(windowBuffer), _G(windowBuffer)->GetColorDepth(), _G(windowPosWidth), _G(windowPosHeight), !opaque);
 	} else {
-		_G(windowBuffer) = BitmapHelper::CreateBitmap(_G(windowPosWidth), _G(windowPosHeight), _GP(game).GetColorDepth());
-		_G(windowBuffer) = ReplaceBitmapWithSupportedFormat(_G(windowBuffer));
+		_G(windowBuffer) = CreateCompatBitmap(_G(windowPosWidth), _G(windowPosHeight));
 	}
 	_G(dialogDDB) = recycle_ddb_bitmap(_G(dialogDDB), _G(windowBuffer), false, opaque);
 	return _G(windowBuffer);


Commit: 448bb2d77ef4e49aaaa4ad5298ab07fa1737115a
    https://github.com/scummvm/scummvm/commit/448bb2d77ef4e49aaaa4ad5298ab07fa1737115a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-14T21:46:58-07:00

Commit Message:
AGS: Moved "LucasFan" font hack to game_init, enable for all systems

>From upstream 5d84ea958f9d2ba5582e69f211dbcb006ed4e187

Changed paths:
    engines/ags/engine/game/game_init.cpp
    engines/ags/shared/font/ags_font_renderer.h
    engines/ags/shared/font/fonts.cpp
    engines/ags/shared/font/fonts.h
    engines/ags/shared/font/ttf_font_renderer.cpp
    engines/ags/shared/font/ttf_font_renderer.h
    engines/ags/shared/font/wfn_font_renderer.h


diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 3ba652ab5f5..55fa74b409a 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -52,6 +52,7 @@
 #include "ags/engine/script/exports.h"
 #include "ags/engine/script/script.h"
 #include "ags/engine/script/script_runtime.h"
+#include "ags/shared/util/string_compat.h"
 #include "ags/shared/util/string_utils.h"
 #include "ags/engine/media/audio/audio_system.h"
 #include "ags/globals.h"
@@ -281,6 +282,24 @@ void LoadFonts(GameDataVersion data_ver) {
 			}
 		}
 	}
+
+	// Additional fixups - after all the fonts are registered
+	for (int i = 0; i < _GP(game).numfonts; ++i) {
+		if (!is_bitmap_font(i)) {
+			// Check for the LucasFan font since it comes with an outline font that
+			// is drawn incorrectly with Freetype versions > 2.1.3.
+			// A simple workaround is to disable outline fonts for it and use
+			// automatic outline drawing.
+			const int outline_font = get_font_outline(i);
+			if (outline_font < 0)
+				continue;
+			const char *name = get_font_name(i);
+			const char *outline_name = get_font_name(outline_font);
+			if ((ags_stricmp(name, "LucasFan-Font") == 0) &&
+					(ags_stricmp(outline_name, "Arcade") == 0))
+				set_font_outline(i, FONT_OUTLINE_AUTO);
+		}
+	}
 }
 
 void LoadLipsyncData() {
diff --git a/engines/ags/shared/font/ags_font_renderer.h b/engines/ags/shared/font/ags_font_renderer.h
index 9cfaa8ee092..0936d2ddfdb 100644
--- a/engines/ags/shared/font/ags_font_renderer.h
+++ b/engines/ags/shared/font/ags_font_renderer.h
@@ -59,10 +59,13 @@ struct FontMetrics {
 // NOTE: this extending interface is not yet exposed to plugins
 class IAGSFontRenderer2 {
 public:
+	// Tells if this is a bitmap font (otherwise it's a vector font)
 	virtual bool IsBitmapFont() = 0;
 	// Load font, applying extended font rendering parameters
 	virtual bool LoadFromDiskEx(int fontNumber, int fontSize, const FontRenderParams *params,
 		FontMetrics *metrics) = 0;
+	// Gets font's name; must return an empty string if no name is available
+	virtual const char *GetName(int fontNumber) = 0;
 protected:
 	IAGSFontRenderer2() {}
 	~IAGSFontRenderer2() {}
diff --git a/engines/ags/shared/font/fonts.cpp b/engines/ags/shared/font/fonts.cpp
index 5451f57b140..49bb4438bba 100644
--- a/engines/ags/shared/font/fonts.cpp
+++ b/engines/ags/shared/font/fonts.cpp
@@ -110,6 +110,13 @@ bool font_supports_extended_characters(size_t fontNumber) {
 	return _GP(fonts)[fontNumber].Renderer->SupportsExtendedCharacters(fontNumber);
 }
 
+const char *get_font_name(size_t fontNumber) {
+	if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer2)
+		return "";
+	const char *name = _GP(fonts)[fontNumber].Renderer2->GetName(fontNumber);
+	return name ? name : "";
+}
+
 void ensure_text_valid_for_font(char *text, size_t fontnum) {
 	if (fontnum >= _GP(fonts).size() || !_GP(fonts)[fontnum].Renderer)
 		return;
diff --git a/engines/ags/shared/font/fonts.h b/engines/ags/shared/font/fonts.h
index 2c6bb129438..b09c5d1fb84 100644
--- a/engines/ags/shared/font/fonts.h
+++ b/engines/ags/shared/font/fonts.h
@@ -71,6 +71,8 @@ bool font_first_renderer_loaded();
 bool is_font_loaded(size_t fontNumber);
 bool is_bitmap_font(size_t fontNumber);
 bool font_supports_extended_characters(size_t fontNumber);
+// Get font's name, if it's available, otherwise returns empty string
+const char *get_font_name(size_t fontNumber);
 // TODO: with changes to WFN font renderer that implemented safe rendering of
 // strings containing invalid chars (since 3.3.1) this function is not
 // important, except for (maybe) few particular cases.
diff --git a/engines/ags/shared/font/ttf_font_renderer.cpp b/engines/ags/shared/font/ttf_font_renderer.cpp
index a82c798a1e6..982af7e67fa 100644
--- a/engines/ags/shared/font/ttf_font_renderer.cpp
+++ b/engines/ags/shared/font/ttf_font_renderer.cpp
@@ -22,17 +22,11 @@
 #include "ags/lib/alfont/alfont.h"
 #include "ags/shared/core/platform.h"
 #include "ags/globals.h"
-
-#define AGS_OUTLINE_FONT_FIX (!AGS_PLATFORM_OS_WINDOWS)
-
 #include "ags/shared/core/asset_manager.h"
 #include "ags/shared/font/ttf_font_renderer.h"
 #include "ags/shared/util/stream.h"
-
-#if AGS_OUTLINE_FONT_FIX // TODO: factor out the hack in LoadFromDiskEx
 #include "ags/shared/ac/game_struct_defines.h"
 #include "ags/shared/font/fonts.h"
-#endif
 
 namespace AGS3 {
 
@@ -143,6 +137,10 @@ bool TTFFontRenderer::LoadFromDiskEx(int fontNumber, int fontSize,
 	return true;
 }
 
+const char *TTFFontRenderer::GetName(int fontNumber) {
+	return alfont_get_name(_fontData[fontNumber].AlFont);
+}
+
 void TTFFontRenderer::FreeMemory(int fontNumber) {
 	alfont_destroy_font(_fontData[fontNumber].AlFont);
 	_fontData.erase(fontNumber);
diff --git a/engines/ags/shared/font/ttf_font_renderer.h b/engines/ags/shared/font/ttf_font_renderer.h
index c8afd6c896b..80a30b492dc 100644
--- a/engines/ags/shared/font/ttf_font_renderer.h
+++ b/engines/ags/shared/font/ttf_font_renderer.h
@@ -49,6 +49,7 @@ public:
 	bool IsBitmapFont() override;
 	bool LoadFromDiskEx(int fontNumber, int fontSize, const FontRenderParams *params,
 		FontMetrics *metrics) override;
+	const char *GetName(int fontNumber) override;
 
 private:
 	struct FontData {
diff --git a/engines/ags/shared/font/wfn_font_renderer.h b/engines/ags/shared/font/wfn_font_renderer.h
index a0d87a7c5cf..13600faf736 100644
--- a/engines/ags/shared/font/wfn_font_renderer.h
+++ b/engines/ags/shared/font/wfn_font_renderer.h
@@ -47,6 +47,7 @@ public:
 	bool IsBitmapFont() override;
 	bool LoadFromDiskEx(int fontNumber, int fontSize,
 		const FontRenderParams *params, FontMetrics *metrics) override;
+	const char *GetName(int fontNumber) override { return ""; }
 
 private:
 	struct FontData {




More information about the Scummvm-git-logs mailing list