Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

Nor-s
Copy link
Collaborator

@Nor-s Nor-s commented Oct 5, 2025

Changes

  1. Fixed a saver background reference leak 102efc5

  2. Added Static PNG Saver in savers/png

    • Copied the necessary PNG encoding functions from lodepng in loaders/png/tvgLodePng.cpp and loaders/png/tvgLodePng.h
      • Enabled CRC in tvgLodePng.cpp when THORVG_PNG_SAVER_SUPPORT is defined. (used only by the saver)
    • Added meson option for savers/png
      • Note: png_loader = png_loader or png_saver currently, the lodepng-based implementation resides under the loader module, which introduces an unintended dependency between the loader and saver.
  3. Added External(libpng-based) PNG Saver in savers/external_png

    • Since the static PNG version caused a dependency between the loader and saver(tvgLodePng), a new libpng-based PNG saver (external_png) was added to keep them separate.

issue: #3737

Output


image

@github-actions github-actions bot added infrastructure Dev infrastructure tools Tvg Viewer / Tools test Unit tests renderer Core rendering labels Oct 5, 2025
@Nor-s Nor-s self-assigned this Oct 5, 2025
@Nor-s Nor-s force-pushed the issue-3737-support-png-saver branch from 85cce51 to 6917a3d Compare October 7, 2025 11:47
@github-actions github-actions bot added image Bitmap image features (jpg/png/webp/gif) and removed tools Tvg Viewer / Tools labels Oct 7, 2025
@Nor-s Nor-s force-pushed the issue-3737-support-png-saver branch 3 times, most recently from 3ba970d to 174f70f Compare October 7, 2025 14:16
@Nor-s Nor-s marked this pull request as ready for review October 7, 2025 14:22
@Nor-s Nor-s requested a review from hermet as a code owner October 7, 2025 14:22
@hermet hermet requested a review from Copilot October 10, 2025 06:12
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds PNG saver functionality to ThorVG with two implementation options: a static PNG saver using lodepng code and an external PNG saver using libpng. The changes include necessary PNG encoding functions copied from the loader module, meson build configuration updates, and test coverage for the new PNG saving capabilities.

Key changes:

  • Added static PNG saver implementation in savers/png using lodepng functions
  • Added external PNG saver implementation in savers/external_png using libpng
  • Updated build system configuration to support PNG saver options
  • Fixed a background reference leak in the saver module

