8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
17#ifdef YAZE_CLI_HAS_PNG
27#ifdef YAZE_CLI_HAS_PNG
30 std::vector<uint8_t>* buf;
33void PngWrite(png_structp png, png_bytep data, png_size_t len) {
34 auto* ctx =
static_cast<PngCtx*
>(png_get_io_ptr(png));
35 ctx->buf->insert(ctx->buf->end(), data, data + len);
38void PngFlush(png_structp ) {}
50 if (tile == 0xB0 || tile == 0xB1)
51 return {0, 210, 170, 150};
52 if (tile >= 0xB2 && tile <= 0xB5)
53 return {0, 190, 255, 150};
55 return {255, 215, 0, 170};
56 if (tile >= 0xB7 && tile <= 0xBA)
57 return {255, 80, 0, 200};
58 if (tile >= 0xBB && tile <= 0xBE)
59 return {80, 150, 255, 150};
60 if (tile >= 0xD0 && tile <= 0xD3)
61 return {210, 0, 255, 170};
62 return {140, 140, 140, 120};
68 : rom_(rom), game_data_(game_data) {}
73 return absl::FailedPreconditionError(
"ROM not loaded");
76 return absl::FailedPreconditionError(
"GameData not available");
79 return absl::InvalidArgumentError(
80 absl::StrFormat(
"Invalid room_id 0x%02X", req.
room_id));
83 std::lock_guard<std::mutex> lock(
mu_);
100 if (!composite.is_active() || composite.width() <= 0) {
101 return absl::InternalError(
"Composite bitmap is empty after render");
104 const int out_w =
static_cast<int>(composite.width() * req.
scale);
105 const int out_h =
static_cast<int>(composite.height() * req.
scale);
110 return rgba_or.status();
111 auto rgba = std::move(rgba_or).value();
119#ifdef YAZE_CLI_HAS_PNG
120 auto png_or =
EncodePng(rgba, out_w, out_h);
122 return png_or.status();
125 result.
png_data = std::move(png_or).value();
130 result.
width = out_w;
138 return absl::InvalidArgumentError(
139 absl::StrFormat(
"Invalid room_id 0x%02X", room_id));
142 std::lock_guard<std::mutex> lock(
mu_);
149 meta.room_id = room_id;
154 meta.effect =
static_cast<int>(room.
effect());
155 meta.collision =
static_cast<int>(room.
collision());
156 meta.tag1 =
static_cast<int>(room.
tag1());
157 meta.tag2 =
static_cast<int>(room.
tag2());
160 meta.object_count =
static_cast<int>(room.
GetTileObjects().size());
161 meta.sprite_count =
static_cast<int>(room.
GetSprites().size());
171 SDL_Surface* surface = bitmap.
surface();
173 return absl::InternalError(
"Bitmap has no SDL surface");
176 const int src_w = bitmap.
width();
177 const int src_h = bitmap.
height();
182 const uint8_t* indexed = bitmap.
data();
184 return absl::InternalError(
"Bitmap has no pixel data");
188 std::vector<uint8_t> rgba(
static_cast<size_t>(out_w) * out_h * 4, 0xFF);
191 for (
int oy = 0; oy < out_h; ++oy) {
192 const int sy = oy * src_h / out_h;
193 for (
int ox = 0; ox < out_w; ++ox) {
194 const int sx = ox * src_w / out_w;
195 if (sx >= src_w || sy >= src_h)
197 const uint8_t idx = indexed[sy * src_w + sx];
199 uint8_t r = 0, g = 0, b = 0;
200 if (pal &&
static_cast<int>(idx) < pal->ncolors) {
201 r = pal->colors[idx].r;
202 g = pal->colors[idx].g;
203 b = pal->colors[idx].b;
206 const size_t base =
static_cast<size_t>((oy * out_w + ox) * 4);
210 rgba[base + 3] = 255;
219 uint32_t flags,
float scale) {
224 for (
int tx = 0; tx < 64; ++tx) {
225 draw.
DrawLine(
static_cast<float>(tx * 8), 0,
static_cast<float>(tx * 8),
226 511, 60, 60, 60, 80);
228 for (
int ty = 0; ty < 64; ++ty) {
229 draw.
DrawLine(0,
static_cast<float>(ty * 8), 511,
230 static_cast<float>(ty * 8), 60, 60, 60, 80);
238 for (
int ty = 0; ty < 64; ++ty) {
239 for (
int tx = 0; tx < 64; ++tx) {
240 const uint8_t val = cc.
tiles[ty * 64 + tx];
245 const bool is_track =
246 (val >= 0xB0 && val <= 0xBE) || (val >= 0xD0 && val <= 0xD3);
250 const auto c = CollisionColor(val);
254 static_cast<float>(ty * 8), 8.0f, 8.0f, c.r, c.g,
264 const float px =
static_cast<float>(obj.x() * 8);
265 const float py =
static_cast<float>(obj.y() * 8);
267 static_cast<float>(std::max(1,
static_cast<int>(obj.width_)) * 8);
269 static_cast<float>(std::max(1,
static_cast<int>(obj.height_)) * 8);
270 draw.
DrawRect(px, py, pw, ph, 255, 200, 0, 200);
278 const float px =
static_cast<float>(spr.nx() * 16);
279 const float py =
static_cast<float>(spr.ny() * 16);
281 draw.
DrawRect(px, py, 16.0f, 16.0f, 255, 60, 60, 230);
287 draw.
DrawLine(256, 0, 256, 511, 200, 200, 255, 120);
288 draw.
DrawLine(0, 256, 511, 256, 200, 200, 255, 120);
292#ifdef YAZE_CLI_HAS_PNG
294 const std::vector<uint8_t>& rgba,
int width,
int height) {
296 png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
298 return absl::InternalError(
"png_create_write_struct failed");
300 png_infop info = png_create_info_struct(png);
302 png_destroy_write_struct(&png,
nullptr);
303 return absl::InternalError(
"png_create_info_struct failed");
306 std::vector<uint8_t> output;
309 if (setjmp(png_jmpbuf(png))) {
310 png_destroy_write_struct(&png, &info);
311 return absl::InternalError(
"PNG encoding error");
314 png_set_write_fn(png, &ctx, PngWrite, PngFlush);
315 png_set_IHDR(png, info,
static_cast<uint32_t
>(width),
316 static_cast<uint32_t
>(height), 8, PNG_COLOR_TYPE_RGBA,
317 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
318 PNG_FILTER_TYPE_DEFAULT);
319 png_write_info(png, info);
321 std::vector<png_bytep> rows(
static_cast<size_t>(height));
322 for (
int row = 0; row < height; ++row) {
323 rows[
static_cast<size_t>(row)] =
const_cast<png_bytep
>(
324 rgba.data() +
static_cast<size_t>(row) * width * 4);
326 png_write_image(png, rows.data());
327 png_write_end(png,
nullptr);
328 png_destroy_write_struct(&png, &info);
334 const std::vector<uint8_t>& ,
int ,
int ) {
335 return absl::UnimplementedError(
"PNG encoding unavailable (libpng missing)");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
void DrawRect(float x, float y, float w, float h, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
void DrawLine(float x0, float y0, float x1, float y1, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
void DrawFilledRect(float x, float y, float w, float h, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
zelda3::GameData * game_data_
absl::StatusOr< std::vector< uint8_t > > EncodePng(const std::vector< uint8_t > &rgba, int width, int height)
RenderService(Rom *rom, zelda3::GameData *game_data)
absl::StatusOr< RoomMetadata > GetDungeonRoomMetadata(int room_id)
void ApplyOverlays(std::vector< uint8_t > &rgba, int width, int height, const zelda3::Room &room, uint32_t flags, float scale)
absl::StatusOr< std::vector< uint8_t > > BitmapToRgba(const gfx::Bitmap &bitmap, int width, int height)
absl::StatusOr< RenderResult > RenderDungeonRoom(const RenderRequest &req)
Represents a bitmap image optimized for SNES ROM hacking.
const uint8_t * data() const
SDL_Surface * surface() const
RoomLayerManager - Manages layer visibility and compositing.
const CustomCollisionMap & custom_collision() const
uint16_t message_id() const
void LoadRoomGraphics(uint8_t entrance_blockset=0xFF)
void RenderRoomGraphics()
CollisionKey collision() const
gfx::Bitmap & GetCompositeBitmap(RoomLayerManager &layer_mgr)
Get a composite bitmap of all layers merged.
uint8_t spriteset() const
const std::vector< zelda3::Sprite > & GetSprites() const
const std::vector< RoomObject > & GetTileObjects() const
uint8_t layout_id() const
bool has_custom_collision() const
void SetGameData(GameData *data)
constexpr uint32_t kCollision
constexpr uint32_t kSprites
constexpr uint32_t kCameraQuads
constexpr uint32_t kObjects
constexpr uint32_t kTrack
TileColor CollisionColor(uint8_t tile)
Room LoadRoomFromRom(Rom *rom, int room_id)
constexpr int kNumberOfRooms
SDL2/SDL3 compatibility layer.
std::vector< uint8_t > png_data
std::array< uint8_t, 64 *64 > tiles