openMSX
OSDImageBasedWidget.cc
Go to the documentation of this file.
1 #include "OSDImageBasedWidget.hh"
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 
16 using namespace gl;
17 
18 namespace openmsx {
19 
20 OSDImageBasedWidget::OSDImageBasedWidget(Display& display_, const TclObject& name_)
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 
33 static 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 
88 void 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 
103 static 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 }
113 void 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 
132 static 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 
153 bool 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 
164 float OSDImageBasedWidget::getCurrentFadeValue() const
165 {
166  if (!isFading()) {
167  return startFadeValue;
168  }
169  return getCurrentFadeValue(Timer::getTime());
170 }
171 
172 float 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 
196 void 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 
211 vec2 OSDImageBasedWidget::getTransformedPos(const OutputSurface& output) const
212 {
213  return getParent()->transformPos(
214  output, float(getScaleFactor(output)) * getPos(), getRelPos());
215 }
216 
217 void 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 
258 void 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
const OSDTopWidget & getTopWidget() const
Definition: OSDGUI.hh:19
void refresh() const
Definition: OSDGUI.cc:25
virtual std::unique_ptr< BaseImage > createSDL(OutputSurface &output)=0
void setProperty(Interpreter &interp, std::string_view name, const TclObject &value) override
std::unique_ptr< BaseImage > image
void createImage(OutputSurface &output)
virtual uint8_t getFadedAlpha() const =0
virtual std::unique_ptr< BaseImage > createGL(OutputSurface &output)=0
float getRecursiveFadeValue() const override
bool isRecursiveFading() const override
void paintGL(OutputSurface &output) override
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:67
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:34
gl::vec2 getPos() const
Definition: OSDWidget.hh:33
bool needSuppressErrors() const
Definition: OSDWidget.cc:305
virtual void setProperty(Interpreter &interp, std::string_view name, const TclObject &value)
Definition: OSDWidget.cc:215
OSDWidget * getParent()
Definition: OSDWidget.hh:37
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
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:296
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
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:227
constexpr auto transform(Range &&range, UnaryOp op)
Definition: view.hh:392
constexpr auto xrange(T e)
Definition: xrange.hh:155