Files
scummvm/engines/ags/engine/gui/gui_engine.cpp
T
Walter Agazzi 7af9888de2 AGS: Engine: rewrote dynamic sprite change notification method
Previous existing method ran through virtually all the game objects that may reference a sprite, and
checked each one of them. Those who did reference the modified dynamic sprite were marked for
update and/or had their draw caches cleared.
Furthermore, for safety reasons, the Graphic properties of these objects were reset to 0.

This process was slow under two conditions:
- if there are alot of objects (especially overlays which may be created at runtime in large quantities);
- if there are alot of temporary dynamic sprites created and deleted per game frame (for example,
when drawing something complicated).

The new method:
1. We no longer reset object properties, this turned to be a bad design. There's another safety fix done
in the SpriteCache, which returns a placeholder sprite for any invalid sprite ID. This should make things
simpler and faster.
2. For the texture-based renderers, it turned out that updating the shared texture entries was enough
to make objects using them redraw. So for these renderers we don't need to reset any more caches.
3. For software renderer, introduced a GameState::spritemodified vector of bools. Whenever a sprite is
modified or deleted, engine sets a corresponding flag in this vector, and that triggers an update at the
next draw pass for all the objects that still reference that sprite.

GUI PROBLEM: because GUIs rendering is still different from other objects, even in texture render
mode, they are still marked for update the old way.
This should be addressed separately in the future.
From upstream 0b84a21cfb486e3a9fa03e6c2b354750742ed3fd
and 3f70782761a698a69d31af77833c05bf38eb7394
2024-10-30 18:07:42 +02:00

183 lines
6.4 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//=============================================================================
//
// Implementation from acgui.h and acgui.cpp specific to Engine runtime
//
//=============================================================================
#include "ags/shared/ac/game_version.h"
#include "ags/engine/ac/system.h"
#include "ags/shared/font/fonts.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/gui/gui_button.h"
#include "ags/shared/gui/gui_inv.h"
#include "ags/shared/gui/gui_label.h"
#include "ags/shared/gui/gui_listbox.h"
#include "ags/shared/gui/gui_textbox.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/global_translation.h"
#include "ags/engine/ac/string.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/engine/gfx/blender.h"
#include "ags/globals.h"
namespace AGS3 {
extern void wouttext_outline(Shared::Bitmap *ds, int xxp, int yyp, int usingfont, color_t text_color, const char *texx);
using namespace AGS::Shared;
// For engine these are defined in ac.cpp
extern void replace_macro_tokens(const char *, String &);
bool GUIMain::HasAlphaChannel() const {
if (this->BgImage > 0) {
// alpha state depends on background image
return is_sprite_alpha(this->BgImage);
}
if (this->BgColor > 0) {
// not alpha transparent if there is a background color
return false;
}
// transparent background, enable alpha blending
return _GP(game).GetColorDepth() >= 24 &&
// transparent background have alpha channel only since 3.2.0;
// "classic" gui rendering mode historically had non-alpha transparent backgrounds
// (3.2.0 broke the compatibility, now we restore it)
_G(loaded_game_file_version) >= kGameVersion_320 && _GP(game).options[OPT_NEWGUIALPHA] != kGuiAlphaRender_Legacy;
}
//=============================================================================
// Engine-specific implementation split out of acgui.cpp
//=============================================================================
int get_adjusted_spritewidth(int spr) {
return _GP(game).SpriteInfos[spr].Width;
}
int get_adjusted_spriteheight(int spr) {
return _GP(game).SpriteInfos[spr].Height;
}
bool is_sprite_alpha(int spr) {
return ((_GP(game).SpriteInfos[spr].Flags & SPF_ALPHACHANNEL) != 0);
}
void set_eip_guiobj(int eip) {
_G(eip_guiobj) = eip;
}
int get_eip_guiobj() {
return _G(eip_guiobj);
}
namespace AGS {
namespace Shared {
String GUI::ApplyTextDirection(const String &text) {
if (_GP(game).options[OPT_RIGHTLEFTWRITE] == 0)
return text;
String res_text = text;
(get_uformat() == U_UTF8) ? res_text.ReverseUTF8() : res_text.Reverse();
return res_text;
}
String GUI::TransformTextForDrawing(const String &text, bool translate, bool apply_direction) {
String res_text = translate ? String(get_translation(text.GetCStr())) : text;
if (translate && apply_direction)
res_text = ApplyTextDirection(res_text);
return res_text;
}
size_t GUI::SplitLinesForDrawing(const char *text, bool is_translated, SplitLines &lines, int font, int width, size_t max_lines) {
// Use the engine's word wrap tool, to have RTL writing and other features
return break_up_text_into_lines(text, is_translated, lines, width, font);
}
void GUIObject::MarkChanged() {
_hasChanged = true;
_GP(guis)[ParentId].MarkControlChanged();
}
void GUIObject::MarkParentChanged() {
_GP(guis)[ParentId].MarkControlChanged();
}
void GUIObject::MarkPositionChanged(bool self_changed) {
_hasChanged |= self_changed;
_GP(guis)[ParentId].NotifyControlPosition();
}
void GUIObject::MarkStateChanged(bool self_changed, bool parent_changed) {
_hasChanged |= self_changed;
_GP(guis)[ParentId].NotifyControlState(Id, self_changed | parent_changed);
}
void GUIObject::ClearChanged() {
_hasChanged = false;
}
int GUILabel::PrepareTextToDraw() {
const bool is_translated = (Flags & kGUICtrl_Translated) != 0;
replace_macro_tokens(is_translated ? get_translation(Text.GetCStr()) : Text.GetCStr(), _textToDraw);
return GUI::SplitLinesForDrawing(_textToDraw.GetCStr(), is_translated, _GP(Lines), Font, _width);
}
void GUITextBox::DrawTextBoxContents(Bitmap *ds, int x, int y, color_t text_color) {
_textToDraw = Text;
bool reverse = false;
// Text boxes input is never "translated" in regular sense,
// but they use this flag to apply text direction
if ((_G(loaded_game_file_version) >= kGameVersion_361) && ((Flags & kGUICtrl_Translated) != 0)) {
_textToDraw = GUI::ApplyTextDirection(Text);
reverse = _GP(game).options[OPT_RIGHTLEFTWRITE] != 0;
}
Line tpos = GUI::CalcTextPositionHor(_textToDraw.GetCStr(), Font,
x + 1 + get_fixed_pixel_size(1), x + _width - 1, y + 1 + get_fixed_pixel_size(1),
reverse ? kAlignTopRight : kAlignTopLeft);
wouttext_outline(ds, tpos.X1, tpos.Y1, Font, text_color, _textToDraw.GetCStr());
if (IsGUIEnabled(this)) {
// draw a cursor
const int cursor_width = get_fixed_pixel_size(5);
int draw_at_x = reverse ? tpos.X1 - 3 - cursor_width : tpos.X2 + 3;
int draw_at_y = y + 1 + get_font_height(Font);
ds->DrawRect(Rect(draw_at_x, draw_at_y, draw_at_x + cursor_width, draw_at_y + (get_fixed_pixel_size(1) - 1)), text_color);
}
}
void GUIListBox::PrepareTextToDraw(const String &text) {
_textToDraw = GUI::TransformTextForDrawing(text, (Flags & kGUICtrl_Translated) != 0, (_G(loaded_game_file_version) >= kGameVersion_361));
}
void GUIButton::PrepareTextToDraw() {
_textToDraw = GUI::TransformTextForDrawing(_text, (Flags & kGUICtrl_Translated) != 0, (_G(loaded_game_file_version) >= kGameVersion_361));
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3