Experimenting with libimagequant for mapnik/mapnik#2706
Still TODO: 1 & 4 bit encodings proper Mason integration some error checking adjustable dithering settings??
This commit is contained in:
parent
c3dfda4977
commit
01380588ba
5 changed files with 105 additions and 9 deletions
13
SConstruct
13
SConstruct
|
@ -84,6 +84,7 @@ pretty_dep_names = {
|
|||
'jpeg':'JPEG C library | configure with JPEG_LIBS & JPEG_INCLUDES',
|
||||
'tiff':'TIFF C library | configure with TIFF_LIBS & TIFF_INCLUDES',
|
||||
'png':'PNG C library | configure with PNG_LIBS & PNG_INCLUDES',
|
||||
'imgquant':'ImageQuant C library | configure with IQ_LIBS & IQ_INCLUDES',
|
||||
'webp':'WEBP C library | configure with WEBP_LIBS & WEBP_INCLUDES',
|
||||
'icuuc':'ICU C++ library | configure with ICU_LIBS & ICU_INCLUDES or use ICU_LIB_NAME to specify custom lib name | more info: http://site.icu-project.org/',
|
||||
'harfbuzz':'HarfBuzz text shaping library | configure with HB_LIBS & HB_INCLUDES',
|
||||
|
@ -346,6 +347,9 @@ opts.AddVariables(
|
|||
BoolVariable('PNG', 'Build Mapnik with PNG read and write support', 'True'),
|
||||
PathVariable('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include', PathVariable.PathAccept),
|
||||
PathVariable('PNG_LIBS','Search path for libpng library files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
|
||||
BoolVariable('IQ', 'Build Mapnik with ImageQuant support', 'True'),
|
||||
PathVariable('IQ_INCLUDES', 'Search path for libimagequant include files', '/usr/include', PathVariable.PathAccept),
|
||||
PathVariable('IQ_LIBS','Search path for libimagequant library files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
|
||||
BoolVariable('JPEG', 'Build Mapnik with JPEG read and write support', 'True'),
|
||||
PathVariable('JPEG_INCLUDES', 'Search path for libjpeg include files', '/usr/include', PathVariable.PathAccept),
|
||||
PathVariable('JPEG_LIBS', 'Search path for libjpeg library files', '/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
|
||||
|
@ -1304,6 +1308,15 @@ if not preconfigured:
|
|||
else:
|
||||
env['SKIPPED_DEPS'].extend(['png'])
|
||||
|
||||
if env['IQ']:
|
||||
OPTIONAL_LIBSHEADERS.append(['imagequant', 'libimagequant.h', False,'C','-DHAVE_IQ'])
|
||||
inc_path = env['%s_INCLUDES' % 'IQ']
|
||||
lib_path = env['%s_LIBS' % 'IQ']
|
||||
env.AppendUnique(CPPPATH = fix_path(inc_path))
|
||||
env.AppendUnique(LIBPATH = fix_path(lib_path))
|
||||
else:
|
||||
env['SKIPPED_DEPS'].extend(['iq'])
|
||||
|
||||
if env['WEBP']:
|
||||
OPTIONAL_LIBSHEADERS.append(['webp', 'webp/decode.h', False,'C','-DHAVE_WEBP'])
|
||||
inc_path = env['%s_INCLUDES' % 'WEBP']
|
||||
|
|
|
@ -107,6 +107,8 @@ CAIRO_INCLUDES = '${MASON_LINKED_REL}/include'
|
|||
CAIRO_LIBS = '${MASON_LINKED_REL}/lib'
|
||||
SQLITE_INCLUDES = '${MASON_LINKED_REL}/include'
|
||||
SQLITE_LIBS = '${MASON_LINKED_REL}/lib'
|
||||
IQ_INCLUDES = /usr/local/include
|
||||
IQ_LIBS = /usr/local/lib
|
||||
BENCHMARK = True
|
||||
CPP_TESTS = True
|
||||
PGSQL2SQLITE = True
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
// zlib
|
||||
#include <zlib.h> // for Z_DEFAULT_COMPRESSION
|
||||
#include "memory_datasource.hpp"
|
||||
#include "palette.hpp"
|
||||
|
||||
// boost
|
||||
|
||||
|
@ -39,6 +41,8 @@
|
|||
extern "C"
|
||||
{
|
||||
#include <png.h>
|
||||
// TODO: make this optional
|
||||
#include "libimagequant.h"
|
||||
}
|
||||
|
||||
#define MAX_OCTREE_LEVELS 4
|
||||
|
@ -46,22 +50,27 @@ extern "C"
|
|||
namespace mapnik {
|
||||
|
||||
struct png_options {
|
||||
|
||||
enum quantization_type { HEXTREE = 0, OCTTREE = 1, IMGQUANT = 2 };
|
||||
|
||||
int colors;
|
||||
int compression;
|
||||
int strategy;
|
||||
int trans_mode;
|
||||
int iq_speed;
|
||||
double gamma;
|
||||
bool paletted;
|
||||
bool use_hextree;
|
||||
quantization_type quantization;
|
||||
bool use_miniz;
|
||||
png_options() :
|
||||
colors(256),
|
||||
compression(Z_DEFAULT_COMPRESSION),
|
||||
strategy(Z_DEFAULT_STRATEGY),
|
||||
trans_mode(-1),
|
||||
iq_speed(3),
|
||||
gamma(-1),
|
||||
paletted(true),
|
||||
use_hextree(true),
|
||||
quantization(HEXTREE),
|
||||
use_miniz(false) {}
|
||||
};
|
||||
|
||||
|
@ -704,6 +713,55 @@ void save_as_png8_pal(T1 & file,
|
|||
save_as_png8<T1, T2, rgba_palette>(file, image, pal, pal.palette(), pal.alphaTable(), opts);
|
||||
}
|
||||
|
||||
// TODO: This only works with rgba8_t image types
|
||||
template <typename T1, typename T2>
|
||||
void save_as_png8_libimagequant(T1 & file,
|
||||
T2 const& image,
|
||||
png_options const& opts)
|
||||
{
|
||||
unsigned width = image.width();
|
||||
unsigned height = image.height();
|
||||
|
||||
// TODO: can this be done as a single blob, rather than using rows?
|
||||
uint32_t *buf[height];
|
||||
|
||||
// TODO: this won't work on big-endian architectures, liq expects
|
||||
// data in RGBA byte order
|
||||
for (size_t y = 0; y < height; ++y)
|
||||
{
|
||||
buf[y] = (uint32_t *)image.get_row(y);
|
||||
}
|
||||
|
||||
liq_attr *attr = liq_attr_create();
|
||||
// TODO: set gamma
|
||||
// TODO: error checking
|
||||
liq_set_speed(attr, opts.iq_speed);
|
||||
liq_image *liq_image = liq_image_create_rgba_rows(attr, (void **)buf, width, height, 0);
|
||||
liq_result *res = liq_quantize_image(attr, liq_image);
|
||||
|
||||
// Store palettized version
|
||||
image_gray8 reduced_image(width, height);
|
||||
liq_write_remapped_image(res, liq_image, (void *)reduced_image.data(), width*height*sizeof(gray8_t));
|
||||
const liq_palette *liq_pal = liq_get_palette(res);
|
||||
|
||||
std::vector<mapnik::rgb> palette;
|
||||
std::vector<unsigned> alpha;
|
||||
|
||||
for (int i = 0; i < liq_pal->count; ++i)
|
||||
{
|
||||
palette.push_back({ liq_pal->entries[i].r, liq_pal->entries[i].g, liq_pal->entries[i].b });
|
||||
alpha.push_back(liq_pal->entries[i].a);
|
||||
}
|
||||
|
||||
// TODO: support 1 & 4 bit packing, not just 8, to reduce the file size, particularly on blank
|
||||
// or solid tiles
|
||||
save_as_png(file, palette, reduced_image, width, height, 8, alpha, opts);
|
||||
|
||||
liq_attr_destroy(attr);
|
||||
liq_image_destroy(liq_image);
|
||||
liq_result_destroy(res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // MAPNIK_PNG_IO_HPP
|
||||
|
|
|
@ -73,6 +73,9 @@ if '-DHAVE_PNG' in env['CPPDEFINES']:
|
|||
lib_env['LIBS'].append('png')
|
||||
enabled_imaging_libraries.append('png_reader.cpp')
|
||||
|
||||
if '-DHAVE_IQ' in env['CPPDEFINES']:
|
||||
lib_env['LIBS'].append('imagequant')
|
||||
|
||||
if '-DMAPNIK_USE_PROJ4' in env['CPPDEFINES']:
|
||||
lib_env['LIBS'].append('proj')
|
||||
|
||||
|
|
|
@ -80,11 +80,23 @@ void handle_png_options(std::string const& type,
|
|||
}
|
||||
else if (t == "m=o")
|
||||
{
|
||||
opts.use_hextree = false;
|
||||
opts.quantization = png_options::OCTTREE;
|
||||
}
|
||||
else if (t == "m=h")
|
||||
{
|
||||
opts.use_hextree = true;
|
||||
opts.quantization = png_options::HEXTREE;
|
||||
}
|
||||
else if (t == "m=iq")
|
||||
{
|
||||
opts.quantization = png_options::IMGQUANT;
|
||||
|
||||
}
|
||||
else if (boost::algorithm::starts_with(t, "iq="))
|
||||
{
|
||||
if (!mapnik::util::string2int(t.substr(3), opts.iq_speed) || opts.iq_speed < 1 || opts.iq_speed > 10)
|
||||
{
|
||||
throw ImageWriterException("invalid iq speed parameter: " + t.substr(3));
|
||||
}
|
||||
}
|
||||
else if (t == "e=miniz")
|
||||
{
|
||||
|
@ -224,14 +236,18 @@ void process_rgba8_png_pal(T const& image,
|
|||
}
|
||||
else if (opts.paletted)
|
||||
{
|
||||
if (opts.use_hextree)
|
||||
if (opts.quantization == png_options::HEXTREE)
|
||||
{
|
||||
save_as_png8_hex(stream, image, opts);
|
||||
}
|
||||
else
|
||||
else if (opts.quantization == png_options::OCTTREE)
|
||||
{
|
||||
save_as_png8_oct(stream, image, opts);
|
||||
}
|
||||
else
|
||||
{
|
||||
save_as_png8_libimagequant(stream, image, opts);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -252,14 +268,18 @@ void process_rgba8_png(T const& image,
|
|||
handle_png_options(t, opts);
|
||||
if (opts.paletted)
|
||||
{
|
||||
if (opts.use_hextree)
|
||||
if (opts.quantization == png_options::HEXTREE)
|
||||
{
|
||||
save_as_png8_hex(stream, image, opts);
|
||||
}
|
||||
else
|
||||
else if (opts.quantization == png_options::OCTTREE)
|
||||
{
|
||||
save_as_png8_oct(stream, image, opts);
|
||||
}
|
||||
else
|
||||
{
|
||||
save_as_png8_libimagequant(stream, image, opts);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue