New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stb_image and stb_image_write: allow to use zlib for png compression #113

Closed
guillaumechereau opened this Issue Apr 21, 2015 · 17 comments

Comments

Projects
None yet
6 participants
@guillaumechereau
Copy link

guillaumechereau commented Apr 21, 2015

I noticed that the zlib compression in stb_image_write was rather poor compared to what the actual zlib library would do. For example saving an rgba image filled with the gray color (128,128,128,255) prodices a png of size 42KB. When I use zlib to compress the data instead of stb algorithm, I get 6.4KB.

Many projects already use zlib, so I think having the option to use it would be nice, if that is not too much work.

@nothings

This comment has been minimized.

Copy link
Owner

nothings commented Apr 22, 2015

This is documented. Use libpng?

@nothings

This comment has been minimized.

Copy link
Owner

nothings commented Apr 22, 2015

Although, note to self: worth investigating why that doesn't compress smaller.

@guillaumechereau

This comment has been minimized.

Copy link

guillaumechereau commented Apr 22, 2015

libpng works, but I still prefer stb_image_write interface, plus the fact that it is a single file.
Isn't the compression problem due to the fact that you use a fixed huffman tree?

@nothings

This comment has been minimized.

Copy link
Owner

nothings commented Apr 22, 2015

If you hook it up to zlib it's no longer a single file.

I guess my stance is that if you want something to ship to customers, you might as well use libpng; stb_image_write is intended for quick-and-dirty stuff where you can always run pngcrush etc if you really care.

The fixed tree definitely limits the effective compression, but a basically-constant file should probably compress better anyway, so I wonder if the match processing isn't right.

@r-lyeh-archived

This comment has been minimized.

Copy link

r-lyeh-archived commented Apr 22, 2015

@guillaumechereau, lodepng is somewhere between stb_image_write and libpng. you might give it a try as well.

@jarikomppa

This comment has been minimized.

Copy link
Contributor

jarikomppa commented Apr 22, 2015

miniz appears to be a small, unlicensed zlib implementation - https://code.google.com/p/miniz/ - could possibly be used as an alternative?

@guillaumechereau

This comment has been minimized.

Copy link

guillaumechereau commented Apr 22, 2015

On the platforms I am mostly interested in (android), zlib is always present, so it is basically free to use it. I thought adding a test macro like STB_HAS_ZLIB to the code could have been a solution. But I agree with @nothings that if the stb implementation has a problem, it would be better to fix it first.

@jarikomppa

This comment has been minimized.

Copy link
Contributor

jarikomppa commented Apr 22, 2015

I meant, if a better zlib implementation is wanted in stb_image_write and the desire is to keep it in one file, that might be one shortcut..

@DanielGibson

This comment has been minimized.

Copy link
Contributor

DanielGibson commented Jul 16, 2015

miniz actually contains a png compression function that is easy to use, see https://code.google.com/p/miniz/source/browse/trunk/miniz.c#822
Maybe just use that instead of stb_image_write?

