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<string_view> OSDImageBasedWidget::getProperties() const
36 {
37  auto result = OSDWidget::getProperties();
38  static 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, 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 }
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 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 
161 bool OSDImageBasedWidget::isFading() const
162 {
163  return (startFadeValue != fadeTarget) && (fadePeriod != 0.0f);
164 }
165 
166 float OSDImageBasedWidget::getCurrentFadeValue() const
167 {
168  if (!isFading()) {
169  return startFadeValue;
170  }
171  return getCurrentFadeValue(Timer::getTime());
172 }
173 
174 float OSDImageBasedWidget::getCurrentFadeValue(uint64_t now) const
175 {
176  assert(now >= startFadeTime);
177 
178  int diff = int(now - startFadeTime); // int should be big enough
179  assert(fadePeriod != 0.0f);
180  float delta = diff / (1000000.0f * fadePeriod);
181  if (startFadeValue < fadeTarget) {
182  float tmp = startFadeValue + delta;
183  if (tmp >= fadeTarget) {
184  startFadeValue = fadeTarget;
185  return startFadeValue;
186  }
187  return tmp;
188  } else {
189  float tmp = startFadeValue - delta;
190  if (tmp <= fadeTarget) {
191  startFadeValue = fadeTarget;
192  return startFadeValue;
193  }
194  return tmp;
195  }
196 }
197 
198 void OSDImageBasedWidget::updateCurrentFadeValue()
199 {
200  auto now = Timer::getTime();
201  if (isFading()) {
202  startFadeValue = getCurrentFadeValue(now);
203  }
204  startFadeTime = now;
205 }
206 
208 {
209  error = false;
210  image.reset();
211 }
212 
213 vec2 OSDImageBasedWidget::getTransformedPos(const OutputSurface& output) const
214 {
215  return getParent()->transformPos(
216  output, float(getScaleFactor(output)) * getPos(), getRelPos());
217 }
218 
219 void OSDImageBasedWidget::setError(string message)
220 {
221  error = true;
222 
223  // The suppressErrors property only exists to break an infinite loop
224  // when an error occurs (e.g. couldn't load font) while displaying the
225  // error message on the OSD system.
226  // The difficulty in detecting this loop is that it's not a recursive
227  // loop, but each iteration takes one frame: on the CliComm Tcl callback,
228  // the OSD widgets get created, but only the next frame, when this new
229  // widget is actually drawn the next error occurs.
230  if (!needSuppressErrors()) {
231  getDisplay().getOSDGUI().getTopWidget().queueError(std::move(message));
232  }
233 }
234 
236 {
237  paint(output, false);
238 }
239 
241 {
242  paint(output, true);
243 }
244 
246 {
247  if (!image && !hasError()) {
248  try {
249  if (getDisplay().getOSDGUI().isOpenGL()) {
250  image = createGL(output);
251  } else {
252  image = createSDL(output);
253  }
254  } catch (MSXException& e) {
255  setError(std::move(e).getMessage());
256  }
257  }
258 }
259 
260 void OSDImageBasedWidget::paint(OutputSurface& output, bool openGL)
261 {
262  // Note: Even when alpha == 0 we still create the image:
263  // It may be needed to get the dimensions to be able to position
264  // child widgets.
265  assert(openGL == getDisplay().getOSDGUI().isOpenGL()); (void)openGL;
266  createImage(output);
267 
268  auto fadedAlpha = getFadedAlpha();
269  if ((fadedAlpha != 0) && image) {
270  ivec2 drawPos = round(getTransformedPos(output));
271  image->draw(output, drawPos, fadedAlpha);
272  }
273  if (isFading()) {
275  }
276 }
277 
278 } // namespace openmsx
auto transform(Range &&range, UnaryOp op)
Definition: view.hh:312
void setError(std::string message)
Represents the output window/screen of openMSX.
Definition: Display.hh:31
gl::vec2 getPos() const
Definition: OSDWidget.hh:23
bool needSuppressErrors() const
Definition: OSDWidget.cc:319
auto xrange(T e)
Definition: xrange.hh:170
void paintGL(OutputSurface &output) override
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
void setProperty(Interpreter &interp, string_view name, const TclObject &value) override
gl::vec2 getRelPos() const
Definition: OSDWidget.hh:24
void queueError(std::string message)
Definition: OSDTopWidget.cc:40
std::unique_ptr< BaseImage > image
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:116
A frame buffer where pixels can be written to.
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
virtual void getProperty(string_view name, TclObject &result) const
Definition: OSDWidget.cc:273
OSDGUI & getOSDGUI()
Definition: Display.hh:45
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:191
void paintSDL(OutputSurface &output) override
virtual std::unique_ptr< BaseImage > createSDL(OutputSurface &output)=0
void append(Result &)
Definition: stl.hh:354
int getScaleFactor(const OutputSurface &output) const
Definition: OSDWidget.cc:363
gl::vec2 transformPos(const OutputSurface &output, gl::vec2 pos, gl::vec2 relPos) const
Definition: OSDWidget.cc:374
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:134
float getRecursiveFadeValue() const override
void getProperty(string_view name, TclObject &result) const override
virtual std::unique_ptr< BaseImage > createGL(OutputSurface &output)=0
virtual void setProperty(Interpreter &interp, string_view name, const TclObject &value)
Definition: OSDWidget.cc:229
void createImage(OutputSurface &output)
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:92
OSDWidget * getParent()
Definition: OSDWidget.hh:27
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
const OSDTopWidget & getTopWidget() const
Definition: OSDGUI.hh:19
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
virtual std::vector< string_view > getProperties() const
Definition: OSDWidget.cc:220
Display & getDisplay() const
Definition: OSDWidget.hh:51
uint64_t getTime()
Get current (real) time in us.
Definition: Timer.cc:8
std::vector< string_view > getProperties() const override
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:122
virtual float getRecursiveFadeValue() const
Definition: OSDWidget.cc:301
virtual uint8_t getFadedAlpha() const =0
Definition: gl_mat.hh:24
void refresh() const
Definition: OSDGUI.cc:28