9#include "absl/status/status.h"
10#include "absl/strings/numbers.h"
11#include "absl/strings/str_format.h"
19#ifdef YAZE_CLI_HAS_PNG
30 if (!std::isfinite(scale) || scale < 0.25f || scale > 8.0f) {
31 return absl::InvalidArgumentError(
32 "Render scale must be finite and between 0.25 and 8.0");
34 return absl::OkStatus();
37#ifdef YAZE_CLI_HAS_PNG
40 std::vector<uint8_t>* buf;
43void PngWrite(png_structp png, png_bytep data, png_size_t len) {
44 auto* ctx =
static_cast<PngCtx*
>(png_get_io_ptr(png));
45 ctx->buf->insert(ctx->buf->end(), data, data + len);
48void PngFlush(png_structp ) {}
60 if (tile == 0xB0 || tile == 0xB1)
61 return {0, 210, 170, 150};
62 if (tile >= 0xB2 && tile <= 0xB5)
63 return {0, 190, 255, 150};
65 return {255, 215, 0, 170};
66 if (tile >= 0xB7 && tile <= 0xBA)
67 return {255, 80, 0, 200};
68 if (tile >= 0xBB && tile <= 0xBE)
69 return {80, 150, 255, 150};
70 if (tile >= 0xD0 && tile <= 0xD3)
71 return {210, 0, 255, 170};
72 return {140, 140, 140, 120};
79 if (!absl::SimpleAtof(value, &scale)) {
80 return absl::InvalidArgumentError(
81 absl::StrFormat(
"Invalid scale value: %s", value));
83 if (
const auto status = ValidateRenderScale(scale); !status.ok()) {
90 : rom_(rom), game_data_(game_data) {}
94 if (
const auto status = ValidateRenderScale(req.
scale); !status.ok()) {
98 return absl::FailedPreconditionError(
"ROM not loaded");
101 return absl::FailedPreconditionError(
"GameData not available");
104 return absl::InvalidArgumentError(
105 absl::StrFormat(
"Invalid room_id 0x%02X", req.
room_id));
108#ifndef YAZE_CLI_HAS_PNG
109 return absl::UnimplementedError(
"PNG encoding unavailable (libpng missing)");
112 std::lock_guard<std::mutex> lock(
mu_);
131 if (!composite.is_active() || composite.width() <= 0 ||
132 composite.height() <= 0) {
133 return absl::InternalError(
"Composite bitmap is empty after render");
136 const int out_w =
static_cast<int>(composite.width() * req.
scale);
137 const int out_h =
static_cast<int>(composite.height() * req.
scale);
142 return rgba_or.status();
143 auto rgba = std::move(rgba_or).value();
151 auto png_or =
EncodePng(rgba, out_w, out_h);
153 return png_or.status();
156 result.
png_data = std::move(png_or).value();
157 result.
width = out_w;
165 return absl::InvalidArgumentError(
166 absl::StrFormat(
"Invalid room_id 0x%02X", room_id));
169 std::lock_guard<std::mutex> lock(
mu_);
183 meta.
tag1 =
static_cast<int>(room.
tag1());
184 meta.
tag2 =
static_cast<int>(room.
tag2());
198 SDL_Surface* surface = bitmap.
surface();
200 return absl::InternalError(
"Bitmap has no SDL surface");
203 const int src_w = bitmap.
width();
204 const int src_h = bitmap.
height();
209 const uint8_t* indexed = bitmap.
data();
211 return absl::InternalError(
"Bitmap has no pixel data");
215 std::vector<uint8_t> rgba(
static_cast<size_t>(out_w) * out_h * 4, 0xFF);
218 for (
int oy = 0; oy < out_h; ++oy) {
219 const int sy = oy * src_h / out_h;
220 for (
int ox = 0; ox < out_w; ++ox) {
221 const int sx = ox * src_w / out_w;
222 if (sx >= src_w || sy >= src_h)
224 const uint8_t idx = indexed[sy * src_w + sx];
226 uint8_t r = 0, g = 0, b = 0;
227 if (pal &&
static_cast<int>(idx) < pal->ncolors) {
228 r = pal->colors[idx].r;
229 g = pal->colors[idx].g;
230 b = pal->colors[idx].b;
233 const size_t base =
static_cast<size_t>((oy * out_w + ox) * 4);
237 rgba[base + 3] = 255;
246 uint32_t flags,
float scale) {
251 for (
int tx = 0; tx < 64; ++tx) {
252 draw.
DrawLine(
static_cast<float>(tx * 8), 0,
static_cast<float>(tx * 8),
253 511, 60, 60, 60, 80);
255 for (
int ty = 0; ty < 64; ++ty) {
256 draw.
DrawLine(0,
static_cast<float>(ty * 8), 511,
257 static_cast<float>(ty * 8), 60, 60, 60, 80);
265 for (
int ty = 0; ty < 64; ++ty) {
266 for (
int tx = 0; tx < 64; ++tx) {
267 const uint8_t val = cc.
tiles[ty * 64 + tx];
272 const bool is_track =
273 (val >= 0xB0 && val <= 0xBE) || (val >= 0xD0 && val <= 0xD3);
277 const auto c = CollisionColor(val);
281 static_cast<float>(ty * 8), 8.0f, 8.0f, c.r, c.g,
291 const float px =
static_cast<float>(obj.x() * 8);
292 const float py =
static_cast<float>(obj.y() * 8);
294 static_cast<float>(std::max(1,
static_cast<int>(obj.width_)) * 8);
296 static_cast<float>(std::max(1,
static_cast<int>(obj.height_)) * 8);
297 draw.
DrawRect(px, py, pw, ph, 255, 200, 0, 200);
305 const float px =
static_cast<float>(spr.nx() * 16);
306 const float py =
static_cast<float>(spr.ny() * 16);
308 draw.
DrawRect(px, py, 16.0f, 16.0f, 255, 60, 60, 230);
314 draw.
DrawLine(256, 0, 256, 511, 200, 200, 255, 120);
315 draw.
DrawLine(0, 256, 511, 256, 200, 200, 255, 120);
319#ifdef YAZE_CLI_HAS_PNG
321 const std::vector<uint8_t>& rgba,
int width,
int height) {
323 png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
325 return absl::InternalError(
"png_create_write_struct failed");
327 png_infop info = png_create_info_struct(png);
329 png_destroy_write_struct(&png,
nullptr);
330 return absl::InternalError(
"png_create_info_struct failed");
333 std::vector<uint8_t> output;
336 if (setjmp(png_jmpbuf(png))) {
337 png_destroy_write_struct(&png, &info);
338 return absl::InternalError(
"PNG encoding error");
341 png_set_write_fn(png, &ctx, PngWrite, PngFlush);
342 png_set_IHDR(png, info,
static_cast<uint32_t
>(width),
343 static_cast<uint32_t
>(height), 8, PNG_COLOR_TYPE_RGBA,
344 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
345 PNG_FILTER_TYPE_DEFAULT);
346 png_write_info(png, info);
348 std::vector<png_bytep> rows(
static_cast<size_t>(height));
349 for (
int row = 0; row < height; ++row) {
350 rows[
static_cast<size_t>(row)] =
const_cast<png_bytep
>(
351 rgba.data() +
static_cast<size_t>(row) * width * 4);
353 png_write_image(png, rows.data());
354 png_write_end(png,
nullptr);
355 png_destroy_write_struct(&png, &info);
361 const std::vector<uint8_t>& ,
int ,
int ) {
362 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.
void ApplyLayerMerging(const LayerMergeType &merge_type)
void ApplyRoomEffect(EffectKey effect)
const CustomCollisionMap & custom_collision() const
uint16_t message_id() const
void RenderRoomGraphics()
CollisionKey collision() const
void LoadRoomGraphics(std::optional< uint8_t > entrance_blockset=std::nullopt)
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
const LayerMergeType & layer_merging() 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
absl::Status ValidateRenderScale(float scale)
TileColor CollisionColor(uint8_t tile)
absl::StatusOr< float > ParseRenderScale(absl::string_view value)
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