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