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
74uint32_t ImGuiPalette::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::span<const uint16_t, 16> ImGuiPalette::getPalette(const VDP* vdp) const
94{
95 if (whichPalette == PALETTE_CUSTOM) {
96 return customPalette;
97 } else if (whichPalette == PALETTE_VDP && vdp && !vdp->isMSX1VDP()) {
98 return vdp->getPalette();
99 } else {
100 return fixedPalette;
101 }
102}
103
105{
106 if (!window.open) return;
107 im::Window("Palette editor", window, [&]{
108 VDP* vdp = motherBoard ? dynamic_cast<VDP*>(motherBoard->findDevice("VDP")) : nullptr; // TODO name based OK?
109
110 im::Disabled(!vdp || vdp->isMSX1VDP(), [&]{
111 ImGui::RadioButton("VDP palette", &whichPalette, PALETTE_VDP);
112 });
113 ImGui::SameLine();
114 ImGui::RadioButton("Custom palette", &whichPalette, PALETTE_CUSTOM);
115 ImGui::SameLine();
116 ImGui::RadioButton("Fixed palette", &whichPalette, PALETTE_FIXED);
117
118 std::array<uint16_t, 16> paletteCopy;
119 std::span<uint16_t, 16> palette = customPalette;
120 bool disabled = (whichPalette == PALETTE_FIXED) ||
121 ((whichPalette == PALETTE_VDP) && (!vdp || vdp->isMSX1VDP()));
122 if (disabled) {
123 palette = std::span<uint16_t, 16>{const_cast<uint16_t*>(fixedPalette.data()), 16};
124 } else if (whichPalette == PALETTE_VDP) {
125 ranges::copy(vdp->getPalette(), paletteCopy);
126 palette = paletteCopy;
127 }
128
129 im::Table("left/right", 2, [&]{
130 ImGui::TableSetupColumn("left", ImGuiTableColumnFlags_WidthFixed, 200.0f);
131 ImGui::TableSetupColumn("right", ImGuiTableColumnFlags_WidthFixed, 150.0f);
132
133 if (ImGui::TableNextColumn()) { // left pane
134 im::Table("grid", 4, [&]{
135 im::ID_for_range(16, [&](int i) {
136 if (ImGui::TableNextColumn()) {
137 if (i == selectedColor) {
138 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImGuiCol_HeaderActive));
139 }
140 auto color = toRGBA(palette[i]);
141 if (coloredButton("", color, {44.0f, 30.0f})) {
142 selectedColor = i;
143 }
144 }
145 });
146 });
147 }
148 if (ImGui::TableNextColumn()) { // right pane
149 ImGui::StrCat("Color ", selectedColor);
151 ImGui::SameLine();
152 coloredButton("##color", toRGBA(palette[selectedColor]), {150.0f, 30.0f});
153 ImGui::Spacing();
154 ImGui::Spacing();
155
156 im::Disabled(disabled, [&]{
157 static constexpr std::array names = {"R"sv, "G"sv, "B"sv};
158 auto rgb = extractRGB(palette[selectedColor]);
159 im::ID_for_range(3, [&](int i) { // rgb sliders
160 ImGui::AlignTextToFramePadding();
161 ImGui::TextUnformatted(names[i]);
162 ImGui::SameLine();
164 ImGuiCol_FrameBg, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.5f, 0.5f)),
165 ImGuiCol_FrameBgHovered, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.6f, 0.5f)),
166 ImGuiCol_FrameBgActive, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.7f, 0.5f)),
167 ImGuiCol_SliderGrab, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.9f, 0.9f)), [&]{
168 ImGui::SetNextItemWidth(-FLT_MIN);
169 if (ImGui::SliderInt("##v", &rgb[i], 0, 7, "%d", ImGuiSliderFlags_AlwaysClamp)) {
170 assert(!disabled);
171 insertRGB(palette, selectedColor, rgb);
172 if (whichPalette == PALETTE_VDP) {
173 auto time = motherBoard->getCurrentTime();
174 vdp->setPalette(selectedColor, palette[selectedColor], time);
175 }
176 }
177 });
178 });
179 });
180 }
181 });
182 });
183}
184
185} // namespace openmsx
int g
im::WindowStatus window
void save(ImGuiTextBuffer &buf) override
std::span< const uint16_t, 16 > getPalette(const VDP *vdp) const
static uint32_t toRGBA(uint16_t msxColor)
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
void setPalette(unsigned index, word grb, EmuTime::param time)
Sets a palette entry.
Definition VDP.cc:761
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:43
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:24
auto split_view(std::string_view str, Separators separators)
Definition StringOp.hh:83
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