(Of course it would be great if stb_image_write's png compression was closer to libpng, but then the optimization should be done in the lib itself and not by using other dependencies - especially if the dependency can compress png images itself anyway)

@nothings

This comment has been minimized.

Copy link
Owner

nothings commented Jul 17, 2015

It looks to me like the miniz doesn't use PNG filters, so it's just compressing the raw image data as is, which isn't going to be very good compression either, but along a different axis. But maybe I'm misreading the code.

@DanielGibson

This comment has been minimized.

Copy link
Contributor

DanielGibson commented Jul 17, 2015

I thought I read claims that miniz's png compression is at least as good as libpng, but I can't find them anymore so I may misremember.

Maybe someone could do a quick comparison between miniz, stb_image_write and maybe libpng, but I'm feeling lazy :-/

@DanielGibson

This comment has been minimized.

Copy link
Contributor

DanielGibson commented Jul 18, 2015

Ok, felt a bit less lazy today: http://wp.me/pEPJ4-5U compares stbi_write_png() with miniz's png compressor, LodePNG's lodepng_encode_file() and those 100 functions libpng needs to get the job done.

TL;DR: You were right, miniz's PNG compressor is not really better than yours: Sometimes it's a bit better, sometimes worse, and both compress a lot less than libpng and LodePNG.

So if anyone wants to create PNG images with better compression I'd suggest either improving stbi_write_png() or using LodePNG (which is almost as easy to integrate and use as stb_image_write).
(For the sake of completeness: LodePNG can also load PNGs, but is slower at it than stb_image)

@DanielGibson

This comment has been minimized.

Copy link
Contributor

DanielGibson commented Jul 18, 2015

I hacked stbi_write_png_to_mem() to use miniz's mz_compress2() instead of stbi_zlib_compress() - the compression ratio gets very close to libpng then.

The corresponding lines with a bit of context, if anyone wants to try it:

   STBIW_FREE(line_buffer);

   // zlib = stbi_zlib_compress(filt, y*(x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
   mz_ulong buflen = mz_compressBound(y*(x*n+1));
   zlib = STBIW_MALLOC(buflen);
   if(!zlib || mz_compress2(zlib, &buflen, filt, y*(x*n+1), 9))
   {
      STBIW_FREE(filt);
      STBIW_FREE(zlib);
      return 0;
   }

   zlen = buflen;

   STBIW_FREE(filt);
   if (!zlib) return 0;

(+ you need to #include "miniz.c" somewhere in the implementation part)

So I guess the png compression could indeed be improved a lot by borrowing miniz code (which is also public domain).

(From the miniz side it would be easier actually, they'd just have to borrow the stbi_write_png_to_mem() code + 15lines of helpers, which is much shorter than the miniz compression code)

@nothings

This comment has been minimized.

Copy link
Owner

nothings commented Sep 13, 2015

@guillaumechereau do you remember what dimensions your gray test image was?

@godratio

This comment has been minimized.

Copy link

godratio commented Jun 27, 2017

This is would be absolutely valuable for stb_image to upgrade here. Its just this one thing away from being useable production side.

DanielGibson added a commit to DanielGibson/stb that referenced this issue Jul 4, 2017

stb_image_write.h: Allow setting custom zlib compress function for PNG
The builtin stbi_zlib_compress does not compress as well as zlib or
miniz (which is not too surprising as it's <200 LOC), thus PNGs created
by stb_image_write are about 20-50% bigger than PNGs compressed with
libpng.
This change lets the user supply a custom deflate/zlib-style compress
function, which improves compression a lot. This was requested in nothings#113.

Example for zlib:

#include <zlib.h>
unsigned char* compress_for_stbiw(unsigned char *data, int data_len,
                                  int *out_len, int quality)
{
  uLongf bufSize = compressBound(data_len);
  unsigned char* buf = malloc(bufSize);
  if(buf == NULL)  return NULL;
  if(compress2(buf, &bufSize, data, data_len, quality) != Z_OK)
  {
    free(buf);
    return NULL;
  }
  *out_len = bufSize;

  return buf;
}
#define STBIW_ZLIB_COMPRESS compress_for_stbiw
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
// ...
@DanielGibson

This comment has been minimized.

Copy link
Contributor

DanielGibson commented Jul 4, 2017

I've implemented this, see #470

DanielGibson added a commit to DanielGibson/stb that referenced this issue Jul 4, 2017

stb_image_write.h: Allow setting custom zlib compress function for PNG
The builtin stbi_zlib_compress does not compress as well as zlib or
miniz (which is not too surprising as it's <200 LOC), thus PNGs created
by stb_image_write are about 20-50% bigger than PNGs compressed with
libpng.
This change lets the user supply a custom deflate/zlib-style compress
function, which improves compression a lot. This was requested in nothings#113.

Example for zlib:

#include <zlib.h>
unsigned char* compress_for_stbiw(unsigned char *data, int data_len,
                                  int *out_len, int quality)
{
  uLongf bufSize = compressBound(data_len);
  // note that buf will be free'd by stb_image_write.h
  // with STBIW_FREE() (plain free() by default)
  unsigned char* buf = malloc(bufSize);
  if(buf == NULL)  return NULL;
  if(compress2(buf, &bufSize, data, data_len, quality) != Z_OK)
  {
    free(buf);
    return NULL;
  }
  *out_len = bufSize;

  return buf;
}
#define STBIW_ZLIB_COMPRESS compress_for_stbiw
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
// ...
@nothings

This comment has been minimized.

Copy link
Owner

nothings commented Jan 29, 2018

fixed by #470

@nothings nothings closed this Jan 29, 2018

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