openMSX
ImGuiPalette.cc
Go to the documentation of this file.
1#include "ImGuiPalette.hh"
2
3#include "ImGuiCpp.hh"
4#include "ImGuiUtils.hh"
5
6#include "VDP.hh"
7
8#include "StringOp.hh"
9#include "xrange.hh"
10
11#include <imgui.h>
12
13#include <cassert>
14
15using namespace std::literals;
16
17
18namespace openmsx {
19
20static constexpr std::array<uint16_t, 16> fixedPalette = { // GRB
21 0x000, 0x000, 0x611, 0x733,
22 0x117, 0x327, 0x151, 0x627,
23 0x171, 0x373, 0x661, 0x664,
24 0x411, 0x265, 0x555, 0x777,
25};
26
28 : ImGuiPart(manager_)
29{
30 customPalette = fixedPalette;
31}
32
33void ImGuiPalette::save(ImGuiTextBuffer& buf)
34{
35 savePersistent(buf, *this, persistentElements);
36
37 buf.append("customPalette=");
38 for (auto i : xrange(16)) {
39 if (i) buf.append(",");
40 buf.appendf("%d", customPalette[i]);
41 }
42 buf.append("\n");
43}
44
45void ImGuiPalette::loadLine(std::string_view name, zstring_view value)
46{
47 if (loadOnePersistent(name, value, *this, persistentElements)) {
48 // already handled
49 } else if (name == "customPalette") {
50 int i = 0;
51 for (auto s : StringOp::split_view(value, ',')) {
52 if (auto v = StringOp::stringTo<uint16_t>(s)) {
53 customPalette[i] = *v;
54 }
55 if (++i == 16) break;
56 }
57 }
58}
59
60static std::array<int, 3> extractRGB(uint16_t grb)
61{
62 auto r = (grb >> 4) & 7;
63 auto g = (grb >> 8) & 7;
64 auto b = (grb >> 0) & 7;
65 return {r, g, b};
66}
67
68static void insertRGB(std::span<uint16_t, 16> msxPalette, unsigned index, std::array<int, 3> rgb)
69{
70 assert(index < 16);
71 msxPalette[index] = narrow<int16_t>(((rgb[1] & 7) << 8) | ((rgb[0] & 7) << 4) | ((rgb[2] & 7) << 0));
72}
73
74[[nodiscard]] static uint32_t toRGBA(uint16_t msxColor)
75{
76 auto [r, g, b] = extractRGB(msxColor);
77 static constexpr float scale = 1.0f / 7.0f;
78 return ImColor(float(r) * scale, float(g) * scale, float(b) * scale);
79}
80
81static bool coloredButton(const char* id, ImU32 color, ImVec2 size)
82{
83 bool result = false;
84 auto col = ImGui::ColorConvertU32ToFloat4(color);
85 im::StyleColor(ImGuiCol_Button, col,
86 ImGuiCol_ButtonHovered, col,
87 ImGuiCol_ButtonActive, col, [&]{
88 result = ImGui::Button(id, size);
89 });
90 return result;
91}
92
93std::array<uint32_t, 16> ImGuiPalette::getPalette(const VDP* vdp) const
94{
95 std::array<uint32_t, 16> result;
96 if (vdp && vdp->isMSX1VDP() && (whichPalette != PALETTE_CUSTOM)) {
97 auto rgb = vdp->getMSX1Palette();
98 for (auto i : xrange(16)) {
99 auto [r, g, b] = rgb[i];
100 result[i] = ImColor(r, g, b);
101 }
102 } else {
103 auto rgb = [&]() -> std::span<const uint16_t, 16> {
104 if (whichPalette == PALETTE_CUSTOM) {
105 return customPalette;
106 } else if (whichPalette == PALETTE_VDP && vdp && !vdp->isMSX1VDP()) {
107 return vdp->getPalette();
108 } else {
109 return fixedPalette;
110 }
111 }();
112 for (auto i : xrange(16)) {
113 result[i] = toRGBA(rgb[i]);
114 }
115 }
116 return result;
117}
118
120{
121 if (!window.open) return;
122 im::Window("Palette editor", window, [&]{
123 VDP* vdp = motherBoard ? dynamic_cast<VDP*>(motherBoard->findDevice("VDP")) : nullptr; // TODO name based OK?
124
125 ImGui::RadioButton("VDP palette", &whichPalette, PALETTE_VDP);
126 ImGui::SameLine();
127 ImGui::RadioButton("Custom palette", &whichPalette, PALETTE_CUSTOM);
128 ImGui::SameLine();
129 ImGui::RadioButton("Fixed palette", &whichPalette, PALETTE_FIXED);
130
131 std::array<uint16_t, 16> paletteCopy;
132 std::span<uint16_t, 16> palette = customPalette;
133 bool disabled = (whichPalette == PALETTE_FIXED) ||
134 ((whichPalette == PALETTE_VDP) && (!vdp || vdp->isMSX1VDP()));
135 if (disabled) {
136 palette = std::span<uint16_t, 16>{const_cast<uint16_t*>(fixedPalette.data()), 16};
137 } else if (whichPalette == PALETTE_VDP) {
138 assert(vdp);
139 ranges::copy(vdp->getPalette(), paletteCopy);
140 palette = paletteCopy;
141 }
142
143 im::Table("left/right", 2, [&]{
144 ImGui::TableSetupColumn("left", ImGuiTableColumnFlags_WidthFixed, 200.0f);
145 ImGui::TableSetupColumn("right", ImGuiTableColumnFlags_WidthFixed, 150.0f);
146
147 if (ImGui::TableNextColumn()) { // left pane
148 std::array<uint32_t, 16> paletteRGB = getPalette(vdp);
149 im::Table("grid", 4, [&]{
150 im::ID_for_range(16, [&](int i) {
151 if (ImGui::TableNextColumn()) {
152 if (i == selectedColor) {
153 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImGuiCol_HeaderActive));
154 }
155 if (coloredButton("", paletteRGB[i], {44.0f, 30.0f})) {
156 selectedColor = i;
157 }
158 }
159 });
160 });
161 }
162 if (ImGui::TableNextColumn()) { // right pane
163 ImGui::StrCat("Color ", selectedColor);
165 ImGui::SameLine();
166 coloredButton("##color", toRGBA(palette[selectedColor]), {150.0f, 30.0f});
167 ImGui::Spacing();
168 ImGui::Spacing();
169
170 im::Disabled(disabled, [&]{
171 static constexpr std::array names = {"R"sv, "G"sv, "B"sv};
172 auto rgb = extractRGB(palette[selectedColor]);
173 im::ID_for_range(3, [&](int i) { // rgb sliders
174 ImGui::AlignTextToFramePadding();
175 ImGui::TextUnformatted(names[i]);
176 ImGui::SameLine();
178 ImGuiCol_FrameBg, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.5f, 0.5f)),
179 ImGuiCol_FrameBgHovered, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.6f, 0.5f)),
180 ImGuiCol_FrameBgActive, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.7f, 0.5f)),
181 ImGuiCol_SliderGrab, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.9f, 0.9f)), [&]{
182 ImGui::SetNextItemWidth(-FLT_MIN);
183 if (ImGui::SliderInt("##v", &rgb[i], 0, 7, "%d", ImGuiSliderFlags_AlwaysClamp)) {
184 assert(!disabled);
185 insertRGB(palette, selectedColor, rgb);
186 if (whichPalette == PALETTE_VDP) {
187 auto time = motherBoard->getCurrentTime();
188 vdp->setPalette(selectedColor, palette[selectedColor], time);
189 }
190 }
191 });
192 });
193 });
194 }
195 });
196 });
197}
198
199} // namespace openmsx
int g
im::WindowStatus window
void save(ImGuiTextBuffer &buf) override
std::array< uint32_t, 16 > getPalette(const VDP *vdp) const
void loadLine(std::string_view name, zstring_view value) override
void paint(MSXMotherBoard *motherBoard) override
ImGuiPalette(ImGuiManager &manager_)
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
MSXDevice * findDevice(std::string_view name)
Find a MSXDevice by name.
Unified implementation of MSX Video Display Processors (VDPs).
Definition VDP.hh:67
std::array< std::array< uint8_t, 3 >, 16 > getMSX1Palette() const
Get the (fixed) palette for this MSX1 VDP.
Definition VDP.cc:1550
void setPalette(unsigned index, word grb, EmuTime::param time)
Sets a palette entry.
Definition VDP.cc:762
uint16_t getPalette(unsigned index) const
Gets a palette entry.
Definition VDP.hh:279
bool isMSX1VDP() const
Is this an MSX1 VDP?
Definition VDP.hh:108
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
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:26
auto split_view(std::string_view str, Separators separators)
Definition StringOp.hh:83
constexpr mat4 scale(const vec3 &xyz)
void Table(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, std::invocable<> auto next)
Definition ImGuiCpp.hh:455
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:63
void StyleColor(bool active, Args &&...args)
Definition ImGuiCpp.hh:175
void Disabled(bool b, std::invocable<> auto next)
Definition ImGuiCpp.hh:506
void ID_for_range(std::integral auto count, std::invocable< int > auto next)
Definition ImGuiCpp.hh:281
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)
constexpr auto copy(InputRange &&range, OutputIter out)
Definition ranges.hh:252
constexpr auto xrange(T e)
Definition xrange.hh:132