backend-infra-engineer: Pre-0.2.2 snapshot (2023)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "bitmap.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <png.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -23,35 +24,180 @@ void GrayscalePalette(SDL_Palette *palette) {
|
||||
palette->colors[i].b = i * 31;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Bitmap::Bitmap(int width, int height, int depth, uchar *data) {
|
||||
Create(width, height, depth, data);
|
||||
void PngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
std::vector<uint8_t> *p = (std::vector<uint8_t> *)png_get_io_ptr(png_ptr);
|
||||
p->insert(p->end(), data, data + length);
|
||||
}
|
||||
|
||||
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer) {
|
||||
png_structp png_ptr = png_create_write_struct("1.6.40", NULL, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
SDL_Log("Failed to create PNG write struct");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
||||
SDL_Log("Failed to create PNG info struct");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
SDL_Log("Error during PNG write");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn(png_ptr, &buffer, PngWriteCallback, NULL);
|
||||
|
||||
png_colorp pal_ptr;
|
||||
|
||||
/* Prepare chunks */
|
||||
int colortype = PNG_COLOR_MASK_COLOR;
|
||||
int i = 0;
|
||||
SDL_Palette *pal;
|
||||
if (surface->format->BytesPerPixel > 0 &&
|
||||
surface->format->BytesPerPixel <= 8 && (pal = surface->format->palette)) {
|
||||
SDL_Log("Writing PNG image with palette");
|
||||
colortype |= PNG_COLOR_MASK_PALETTE;
|
||||
pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color));
|
||||
for (i = 0; i < pal->ncolors; i++) {
|
||||
pal_ptr[i].red = pal->colors[i].r;
|
||||
pal_ptr[i].green = pal->colors[i].g;
|
||||
pal_ptr[i].blue = pal->colors[i].b;
|
||||
}
|
||||
png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
|
||||
free(pal_ptr);
|
||||
}
|
||||
|
||||
auto depth = surface->format->BitsPerPixel;
|
||||
|
||||
// Set image attributes.
|
||||
png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, depth, colortype,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_set_bgr(png_ptr);
|
||||
|
||||
// Write the image data.
|
||||
std::vector<png_bytep> row_pointers(surface->h);
|
||||
for (int y = 0; y < surface->h; ++y) {
|
||||
row_pointers[y] = (png_bytep)(surface->pixels) + y * surface->pitch;
|
||||
}
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, row_pointers.data());
|
||||
|
||||
SDL_Log("Writing PNG image...");
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
SDL_Log("PNG image write complete");
|
||||
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PngReadCallback(png_structp png_ptr, png_bytep outBytes,
|
||||
png_size_t byteCountToRead) {
|
||||
png_voidp io_ptr = png_get_io_ptr(png_ptr);
|
||||
if (!io_ptr) return;
|
||||
|
||||
std::vector<uint8_t> *png_data =
|
||||
reinterpret_cast<std::vector<uint8_t> *>(io_ptr);
|
||||
size_t pos = png_data->size() - byteCountToRead;
|
||||
memcpy(outBytes, png_data->data() + pos, byteCountToRead);
|
||||
png_data->resize(pos); // Reduce the buffer size
|
||||
}
|
||||
|
||||
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
||||
SDL_Surface **outSurface) {
|
||||
std::vector<uint8_t> data(png_data);
|
||||
png_structp png_ptr = png_create_read_struct("1.6.40", NULL, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
throw std::runtime_error("Failed to create PNG read struct");
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
throw std::runtime_error("Failed to create PNG info struct");
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
throw std::runtime_error("Error during PNG read");
|
||||
}
|
||||
|
||||
// Set our custom read function
|
||||
png_set_read_fn(png_ptr, &data, PngReadCallback);
|
||||
|
||||
// Read the PNG info
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
uint32_t width = png_get_image_width(png_ptr, info_ptr);
|
||||
uint32_t height = png_get_image_height(png_ptr, info_ptr);
|
||||
png_byte color_type = png_get_color_type(png_ptr, info_ptr);
|
||||
png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||
|
||||
// Set up transformations, e.g., strip 16-bit PNGs down to 8-bit, expand
|
||||
// palettes, etc.
|
||||
if (bit_depth == 16) {
|
||||
png_set_strip_16(png_ptr);
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
// PNG files pack pixels, expand them
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
|
||||
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||
}
|
||||
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
// Update info structure with transformations
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
|
||||
// Read the file
|
||||
std::vector<png_bytep> row_pointers(height);
|
||||
std::vector<uint8_t> raw_data(width * height *
|
||||
4); // Assuming 4 bytes per pixel (RGBA)
|
||||
for (size_t y = 0; y < height; y++) {
|
||||
row_pointers[y] = &raw_data[y * width * 4];
|
||||
}
|
||||
|
||||
png_read_image(png_ptr, row_pointers.data());
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
// Create SDL_Surface from raw pixel data
|
||||
*outSurface = SDL_CreateRGBSurfaceWithFormatFrom(
|
||||
raw_data.data(), width, height, 32, width * 4, SDL_PIXELFORMAT_RGBA32);
|
||||
if (*outSurface == nullptr) {
|
||||
SDL_Log("SDL_CreateRGBSurfaceWithFormatFrom failed: %s\n", SDL_GetError());
|
||||
} else {
|
||||
SDL_Log("Successfully created SDL_Surface from PNG data");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Bitmap::Bitmap(int width, int height, int depth, int data_size) {
|
||||
Create(width, height, depth, data_size);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height, int depth, uchar *data, int data_size) {
|
||||
Create(width, height, depth, data, data_size);
|
||||
}
|
||||
|
||||
// Pass raw pixel data directly to the surface
|
||||
void Bitmap::Create(int width, int height, int depth, uchar *data) {
|
||||
active_ = true;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
depth_ = depth;
|
||||
pixel_data_ = data;
|
||||
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
|
||||
SDL_PIXELFORMAT_INDEX8),
|
||||
SDL_Surface_Deleter());
|
||||
surface_->pixels = pixel_data_;
|
||||
GrayscalePalette(surface_->format->palette);
|
||||
}
|
||||
|
||||
// Reserves data to later draw to surface via pointer
|
||||
void Bitmap::Create(int width, int height, int depth, int size) {
|
||||
active_ = true;
|
||||
@@ -69,23 +215,7 @@ void Bitmap::Create(int width, int height, int depth, int size) {
|
||||
GrayscalePalette(surface_->format->palette);
|
||||
}
|
||||
|
||||
// Pass raw pixel data directly to the surface
|
||||
void Bitmap::Create(int width, int height, int depth, uchar *data, int size) {
|
||||
active_ = true;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
depth_ = depth;
|
||||
pixel_data_ = data;
|
||||
data_size_ = size;
|
||||
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
|
||||
SDL_PIXELFORMAT_INDEX8),
|
||||
SDL_Surface_Deleter());
|
||||
surface_->pixels = pixel_data_;
|
||||
GrayscalePalette(surface_->format->palette);
|
||||
}
|
||||
|
||||
void Bitmap::Create(int width, int height, int depth, Bytes data) {
|
||||
void Bitmap::Create(int width, int height, int depth, const Bytes &data) {
|
||||
active_ = true;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
@@ -100,9 +230,62 @@ void Bitmap::Create(int width, int height, int depth, Bytes data) {
|
||||
GrayscalePalette(surface_->format->palette);
|
||||
}
|
||||
|
||||
void Bitmap::Apply(Bytes data) {
|
||||
pixel_data_ = data.data();
|
||||
data_ = data;
|
||||
// Creates the texture that will be displayed to the screen.
|
||||
void Bitmap::CreateTexture(SDL_Renderer *renderer) {
|
||||
// Ensure width and height are non-zero
|
||||
if (width_ <= 0 || height_ <= 0) {
|
||||
SDL_Log("Invalid texture dimensions: width=%d, height=%d\n", width_,
|
||||
height_);
|
||||
return;
|
||||
}
|
||||
|
||||
texture_ = std::shared_ptr<SDL_Texture>{
|
||||
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888,
|
||||
SDL_TEXTUREACCESS_STREAMING, width_, height_),
|
||||
SDL_Texture_Deleter{}};
|
||||
if (texture_ == nullptr) {
|
||||
SDL_Log("SDL_CreateTextureFromSurface failed: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_Surface *converted_surface =
|
||||
SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (converted_surface) {
|
||||
// Create texture from the converted surface
|
||||
converted_surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
converted_surface, SDL_Surface_Deleter());
|
||||
} else {
|
||||
// Handle the error
|
||||
SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
|
||||
&converted_surface_->pitch);
|
||||
|
||||
memcpy(texture_pixels, converted_surface_->pixels,
|
||||
converted_surface_->h * converted_surface_->pitch);
|
||||
|
||||
SDL_UnlockTexture(texture_.get());
|
||||
}
|
||||
|
||||
void Bitmap::UpdateTexture(SDL_Renderer *renderer) {
|
||||
SDL_Surface *converted_surface =
|
||||
SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (converted_surface) {
|
||||
// Create texture from the converted surface
|
||||
converted_surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
converted_surface, SDL_Surface_Deleter());
|
||||
} else {
|
||||
// Handle the error
|
||||
SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
|
||||
&converted_surface_->pitch);
|
||||
|
||||
memcpy(texture_pixels, converted_surface_->pixels,
|
||||
converted_surface_->h * converted_surface_->pitch);
|
||||
|
||||
SDL_UnlockTexture(texture_.get());
|
||||
}
|
||||
|
||||
// Creates the texture that will be displayed to the screen.
|
||||
@@ -112,22 +295,107 @@ void Bitmap::CreateTexture(std::shared_ptr<SDL_Renderer> renderer) {
|
||||
SDL_Texture_Deleter{}};
|
||||
}
|
||||
|
||||
void Bitmap::UpdateTexture(std::shared_ptr<SDL_Renderer> renderer) {
|
||||
// SDL_DestroyTexture(texture_.get());
|
||||
// texture_ = nullptr;
|
||||
texture_ = std::shared_ptr<SDL_Texture>{
|
||||
SDL_CreateTextureFromSurface(renderer.get(), surface_.get()),
|
||||
SDL_Texture_Deleter{}};
|
||||
}
|
||||
|
||||
void Bitmap::SaveSurfaceToFile(std::string_view filename) {
|
||||
SDL_SaveBMP(surface_.get(), filename.data());
|
||||
}
|
||||
|
||||
void Bitmap::SetSurface(SDL_Surface *surface) {
|
||||
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
surface, SDL_Surface_Deleter());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Bitmap::GetPngData() {
|
||||
ConvertSurfaceToPNG(surface_.get(), png_data_);
|
||||
return png_data_;
|
||||
}
|
||||
|
||||
void Bitmap::LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||
int height) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
SDL_Surface *surface = surface_.get();
|
||||
ConvertPngToSurface(png_data, &surface);
|
||||
surface_.reset(surface);
|
||||
}
|
||||
|
||||
// Convert SNESPalette to SDL_Palette for surface.
|
||||
void Bitmap::ApplyPalette(const SNESPalette &palette) {
|
||||
palette_ = palette;
|
||||
for (int i = 0; i < palette.size_; ++i) {
|
||||
if (palette.GetColor(i).transparent) {
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
for (int i = 0; i < palette.size(); ++i) {
|
||||
if (palette.GetColor(i).IsTransparent()) {
|
||||
surface_->format->palette->colors[i].r = 0;
|
||||
surface_->format->palette->colors[i].g = 0;
|
||||
surface_->format->palette->colors[i].b = 0;
|
||||
surface_->format->palette->colors[i].a = 0;
|
||||
} else {
|
||||
surface_->format->palette->colors[i].r = palette.GetColor(i).rgb.x;
|
||||
surface_->format->palette->colors[i].g = palette.GetColor(i).rgb.y;
|
||||
surface_->format->palette->colors[i].b = palette.GetColor(i).rgb.z;
|
||||
surface_->format->palette->colors[i].a = palette.GetColor(i).rgb.w;
|
||||
surface_->format->palette->colors[i].r = palette.GetColor(i).GetRGB().x;
|
||||
surface_->format->palette->colors[i].g = palette.GetColor(i).GetRGB().y;
|
||||
surface_->format->palette->colors[i].b = palette.GetColor(i).GetRGB().z;
|
||||
surface_->format->palette->colors[i].a = palette.GetColor(i).GetRGB().w;
|
||||
}
|
||||
}
|
||||
SDL_LockSurface(surface_.get());
|
||||
}
|
||||
|
||||
void Bitmap::ApplyPaletteWithTransparent(const SNESPalette &palette,
|
||||
int index) {
|
||||
auto start_index = index * 7;
|
||||
palette_ = palette.sub_palette(start_index, start_index + 7);
|
||||
std::vector<ImVec4> colors;
|
||||
colors.push_back(ImVec4(0, 0, 0, 0));
|
||||
for (int i = start_index; i < start_index + 7; ++i) {
|
||||
colors.push_back(palette.GetColor(i).GetRGB());
|
||||
}
|
||||
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
int i = 0;
|
||||
for (auto &each : colors) {
|
||||
surface_->format->palette->colors[i].r = each.x;
|
||||
surface_->format->palette->colors[i].g = each.y;
|
||||
surface_->format->palette->colors[i].b = each.z;
|
||||
surface_->format->palette->colors[i].a = each.w;
|
||||
i++;
|
||||
}
|
||||
SDL_LockSurface(surface_.get());
|
||||
}
|
||||
|
||||
void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
for (int i = 0; i < palette.size(); ++i) {
|
||||
surface_->format->palette->colors[i].r = palette[i].r;
|
||||
surface_->format->palette->colors[i].g = palette[i].g;
|
||||
surface_->format->palette->colors[i].b = palette[i].b;
|
||||
surface_->format->palette->colors[i].a = palette[i].a;
|
||||
}
|
||||
SDL_LockSurface(surface_.get());
|
||||
}
|
||||
|
||||
void Bitmap::InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
|
||||
const Bytes &data) {
|
||||
active_ = true;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
depth_ = depth;
|
||||
data_ = data;
|
||||
data_size_ = data.size();
|
||||
pixel_data_ = data_.data();
|
||||
|
||||
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
|
||||
SDL_PIXELFORMAT_INDEX8),
|
||||
SDL_Surface_Deleter());
|
||||
|
||||
surface_->pixels = pixel_data_;
|
||||
GrayscalePalette(surface_->format->palette);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
@@ -19,33 +20,136 @@ namespace gfx {
|
||||
class Bitmap {
|
||||
public:
|
||||
Bitmap() = default;
|
||||
Bitmap(int width, int height, int depth, uchar *data);
|
||||
|
||||
Bitmap(int width, int height, int depth, int data_size);
|
||||
Bitmap(int width, int height, int depth, uchar *data, int data_size);
|
||||
|
||||
void Create(int width, int height, int depth, uchar *data);
|
||||
void Create(int width, int height, int depth, int data_size);
|
||||
void Create(int width, int height, int depth, uchar *data, int data_size);
|
||||
void Create(int width, int height, int depth, Bytes data);
|
||||
|
||||
void Apply(Bytes data);
|
||||
|
||||
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
|
||||
void ApplyPalette(const SNESPalette &palette);
|
||||
|
||||
void WriteToPixel(int position, uchar value) {
|
||||
this->pixel_data_[position] = value;
|
||||
Bitmap(int width, int height, int depth, const Bytes &data)
|
||||
: width_(width), height_(height), depth_(depth), data_(data) {
|
||||
InitializeFromData(width, height, depth, data);
|
||||
}
|
||||
|
||||
int GetWidth() const { return width_; }
|
||||
int GetHeight() const { return height_; }
|
||||
auto GetSize() const { return data_size_; }
|
||||
auto GetData() const { return pixel_data_; }
|
||||
auto GetByte(int i) const { return pixel_data_[i]; }
|
||||
auto GetTexture() const { return texture_.get(); }
|
||||
auto GetSurface() const { return surface_.get(); }
|
||||
void Create(int width, int height, int depth, int data_size);
|
||||
void Create(int width, int height, int depth, const Bytes &data);
|
||||
|
||||
void InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
|
||||
const Bytes &data);
|
||||
|
||||
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void CreateTexture(SDL_Renderer *renderer);
|
||||
void UpdateTexture(SDL_Renderer *renderer);
|
||||
|
||||
void SaveSurfaceToFile(std::string_view filename);
|
||||
void SetSurface(SDL_Surface *surface);
|
||||
std::vector<uint8_t> GetPngData();
|
||||
void LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||
int height);
|
||||
|
||||
void ApplyPalette(const SNESPalette &palette);
|
||||
void ApplyPaletteWithTransparent(const SNESPalette &palette, int index);
|
||||
void ApplyPalette(const std::vector<SDL_Color> &palette);
|
||||
|
||||
void WriteToPixel(int position, uchar value) {
|
||||
if (pixel_data_ == nullptr) {
|
||||
pixel_data_ = data_.data();
|
||||
}
|
||||
pixel_data_[position] = value;
|
||||
modified_ = true;
|
||||
}
|
||||
|
||||
void WriteWordToPixel(int position, uint16_t value) {
|
||||
if (pixel_data_ == nullptr) {
|
||||
pixel_data_ = data_.data();
|
||||
}
|
||||
pixel_data_[position] = value & 0xFF;
|
||||
pixel_data_[position + 1] = (value >> 8) & 0xFF;
|
||||
modified_ = true;
|
||||
}
|
||||
|
||||
void Get8x8Tile(int tile_index, int x, int y, std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset) {
|
||||
int tile_offset = tile_index * 64;
|
||||
int tile_x = x * 8;
|
||||
int tile_y = y * 8;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int row_offset = tile_offset + (i * 8);
|
||||
for (int j = 0; j < 8; j++) {
|
||||
int pixel_offset = row_offset + j;
|
||||
int pixel_value = data_[pixel_offset];
|
||||
tile_data[tile_data_offset] = pixel_value;
|
||||
tile_data_offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteColor(int position, const ImVec4 &color) {
|
||||
// Convert ImVec4 (RGBA) to SDL_Color (RGBA)
|
||||
SDL_Color sdl_color;
|
||||
sdl_color.r = static_cast<Uint8>(color.x * 255);
|
||||
sdl_color.g = static_cast<Uint8>(color.y * 255);
|
||||
sdl_color.b = static_cast<Uint8>(color.z * 255);
|
||||
sdl_color.a = static_cast<Uint8>(color.w * 255);
|
||||
|
||||
// Map SDL_Color to the nearest color index in the surface's palette
|
||||
Uint8 index =
|
||||
SDL_MapRGB(surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
|
||||
|
||||
// Write the color index to the pixel data
|
||||
pixel_data_[position] = index;
|
||||
modified_ = true;
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
// Reset texture_
|
||||
if (texture_) {
|
||||
texture_.reset();
|
||||
}
|
||||
|
||||
// Reset surface_ and its pixel data
|
||||
if (surface_) {
|
||||
surface_->pixels = nullptr;
|
||||
surface_.reset();
|
||||
}
|
||||
|
||||
// Reset data_
|
||||
data_.clear();
|
||||
|
||||
// Reset other members if necessary
|
||||
active_ = false;
|
||||
pixel_data_ = nullptr;
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
depth_ = 0;
|
||||
data_size_ = 0;
|
||||
palette_.Clear();
|
||||
}
|
||||
|
||||
auto sdl_palette() {
|
||||
if (surface_ == nullptr) {
|
||||
throw std::runtime_error("Surface is null.");
|
||||
}
|
||||
return surface_->format->palette;
|
||||
}
|
||||
auto palette() const { return palette_; }
|
||||
auto palette_size() const { return palette_.size(); }
|
||||
|
||||
int width() const { return width_; }
|
||||
int height() const { return height_; }
|
||||
auto depth() const { return depth_; }
|
||||
auto size() const { return data_size_; }
|
||||
auto data() const { return data_.data(); }
|
||||
auto &mutable_data() { return data_; }
|
||||
auto mutable_pixel_data() { return pixel_data_; }
|
||||
auto surface() const { return surface_.get(); }
|
||||
auto mutable_surface() { return surface_.get(); }
|
||||
void set_data(const Bytes &data) { data_ = data; }
|
||||
|
||||
auto vector() const { return data_; }
|
||||
auto at(int i) const { return data_[i]; }
|
||||
auto texture() const { return texture_.get(); }
|
||||
auto modified() const { return modified_; }
|
||||
void set_modified(bool modified) { modified_ = modified; }
|
||||
auto IsActive() const { return active_; }
|
||||
auto SetActive(bool active) { active_ = active; }
|
||||
|
||||
private:
|
||||
struct SDL_Texture_Deleter {
|
||||
@@ -71,13 +175,75 @@ class Bitmap {
|
||||
int height_ = 0;
|
||||
int depth_ = 0;
|
||||
int data_size_ = 0;
|
||||
|
||||
bool freed_ = false;
|
||||
bool active_ = false;
|
||||
bool modified_ = false;
|
||||
void *texture_pixels = nullptr;
|
||||
|
||||
uchar *pixel_data_;
|
||||
Bytes data_;
|
||||
|
||||
std::vector<uint8_t> png_data_;
|
||||
|
||||
gfx::SNESPalette palette_;
|
||||
std::shared_ptr<SDL_Texture> texture_ = nullptr;
|
||||
std::shared_ptr<SDL_Surface> surface_ = nullptr;
|
||||
std::shared_ptr<SDL_Surface> converted_surface_ = nullptr;
|
||||
};
|
||||
|
||||
using BitmapTable = std::unordered_map<int, gfx::Bitmap>;
|
||||
|
||||
class BitmapManager {
|
||||
private:
|
||||
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>> bitmap_cache_;
|
||||
|
||||
public:
|
||||
void LoadBitmap(int id, const Bytes &data, int width, int height, int depth) {
|
||||
bitmap_cache_[id] =
|
||||
std::make_shared<gfx::Bitmap>(width, height, depth, data);
|
||||
}
|
||||
|
||||
std::shared_ptr<gfx::Bitmap> const &CopyBitmap(const gfx::Bitmap &bitmap,
|
||||
int id) {
|
||||
auto new_bitmap = std::make_shared<gfx::Bitmap>(
|
||||
bitmap.width(), bitmap.height(), bitmap.depth(), bitmap.vector());
|
||||
bitmap_cache_[id] = new_bitmap;
|
||||
return new_bitmap;
|
||||
}
|
||||
|
||||
std::shared_ptr<gfx::Bitmap> const &operator[](int id) {
|
||||
auto it = bitmap_cache_.find(id);
|
||||
if (it != bitmap_cache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto mutable_bitmap(int id) { return bitmap_cache_[id]; }
|
||||
|
||||
using value_type = std::pair<const int, std::shared_ptr<gfx::Bitmap>>;
|
||||
using iterator =
|
||||
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>>::iterator;
|
||||
using const_iterator =
|
||||
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>>::const_iterator;
|
||||
|
||||
iterator begin() noexcept { return bitmap_cache_.begin(); }
|
||||
iterator end() noexcept { return bitmap_cache_.end(); }
|
||||
const_iterator begin() const noexcept { return bitmap_cache_.begin(); }
|
||||
const_iterator end() const noexcept { return bitmap_cache_.end(); }
|
||||
const_iterator cbegin() const noexcept { return bitmap_cache_.cbegin(); }
|
||||
const_iterator cend() const noexcept { return bitmap_cache_.cend(); }
|
||||
|
||||
std::shared_ptr<gfx::Bitmap> const &GetBitmap(int id) {
|
||||
auto it = bitmap_cache_.find(id);
|
||||
if (it != bitmap_cache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr; // or handle the error accordingly
|
||||
}
|
||||
|
||||
void ClearCache() { bitmap_cache_.clear(); }
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
1186
src/app/gfx/compression.cc
Normal file
1186
src/app/gfx/compression.cc
Normal file
File diff suppressed because it is too large
Load Diff
213
src/app/gfx/compression.h
Normal file
213
src/app/gfx/compression.h
Normal file
@@ -0,0 +1,213 @@
|
||||
#ifndef YAZE_APP_GFX_COMPRESSION_H
|
||||
#define YAZE_APP_GFX_COMPRESSION_H
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/constants.h"
|
||||
|
||||
#define BUILD_HEADER(command, length) (command << 5) + (length - 1)
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
namespace lc_lz2 {
|
||||
|
||||
constexpr int kCommandDirectCopy = 0;
|
||||
constexpr int kCommandByteFill = 1;
|
||||
constexpr int kCommandWordFill = 2;
|
||||
constexpr int kCommandIncreasingFill = 3;
|
||||
constexpr int kCommandRepeatingBytes = 4;
|
||||
constexpr int kCommandLongLength = 7;
|
||||
constexpr int kMaxLengthNormalHeader = 32;
|
||||
constexpr int kMaxLengthCompression = 1024;
|
||||
constexpr int kNintendoMode1 = 0;
|
||||
constexpr int kNintendoMode2 = 1;
|
||||
constexpr int kSnesByteMax = 0xFF;
|
||||
constexpr int kCommandMod = 0x07;
|
||||
constexpr int kExpandedMod = 0xE0;
|
||||
constexpr int kExpandedLengthMod = 0x3FF;
|
||||
constexpr int kNormalLengthMod = 0x1F;
|
||||
constexpr int kCompressionStringMod = 7 << 5;
|
||||
|
||||
// Represents a command in the compression algorithm.
|
||||
struct CompressionCommand {
|
||||
// The command arguments for each possible command.
|
||||
std::array<std::array<char, 2>, 5> arguments;
|
||||
|
||||
// The size of each possible command.
|
||||
std::array<uint, 5> cmd_size;
|
||||
|
||||
// The size of the data processed by each possible command.
|
||||
std::array<uint, 5> data_size;
|
||||
};
|
||||
|
||||
using CommandArgumentArray = std::array<std::array<char, 2>, 5>;
|
||||
using CommandSizeArray = std::array<uint, 5>;
|
||||
using DataSizeArray = std::array<uint, 5>;
|
||||
|
||||
// Represents a piece of compressed data.
|
||||
struct CompressionPiece {
|
||||
char command;
|
||||
int length;
|
||||
int argument_length;
|
||||
std::string argument;
|
||||
std::shared_ptr<CompressionPiece> next = nullptr;
|
||||
CompressionPiece() = default;
|
||||
CompressionPiece(int cmd, int len, std::string args, int arg_len)
|
||||
: command(cmd), length(len), argument_length(arg_len), argument(args) {}
|
||||
};
|
||||
using CompressionPiece = struct CompressionPiece;
|
||||
using CompressionPiecePointer = std::shared_ptr<CompressionPiece>;
|
||||
|
||||
void PrintCompressionPiece(const CompressionPiecePointer& piece);
|
||||
|
||||
void PrintCompressionChain(const CompressionPiecePointer& chain_head);
|
||||
|
||||
// Compression V1
|
||||
|
||||
void CheckByteRepeat(const uchar* rom_data, DataSizeArray& data_size_taken,
|
||||
CommandArgumentArray& cmd_args, uint& src_data_pos,
|
||||
const uint last_pos);
|
||||
|
||||
void CheckWordRepeat(const uchar* rom_data, DataSizeArray& data_size_taken,
|
||||
CommandArgumentArray& cmd_args, uint& src_data_pos,
|
||||
const uint last_pos);
|
||||
|
||||
void CheckIncByte(const uchar* rom_data, DataSizeArray& data_size_taken,
|
||||
CommandArgumentArray& cmd_args, uint& src_data_pos,
|
||||
const uint last_pos);
|
||||
|
||||
void CheckIntraCopy(const uchar* rom_data, DataSizeArray& data_size_taken,
|
||||
CommandArgumentArray& cmd_args, uint& src_data_pos,
|
||||
const uint last_pos, uint start);
|
||||
|
||||
void ValidateForByteGain(const DataSizeArray& data_size_taken,
|
||||
const CommandSizeArray& cmd_size, uint& max_win,
|
||||
uint& cmd_with_max);
|
||||
|
||||
void CompressionCommandAlternative(const uchar* rom_data,
|
||||
CompressionPiecePointer& compressed_chain,
|
||||
const CommandSizeArray& cmd_size,
|
||||
const CommandArgumentArray& cmd_args,
|
||||
uint& src_data_pos, uint& comp_accumulator,
|
||||
uint& cmd_with_max, uint& max_win);
|
||||
|
||||
// Compression V2
|
||||
|
||||
void CheckByteRepeatV2(const uchar* data, uint& src_pos, const uint last_pos,
|
||||
CompressionCommand& cmd);
|
||||
|
||||
void CheckWordRepeatV2(const uchar* data, uint& src_pos, const uint last_pos,
|
||||
CompressionCommand& cmd);
|
||||
|
||||
void CheckIncByteV2(const uchar* data, uint& src_pos, const uint last_pos,
|
||||
CompressionCommand& cmd);
|
||||
|
||||
void CheckIntraCopyV2(const uchar* data, uint& src_pos, const uint last_pos,
|
||||
uint start, CompressionCommand& cmd);
|
||||
|
||||
void ValidateForByteGainV2(const CompressionCommand& cmd, uint& max_win,
|
||||
uint& cmd_with_max);
|
||||
|
||||
void CompressionCommandAlternativeV2(const uchar* data,
|
||||
const CompressionCommand& cmd,
|
||||
CompressionPiecePointer& compressed_chain,
|
||||
uint& src_pos, uint& comp_accumulator,
|
||||
uint& cmd_with_max, uint& max_win);
|
||||
|
||||
absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
|
||||
const int length, int mode = 1,
|
||||
bool check = false);
|
||||
|
||||
absl::StatusOr<Bytes> CompressGraphics(const uchar* data, const int pos,
|
||||
const int length);
|
||||
absl::StatusOr<Bytes> CompressOverworld(const uchar* data, const int pos,
|
||||
const int length);
|
||||
|
||||
absl::StatusOr<CompressionPiecePointer> SplitCompressionPiece(
|
||||
CompressionPiecePointer& piece, int mode);
|
||||
|
||||
Bytes CreateCompressionString(CompressionPiecePointer& start, int mode);
|
||||
|
||||
absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
|
||||
int mode, int start, int src_data_pos);
|
||||
|
||||
CompressionPiecePointer MergeCopy(CompressionPiecePointer& start);
|
||||
|
||||
// Compression V3
|
||||
|
||||
struct CompressionContext {
|
||||
std::vector<uint8_t> data;
|
||||
std::vector<uint8_t> compressed_data;
|
||||
std::vector<CompressionPiece> compression_pieces;
|
||||
std::vector<uint8_t> compression_string;
|
||||
uint src_pos;
|
||||
uint last_pos;
|
||||
uint start;
|
||||
uint comp_accumulator = 0;
|
||||
uint cmd_with_max = kCommandDirectCopy;
|
||||
uint max_win = 0;
|
||||
CompressionCommand current_cmd = {};
|
||||
int mode;
|
||||
|
||||
// Constructor to initialize the context
|
||||
CompressionContext(const std::vector<uint8_t>& data_, const int start,
|
||||
const int length)
|
||||
: data(data_), src_pos(start), last_pos(start + length - 1), mode(0) {}
|
||||
|
||||
// Constructor to initialize the context
|
||||
CompressionContext(const std::vector<uint8_t>& data_, const int start,
|
||||
const int length, int mode_)
|
||||
: data(data_),
|
||||
src_pos(start),
|
||||
last_pos(start + length - 1),
|
||||
mode(mode_) {}
|
||||
};
|
||||
|
||||
void CheckByteRepeatV3(CompressionContext& context);
|
||||
void CheckWordRepeatV3(CompressionContext& context);
|
||||
void CheckIncByteV3(CompressionContext& context);
|
||||
void CheckIntraCopyV3(CompressionContext& context);
|
||||
|
||||
void InitializeCompression(CompressionContext& context);
|
||||
void CheckAvailableCompressionCommands(CompressionContext& context);
|
||||
void DetermineBestCompression(CompressionContext& context);
|
||||
void HandleDirectCopy(CompressionContext& context);
|
||||
void AddCompressionToChain(CompressionContext& context);
|
||||
absl::Status ValidateCompressionResultV3(const CompressionContext& context);
|
||||
|
||||
absl::StatusOr<CompressionPiece> SplitCompressionPieceV3(
|
||||
CompressionPiece& piece, int mode);
|
||||
void FinalizeCompression(CompressionContext& context);
|
||||
|
||||
absl::StatusOr<Bytes> CompressV3(const std::vector<uint8_t> data,
|
||||
const int start, const int length,
|
||||
int mode = 1, bool check = false);
|
||||
|
||||
// Decompression
|
||||
|
||||
std::string SetBuffer(const std::vector<uint8_t>& data, int src_pos,
|
||||
int comp_accumulator);
|
||||
std::string SetBuffer(const uchar* data, int src_pos, int comp_accumulator);
|
||||
void memfill(const uchar* data, Bytes& buffer, int buffer_pos, int offset,
|
||||
int length);
|
||||
|
||||
absl::StatusOr<Bytes> DecompressV2(const uchar* data, int offset,
|
||||
int size = 0x800, int mode = 1);
|
||||
absl::StatusOr<Bytes> DecompressGraphics(const uchar* data, int pos, int size);
|
||||
absl::StatusOr<Bytes> DecompressOverworld(const uchar* data, int pos, int size);
|
||||
absl::StatusOr<Bytes> DecompressOverworld(const std::vector<uint8_t> data,
|
||||
int pos, int size);
|
||||
|
||||
} // namespace lc_lz2
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GFX_COMPRESSION_H
|
||||
281
src/app/gfx/scad_format.cc
Normal file
281
src/app/gfx/scad_format.cc
Normal file
@@ -0,0 +1,281 @@
|
||||
#include "scad_format.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
void FindMetastamp() {
|
||||
int matching_position = -1;
|
||||
bool matched = false;
|
||||
Bytes cgx_rom;
|
||||
Bytes raw_data_;
|
||||
for (int i = 0;
|
||||
i < cgx_rom.size() - sizeof(kMatchedBytes) - kOffsetFromMatchedBytesEnd;
|
||||
i++) {
|
||||
raw_data_.push_back(cgx_rom[i]);
|
||||
bool is_match = std::equal(std::begin(kMatchedBytes),
|
||||
std::end(kMatchedBytes), &cgx_rom[i]);
|
||||
if (is_match) {
|
||||
matching_position = i;
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
int bpp_marker_position =
|
||||
matching_position + sizeof(kMatchedBytes) + kOffsetFromMatchedBytesEnd;
|
||||
int bpp_marker = cgx_rom[bpp_marker_position];
|
||||
std::string bpp_type = (bpp_marker == 0x31) ? "8bpp" : "4bpp";
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status LoadCgx(uint8_t bpp, std::string_view filename,
|
||||
std::vector<uint8_t>& cgx_data,
|
||||
std::vector<uint8_t>& cgx_loaded,
|
||||
std::vector<uint8_t>& cgx_header) {
|
||||
std::ifstream file(filename.data(), std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return absl::NotFoundError("CGX file not found.");
|
||||
}
|
||||
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
cgx_data =
|
||||
std::vector<uint8_t>(file_content.begin(), file_content.end() - 0x500);
|
||||
file.seekg(cgx_data.size() + 0x100);
|
||||
cgx_header = std::vector<uint8_t>((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
if (bpp > 8) {
|
||||
cgx_loaded = gfx::Bpp8SnesToIndexed(cgx_data, 40);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
cgx_loaded = gfx::Bpp8SnesToIndexed(cgx_data, bpp);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
|
||||
std::vector<uint8_t>& map_data) {
|
||||
std::ifstream file(filename.data(), std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return absl::NotFoundError("SCR/PNL/MAP file not found.");
|
||||
}
|
||||
|
||||
// Check if file extension is PNL
|
||||
bool pnl = false;
|
||||
if (filename.find("PNL") != std::string::npos) {
|
||||
std::vector<uint8_t> scr_data;
|
||||
map_data.resize(0x8000);
|
||||
scr_data.resize(0x8000);
|
||||
|
||||
// Read from file for 0x8000 bytes
|
||||
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
scr_data = std::vector<uint8_t>(file_content.begin(), file_content.end());
|
||||
|
||||
int md = 0x100;
|
||||
|
||||
for (int i = input_value * 0x400; i < 0x1000 + input_value * 0x400;
|
||||
i += 2) {
|
||||
auto b1_pos = (i - (input_value * 0x400));
|
||||
map_data[b1_pos] = gfx::TileInfoToShort(
|
||||
gfx::GetTilesInfo((ushort)scr_data[md + (i * 2)]));
|
||||
|
||||
auto b2_pos = (i - (input_value * 0x400) + 1);
|
||||
map_data[b2_pos] = gfx::TileInfoToShort(
|
||||
gfx::GetTilesInfo((ushort)scr_data[md + (i * 2) + 2]));
|
||||
}
|
||||
// 0x900
|
||||
|
||||
} else {
|
||||
int offset = 0;
|
||||
std::vector<uint8_t> scr_data;
|
||||
map_data.resize(0x2000);
|
||||
scr_data.resize(0x2000);
|
||||
|
||||
// read from file for 0x2000 bytes
|
||||
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
scr_data = std::vector<uint8_t>(file_content.begin(), file_content.end());
|
||||
|
||||
for (int i = 0; i < 0x1000 - offset; i++) {
|
||||
map_data[i] = gfx::TileInfoToShort(
|
||||
gfx::GetTilesInfo((ushort)scr_data[((i + offset) * 2)]));
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector<uint8_t>& map_data,
|
||||
std::vector<uint8_t>& map_bitmap_data,
|
||||
std::vector<uint8_t>& cgx_loaded) {
|
||||
const std::vector<uint16_t> dimensions = {0x000, 0x400, 0x800, 0xC00};
|
||||
uint8_t p = 0;
|
||||
for (const auto each_dimension : dimensions) {
|
||||
p = each_dimension;
|
||||
// for each tile on the tile buffer
|
||||
for (int i = 0; i < 0x400; i++) {
|
||||
if (map_data[i + p] != 0xFFFF) {
|
||||
auto t = gfx::GetTilesInfo(map_data[i + p]);
|
||||
|
||||
for (auto yl = 0; yl < 8; yl++) {
|
||||
for (auto xl = 0; xl < 8; xl++) {
|
||||
int mx = xl * (1 - t.horizontal_mirror_) +
|
||||
(7 - xl) * (t.horizontal_mirror_);
|
||||
int my =
|
||||
yl * (1 - t.vertical_mirror_) + (7 - yl) * (t.vertical_mirror_);
|
||||
|
||||
int ty = (t.id_ / 16) * 1024;
|
||||
int tx = (t.id_ % 16) * 8;
|
||||
auto pixel = cgx_loaded[(tx + ty) + (yl * 128) + xl];
|
||||
|
||||
int index = (((i % 32) * 8) + ((i / 32) * 2048) + mx + (my * 256));
|
||||
|
||||
if (bpp != 8) {
|
||||
map_bitmap_data[index] =
|
||||
(uchar)((pixel & 0xFF) + t.palette_ * 16);
|
||||
} else {
|
||||
map_bitmap_data[index] = (uchar)(pixel & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::vector<SDL_Color> DecodeColFile(const std::string_view filename) {
|
||||
std::vector<SDL_Color> decoded_col;
|
||||
std::ifstream file(filename.data(), std::ios::binary | std::ios::ate);
|
||||
|
||||
if (!file.is_open()) {
|
||||
return decoded_col; // Return an empty vector if the file couldn't be
|
||||
// opened.
|
||||
}
|
||||
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> buffer(size);
|
||||
if (file.read(buffer.data(), size)) {
|
||||
buffer.resize(size - 0x200);
|
||||
|
||||
int k = 0;
|
||||
for (size_t i = 0; i < buffer.size() / 2; i++) {
|
||||
uint16_t current_color = static_cast<unsigned char>(buffer[k]) |
|
||||
(static_cast<unsigned char>(buffer[k + 1]) << 8);
|
||||
|
||||
SDL_Color color;
|
||||
color.r = (current_color & 31) << 3;
|
||||
color.g = ((current_color >> 5) & 31) << 3;
|
||||
color.b = ((current_color >> 10) & 31) << 3;
|
||||
color.a = (i & 0xF) == 0 ? 0 : 255;
|
||||
|
||||
decoded_col.push_back(color);
|
||||
k += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded_col;
|
||||
}
|
||||
|
||||
absl::Status DecodeObjFile(
|
||||
std::string_view filename, std::vector<uint8_t>& obj_data,
|
||||
std::vector<uint8_t> actual_obj_data,
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> decoded_obj,
|
||||
std::vector<uint8_t>& decoded_extra_obj, int& obj_loaded) {
|
||||
std::vector<uint8_t> header_obj;
|
||||
int obj_range;
|
||||
int expected_cut;
|
||||
if (obj_loaded == 0) {
|
||||
obj_range = 0x180;
|
||||
expected_cut = 0x500;
|
||||
} else {
|
||||
obj_range = 0x300;
|
||||
expected_cut = 0x900;
|
||||
}
|
||||
|
||||
std::ifstream file(filename.data(), std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return absl::NotFoundError("OBJ file not found.");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
obj_data = file_content;
|
||||
file.close();
|
||||
|
||||
int cut = obj_data.size() & 0x0FFF;
|
||||
actual_obj_data =
|
||||
std::vector<uint8_t>(obj_data.begin(), obj_data.end() - cut);
|
||||
decoded_extra_obj =
|
||||
std::vector<uint8_t>(obj_data.begin() + actual_obj_data.size(),
|
||||
obj_data.begin() + actual_obj_data.size() + 0x100);
|
||||
header_obj = std::vector<uint8_t>(
|
||||
actual_obj_data.begin() + actual_obj_data.size(), actual_obj_data.end());
|
||||
|
||||
if (cut > expected_cut) {
|
||||
std::vector<uint8_t> scad_data;
|
||||
int j = 0;
|
||||
int k = (obj_loaded == 0) ? 63 : 127;
|
||||
|
||||
for (size_t i = 0; i < (actual_obj_data.size() / 6); i++) {
|
||||
std::vector<uint8_t> data = {
|
||||
actual_obj_data[k * 6 + 0 + j], // display
|
||||
actual_obj_data[k * 6 + 1 + j], // unknown
|
||||
actual_obj_data[k * 6 + 2 + j], // y-disp
|
||||
actual_obj_data[k * 6 + 3 + j], // x-disp
|
||||
actual_obj_data[k * 6 + 5 + j], // props
|
||||
actual_obj_data[k * 6 + 4 + j] // tile
|
||||
};
|
||||
scad_data.insert(scad_data.end(), data.begin(), data.end());
|
||||
|
||||
k = k - 1;
|
||||
if (k == -1) {
|
||||
k = (obj_loaded == 0) ? 63 : 127;
|
||||
j = j + ((k + 1) * 6);
|
||||
}
|
||||
}
|
||||
|
||||
int extra_data_range = 0x400 * (obj_loaded + 1) + 0x100;
|
||||
for (int i = 0; i < extra_data_range; i++) {
|
||||
scad_data.push_back(header_obj[i]);
|
||||
}
|
||||
|
||||
obj_data = scad_data;
|
||||
actual_obj_data =
|
||||
std::vector<uint8_t>(obj_data.begin(), obj_data.end() - cut);
|
||||
}
|
||||
|
||||
decoded_obj.clear();
|
||||
for (int k = 0; k < 128; k++) {
|
||||
decoded_obj["frame " + std::to_string(k)] = std::vector<uint8_t>(obj_range);
|
||||
for (int i = 0; i < obj_range; i++) {
|
||||
try {
|
||||
decoded_obj["frame " + std::to_string(k)][i] =
|
||||
obj_data[i + (obj_range * k)];
|
||||
} catch (...) {
|
||||
decoded_obj["frame " + std::to_string(k)][i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
78
src/app/gfx/scad_format.h
Normal file
78
src/app/gfx/scad_format.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef YAZE_APP_GFX_scad_format_H
|
||||
#define YAZE_APP_GFX_scad_format_H
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
// キャラクタ(.SCH)ファイル
|
||||
// ヘッダー情報
|
||||
// アドレス 説明
|
||||
// 00000 - 00003 ファイルタイプ "SCH"
|
||||
// 00004 - 00008 ビットモード "?BIT"
|
||||
// 00009 - 00013 バージョンナンバー "Ver-????\n"
|
||||
// 00014 - 00017 ヘッダーサイズ
|
||||
// 00018 - 0001B ハード名 "SFC" or "CGB" or "GB"
|
||||
// 0001C - 0001C BG/OBJフラグ(AGBの時)
|
||||
// 0001D - 0001D Color Pallette Number
|
||||
// 0001D - 000FF 予約
|
||||
// 00100 - 001FF Color Path
|
||||
struct CgxHeader {
|
||||
char file_type[4];
|
||||
char bit_mode[5];
|
||||
char version_number[9];
|
||||
uint32_t header_size;
|
||||
char hardware_name[4];
|
||||
uint8_t bg_obj_flag;
|
||||
uint8_t color_palette_number;
|
||||
uint8_t reserved[0xE3];
|
||||
uint8_t color_path[0x100];
|
||||
};
|
||||
|
||||
constexpr uint16_t kMatchedBytes[] = {0x4E, 0x41, 0x4B, 0x31, 0x39, 0x38, 0x39};
|
||||
constexpr uint16_t kOffsetFromMatchedBytesEnd = 0x1D;
|
||||
|
||||
void FindMetastamp();
|
||||
|
||||
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
|
||||
std::vector<uint8_t>& map_data);
|
||||
|
||||
absl::Status LoadCgx(uint8_t bpp, std::string_view filename,
|
||||
std::vector<uint8_t>& cgx_data,
|
||||
std::vector<uint8_t>& cgx_loaded,
|
||||
std::vector<uint8_t>& cgx_header);
|
||||
|
||||
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector<uint8_t>& map_bitmap_data,
|
||||
std::vector<uint8_t>& map_data,
|
||||
std::vector<uint8_t>& cgx_loaded);
|
||||
|
||||
std::vector<SDL_Color> DecodeColFile(const std::string_view filename);
|
||||
|
||||
absl::Status DecodeObjFile(
|
||||
std::string_view filename, std::vector<uint8_t>& obj_data,
|
||||
std::vector<uint8_t> actual_obj_data,
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> decoded_obj,
|
||||
std::vector<uint8_t>& decoded_extra_obj, int& obj_loaded);
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GFX_scad_format_H
|
||||
@@ -10,91 +10,129 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/flat_hash_map.h" // for flat_hash_map
|
||||
#include "absl/status/status.h" // for Status
|
||||
#include "app/core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
ushort ConvertRGBtoSNES(const snes_color color) {
|
||||
uchar red = color.red / 8;
|
||||
uchar green = color.green / 8;
|
||||
uchar blue = color.blue / 8;
|
||||
return blue * 1024 + green * 32 + red;
|
||||
// Define a hash map to hold the addresses of different palette groups
|
||||
const absl::flat_hash_map<std::string, uint32_t> paletteGroupAddresses = {
|
||||
{"ow_main", core::overworldPaletteMain},
|
||||
{"ow_aux", core::overworldPaletteAuxialiary},
|
||||
{"ow_animated", core::overworldPaletteAnimated},
|
||||
{"hud", core::hudPalettes},
|
||||
{"global_sprites", core::globalSpritePalettesLW},
|
||||
{"armors", core::armorPalettes},
|
||||
{"swords", core::swordPalettes},
|
||||
{"shields", core::shieldPalettes},
|
||||
{"sprites_aux1", core::spritePalettesAux1},
|
||||
{"sprites_aux2", core::spritePalettesAux2},
|
||||
{"sprites_aux3", core::spritePalettesAux3},
|
||||
{"dungeon_main", core::dungeonMainPalettes},
|
||||
{"grass", core::hardcodedGrassLW},
|
||||
{"3d_object", core::triforcePalette},
|
||||
{"ow_mini_map", core::overworldMiniMapPalettes},
|
||||
};
|
||||
|
||||
// Define a hash map to hold the number of colors in each palette group
|
||||
const absl::flat_hash_map<std::string, uint32_t> paletteGroupColorCounts = {
|
||||
{"ow_main", 35}, {"ow_aux", 21}, {"ow_animated", 7},
|
||||
{"hud", 32}, {"global_sprites", 60}, {"armors", 15},
|
||||
{"swords", 3}, {"shields", 4}, {"sprites_aux1", 7},
|
||||
{"sprites_aux2", 7}, {"sprites_aux3", 7}, {"dungeon_main", 90},
|
||||
{"grass", 1}, {"3d_object", 8}, {"ow_mini_map", 128},
|
||||
};
|
||||
|
||||
constexpr uint16_t SNES_RED_MASK = 32;
|
||||
constexpr uint16_t SNES_GREEN_MASK = 32;
|
||||
constexpr uint16_t SNES_BLUE_MASK = 32;
|
||||
|
||||
constexpr uint16_t SNES_GREEN_SHIFT = 32;
|
||||
constexpr uint16_t SNES_BLUE_SHIFT = 1024;
|
||||
|
||||
uint16_t ConvertRGBtoSNES(const snes_color& color) {
|
||||
uint16_t red = color.red / 8;
|
||||
uint16_t green = color.green / 8;
|
||||
uint16_t blue = color.blue / 8;
|
||||
return (blue * SNES_BLUE_SHIFT) + (green * SNES_GREEN_SHIFT) + red;
|
||||
}
|
||||
|
||||
snes_color ConvertSNEStoRGB(const ushort color) {
|
||||
snes_color toret;
|
||||
|
||||
toret.red = ((color) % 32) * 8;
|
||||
toret.green = ((color / 32) % 32) * 8;
|
||||
toret.blue = ((color / 1024) % 32) * 8;
|
||||
|
||||
toret.red = toret.red + toret.red / 32;
|
||||
toret.green = toret.green + toret.green / 32;
|
||||
toret.blue = toret.blue + toret.blue / 32;
|
||||
return toret;
|
||||
uint16_t ConvertRGBtoSNES(const ImVec4& color) {
|
||||
snes_color new_color;
|
||||
new_color.red = color.x * 255;
|
||||
new_color.green = color.y * 255;
|
||||
new_color.blue = color.z * 255;
|
||||
return ConvertRGBtoSNES(new_color);
|
||||
}
|
||||
|
||||
snes_palette* Extract(const char* data, const unsigned int offset,
|
||||
const unsigned int palette_size) {
|
||||
snes_palette* toret = nullptr; // palette_create(palette_size, 0)
|
||||
unsigned colnum = 0;
|
||||
for (int i = 0; i < palette_size * 2; i += 2) {
|
||||
unsigned short snes_color;
|
||||
snes_color = ((uchar)data[offset + i + 1]) << 8;
|
||||
snes_color = snes_color | ((uchar)data[offset + i]);
|
||||
toret->colors[colnum] = ConvertSNEStoRGB(snes_color);
|
||||
colnum++;
|
||||
snes_color ConvertSNEStoRGB(uint16_t color_snes) {
|
||||
snes_color result;
|
||||
|
||||
result.red = (color_snes % SNES_RED_MASK) * 8;
|
||||
result.green = ((color_snes / SNES_GREEN_MASK) % SNES_GREEN_MASK) * 8;
|
||||
result.blue = ((color_snes / SNES_BLUE_SHIFT) % SNES_BLUE_MASK) * 8;
|
||||
|
||||
result.red += result.red / SNES_RED_MASK;
|
||||
result.green += result.green / SNES_GREEN_MASK;
|
||||
result.blue += result.blue / SNES_BLUE_MASK;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<snes_color> Extract(const char* data, unsigned int offset,
|
||||
unsigned int palette_size) {
|
||||
std::vector<snes_color> palette(palette_size);
|
||||
for (unsigned int i = 0; i < palette_size * 2; i += 2) {
|
||||
uint16_t snes_color = (static_cast<uint8_t>(data[offset + i + 1]) << 8) |
|
||||
static_cast<uint8_t>(data[offset + i]);
|
||||
palette[i / 2] = ConvertSNEStoRGB(snes_color);
|
||||
}
|
||||
return toret;
|
||||
return palette;
|
||||
}
|
||||
|
||||
char* Convert(const snes_palette pal) {
|
||||
char* toret = (char*)malloc(pal.size * 2);
|
||||
for (unsigned int i = 0; i < pal.size; i++) {
|
||||
unsigned short snes_data = ConvertRGBtoSNES(pal.colors[i]);
|
||||
toret[i * 2] = snes_data & 0xFF;
|
||||
toret[i * 2 + 1] = snes_data >> 8;
|
||||
std::vector<char> Convert(const std::vector<snes_color>& palette) {
|
||||
std::vector<char> data(palette.size() * 2);
|
||||
for (unsigned int i = 0; i < palette.size(); i++) {
|
||||
uint16_t snes_data = ConvertRGBtoSNES(palette[i]);
|
||||
data[i * 2] = snes_data & 0xFF;
|
||||
data[i * 2 + 1] = snes_data >> 8;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
SNESColor ReadColorFromROM(int offset, const uchar* rom) {
|
||||
short color = (ushort)((rom[offset + 1]) << 8) | rom[offset];
|
||||
snes_color new_color;
|
||||
new_color.red = (color & 0x1F) * 8;
|
||||
new_color.green = ((color >> 5) & 0x1F) * 8;
|
||||
new_color.blue = ((color >> 10) & 0x1F) * 8;
|
||||
SNESColor snes_color(new_color);
|
||||
return snes_color;
|
||||
}
|
||||
|
||||
SNESColor GetCgxColor(uint16_t color) {
|
||||
ImVec4 rgb;
|
||||
rgb.x = (color & 0x1F) * 8;
|
||||
rgb.y = ((color & 0x3E0) >> 5) * 8;
|
||||
rgb.z = ((color & 0x7C00) >> 10) * 8;
|
||||
SNESColor toret;
|
||||
toret.SetRGB(rgb);
|
||||
return toret;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
std::vector<SNESColor> GetColFileData(uchar* data) {
|
||||
std::vector<SNESColor> colors;
|
||||
colors.reserve(256);
|
||||
colors.resize(256);
|
||||
|
||||
SNESColor::SNESColor() : rgb(ImVec4(0.f, 0.f, 0.f, 0.f)) {}
|
||||
for (int i = 0; i < 512; i += 2) {
|
||||
colors[i / 2] = GetCgxColor((uint16_t)((data[i + 1] << 8) + data[i]));
|
||||
}
|
||||
|
||||
SNESColor::SNESColor(snes_color val) {
|
||||
rgb.x = val.red;
|
||||
rgb.y = val.green;
|
||||
rgb.z = val.blue;
|
||||
}
|
||||
|
||||
SNESColor::SNESColor(ImVec4 val) : rgb(val) {
|
||||
snes_color col;
|
||||
col.red = (uchar)val.x;
|
||||
col.blue = (uchar)val.y;
|
||||
col.green = (uchar)val.z;
|
||||
snes = ConvertRGBtoSNES(col);
|
||||
}
|
||||
|
||||
void SNESColor::setRgb(ImVec4 val) {
|
||||
rgb = val;
|
||||
snes_color col;
|
||||
col.red = val.x;
|
||||
col.blue = val.y;
|
||||
col.green = val.z;
|
||||
snes = ConvertRGBtoSNES(col);
|
||||
}
|
||||
|
||||
void SNESColor::setSNES(snes_color val) {
|
||||
rgb = ImVec4(val.red, val.green, val.blue, 255.f);
|
||||
}
|
||||
|
||||
void SNESColor::setSNES(uint16_t val) {
|
||||
snes = val;
|
||||
snes_color col = ConvertSNEStoRGB(val);
|
||||
rgb = ImVec4(col.red, col.green, col.blue, 0.f);
|
||||
return colors;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -110,10 +148,10 @@ SNESPalette::SNESPalette(char* data) : size_(sizeof(data) / 2) {
|
||||
assert((sizeof(data) % 4 == 0) && (sizeof(data) <= 32));
|
||||
for (unsigned i = 0; i < sizeof(data); i += 2) {
|
||||
SNESColor col;
|
||||
col.snes = static_cast<uchar>(data[i + 1]) << 8;
|
||||
col.snes = col.snes | static_cast<uchar>(data[i]);
|
||||
snes_color mColor = ConvertSNEStoRGB(col.snes);
|
||||
col.rgb = ImVec4(mColor.red, mColor.green, mColor.blue, 1.f);
|
||||
col.SetSNES(static_cast<uchar>(data[i + 1]) << 8);
|
||||
col.SetSNES(col.GetSNES() | static_cast<uchar>(data[i]));
|
||||
snes_color mColor = ConvertSNEStoRGB(col.GetSNES());
|
||||
col.SetRGB(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f));
|
||||
colors.push_back(col);
|
||||
}
|
||||
}
|
||||
@@ -123,10 +161,10 @@ SNESPalette::SNESPalette(const unsigned char* snes_pal)
|
||||
assert((sizeof(snes_pal) % 4 == 0) && (sizeof(snes_pal) <= 32));
|
||||
for (unsigned i = 0; i < sizeof(snes_pal); i += 2) {
|
||||
SNESColor col;
|
||||
col.snes = snes_pal[i + 1] << (uint16_t)8;
|
||||
col.snes = col.snes | snes_pal[i];
|
||||
snes_color mColor = ConvertSNEStoRGB(col.snes);
|
||||
col.rgb = ImVec4(mColor.red, mColor.green, mColor.blue, 1.f);
|
||||
col.SetSNES(snes_pal[i + 1] << (uint16_t)8);
|
||||
col.SetSNES(col.GetSNES() | snes_pal[i]);
|
||||
snes_color mColor = ConvertSNEStoRGB(col.GetSNES());
|
||||
col.SetRGB(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f));
|
||||
colors.push_back(col);
|
||||
}
|
||||
}
|
||||
@@ -134,7 +172,7 @@ SNESPalette::SNESPalette(const unsigned char* snes_pal)
|
||||
SNESPalette::SNESPalette(const std::vector<ImVec4>& cols) {
|
||||
for (const auto& each : cols) {
|
||||
SNESColor scol;
|
||||
scol.setRgb(each);
|
||||
scol.SetRGB(each);
|
||||
colors.push_back(scol);
|
||||
}
|
||||
size_ = cols.size();
|
||||
@@ -143,7 +181,7 @@ SNESPalette::SNESPalette(const std::vector<ImVec4>& cols) {
|
||||
SNESPalette::SNESPalette(const std::vector<snes_color>& cols) {
|
||||
for (const auto& each : cols) {
|
||||
SNESColor scol;
|
||||
scol.setSNES(each);
|
||||
scol.SetSNES(ConvertRGBtoSNES(each));
|
||||
colors.push_back(scol);
|
||||
}
|
||||
size_ = cols.size();
|
||||
@@ -156,32 +194,15 @@ SNESPalette::SNESPalette(const std::vector<SNESColor>& cols) {
|
||||
size_ = cols.size();
|
||||
}
|
||||
|
||||
void SNESPalette::Create(const std::vector<SNESColor>& cols) {
|
||||
for (const auto each : cols) {
|
||||
colors.push_back(each);
|
||||
}
|
||||
size_ = cols.size();
|
||||
}
|
||||
|
||||
char* SNESPalette::encode() {
|
||||
auto data = new char[size_ * 2];
|
||||
for (unsigned int i = 0; i < size_; i++) {
|
||||
std::cout << colors[i].snes << std::endl;
|
||||
data[i * 2] = (char)(colors[i].snes & 0xFF);
|
||||
data[i * 2 + 1] = (char)(colors[i].snes >> 8);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
SDL_Palette* SNESPalette::GetSDL_Palette() {
|
||||
auto sdl_palette = std::make_shared<SDL_Palette>();
|
||||
sdl_palette->ncolors = size_;
|
||||
|
||||
auto color = std::vector<SDL_Color>(size_);
|
||||
for (int i = 0; i < size_; i++) {
|
||||
color[i].r = (uint8_t)colors[i].rgb.x * 100;
|
||||
color[i].g = (uint8_t)colors[i].rgb.y * 100;
|
||||
color[i].b = (uint8_t)colors[i].rgb.z * 100;
|
||||
color[i].r = (uint8_t)colors[i].GetRGB().x * 100;
|
||||
color[i].g = (uint8_t)colors[i].GetRGB().y * 100;
|
||||
color[i].b = (uint8_t)colors[i].GetRGB().z * 100;
|
||||
color[i].a = 0;
|
||||
std::cout << "Color " << i << " added (R:" << color[i].r
|
||||
<< " G:" << color[i].g << " B:" << color[i].b << ")" << std::endl;
|
||||
@@ -190,7 +211,87 @@ SDL_Palette* SNESPalette::GetSDL_Palette() {
|
||||
return sdl_palette.get();
|
||||
}
|
||||
|
||||
PaletteGroup::PaletteGroup(uint8_t mSize) : size(mSize) {}
|
||||
SNESPalette ReadPaletteFromROM(int offset, int num_colors, const uchar* rom) {
|
||||
int color_offset = 0;
|
||||
std::vector<gfx::SNESColor> colors(num_colors);
|
||||
|
||||
while (color_offset < num_colors) {
|
||||
short color = (ushort)((rom[offset + 1]) << 8) | rom[offset];
|
||||
gfx::snes_color new_color;
|
||||
new_color.red = (color & 0x1F) * 8;
|
||||
new_color.green = ((color >> 5) & 0x1F) * 8;
|
||||
new_color.blue = ((color >> 10) & 0x1F) * 8;
|
||||
colors[color_offset].SetSNES(ConvertRGBtoSNES(new_color));
|
||||
if (color_offset == 0) {
|
||||
colors[color_offset].SetTransparent(true);
|
||||
}
|
||||
color_offset++;
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
gfx::SNESPalette palette(colors);
|
||||
return palette;
|
||||
}
|
||||
|
||||
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
|
||||
size_t color_index) {
|
||||
// Retrieve the base address for the palette group
|
||||
uint32_t base_address = paletteGroupAddresses.at(group_name);
|
||||
|
||||
// Retrieve the number of colors for each palette in the group
|
||||
uint32_t colors_per_palette = paletteGroupColorCounts.at(group_name);
|
||||
|
||||
// Calculate the address for thes specified color in the ROM
|
||||
uint32_t address = base_address + (palette_index * colors_per_palette * 2) +
|
||||
(color_index * 2);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
std::array<float, 4> ToFloatArray(const SNESColor& color) {
|
||||
std::array<float, 4> colorArray;
|
||||
colorArray[0] = color.GetRGB().x / 255.0f;
|
||||
colorArray[1] = color.GetRGB().y / 255.0f;
|
||||
colorArray[2] = color.GetRGB().z / 255.0f;
|
||||
colorArray[3] = color.GetRGB().w;
|
||||
return colorArray;
|
||||
}
|
||||
|
||||
PaletteGroup::PaletteGroup(uint8_t mSize) : size_(mSize) {}
|
||||
|
||||
PaletteGroup CreatePaletteGroupFromColFile(
|
||||
std::vector<SNESColor>& palette_rows) {
|
||||
PaletteGroup toret;
|
||||
|
||||
for (int i = 0; i < palette_rows.size(); i += 8) {
|
||||
SNESPalette palette;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
palette.AddColor(palette_rows[i + j].GetRomRGB());
|
||||
}
|
||||
toret.AddPalette(palette);
|
||||
}
|
||||
return toret;
|
||||
}
|
||||
|
||||
// Take a SNESPalette with N many colors and divide it into palettes of 8 colors
|
||||
// each
|
||||
PaletteGroup CreatePaletteGroupFromLargePalette(SNESPalette& palette) {
|
||||
PaletteGroup toret;
|
||||
|
||||
std::cout << "Palette size is " << palette.size() << std::endl;
|
||||
|
||||
for (int i = 0; i < palette.size(); i += 8) {
|
||||
SNESPalette new_palette;
|
||||
if (i + 8 < palette.size()) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
new_palette.AddColor(palette[i + j]);
|
||||
}
|
||||
}
|
||||
|
||||
toret.AddPalette(new_palette);
|
||||
}
|
||||
return toret;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/casts.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -18,47 +20,111 @@ namespace app {
|
||||
namespace gfx {
|
||||
|
||||
struct snes_color {
|
||||
uchar red;
|
||||
uchar blue;
|
||||
uchar green;
|
||||
uint16_t red; /**< Red component of the color. */
|
||||
uint16_t blue; /**< Blue component of the color. */
|
||||
uint16_t green; /**< Green component of the color. */
|
||||
};
|
||||
using snes_color = struct snes_color;
|
||||
|
||||
struct snes_palette {
|
||||
uint id;
|
||||
uint size;
|
||||
snes_color* colors;
|
||||
uint id; /**< ID of the palette. */
|
||||
uint size; /**< Size of the palette. */
|
||||
snes_color* colors; /**< Pointer to the colors in the palette. */
|
||||
};
|
||||
using snes_palette = struct snes_palette;
|
||||
|
||||
ushort ConvertRGBtoSNES(const snes_color color);
|
||||
snes_color ConvertSNEStoRGB(const ushort snes_color);
|
||||
snes_palette* Extract(const char* data, const unsigned int offset,
|
||||
const unsigned int palette_size);
|
||||
char* Convert(const snes_palette pal);
|
||||
uint16_t ConvertRGBtoSNES(const snes_color& color);
|
||||
uint16_t ConvertRGBtoSNES(const ImVec4& color);
|
||||
snes_color ConvertSNEStoRGB(uint16_t snes_color);
|
||||
|
||||
/**
|
||||
* @brief Extracts a vector of SNES colors from a data buffer.
|
||||
*
|
||||
* @param data The data buffer to extract from.
|
||||
* @param offset The offset in the buffer to start extracting from.
|
||||
* @param palette_size The size of the palette to extract.
|
||||
* @return A vector of SNES colors extracted from the buffer.
|
||||
*/
|
||||
std::vector<snes_color> Extract(const char* data, unsigned int offset,
|
||||
unsigned int palette_size);
|
||||
|
||||
/**
|
||||
* @brief Converts a vector of SNES colors to a vector of characters.
|
||||
*
|
||||
* @param palette The vector of SNES colors to convert.
|
||||
* @return A vector of characters representing the converted SNES colors.
|
||||
*/
|
||||
std::vector<char> Convert(const std::vector<snes_color>& palette);
|
||||
|
||||
struct SNESColor {
|
||||
SNESColor();
|
||||
explicit SNESColor(ImVec4);
|
||||
explicit SNESColor(snes_color);
|
||||
SNESColor() : rgb(0.f, 0.f, 0.f, 0.f), snes(0) {}
|
||||
|
||||
void setRgb(ImVec4);
|
||||
void setSNES(snes_color);
|
||||
void setSNES(uint16_t);
|
||||
void setTransparent(bool t) { transparent = t; }
|
||||
|
||||
auto RGB() {
|
||||
return ImVec4(rgb.x / 255, rgb.y / 255, rgb.z / 255, rgb.w);
|
||||
explicit SNESColor(const ImVec4 val) : rgb(val) {
|
||||
snes_color color;
|
||||
color.red = val.x / 255;
|
||||
color.green = val.y / 255;
|
||||
color.blue = val.z / 255;
|
||||
snes = ConvertRGBtoSNES(color);
|
||||
}
|
||||
|
||||
bool transparent = false;
|
||||
uint16_t snes = 0;
|
||||
explicit SNESColor(const snes_color val)
|
||||
: rgb(val.red, val.green, val.blue, 255.f),
|
||||
snes(ConvertRGBtoSNES(val)),
|
||||
rom_color(val) {}
|
||||
|
||||
ImVec4 GetRGB() const { return rgb; }
|
||||
void SetRGB(const ImVec4 val) {
|
||||
rgb.x = val.x / 255;
|
||||
rgb.y = val.y / 255;
|
||||
rgb.z = val.z / 255;
|
||||
snes_color color;
|
||||
color.red = val.x;
|
||||
color.green = val.y;
|
||||
color.blue = val.z;
|
||||
rom_color = color;
|
||||
snes = ConvertRGBtoSNES(color);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
snes_color GetRomRGB() const { return rom_color; }
|
||||
|
||||
uint16_t GetSNES() const { return snes; }
|
||||
void SetSNES(uint16_t val) {
|
||||
snes = val;
|
||||
snes_color col = ConvertSNEStoRGB(val);
|
||||
rgb = ImVec4(col.red, col.green, col.blue, 0.f);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
bool IsModified() const { return modified; }
|
||||
bool IsTransparent() const { return transparent; }
|
||||
void SetTransparent(bool t) { transparent = t; }
|
||||
void SetModified(bool m) { modified = m; }
|
||||
|
||||
private:
|
||||
ImVec4 rgb;
|
||||
uint16_t snes;
|
||||
snes_color rom_color;
|
||||
bool modified = false;
|
||||
bool transparent = false;
|
||||
};
|
||||
|
||||
gfx::SNESColor ReadColorFromROM(int offset, const uchar* rom);
|
||||
|
||||
SNESColor GetCgxColor(uint16_t color);
|
||||
std::vector<SNESColor> GetColFileData(uchar* data);
|
||||
|
||||
class SNESPalette {
|
||||
public:
|
||||
template <typename T>
|
||||
explicit SNESPalette(const std::vector<T>& data) {
|
||||
for (const auto& item : data) {
|
||||
colors.push_back(SNESColor(item));
|
||||
}
|
||||
}
|
||||
|
||||
SNESPalette() = default;
|
||||
|
||||
explicit SNESPalette(uint8_t mSize);
|
||||
explicit SNESPalette(char* snesPal);
|
||||
explicit SNESPalette(const unsigned char* snes_pal);
|
||||
@@ -66,50 +132,146 @@ class SNESPalette {
|
||||
explicit SNESPalette(const std::vector<snes_color>&);
|
||||
explicit SNESPalette(const std::vector<SNESColor>&);
|
||||
|
||||
void Create(const std::vector<SNESColor>&);
|
||||
void AddColor(SNESColor color) { colors.push_back(color); }
|
||||
auto GetColor(int i) const { return colors[i]; }
|
||||
SDL_Palette* GetSDL_Palette();
|
||||
|
||||
SNESColor operator[](int i) {
|
||||
void Create(const std::vector<SNESColor>& cols) {
|
||||
for (const auto& each : cols) {
|
||||
colors.push_back(each);
|
||||
}
|
||||
size_ = cols.size();
|
||||
}
|
||||
|
||||
void AddColor(SNESColor color) {
|
||||
colors.push_back(color);
|
||||
size_++;
|
||||
}
|
||||
|
||||
void AddColor(snes_color color) {
|
||||
colors.emplace_back(color);
|
||||
size_++;
|
||||
}
|
||||
|
||||
auto GetColor(int i) const {
|
||||
if (i > size_) {
|
||||
std::cout << "SNESPalette: Index out of bounds" << std::endl;
|
||||
return colors[0];
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
}
|
||||
return colors[i];
|
||||
}
|
||||
|
||||
char* encode();
|
||||
SDL_Palette* GetSDL_Palette();
|
||||
void Clear() {
|
||||
colors.clear();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
int size_ = 0;
|
||||
std::vector<SNESColor> colors;
|
||||
auto size() const { return colors.size(); }
|
||||
|
||||
SNESColor& operator[](int i) {
|
||||
if (i > size_) {
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
}
|
||||
return colors[i];
|
||||
}
|
||||
|
||||
void operator()(int i, const SNESColor& color) {
|
||||
if (i >= size_) {
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
}
|
||||
colors[i] = color;
|
||||
}
|
||||
|
||||
void operator()(int i, const ImVec4& color) {
|
||||
if (i >= size_) {
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
}
|
||||
colors[i].SetRGB(color);
|
||||
colors[i].SetModified(true);
|
||||
}
|
||||
|
||||
SNESPalette sub_palette(int start, int end) const {
|
||||
SNESPalette pal;
|
||||
for (int i = start; i < end; i++) {
|
||||
pal.AddColor(colors[i]);
|
||||
}
|
||||
return pal;
|
||||
}
|
||||
|
||||
private:
|
||||
int size_ = 0; /**< The size of the palette. */
|
||||
std::vector<SNESColor> colors; /**< The colors in the palette. */
|
||||
};
|
||||
|
||||
SNESPalette ReadPaletteFromROM(int offset, int num_colors, const uchar* rom);
|
||||
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
|
||||
size_t color_index);
|
||||
std::array<float, 4> ToFloatArray(const SNESColor& color);
|
||||
|
||||
struct PaletteGroup {
|
||||
PaletteGroup() = default;
|
||||
|
||||
explicit PaletteGroup(uint8_t mSize);
|
||||
void AddPalette(SNESPalette pal) {
|
||||
|
||||
absl::Status AddPalette(SNESPalette pal) {
|
||||
palettes.emplace_back(pal);
|
||||
size = palettes.size();
|
||||
size_ = palettes.size();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
void AddColor(SNESColor color) {
|
||||
if (size == 0) {
|
||||
SNESPalette empty_pal;
|
||||
palettes.emplace_back(empty_pal);
|
||||
|
||||
absl::Status AddColor(SNESColor color) {
|
||||
if (size_ == 0) {
|
||||
palettes.emplace_back();
|
||||
}
|
||||
palettes[0].AddColor(color);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
palettes.clear();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
auto size() const { return palettes.size(); }
|
||||
|
||||
SNESPalette operator[](int i) {
|
||||
if (i > size) {
|
||||
if (i > size_) {
|
||||
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
|
||||
return palettes[0];
|
||||
}
|
||||
return palettes[i];
|
||||
}
|
||||
int size = 0;
|
||||
|
||||
const SNESPalette& operator[](int i) const {
|
||||
if (i > size_) {
|
||||
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
|
||||
return palettes[0];
|
||||
}
|
||||
return palettes[i];
|
||||
}
|
||||
|
||||
absl::Status operator()(int i, const SNESColor& color) {
|
||||
if (i >= size_) {
|
||||
return absl::InvalidArgumentError("PaletteGroup: Index out of bounds");
|
||||
}
|
||||
palettes[i](0, color);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status operator()(int i, const ImVec4& color) {
|
||||
if (i >= size_) {
|
||||
return absl::InvalidArgumentError("PaletteGroup: Index out of bounds");
|
||||
}
|
||||
palettes[i](0, color);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
int size_ = 0;
|
||||
std::vector<SNESPalette> palettes;
|
||||
};
|
||||
|
||||
PaletteGroup CreatePaletteGroupFromColFile(std::vector<SNESColor>& colors);
|
||||
|
||||
PaletteGroup CreatePaletteGroupFromLargePalette(SNESPalette& palette);
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -9,6 +9,327 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
tile8 UnpackBppTile(const Bytes& data, const uint32_t offset,
|
||||
const uint32_t bpp) {
|
||||
tile8 tile;
|
||||
assert(bpp >= 1 && bpp <= 8);
|
||||
unsigned int bpp_pos[8]; // More for conveniance and readibility
|
||||
for (int col = 0; col < 8; col++) {
|
||||
for (int row = 0; row < 8; row++) {
|
||||
if (bpp == 1) {
|
||||
tile.data[col * 8 + row] = (data[offset + col] >> (7 - row)) & 0x01;
|
||||
continue;
|
||||
}
|
||||
/* SNES bpp format interlace each byte of the first 2 bitplanes.
|
||||
* | byte 1 of first bitplane | byte 1 of second bitplane |
|
||||
* | byte 2 of first bitplane | byte 2 of second bitplane | ..
|
||||
*/
|
||||
bpp_pos[0] = offset + col * 2;
|
||||
bpp_pos[1] = offset + col * 2 + 1;
|
||||
char mask = 1 << (7 - row);
|
||||
tile.data[col * 8 + row] = (data[bpp_pos[0]] & mask) == mask;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[1]] & mask) == mask)
|
||||
<< 1;
|
||||
if (bpp == 3) {
|
||||
// When we have 3 bitplanes, the bytes for the third bitplane are after
|
||||
// the 16 bytes of the 2 bitplanes.
|
||||
bpp_pos[2] = offset + 16 + col;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[2]] & mask) == mask)
|
||||
<< 2;
|
||||
}
|
||||
if (bpp >= 4) {
|
||||
// For 4 bitplanes, the 2 added bitplanes are interlaced like the first
|
||||
// two.
|
||||
bpp_pos[2] = offset + 16 + col * 2;
|
||||
bpp_pos[3] = offset + 16 + col * 2 + 1;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[2]] & mask) == mask)
|
||||
<< 2;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[3]] & mask) == mask)
|
||||
<< 3;
|
||||
}
|
||||
if (bpp == 8) {
|
||||
bpp_pos[4] = offset + 32 + col * 2;
|
||||
bpp_pos[5] = offset + 32 + col * 2 + 1;
|
||||
bpp_pos[6] = offset + 48 + col * 2;
|
||||
bpp_pos[7] = offset + 48 + col * 2 + 1;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[4]] & mask) == mask)
|
||||
<< 4;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[5]] & mask) == mask)
|
||||
<< 5;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[6]] & mask) == mask)
|
||||
<< 6;
|
||||
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[7]] & mask) == mask)
|
||||
<< 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
Bytes PackBppTile(const tile8& tile, const uint32_t bpp) {
|
||||
// Allocate memory for output data
|
||||
std::vector<uchar> output(bpp * 8, 0); // initialized with 0
|
||||
unsigned maxcolor = 2 << bpp;
|
||||
|
||||
// Iterate over all columns and rows of the tile
|
||||
for (unsigned int col = 0; col < 8; col++) {
|
||||
for (unsigned int row = 0; row < 8; row++) {
|
||||
uchar color = tile.data[col * 8 + row];
|
||||
if (color > maxcolor) {
|
||||
throw std::invalid_argument("Invalid color value.");
|
||||
}
|
||||
|
||||
// 1bpp format
|
||||
if (bpp == 1) output[col] += (uchar)((color & 1) << (7 - row));
|
||||
|
||||
// 2bpp format
|
||||
if (bpp >= 2) {
|
||||
output[col * 2] += (uchar)((color & 1) << (7 - row));
|
||||
output[col * 2 + 1] += (uchar)((uchar)((color & 2) == 2) << (7 - row));
|
||||
}
|
||||
|
||||
// 3bpp format
|
||||
if (bpp == 3)
|
||||
output[16 + col] += (uchar)(((color & 4) == 4) << (7 - row));
|
||||
|
||||
// 4bpp format
|
||||
if (bpp >= 4) {
|
||||
output[16 + col * 2] += (uchar)(((color & 4) == 4) << (7 - row));
|
||||
output[16 + col * 2 + 1] += (uchar)(((color & 8) == 8) << (7 - row));
|
||||
}
|
||||
|
||||
// 8bpp format
|
||||
if (bpp == 8) {
|
||||
output[32 + col * 2] += (uchar)(((color & 16) == 16) << (7 - row));
|
||||
output[32 + col * 2 + 1] += (uchar)(((color & 32) == 32) << (7 - row));
|
||||
output[48 + col * 2] += (uchar)(((color & 64) == 64) << (7 - row));
|
||||
output[48 + col * 2 + 1] +=
|
||||
(uchar)(((color & 128) == 128) << (7 - row));
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<uchar> ConvertBpp(const std::vector<uchar>& tiles,
|
||||
uint32_t from_bpp, uint32_t to_bpp) {
|
||||
unsigned int nb_tile = tiles.size() / (from_bpp * 8);
|
||||
std::vector<uchar> converted(nb_tile * to_bpp * 8);
|
||||
|
||||
for (unsigned int i = 0; i < nb_tile; i++) {
|
||||
tile8 tile = UnpackBppTile(tiles, i * from_bpp * 8, from_bpp);
|
||||
std::vector<uchar> packed_tile = PackBppTile(tile, to_bpp);
|
||||
std::memcpy(converted.data() + i * to_bpp * 8, packed_tile.data(),
|
||||
to_bpp * 8);
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
std::vector<uchar> Convert3bppTo4bpp(const std::vector<uchar>& tiles) {
|
||||
return ConvertBpp(tiles, 3, 4);
|
||||
}
|
||||
|
||||
std::vector<uchar> Convert4bppTo3bpp(const std::vector<uchar>& tiles) {
|
||||
return ConvertBpp(tiles, 4, 3);
|
||||
}
|
||||
|
||||
Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
|
||||
int xx = 0; // positions where we are at on the sheet
|
||||
int yy = 0;
|
||||
int pos = 0;
|
||||
int ypos = 0;
|
||||
int num_tiles = 64;
|
||||
int buffer_size = 0x1000;
|
||||
if (bpp == 2) {
|
||||
bpp = 16;
|
||||
num_tiles = 128;
|
||||
buffer_size = 0x2000;
|
||||
} else if (bpp == 3) {
|
||||
bpp = 24;
|
||||
}
|
||||
Bytes sheet_buffer_out(buffer_size);
|
||||
|
||||
for (int i = 0; i < num_tiles; i++) { // for each tiles, 16 per line
|
||||
for (int y = 0; y < 8; y++) { // for each line
|
||||
for (int x = 0; x < 8; x++) { //[0] + [1] + [16]
|
||||
auto b1 = (sheet[(y * 2) + (bpp * pos)] & (kGraphicsBitmap[x]));
|
||||
auto b2 = (sheet[((y * 2) + (bpp * pos)) + 1] & (kGraphicsBitmap[x]));
|
||||
auto b3 = (sheet[(16 + y) + (bpp * pos)] & (kGraphicsBitmap[x]));
|
||||
unsigned char b = 0;
|
||||
if (b1 != 0) {
|
||||
b |= 1;
|
||||
}
|
||||
if (b2 != 0) {
|
||||
b |= 2;
|
||||
}
|
||||
if (b3 != 0 && bpp != 16) {
|
||||
b |= 4;
|
||||
}
|
||||
sheet_buffer_out[x + xx + (y * 128) + (yy * 1024)] = b;
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
ypos++;
|
||||
xx += 8;
|
||||
if (ypos >= 16) {
|
||||
yy++;
|
||||
xx = 0;
|
||||
ypos = 0;
|
||||
}
|
||||
}
|
||||
return sheet_buffer_out;
|
||||
}
|
||||
|
||||
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp) {
|
||||
// 3BPP
|
||||
// [r0,bp1],[r0,bp2],[r1,bp1],[r1,bp2],[r2,bp1],[r2,bp2],[r3,bp1],[r3,bp2]
|
||||
// [r4,bp1],[r4,bp2],[r5,bp1],[r5,bp2],[r6,bp1],[r6,bp2],[r7,bp1],[r7,bp2]
|
||||
// [r0,bp3],[r0,bp4],[r1,bp3],[r1,bp4],[r2,bp3],[r2,bp4],[r3,bp3],[r3,bp4]
|
||||
// [r4,bp3],[r4,bp4],[r5,bp3],[r5,bp4],[r6,bp3],[r6,bp4],[r7,bp3],[r7,bp4]
|
||||
// [r0,bp5],[r0,bp6],[r1,bp5],[r1,bp6],[r2,bp5],[r2,bp6],[r3,bp5],[r3,bp6]
|
||||
// [r4,bp5],[r4,bp6],[r5,bp5],[r5,bp6],[r6,bp5],[r6,bp6],[r7,bp5],[r7,bp6]
|
||||
// [r0,bp7],[r0,bp8],[r1,bp7],[r1,bp8],[r2,bp7],[r2,bp8],[r3,bp7],[r3,bp8]
|
||||
// [r4,bp7],[r4,bp8],[r5,bp7],[r5,bp8],[r6,bp7],[r6,bp8],[r7,bp7],[r7,bp8]
|
||||
|
||||
// 16 tiles = 1024 bytes
|
||||
auto buffer = Bytes(data.size());
|
||||
std::vector<std::vector<uint8_t>> bitmap_data;
|
||||
bitmap_data.resize(0x80);
|
||||
for (auto& each : bitmap_data) {
|
||||
each.reserve(0x800);
|
||||
}
|
||||
int yy = 0;
|
||||
int xx = 0;
|
||||
int pos = 0;
|
||||
|
||||
const uint16_t sheet_width = 128;
|
||||
|
||||
// 64 = 4096 bytes
|
||||
// 16 = 1024?
|
||||
int ypos = 0;
|
||||
// for each tiles //16 per lines
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
// for each lines
|
||||
for (int y = 0; y < 8; y++) {
|
||||
//[0] + [1] + [16]
|
||||
for (int x = 0; x < 8; x++) {
|
||||
const uint16_t bitmask[] = {0x80, 0x40, 0x20, 0x10,
|
||||
0x08, 0x04, 0x02, 0x01};
|
||||
auto b1 = (data[(y * 2) + ((bpp * 8) * pos)] & (bitmask[x]));
|
||||
auto b2 = (data[((y * 2) + ((bpp * 8) * pos)) + 1] & (bitmask[x]));
|
||||
auto b3 = (data[(y * 2) + ((bpp * 8) * pos) + 16] & (bitmask[x]));
|
||||
auto b4 = (data[(y * 2) + ((bpp * 8) * pos) + 17] & (bitmask[x]));
|
||||
auto b5 = (data[(y * 2) + ((bpp * 8) * pos) + 32] & (bitmask[x]));
|
||||
auto b6 = (data[(y * 2) + ((bpp * 8) * pos) + 33] & (bitmask[x]));
|
||||
auto b7 = (data[(y * 2) + ((bpp * 8) * pos) + 48] & (bitmask[x]));
|
||||
auto b8 = (data[(y * 2) + ((bpp * 8) * pos) + 49] & (bitmask[x]));
|
||||
|
||||
auto b = 0;
|
||||
if (b1 != 0) {
|
||||
b |= 1;
|
||||
}
|
||||
if (b2 != 0) {
|
||||
b |= 2;
|
||||
}
|
||||
if (bpp >= 4) {
|
||||
if (b3 != 0) {
|
||||
b |= 4;
|
||||
}
|
||||
if (b4 != 0) {
|
||||
b |= 8;
|
||||
}
|
||||
}
|
||||
if (bpp >= 8) {
|
||||
if (b5 != 0) {
|
||||
b |= 0x10;
|
||||
}
|
||||
if (b6 != 0) {
|
||||
b |= 0x20;
|
||||
}
|
||||
if (b7 != 0) {
|
||||
b |= 0x40;
|
||||
}
|
||||
if (b8 != 0) {
|
||||
b |= 0x80;
|
||||
}
|
||||
}
|
||||
// bitmap_data[((x + xx) * sheet_width) + y + (yy * 8)] = b;
|
||||
bitmap_data[x + xx][y + (yy * 8)] = b;
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
ypos++;
|
||||
xx += 8;
|
||||
if (ypos >= 16) {
|
||||
yy++;
|
||||
xx = 0;
|
||||
ypos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (int y = 0; y < (data.size() / 64); y++) {
|
||||
for (int x = 0; x < sheet_width; x++) { // 128 assumption
|
||||
if (n < data.size()) {
|
||||
// buffer[n] = bitmap_data[(x * sheet_width) + y];
|
||||
buffer[n] = bitmap_data[x][y];
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint16_t TileInfoToWord(TileInfo tile_info) {
|
||||
uint16_t result = 0;
|
||||
|
||||
// Copy the id_ value
|
||||
result |= tile_info.id_ & 0x3FF; // ids are 10 bits
|
||||
|
||||
// Set the vertical_mirror_, horizontal_mirror_, and over_ flags
|
||||
result |= (tile_info.vertical_mirror_ ? 1 : 0) << 15;
|
||||
result |= (tile_info.horizontal_mirror_ ? 1 : 0) << 14;
|
||||
result |= (tile_info.over_ ? 1 : 0) << 13;
|
||||
|
||||
// Set the palette_
|
||||
result |= (tile_info.palette_ & 0x07) << 10; // palettes are 3 bits
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TileInfo WordToTileInfo(uint16_t word) {
|
||||
// Extract the id_ value
|
||||
uint16_t id = word & 0x3FF; // ids are 10 bits
|
||||
|
||||
// Extract the vertical_mirror_, horizontal_mirror_, and over_ flags
|
||||
bool vertical_mirror = (word >> 15) & 0x01;
|
||||
bool horizontal_mirror = (word >> 14) & 0x01;
|
||||
bool over = (word >> 13) & 0x01;
|
||||
|
||||
// Extract the palette_
|
||||
uint8_t palette = (word >> 10) & 0x07; // palettes are 3 bits
|
||||
|
||||
return TileInfo(id, palette, vertical_mirror, horizontal_mirror, over);
|
||||
}
|
||||
|
||||
ushort TileInfoToShort(TileInfo tile_info) {
|
||||
ushort result = 0;
|
||||
|
||||
// Copy the id_ value
|
||||
result |= tile_info.id_ & 0x3FF; // ids are 10 bits
|
||||
|
||||
// Set the vertical_mirror_, horizontal_mirror_, and over_ flags
|
||||
result |= (tile_info.vertical_mirror_ ? 1 : 0) << 10;
|
||||
result |= (tile_info.horizontal_mirror_ ? 1 : 0) << 11;
|
||||
result |= (tile_info.over_ ? 1 : 0) << 12;
|
||||
|
||||
// Set the palette_
|
||||
result |= (tile_info.palette_ & 0x07) << 13; // palettes are 3 bits
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TileInfo GetTilesInfo(ushort tile) {
|
||||
// vhopppcc cccccccc
|
||||
bool o = false;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define YAZE_APP_GFX_SNES_TILE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/constants.h"
|
||||
@@ -10,13 +11,30 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
constexpr uchar kGraphicsBitmap[8] = {0x80, 0x40, 0x20, 0x10,
|
||||
0x08, 0x04, 0x02, 0x01};
|
||||
|
||||
Bytes SnesTo8bppSheet(Bytes sheet, int bpp);
|
||||
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp = 0);
|
||||
|
||||
struct tile8 {
|
||||
unsigned int id;
|
||||
uint32_t id;
|
||||
char data[64];
|
||||
unsigned int palette_id;
|
||||
uint32_t palette_id;
|
||||
};
|
||||
using tile8 = struct tile8;
|
||||
|
||||
tile8 UnpackBppTile(const Bytes& data, const uint32_t offset,
|
||||
const uint32_t bpp);
|
||||
|
||||
Bytes PackBppTile(const tile8& tile, const uint32_t bpp);
|
||||
|
||||
std::vector<uchar> ConvertBpp(const std::vector<uchar>& tiles,
|
||||
uint32_t from_bpp, uint32_t to_bpp);
|
||||
|
||||
std::vector<uchar> Convert3bppTo4bpp(const std::vector<uchar>& tiles);
|
||||
std::vector<uchar> Convert4bppTo3bpp(const std::vector<uchar>& tiles);
|
||||
|
||||
// vhopppcc cccccccc
|
||||
// [0, 1]
|
||||
// [2, 3]
|
||||
@@ -34,19 +52,66 @@ class TileInfo {
|
||||
vertical_mirror_(v),
|
||||
horizontal_mirror_(h),
|
||||
palette_(palette) {}
|
||||
|
||||
bool operator==(const TileInfo& other) const {
|
||||
return id_ == other.id_ && over_ == other.over_ &&
|
||||
vertical_mirror_ == other.vertical_mirror_ &&
|
||||
horizontal_mirror_ == other.horizontal_mirror_ &&
|
||||
palette_ == other.palette_;
|
||||
}
|
||||
};
|
||||
|
||||
uint16_t TileInfoToWord(TileInfo tile_info);
|
||||
TileInfo WordToTileInfo(uint16_t word);
|
||||
ushort TileInfoToShort(TileInfo tile_info);
|
||||
|
||||
TileInfo GetTilesInfo(ushort tile);
|
||||
|
||||
class Tile32 {
|
||||
public:
|
||||
ushort tile0_;
|
||||
ushort tile1_;
|
||||
ushort tile2_;
|
||||
ushort tile3_;
|
||||
uint16_t tile0_;
|
||||
uint16_t tile1_;
|
||||
uint16_t tile2_;
|
||||
uint16_t tile3_;
|
||||
|
||||
Tile32(ushort t0, ushort t1, ushort t2, ushort t3)
|
||||
// Default constructor
|
||||
Tile32() : tile0_(0), tile1_(0), tile2_(0), tile3_(0) {}
|
||||
|
||||
// Parameterized constructor
|
||||
Tile32(uint16_t t0, uint16_t t1, uint16_t t2, uint16_t t3)
|
||||
: tile0_(t0), tile1_(t1), tile2_(t2), tile3_(t3) {}
|
||||
|
||||
// Copy constructor
|
||||
Tile32(const Tile32& other)
|
||||
: tile0_(other.tile0_),
|
||||
tile1_(other.tile1_),
|
||||
tile2_(other.tile2_),
|
||||
tile3_(other.tile3_) {}
|
||||
|
||||
// Constructor from packed value
|
||||
Tile32(uint64_t packedVal) {
|
||||
tile0_ = (packedVal >> 48) & 0xFFFF;
|
||||
tile1_ = (packedVal >> 32) & 0xFFFF;
|
||||
tile2_ = (packedVal >> 16) & 0xFFFF;
|
||||
tile3_ = packedVal & 0xFFFF;
|
||||
}
|
||||
|
||||
// Equality operator
|
||||
bool operator==(const Tile32& other) const {
|
||||
return tile0_ == other.tile0_ && tile1_ == other.tile1_ &&
|
||||
tile2_ == other.tile2_ && tile3_ == other.tile3_;
|
||||
}
|
||||
|
||||
// Inequality operator
|
||||
bool operator!=(const Tile32& other) const { return !(*this == other); }
|
||||
|
||||
// Get packed uint64_t representation
|
||||
uint64_t GetPackedValue() const {
|
||||
return (static_cast<uint64_t>(tile0_) << 48) |
|
||||
(static_cast<uint64_t>(tile1_) << 32) |
|
||||
(static_cast<uint64_t>(tile2_) << 16) |
|
||||
static_cast<uint64_t>(tile3_);
|
||||
}
|
||||
};
|
||||
|
||||
class Tile16 {
|
||||
@@ -65,6 +130,13 @@ class Tile16 {
|
||||
tiles_info.push_back(tile2_);
|
||||
tiles_info.push_back(tile3_);
|
||||
}
|
||||
|
||||
bool operator==(const Tile16& other) const {
|
||||
return tile0_ == other.tile0_ && tile1_ == other.tile1_ &&
|
||||
tile2_ == other.tile2_ && tile3_ == other.tile3_;
|
||||
}
|
||||
|
||||
bool operator!=(const Tile16& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
class OAMTile {
|
||||
|
||||
Reference in New Issue
Block a user