OXYGENHUB
◆ main openmsx / src / imgui / ImGuiPalette.cc
5.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 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
15
using namespace std::literals;
16
17
18
namespace openmsx {
19
20
static 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
27
ImGuiPalette::ImGuiPalette(ImGuiManager& manager_)
28
	: ImGuiPart(manager_)
29
{
30
	customPalette = fixedPalette;
31
}
32
33
void 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
45
void 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
60
static 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
68
static 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
81
static 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
93
std::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
119
void ImGuiPalette::paint(MSXMotherBoard* motherBoard)
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; // uninitialized
132
		bool disabled = (whichPalette == PALETTE_FIXED) ||
133
				((whichPalette == PALETTE_VDP) && (!vdp || vdp->isMSX1VDP()));
134
		auto palette = [&] -> std::span<uint16_t, 16> {
135
			if (disabled) {
136
				copy_to_range(fixedPalette, paletteCopy);
137
				return paletteCopy;
138
			} else if (whichPalette == PALETTE_VDP) {
139
				assert(vdp);
140
				copy_to_range(vdp->getPalette(), paletteCopy);
141
				return paletteCopy;
142
			} else {
143
				return customPalette;
144
			}
145
		}();
146
147
		im::Table("left/right", 2, [&]{
148
			ImGui::TableSetupColumn("left",  ImGuiTableColumnFlags_WidthFixed, 200.0f);
149
			ImGui::TableSetupColumn("right", ImGuiTableColumnFlags_WidthFixed, 150.0f);
150
151
			if (ImGui::TableNextColumn()) { // left pane
152
				std::array<uint32_t, 16> paletteRGB = getPalette(vdp);
153
				im::Table("grid", 4, [&]{
154
					im::ID_for_range(16, [&](int i) {
155
						if (ImGui::TableNextColumn()) {
156
							im::StyleColor(i == selectedColor, ImGuiCol_Border, ImGui::GetColorU32(ImGuiCol_HeaderActive), [&]{
157
								ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, i == selectedColor ? 3.0f : 1.0f);
158
								if (coloredButton("", paletteRGB[i], {44.0f, 30.0f})) {
159
									selectedColor = i;
160
								}
161
								ImGui::PopStyleVar();
162
							});
163
						}
164
					});
165
				});
166
			}
167
			if (ImGui::TableNextColumn()) { // right pane
168
				ImGui::StrCat("Color ", selectedColor);
169
				ImGui::TextUnformatted(" "sv);
170
				ImGui::SameLine();
171
				coloredButton("##color", toRGBA(palette[selectedColor]), {150.0f, 30.0f});
172
				ImGui::Spacing();
173
				ImGui::Spacing();
174
175
				im::Disabled(disabled, [&]{
176
					static constexpr std::array names = {"R"sv, "G"sv, "B"sv};
177
					auto rgb = extractRGB(palette[selectedColor]);
178
					im::ID_for_range(3, [&](int i) { // rgb sliders
179
						ImGui::AlignTextToFramePadding();
180
						ImGui::TextUnformatted(names[i]);
181
						ImGui::SameLine();
182
						im::StyleColor(
183
							ImGuiCol_FrameBg,        static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.5f, 0.5f)),
184
							ImGuiCol_FrameBgHovered, static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.6f, 0.5f)),
185
							ImGuiCol_FrameBgActive,  static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.7f, 0.5f)),
186
							ImGuiCol_SliderGrab,     static_cast<ImVec4>(ImColor::HSV(float(i) * (1.0f / 3.0f), 0.9f, 0.9f)), [&]{
187
							ImGui::SetNextItemWidth(-FLT_MIN);
188
							if (ImGui::SliderInt("##v", &rgb[i], 0, 7, "%d", ImGuiSliderFlags_AlwaysClamp)) {
189
								assert(!disabled);
190
								insertRGB(palette, selectedColor, rgb);
191
								if (whichPalette == PALETTE_VDP) {
192
									auto time = motherBoard->getCurrentTime();
193
									vdp->setPalette(selectedColor, palette[selectedColor], time);
194
								}
195
							}
196
						});
197
					});
198
				});
199
			}
200
		});
201
	});
202
}
203
204
} // namespace openmsx