| 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 = { |
| 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 |
|
| 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; |
| 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 |
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()) { |
| 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()) { |
| 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) { |
| 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 |
} |