backend-infra-engineer: Pre-0.2.2 2024 Q2 snapshot
This commit is contained in:
@@ -325,54 +325,76 @@ void Bitmap::LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||
}
|
||||
|
||||
// Convert SNESPalette to SDL_Palette for surface.
|
||||
void Bitmap::ApplyPalette(const SnesPalette &palette) {
|
||||
absl::Status Bitmap::ApplyPalette(const SnesPalette &palette) {
|
||||
if (surface_ == nullptr) {
|
||||
return absl::FailedPreconditionError("Surface is null");
|
||||
}
|
||||
|
||||
if (surface_->format == nullptr || surface_->format->palette == nullptr) {
|
||||
return absl::FailedPreconditionError("Surface format or palette is null");
|
||||
}
|
||||
|
||||
palette_ = palette;
|
||||
|
||||
SDL_Palette *sdlPalette = surface_->format->palette;
|
||||
if (sdlPalette == nullptr) {
|
||||
return absl::InternalError("Failed to get SDL palette");
|
||||
}
|
||||
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
|
||||
for (int i = 0; i < palette.size(); ++i) {
|
||||
if (palette.GetColor(i).is_transparent()) {
|
||||
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;
|
||||
ASSIGN_OR_RETURN(gfx::SnesColor pal_color, palette.GetColor(i));
|
||||
if (pal_color.is_transparent()) {
|
||||
sdlPalette->colors[i].r = 0;
|
||||
sdlPalette->colors[i].g = 0;
|
||||
sdlPalette->colors[i].b = 0;
|
||||
sdlPalette->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;
|
||||
sdlPalette->colors[i].r = pal_color.rgb().x;
|
||||
sdlPalette->colors[i].g = pal_color.rgb().y;
|
||||
sdlPalette->colors[i].b = pal_color.rgb().z;
|
||||
sdlPalette->colors[i].a = pal_color.rgb().w;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LockSurface(surface_.get());
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||
int palette_id) {
|
||||
absl::Status Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||
int palette_id) {
|
||||
auto start_index = palette_id * 8;
|
||||
palette_ = palette.sub_palette(start_index, start_index + 8);
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
for (int i = 0; i < palette_.size(); ++i) {
|
||||
if (palette_.GetColor(i).is_transparent()) {
|
||||
ASSIGN_OR_RETURN(auto pal_color, palette_.GetColor(i));
|
||||
if (pal_color.is_transparent()) {
|
||||
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 = pal_color.rgb().x;
|
||||
surface_->format->palette->colors[i].g = pal_color.rgb().y;
|
||||
surface_->format->palette->colors[i].b = pal_color.rgb().z;
|
||||
surface_->format->palette->colors[i].a = pal_color.rgb().w;
|
||||
}
|
||||
}
|
||||
SDL_LockSurface(surface_.get());
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
|
||||
int length) {
|
||||
absl::Status Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette,
|
||||
int index, int length) {
|
||||
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).rgb());
|
||||
ASSIGN_OR_RETURN(auto pal_color, palette.GetColor(i));
|
||||
colors.push_back(pal_color.rgb());
|
||||
}
|
||||
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
@@ -385,6 +407,7 @@ void Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
|
||||
i++;
|
||||
}
|
||||
SDL_LockSurface(surface_.get());
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
|
||||
|
||||
@@ -15,11 +15,32 @@
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
/**
|
||||
* @namespace yaze::app::gfx
|
||||
* @brief Contains classes for handling graphical data.
|
||||
*/
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief Convert SDL_Surface to PNG image data.
|
||||
*/
|
||||
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer);
|
||||
|
||||
/**
|
||||
* @brief Convert PNG image data to SDL_Surface.
|
||||
*/
|
||||
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
||||
SDL_Surface **outSurface);
|
||||
|
||||
/**
|
||||
* @brief Represents a bitmap image.
|
||||
*
|
||||
* The `Bitmap` class provides functionality to create, manipulate, and display
|
||||
* bitmap images. It supports various operations such as creating a bitmap
|
||||
* object, creating and updating textures, applying palettes, and accessing
|
||||
* pixel data.
|
||||
*/
|
||||
class Bitmap {
|
||||
public:
|
||||
Bitmap() = default;
|
||||
@@ -29,14 +50,41 @@ class Bitmap {
|
||||
: width_(width), height_(height), depth_(depth), data_(data) {
|
||||
InitializeFromData(width, height, depth, data);
|
||||
}
|
||||
Bitmap(int width, int height, int depth, const Bytes &data,
|
||||
const SnesPalette &palette)
|
||||
: width_(width),
|
||||
height_(height),
|
||||
depth_(depth),
|
||||
data_(data),
|
||||
palette_(palette) {
|
||||
InitializeFromData(width, height, depth, data);
|
||||
ApplyPalette(palette);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a bitmap object and reserves space for graphical data.
|
||||
*/
|
||||
void Create(int width, int height, int depth, int data_size);
|
||||
|
||||
/**
|
||||
* @brief Creates a bitmap object with the provided graphical data.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Creates the underlying SDL_Texture to be displayed.
|
||||
*
|
||||
* Converts the surface from a RGB to ARGB format.
|
||||
* Uses SDL_TEXTUREACCESS_STREAMING to allow for live updates.
|
||||
*/
|
||||
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
|
||||
/**
|
||||
* @brief Updates the underlying SDL_Texture when it already exists.
|
||||
*/
|
||||
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void CreateTexture(SDL_Renderer *renderer);
|
||||
void UpdateTexture(SDL_Renderer *renderer, bool use_sdl_update = false);
|
||||
@@ -47,11 +95,15 @@ class Bitmap {
|
||||
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,
|
||||
int length = 7);
|
||||
/**
|
||||
* @brief Copy color data from the SnesPalette into the SDL_Palette
|
||||
*/
|
||||
absl::Status ApplyPalette(const SnesPalette &palette);
|
||||
absl::Status ApplyPaletteWithTransparent(const SnesPalette &palette,
|
||||
int index, int length = 7);
|
||||
void ApplyPalette(const std::vector<SDL_Color> &palette);
|
||||
void ApplyPaletteFromPaletteGroup(const SnesPalette &palette, int palette_id);
|
||||
absl::Status ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||
int palette_id);
|
||||
|
||||
void WriteToPixel(int position, uchar value) {
|
||||
if (pixel_data_ == nullptr) {
|
||||
@@ -236,6 +288,9 @@ class Bitmap {
|
||||
|
||||
using BitmapTable = std::unordered_map<int, gfx::Bitmap>;
|
||||
|
||||
/**
|
||||
* @brief Hash map container of shared pointers to Bitmaps.
|
||||
*/
|
||||
class BitmapManager {
|
||||
private:
|
||||
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>> bitmap_cache_;
|
||||
|
||||
@@ -474,7 +474,7 @@ Bytes CreateCompressionString(CompressionPiecePointer& start, int mode) {
|
||||
absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
|
||||
int mode, int start, int src_data_pos) {
|
||||
if (chain_head->next != nullptr) {
|
||||
ROM temp_rom;
|
||||
Rom temp_rom;
|
||||
RETURN_IF_ERROR(
|
||||
temp_rom.LoadFromBytes(CreateCompressionString(chain_head->next, mode)))
|
||||
ASSIGN_OR_RETURN(auto decomp_data,
|
||||
@@ -1173,7 +1173,7 @@ void AddCompressionToChain(CompressionContext& context) {
|
||||
|
||||
absl::Status ValidateCompressionResultV3(const CompressionContext& context) {
|
||||
if (!context.compressed_data.empty()) {
|
||||
ROM temp_rom;
|
||||
Rom temp_rom;
|
||||
RETURN_IF_ERROR(temp_rom.LoadFromBytes(context.compressed_data));
|
||||
ASSIGN_OR_RETURN(auto decomp_data,
|
||||
DecompressV2(temp_rom.data(), 0, temp_rom.size()))
|
||||
|
||||
@@ -15,6 +15,12 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @namespace yaze::app::gfx::lc_lz2
|
||||
* @brief Contains the LC_LZ2 compression algorithm.
|
||||
*/
|
||||
namespace lc_lz2 {
|
||||
|
||||
const int D_NINTENDO_C_MODE1 = 0;
|
||||
const int D_NINTENDO_C_MODE2 = 1;
|
||||
|
||||
@@ -29,15 +35,6 @@ const int D_MAX_LENGTH = 1024;
|
||||
|
||||
const int INITIAL_ALLOC_SIZE = 1024;
|
||||
|
||||
namespace lc_lz2 {
|
||||
|
||||
absl::StatusOr<Bytes> ZS_Compress(const std::vector<uint8_t>& data,
|
||||
const int start, const int length,
|
||||
int mode = 1, bool check = false);
|
||||
|
||||
absl::StatusOr<Bytes> ZS_CompressOverworld(const std::vector<uint8_t> data,
|
||||
const int pos, const int length);
|
||||
|
||||
constexpr int kCommandDirectCopy = 0;
|
||||
constexpr int kCommandByteFill = 1;
|
||||
constexpr int kCommandWordFill = 2;
|
||||
@@ -141,6 +138,10 @@ void CompressionCommandAlternativeV2(const uchar* data,
|
||||
uint& src_pos, uint& comp_accumulator,
|
||||
uint& cmd_with_max, uint& max_win);
|
||||
|
||||
/**
|
||||
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
|
||||
* \deprecated Use Compress and Uncompress instead.
|
||||
*/
|
||||
absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
|
||||
const int length, int mode = 1,
|
||||
bool check = false);
|
||||
@@ -208,6 +209,10 @@ absl::StatusOr<CompressionPiece> SplitCompressionPieceV3(
|
||||
CompressionPiece& piece, int mode);
|
||||
void FinalizeCompression(CompressionContext& context);
|
||||
|
||||
/**
|
||||
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
|
||||
* \deprecated Use Compress and Uncompress instead.
|
||||
*/
|
||||
absl::StatusOr<Bytes> CompressV3(const std::vector<uint8_t>& data,
|
||||
const int start, const int length,
|
||||
int mode = 1, bool check = false);
|
||||
@@ -227,6 +232,10 @@ 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);
|
||||
|
||||
/**
|
||||
* @brief Decompresses a buffer of data using the LC_LZ2 algorithm.
|
||||
* \deprecated Use Compress and Uncompress instead.
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
namespace scad_format {
|
||||
|
||||
void FindMetastamp() {
|
||||
int matching_position = -1;
|
||||
@@ -276,6 +277,7 @@ absl::Status DecodeObjFile(
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace scad_format
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
} // namespace yaze
|
||||
|
||||
@@ -22,18 +22,27 @@ 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
|
||||
/**
|
||||
* @namespace yaze::app::gfx::scad_format
|
||||
* @brief Loading from prototype SCAD format
|
||||
*/
|
||||
namespace scad_format {
|
||||
|
||||
/**
|
||||
* @brief Cgx file header
|
||||
* キャラクタ(.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];
|
||||
@@ -49,28 +58,47 @@ struct CgxHeader {
|
||||
constexpr uint16_t kMatchedBytes[] = {0x4E, 0x41, 0x4B, 0x31, 0x39, 0x38, 0x39};
|
||||
constexpr uint16_t kOffsetFromMatchedBytesEnd = 0x1D;
|
||||
|
||||
/**
|
||||
* @brief Find metastamp in CGX file
|
||||
*/
|
||||
void FindMetastamp();
|
||||
|
||||
/**
|
||||
* @brief Load Scr file (screen data)
|
||||
*/
|
||||
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
|
||||
std::vector<uint8_t>& map_data);
|
||||
|
||||
/**
|
||||
* @brief Load Cgx file (graphical content)
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Draw screen tilemap with graphical data
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Decode color file
|
||||
*/
|
||||
std::vector<SDL_Color> DecodeColFile(const std::string_view filename);
|
||||
|
||||
/**
|
||||
* @brief Decode obj file
|
||||
*/
|
||||
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 scad_format
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -10,6 +10,9 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief Primitive of 16-bit RGB SNES color.
|
||||
*/
|
||||
struct snes_color {
|
||||
uint16_t red; /**< Red component of the color. */
|
||||
uint16_t blue; /**< Blue component of the color. */
|
||||
@@ -26,6 +29,16 @@ std::vector<snes_color> Extract(const char* data, unsigned int offset,
|
||||
|
||||
std::vector<char> Convert(const std::vector<snes_color>& palette);
|
||||
|
||||
/**
|
||||
* @brief SNES Color container
|
||||
*
|
||||
* Used for displaying the color to the screen and writing
|
||||
* the color to the Rom file in the correct format.
|
||||
*
|
||||
* SNES colors may be represented in one of three formats:
|
||||
* - Color data from the rom in a snes_color struct
|
||||
* - Color data for displaying to the UI via ImVec4
|
||||
*/
|
||||
class SnesColor {
|
||||
public:
|
||||
SnesColor() : rgb_(0.f, 0.f, 0.f, 0.f), snes_(0) {}
|
||||
@@ -53,6 +66,7 @@ class SnesColor {
|
||||
}
|
||||
|
||||
ImVec4 rgb() const { return rgb_; }
|
||||
|
||||
void set_rgb(const ImVec4 val) {
|
||||
rgb_.x = val.x / 255;
|
||||
rgb_.y = val.y / 255;
|
||||
@@ -65,6 +79,7 @@ class SnesColor {
|
||||
snes_ = ConvertRGBtoSNES(color);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
void set_snes(uint16_t val) {
|
||||
snes_ = val;
|
||||
snes_color col = ConvertSNEStoRGB(val);
|
||||
|
||||
@@ -20,6 +20,169 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @namespace yaze::app::gfx::palette_group_internal
|
||||
* @brief Internal functions for loading palettes by group.
|
||||
*/
|
||||
namespace palette_group_internal {
|
||||
absl::Status LoadOverworldMainPalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.overworld_main.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::overworldPaletteMain + (i * (35 * 2)),
|
||||
/*num_colors*/ 35, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadOverworldAuxiliaryPalettes(
|
||||
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
RETURN_IF_ERROR(
|
||||
palette_groups.overworld_aux.AddPalette(gfx::ReadPaletteFromRom(
|
||||
core::overworldPaletteAuxialiary + (i * (21 * 2)),
|
||||
/*num_colors*/ 21, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadOverworldAnimatedPalettes(
|
||||
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 14; i++) {
|
||||
RETURN_IF_ERROR(
|
||||
palette_groups.overworld_animated.AddPalette(gfx::ReadPaletteFromRom(
|
||||
core::overworldPaletteAnimated + (i * (7 * 2)), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadHUDPalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.hud.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::hudPalettes + (i * 64), 32, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadGlobalSpritePalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
RETURN_IF_ERROR(palette_groups.global_sprites.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::globalSpritePalettesLW, 60, data)))
|
||||
RETURN_IF_ERROR(palette_groups.global_sprites.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::globalSpritePalettesDW, 60, data)))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadArmorPalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.armors.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::armorPalettes + (i * 30), 15, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSwordPalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.swords.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::swordPalettes + (i * 6), 3, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadShieldPalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.shields.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::shieldPalettes + (i * 8), 4, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSpriteAux1Palettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 12; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.sprites_aux1.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::spritePalettesAux1 + (i * 14), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSpriteAux2Palettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 11; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.sprites_aux2.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::spritePalettesAux2 + (i * 14), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSpriteAux3Palettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 24; i++) {
|
||||
RETURN_IF_ERROR(palette_groups.sprites_aux3.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::spritePalettesAux3 + (i * 14), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadDungeonMainPalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
RETURN_IF_ERROR(
|
||||
palette_groups.dungeon_main.AddPalette(gfx::ReadPaletteFromRom(
|
||||
core::dungeonMainPalettes + (i * 180), 90, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadGrassColors(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
RETURN_IF_ERROR(palette_groups.grass.AddColor(
|
||||
gfx::ReadColorFromRom(core::hardcodedGrassLW, rom_data.data())))
|
||||
RETURN_IF_ERROR(palette_groups.grass.AddColor(
|
||||
gfx::ReadColorFromRom(core::hardcodedGrassDW, rom_data.data())))
|
||||
RETURN_IF_ERROR(palette_groups.grass.AddColor(
|
||||
gfx::ReadColorFromRom(core::hardcodedGrassSpecial, rom_data.data())))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Load3DObjectPalettes(const Bytes& rom_data,
|
||||
gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
RETURN_IF_ERROR(palette_groups.object_3d.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::triforcePalette, 8, data)))
|
||||
RETURN_IF_ERROR(palette_groups.object_3d.AddPalette(
|
||||
gfx::ReadPaletteFromRom(core::crystalPalette, 8, data)))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadOverworldMiniMapPalettes(
|
||||
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
RETURN_IF_ERROR(
|
||||
palette_groups.overworld_mini_map.AddPalette(gfx::ReadPaletteFromRom(
|
||||
core::overworldMiniMapPalettes + (i * 256), 128, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
} // namespace palette_group_internal
|
||||
|
||||
const absl::flat_hash_map<std::string, uint32_t> kPaletteGroupAddressMap = {
|
||||
{"ow_main", core::overworldPaletteMain},
|
||||
{"ow_aux", core::overworldPaletteAuxialiary},
|
||||
@@ -194,6 +357,7 @@ absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
|
||||
for (int i = 0; i < palette.size(); i += 8) {
|
||||
SnesPalette new_palette;
|
||||
if (i + 8 < palette.size()) {
|
||||
// new_palette.AddColor(SnesColor(ImVec4(0,0,0,0)));
|
||||
for (int j = 0; j < 8; j++) {
|
||||
new_palette.AddColor(palette[i + j]);
|
||||
}
|
||||
@@ -204,6 +368,27 @@ absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
|
||||
return toret;
|
||||
}
|
||||
|
||||
using namespace palette_group_internal;
|
||||
|
||||
absl::Status LoadAllPalettes(const Bytes& rom_data, PaletteGroupMap& groups) {
|
||||
RETURN_IF_ERROR(LoadOverworldMainPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadOverworldAuxiliaryPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadOverworldAnimatedPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadHUDPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadGlobalSpritePalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadArmorPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadSwordPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadShieldPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadSpriteAux1Palettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadSpriteAux2Palettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadSpriteAux3Palettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadDungeonMainPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadGrassColors(rom_data, groups))
|
||||
RETURN_IF_ERROR(Load3DObjectPalettes(rom_data, groups))
|
||||
RETURN_IF_ERROR(LoadOverworldMiniMapPalettes(rom_data, groups))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -21,6 +21,9 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief Primitive of a SNES color palette.
|
||||
*/
|
||||
struct snes_palette {
|
||||
uint id; /**< ID of the palette. */
|
||||
uint size; /**< Size of the palette. */
|
||||
@@ -31,6 +34,18 @@ using snes_palette = struct snes_palette;
|
||||
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
|
||||
size_t color_index);
|
||||
|
||||
/**
|
||||
* @brief Represents a palette of colors for the Super Nintendo Entertainment
|
||||
* System (SNES).
|
||||
*
|
||||
* The `SnesPalette` class provides functionality to create, modify, and access
|
||||
* colors in an SNES palette. It supports various constructors to initialize the
|
||||
* palette with different types of data. The palette can be modified by adding
|
||||
* or changing colors, and it can be cleared to remove all colors. Colors in the
|
||||
* palette can be accessed using index-based access or through the `GetColor`
|
||||
* method. The class also provides a method to create a sub-palette by selecting
|
||||
* a range of colors from the original palette.
|
||||
*/
|
||||
class SnesPalette {
|
||||
public:
|
||||
template <typename T>
|
||||
@@ -69,10 +84,9 @@ class SnesPalette {
|
||||
size_++;
|
||||
}
|
||||
|
||||
auto GetColor(int i) const {
|
||||
absl::StatusOr<SnesColor> GetColor(int i) const {
|
||||
if (i > size_) {
|
||||
std::cout << "SNESPalette: Index out of bounds" << std::endl;
|
||||
return colors[0];
|
||||
return absl::InvalidArgumentError("SnesPalette: Index out of bounds");
|
||||
}
|
||||
return colors[i];
|
||||
}
|
||||
@@ -126,13 +140,17 @@ SnesPalette ReadPaletteFromRom(int offset, int num_colors, const uint8_t* rom);
|
||||
|
||||
std::array<float, 4> ToFloatArray(const SnesColor& color);
|
||||
|
||||
/**
|
||||
* @brief Represents a group of palettes.
|
||||
*
|
||||
* Supports adding palettes and colors, clearing the group, and accessing
|
||||
* palettes and colors by index.
|
||||
*/
|
||||
struct PaletteGroup {
|
||||
PaletteGroup() = default;
|
||||
|
||||
explicit PaletteGroup(uint8_t mSize);
|
||||
|
||||
auto mutable_palette(int i) { return &palettes[i]; }
|
||||
|
||||
absl::Status AddPalette(SnesPalette pal) {
|
||||
palettes.emplace_back(pal);
|
||||
size_ = palettes.size();
|
||||
@@ -152,7 +170,10 @@ struct PaletteGroup {
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
auto name() const { return name_; }
|
||||
auto size() const { return palettes.size(); }
|
||||
auto mutable_palette(int i) { return &palettes[i]; }
|
||||
auto palette(int i) const { return palettes[i]; }
|
||||
|
||||
SnesPalette operator[](int i) {
|
||||
if (i > size_) {
|
||||
@@ -188,17 +209,128 @@ struct PaletteGroup {
|
||||
|
||||
private:
|
||||
int size_ = 0;
|
||||
std::string name_;
|
||||
std::vector<SnesPalette> palettes;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a mapping of palette groups.
|
||||
*
|
||||
* Originally, this was an actual std::unordered_map but since the palette
|
||||
* groups supported never change, it was changed to a struct with a method to
|
||||
* get the group by name.
|
||||
*/
|
||||
struct PaletteGroupMap {
|
||||
PaletteGroup overworld_main;
|
||||
PaletteGroup overworld_aux;
|
||||
PaletteGroup overworld_animated;
|
||||
PaletteGroup hud;
|
||||
PaletteGroup global_sprites;
|
||||
PaletteGroup armors;
|
||||
PaletteGroup swords;
|
||||
PaletteGroup shields;
|
||||
PaletteGroup sprites_aux1;
|
||||
PaletteGroup sprites_aux2;
|
||||
PaletteGroup sprites_aux3;
|
||||
PaletteGroup dungeon_main;
|
||||
PaletteGroup grass;
|
||||
PaletteGroup object_3d;
|
||||
PaletteGroup overworld_mini_map;
|
||||
|
||||
auto get_group(const std::string& group_name) {
|
||||
if (group_name == "ow_main") {
|
||||
return &overworld_main;
|
||||
} else if (group_name == "ow_aux") {
|
||||
return &overworld_aux;
|
||||
} else if (group_name == "ow_animated") {
|
||||
return &overworld_animated;
|
||||
} else if (group_name == "hud") {
|
||||
return &hud;
|
||||
} else if (group_name == "global_sprites") {
|
||||
return &global_sprites;
|
||||
} else if (group_name == "armors") {
|
||||
return &armors;
|
||||
} else if (group_name == "swords") {
|
||||
return &swords;
|
||||
} else if (group_name == "shields") {
|
||||
return &shields;
|
||||
} else if (group_name == "sprites_aux1") {
|
||||
return &sprites_aux1;
|
||||
} else if (group_name == "sprites_aux2") {
|
||||
return &sprites_aux2;
|
||||
} else if (group_name == "sprites_aux3") {
|
||||
return &sprites_aux3;
|
||||
} else if (group_name == "dungeon_main") {
|
||||
return &dungeon_main;
|
||||
} else if (group_name == "grass") {
|
||||
return &grass;
|
||||
} else if (group_name == "3d_object") {
|
||||
return &object_3d;
|
||||
} else if (group_name == "ow_mini_map") {
|
||||
return &overworld_mini_map;
|
||||
} else {
|
||||
throw std::out_of_range("PaletteGroupMap: Group not found");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void for_each(Func&& func) {
|
||||
func(overworld_main);
|
||||
func(overworld_aux);
|
||||
func(overworld_animated);
|
||||
func(hud);
|
||||
func(global_sprites);
|
||||
func(armors);
|
||||
func(swords);
|
||||
func(shields);
|
||||
func(sprites_aux1);
|
||||
func(sprites_aux2);
|
||||
func(sprites_aux3);
|
||||
func(dungeon_main);
|
||||
func(grass);
|
||||
func(object_3d);
|
||||
func(overworld_mini_map);
|
||||
}
|
||||
};
|
||||
|
||||
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromColFile(
|
||||
std::vector<SnesColor>& colors);
|
||||
|
||||
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
|
||||
SnesPalette& palette);
|
||||
|
||||
/**
|
||||
* @brief Loads all the palettes for the game.
|
||||
*
|
||||
* This function loads all the palettes for the game, including overworld,
|
||||
* HUD, armor, swords, shields, sprites, dungeon, grass, and 3D object
|
||||
* palettes. It also adds the loaded palettes to their respective palette
|
||||
* groups.
|
||||
*
|
||||
*/
|
||||
absl::Status LoadAllPalettes(const Bytes& rom_data, PaletteGroupMap& groups);
|
||||
|
||||
/**
|
||||
* @brief Represents a set of palettes used in a SNES graphics system.
|
||||
*/
|
||||
struct Paletteset {
|
||||
/**
|
||||
* @brief Default constructor for Paletteset.
|
||||
*/
|
||||
Paletteset() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructor for Paletteset.
|
||||
* @param main The main palette.
|
||||
* @param animated The animated palette.
|
||||
* @param aux1 The first auxiliary palette.
|
||||
* @param aux2 The second auxiliary palette.
|
||||
* @param background The background color.
|
||||
* @param hud The HUD palette.
|
||||
* @param spr The sprite palette.
|
||||
* @param spr2 The second sprite palette.
|
||||
* @param comp The composite palette.
|
||||
*/
|
||||
Paletteset(gfx::SnesPalette main, gfx::SnesPalette animated,
|
||||
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
|
||||
gfx::SnesColor background, gfx::SnesPalette hud,
|
||||
@@ -212,15 +344,16 @@ struct Paletteset {
|
||||
spr(spr),
|
||||
spr2(spr2),
|
||||
composite(comp) {}
|
||||
gfx::SnesPalette main;
|
||||
gfx::SnesPalette animated;
|
||||
gfx::SnesPalette aux1;
|
||||
gfx::SnesPalette aux2;
|
||||
gfx::SnesColor background;
|
||||
gfx::SnesPalette hud;
|
||||
gfx::SnesPalette spr;
|
||||
gfx::SnesPalette spr2;
|
||||
gfx::SnesPalette composite;
|
||||
|
||||
gfx::SnesPalette main; /**< The main palette. */
|
||||
gfx::SnesPalette animated; /**< The animated palette. */
|
||||
gfx::SnesPalette aux1; /**< The first auxiliary palette. */
|
||||
gfx::SnesPalette aux2; /**< The second auxiliary palette. */
|
||||
gfx::SnesColor background; /**< The background color. */
|
||||
gfx::SnesPalette hud; /**< The HUD palette. */
|
||||
gfx::SnesPalette spr; /**< The sprite palette. */
|
||||
gfx::SnesPalette spr2; /**< The second sprite palette. */
|
||||
gfx::SnesPalette composite; /**< The composite palette. */
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
@@ -135,7 +135,7 @@ std::vector<uint8_t> Convert4bppTo3bpp(const std::vector<uint8_t>& tiles) {
|
||||
return ConvertBpp(tiles, 4, 3);
|
||||
}
|
||||
|
||||
Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
|
||||
Bytes SnesTo8bppSheet(const Bytes& sheet, int bpp) {
|
||||
int xx = 0; // positions where we are at on the sheet
|
||||
int yy = 0;
|
||||
int pos = 0;
|
||||
@@ -148,6 +148,11 @@ Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
|
||||
buffer_size = 0x2000;
|
||||
} else if (bpp == 3) {
|
||||
bpp = 24;
|
||||
} else if (bpp == 4) {
|
||||
bpp = 32;
|
||||
buffer_size = 0x4000;
|
||||
} else if (bpp == 8) {
|
||||
bpp = 64;
|
||||
}
|
||||
Bytes sheet_buffer_out(buffer_size);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace gfx {
|
||||
constexpr uint8_t kGraphicsBitmap[8] = {0x80, 0x40, 0x20, 0x10,
|
||||
0x08, 0x04, 0x02, 0x01};
|
||||
|
||||
Bytes SnesTo8bppSheet(Bytes sheet, int bpp);
|
||||
Bytes SnesTo8bppSheet(const Bytes& sheet, int bpp);
|
||||
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp = 0);
|
||||
|
||||
struct tile8 {
|
||||
@@ -35,9 +35,14 @@ std::vector<uint8_t> ConvertBpp(const std::vector<uint8_t>& tiles,
|
||||
std::vector<uint8_t> Convert3bppTo4bpp(const std::vector<uint8_t>& tiles);
|
||||
std::vector<uint8_t> Convert4bppTo3bpp(const std::vector<uint8_t>& tiles);
|
||||
|
||||
// vhopppcc cccccccc
|
||||
// [0, 1]
|
||||
// [2, 3]
|
||||
/**
|
||||
* @brief SNES 16-bit tile metadata container
|
||||
*
|
||||
* Format:
|
||||
* vhopppcc cccccccc
|
||||
* [0, 1]
|
||||
* [2, 3]
|
||||
*/
|
||||
class TileInfo {
|
||||
public:
|
||||
uint16_t id_;
|
||||
@@ -67,6 +72,9 @@ uint16_t TileInfoToShort(TileInfo tile_info);
|
||||
|
||||
TileInfo GetTilesInfo(uint16_t tile);
|
||||
|
||||
/**
|
||||
* @brief Tile composition of four 16x16 tiles.
|
||||
*/
|
||||
class Tile32 {
|
||||
public:
|
||||
uint16_t tile0_;
|
||||
@@ -113,6 +121,9 @@ class Tile32 {
|
||||
bool operator!=(const Tile32& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Tile composition of four 8x8 tiles.
|
||||
*/
|
||||
class Tile16 {
|
||||
public:
|
||||
TileInfo tile0_;
|
||||
@@ -138,7 +149,10 @@ class Tile16 {
|
||||
bool operator!=(const Tile16& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
class OAMTile {
|
||||
/**
|
||||
* @brief Object Attribute Memory tile abstraction container
|
||||
*/
|
||||
class OamTile {
|
||||
public:
|
||||
int x_;
|
||||
int y_;
|
||||
@@ -146,8 +160,8 @@ class OAMTile {
|
||||
int my_;
|
||||
int pal_;
|
||||
uint16_t tile_;
|
||||
OAMTile() = default;
|
||||
OAMTile(int x, int y, uint16_t tile, int pal, bool upper = false, int mx = 0,
|
||||
OamTile() = default;
|
||||
OamTile(int x, int y, uint16_t tile, int pal, bool upper = false, int mx = 0,
|
||||
int my = 0)
|
||||
: x_(x), y_(y), mx_(mx), my_(my), pal_(pal) {
|
||||
if (upper) {
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "app/gfx/tilesheet.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_color.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
absl::StatusOr<Tilesheet> CreateTilesheetFromGraphicsBuffer(
|
||||
const uint8_t* graphics_buffer, int width, int height, TileType tile_type,
|
||||
int sheet_id) {
|
||||
Tilesheet tilesheet;
|
||||
|
||||
// Calculate the offset in the graphics buffer based on the sheet ID
|
||||
int sheet_offset = sheet_id * width * height;
|
||||
|
||||
// Initialize the tilesheet with the specified width, height, and tile type
|
||||
tilesheet.Init(width, height, tile_type);
|
||||
|
||||
// Iterate over the tiles in the sheet and copy them into the tilesheet
|
||||
for (int row = 0; row < height; ++row) {
|
||||
for (int col = 0; col < width; ++col) {
|
||||
// Calculate the index of the current tile in the graphics buffer
|
||||
int tile_index = sheet_offset + (row * width + col) * 64;
|
||||
|
||||
// Copy the tile data into the tilesheet
|
||||
for (int y = 0; y < 8; ++y) {
|
||||
for (int x = 0; x < 8; ++x) {
|
||||
int srcIndex = tile_index + (y * 8 + x);
|
||||
int destX = col * 8 + x;
|
||||
int destY = row * 8 + y;
|
||||
int destIndex = (destY * width * 8) + destX;
|
||||
tilesheet.mutable_bitmap()->mutable_data()[destIndex] =
|
||||
graphics_buffer[srcIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tilesheet;
|
||||
}
|
||||
|
||||
void Tilesheet::Init(int width, int height, TileType tile_type) {
|
||||
bitmap_ = std::make_shared<Bitmap>(width, height, 8, 0x20000);
|
||||
internal_data_.resize(0x20000);
|
||||
tile_type_ = tile_type;
|
||||
if (tile_type_ == TileType::Tile8) {
|
||||
tile_width_ = 8;
|
||||
tile_height_ = 8;
|
||||
} else {
|
||||
tile_width_ = 16;
|
||||
tile_height_ = 16;
|
||||
}
|
||||
}
|
||||
|
||||
void Tilesheet::ComposeTile16(const std::vector<uint8_t>& graphics_buffer,
|
||||
const TileInfo& top_left,
|
||||
const TileInfo& top_right,
|
||||
const TileInfo& bottom_left,
|
||||
const TileInfo& bottom_right) {
|
||||
// Calculate the base position for this Tile16 in the full-size bitmap
|
||||
int tiles_per_row = bitmap_->width() / tile_width_;
|
||||
int tile16_row = num_tiles_ / tiles_per_row;
|
||||
int tile16_column = num_tiles_ % tiles_per_row;
|
||||
int baseX = tile16_column * tile_width_;
|
||||
int baseY = tile16_row * tile_height_;
|
||||
|
||||
// Compose and place each part of the Tile16
|
||||
ComposeAndPlaceTilePart(graphics_buffer, top_left, baseX, baseY);
|
||||
ComposeAndPlaceTilePart(graphics_buffer, top_right, baseX + 8, baseY);
|
||||
ComposeAndPlaceTilePart(graphics_buffer, bottom_left, baseX, baseY + 8);
|
||||
ComposeAndPlaceTilePart(graphics_buffer, bottom_right, baseX + 8, baseY + 8);
|
||||
|
||||
tile_info_.push_back({top_left, top_right, bottom_left, bottom_right});
|
||||
|
||||
num_tiles_++;
|
||||
}
|
||||
|
||||
void Tilesheet::ComposeAndPlaceTilePart(
|
||||
const std::vector<uint8_t>& graphics_buffer, const TileInfo& tile_info,
|
||||
int baseX, int baseY) {
|
||||
std::vector<uint8_t> tile_data =
|
||||
FetchTileDataFromGraphicsBuffer(graphics_buffer, tile_info.id_);
|
||||
|
||||
if (tile_info.vertical_mirror_) {
|
||||
MirrorTileDataVertically(tile_data);
|
||||
}
|
||||
if (tile_info.horizontal_mirror_) {
|
||||
MirrorTileDataHorizontally(tile_data);
|
||||
}
|
||||
|
||||
// Place the tile data into the full-size bitmap at the calculated position
|
||||
for (int y = 0; y < 8; ++y) {
|
||||
for (int x = 0; x < 8; ++x) {
|
||||
int srcIndex = y * 8 + x;
|
||||
int destX = baseX + x;
|
||||
int destY = baseY + y;
|
||||
int destIndex = (destY * bitmap_->width()) + destX;
|
||||
internal_data_[destIndex] = tile_data[srcIndex];
|
||||
}
|
||||
}
|
||||
|
||||
bitmap_->set_data(internal_data_);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#ifndef YAZE_APP_GFX_TILESHEET_H
|
||||
#define YAZE_APP_GFX_TILESHEET_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_color.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -12,6 +15,14 @@ namespace gfx {
|
||||
|
||||
enum class TileType { Tile8, Tile16 };
|
||||
|
||||
/**
|
||||
* @class Tilesheet
|
||||
* @brief Represents a tilesheet, which is a collection of tiles stored in a
|
||||
* bitmap.
|
||||
*
|
||||
* The Tilesheet class provides methods to manipulate and extract tiles from the
|
||||
* tilesheet. It also supports copying and mirroring tiles within the tilesheet.
|
||||
*/
|
||||
class Tilesheet {
|
||||
public:
|
||||
Tilesheet() = default;
|
||||
@@ -22,68 +33,14 @@ class Tilesheet {
|
||||
tile_height_(tileHeight),
|
||||
tile_type_(tile_type) {}
|
||||
|
||||
void Init(int width, int height, TileType tile_type) {
|
||||
bitmap_ = std::make_shared<Bitmap>(width, height, 8, 0x20000);
|
||||
internal_data_.resize(0x20000);
|
||||
tile_type_ = tile_type;
|
||||
if (tile_type_ == TileType::Tile8) {
|
||||
tile_width_ = 8;
|
||||
tile_height_ = 8;
|
||||
} else {
|
||||
tile_width_ = 16;
|
||||
tile_height_ = 16;
|
||||
}
|
||||
}
|
||||
void Init(int width, int height, TileType tile_type);
|
||||
|
||||
void ComposeTile16(const std::vector<uint8_t>& graphics_buffer,
|
||||
const TileInfo& top_left, const TileInfo& top_right,
|
||||
const TileInfo& bottom_left,
|
||||
const TileInfo& bottom_right) {
|
||||
// Calculate the base position for this Tile16 in the full-size bitmap
|
||||
int tiles_per_row = bitmap_->width() / tile_width_;
|
||||
int tile16_row = num_tiles_ / tiles_per_row;
|
||||
int tile16_column = num_tiles_ % tiles_per_row;
|
||||
int baseX = tile16_column * tile_width_;
|
||||
int baseY = tile16_row * tile_height_;
|
||||
|
||||
// Compose and place each part of the Tile16
|
||||
ComposeAndPlaceTilePart(graphics_buffer, top_left, baseX, baseY);
|
||||
ComposeAndPlaceTilePart(graphics_buffer, top_right, baseX + 8, baseY);
|
||||
ComposeAndPlaceTilePart(graphics_buffer, bottom_left, baseX, baseY + 8);
|
||||
ComposeAndPlaceTilePart(graphics_buffer, bottom_right, baseX + 8,
|
||||
baseY + 8);
|
||||
|
||||
tile_info_.push_back({top_left, top_right, bottom_left, bottom_right});
|
||||
|
||||
num_tiles_++;
|
||||
}
|
||||
const TileInfo& bottom_left, const TileInfo& bottom_right);
|
||||
|
||||
void ComposeAndPlaceTilePart(const std::vector<uint8_t>& graphics_buffer,
|
||||
const TileInfo& tile_info, int baseX,
|
||||
int baseY) {
|
||||
std::vector<uint8_t> tile_data =
|
||||
FetchTileDataFromGraphicsBuffer(graphics_buffer, tile_info.id_);
|
||||
|
||||
if (tile_info.vertical_mirror_) {
|
||||
MirrorTileDataVertically(tile_data);
|
||||
}
|
||||
if (tile_info.horizontal_mirror_) {
|
||||
MirrorTileDataHorizontally(tile_data);
|
||||
}
|
||||
|
||||
// Place the tile data into the full-size bitmap at the calculated position
|
||||
for (int y = 0; y < 8; ++y) {
|
||||
for (int x = 0; x < 8; ++x) {
|
||||
int srcIndex = y * 8 + x;
|
||||
int destX = baseX + x;
|
||||
int destY = baseY + y;
|
||||
int destIndex = (destY * bitmap_->width()) + destX;
|
||||
internal_data_[destIndex] = tile_data[srcIndex];
|
||||
}
|
||||
}
|
||||
|
||||
bitmap_->set_data(internal_data_);
|
||||
}
|
||||
const TileInfo& tile_info, int baseX, int baseY);
|
||||
|
||||
// Extracts a tile from the tilesheet
|
||||
Bitmap GetTile(int tileX, int tileY, int bmp_width, int bmp_height) {
|
||||
@@ -230,6 +187,10 @@ class Tilesheet {
|
||||
TileType tile_type_;
|
||||
};
|
||||
|
||||
absl::StatusOr<Tilesheet> CreateTilesheetFromGraphicsBuffer(
|
||||
const uint8_t* graphics_buffer, int width, int height, TileType tile_type,
|
||||
int sheet_id);
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
Reference in New Issue
Block a user