openMSX
OSDImageBasedWidget.cc
Go to the documentation of this file.
2#include "OSDTopWidget.hh"
3#include "OSDGUI.hh"
4#include "BaseImage.hh"
5#include "Display.hh"
6#include "TclObject.hh"
7#include "CommandException.hh"
8#include "Timer.hh"
9#include "ranges.hh"
10#include "stl.hh"
11#include "view.hh"
12#include "xrange.hh"
13#include <algorithm>
14#include <cassert>
15
16using namespace gl;
17
18namespace openmsx {
19
21 : OSDWidget(display_, name_)
22 , startFadeTime(0)
23 , fadePeriod(0.0)
24 , fadeTarget(1.0)
25 , startFadeValue(1.0)
26 , error(false)
27{
28 ranges::fill(rgba, 0x000000ff);
29}
30
32
33static void get4(Interpreter& interp, const TclObject& value, uint32_t* result)
34{
35 auto len = value.getListLength(interp);
36 if (len == 4) {
37 for (auto i : xrange(4)) {
38 result[i] = value.getListIndex(interp, i).getInt(interp);
39 }
40 } else if (len == 1) {
41 uint32_t val = value.getInt(interp);
42 for (auto i : xrange(4)) {
43 result[i] = val;
44 }
45 } else {
46 throw CommandException("Expected either 1 or 4 values.");
47 }
48}
50 Interpreter& interp, std::string_view propName, const TclObject& value)
51{
52 if (propName == "-rgba") {
53 uint32_t newRGBA[4];
54 get4(interp, value, newRGBA);
55 setRGBA(newRGBA);
56 } else if (propName == "-rgb") {
57 uint32_t newRGB[4];
58 get4(interp, value, newRGB);
59 uint32_t newRGBA[4];
60 for (auto i : xrange(4)) {
61 newRGBA[i] = (rgba[i] & 0x000000ff) |
62 ((newRGB[i] << 8) & 0xffffff00);
63 }
64 setRGBA(newRGBA);
65 } else if (propName == "-alpha") {
66 uint32_t newAlpha[4];
67 get4(interp, value, newAlpha);
68 uint32_t newRGBA[4];
69 for (auto i : xrange(4)) {
70 newRGBA[i] = (rgba[i] & 0xffffff00) |
71 (newAlpha[i] & 0x000000ff);
72 }
73 setRGBA(newRGBA);
74 } else if (propName == "-fadePeriod") {
75 updateCurrentFadeValue();
76 fadePeriod = value.getDouble(interp);
77 } else if (propName == "-fadeTarget") {
78 updateCurrentFadeValue();
79 fadeTarget = std::clamp(value.getDouble(interp), 0.0, 1.0);
80 } else if (propName == "-fadeCurrent") {
81 startFadeValue = std::clamp(value.getDouble(interp), 0.0, 1.0);
82 startFadeTime = Timer::getTime();
83 } else {
84 OSDWidget::setProperty(interp, propName, value);
85 }
86}
87
88void OSDImageBasedWidget::setRGBA(const uint32_t newRGBA[4])
89{
90 if ((rgba[0] == newRGBA[0]) &&
91 (rgba[1] == newRGBA[1]) &&
92 (rgba[2] == newRGBA[2]) &&
93 (rgba[3] == newRGBA[3])) {
94 // not changed
95 return;
96 }
98 for (auto i : xrange(4)) {
99 rgba[i] = newRGBA[i];
100 }
101}
102
103static void set4(const uint32_t rgba[4], uint32_t mask, unsigned shift, TclObject& result)
104{
105 if ((rgba[0] == rgba[1]) && (rgba[0] == rgba[2]) && (rgba[0] == rgba[3])) {
106 result = (rgba[0] & mask) >> shift;
107 } else {
108 result.addListElements(view::transform(xrange(4), [&](auto i) {
109 return int((rgba[i] & mask) >> shift);
110 }));
111 }
112}
113void OSDImageBasedWidget::getProperty(std::string_view propName, TclObject& result) const
114{
115 if (propName == "-rgba") {
116 set4(rgba, 0xffffffff, 0, result);
117 } else if (propName == "-rgb") {
118 set4(rgba, 0xffffff00, 8, result);
119 } else if (propName == "-alpha") {
120 set4(rgba, 0x000000ff, 0, result);
121 } else if (propName == "-fadePeriod") {
122 result = fadePeriod;
123 } else if (propName == "-fadeTarget") {
124 result = fadeTarget;
125 } else if (propName == "-fadeCurrent") {
126 result = getCurrentFadeValue();
127 } else {
128 OSDWidget::getProperty(propName, result);
129 }
130}
131
132static constexpr bool constantAlpha(const uint32_t rgba[4])
133{
134 return ((rgba[0] & 0xff) == (rgba[1] & 0xff)) &&
135 ((rgba[0] & 0xff) == (rgba[2] & 0xff)) &&
136 ((rgba[0] & 0xff) == (rgba[3] & 0xff));
137}
139{
140 return constantAlpha(rgba);
141}
142
144{
145 return getParent()->getRecursiveFadeValue() * getCurrentFadeValue();
146}
147
149{
150 return (getFadedAlpha() != 0) || isRecursiveFading();
151}
152
153bool OSDImageBasedWidget::isFading() const
154{
155 return (startFadeValue != fadeTarget) && (fadePeriod != 0.0f);
156}
157
159{
160 if (isFading()) return true;
161 return getParent()->isRecursiveFading();
162}
163
164float OSDImageBasedWidget::getCurrentFadeValue() const
165{
166 if (!isFading()) {
167 return startFadeValue;
168 }
169 return getCurrentFadeValue(Timer::getTime());
170}
171
172float OSDImageBasedWidget::getCurrentFadeValue(uint64_t now) const
173{
174 assert(now >= startFadeTime);
175
176 int diff = int(now - startFadeTime); // int should be big enough
177 assert(fadePeriod != 0.0f);
178 float delta = diff / (1000000.0f * fadePeriod);
179 if (startFadeValue < fadeTarget) {
180 float tmp = startFadeValue + delta;
181 if (tmp >= fadeTarget) {
182 startFadeValue = fadeTarget;
183 return startFadeValue;
184 }
185 return tmp;
186 } else {
187 float tmp = startFadeValue - delta;
188 if (tmp <= fadeTarget) {
189 startFadeValue = fadeTarget;
190 return startFadeValue;
191 }
192 return tmp;
193 }
194}
195
196void OSDImageBasedWidget::updateCurrentFadeValue()
197{
198 auto now = Timer::getTime();
199 if (isFading()) {
200 startFadeValue = getCurrentFadeValue(now);
201 }
202 startFadeTime = now;
203}
204
206{
207 error = false;
208 image.reset();
209}
210
211vec2 OSDImageBasedWidget::getTransformedPos(const OutputSurface& output) const
212{
213 return getParent()->transformPos(
214 output, float(getScaleFactor(output)) * getPos(), getRelPos());
215}
216
217void OSDImageBasedWidget::setError(std::string message)
218{
219 error = true;
220
221 // The suppressErrors property only exists to break an infinite loop
222 // when an error occurs (e.g. couldn't load font) while displaying the
223 // error message on the OSD system.
224 // The difficulty in detecting this loop is that it's not a recursive
225 // loop, but each iteration takes one frame: on the CliComm Tcl callback,
226 // the OSD widgets get created, but only the next frame, when this new
227 // widget is actually drawn the next error occurs.
228 if (!needSuppressErrors()) {
229 getDisplay().getOSDGUI().getTopWidget().queueError(std::move(message));
230 }
231}
232
234{
235 paint(output, false);
236}
237
239{
240 paint(output, true);
241}
242
244{
245 if (!image && !hasError()) {
246 try {
247 if (getDisplay().getOSDGUI().isOpenGL()) {
248 image = createGL(output);
249 } else {
250 image = createSDL(output);
251 }
252 } catch (MSXException& e) {
253 setError(std::move(e).getMessage());
254 }
255 }
256}
257
258void OSDImageBasedWidget::paint(OutputSurface& output, bool openGL)
259{
260 // Note: Even when alpha == 0 we still create the image:
261 // It may be needed to get the dimensions to be able to position
262 // child widgets.
263 assert(openGL == getDisplay().getOSDGUI().isOpenGL()); (void)openGL;
264 createImage(output);
265
266 auto fadedAlpha = getFadedAlpha();
267 if ((fadedAlpha != 0) && image) {
268 ivec2 drawPos = round(getTransformedPos(output));
269 image->draw(output, drawPos, fadedAlpha);
270 }
271 if (isRecursiveFading()) {
273 }
274}
275
276} // namespace openmsx
Represents the output window/screen of openMSX.
Definition: Display.hh:33
OSDGUI & getOSDGUI()
Definition: Display.hh:45
void refresh() const
Definition: OSDGUI.cc:26
const OSDTopWidget & getTopWidget() const
Definition: OSDGUI.hh:19
void setProperty(Interpreter &interp, std::string_view name, const TclObject &value) override
virtual std::unique_ptr< BaseImage > createSDL(OutputSurface &output)=0
std::unique_ptr< BaseImage > image
void createImage(OutputSurface &output)
virtual uint8_t getFadedAlpha() const =0
float getRecursiveFadeValue() const override
bool isRecursiveFading() const override
OSDImageBasedWidget(Display &display, const TclObject &name)
void paintGL(OutputSurface &output) override
virtual std::unique_ptr< BaseImage > createGL(OutputSurface &output)=0
void setError(std::string message)
void getProperty(std::string_view name, TclObject &result) const override
void paintSDL(OutputSurface &output) override
void queueError(std::string message)
Definition: OSDTopWidget.cc:49
virtual void getProperty(std::string_view name, TclObject &result) const
Definition: OSDWidget.cc:259
Display & getDisplay() const
Definition: OSDWidget.hh:69
int getScaleFactor(const OutputSurface &output) const
Definition: OSDWidget.cc:349
gl::vec2 transformPos(const OutputSurface &output, gl::vec2 pos, gl::vec2 relPos) const
Definition: OSDWidget.cc:360
virtual float getRecursiveFadeValue() const
Definition: OSDWidget.cc:287
gl::vec2 getRelPos() const
Definition: OSDWidget.hh:36
gl::vec2 getPos() const
Definition: OSDWidget.hh:35
bool needSuppressErrors() const
Definition: OSDWidget.cc:305
OSDWidget * getParent()
Definition: OSDWidget.hh:39
virtual void setProperty(Interpreter &interp, std::string_view name, const TclObject &value)
Definition: OSDWidget.cc:215
virtual bool isRecursiveFading() const =0
A frame buffer where pixels can be written to.
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:125
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:143
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:101
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
constexpr double e
Definition: Math.hh:18
Definition: gl_mat.hh:23
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:292
uint64_t getTime()
Get current (real) time in us.
Definition: Timer.cc:7
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr nibble mask[4][13]
Definition: RP5C01.cc:34
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:256
constexpr auto transform(Range &&range, UnaryOp op)
Definition: view.hh:378
constexpr auto xrange(T e)
Definition: xrange.hh:133