Compress-Zopfli
view release on metacpan or search on metacpan
zopflib/src/zopflipng/zopflipng_lib.cc view on Meta::CPAN
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: lode.vandevenne@gmail.com (Lode Vandevenne)
// Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
// See zopflipng_lib.h
#include "zopflipng_lib.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <set>
#include <vector>
#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];
}
// Counts amount of colors in the image, up to 257. If transparent_counts_as_one
// is enabled, any color with alpha channel 0 is treated as a single color with
// index 0.
void CountColors(std::set<unsigned>* unique,
const unsigned char* image, unsigned w, unsigned h,
bool transparent_counts_as_one) {
unique->clear();
for (size_t i = 0; i < w * h; i++) {
unsigned index = ColorIndex(&image[i * 4]);
if (transparent_counts_as_one && image[i * 4 + 3] == 0) index = 0;
unique->insert(index);
if (unique->size() > 256) break;
}
}
// Remove RGB information from pixels with alpha=0
void LossyOptimizeTransparent(lodepng::State* inputstate, unsigned char* image,
unsigned w, unsigned h) {
// First check if we want to preserve potential color-key background color,
// or instead use the last encountered RGB value all the time to save bytes.
bool key = true;
for (size_t i = 0; i < w * h; i++) {
if (image[i * 4 + 3] > 0 && image[i * 4 + 3] < 255) {
key = false;
break;
}
}
std::set<unsigned> count; // Color count, up to 257.
CountColors(&count, image, w, h, true);
// If true, means palette is possible so avoid using different RGB values for
// the transparent color.
bool palette = count.size() <= 256;
// Choose the color key or first initial background color.
int r = 0, g = 0, b = 0;
if (key || palette) {
for (size_t i = 0; i < w * h; i++) {
if (image[i * 4 + 3] == 0) {
// Use RGB value of first encountered transparent pixel. This can be
// used as a valid color key, or in case of palette ensures a color
// existing in the input image palette is used.
r = image[i * 4 + 0];
g = image[i * 4 + 1];
b = image[i * 4 + 2];
break;
}
}
}
zopflib/src/zopflipng/zopflipng_lib.cc view on Meta::CPAN
image.clear();
error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16);
bit16 = true;
}
if (!error) {
// If lossy_transparent, remove RGB information from pixels with alpha=0
if (png_options.lossy_transparent && !bit16) {
LossyOptimizeTransparent(&inputstate, &image[0], w, h);
}
if (png_options.auto_filter_strategy) {
error = AutoChooseFilterStrategy(image, w, h, inputstate, bit16,
keep_colortype, origpng,
/* Don't try brute force */
kNumFilterStrategies - 1,
filterstrategies, strategy_enable);
}
}
if (!error) {
size_t bestsize = 0;
for (int i = 0; i < kNumFilterStrategies; i++) {
if (!strategy_enable[i]) continue;
std::vector<unsigned char> temp;
error = TryOptimize(image, w, h, inputstate, bit16, keep_colortype,
origpng, filterstrategies[i], true /* use_zopfli */,
windowsize, &png_options, &temp);
if (!error) {
if (verbose) {
printf("Filter strategy %s: %d bytes\n",
strategy_name[i].c_str(), (int) temp.size());
}
if (bestsize == 0 || temp.size() < bestsize) {
bestsize = temp.size();
(*resultpng).swap(temp); // Store best result so far in the output.
}
}
}
if (!png_options.keepchunks.empty()) {
KeepChunks(origpng, png_options.keepchunks, resultpng);
}
}
return error;
}
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]);
}
const std::vector<unsigned char> origpng_cc(origpng, origpng + origpng_size);
std::vector<unsigned char> resultpng_cc;
int ret = ZopfliPNGOptimize(origpng_cc, opts, !!verbose, &resultpng_cc);
if (ret) {
return ret;
}
*resultpng_size = resultpng_cc.size();
*resultpng = (unsigned char*) malloc(resultpng_cc.size());
if (!(*resultpng)) {
return ENOMEM;
}
memcpy(*resultpng,
reinterpret_cast<unsigned char*>(&resultpng_cc[0]),
resultpng_cc.size());
return 0;
}
( run in 0.795 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )