Compress-Zopfli

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN


[1]: https://github.com/google/zopfli
[2]: https://travis-ci.org/mgreter/perl-zopfli
[3]: http://badge.fury.io/pl/Compress-Zopfli

# SYNOPSIS

```perl
use Compress::Zopfli;
$gz = compress($input, ZOPFLI_FORMAT_GZIP, {
    iterations => 15,
    blocksplitting => 1,
    blocksplittingmax => 15,
});
```

# DESCRIPTION

The `Compress::Zopfli` module provides a Perl interface to the `zopfli`
compression library. The zopfli library is bundled with `Compress::Zopfli`
, so you don't need the `zopfli` library installed on your system.

README.md  view on Meta::CPAN


This is the only function provided by `Compress::Zopfli`. The input must
be a string, as the underlying function does not seem to support any streaming
interface. More convenient APIs may be implemented on top.

# OPTIONS

Options map directly to the `zopfli` low-level function. Must be a hash
reference (i.e. anonymous hash) and supports the following options:

- `iterations`

    Maximum amount of times to rerun forward and backward pass to optimize LZ77
    compression cost. Good values: 10, 15 for small files, 5 for files over
    several MB in size or it will be too slow. Default: 15

- `blocksplitting`

    If true, splits the data in multiple deflate blocks with optimal choice for
    the block boundaries. Block splitting gives better compression. Default: on.

README.md  view on Meta::CPAN

the module aliases to avoid passing the **ZOPFLI_FORMAT**:

- `Compress::Zopfli::GZIP`
- `Compress::Zopfli::ZLIB`
- `Compress::Zopfli::Deflate`

They export one `compress` function without the **ZOPFLI_FORMAT** option.

```perl
use Compress::Zopfli::Deflate;
compress $input, { iterations: 20 };
```

# CONSTANTS

All the `zopfli` constants are automatically imported when you make use
of `Compress::Zopfli`.

- `ZOPFLI_FORMAT_GZIP`: RFC 1952
- `ZOPFLI_FORMAT_ZLIB`: RFC 1950
- `ZOPFLI_FORMAT_DEFLATE`: RFC 1951

