diff --git a/src/Apps/Game/tools/asset_pipeline/SpriteAssetTool.cpp b/src/Apps/Game/tools/asset_pipeline/SpriteAssetTool.cpp index 7abf5c1..54d57eb 100644 --- a/src/Apps/Game/tools/asset_pipeline/SpriteAssetTool.cpp +++ b/src/Apps/Game/tools/asset_pipeline/SpriteAssetTool.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,7 @@ namespace struct Image { - std::vector pixels; + std::vector pixels; int32_t width; int32_t height; @@ -65,6 +66,52 @@ namespace } }; + static uint16_t PackRgba5551(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + return static_cast( + ((static_cast(r) >> 3) << 11) | + ((static_cast(g) >> 3) << 6) | + ((static_cast(b) >> 3) << 1) | + (a ? 1u : 0u)); + } + + static Uint32 ReadSurfacePixel(const SDL_Surface* surface, int32_t x, int32_t y) + { + const uint8_t* row = static_cast(surface->pixels) + + static_cast(y) * static_cast(surface->pitch); + const uint8_t* src = row + static_cast(x) * static_cast(surface->format->BytesPerPixel); + + switch (surface->format->BytesPerPixel) + { + case 1: + return src[0]; + case 2: + { + uint16_t pixel = 0; + std::memcpy(&pixel, src, sizeof(pixel)); + return static_cast(pixel); + } + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + { + return (static_cast(src[0]) << 16) | + (static_cast(src[1]) << 8) | + static_cast(src[2]); + } + return static_cast(src[0]) | + (static_cast(src[1]) << 8) | + (static_cast(src[2]) << 16); + case 4: + { + Uint32 pixel = 0; + std::memcpy(&pixel, src, sizeof(pixel)); + return pixel; + } + default: + return 0; + } + } + struct AtlasRegion { const SourceSpec* source; @@ -105,39 +152,34 @@ namespace return false; } - SDL_Surface* rgba_surface = SDL_ConvertSurfaceFormat(loaded_surface, SDL_PIXELFORMAT_RGBA8888, 0); - SDL_FreeSurface(loaded_surface); - if (rgba_surface == nullptr) - { - std::cerr << "SDL_ConvertSurfaceFormat failed: " << SDL_GetError() << std::endl; - return false; - } - - if (SDL_LockSurface(rgba_surface) != 0) + if (SDL_LockSurface(loaded_surface) != 0) { std::cerr << "SDL_LockSurface failed: " << SDL_GetError() << std::endl; - SDL_FreeSurface(rgba_surface); + SDL_FreeSurface(loaded_surface); return false; } - image.width = rgba_surface->w; - image.height = rgba_surface->h; + image.width = loaded_surface->w; + image.height = loaded_surface->h; image.pixels.assign(static_cast(image.width) * static_cast(image.height), 0); for (int32_t y = 0; y < image.height; ++y) { - const uint8_t* src_row = static_cast(rgba_surface->pixels) + y * rgba_surface->pitch; - const uint32_t* src_pixels = reinterpret_cast(src_row); for (int32_t x = 0; x < image.width; ++x) { - const uint32_t pixel = src_pixels[x]; + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; + uint8_t a = 0; + const Uint32 surface_pixel = ReadSurfacePixel(loaded_surface, x, y); + SDL_GetRGBA(surface_pixel, loaded_surface->format, &r, &g, &b, &a); image.pixels[static_cast(y) * static_cast(image.width) + static_cast(x)] = - (pixel & 0xFFu) == 0u ? 0x00000000u : pixel; + a == 0 ? 0u : PackRgba5551(r, g, b, a); } } - SDL_UnlockSurface(rgba_surface); - SDL_FreeSurface(rgba_surface); + SDL_UnlockSurface(loaded_surface); + SDL_FreeSurface(loaded_surface); return image.is_valid(); } @@ -245,11 +287,11 @@ namespace return atlas_height > 0; } - static std::vector BuildAtlasPixels(const std::vector& regions, int32_t atlas_height) + static std::vector BuildAtlasPixels(const std::vector& regions, int32_t atlas_height) { - std::vector atlas_pixels( + std::vector atlas_pixels( static_cast(AtlasWidth) * static_cast(atlas_height), - 0x00000000u); + 0u); for (size_t i = 0; i < regions.size(); ++i) { @@ -262,28 +304,15 @@ namespace static_cast(region.x); std::copy( - region.image.pixels.begin() + static_cast::difference_type>(src_offset), - region.image.pixels.begin() + static_cast::difference_type>(src_offset + static_cast(region.image.width)), - atlas_pixels.begin() + static_cast::difference_type>(dst_offset)); + region.image.pixels.begin() + static_cast::difference_type>(src_offset), + region.image.pixels.begin() + static_cast::difference_type>(src_offset + static_cast(region.image.width)), + atlas_pixels.begin() + static_cast::difference_type>(dst_offset)); } } return atlas_pixels; } - static uint16_t rgba8888_to_rgba5551(uint32_t c) - { - uint32_t r = (c >> 24) & 0xFFu; - uint32_t g = (c >> 16) & 0xFFu; - uint32_t b = (c >> 8) & 0xFFu; - uint32_t a = c & 0xFFu; - return static_cast( - ((r >> 3) << 11) | - ((g >> 3) << 6) | - ((b >> 3) << 1) | - (a ? 1u : 0u)); - } - static void WritePixel5551(std::ofstream& file, uint16_t pixel) { file << "0x" @@ -299,7 +328,7 @@ namespace static bool WriteAtlasHeader( const std::vector& regions, - const std::vector& atlas_pixels, + const std::vector& atlas_pixels, int32_t atlas_height) { std::ofstream file(OutputHeaderPath); @@ -326,7 +355,7 @@ namespace const size_t end = std::min(i + 12, atlas_pixels.size()); for (size_t j = i; j < end; ++j) { - WritePixel5551(file, rgba8888_to_rgba5551(atlas_pixels[j])); + WritePixel5551(file, atlas_pixels[j]); file << ", "; } file << "\n"; @@ -375,7 +404,7 @@ namespace return false; } - const std::vector atlas_pixels = BuildAtlasPixels(regions, atlas_height); + const std::vector atlas_pixels = BuildAtlasPixels(regions, atlas_height); if (!WriteAtlasHeader(regions, atlas_pixels, atlas_height)) { return false; diff --git a/src/Core/Asset/SpriteAssetLoader.cpp b/src/Core/Asset/SpriteAssetLoader.cpp index ce1cf3e..d306a3b 100644 --- a/src/Core/Asset/SpriteAssetLoader.cpp +++ b/src/Core/Asset/SpriteAssetLoader.cpp @@ -163,6 +163,10 @@ namespace Asset { return false; } + if (image.format != RenderData::PixelFormat::RGBA5551) + { + return false; + } size_t pixelCount = 0; if (!CheckPixelCount(static_cast(image.width), static_cast(image.height), pixelCount)) @@ -182,21 +186,10 @@ namespace Asset WriteU32LE(file, static_cast(image.height)); WriteU32LE(file, SpriteFormatRgba5551); - if (image.format == RenderData::PixelFormat::RGBA5551) + const uint16_t* pixels16 = static_cast(image.pixels); + for (size_t i = 0; i < pixelCount; ++i) { - const uint16_t* pixels16 = static_cast(image.pixels); - for (size_t i = 0; i < pixelCount; ++i) - { - WritePixel5551(file, pixels16[i]); - } - } - else - { - const uint32_t* pixels32 = static_cast(image.pixels); - for (size_t i = 0; i < pixelCount; ++i) - { - WritePixel5551(file, RenderData::rgba8888_to_rgba5551(pixels32[i])); - } + WritePixel5551(file, pixels16[i]); } return file.good(); diff --git a/src/Core/Draw2D/DrawContext.cpp b/src/Core/Draw2D/DrawContext.cpp index 72d52e1..c05233f 100644 --- a/src/Core/Draw2D/DrawContext.cpp +++ b/src/Core/Draw2D/DrawContext.cpp @@ -124,53 +124,9 @@ namespace Core if (dst_x + end_dx > screen_w) end_dx = screen_w - dst_x; if (dst_y + end_dy > screen_h) end_dy = screen_h - dst_y; - if (img.format == RenderData::PixelFormat::RGBA5551) - { - Core::FramePixel* dst = static_cast(frameBuffer->get_buffer()); - const int32_t fb_w = frameBuffer->get_width(); - const uint16_t* src = static_cast(img.pixels); - - if (scale == 1) - { - for (int32_t sy = start_dy; sy < end_dy; ++sy) - { - const int32_t read_y = flip_v ? (src_y + src_h - 1 - sy) : (src_y + sy); - const int32_t dst_y_abs = dst_y + sy; - Core::FramePixel* dst_row = dst + dst_y_abs * fb_w; - - for (int32_t sx = start_dx; sx < end_dx; ++sx) - { - const int32_t read_x = flip_h ? (src_x + src_w - 1 - sx) : (src_x + sx); - const uint16_t pixel = src[read_y * img_w + read_x]; - if (!RenderData::rgba5551_is_opaque(pixel)) continue; - dst_row[dst_x + sx] = RenderData::rgba5551_to_rgb565(pixel); - } - } - return; - } - - for (int32_t dy_abs = start_dy; dy_abs < end_dy; ++dy_abs) - { - const int32_t sy = dy_abs / scale; - const int32_t read_y = flip_v ? (src_y + src_h - 1 - sy) : (src_y + sy); - const int32_t dst_y_abs = dst_y + dy_abs; - Core::FramePixel* dst_row = dst + dst_y_abs * fb_w; - - for (int32_t dx_abs = start_dx; dx_abs < end_dx; ++dx_abs) - { - const int32_t sx = dx_abs / scale; - const int32_t read_x = flip_h ? (src_x + src_w - 1 - sx) : (src_x + sx); - const uint16_t pixel = src[read_y * img_w + read_x]; - if (!RenderData::rgba5551_is_opaque(pixel)) continue; - dst_row[dst_x + dx_abs] = RenderData::rgba5551_to_rgb565(pixel); - } - } - return; - } - - const bool has_key = img.has_color_key; - const uint32_t key = RenderData::rgba5551_to_rgba8888(img.color_key); - const uint32_t* src32 = static_cast(img.pixels); + Core::FramePixel* dst = static_cast(frameBuffer->get_buffer()); + const int32_t fb_w = frameBuffer->get_width(); + const uint16_t* src = static_cast(img.pixels); if (scale == 1) { @@ -178,14 +134,14 @@ namespace Core { const int32_t read_y = flip_v ? (src_y + src_h - 1 - sy) : (src_y + sy); const int32_t dst_y_abs = dst_y + sy; + Core::FramePixel* dst_row = dst + dst_y_abs * fb_w; for (int32_t sx = start_dx; sx < end_dx; ++sx) { const int32_t read_x = flip_h ? (src_x + src_w - 1 - sx) : (src_x + sx); - const uint32_t pixel = src32[read_y * img_w + read_x]; - if ((pixel & 0xFFu) == 0u) continue; - if (has_key && pixel == key) continue; - frameBuffer->set_pixel_unsafe(dst_x + sx, dst_y_abs, pixel); + const uint16_t pixel = src[read_y * img_w + read_x]; + if (!RenderData::rgba5551_is_opaque(pixel)) continue; + dst_row[dst_x + sx] = RenderData::rgba5551_to_rgb565(pixel); } } return; @@ -196,15 +152,15 @@ namespace Core const int32_t sy = dy_abs / scale; const int32_t read_y = flip_v ? (src_y + src_h - 1 - sy) : (src_y + sy); const int32_t dst_y_abs = dst_y + dy_abs; + Core::FramePixel* dst_row = dst + dst_y_abs * fb_w; for (int32_t dx_abs = start_dx; dx_abs < end_dx; ++dx_abs) { const int32_t sx = dx_abs / scale; const int32_t read_x = flip_h ? (src_x + src_w - 1 - sx) : (src_x + sx); - const uint32_t pixel = src32[read_y * img_w + read_x]; - if ((pixel & 0xFFu) == 0u) continue; - if (has_key && pixel == key) continue; - frameBuffer->set_pixel_unsafe(dst_x + dx_abs, dst_y_abs, pixel); + const uint16_t pixel = src[read_y * img_w + read_x]; + if (!RenderData::rgba5551_is_opaque(pixel)) continue; + dst_row[dst_x + dx_abs] = RenderData::rgba5551_to_rgb565(pixel); } } } diff --git a/src/Core/RenderData/Image.h b/src/Core/RenderData/Image.h index c7d383b..e758e67 100644 --- a/src/Core/RenderData/Image.h +++ b/src/Core/RenderData/Image.h @@ -6,7 +6,6 @@ namespace RenderData { enum class PixelFormat { - RGBA8888, RGBA5551 }; @@ -24,31 +23,6 @@ namespace RenderData return (c & 0x1u) != 0; } - inline uint32_t rgba5551_to_rgba8888(uint16_t c) - { - uint32_t r = (c >> 11) & 0x1Fu; - uint32_t g = (c >> 6) & 0x1Fu; - uint32_t b = (c >> 1) & 0x1Fu; - uint32_t a = c & 0x1u; - return (r << 27) | (r << 24) | - (g << 19) | (g << 16) | - (b << 11) | (b << 8) | - (a ? 0xFFu : 0x00u); - } - - inline uint16_t rgba8888_to_rgba5551(uint32_t c) - { - uint32_t r = (c >> 24) & 0xFFu; - uint32_t g = (c >> 16) & 0xFFu; - uint32_t b = (c >> 8) & 0xFFu; - uint32_t a = c & 0xFFu; - return static_cast( - ((r >> 3) << 11) | - ((g >> 3) << 6) | - ((b >> 3) << 1) | - (a ? 1u : 0u)); - } - struct Image { const void* pixels; @@ -61,29 +35,25 @@ namespace RenderData Image() : pixels(nullptr), width(0), height(0), color_key(0), has_color_key(false), - format(PixelFormat::RGBA8888) {} + format(PixelFormat::RGBA5551) {} Image(const void* pixels, int32_t width, int32_t height, - PixelFormat fmt = PixelFormat::RGBA8888) + PixelFormat fmt = PixelFormat::RGBA5551) : pixels(pixels), width(width), height(height), color_key(0), has_color_key(false), format(fmt) {} Image(const void* pixels, int32_t width, int32_t height, uint16_t color_key, - PixelFormat fmt = PixelFormat::RGBA8888) + PixelFormat fmt = PixelFormat::RGBA5551) : pixels(pixels), width(width), height(height), color_key(color_key), has_color_key(true), format(fmt) {} uint32_t get_pixel(int32_t x, int32_t y) const { - if (format == PixelFormat::RGBA5551) - { - return rgba5551_to_rgba8888( - static_cast(pixels)[y * width + x]); - } - return static_cast(pixels)[y * width + x]; + return static_cast( + static_cast(pixels)[y * width + x]); } }; } diff --git a/src/Rasterizer/SpriteRasterizer.cpp b/src/Rasterizer/SpriteRasterizer.cpp index 836a25f..10b8c81 100644 --- a/src/Rasterizer/SpriteRasterizer.cpp +++ b/src/Rasterizer/SpriteRasterizer.cpp @@ -5,6 +5,23 @@ namespace Rasterizer { + namespace + { + inline uint16_t rgba5551_to_rgb565(uint16_t c) + { + const uint16_t r = static_cast((c >> 11) & 0x1Fu); + const uint16_t g = static_cast((c >> 6) & 0x1Fu); + const uint16_t b = static_cast((c >> 1) & 0x1Fu); + const uint16_t g6 = static_cast((g << 1) | (g >> 4)); + return static_cast((r << 11) | (g6 << 5) | b); + } + + inline bool rgba5551_is_opaque(uint16_t c) + { + return (c & 0x1u) != 0; + } + } + void SpriteRasterizer::DrawImage(const RenderData::Image& image, int32_t x, int32_t y) { DrawSprite(RenderData::Sprite(&image), x, y); @@ -27,18 +44,21 @@ namespace Rasterizer return; } + Core::FramePixel* dst = static_cast(frameBuffer->get_buffer()); + const int32_t fb_w = frameBuffer->get_width(); + for (int32_t sy = minY; sy < maxY; ++sy) { + Core::FramePixel* dst_row = dst + (y + sy) * fb_w; for (int32_t sx = minX; sx < maxX; ++sx) { - const uint32_t color = sprite.get_pixel_fast(sx, sy); - const uint8_t alpha = static_cast(color & 0xFF); - if (alpha == 0) + const uint16_t color = sprite.get_pixel_fast(sx, sy); + if (!rgba5551_is_opaque(color)) { continue; } - frameBuffer->set_pixel(x + sx, y + sy, color); + dst_row[x + sx] = rgba5551_to_rgb565(color); } } } diff --git a/src/RenderData/Image.h b/src/RenderData/Image.h index 1d6f8da..bf1f39b 100644 --- a/src/RenderData/Image.h +++ b/src/RenderData/Image.h @@ -11,7 +11,7 @@ namespace RenderData private: int32_t width; int32_t height; - std::vector pixels; + std::vector pixels; static bool calculate_pixel_count(int32_t width, int32_t height, size_t& count) { @@ -47,7 +47,7 @@ namespace RenderData } } - Image(int32_t width, int32_t height, const std::vector& pixels) : width(0), height(0) + Image(int32_t width, int32_t height, const std::vector& pixels) : width(0), height(0) { size_t pixelCount = 0; if (calculate_pixel_count(width, height, pixelCount) && pixels.size() == pixelCount) @@ -64,9 +64,9 @@ namespace RenderData size_t total_pixels() const { return pixels.size(); } - const uint32_t* data() const { return pixels.data(); } + const uint16_t* data() const { return pixels.data(); } - uint32_t* data() { return pixels.data(); } + uint16_t* data() { return pixels.data(); } bool is_valid() const { @@ -74,7 +74,7 @@ namespace RenderData return calculate_pixel_count(width, height, pixelCount) && pixels.size() == pixelCount; } - uint32_t get_pixel(int32_t x, int32_t y) const + uint16_t get_pixel(int32_t x, int32_t y) const { if (x < 0 || x >= width || y < 0 || y >= height) { @@ -85,13 +85,13 @@ namespace RenderData return pixels[index]; } - uint32_t get_pixel_fast(int32_t x, int32_t y) const + uint16_t get_pixel_fast(int32_t x, int32_t y) const { size_t index = static_cast(y) * width + x; return pixels[index]; } - void set_pixel(int32_t x, int32_t y, uint32_t color) + void set_pixel(int32_t x, int32_t y, uint16_t color) { if (x < 0 || x >= width || y < 0 || y >= height) { diff --git a/src/RenderData/Sprite.h b/src/RenderData/Sprite.h index 743e3ec..cc50773 100644 --- a/src/RenderData/Sprite.h +++ b/src/RenderData/Sprite.h @@ -45,7 +45,7 @@ namespace RenderData return x + width <= image->get_width() && y + height <= image->get_height(); } - uint32_t get_pixel(int32_t localX, int32_t localY) const + uint16_t get_pixel(int32_t localX, int32_t localY) const { if (!is_valid() || localX < 0 || localX >= width || localY < 0 || localY >= height) { @@ -55,7 +55,7 @@ namespace RenderData return image->get_pixel(x + localX, y + localY); } - uint32_t get_pixel_fast(int32_t localX, int32_t localY) const + uint16_t get_pixel_fast(int32_t localX, int32_t localY) const { return image->get_pixel_fast(x + localX, y + localY); } diff --git a/tests/render_pipeline_tests.cpp b/tests/render_pipeline_tests.cpp index fafb8ea..be106a2 100644 --- a/tests/render_pipeline_tests.cpp +++ b/tests/render_pipeline_tests.cpp @@ -53,6 +53,21 @@ namespace assert(pixels[1] == 0xF800u); } + void TestImageDefaultsToRgba5551AndReturnsRawPixels() + { + const uint16_t default_pixels[] = { 0xF801u, 0x0000u }; + RenderData::Image default_image(default_pixels, 1, 1); + assert(default_image.format == RenderData::PixelFormat::RGBA5551); + + const uint16_t explicit_pixels[] = { 0x07C1u }; + RenderData::Image explicit_image( + explicit_pixels, + 1, + 1, + RenderData::PixelFormat::RGBA5551); + assert(explicit_image.get_pixel(0, 0) == 0x07C1u); + } + void TestDrawSpriteUsesOneBitAlpha() { const uint16_t sprite_pixels[] = { @@ -199,6 +214,7 @@ int main() { TestFramePixelIsRgb565(); TestFrameBufferStoresRgb565(); + TestImageDefaultsToRgba5551AndReturnsRawPixels(); TestDrawSpriteUsesOneBitAlpha(); TestSpriteAssetLoaderRoundTripsRgba5551(); TestDrawTextUsesBitMaskAndColor();