From 9bb90784ec445f9c582527b222c49108b43e0bb1 Mon Sep 17 00:00:00 2001 From: cromachina <82557197+cromachina@users.noreply.github.com> Date: Fri, 8 May 2026 18:50:17 -0400 Subject: [PATCH 1/5] Fix Thumbnail-Sai1 to do the BGRA to RGBA swap --- include/sai.hpp | 2 +- samples/Thumbnail-Sai1.cpp | 14 ++++++++++++++ source/document.cpp | 28 ---------------------------- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/include/sai.hpp b/include/sai.hpp index ffc03fe..e99bbf5 100644 --- a/include/sai.hpp +++ b/include/sai.hpp @@ -352,7 +352,7 @@ class Document : public VirtualFileSystem // Returns (0,0) if an error has occured std::tuple GetCanvasSize(); - // Returns (RGBA Pixel Data, Width, Height). + // Returns (BGRA Pixel Data, Width, Height). // Returns (null,0,0) if an error has occured. std::tuple, std::uint32_t, std::uint32_t> GetThumbnail(); diff --git a/samples/Thumbnail-Sai1.cpp b/samples/Thumbnail-Sai1.cpp index 62084f5..9c37968 100644 --- a/samples/Thumbnail-Sai1.cpp +++ b/samples/Thumbnail-Sai1.cpp @@ -11,6 +11,7 @@ Sample code for extracting the thumbnail image from a user-created sai file #include #include #include +#include #include "stb_image_write.h" @@ -19,6 +20,18 @@ const char* const Help "\tThumbnail (filename) (output)\n" "\tWunkolo - Wunkolo@gmail.com"; +void bgraToRgba(std::byte* Pixels, std::size_t PixelCount) +{ + std::size_t i = 0; + std::size_t ByteCount = PixelCount * 4; + + while(i < ByteCount) + { + std::swap(Pixels[i], Pixels[i + 2]); + i += 4; + } +} + int main(int argc, char* argv[]) { if( argc < 3 ) @@ -39,6 +52,7 @@ int main(int argc, char* argv[]) Width = Height = 0; std::unique_ptr Pixels = {}; std::tie(Pixels, Width, Height) = FileIn.GetThumbnail(); + bgraToRgba(Pixels.get(), Width * Height); stbi_write_png(argv[2], Width, Height, 4, Pixels.get(), 0); diff --git a/source/document.cpp b/source/document.cpp index 269d625..2ee51af 100644 --- a/source/document.cpp +++ b/source/document.cpp @@ -3,9 +3,6 @@ #include -#ifdef __x86_64__ -#include -#endif namespace sai { @@ -52,31 +49,6 @@ std::tuple, std::uint32_t, std::uint32_t> Document: Thumbnail->Read({Pixels.get(), PixelCount * sizeof(std::uint32_t)}); -#if 0 - //// BGRA to RGBA - std::size_t i = 0; - - //// Simd speedup, four pixels at a time - while( i < ((PixelCount * sizeof(std::uint32_t)) & ~0xF) ) - { - const __m128i Swizzle - = _mm_set_epi8(15, 12, 13, 14, 11, 8, 9, 10, 7, 4, 5, 6, 3, 0, 1, 2); - - __m128i QuadPixel = _mm_loadu_si128(reinterpret_cast<__m128i*>(&Pixels[i])); - - QuadPixel = _mm_shuffle_epi8(QuadPixel, Swizzle); - - _mm_store_si128(reinterpret_cast<__m128i*>(&Pixels[i]), QuadPixel); - - i += (sizeof(std::uint32_t) * 4); - } - - for( ; i < PixelCount * sizeof(std::uint32_t); i += sizeof(std::uint32_t) ) - { - std::swap(Pixels[i], Pixels[i + 2]); - } -#endif - return std::make_tuple(std::move(Pixels), Header.Width, Header.Height); } return std::make_tuple(nullptr, 0, 0); From 9641f4aece8ba636527c2cfd136025d3603ca8ee Mon Sep 17 00:00:00 2001 From: cromachina <82557197+cromachina@users.noreply.github.com> Date: Fri, 8 May 2026 19:00:30 -0400 Subject: [PATCH 2/5] Fix Thumbnail-Sai2 to have the same file in/out arguments as Thumbnail-Sai1 (makes it easier to use this program as a Linux thumbnailer). --- samples/Thumbnail-Sai2.cpp | 77 +++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/samples/Thumbnail-Sai2.cpp b/samples/Thumbnail-Sai2.cpp index 2cfba5e..2d36540 100644 --- a/samples/Thumbnail-Sai2.cpp +++ b/samples/Thumbnail-Sai2.cpp @@ -31,7 +31,9 @@ using ThumbnailT = std::tuple, std::uint32_t, std::uint32_t>; bool IterateCanvasItem( - const std::filesystem::path& FilePath, const sai2::CanvasHeader& Header, + const std::filesystem::path& FilePath, + const std::filesystem::path& DestPath, + const sai2::CanvasHeader& Header, const sai2::CanvasEntry& TableEntry, std::span Bytes ) { @@ -47,10 +49,6 @@ bool IterateCanvasItem( if( const auto JpegStream = sai2::ExtractJssfToJpeg(Bytes); !std::get<0>(JpegStream).empty() ) { - std::filesystem::path DestPath(FilePath); - DestPath.replace_filename(FilePath.stem().string() + "-thumbnail"); - DestPath.replace_extension("png"); - // Decode jpeg stream const auto JpegData = std::get<0>(JpegStream); @@ -133,42 +131,45 @@ int main(int argc, char* argv[]) Args.emplace_back(argv[i]); } - for( const std::string_view& Arg : Args ) + if(Args.size() < 2) { - std::puts(Arg.data()); - const std::filesystem::path FilePath(Arg); - if( !(std::filesystem::exists(FilePath) - && std::filesystem::is_regular_file(FilePath)) ) - { - // Not a file - std::printf("Invalid path %s\n", Arg.data()); - continue; - } - const std::uintmax_t FileSize = std::filesystem::file_size(FilePath); + std::printf("Input and output paths required.\n"); + return EXIT_FAILURE; + } - std::ifstream File(FilePath); - std::vector FileData(FileSize); + const std::filesystem::path FilePath(Args[0]); + const std::filesystem::path DestPath(Args[1]); + if( !(std::filesystem::exists(FilePath) + && std::filesystem::is_regular_file(FilePath)) ) + { + // Not a file + std::printf("Invalid path %s\n", FilePath.string()); + return EXIT_FAILURE; + } + const std::uintmax_t FileSize = std::filesystem::file_size(FilePath); - if( File.is_open() ) - { - File.read(reinterpret_cast(FileData.data()), FileSize); - File.close(); - - const auto CanvasDataProc = [&FilePath]( - const sai2::CanvasHeader& Header, - const sai2::CanvasEntry& TableEntry, - std::span Bytes - ) { - return IterateCanvasItem(FilePath, Header, TableEntry, Bytes); - }; - - sai2::IterateCanvasData(FileData, CanvasDataProc); - } - else - { - std::printf("Error reading file contents %s\n", Arg.data()); - continue; - } + std::ifstream File(FilePath); + std::vector FileData(FileSize); + + if( File.is_open() ) + { + File.read(reinterpret_cast(FileData.data()), FileSize); + File.close(); + + const auto CanvasDataProc = [&FilePath, &DestPath]( + const sai2::CanvasHeader& Header, + const sai2::CanvasEntry& TableEntry, + std::span Bytes + ) { + return IterateCanvasItem(FilePath, DestPath, Header, TableEntry, Bytes); + }; + + sai2::IterateCanvasData(FileData, CanvasDataProc); + } + else + { + std::printf("Error reading file contents %s\n", FilePath.string()); + return EXIT_FAILURE; } return EXIT_SUCCESS; From 6d2800937a8d4c2babf06111fe55b338993f303b Mon Sep 17 00:00:00 2001 From: cromachina <82557197+cromachina@users.noreply.github.com> Date: Fri, 8 May 2026 20:51:09 -0400 Subject: [PATCH 3/5] Fix DestPath replacement for lossless thumbnail case --- flake.nix | 33 +++++++++++++++++++++++++++++++++ samples/Thumbnail-Sai2.cpp | 4 ---- 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 flake.nix diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..98a4bae --- /dev/null +++ b/flake.nix @@ -0,0 +1,33 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = { + nixpkgs, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + libsai = pkgs.stdenv.mkDerivation { + name = "libsai"; + src = pkgs.lib.sourceByRegex ./. [ + "^include.*" + "^source.*" + "^samples.*" + "CMakeLists.txt" + ]; + nativeBuildInputs = [ pkgs.cmake ]; + installPhase = '' + mkdir -p $out/bin + cp Thumbnail-Sai1 $out/bin/thumbnail-sai1 + cp Thumbnail-Sai2 $out/bin/thumbnail-sai2 + ''; + }; + in { + packages.default = libsai; + } + ); +} \ No newline at end of file diff --git a/samples/Thumbnail-Sai2.cpp b/samples/Thumbnail-Sai2.cpp index 2d36540..d095ea8 100644 --- a/samples/Thumbnail-Sai2.cpp +++ b/samples/Thumbnail-Sai2.cpp @@ -83,10 +83,6 @@ bool IterateCanvasItem( if( auto Extracted = sai2::ExtractDpcmToBGRA(Header, Bytes); !std::get<0>(Extracted).empty() ) { - std::filesystem::path DestPath(FilePath); - DestPath.replace_filename(FilePath.stem().string() + "-thumbnail"); - DestPath.replace_extension("png"); - std::vector& ThumbnailData = std::get<0>(Extracted); const std::span ThumbnailImage( From 4eeaa8fe642d18f8af305bcba1bbcbe55db7929d Mon Sep 17 00:00:00 2001 From: cromachina <82557197+cromachina@users.noreply.github.com> Date: Fri, 8 May 2026 21:15:30 -0400 Subject: [PATCH 4/5] Minor fixes for PR --- samples/Thumbnail-Sai1.cpp | 8 ++++---- samples/Thumbnail-Sai2.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/samples/Thumbnail-Sai1.cpp b/samples/Thumbnail-Sai1.cpp index 9c37968..32d2699 100644 --- a/samples/Thumbnail-Sai1.cpp +++ b/samples/Thumbnail-Sai1.cpp @@ -20,12 +20,12 @@ const char* const Help "\tThumbnail (filename) (output)\n" "\tWunkolo - Wunkolo@gmail.com"; -void bgraToRgba(std::byte* Pixels, std::size_t PixelCount) +void ConvertBGRA8ToRGBA8(std::span Pixels) { std::size_t i = 0; - std::size_t ByteCount = PixelCount * 4; + std::size_t Count = Pixels.size(); - while(i < ByteCount) + while(i < Count) { std::swap(Pixels[i], Pixels[i + 2]); i += 4; @@ -52,7 +52,7 @@ int main(int argc, char* argv[]) Width = Height = 0; std::unique_ptr Pixels = {}; std::tie(Pixels, Width, Height) = FileIn.GetThumbnail(); - bgraToRgba(Pixels.get(), Width * Height); + ConvertBGRA8ToRGBA8({ Pixels.get(), Width * Height * 4 }); stbi_write_png(argv[2], Width, Height, 4, Pixels.get(), 0); diff --git a/samples/Thumbnail-Sai2.cpp b/samples/Thumbnail-Sai2.cpp index d095ea8..9e29f7e 100644 --- a/samples/Thumbnail-Sai2.cpp +++ b/samples/Thumbnail-Sai2.cpp @@ -129,7 +129,7 @@ int main(int argc, char* argv[]) if(Args.size() < 2) { - std::printf("Input and output paths required.\n"); + std::puts("Input and output paths required."); return EXIT_FAILURE; } @@ -139,7 +139,7 @@ int main(int argc, char* argv[]) && std::filesystem::is_regular_file(FilePath)) ) { // Not a file - std::printf("Invalid path %s\n", FilePath.string()); + std::printf("Invalid path %s\n", FilePath.string().c_str()); return EXIT_FAILURE; } const std::uintmax_t FileSize = std::filesystem::file_size(FilePath); @@ -164,7 +164,7 @@ int main(int argc, char* argv[]) } else { - std::printf("Error reading file contents %s\n", FilePath.string()); + std::printf("Error reading file contents %s\n", FilePath.string().c_str()); return EXIT_FAILURE; } From eb80dc2026795a4ae91360acd79312eca2c0af86 Mon Sep 17 00:00:00 2001 From: cromachina <82557197+cromachina@users.noreply.github.com> Date: Fri, 8 May 2026 21:19:52 -0400 Subject: [PATCH 5/5] Remove local build file accidentally committed --- flake.nix | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 flake.nix diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 98a4bae..0000000 --- a/flake.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - }; - outputs = { - nixpkgs, - flake-utils, - ... - }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacyPackages.${system}; - libsai = pkgs.stdenv.mkDerivation { - name = "libsai"; - src = pkgs.lib.sourceByRegex ./. [ - "^include.*" - "^source.*" - "^samples.*" - "CMakeLists.txt" - ]; - nativeBuildInputs = [ pkgs.cmake ]; - installPhase = '' - mkdir -p $out/bin - cp Thumbnail-Sai1 $out/bin/thumbnail-sai1 - cp Thumbnail-Sai2 $out/bin/thumbnail-sai2 - ''; - }; - in { - packages.default = libsai; - } - ); -} \ No newline at end of file