Reviewed Changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/testSavers.cpp Added PNG saver test cases and updated GIF test file names
src/savers/png/tvgPngSaver.h Header file for static PNG saver implementation
src/savers/png/tvgPngSaver.cpp Implementation of static PNG saver using lodepng
src/savers/external_png/tvgPngSaver.h Header file for external PNG saver implementation
src/savers/external_png/tvgPngSaver.cpp Implementation of external PNG saver using libpng
src/renderer/tvgSaver.cpp Updated saver registry to support PNG format and fixed background reference leak
src/loaders/png/tvgLodePng.h Added PNG encoder structures and function declarations
src/loaders/png/tvgLodePng.cpp Added PNG encoding functionality when saver support is enabled
meson_options.txt Added PNG to saver options
meson.build Added PNG saver configuration and dependencies

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@Nor-s Nor-s force-pushed the issue-3737-support-png-saver branch from 3848504 to e863091 Compare October 12, 2025 05:49
@hermet hermet requested a review from tinyjin October 13, 2025 03:35
@hermet hermet force-pushed the main branch 5 times, most recently from d471af2 to 0aaa738 Compare October 13, 2025 13:18
@Nor-s Nor-s force-pushed the issue-3737-support-png-saver branch 2 times, most recently from f8b6dff to 7fa65c4 Compare October 13, 2025 15:27
Nor-s added 4 commits October 14, 2025 16:31
- support: static png saver (Copied only necessary encoding functions from the lodepng opensource. See: https://lodev.org/lodepng/)
- support: libpng based png saver
- additional changes: tvgLodePng.cpp: enable crc when defined THORVG_PNG_SAVER_SUPPORT

- issue: thorvg#3737
- lodepng_malloc -> tvg::malloc
- lodepng_realloc -> tvg::realloc
- lodepng_free -> tvg::free
- The PNG saver used a color space incompatible with PNG encoding.
- Adjusted to use the correct ColorSpace::ABGR8888 without pixel conversion.
@hermet hermet force-pushed the issue-3737-support-png-saver branch from 7fa65c4 to ceffcf5 Compare October 14, 2025 07:31
@hermet
Copy link
Member

hermet commented Oct 14, 2025

submitted separately -> aca4a24

@hermet hermet added the feature New feature additions label Oct 14, 2025
@hermet
Copy link
Member

hermet commented Oct 14, 2025

@Nor-s Hello, I just skimmed through your main commit to decide the approach first.

Basic approach would be like this:

savers/png/tvgPngEncoder //only encoding available
loaders/png/tvgPngDecoder //only decoding available

So that two modules have no dependency and for clean maintenance.

The key decision point for the design here would be how much this increases the binary size. After making visible data, we can decide the approach with:

common/tvgPngCodec // shared by both saver and loader

No remarkable binary size diff, I might prefer to separate implementation.

Thanks for your contribution.

@hermet hermet removed the request for review from tinyjin October 14, 2025 08:22
@Nor-s
Copy link
Collaborator Author

Nor-s commented Oct 16, 2025

@hermet

Hello, thank you for the review.

For now, I’ve made a temporary commit to separate the implementation into tvgPngEncoder and tvgPngDecoder.

To estimate the size of the common code, I’ve temporarily added the encoder-related logic to tvgPngEncoder2.

I haven’t compared the binary size yet, but I’ll leave a comment later after checking it.

Thank you.


Common Functions

The following table was generated by AI.

Function Description Parameters Return Type
HuffmanTree_init Initializes a HuffmanTree structure. HuffmanTree* tree void
HuffmanTree_cleanup Frees memory used by a HuffmanTree. HuffmanTree* tree void
lodepng_memcpy Custom implementation of memcpy. void* dst, const void* src, size_t size void
lodepng_memset Custom implementation of memset. void* dst, int value, size_t num void
ucvector_resize Resizes a dynamic unsigned char vector. ucvector* p, size_t size unsigned (1 = success, 0 = failure)
ucvector_init Initializes a dynamic unsigned char vector. unsigned char* buffer, size_t size ucvector
reverseBits Reverses the bit order of the lower num bits. unsigned bits, unsigned num unsigned
HuffmanTree_makeTable Builds lookup tables for Huffman decoding. HuffmanTree* tree unsigned (error code)
HuffmanTree_makeFromLengths2 Builds Huffman codes from code lengths (step 2). HuffmanTree* tree unsigned
HuffmanTree_makeFromLengths Builds a Huffman tree from bit length data. HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen unsigned
generateFixedLitLenTree Creates a fixed literal/length Huffman tree (per Deflate spec). HuffmanTree* tree unsigned
generateFixedDistanceTree Creates a fixed distance Huffman tree (per Deflate spec). HuffmanTree* tree unsigned
getNumColorChannels Returns the number of color channels for a color type. LodePNGColorType colortype unsigned
lodepng_get_bpp_lct Gets bits per pixel for a given color type and bit depth. LodePNGColorType colortype, unsigned bitdepth unsigned
lodepng_get_raw_size_lct Computes raw image size in bytes (color type + bit depth). unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth size_t
lodepng_get_raw_size Returns raw buffer size for an image. unsigned w, unsigned h, const LodePNGColorMode* color size_t
lodepng_get_raw_size_idat Returns raw IDAT chunk buffer size (with filter bytes). unsigned w, unsigned h, unsigned bpp size_t
color_tree_get Finds a color index in a ColorTree. ColorTree* tree, unsigned char r, g, b, a int
lodepng_color_mode_alloc_palette Allocates and initializes a color palette. LodePNGColorMode* info void
lodepng_palette_clear Frees a color palette. LodePNGColorMode* info void
lodepng_color_mode_cleanup Cleans up a color mode (palette etc.). LodePNGColorMode* info void
lodepng_color_mode_copy Copies one color mode to another. LodePNGColorMode* dest, const LodePNGColorMode* source unsigned
lodepng_info_cleanup Cleans up a LodePNGInfo structure. LodePNGInfo* info void
lodepng_color_mode_init Initializes a LodePNGColorMode. LodePNGColorMode* info void
lodepng_info_init Initializes a LodePNGInfo structure. LodePNGInfo* info void
lodepng_color_mode_equal Checks if two color modes are equal. const LodePNGColorMode* a, const LodePNGColorMode* b int
color_tree_init Initializes a ColorTree. ColorTree* tree void
color_tree_add Adds a color to a ColorTree. ColorTree* tree, unsigned char r, g, b, a, unsigned index unsigned
getPixelColorRGBA16 Extracts RGBA16 pixel values. unsigned short* r,g,b,a, const unsigned char* in, size_t i, const LodePNGColorMode* mode void
rgba16ToPixel Converts RGBA16 to target color mode pixel. unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r,g,b,a void
addColorBits Adds color bits to output stream. unsigned char* out, size_t index, unsigned bits, unsigned in void
rgba8ToPixel Converts RGBA8 color to specified mode pixel. unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree, unsigned char r,g,b,a unsigned
readBitFromReversedStream Reads a single bit (MSB first). size_t* bitpointer, const unsigned char* bitstream unsigned char
readBitsFromReversedStream Reads multiple bits from a bitstream. size_t* bitpointer, const unsigned char* bitstream, size_t nbits unsigned
getPixelColorsRGBA8 Converts raw input to RGBA8 buffer. unsigned char* buffer, size_t numpixels, const unsigned char* in, const LodePNGColorMode* mode void
color_tree_cleanup Recursively frees a ColorTree. ColorTree* tree void
getPixelColorsRGB8 Converts raw input to RGB8 buffer. unsigned char* buffer, size_t numpixels, const unsigned char* in, const LodePNGColorMode* mode void
getPixelColorRGBA8 Extracts RGBA8 values from input. unsigned char* r,g,b,a, const unsigned char* in, size_t i, const LodePNGColorMode* mode void
lodepng_convert Converts between different color formats. unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h unsigned
lodepng_compress_settings_init Initializes compression settings. LodePNGCompressSettings* settings void
lodepng_encoder_settings_init Initializes encoder settings. LodePNGEncoderSettings* settings void
lodepng_state_init Initializes a LodePNG state. LodePNGState* state void
lodepng_state_cleanup Frees all memory in a LodePNG state. LodePNGState* state void
lodepng_addofl Checks integer addition overflow. size_t a, size_t b, size_t* result int
lodepng_read32bitInt Reads 32-bit integer from bytes (big-endian). const unsigned char* buffer unsigned
lodepng_chunk_length Reads PNG chunk length. const unsigned char* chunk unsigned
setBitOfReversedStream Sets a single bit in a reversed bitstream. size_t* bitpointer, unsigned char* bitstream, unsigned char bit void
update_adler32 Updates Adler-32 checksum. unsigned adler, const unsigned char* data, unsigned len unsigned
adler32 Computes Adler-32 checksum. const unsigned char* data, unsigned len unsigned
paethPredictor Implements PNG Paeth predictor filter. short a, short b, short c unsigned char
Adam7_getpassvalues Calculates dimensions for each Adam7 interlace pass. unsigned passw[7], passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp void
checkColorValidity Validates color type and bit depth combination. LodePNGColorType colortype, unsigned bd unsigned

@Nor-s Nor-s force-pushed the issue-3737-support-png-saver branch from 163382f to d810dca Compare October 19, 2025 14:44
@Nor-s
Copy link
Collaborator Author

Nor-s commented Oct 19, 2025

@hermet

Hello, I’ve checked the binary size differences.
The unified version (before d810dca) is about 16.928KB smaller than the split version (encoder/decoder separated).

Would you consider this size difference negligible?
If yes, I agree that keeping the separated implementation for cleaner maintenance sounds reasonable.

Thanks for your feedback!

Case File Size (Bytes) text data text + data
No Png 1109984 1082117 17880 1099997 (-)
Png Loader 1159136 1132961 18040 1151001 (+ 51004)
Png Saver 1171424 1142597 17976 1160573 (+ 60576)
Png Loader + Png Saver 1220576 1193333 18136 1211469 (+111472)
Unified PNG Module (no split) 1204192 1176405 18136 1194541 (+ 94544)
details...

1. Loader + Saver

Image
meson builddir -Dloaders='svg,lottie,ttf,png'  -Dsavers='gif,png' -Dengines=sw -Dstatic=true -Dbuildtype=release --wipe
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1443480 Oct 19 23:13 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ strip ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1220576 Oct 19 23:13 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ size ./builddir/src/libthorvg.so.1.0.0
   text    data     bss     dec     hex filename
1193333   18136  528832 1740301  1a8e0d ./builddir/src/libthorvg.so.1.0.0

2. Only Saver

Image
meson builddir -Dloaders='svg,lottie,ttf'  -Dsavers='gif,png' -Dengines=sw -Dstatic=true -Dbuildtype=release --wipe
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ meson compile -C builddir
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /usr/bin/ninja -C /home/nsg/gitrepo/OpenSource/gifinspector/subprojects/thorvg/builddir
ninja: Entering directory `/home/nsg/gitrepo/OpenSource/gifinspector/subprojects/thorvg/builddir'
[242/242] Linking target src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1392432 Oct 19 23:10 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ strip ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1171424 Oct 19 23:11 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ size ./builddir/src/libthorvg.so.1.0.0
   text    data     bss     dec     hex filename
1142597   17976  528832 1689405  19c73d ./builddir/src/libthorvg.so.1.0.0

3. Only Loader

Image
meson builddir -Dloaders='svg,lottie,ttf,png'  -Dsavers='gif' -Dengines=sw -Dstatic=true -Dbuildtype=release --wipe
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ meson compile -C builddir
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /usr/bin/ninja -C /home/nsg/gitrepo/OpenSource/gifinspector/subprojects/thorvg/builddir
ninja: Entering directory `/home/nsg/gitrepo/OpenSource/gifinspector/subprojects/thorvg/builddir'
[242/242] Linking target src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1378176 Oct 19 23:16 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ strip ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1159136 Oct 19 23:17 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ size ./builddir/src/libthorvg.so.1.0.0
   text    data     bss     dec     hex filename
1132961   18040  528832 1679833  19a1d9 ./builddir/src/libthorvg.so.1.0.0

4. No Png

Image
meson builddir -Dloaders='svg,lottie,ttf'  -Dsavers='gif' -Dengines=sw -Dstatic=true -Dbuildtype=release --wipe
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1326816 Oct 19 23:24 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ strip ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1109984 Oct 19 23:25 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ size ./builddir/src/libthorvg.so.1.0.0
   text    data     bss     dec     hex filename
1082117   17880  528832 1628829  18da9d ./builddir/src/libthorvg.so.1.0.0

5. before d810dca

Image
meson builddir -Dloaders='svg,lottie,ttf,png'  -Dsavers='gif,png' -Dengines=sw -Dstatic=true -Dbuildtype=release --wipe
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ meson compile -C builddir
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /usr/bin/ninja -C /home/nsg/gitrepo/OpenSource/gifinspector/subprojects/thorvg/builddir
ninja: Entering directory `/home/nsg/gitrepo/OpenSource/gifinspector/subprojects/thorvg/builddir'
[243/243] Linking target src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1427040 Oct 19 23:46 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ strip ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ ls -l ./builddir/src/libthorvg.so.1.0.0
-rwxr-xr-x 1 nsg nsg 1204192 Oct 19 23:46 ./builddir/src/libthorvg.so.1.0.0
nsg@DESKTOP-P8H04FD:~/gitrepo/OpenSource/gifinspector/subprojects/thorvg$ size ./builddir/src/libthorvg.so.1.0.0
   text    data     bss     dec     hex filename
1176405   18136  528832 1723373  1a4bed ./builddir/src/libthorvg.so.1.0.0

@hermet
Copy link
Member

hermet commented Oct 20, 2025

1204192

@Nor-s Thanks for the detailed information. I think we need to proceed with a unified version -> (common/tvgPngCodec.h and .cpp) I think you can first add a commit to refactor the PNG loader (png/tvgLodePng → common/tvgPngCodec). We can then build the saver feature on top of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature additions image Bitmap image features (jpg/png/webp/gif) infrastructure Dev infrastructure renderer Core rendering test Unit tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants