1
0
mirror of synced 2024-12-04 20:17:57 +01:00
ImHex/plugins/visualizers/source/content/pl_visualizers/image.cpp
paxcut 057543da15
feat: Extended bitmap visualizer to handle indexed colormaps (#1901)
### Problem description
Older image format use to store color values in a small lookup table so
that the image could simply store an index to the table. The pattern
language is not designed to handle this sort of operation which makes
this extension necessary to be able to visualize images of this type.

### Implementation description

The changes add an optional parameter to the bitmap visualizer which
holds the color lookup table. If the image contains values that are
outside the range of possible colors then the first color in the map is
chosen. The dimensions of the image can be equal to or smaller than
rows*columns depending on how the indices to the color map are stored.
For example, you can use 4 bit indices which would make the image half
the size (in bytes) but that limits the number of colors to 16. To store
colors in sizes larger than one byte use an array of the appropriate
sized integers.

### Screenshots


![image](https://github.com/user-attachments/assets/d068cb7e-3ff3-450d-8ac2-1bfc6e38043f)
2024-09-15 15:16:36 +02:00

159 lines
6.1 KiB
C++

#include <hex/helpers/utils.hpp>
#include <content/visualizer_helpers.hpp>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex::plugin::visualizers {
std::vector<u32> getIndices(pl::ptrn::Pattern *colorTablePattern, u128 width, u128 height);
ImGuiExt::Texture getTexture(pl::ptrn::Pattern *colorTablePattern, std::vector<u32>& indices, u128 width, u128 height);
void drawImageVisualizer(pl::ptrn::Pattern &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImGuiExt::Texture texture;
static float scale = 1.0F;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto data = pattern->getBytes();
texture = ImGuiExt::Texture::fromImage(data.data(), data.size(), ImGuiExt::Texture::Filter::Nearest);
scale = 200_scaled / texture.getSize().x;
}
if (texture.isValid())
ImGui::Image(texture, texture.getSize() * scale);
if (ImGui::IsWindowHovered()) {
auto scrollDelta = ImGui::GetIO().MouseWheel;
if (scrollDelta != 0.0F) {
scale += scrollDelta * 0.1F;
scale = std::clamp(scale, 0.1F, 10.0F);
}
}
}
void drawBitmapVisualizer(pl::ptrn::Pattern &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImGuiExt::Texture texture;
static float scale = 1.0F;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto width = arguments[1].toUnsigned();
auto height = arguments[2].toUnsigned();
bool hasColorTable = false;
if (arguments.size() == 4) {
auto colorTablePattern = arguments[3].toPattern();
if (colorTablePattern->getSize() > 0) {
auto indices = getIndices(pattern.get(), width, height);
texture = getTexture(colorTablePattern.get(), indices, width, height);
hasColorTable = true;
}
}
if (!hasColorTable) {
auto data = pattern->getBytes();
texture = ImGuiExt::Texture::fromBitmap(data.data(), data.size(), width, height,ImGuiExt::Texture::Filter::Nearest);
}
}
if (texture.isValid())
ImGui::Image(texture, texture.getSize() * scale);
if (ImGui::IsWindowHovered()) {
auto scrollDelta = ImGui::GetIO().MouseWheel;
if (scrollDelta != 0.0F) {
scale += scrollDelta * 0.1F;
scale = std::clamp(scale, 0.1F, 10.0F);
}
}
}
template <typename T> ImGuiExt::Texture unmapColors(pl::ptrn::Pattern *colorTablePattern, std::vector<u32>& indices, u128 width, u128 height) {
std::vector<T> colorTable = patternToArray<T>(colorTablePattern);
auto colorCount = colorTable.size();
auto indexCount = indices.size();
std::vector<T> image(indexCount);
for (u32 i = 0; i < indexCount; i++) {
auto index = indices[i];
if (index >= colorCount)
index = 0;
image[i] = colorTable[index];
}
void *tmp = image.data();
ImU8 *data = static_cast<ImU8 *>(tmp);
ImGuiExt::Texture texture = ImGuiExt::Texture::fromBitmap(data, indexCount*sizeof(T), width, height, ImGuiExt::Texture::Filter::Nearest);
return texture;
}
std::vector<u32> getIndices(pl::ptrn::Pattern *pattern, u128 width, u128 height) {
auto indexCount = 2 * width * height / pattern->getSize();
std::vector<u32> indices;
auto *iterable = dynamic_cast<pl::ptrn::IIterable *>(pattern);
if (iterable == nullptr || iterable->getEntryCount() <= 0)
return indices;
auto content = iterable->getEntry(0);
auto byteCount = content->getSize();
if (byteCount >= indexCount && indexCount != 0) {
auto bytePerIndex = byteCount / indexCount;
if (bytePerIndex == 1) {
auto temp = patternToArray<u8>(pattern);
indices = std::vector<u32>(temp.begin(), temp.end());
} else if (bytePerIndex == 2) {
auto temp = patternToArray<u16>(pattern);
indices = std::vector<u32>(temp.begin(), temp.end());
} else if (bytePerIndex == 4) {
auto temp = patternToArray<u32>(pattern);
indices = std::vector<u32>(temp.begin(), temp.end());
}
} else if (indexCount != 0) {
auto indicesPerByte = indexCount / byteCount;
auto temp = patternToArray<u8>(pattern);
if (indicesPerByte == 2) {
for (u32 i = 0; i < temp.size(); i++) {
indices.push_back(temp[i] & 0xF);
indices.push_back((temp[i] >> 4) & 0xF);
}
} else if (indicesPerByte == 4) {
for (u32 i = 0; i < temp.size(); i++) {
indices.push_back(temp[i] & 0x3);
indices.push_back((temp[i] >> 2) & 0x3);
indices.push_back((temp[i] >> 4) & 0x3);
indices.push_back((temp[i] >> 6) & 0x3);
}
}
}
return indices;
}
ImGuiExt::Texture getTexture(pl::ptrn::Pattern *colorTablePattern, std::vector<u32>& indices, u128 width, u128 height) {
ImGuiExt::Texture texture;
auto iterable = dynamic_cast<pl::ptrn::IIterable *>(colorTablePattern);
if (iterable == nullptr || iterable->getEntryCount() <= 0)
return texture;
auto content = iterable->getEntry(0);
auto colorTypeSize = content->getSize()*2;
if (colorTypeSize == 1) {
texture = unmapColors<u8>(colorTablePattern, indices, width, height);
} else if (colorTypeSize == 2) {
texture = unmapColors<u16>(colorTablePattern, indices, width, height);
} else if (colorTypeSize == 4) {
texture = unmapColors<u32>(colorTablePattern, indices, width, height);
}
return texture;
}
}