openMSX
ImGuiCharacter.cc
Go to the documentation of this file.
1#include "ImGuiCharacter.hh"
2
3#include "ImGuiCpp.hh"
4#include "ImGuiManager.hh"
5#include "ImGuiPalette.hh"
6#include "ImGuiUtils.hh"
7
8#include "DisplayMode.hh"
9#include "MSXMotherBoard.hh"
10#include "VDP.hh"
11#include "VDPVRAM.hh"
12
13#include "one_of.hh"
14#include "ranges.hh"
15#include "view.hh"
16
17#include <imgui.h>
18
19namespace openmsx {
20
21using namespace std::literals;
22
23void ImGuiCharacter::save(ImGuiTextBuffer& buf)
24{
25 savePersistent(buf, *this, persistentElements);
26}
27
28void ImGuiCharacter::loadLine(std::string_view name, zstring_view value)
29{
30 loadOnePersistent(name, value, *this, persistentElements);
31}
32
33void ImGuiCharacter::initHexDigits()
34{
35 smallHexDigits = gl::Texture(false, false); // no interpolation, no wrapping
36
37 // font definition: 16 glyphs 0-9 A-F, each 5 x 8 pixels
38 static constexpr int charWidth = 5;
39 static constexpr int charHeight = 8;
40 static constexpr int totalWidth = 16 * charWidth;
41 static constexpr int totalSize = totalWidth * charHeight;
42 static constexpr std::string_view glyphs =
43 " ... ... ................................................ ........ .........."
44 ".MMM...M. .MMM..MMM..M.M..MMM..MMM..MMM..MMM..MMM..MMM..MM....MM..MM...MMM..MMM."
45 ".M.M..MM. ...M....M..M.M..M....M......M..M.M..M.M..M.M..M.M..M....M.M..M....M..."
46 ".M.M...M. .MM..MMM..M.M..MMM..MMM. .M..MMM..MMM..MMM..MM...M. .M.M..MMM..MMM."
47 ".M.M. .M. .M.. ...M..MMM....M..M.M. .M..M.M....M..M.M..M.M..M. .M.M..M....M..."
48 ".M.M. .M. .M......M....M....M..M.M. .M..M.M....M..M.M..M.M..M....M.M..M....M. "
49 ".MMM. .M. .MMM..MMM. .M..MMM..MMM. .M..MMM..MMM..M.M..MM....MM..MM...MMM..M. "
50 " ... ... .......... ............. ...................... ........ ........ ";
51 static_assert(glyphs.size() == totalSize);
52
53 // transform to 32-bit RGBA
54 std::array<uint32_t, totalSize> pixels;
55 for (auto [c, p] : view::zip_equal(glyphs, pixels)) {
56 p = (c == ' ') ? ImColor(0.0f, 0.0f, 0.0f, 0.0f) // transparent
57 : (c == '.') ? ImColor(0.0f, 0.0f, 0.0f, 0.7f) // black semi-transparent outline
58 : ImColor(1.0f, 1.0f, 1.0f, 0.7f); // white semi-transparent
59 }
60
61 // and upload as a texture
62 smallHexDigits.bind();
63 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, totalWidth, charHeight, 0,
64 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
65}
66
68{
69 if (!show || !motherBoard) return;
70 ImGui::SetNextWindowSize({686, 886}, ImGuiCond_FirstUseEver);
71 im::Window("Tile viewer", &show, [&]{
72 auto* vdp = dynamic_cast<VDP*>(motherBoard->findDevice("VDP")); // TODO name based OK?
73 if (!vdp) return;
74 const auto& vram = vdp->getVRAM().getData();
75
76 int vdpMode = [&] {
77 auto base = vdp->getDisplayMode().getBase();
78 if (base == DisplayMode::TEXT1) return TEXT40;
79 if (base == DisplayMode::TEXT2) return TEXT80;
80 if (base == DisplayMode::GRAPHIC1) return SCR1;
81 if (base == DisplayMode::GRAPHIC2) return SCR2;
82 if (base == DisplayMode::GRAPHIC3) return SCR4;
83 if (base == DisplayMode::MULTICOLOR) return SCR3;
84 return OTHER;
85 }();
86 auto modeToStr = [](int mode) {
87 if (mode == TEXT40) return "screen 0, width 40";
88 if (mode == TEXT80) return "screen 0, width 80";
89 if (mode == SCR1) return "screen 1";
90 if (mode == SCR2) return "screen 2";
91 if (mode == SCR3) return "screen 3";
92 if (mode == SCR4) return "screen 4";
93 if (mode == OTHER) return "non-character";
94 assert(false); return "ERROR";
95 };
96 auto patMult = [](int mode) { return 1 << (mode == one_of(SCR2, SCR4) ? 13 : 11); };
97 auto colMult = [](int mode) { return 1 << (mode == one_of(SCR2, SCR4) ? 13 :
98 mode == TEXT80 ? 9 : 6); };
99 auto namMult = [](int mode) { return 1 << (mode == TEXT80 ? 12 : 10); };
100 int vdpFgCol = vdp->getForegroundColor() & 15;
101 int vdpBgCol = vdp->getBackgroundColor() & 15;
102 int vdpFgBlink = vdp->getBlinkForegroundColor() & 15;
103 int vdpBgBlink = vdp->getBlinkBackgroundColor() & 15;
104 bool vdpBlink = vdp->getBlinkState();
105 int vdpPatBase = vdp->getPatternTableBase() & ~(patMult(vdpMode) - 1);
106 int vdpColBase = vdp->getColorTableBase() & ~(colMult(vdpMode) - 1);
107 int vdpNamBase = vdp->getNameTableBase() & ~(namMult(vdpMode) - 1);
108 int vdpLines = vdp->getNumberOfLines();
109 int vdpColor0 = vdp->getTransparency() ? vdpBgCol
110 : 16; // no replacement
111
112 bool manMode = overrideAll || overrideMode;
113 bool manFgCol = overrideAll || overrideFgCol;
114 bool manBgCol = overrideAll || overrideBgCol;
115 bool manFgBlink = overrideAll || overrideFgBlink;
116 bool manBgBlink = overrideAll || overrideBgBlink;
117 bool manBlink = overrideAll || overrideBlink;
118 bool manPat = overrideAll || overridePat;
119 bool manCol = overrideAll || overrideCol;
120 bool manNam = overrideAll || overrideNam;
121 bool manRows = overrideAll || overrideRows;
122 bool manColor0 = overrideAll || overrideColor0;
123
124 im::TreeNode("Settings", ImGuiTreeNodeFlags_DefaultOpen, [&]{
125 static const char* const color0Str = "0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000none\000";
126 im::Group([&]{
127 ImGui::TextUnformatted("VDP settings");
128 im::Disabled(manMode, [&]{
129 ImGui::AlignTextToFramePadding();
130 ImGui::StrCat("Screen mode: ", modeToStr(vdpMode));
131 });
132 im::Disabled(manFgCol, [&]{
133 ImGui::AlignTextToFramePadding();
134 ImGui::StrCat("Foreground color: ", vdpFgCol);
135 });
136 im::Disabled(manBgCol, [&]{
137 ImGui::AlignTextToFramePadding();
138 ImGui::StrCat("Background color: ", vdpBgCol);
139 });
140 im::Disabled(manFgBlink, [&]{
141 ImGui::AlignTextToFramePadding();
142 ImGui::StrCat("Foreground blink color: ", vdpFgBlink);
143 });
144 im::Disabled(manBgBlink, [&]{
145 ImGui::AlignTextToFramePadding();
146 ImGui::StrCat("Background blink color: ", vdpBgBlink);
147 });
148 im::Disabled(manBlink, [&]{
149 ImGui::AlignTextToFramePadding();
150 ImGui::StrCat("Blink: ", vdpBlink ? "enabled" : "disabled");
151 });
152 im::Disabled(manPat, [&]{
153 ImGui::AlignTextToFramePadding();
154 ImGui::StrCat("Pattern table: 0x", hex_string<5>(vdpPatBase));
155 });
156 im::Disabled(manCol, [&]{
157 ImGui::AlignTextToFramePadding();
158 ImGui::StrCat("Color table: 0x", hex_string<5>(vdpColBase));
159 });
160 im::Disabled(manNam, [&]{
161 ImGui::AlignTextToFramePadding();
162 ImGui::StrCat("Name table: 0x", hex_string<5>(vdpNamBase));
163 });
164 im::Disabled(manRows, [&]{
165 ImGui::AlignTextToFramePadding();
166 ImGui::StrCat("Visible rows: ", (vdpLines == 192) ? "24" : "26.5");
167 });
168 im::Disabled(manColor0, [&]{
169 ImGui::AlignTextToFramePadding();
170 ImGui::StrCat("Replace color 0: ", getComboString(vdpColor0, color0Str));
171 });
172 });
173 ImGui::SameLine();
174 im::Group([&]{
175 ImGui::Checkbox("Manual override", &overrideAll);
176 im::Group([&]{
177 im::Disabled(overrideAll, [&]{
178 ImGui::Checkbox("##o-mode", overrideAll ? &overrideAll : &overrideMode);
179 ImGui::Checkbox("##o-fgCol", overrideAll ? &overrideAll : &overrideFgCol);
180 ImGui::Checkbox("##o-bgCol", overrideAll ? &overrideAll : &overrideBgCol);
181 ImGui::Checkbox("##o-fgBlink", overrideAll ? &overrideAll : &overrideFgBlink);
182 ImGui::Checkbox("##o-bgBlink", overrideAll ? &overrideAll : &overrideBgBlink);
183 ImGui::Checkbox("##o-blink", overrideAll ? &overrideAll : &overrideBlink);
184 ImGui::Checkbox("##o-pat", overrideAll ? &overrideAll : &overridePat);
185 ImGui::Checkbox("##o-col", overrideAll ? &overrideAll : &overrideCol);
186 ImGui::Checkbox("##o-nam", overrideAll ? &overrideAll : &overrideNam);
187 ImGui::Checkbox("##o-rows", overrideAll ? &overrideAll : &overrideRows);
188 ImGui::Checkbox("##o-color0", overrideAll ? &overrideAll : &overrideColor0);
189 });
190 });
191 ImGui::SameLine();
192 im::Group([&]{
193 im::ItemWidth(ImGui::GetFontSize() * 9.0f, [&]{
194 im::Disabled(!manMode, [&]{
195 ImGui::Combo("##mode", &manualMode, "screen 0,40\000screen 0,80\000screen 1\000screen 2\000screen 3\000screen 4\000");
196 });
197 static const char* const range0_15 = "0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000";
198 im::Disabled(manualMode != one_of(TEXT40, TEXT80), [&]{
199 im::Disabled(!manFgCol, [&]{
200 ImGui::Combo("##fgCol", &manualFgCol, range0_15);
201 });
202 im::Disabled(!manBgCol, [&]{
203 ImGui::Combo("##bgCol", &manualBgCol, range0_15);
204 });
205 });
206 im::Disabled(manualMode != TEXT80, [&]{
207 im::Disabled(!manFgBlink, [&]{
208 ImGui::Combo("##fgBlink", &manualFgBlink, range0_15);
209 });
210 im::Disabled(!manBgBlink, [&]{
211 ImGui::Combo("##bgBlink", &manualBgBlink, range0_15);
212 });
213 im::Disabled(!manBlink, [&]{
214 ImGui::Combo("##blink", &manualBlink, "disabled\000enabled\000");
215 });
216 });
217 im::Disabled(!manPat, [&]{
218 comboHexSequence<5>("##pattern", &manualPatBase, patMult(manualMode));
219 });
220 im::Disabled(manualMode == one_of(TEXT40, SCR3), [&]{
221 im::Disabled(!manCol, [&]{
222 comboHexSequence<5>("##color", &manualColBase, colMult(manualMode));
223 });
224 });
225 im::Disabled(!manNam, [&]{
226 comboHexSequence<5>("##name", &manualNamBase, namMult(manualMode));
227 });
228 im::Disabled(!manRows, [&]{
229 ImGui::Combo("##rows", &manualRows, "24\00026.5\00032\000");
230 });
231 im::Disabled(!manColor0, [&]{
232 ImGui::Combo("##Color 0 replacement", &manualColor0, color0Str);
233 });
234 });
235 });
236 });
237
238 ImGui::SameLine();
239 ImGui::Dummy(ImVec2(25, 1));
240 ImGui::SameLine();
241 im::Group([&]{
242 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
243 ImGui::Combo("Palette", &manager.palette->whichPalette, "VDP\000Custom\000Fixed\000");
244 if (ImGui::Button("Open palette editor")) {
245 manager.palette->window.raise();
246 }
247 ImGui::Separator();
248 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.0f);
249 ImGui::Combo("Zoom", &zoom, "1x\0002x\0003x\0004x\0005x\0006x\0007x\0008x\000");
250 ImGui::Checkbox("grid", &grid);
251 ImGui::SameLine();
252 im::Disabled(!grid, [&]{
253 ImGui::ColorEdit4("Grid color", gridColor.data(),
254 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
255 });
256 ImGui::Checkbox("Name table overlay", &nameTableOverlay);
257 });
258 });
259 int manualLines = (manualRows == 0) ? 192
260 : (manualRows == 1) ? 212
261 : 256;
262
263 int mode = manMode ? manualMode : vdpMode;
264 if (mode == SCR4) mode = SCR2;
265
266 int lines = manRows ? manualLines : vdpLines;
267 int color0 = manColor0 ? manualColor0 : vdpColor0;
268
269 VramTable patTable(vram);
270 unsigned patReg = (manPat ? (manualPatBase | (patMult(manualMode) - 1)) : vdp->getPatternTableBase()) >> 11;
271 patTable.setRegister(patReg, 11);
272
273 VramTable colTable(vram);
274 unsigned colReg = (manCol ? (manualColBase | (colMult(manualMode) - 1)) : vdp->getColorTableBase()) >> 6;
275 colTable.setRegister(colReg, 6);
276
277 VramTable namTable(vram);
278 unsigned namReg = (manNam ? (manualNamBase | (namMult(manualMode) - 1)) : vdp->getNameTableBase()) >> 10;
279 namTable.setRegister(namReg, 10);
280 namTable.setIndexSize((mode == TEXT80) ? 12 : 10);
281
282 std::array<uint32_t, 16> palette;
283 auto msxPalette = manager.palette->getPalette(vdp);
284 ranges::transform(msxPalette, palette.data(),
285 [](uint16_t msx) { return ImGuiPalette::toRGBA(msx); });
286 if (color0 < 16) palette[0] = palette[color0];
287
288 auto fgCol = manFgCol ? manualFgCol : vdpFgCol;
289 auto bgCol = manBgCol ? manualBgCol : vdpBgCol;
290 auto fgBlink = manFgBlink ? manualFgBlink : vdpFgBlink;
291 auto bgBlink = manBgBlink ? manualBgBlink : vdpBgBlink;
292 auto blink = manBlink ? bool(manualBlink) : vdpBlink;
293
294 bool narrow = mode == TEXT80;
295 int zx = (1 + zoom) * (narrow ? 1 : 2);
296 int zy = (1 + zoom) * 2;
297 gl::vec2 zm{float(zx), float(zy)};
298
299 // Render the patterns to a texture, we need this both to display the name-table and the pattern-table
300 auto patternTexSize = [&]() -> gl::ivec2 {
301 if (mode == TEXT40) return {192, 64}; // 8 rows of 32 characters, each 6x8 pixels
302 if (mode == TEXT80) return {192, 128}; // 8 rows of 32 characters, each 6x8 pixels, x2 for blink
303 if (mode == SCR1) return {256, 64}; // 8 rows of 32 characters, each 8x8 pixels
304 if (mode == SCR2) return {256, lines == 192 ? 192 : 256}; // 8 rows of 32 characters, each 8x8 pixels, all this 3 or 4 times
305 if (mode == SCR3) return { 64, 64}; // 8 rows of 32 characters, each 2x2 pixels, all this 4 times
306 return {1, 1}; // OTHER -> dummy
307 }();
308 auto patternTexChars = [&]() -> gl::vec2 {
309 if (mode == SCR3) return {32.0f, 32.0f};
310 if (mode == SCR2) return {32.0f, lines == 192 ? 24.0f : 32.0f};
311 if (mode == TEXT80) return {32.0f, 16.0f};
312 return {32.0f, 8.0f};
313 }();
314 auto recipPatTexChars = recip(patternTexChars);
315 auto patternDisplaySize = [&]() -> gl::ivec2 {
316 if (mode == TEXT40) return {192, 64};
317 if (mode == TEXT80) return {192, 128};
318 if (mode == SCR2) return {256, lines == 192 ? 192 : 256};
319 if (mode == SCR3) return {256, 256};
320 return {256, 64}; // SCR1, OTHER
321 }();
322 std::array<uint32_t, 256 * 256> pixels; // max size for SCR2
323 renderPatterns(mode, palette, fgCol, bgCol, fgBlink, bgBlink, patTable, colTable, lines, pixels);
324 if (!patternTex.get()) {
325 patternTex = gl::Texture(false, false); // no interpolation, no wrapping
326 }
327 patternTex.bind();
328 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, patternTexSize.x, patternTexSize.y, 0,
329 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
330
331 // create grid texture
332 auto charWidth = mode == one_of(TEXT40, TEXT80) ? 6 : 8;
333 auto charSize = gl::vec2{float(charWidth), 8.0f};
334 auto charZoom = zm * charSize;
335 auto zoomCharSize = gl::vec2(narrow ? 6.0f : 12.0f, 12.0f) * charSize; // hovered size
336 if (grid) {
337 auto gColor = ImGui::ColorConvertFloat4ToU32(gridColor);
338 auto gridWidth = charWidth * zx;
339 auto gridHeight = 8 * zy;
340 for (auto y : xrange(gridHeight)) {
341 auto* line = &pixels[y * gridWidth];
342 for (auto x : xrange(gridWidth)) {
343 line[x] = (x == 0 || y == 0) ? gColor : 0;
344 }
345 }
346 if (!gridTex.get()) {
347 gridTex = gl::Texture(false, true); // no interpolation, with wrapping
348 }
349 gridTex.bind();
350 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gridWidth, gridHeight, 0,
351 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
352 }
353 if (nameTableOverlay && !smallHexDigits.get()) {
354 initHexDigits();
355 }
356
357 ImGui::Separator();
358 im::TreeNode("Pattern Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
359 auto size = gl::vec2(patternDisplaySize) * zm;
360 im::Child("##pattern", {0, size.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
361 auto pos1 = ImGui::GetCursorPos();
362 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
363 ImGui::Image(patternTex.getImGui(), size);
364 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
365 ImGui::SameLine();
366 im::Group([&]{
367 if (hovered) {
368 auto gridPos = trunc((gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
369 ImGui::StrCat("Pattern: ", gridPos.x + 32 * gridPos.y);
370 auto uv1 = gl::vec2(gridPos) * recipPatTexChars;
371 auto uv2 = uv1 + recipPatTexChars;
372 auto pos2 = ImGui::GetCursorPos();
373 ImGui::Image(patternTex.getImGui(), zoomCharSize, uv1, uv2);
374 if (grid) {
375 ImGui::SetCursorPos(pos2);
376 ImGui::Image(gridTex.getImGui(),
377 zoomCharSize, {}, charSize);
378 }
379 } else {
380 ImGui::Dummy(zoomCharSize);
381 }
382 });
383 if (grid) {
384 ImGui::SetCursorPos(pos1);
385 ImGui::Image(gridTex.getImGui(), size,
386 {}, patternTexChars);
387 }
388 });
389 });
390 ImGui::Separator();
391 im::TreeNode("Name Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
392 float rows = float(lines) * (1.0f / 8.0f); // 24, 26.5 or 32
393 auto columns = [&] {
394 if (mode == TEXT40) return 40;
395 if (mode == TEXT80) return 80;
396 return 32;
397 }();
398 auto charsSize = gl::vec2(float(columns), rows); // (x * y) number of characters
399 auto msxSize = charSize * charsSize; // (x * y) number of MSX pixels
400 auto hostSize = msxSize * zm; // (x * y) number of host pixels
401
402 im::Child("##name", {0.0f, hostSize.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
403 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
404 auto* drawList = ImGui::GetWindowDrawList();
405
406 auto getPattern = [&](unsigned column, unsigned row) {
407 auto block = [&]() -> unsigned {
408 if (mode == TEXT80 && blink) {
409 auto colPat = colTable[10 * row + (column / 8)];
410 return (colPat & (0x80 >> (column % 8))) ? 1 : 0;
411 }
412 if (mode == SCR2) return row / 8;
413 if (mode == SCR3) return row % 4;
414 return 0;
415 }();
416 return namTable[columns * row + column] + 256 * block;
417 };
418 auto getPatternUV = [&](unsigned pattern) -> std::pair<gl::vec2, gl::vec2> {
419 auto patRow = pattern / 32;
420 auto patCol = pattern % 32;
421 gl::vec2 uv1 = gl::vec2{float(patCol), float(patRow)} * recipPatTexChars;
422 gl::vec2 uv2 = uv1 + recipPatTexChars;
423 return {uv1, uv2};
424 };
425
426 if (mode == OTHER) {
427 drawList->AddRectFilled(scrnPos, scrnPos + hostSize, getColor(imColor::GRAY));
428 } else {
429 auto rowsCeil = int(ceilf(rows));
430 auto numChars = rowsCeil * columns;
431 drawList->PushClipRect(scrnPos, scrnPos + hostSize, true);
432 drawList->PushTextureID(patternTex.getImGui());
433 drawList->PrimReserve(6 * numChars, 4 * numChars);
434 for (auto row : xrange(rowsCeil)) {
435 for (auto column : xrange(columns)) {
436 gl::vec2 p1 = scrnPos + charZoom * gl::vec2{float(column), float(row)};
437 gl::vec2 p2 = p1 + charZoom;
438 auto pattern = getPattern(column, row);
439 auto [uv1, uv2] = getPatternUV(pattern);
440 drawList->PrimRectUV(p1, p2, uv1, uv2, 0xffffffff);
441 }
442 }
443 drawList->PopTextureID();
444 if (nameTableOverlay) {
445 drawList->PushTextureID(smallHexDigits.getImGui());
446 drawList->PrimReserve(12 * numChars, 8 * numChars);
447 static constexpr gl::vec2 digitSize{5.0f, 8.0f};
448 static constexpr float texDigitWidth = 1.0f / 16.0f;
449 static constexpr gl::vec2 texDigitSize{texDigitWidth, 1.0f};
450 auto digitOffset = narrow ? gl::vec2{0.0f, digitSize.y} : gl::vec2{digitSize.x, 0.0f};
451 for (auto row : xrange(rowsCeil)) {
452 for (auto column : xrange(columns)) {
453 gl::vec2 p1 = scrnPos + charZoom * gl::vec2{float(column), float(row)};
454 gl::vec2 p2 = p1 + digitOffset;
455 auto pattern = getPattern(column, row);
456 gl::vec2 uv1{narrow_cast<float>((pattern >> 4) & 15) * texDigitWidth, 0.0f};
457 gl::vec2 uv2{narrow_cast<float>((pattern >> 0) & 15) * texDigitWidth, 0.0f};
458 drawList->PrimRectUV(p1, p1 + digitSize, uv1, uv1 + texDigitSize, 0xffffffff);
459 drawList->PrimRectUV(p2, p2 + digitSize, uv2, uv2 + texDigitSize, 0xffffffff);
460 }
461 }
462 drawList->PopTextureID();
463 }
464 drawList->PopClipRect();
465 }
466
467 auto pos1 = ImGui::GetCursorPos();
468 ImGui::Dummy(hostSize);
469 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
470 ImGui::SameLine();
471 im::Group([&]{
472 if (hovered) {
473 auto gridPos = trunc((gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
474 ImGui::StrCat("Column: ", gridPos.x, " Row: ", gridPos.y);
475 auto pattern = getPattern(gridPos.x, gridPos.y);
476 ImGui::StrCat("Pattern: ", pattern);
477 auto [uv1, uv2] = getPatternUV(pattern);
478 auto pos2 = ImGui::GetCursorPos();
479 ImGui::Image(patternTex.getImGui(), zoomCharSize, uv1, uv2);
480 if (grid) {
481 ImGui::SetCursorPos(pos2);
482 ImGui::Image(gridTex.getImGui(),
483 zoomCharSize, {}, charSize);
484 }
485 } else {
486 gl::vec2 textSize = ImGui::CalcTextSize("Column: 31 Row: 23"sv);
487 ImGui::Dummy(max(zoomCharSize, textSize));
488 }
489 });
490 if (grid) {
491 ImGui::SetCursorPos(pos1);
492 ImGui::Image(gridTex.getImGui(), hostSize, {}, charsSize);
493 }
494 });
495 });
496 });
497}
498
499static void draw6(uint8_t pattern, uint32_t fgCol, uint32_t bgCol, std::span<uint32_t, 6> out)
500{
501 out[0] = (pattern & 0x80) ? fgCol : bgCol;
502 out[1] = (pattern & 0x40) ? fgCol : bgCol;
503 out[2] = (pattern & 0x20) ? fgCol : bgCol;
504 out[3] = (pattern & 0x10) ? fgCol : bgCol;
505 out[4] = (pattern & 0x08) ? fgCol : bgCol;
506 out[5] = (pattern & 0x04) ? fgCol : bgCol;
507}
508
509static void draw8(uint8_t pattern, uint32_t fgCol, uint32_t bgCol, std::span<uint32_t, 8> out)
510{
511 out[0] = (pattern & 0x80) ? fgCol : bgCol;
512 out[1] = (pattern & 0x40) ? fgCol : bgCol;
513 out[2] = (pattern & 0x20) ? fgCol : bgCol;
514 out[3] = (pattern & 0x10) ? fgCol : bgCol;
515 out[4] = (pattern & 0x08) ? fgCol : bgCol;
516 out[5] = (pattern & 0x04) ? fgCol : bgCol;
517 out[6] = (pattern & 0x02) ? fgCol : bgCol;
518 out[7] = (pattern & 0x01) ? fgCol : bgCol;
519}
520
521void ImGuiCharacter::renderPatterns(int mode, std::span<const uint32_t, 16> palette,
522 int fgCol, int bgCol, int fgBlink, int bgBlink,
523 VramTable& pat, VramTable& col, int lines, std::span<uint32_t> output)
524{
525 switch (mode) {
526 case TEXT40:
527 case TEXT80: {
528 pat.setIndexSize(11);
529 col.setIndexSize(9); // only matters for TEXT80
530 auto fg = palette[fgCol];
531 auto bg = palette[bgCol];
532 auto fgB = palette[fgBlink];
533 auto bgB = palette[bgBlink];
534 for (auto row : xrange(8)) {
535 for (auto column : xrange(32)) {
536 auto patNum = 32 * row + column;
537 auto offset = 8 * patNum;
538 for (auto y : xrange(8)) {
539 auto pattern = pat[offset + y];
540 auto out = subspan<6>(output, (8 * row + y) * 192 + 6 * column);
541 draw6(pattern, fg, bg, out);
542 if (mode == TEXT80) {
543 auto out2 = subspan<6>(output, (64 + 8 * row + y) * 192 + 6 * column);
544 draw6(pattern, fgB, bgB, out2);
545 }
546 }
547 }
548 }
549 break;
550 }
551 case SCR1:
552 pat.setIndexSize(11);
553 col.setIndexSize(6);
554 for (auto row : xrange(8)) {
555 for (auto group : xrange(4)) { // 32 columns, split in 4 groups of 8
556 auto color = col[4 * row + group];
557 auto fg = palette[color >> 4];
558 auto bg = palette[color & 15];
559 for (auto subColumn : xrange(8)) {
560 auto column = 8 * group + subColumn;
561 auto patNum = 32 * row + column;
562 auto offset = 8 * patNum;
563 for (auto y : xrange(8)) {
564 auto pattern = pat[offset + y];
565 auto out = subspan<8>(output, (8 * row + y) * 256 + 8 * column);
566 draw8(pattern, fg, bg, out);
567 }
568 }
569 }
570 }
571 break;
572 case SCR2:
573 pat.setIndexSize(13);
574 col.setIndexSize(13);
575 for (auto row : xrange((lines == 192 ? 3 : 4) * 8)) {
576 for (auto column : xrange(32)) {
577 auto patNum = 32 * row + column;
578 auto offset = 8 * patNum;
579 for (auto y : xrange(8)) {
580 auto pattern = pat[offset + y];
581 auto color = col[offset + y];
582 auto fg = palette[color >> 4];
583 auto bg = palette[color & 15];
584 auto out = subspan<8>(output, (8 * row + y) * 256 + 8 * column);
585 draw8(pattern, fg, bg, out);
586 }
587 }
588 }
589 break;
590 case SCR3:
591 pat.setIndexSize(11);
592 col.setIndexSize(13); // not used?
593 for (auto group : xrange(4)) {
594 for (auto row : xrange(8)) {
595 for (auto column : xrange(32)) {
596 auto patNum = 32 * row + column;
597 auto offset = 8 * patNum + 2 * group;
598 for (auto y : xrange(2)) {
599 auto out = subspan<2>(output, (16 * group + 2 * row + y) * 64 + 2 * column);
600 auto pattern = pat[offset + y];
601 out[0] = palette[pattern >> 4];
602 out[1] = palette[pattern & 15];
603 }
604 }
605 }
606 }
607 break;
608 default:
609 output[0] = getColor(imColor::GRAY);
610 break;
611 }
612}
613
614} // namespace openmsx
Most basic/generic texture: only contains a texture ID.
Definition GLUtil.hh:39
void bind() const
Makes this texture the active GL texture.
Definition GLUtil.hh:87
GLuint get() const
Returns the underlying openGL handler id.
Definition GLUtil.hh:73
void * getImGui() const
Return as a 'void*' (needed for 'Dear ImGui').
Definition GLUtil.hh:77
static constexpr uint8_t GRAPHIC3
static constexpr uint8_t MULTICOLOR
static constexpr uint8_t GRAPHIC1
static constexpr uint8_t TEXT2
static constexpr uint8_t GRAPHIC2
static constexpr uint8_t TEXT1
void paint(MSXMotherBoard *motherBoard) override
void save(ImGuiTextBuffer &buf) override
void loadLine(std::string_view name, zstring_view value) override
std::unique_ptr< ImGuiPalette > palette
ImGuiManager & manager
Definition ImGuiPart.hh:30
MSXDevice * findDevice(std::string_view name)
Find a MSXDevice by name.
std::span< const uint8_t > getData() const
Only used by debugger.
Definition VDPVRAM.hh:564
Unified implementation of MSX Video Display Processors (VDPs).
Definition VDP.hh:66
VDPVRAM & getVRAM()
Get the VRAM object for this VDP.
Definition VDP.hh:161
void setRegister(unsigned value, unsigned extraLsbBits)
void setIndexSize(unsigned bits)
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
void StrCat(Ts &&...ts)
Definition ImGuiUtils.hh:43
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:37
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:24
vecN< 2, float > vec2
Definition gl_vec.hh:178
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:63
bool TreeNode(const char *label, ImGuiTreeNodeFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:306
void ItemWidth(float item_width, std::invocable<> auto next)
Definition ImGuiCpp.hh:204
void Child(const char *str_id, const ImVec2 &size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:110
void Disabled(bool b, std::invocable<> auto next)
Definition ImGuiCpp.hh:510
void Group(std::invocable<> auto next)
Definition ImGuiCpp.hh:236
This file implemented 3 utility functions:
Definition Autofire.cc:11
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
ImU32 getColor(imColor col)
const char * getComboString(int item, const char *itemsSeparatedByZeros)
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition ranges.hh:279
Definition view.hh:15
constexpr To narrow(From from) noexcept
Definition narrow.hh:37
constexpr auto xrange(T e)
Definition xrange.hh:132