#ifndef YAZE_APP_GFX_BITMAP_H #define YAZE_APP_GFX_BITMAP_H #include #include #include #include "absl/status/status.h" #include "app/core/constants.h" #include "app/core/utils/sdl_deleter.h" #include "app/gfx/snes_palette.h" namespace yaze { namespace app { /** * @namespace yaze::app::gfx * @brief Contains classes for handling graphical data. */ namespace gfx { // Same as SDL_PIXELFORMAT_INDEX8 for reference constexpr Uint32 SNES_PIXELFORMAT_INDEXED = SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1); constexpr Uint32 SNES_PIXELFORMAT_2BPP = SDL_DEFINE_PIXELFORMAT( /*type=*/SDL_PIXELTYPE_INDEX8, /*order=*/0, /*layouts=*/0, /*bits=*/2, /*bytes=*/1); constexpr Uint32 SNES_PIXELFORMAT_4BPP = SDL_DEFINE_PIXELFORMAT( /*type=*/SDL_PIXELTYPE_INDEX8, /*order=*/0, /*layouts=*/0, /*bits=*/4, /*bytes=*/1); constexpr Uint32 SNES_PIXELFORMAT_8BPP = SDL_DEFINE_PIXELFORMAT( /*type=*/SDL_PIXELTYPE_INDEX8, /*order=*/0, /*layouts=*/0, /*bits=*/8, /*bytes=*/1); enum BitmapFormat { kIndexed = 0, k2bpp = 1, k4bpp = 2, k8bpp = 3, }; #if YAZE_LIB_PNG == 1 /** * @brief Convert SDL_Surface to PNG image data. */ bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector &buffer); /** * @brief Convert PNG image data to SDL_Surface. */ void ConvertPngToSurface(const std::vector &png_data, SDL_Surface **outSurface); #endif /** * @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; Bitmap(int width, int height, int depth, int data_size); Bitmap(int width, int height, int depth, const std::vector &data) : width_(width), height_(height), depth_(depth), data_(data) { Create(width, height, depth, data); } Bitmap(int width, int height, int depth, const std::vector &data, const SnesPalette &palette) : width_(width), height_(height), depth_(depth), data_(data), palette_(palette) { Create(width, height, depth, data); if (!ApplyPalette(palette).ok()) { std::cerr << "Error applying palette in bitmap constructor." << std::endl; } } #if YAZE_LIB_PNG == 1 std::vector GetPngData(); #endif void SaveSurfaceToFile(std::string_view filename); /** * @brief Creates a bitmap object with the provided graphical data. */ void Create(int width, int height, int depth, const std::vector &data); void Create(int width, int height, int depth, int format, const std::vector &data); void Reformat(int format); /** * @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(SDL_Renderer *renderer); /** * @brief Updates the underlying SDL_Texture when it already exists. */ void UpdateTexture(SDL_Renderer *renderer); /** * @brief Copy color data from the SnesPalette into the SDL_Palette */ absl::Status ApplyPalette(const SnesPalette &palette); absl::Status ApplyPaletteWithTransparent(const SnesPalette &palette, size_t index, int length = 7); void ApplyPalette(const std::vector &palette); absl::Status ApplyPaletteFromPaletteGroup(const SnesPalette &palette, int palette_id); void Get8x8Tile(int tile_index, int x, int y, std::vector &tile_data, int &tile_data_offset); void Get16x16Tile(int tile_x, int tile_y, std::vector &tile_data, int &tile_data_offset); 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 WriteColor(int position, const ImVec4 &color); void Cleanup() { active_ = false; 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 mutable_palette() { 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(); } auto converted_surface() const { return converted_surface_.get(); } auto mutable_converted_surface() { return converted_surface_.get(); } auto vector() const { return data_; } auto at(int i) const { return data_[i]; } auto texture() const { return texture_.get(); } auto modified() const { return modified_; } auto is_active() const { return active_; } void set_active(bool active) { active_ = active; } void set_data(const std::vector &data) { data_ = data; } void set_modified(bool modified) { modified_ = modified; } private: int width_ = 0; int height_ = 0; int depth_ = 0; int data_size_ = 0; bool active_ = false; bool modified_ = false; void *texture_pixels = nullptr; uint8_t *pixel_data_ = nullptr; std::vector data_; std::vector png_data_; gfx::SnesPalette palette_; std::shared_ptr texture_ = nullptr; std::shared_ptr surface_ = nullptr; std::shared_ptr converted_surface_ = nullptr; }; using BitmapTable = std::unordered_map; } // namespace gfx } // namespace app } // namespace yaze #endif // YAZE_APP_GFX_BITMAP_H