Zopfli.xs  view on Meta::CPAN

               HV* options
    CODE:
    {

        struct ZopfliOptions zopfliOptions;

        ZopfliInitOptions(&zopfliOptions);

        if (!SvIOKp(format)) croak("Format not numerical");

        if (hv_exists(options, "iterations", 10)) {
            SV** sv = hv_fetch(options, "iterations", 10, 0);
            if (!sv) croak("Null-ptr on `iterations` option");
            else if (!SvIOKp(*sv)) croak("`iterations` is not a number");
            else zopfliOptions.numiterations = SvIV(*sv);
        }

        if (hv_exists(options, "blocksplitting", 6)) {
            SV** sv = hv_fetch(options, "blocksplitting", 14, 0);
            if (!sv) croak("Null-ptr on `blocksplitting` option");
            else if (!SvIOKp(*sv)) croak("`blocksplitting` is not a number");
            else zopfliOptions.blocksplitting = SvIV(*sv);
        }

        if (hv_exists(options, "blocksplittingmax", 9)) {

lib/Compress/Zopfli.md  view on Meta::CPAN

# NAME

Compress::Zopfli - Interface to Google Zopfli Compression Algorithm

# SYNOPSIS

    use Compress::Zopfli;
    $gz = compress($input, ZOPFLI_FORMAT_GZIP, {
        iterations => 15,
        blocksplitting => 1,
        blocksplittingmax => 15,
    });

# DESCRIPTION

The _Compress::Zopfli_ module provides a Perl interface to the _zopfli_
compression library. The zopfli library is bundled with _Compress::Zopfli_
, so you don't need the _zopfli_ library installed on your system.

lib/Compress/Zopfli.md  view on Meta::CPAN


This is the only function provided by _Compress::Zopfli_. The input must
be a string. The underlying function does not seem to support any streaming
interface.

# OPTIONS

Options map directly to the _zopfli_ low-level function. Must be a hash
reference (i.e. anonymous hash) and supports the following options:

- **iterations**

    Maximum amount of times to rerun forward and backward pass to optimize LZ77
    compression cost. Good values: 10, 15 for small files, 5 for files over
    several MB in size or it will be too slow. Default: 15

- **blocksplitting**

    If true, splits the data in multiple deflate blocks with optimal choice for
    the block boundaries. Block splitting gives better compression. Default: on.

lib/Compress/Zopfli.md  view on Meta::CPAN

You probably only want to use a certain compression type. For that this
module also includes some convenient module aliases:

\- _Compress::Zopfli::GZIP_
\- _Compress::Zopfli::ZLIB_
\- _Compress::Zopfli::Deflate_

They export one **compress** function without the _ZOPFLI\_FORMAT_ option.

    use Compress::Zopfli::Deflate;
    compress $input, { iterations: 20 };

# CONSTANTS

All the _zopfli_ constants are automatically imported when you make use
of _Compress::Zopfli_. See ["DESCRIPTION"](#description) for a complete list.

# AUTHOR

The _Compress::Zopfli_ module was written by Marcel Greter,
`perl-zopfli@ocbnet.ch`. The latest copy of the module can be found on

lib/Compress/Zopfli.pm  view on Meta::CPAN

__END__

=head1 NAME

Compress::Zopfli - Interface to Google Zopfli Compression Algorithm

=head1 SYNOPSIS

    use Compress::Zopfli;
    $gz = compress($input, ZOPFLI_FORMAT_GZIP, {
        iterations => 15,
        blocksplitting => 1,
        blocksplittingmax => 15,
    });

=head1 DESCRIPTION

The I<Compress::Zopfli> module provides a Perl interface to the I<zopfli>
compression library. The zopfli library is bundled with I<Compress::Zopfli>
, so you don't need the I<zopfli> library installed on your system.

lib/Compress/Zopfli.pm  view on Meta::CPAN

be a string. The underlying function does not seem to support any streaming
interface.

=head1 OPTIONS

Options map directly to the I<zopfli> low-level function. Must be a hash
reference (i.e. anonymous hash) and supports the following options:

=over 5

=item B<iterations>

Maximum amount of times to rerun forward and backward pass to optimize LZ77
compression cost. Good values: 10, 15 for small files, 5 for files over
several MB in size or it will be too slow. Default: 15

=item B<blocksplitting>

If true, splits the data in multiple deflate blocks with optimal choice for
the block boundaries. Block splitting gives better compression. Default: on.

lib/Compress/Zopfli.pm  view on Meta::CPAN

You probably only want to use a certain compression type. For that this
module also includes some convenient module aliases:

- I<Compress::Zopfli::GZIP>
- I<Compress::Zopfli::ZLIB>
- I<Compress::Zopfli::Deflate>

They export one B<compress> function without the I<ZOPFLI_FORMAT> option.

    use Compress::Zopfli::Deflate;
    compress $input, { iterations: 20 };

=head1 CONSTANTS

All the I<zopfli> constants are automatically imported when you make use
of I<Compress::Zopfli>. See L</DESCRIPTION> for a complete list.

=head1 AUTHOR

The I<Compress::Zopfli> module was written by Marcel Greter,
F<perl-zopfli@ocbnet.ch>. The latest copy of the module can be found on

zopflib/src/zopfli/deflate.c  view on Meta::CPAN


  ZopfliInitLZ77Store(in, &lz77);

  for (i = 0; i <= npoints; i++) {
    size_t start = i == 0 ? instart : splitpoints_uncompressed[i - 1];
    size_t end = i == npoints ? inend : splitpoints_uncompressed[i];
    ZopfliBlockState s;
    ZopfliLZ77Store store;
    ZopfliInitLZ77Store(in, &store);
    ZopfliInitBlockState(options, start, end, 1, &s);
    ZopfliLZ77Optimal(&s, in, start, end, options->numiterations, &store);
    totalcost += ZopfliCalculateBlockSizeAutoType(&store, 0, store.size);

    ZopfliAppendLZ77Store(&store, &lz77);
    if (i < npoints) splitpoints[i] = lz77.size;

    ZopfliCleanBlockState(&s);
    ZopfliCleanLZ77Store(&store);
  }

  /* Second block splitting attempt */

zopflib/src/zopfli/squeeze.c  view on Meta::CPAN

  *path = 0;
  *pathsize = 0;
  TraceBackwards(inend - instart, length_array, path, pathsize);
  FollowPath(s, in, instart, inend, *path, *pathsize, store, h);
  assert(cost < ZOPFLI_LARGE_FLOAT);
  return cost;
}

void ZopfliLZ77Optimal(ZopfliBlockState *s,
                       const unsigned char* in, size_t instart, size_t inend,
                       int numiterations,
                       ZopfliLZ77Store* store) {
  /* Dist to get to here with smallest cost. */
  size_t blocksize = inend - instart;
  unsigned short* length_array =
      (unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1));
  unsigned short* path = 0;
  size_t pathsize = 0;
  ZopfliLZ77Store currentstore;
  ZopfliHash hash;
  ZopfliHash* h = &hash;

zopflib/src/zopfli/squeeze.c  view on Meta::CPAN


  /* Do regular deflate, then loop multiple shortest path runs, each time using
  the statistics of the previous run. */

  /* Initial run. */
  ZopfliLZ77Greedy(s, in, instart, inend, &currentstore, h);
  GetStatistics(&currentstore, &stats);

  /* Repeat statistics with each time the cost model from the previous stat
  run. */
  for (i = 0; i < numiterations; i++) {
    ZopfliCleanLZ77Store(&currentstore);
    ZopfliInitLZ77Store(in, &currentstore);
    LZ77OptimalRun(s, in, instart, inend, &path, &pathsize,
                   length_array, GetCostStat, (void*)&stats,
                   &currentstore, h, costs);
    cost = ZopfliCalculateBlockSize(&currentstore, 0, currentstore.size, 2);
    if (s->options->verbose_more || (s->options->verbose && cost < bestcost)) {
      fprintf(stderr, "Iteration %d: %d bit\n", i, (int) cost);
    }
    if (cost < bestcost) {
      /* Copy to the output store. */
      ZopfliCopyLZ77Store(&currentstore, store);
      CopyStats(&stats, &beststats);
      bestcost = cost;
    }
    CopyStats(&stats, &laststats);
    ClearStatFreqs(&stats);
    GetStatistics(&currentstore, &stats);
    if (lastrandomstep != -1) {
      /* This makes it converge slower but better. Do it only once the
      randomness kicks in so that if the user does few iterations, it gives a
      better result sooner. */
      AddWeighedStatFreqs(&stats, 1.0, &laststats, 0.5, &stats);
      CalculateStatistics(&stats);
    }
    if (i > 5 && cost == lastcost) {
      CopyStats(&beststats, &stats);
      RandomizeStatFreqs(&ran_state, &stats);
      CalculateStatistics(&stats);
      lastrandomstep = i;
    }

zopflib/src/zopfli/squeeze.h  view on Meta::CPAN


#include "lz77.h"

/*
Calculates lit/len and dist pairs for given data.
If instart is larger than 0, it uses values before instart as starting
dictionary.
*/
void ZopfliLZ77Optimal(ZopfliBlockState *s,
                       const unsigned char* in, size_t instart, size_t inend,
                       int numiterations,
                       ZopfliLZ77Store* store);

/*
Does the same as ZopfliLZ77Optimal, but optimized for the fixed tree of the
deflate standard.
The fixed tree never gives the best compression. But this gives the best
possible LZ77 encoding possible with the fixed tree.
This does not create or output any fixed tree, only LZ77 data optimized for
using with a fixed tree.
If instart is larger than 0, it uses values before instart as starting

zopflib/src/zopfli/util.c  view on Meta::CPAN


#include "zopfli.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

void ZopfliInitOptions(ZopfliOptions* options) {
  options->verbose = 0;
  options->verbose_more = 0;
  options->numiterations = 15;
  options->blocksplitting = 1;
  options->blocksplittinglast = 0;
  options->blocksplittingmax = 15;
}

zopflib/src/zopfli/zopfli.h  view on Meta::CPAN

  int verbose;

  /* Whether to print more detailed output */
  int verbose_more;

  /*
  Maximum amount of times to rerun forward and backward pass to optimize LZ77
  compression cost. Good values: 10, 15 for small files, 5 for files over
  several MB in size or it will be too slow.
  */
  int numiterations;

  /*
  If true, splits the data in multiple deflate blocks with optimal choice
  for the block boundaries. Block splitting gives better compression. Default:
  true (1).
  */
  int blocksplitting;

  /*
  No longer used, left for compatibility.

zopflib/src/zopfli/zopfli_bin.c  view on Meta::CPAN

    if (StringsEqual(arg, "-v")) options.verbose = 1;
    else if (StringsEqual(arg, "-c")) output_to_stdout = 1;
    else if (StringsEqual(arg, "--deflate")) {
      output_type = ZOPFLI_FORMAT_DEFLATE;
    }
    else if (StringsEqual(arg, "--zlib")) output_type = ZOPFLI_FORMAT_ZLIB;
    else if (StringsEqual(arg, "--gzip")) output_type = ZOPFLI_FORMAT_GZIP;
    else if (StringsEqual(arg, "--splitlast"))  /* Ignore */;
    else if (arg[0] == '-' && arg[1] == '-' && arg[2] == 'i'
        && arg[3] >= '0' && arg[3] <= '9') {
      options.numiterations = atoi(arg + 3);
    }
    else if (StringsEqual(arg, "-h")) {
      fprintf(stderr,
          "Usage: zopfli [OPTION]... FILE...\n"
          "  -h    gives this help\n"
          "  -c    write the result on standard output, instead of disk"
          " filename + '.gz'\n"
          "  -v    verbose mode\n"
          "  --i#  perform # iterations (default 15). More gives"
          " more compression but is slower."
          " Examples: --i10, --i50, --i1000\n");
      fprintf(stderr,
          "  --gzip        output to gzip format (default)\n"
          "  --zlib        output to zlib format instead of gzip\n"
          "  --deflate     output to deflate format instead of gzip\n"
          "  --splitlast   ignored, left for backwards compatibility\n");
      return 0;
    }
  }

  if (options.numiterations < 1) {
    fprintf(stderr, "Error: must have 1 or more iterations\n");
    return 0;
  }

  for (i = 1; i < argc; i++) {
    if (argv[i][0] != '-') {
      char* outfilename;
      filename = argv[i];
      if (output_to_stdout) {
        outfilename = 0;
      } else if (output_type == ZOPFLI_FORMAT_GZIP) {

zopflib/src/zopflipng/zopflipng_bin.cc  view on Meta::CPAN

void ShowHelp() {
  printf("ZopfliPNG, a Portable Network Graphics (PNG) image optimizer.\n"
         "\n"
         "Usage: zopflipng [options]... infile.png outfile.png\n"
         "       zopflipng [options]... --prefix=[fileprefix] [files.png]...\n"
         "\n"
         "If the output file exists, it is considered a result from a"
         " previous run and not overwritten if its filesize is smaller.\n"
         "\n"
         "Options:\n"
         "-m: compress more: use more iterations (depending on file size)\n"
         "--prefix=[fileprefix]: Adds a prefix to output filenames. May also"
         " contain a directory path. When using a prefix, multiple input files"
         " can be given and the output filenames are generated with the"
         " prefix\n"
         " If --prefix is specified without value, 'zopfli_' is used.\n"
         " If input file names contain the prefix, they are not processed but"
         " considered as output from previous runs. This is handy when using"
         " *.png wildcard expansion with multiple runs.\n"
         "-y: do not ask about overwriting files.\n"
         "--lossy_transparent: remove colors behind alpha channel 0. No visual"
         " difference, removes hidden information.\n"
         "--lossy_8bit: convert 16-bit per channel image to 8-bit per"
         " channel.\n"
         "-d: dry run: don't save any files, just see the console output"
         " (e.g. for benchmarking)\n"
         "--always_zopflify: always output the image encoded by Zopfli, even if"
         " it's bigger than the original, for benchmarking the algorithm. Not"
         " good for real optimization.\n"
         "-q: use quick, but not very good, compression"
         " (e.g. for only trying the PNG filter and color types)\n"
         "--iterations=[number]: number of iterations, more iterations makes it"
         " slower but provides slightly better compression. Default: 15 for"
         " small files, 5 for large files.\n"
         "--splitting=[0-3]: ignored, left for backwards compatibility\n"
         "--filters=[types]: filter strategies to try:\n"
         " 0-4: give all scanlines PNG filter type 0-4\n"
         " m: minimum sum\n"
         " e: entropy\n"
         " p: predefined (keep from input, this likely overlaps another"
         " strategy)\n"
         " b: brute force (experimental)\n"

zopflib/src/zopflipng/zopflipng_bin.cc  view on Meta::CPAN

         " web images because web browsers do not use these chunks. By default"
         " ZopfliPNG only keeps (and losslessly modifies) the following chunks"
         " because they are essential: IHDR, PLTE, tRNS, IDAT and IEND.\n"
         "\n"
         "Usage examples:\n"
         "Optimize a file and overwrite if smaller: zopflipng infile.png"
         " outfile.png\n"
         "Compress more: zopflipng -m infile.png outfile.png\n"
         "Optimize multiple files: zopflipng --prefix a.png b.png c.png\n"
         "Compress really good and trying all filter strategies: zopflipng"
         " --iterations=500 --filters=01234mepb --lossy_8bit"
         " --lossy_transparent infile.png outfile.png\n");
}

void PrintSize(const char* label, size_t size) {
  printf("%s: %d (%dK)\n", label, (int) size, (int) size / 1024);
}

void PrintResultSize(const char* label, size_t oldsize, size_t newsize) {
  printf("%s: %d (%dK). Percentage of original: %.3f%%\n",
         label, (int) newsize, (int) newsize / 1024, newsize * 100.0 / oldsize);

zopflib/src/zopflipng/zopflipng_bin.cc  view on Meta::CPAN

  for (int i = 1; i < argc; i++) {
    std::string arg = argv[i];
    if (arg[0] == '-' && arg.size() > 1 && arg[1] != '-') {
      for (size_t pos = 1; pos < arg.size(); pos++) {
        char c = arg[pos];
        if (c == 'y') {
          yes = true;
        } else if (c == 'd') {
          dryrun = true;
        } else if (c == 'm') {
          png_options.num_iterations *= 4;
          png_options.num_iterations_large *= 4;
        } else if (c == 'q') {
          png_options.use_zopfli = false;
        } else if (c == 'h') {
          ShowHelp();
          return 0;
        } else {
          printf("Unknown flag: %c\n", c);
          return 0;
        }
      }

zopflib/src/zopflipng/zopflipng_bin.cc  view on Meta::CPAN

      std::string value = eq >= arg.size() - 1 ? "" : arg.substr(eq + 1);
      int num = atoi(value.c_str());
      if (name == "--always_zopflify") {
        always_zopflify = true;
      } else if (name == "--verbose") {
        png_options.verbose = true;
      } else if (name == "--lossy_transparent") {
        png_options.lossy_transparent = true;
      } else if (name == "--lossy_8bit") {
        png_options.lossy_8bit = true;
      } else if (name == "--iterations") {
        if (num < 1) num = 1;
        png_options.num_iterations = num;
        png_options.num_iterations_large = num;
      } else if (name == "--splitting") {
        // ignored
      } else if (name == "--filters") {
        for (size_t j = 0; j < value.size(); j++) {
          ZopfliPNGFilterStrategy strategy = kStrategyZero;
          char f = value[j];
          switch (f) {
            case '0': strategy = kStrategyZero; break;
            case '1': strategy = kStrategyOne; break;
            case '2': strategy = kStrategyTwo; break;

zopflib/src/zopflipng/zopflipng_lib.cc  view on Meta::CPAN

#include "lodepng/lodepng.h"
#include "lodepng/lodepng_util.h"
#include "../zopfli/deflate.h"

ZopfliPNGOptions::ZopfliPNGOptions()
  : verbose(false)
  , lossy_transparent(false)
  , lossy_8bit(false)
  , auto_filter_strategy(true)
  , use_zopfli(true)
  , num_iterations(15)
  , num_iterations_large(5)
  , block_split_strategy(1) {
}

// Deflate compressor passed as fuction pointer to LodePNG to have it use Zopfli
// as its compression backend.
unsigned CustomPNGDeflate(unsigned char** out, size_t* outsize,
                          const unsigned char* in, size_t insize,
                          const LodePNGCompressSettings* settings) {
  const ZopfliPNGOptions* png_options =
      static_cast<const ZopfliPNGOptions*>(settings->custom_context);
  unsigned char bp = 0;
  ZopfliOptions options;
  ZopfliInitOptions(&options);

  options.verbose = png_options->verbose;
  options.numiterations = insize < 200000
      ? png_options->num_iterations : png_options->num_iterations_large;

  ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize);

  return 0;  // OK
}

// Returns 32-bit integer value for RGBA color.
static unsigned ColorIndex(const unsigned char* color) {
  return color[0] + 256u * color[1] + 65536u * color[2] + 16777216u * color[3];
}

zopflib/src/zopflipng/zopflipng_lib.cc  view on Meta::CPAN

extern "C" void CZopfliPNGSetDefaults(CZopfliPNGOptions* png_options) {

  memset(png_options, 0, sizeof(*png_options));
  // Constructor sets the defaults
  ZopfliPNGOptions opts;

  png_options->lossy_transparent    = opts.lossy_transparent;
  png_options->lossy_8bit           = opts.lossy_8bit;
  png_options->auto_filter_strategy = opts.auto_filter_strategy;
  png_options->use_zopfli           = opts.use_zopfli;
  png_options->num_iterations       = opts.num_iterations;
  png_options->num_iterations_large = opts.num_iterations_large;
  png_options->block_split_strategy = opts.block_split_strategy;
}

extern "C" int CZopfliPNGOptimize(const unsigned char* origpng,
                                  const size_t origpng_size,
                                  const CZopfliPNGOptions* png_options,
                                  int verbose,
                                  unsigned char** resultpng,
                                  size_t* resultpng_size) {
  ZopfliPNGOptions opts;

  // Copy over to the C++-style struct
  opts.lossy_transparent    = !!png_options->lossy_transparent;
  opts.lossy_8bit           = !!png_options->lossy_8bit;
  opts.auto_filter_strategy = !!png_options->auto_filter_strategy;
  opts.use_zopfli           = !!png_options->use_zopfli;
  opts.num_iterations       = png_options->num_iterations;
  opts.num_iterations_large = png_options->num_iterations_large;
  opts.block_split_strategy = png_options->block_split_strategy;

  for (int i = 0; i < png_options->num_filter_strategies; i++) {
    opts.filter_strategies.push_back(png_options->filter_strategies[i]);
  }

  for (int i = 0; i < png_options->num_keepchunks; i++) {
    opts.keepchunks.push_back(png_options->keepchunks[i]);
  }

zopflib/src/zopflipng/zopflipng_lib.h  view on Meta::CPAN

  int num_filter_strategies;

  int auto_filter_strategy;

  char** keepchunks;
  // How many entries in keepchunks.
  int num_keepchunks;

  int use_zopfli;

  int num_iterations;

  int num_iterations_large;

  int block_split_strategy;
} CZopfliPNGOptions;

// Sets the default options
// Does not allocate or set keepchunks or filter_strategies
void CZopfliPNGSetDefaults(CZopfliPNGOptions *png_options);

// Returns 0 on success, error code otherwise
// The caller must free resultpng after use

zopflib/src/zopflipng/zopflipng_lib.h  view on Meta::CPAN

  // Automatically choose filter strategy using less good compression
  bool auto_filter_strategy;

  // PNG chunks to keep
  // chunks to literally copy over from the original PNG to the resulting one
  std::vector<std::string> keepchunks;

  // Use Zopfli deflate compression
  bool use_zopfli;

  // Zopfli number of iterations
  int num_iterations;

  // Zopfli number of iterations on large images
  int num_iterations_large;

  // Unused, left for backwards compatiblity.
  int block_split_strategy;
};

// Returns 0 on success, error code otherwise.
// If verbose is true, it will print some info while working.
int ZopfliPNGOptimize(const std::vector<unsigned char>& origpng,
    const ZopfliPNGOptions& png_options,
    bool verbose,



( run in 1.013 second using v1.01-cache-2.11-cpan-96521ef73a4 )