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