add miniz support - closes #1554
This commit is contained in:
parent
f9fa9c2461
commit
5a99d7fbdb
7 changed files with 5392 additions and 56 deletions
|
@ -8,6 +8,8 @@ For a complete change history, see the git log.
|
||||||
|
|
||||||
## Future
|
## Future
|
||||||
|
|
||||||
|
- Added alternative PNG/ZLIB implementation (`miniz`) that can be enabled with `e=miniz` (#1554)
|
||||||
|
|
||||||
- Added support for setting zlib `Z_FIXED` strategy with format string: `png:z=fixed`
|
- Added support for setting zlib `Z_FIXED` strategy with format string: `png:z=fixed`
|
||||||
|
|
||||||
- Fixed handling of transparency level option in Octree-based PNG encoding (#1556)
|
- Fixed handling of transparency level option in Octree-based PNG encoding (#1556)
|
||||||
|
|
82
include/mapnik/miniz_png.hpp
Normal file
82
include/mapnik/miniz_png.hpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Artem Pavlenko
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAPNIK_MINIZ_PNG_HPP
|
||||||
|
#define MAPNIK_MINIZ_PNG_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/palette.hpp>
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
/* miniz.c porting issues:
|
||||||
|
- duplicate symbols in python bindings require moving miniz.c include to just cpp file
|
||||||
|
- due to http://code.google.com/p/miniz/issues/detail?id=7
|
||||||
|
- avoiding including miniz.c here requires fwd declaring the two structs below
|
||||||
|
- being able to fwd declare requires removing typedef from struct declarations in miniz.c
|
||||||
|
- being able to fwd declare also requires using pointers to structs
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: try using #define MINIZ_HEADER_FILE_ONLY
|
||||||
|
struct tdefl_output_buffer;
|
||||||
|
struct tdefl_compressor;
|
||||||
|
|
||||||
|
namespace mapnik { namespace MiniZ {
|
||||||
|
|
||||||
|
using mapnik::rgb;
|
||||||
|
|
||||||
|
class PNGWriter {
|
||||||
|
|
||||||
|
public:
|
||||||
|
PNGWriter(int level, int strategy);
|
||||||
|
~PNGWriter();
|
||||||
|
private:
|
||||||
|
inline void writeUInt32BE(unsigned char *target, unsigned int value);
|
||||||
|
size_t startChunk(const unsigned char header[], size_t length);
|
||||||
|
void finishChunk(size_t start);
|
||||||
|
public:
|
||||||
|
void writeIHDR(unsigned int width, unsigned int height, unsigned char pixel_depth);
|
||||||
|
void writePLTE(std::vector<rgb> const& palette);
|
||||||
|
void writetRNS(std::vector<unsigned> const& alpha);
|
||||||
|
template<typename T>
|
||||||
|
void writeIDAT(T const& image);
|
||||||
|
void writeIEND();
|
||||||
|
void toStream(std::ostream& stream);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int flags;
|
||||||
|
tdefl_compressor *compressor;
|
||||||
|
tdefl_output_buffer *buffer;
|
||||||
|
static const unsigned char preamble[];
|
||||||
|
static const unsigned char IHDR_tpl[];
|
||||||
|
static const unsigned char PLTE_tpl[];
|
||||||
|
static const unsigned char tRNS_tpl[];
|
||||||
|
static const unsigned char IDAT_tpl[];
|
||||||
|
static const unsigned char IEND_tpl[];
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif // MAPNIK_MINIZ_PNG_HPP
|
|
@ -28,6 +28,7 @@
|
||||||
#include <mapnik/palette.hpp>
|
#include <mapnik/palette.hpp>
|
||||||
#include <mapnik/octree.hpp>
|
#include <mapnik/octree.hpp>
|
||||||
#include <mapnik/hextree.hpp>
|
#include <mapnik/hextree.hpp>
|
||||||
|
#include <mapnik/miniz_png.hpp>
|
||||||
#include <mapnik/image_data.hpp>
|
#include <mapnik/image_data.hpp>
|
||||||
// zlib
|
// zlib
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
@ -56,8 +57,23 @@ void flush_data (png_structp png_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
void save_as_png(T1 & file , T2 const& image, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY)
|
void save_as_png(T1 & file,
|
||||||
|
T2 const& image,
|
||||||
|
int compression = Z_DEFAULT_COMPRESSION,
|
||||||
|
int strategy = Z_DEFAULT_STRATEGY,
|
||||||
|
bool use_miniz = false)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
if (use_miniz)
|
||||||
|
{
|
||||||
|
MiniZ::PNGWriter writer(compression,strategy);
|
||||||
|
writer.writeIHDR(image.width(), image.height(), 32);
|
||||||
|
writer.writeIDAT(image);
|
||||||
|
writer.writeIEND();
|
||||||
|
writer.toStream(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
png_voidp error_ptr=0;
|
png_voidp error_ptr=0;
|
||||||
png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||||
error_ptr,0, 0);
|
error_ptr,0, 0);
|
||||||
|
@ -127,8 +143,10 @@ void reduce_8 (T const& in, image_data_8 & out, octree<rgb> trees[], unsigned l
|
||||||
mapnik::rgb c(U2RED(val), U2GREEN(val), U2BLUE(val));
|
mapnik::rgb c(U2RED(val), U2GREEN(val), U2BLUE(val));
|
||||||
byte index = 0;
|
byte index = 0;
|
||||||
int idx = -1;
|
int idx = -1;
|
||||||
for(int j=levels-1; j>0; j--){
|
for(int j=levels-1; j>0; j--)
|
||||||
if (U2ALPHA(val)>=limits[j] && trees[j].colors()>0) {
|
{
|
||||||
|
if (U2ALPHA(val)>=limits[j] && trees[j].colors()>0)
|
||||||
|
{
|
||||||
index = idx = trees[j].quantize(c);
|
index = idx = trees[j].quantize(c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -173,8 +191,10 @@ void reduce_4 (T const& in, image_data_8 & out, octree<rgb> trees[], unsigned li
|
||||||
mapnik::rgb c(U2RED(val), U2GREEN(val), U2BLUE(val));
|
mapnik::rgb c(U2RED(val), U2GREEN(val), U2BLUE(val));
|
||||||
byte index = 0;
|
byte index = 0;
|
||||||
int idx=-1;
|
int idx=-1;
|
||||||
for(int j=levels-1; j>0; j--){
|
for(int j=levels-1; j>0; j--)
|
||||||
if (U2ALPHA(val)>=limits[j] && trees[j].colors()>0) {
|
{
|
||||||
|
if (U2ALPHA(val)>=limits[j] && trees[j].colors()>0)
|
||||||
|
{
|
||||||
index = idx = trees[j].quantize(c);
|
index = idx = trees[j].quantize(c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -210,8 +230,23 @@ void save_as_png(T & file, std::vector<mapnik::rgb> const& palette,
|
||||||
unsigned color_depth,
|
unsigned color_depth,
|
||||||
int compression,
|
int compression,
|
||||||
int strategy,
|
int strategy,
|
||||||
std::vector<unsigned> const&alpha)
|
std::vector<unsigned> const&alpha,
|
||||||
|
bool use_miniz)
|
||||||
{
|
{
|
||||||
|
if (use_miniz)
|
||||||
|
{
|
||||||
|
MiniZ::PNGWriter writer(compression,strategy);
|
||||||
|
// image.width()/height() does not reflect the actual image dimensions; it
|
||||||
|
// refers to the quantized scanlines.
|
||||||
|
writer.writeIHDR(width, height, color_depth);
|
||||||
|
writer.writePLTE(palette);
|
||||||
|
writer.writetRNS(alpha);
|
||||||
|
writer.writeIDAT(image);
|
||||||
|
writer.writeIEND();
|
||||||
|
writer.toStream(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
png_voidp error_ptr=0;
|
png_voidp error_ptr=0;
|
||||||
png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||||
error_ptr,0, 0);
|
error_ptr,0, 0);
|
||||||
|
@ -277,8 +312,13 @@ void save_as_png(T & file, std::vector<mapnik::rgb> const& palette,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T1,typename T2>
|
template <typename T1,typename T2>
|
||||||
void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 256,
|
void save_as_png8_oct(T1 & file,
|
||||||
int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY, int trans_mode = -1)
|
T2 const& image,
|
||||||
|
const unsigned max_colors = 256,
|
||||||
|
int compression = Z_DEFAULT_COMPRESSION,
|
||||||
|
int strategy = Z_DEFAULT_STRATEGY,
|
||||||
|
int trans_mode = -1,
|
||||||
|
bool use_miniz = false)
|
||||||
{
|
{
|
||||||
// number of alpha ranges in png8 format; 2 results in smallest image with binary transparency
|
// number of alpha ranges in png8 format; 2 results in smallest image with binary transparency
|
||||||
// 3 is minimum for semitransparency, 4 is recommended, anything else is worse
|
// 3 is minimum for semitransparency, 4 is recommended, anything else is worse
|
||||||
|
@ -288,11 +328,14 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
unsigned alphaHist[256];//transparency histogram
|
unsigned alphaHist[256];//transparency histogram
|
||||||
unsigned semiCount = 0;//sum of semitransparent pixels
|
unsigned semiCount = 0;//sum of semitransparent pixels
|
||||||
unsigned meanAlpha = 0;
|
unsigned meanAlpha = 0;
|
||||||
for(int i=0; i<256; i++){
|
for(int i=0; i<256; i++)
|
||||||
|
{
|
||||||
alphaHist[i] = 0;
|
alphaHist[i] = 0;
|
||||||
}
|
}
|
||||||
for (unsigned y = 0; y < height; ++y){
|
for (unsigned y = 0; y < height; ++y)
|
||||||
for (unsigned x = 0; x < width; ++x){
|
{
|
||||||
|
for (unsigned x = 0; x < width; ++x)
|
||||||
|
{
|
||||||
unsigned val = U2ALPHA((unsigned)image.getRow(y)[x]);
|
unsigned val = U2ALPHA((unsigned)image.getRow(y)[x]);
|
||||||
if (trans_mode==0)
|
if (trans_mode==0)
|
||||||
val=255;
|
val=255;
|
||||||
|
@ -312,9 +355,11 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
unsigned alphaHistSum = 0;
|
unsigned alphaHistSum = 0;
|
||||||
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++)
|
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++)
|
||||||
limits[j] = limits[1];
|
limits[j] = limits[1];
|
||||||
for(unsigned i=1; i<256; i++){
|
for(unsigned i=1; i<256; i++)
|
||||||
|
{
|
||||||
alphaHistSum += alphaHist[i];
|
alphaHistSum += alphaHist[i];
|
||||||
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++){
|
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++)
|
||||||
|
{
|
||||||
if (alphaHistSum<semiCount*(j)/4)
|
if (alphaHistSum<semiCount*(j)/4)
|
||||||
limits[j] = i;
|
limits[j] = i;
|
||||||
}
|
}
|
||||||
|
@ -325,15 +370,18 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
// avoid too wide full opaque range
|
// avoid too wide full opaque range
|
||||||
if (limits[TRANSPARENCY_LEVELS-1]<212)
|
if (limits[TRANSPARENCY_LEVELS-1]<212)
|
||||||
limits[TRANSPARENCY_LEVELS-1]=212;
|
limits[TRANSPARENCY_LEVELS-1]=212;
|
||||||
if (TRANSPARENCY_LEVELS==2) {
|
if (TRANSPARENCY_LEVELS==2)
|
||||||
|
{
|
||||||
limits[1]=127;
|
limits[1]=127;
|
||||||
}
|
}
|
||||||
// estimated number of colors from palette assigned to chosen ranges
|
// estimated number of colors from palette assigned to chosen ranges
|
||||||
unsigned cols[MAX_OCTREE_LEVELS];
|
unsigned cols[MAX_OCTREE_LEVELS];
|
||||||
// count colors
|
// count colors
|
||||||
for(unsigned j=1; j<=TRANSPARENCY_LEVELS; j++) {
|
for(unsigned j=1; j<=TRANSPARENCY_LEVELS; j++)
|
||||||
|
{
|
||||||
cols[j-1] = 0;
|
cols[j-1] = 0;
|
||||||
for(unsigned i=limits[j-1]; i<limits[j]; i++){
|
for(unsigned i=limits[j-1]; i<limits[j]; i++)
|
||||||
|
{
|
||||||
cols[j-1] += alphaHist[i];
|
cols[j-1] += alphaHist[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,16 +390,22 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
if (divCoef==0) divCoef = 1;
|
if (divCoef==0) divCoef = 1;
|
||||||
cols[0] = cols[0]>0?1:0; // fully transparent color (one or not at all)
|
cols[0] = cols[0]>0?1:0; // fully transparent color (one or not at all)
|
||||||
|
|
||||||
if (max_colors>=64) {
|
if (max_colors>=64)
|
||||||
|
{
|
||||||
// give chance less populated but not empty cols to have at least few colors(12)
|
// give chance less populated but not empty cols to have at least few colors(12)
|
||||||
unsigned minCols = (12+1)*divCoef/(max_colors-cols[0]);
|
unsigned minCols = (12+1)*divCoef/(max_colors-cols[0]);
|
||||||
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) if (cols[j]>12 && cols[j]<minCols) {
|
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++)
|
||||||
|
{
|
||||||
|
if (cols[j]>12 && cols[j]<minCols)
|
||||||
|
{
|
||||||
divCoef += minCols-cols[j];
|
divCoef += minCols-cols[j];
|
||||||
cols[j] = minCols;
|
cols[j] = minCols;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unsigned usedColors = cols[0];
|
unsigned usedColors = cols[0];
|
||||||
for(unsigned j=1; j<TRANSPARENCY_LEVELS-1; j++){
|
for(unsigned j=1; j<TRANSPARENCY_LEVELS-1; j++)
|
||||||
|
{
|
||||||
cols[j] = cols[j]*(max_colors-cols[0])/divCoef;
|
cols[j] = cols[j]*(max_colors-cols[0])/divCoef;
|
||||||
usedColors += cols[j];
|
usedColors += cols[j];
|
||||||
}
|
}
|
||||||
|
@ -378,8 +432,10 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
unsigned val = row[x];
|
unsigned val = row[x];
|
||||||
|
|
||||||
// insert to proper tree based on alpha range
|
// insert to proper tree based on alpha range
|
||||||
for(unsigned j=TRANSPARENCY_LEVELS-1; j>0; j--){
|
for(unsigned j=TRANSPARENCY_LEVELS-1; j>0; j--)
|
||||||
if (cols[j]>0 && U2ALPHA(val)>=limits[j]) {
|
{
|
||||||
|
if (cols[j]>0 && U2ALPHA(val)>=limits[j])
|
||||||
|
{
|
||||||
trees[j].insert(mapnik::rgb(U2RED(val), U2GREEN(val), U2BLUE(val)));
|
trees[j].insert(mapnik::rgb(U2RED(val), U2GREEN(val), U2BLUE(val)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -390,11 +446,16 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
std::vector<rgb> palette;
|
std::vector<rgb> palette;
|
||||||
palette.reserve(max_colors);
|
palette.reserve(max_colors);
|
||||||
if (cols[0])
|
if (cols[0])
|
||||||
|
{
|
||||||
palette.push_back(rgb(0,0,0));
|
palette.push_back(rgb(0,0,0));
|
||||||
|
}
|
||||||
|
|
||||||
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) {
|
for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++)
|
||||||
if (cols[j]>0) {
|
{
|
||||||
if (leftovers>0) {
|
if (cols[j]>0)
|
||||||
|
{
|
||||||
|
if (leftovers>0)
|
||||||
|
{
|
||||||
cols[j] += leftovers;
|
cols[j] += leftovers;
|
||||||
trees[j].setMaxColors(cols[j]);
|
trees[j].setMaxColors(cols[j]);
|
||||||
leftovers = 0;
|
leftovers = 0;
|
||||||
|
@ -405,7 +466,8 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
assert(pal.size() <= max_colors);
|
assert(pal.size() <= max_colors);
|
||||||
leftovers = cols[j]-pal.size();
|
leftovers = cols[j]-pal.size();
|
||||||
cols[j] = pal.size();
|
cols[j] = pal.size();
|
||||||
for(unsigned i=0; i<pal.size(); i++){
|
for(unsigned i=0; i<pal.size(); i++)
|
||||||
|
{
|
||||||
palette.push_back(pal[i]);
|
palette.push_back(pal[i]);
|
||||||
}
|
}
|
||||||
assert(palette.size() <= 256);
|
assert(palette.size() <= 256);
|
||||||
|
@ -416,14 +478,16 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
std::vector<unsigned> alphaTable;
|
std::vector<unsigned> alphaTable;
|
||||||
//alphaTable.resize(palette.size());//allow semitransparency also in almost opaque range
|
//alphaTable.resize(palette.size());//allow semitransparency also in almost opaque range
|
||||||
if (trans_mode != 0)
|
if (trans_mode != 0)
|
||||||
|
{
|
||||||
alphaTable.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]);
|
alphaTable.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]);
|
||||||
|
}
|
||||||
|
|
||||||
if (palette.size() > 16 )
|
if (palette.size() > 16 )
|
||||||
{
|
{
|
||||||
// >16 && <=256 colors -> write 8-bit color depth
|
// >16 && <=256 colors -> write 8-bit color depth
|
||||||
image_data_8 reduced_image(width,height);
|
image_data_8 reduced_image(width,height);
|
||||||
reduce_8(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable);
|
reduce_8(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable);
|
||||||
save_as_png(file,palette,reduced_image,width,height,8,compression,strategy,alphaTable);
|
save_as_png(file,palette,reduced_image,width,height,8,compression,strategy,alphaTable,use_miniz);
|
||||||
}
|
}
|
||||||
else if (palette.size() == 1)
|
else if (palette.size() == 1)
|
||||||
{
|
{
|
||||||
|
@ -432,11 +496,12 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
unsigned image_height = height;
|
unsigned image_height = height;
|
||||||
image_data_8 reduced_image(image_width,image_height);
|
image_data_8 reduced_image(image_width,image_height);
|
||||||
reduce_1(image,reduced_image,trees, limits, alphaTable);
|
reduce_1(image,reduced_image,trees, limits, alphaTable);
|
||||||
if (meanAlpha<255 && cols[0]==0) {
|
if (meanAlpha<255 && cols[0]==0)
|
||||||
|
{
|
||||||
alphaTable.resize(1);
|
alphaTable.resize(1);
|
||||||
alphaTable[0] = meanAlpha;
|
alphaTable[0] = meanAlpha;
|
||||||
}
|
}
|
||||||
save_as_png(file,palette,reduced_image,width,height,1,compression,strategy,alphaTable);
|
save_as_png(file,palette,reduced_image,width,height,1,compression,strategy,alphaTable,use_miniz);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -445,15 +510,20 @@ void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 25
|
||||||
unsigned image_height = height;
|
unsigned image_height = height;
|
||||||
image_data_8 reduced_image(image_width,image_height);
|
image_data_8 reduced_image(image_width,image_height);
|
||||||
reduce_4(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable);
|
reduce_4(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable);
|
||||||
save_as_png(file,palette,reduced_image,width,height,4,compression,strategy,alphaTable);
|
save_as_png(file,palette,reduced_image,width,height,4,compression,strategy,alphaTable,use_miniz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T1, typename T2, typename T3>
|
template <typename T1, typename T2, typename T3>
|
||||||
void save_as_png8(T1 & file, T2 const& image, T3 const & tree,
|
void save_as_png8(T1 & file,
|
||||||
std::vector<mapnik::rgb> const& palette, std::vector<unsigned> const& alphaTable,
|
T2 const& image,
|
||||||
int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY)
|
T3 const & tree,
|
||||||
|
std::vector<mapnik::rgb> const& palette,
|
||||||
|
std::vector<unsigned> const& alphaTable,
|
||||||
|
int compression = Z_DEFAULT_COMPRESSION,
|
||||||
|
int strategy = Z_DEFAULT_STRATEGY,
|
||||||
|
bool use_miniz = false)
|
||||||
{
|
{
|
||||||
unsigned width = image.width();
|
unsigned width = image.width();
|
||||||
unsigned height = image.height();
|
unsigned height = image.height();
|
||||||
|
@ -462,18 +532,16 @@ void save_as_png8(T1 & file, T2 const& image, T3 const & tree,
|
||||||
{
|
{
|
||||||
// >16 && <=256 colors -> write 8-bit color depth
|
// >16 && <=256 colors -> write 8-bit color depth
|
||||||
image_data_8 reduced_image(width, height);
|
image_data_8 reduced_image(width, height);
|
||||||
|
|
||||||
for (unsigned y = 0; y < height; ++y)
|
for (unsigned y = 0; y < height; ++y)
|
||||||
{
|
{
|
||||||
mapnik::image_data_32::pixel_type const * row = image.getRow(y);
|
mapnik::image_data_32::pixel_type const * row = image.getRow(y);
|
||||||
mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y);
|
mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y);
|
||||||
|
|
||||||
for (unsigned x = 0; x < width; ++x)
|
for (unsigned x = 0; x < width; ++x)
|
||||||
{
|
{
|
||||||
row_out[x] = tree.quantize(row[x]);
|
row_out[x] = tree.quantize(row[x]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
save_as_png(file, palette, reduced_image, width, height, 8, compression, strategy, alphaTable);
|
save_as_png(file, palette, reduced_image, width, height, 8, compression, strategy, alphaTable, use_miniz);
|
||||||
}
|
}
|
||||||
else if (palette.size() == 1)
|
else if (palette.size() == 1)
|
||||||
{
|
{
|
||||||
|
@ -482,7 +550,7 @@ void save_as_png8(T1 & file, T2 const& image, T3 const & tree,
|
||||||
unsigned image_height = height;
|
unsigned image_height = height;
|
||||||
image_data_8 reduced_image(image_width, image_height);
|
image_data_8 reduced_image(image_width, image_height);
|
||||||
reduced_image.set(0);
|
reduced_image.set(0);
|
||||||
save_as_png(file, palette, reduced_image, width, height, 1, compression, strategy, alphaTable);
|
save_as_png(file, palette, reduced_image, width, height, 1, compression, strategy, alphaTable, use_miniz);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -504,14 +572,19 @@ void save_as_png8(T1 & file, T2 const& image, T3 const & tree,
|
||||||
row_out[x>>1] |= index;
|
row_out[x>>1] |= index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
save_as_png(file, palette, reduced_image, width, height, 4, compression, strategy, alphaTable);
|
save_as_png(file, palette, reduced_image, width, height, 4, compression, strategy, alphaTable, use_miniz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T1,typename T2>
|
template <typename T1,typename T2>
|
||||||
void save_as_png8_hex(T1 & file, T2 const& image, int colors = 256,
|
void save_as_png8_hex(T1 & file,
|
||||||
int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY,
|
T2 const& image,
|
||||||
int trans_mode = -1, double gamma = 2.0)
|
int colors = 256,
|
||||||
|
int compression = Z_DEFAULT_COMPRESSION,
|
||||||
|
int strategy = Z_DEFAULT_STRATEGY,
|
||||||
|
int trans_mode = -1,
|
||||||
|
double gamma = 2.0,
|
||||||
|
bool use_miniz = false)
|
||||||
{
|
{
|
||||||
unsigned width = image.width();
|
unsigned width = image.width();
|
||||||
unsigned height = image.height();
|
unsigned height = image.height();
|
||||||
|
@ -546,14 +619,18 @@ void save_as_png8_hex(T1 & file, T2 const& image, int colors = 256,
|
||||||
alphaTable.push_back(pal[i].a);
|
alphaTable.push_back(pal[i].a);
|
||||||
}
|
}
|
||||||
|
|
||||||
save_as_png8<T1, T2, hextree<mapnik::rgba> >(file, image, tree, palette, alphaTable, compression, strategy);
|
save_as_png8<T1, T2, hextree<mapnik::rgba> >(file, image, tree, palette, alphaTable, compression, strategy, use_miniz);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
void save_as_png8_pal(T1 & file, T2 const& image, rgba_palette const& pal,
|
void save_as_png8_pal(T1 & file,
|
||||||
int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY)
|
T2 const& image,
|
||||||
|
rgba_palette const& pal,
|
||||||
|
int compression = Z_DEFAULT_COMPRESSION,
|
||||||
|
int strategy = Z_DEFAULT_STRATEGY,
|
||||||
|
bool use_miniz = false)
|
||||||
{
|
{
|
||||||
save_as_png8<T1, T2, rgba_palette>(file, image, pal, pal.palette(), pal.alphaTable(), compression, strategy);
|
save_as_png8<T1, T2, rgba_palette>(file, image, pal, pal.palette(), pal.alphaTable(), compression, strategy, use_miniz);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ else: # unix, non-macos
|
||||||
|
|
||||||
source = Split(
|
source = Split(
|
||||||
"""
|
"""
|
||||||
|
miniz_png.cpp
|
||||||
color.cpp
|
color.cpp
|
||||||
css_color_grammar.cpp
|
css_color_grammar.cpp
|
||||||
conversions.cpp
|
conversions.cpp
|
||||||
|
|
|
@ -112,7 +112,8 @@ void handle_png_options(std::string const& type,
|
||||||
int * strategy,
|
int * strategy,
|
||||||
int * trans_mode,
|
int * trans_mode,
|
||||||
double * gamma,
|
double * gamma,
|
||||||
bool * use_octree)
|
bool * use_octree,
|
||||||
|
bool * use_miniz)
|
||||||
{
|
{
|
||||||
if (type == "png" || type == "png24" || type == "png32")
|
if (type == "png" || type == "png24" || type == "png32")
|
||||||
{
|
{
|
||||||
|
@ -139,6 +140,10 @@ void handle_png_options(std::string const& type,
|
||||||
{
|
{
|
||||||
*use_octree = true;
|
*use_octree = true;
|
||||||
}
|
}
|
||||||
|
else if (t == "e=miniz")
|
||||||
|
{
|
||||||
|
*use_miniz = true;
|
||||||
|
}
|
||||||
else if (boost::algorithm::starts_with(t, "c="))
|
else if (boost::algorithm::starts_with(t, "c="))
|
||||||
{
|
{
|
||||||
if (*colors < 0)
|
if (*colors < 0)
|
||||||
|
@ -174,9 +179,9 @@ void handle_png_options(std::string const& type,
|
||||||
*/
|
*/
|
||||||
if (!mapnik::util::string2int(t.substr(2),*compression)
|
if (!mapnik::util::string2int(t.substr(2),*compression)
|
||||||
|| *compression < Z_DEFAULT_COMPRESSION
|
|| *compression < Z_DEFAULT_COMPRESSION
|
||||||
|| *compression > Z_BEST_COMPRESSION)
|
|| *compression > 10) // use 10 here rather than Z_BEST_COMPRESSION (9) to allow for MZ_UBER_COMPRESSION
|
||||||
{
|
{
|
||||||
throw ImageWriterException("invalid compression parameter: " + t.substr(2) + " (only -1 through 9 are valid)");
|
throw ImageWriterException("invalid compression parameter: " + t.substr(2) + " (only -1 through 10 are valid)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (boost::algorithm::starts_with(t, "s="))
|
else if (boost::algorithm::starts_with(t, "s="))
|
||||||
|
@ -208,6 +213,10 @@ void handle_png_options(std::string const& type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((*use_miniz == false) && *compression > Z_BEST_COMPRESSION)
|
||||||
|
{
|
||||||
|
throw ImageWriterException("invalid compression value: (only -1 through 9 are valid)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +238,7 @@ void save_to_stream(T const& image,
|
||||||
int trans_mode = -1;
|
int trans_mode = -1;
|
||||||
double gamma = -1;
|
double gamma = -1;
|
||||||
bool use_octree = true;
|
bool use_octree = true;
|
||||||
|
bool use_miniz = false;
|
||||||
|
|
||||||
handle_png_options(t,
|
handle_png_options(t,
|
||||||
&colors,
|
&colors,
|
||||||
|
@ -236,16 +246,25 @@ void save_to_stream(T const& image,
|
||||||
&strategy,
|
&strategy,
|
||||||
&trans_mode,
|
&trans_mode,
|
||||||
&gamma,
|
&gamma,
|
||||||
&use_octree);
|
&use_octree,
|
||||||
|
&use_miniz);
|
||||||
|
|
||||||
if (palette.valid())
|
if (palette.valid())
|
||||||
save_as_png8_pal(stream, image, palette, compression, strategy);
|
{
|
||||||
|
save_as_png8_pal(stream, image, palette, compression, strategy, use_miniz);
|
||||||
|
}
|
||||||
else if (colors < 0)
|
else if (colors < 0)
|
||||||
save_as_png(stream, image, compression, strategy);
|
{
|
||||||
|
save_as_png(stream, image, compression, strategy, use_miniz);
|
||||||
|
}
|
||||||
else if (use_octree)
|
else if (use_octree)
|
||||||
save_as_png8_oct(stream, image, colors, compression, strategy, trans_mode);
|
{
|
||||||
|
save_as_png8_oct(stream, image, colors, compression, strategy, trans_mode, use_miniz);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
save_as_png8_hex(stream, image, colors, compression, strategy, trans_mode, gamma);
|
{
|
||||||
|
save_as_png8_hex(stream, image, colors, compression, strategy, trans_mode, gamma, use_miniz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (boost::algorithm::starts_with(t, "tif"))
|
else if (boost::algorithm::starts_with(t, "tif"))
|
||||||
{
|
{
|
||||||
|
@ -280,6 +299,7 @@ void save_to_stream(T const& image,
|
||||||
int trans_mode = -1;
|
int trans_mode = -1;
|
||||||
double gamma = -1;
|
double gamma = -1;
|
||||||
bool use_octree = true;
|
bool use_octree = true;
|
||||||
|
bool use_miniz = false;
|
||||||
|
|
||||||
handle_png_options(t,
|
handle_png_options(t,
|
||||||
&colors,
|
&colors,
|
||||||
|
@ -287,14 +307,21 @@ void save_to_stream(T const& image,
|
||||||
&strategy,
|
&strategy,
|
||||||
&trans_mode,
|
&trans_mode,
|
||||||
&gamma,
|
&gamma,
|
||||||
&use_octree);
|
&use_octree,
|
||||||
|
&use_miniz);
|
||||||
|
|
||||||
if (colors < 0)
|
if (colors < 0)
|
||||||
save_as_png(stream, image, compression, strategy);
|
{
|
||||||
|
save_as_png(stream, image, compression, strategy, use_miniz);
|
||||||
|
}
|
||||||
else if (use_octree)
|
else if (use_octree)
|
||||||
save_as_png8_oct(stream, image, colors, compression, strategy, trans_mode);
|
{
|
||||||
|
save_as_png8_oct(stream, image, colors, compression, strategy, trans_mode, use_miniz);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
save_as_png8_hex(stream, image, colors, compression, strategy, trans_mode, gamma);
|
{
|
||||||
|
save_as_png8_hex(stream, image, colors, compression, strategy, trans_mode, gamma, use_miniz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (boost::algorithm::starts_with(t, "tif"))
|
else if (boost::algorithm::starts_with(t, "tif"))
|
||||||
{
|
{
|
||||||
|
|
4834
src/miniz.c
Normal file
4834
src/miniz.c
Normal file
File diff suppressed because it is too large
Load diff
313
src/miniz_png.cpp
Normal file
313
src/miniz_png.cpp
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Artem Pavlenko
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/palette.hpp>
|
||||||
|
#include <mapnik/miniz_png.hpp>
|
||||||
|
#include <mapnik/image_data.hpp>
|
||||||
|
#include <mapnik/image_view.hpp>
|
||||||
|
|
||||||
|
// miniz
|
||||||
|
#define MINIZ_NO_ARCHIVE_APIS
|
||||||
|
#define MINIZ_NO_STDIO
|
||||||
|
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
||||||
|
#include "miniz.c"
|
||||||
|
|
||||||
|
// zlib
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace mapnik { namespace MiniZ {
|
||||||
|
|
||||||
|
PNGWriter::PNGWriter(int level, int strategy)
|
||||||
|
{
|
||||||
|
buffer = NULL;
|
||||||
|
compressor = NULL;
|
||||||
|
|
||||||
|
if (level == -1)
|
||||||
|
{
|
||||||
|
level = MZ_DEFAULT_LEVEL; // 6
|
||||||
|
}
|
||||||
|
else if (level < 0 || level > 10)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("compression level must be between 0 and 10");
|
||||||
|
}
|
||||||
|
flags = s_tdefl_num_probes[level]| (level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0 | TDEFL_WRITE_ZLIB_HEADER;
|
||||||
|
|
||||||
|
if (strategy == Z_FILTERED) flags |= TDEFL_FILTER_MATCHES;
|
||||||
|
else if (strategy == Z_HUFFMAN_ONLY) flags &= ~TDEFL_MAX_PROBES_MASK;
|
||||||
|
else if (strategy == Z_RLE) flags |= TDEFL_RLE_MATCHES;
|
||||||
|
else if (strategy == Z_FIXED) flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
|
||||||
|
|
||||||
|
buffer = (tdefl_output_buffer *)MZ_MALLOC(sizeof(tdefl_output_buffer));
|
||||||
|
if (buffer == NULL)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->m_pBuf = NULL;
|
||||||
|
buffer->m_capacity = 8192;
|
||||||
|
buffer->m_expandable = MZ_TRUE;
|
||||||
|
buffer->m_pBuf = (mz_uint8 *)MZ_MALLOC(buffer->m_capacity);
|
||||||
|
if (buffer->m_pBuf == NULL)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
compressor = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
|
||||||
|
if (compressor == NULL)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset output buffer.
|
||||||
|
buffer->m_size = 0;
|
||||||
|
tdefl_init(compressor, tdefl_output_buffer_putter, buffer, flags);
|
||||||
|
|
||||||
|
// Write preamble.
|
||||||
|
mz_bool status = tdefl_output_buffer_putter(preamble, 8, buffer);
|
||||||
|
if (status != MZ_TRUE)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PNGWriter::~PNGWriter()
|
||||||
|
{
|
||||||
|
if (compressor)
|
||||||
|
{
|
||||||
|
MZ_FREE(compressor);
|
||||||
|
}
|
||||||
|
if (buffer)
|
||||||
|
{
|
||||||
|
if (buffer->m_pBuf)
|
||||||
|
{
|
||||||
|
MZ_FREE(buffer->m_pBuf);
|
||||||
|
}
|
||||||
|
MZ_FREE(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PNGWriter::writeUInt32BE(mz_uint8 *target, mz_uint32 value)
|
||||||
|
{
|
||||||
|
target[0] = (value >> 24) & 0xFF;
|
||||||
|
target[1] = (value >> 16) & 0xFF;
|
||||||
|
target[2] = (value >> 8) & 0xFF;
|
||||||
|
target[3] = value & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PNGWriter::startChunk(const mz_uint8 header[], size_t length)
|
||||||
|
{
|
||||||
|
size_t start = buffer->m_size;
|
||||||
|
mz_bool status = tdefl_output_buffer_putter(header, length, buffer);
|
||||||
|
if (status != MZ_TRUE)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PNGWriter::finishChunk(size_t start)
|
||||||
|
{
|
||||||
|
// Write chunk length at the beginning of the chunk.
|
||||||
|
size_t payloadLength = buffer->m_size - start - 4 - 4;
|
||||||
|
writeUInt32BE(buffer->m_pBuf + start, payloadLength);
|
||||||
|
|
||||||
|
// Write CRC32 checksum. Don't include the 4-byte length, but /do/ include
|
||||||
|
// the 4-byte chunk name.
|
||||||
|
mz_uint32 crc = mz_crc32(MZ_CRC32_INIT, buffer->m_pBuf + start + 4, payloadLength + 4);
|
||||||
|
mz_uint8 checksum[] = { crc >> 24, crc >> 16, crc >> 8, crc };
|
||||||
|
mz_bool status = tdefl_output_buffer_putter(checksum, 4, buffer);
|
||||||
|
if (status != MZ_TRUE)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PNGWriter::writeIHDR(mz_uint32 width, mz_uint32 height, mz_uint8 pixel_depth)
|
||||||
|
{
|
||||||
|
// Write IHDR chunk.
|
||||||
|
size_t IHDR = startChunk(IHDR_tpl, 21);
|
||||||
|
writeUInt32BE(buffer->m_pBuf + IHDR + 8, width);
|
||||||
|
writeUInt32BE(buffer->m_pBuf + IHDR + 12, height);
|
||||||
|
|
||||||
|
if (pixel_depth == 32)
|
||||||
|
{
|
||||||
|
// Alpha full color image.
|
||||||
|
buffer->m_pBuf[IHDR + 16] = 8; // bit depth
|
||||||
|
buffer->m_pBuf[IHDR + 17] = 6; // color type (6 == true color with alpha)
|
||||||
|
}
|
||||||
|
else if (pixel_depth == 24)
|
||||||
|
{
|
||||||
|
// Full color image.
|
||||||
|
buffer->m_pBuf[IHDR + 16] = 8; // bit depth
|
||||||
|
buffer->m_pBuf[IHDR + 17] = 2; // color type (2 == true color without alpha)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Paletted image.
|
||||||
|
buffer->m_pBuf[IHDR + 16] = pixel_depth; // bit depth
|
||||||
|
buffer->m_pBuf[IHDR + 17] = 3; // color type (3 == indexed color)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->m_pBuf[IHDR + 18] = 0; // compression method
|
||||||
|
buffer->m_pBuf[IHDR + 19] = 0; // filter method
|
||||||
|
buffer->m_pBuf[IHDR + 20] = 0; // interlace method
|
||||||
|
finishChunk(IHDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PNGWriter::writePLTE(std::vector<rgb> const& palette)
|
||||||
|
{
|
||||||
|
// Write PLTE chunk.
|
||||||
|
size_t PLTE = startChunk(PLTE_tpl, 8);
|
||||||
|
const mz_uint8 *colors = reinterpret_cast<const mz_uint8 *>(&palette[0]);
|
||||||
|
mz_bool status = tdefl_output_buffer_putter(colors, palette.size() * 3, buffer);
|
||||||
|
if (status != MZ_TRUE)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
finishChunk(PLTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PNGWriter::writetRNS(std::vector<unsigned> const& alpha)
|
||||||
|
{
|
||||||
|
if (alpha.size() == 0) return;
|
||||||
|
|
||||||
|
std::vector<unsigned char> transparency(alpha.size());
|
||||||
|
unsigned char transparencySize = 0; // Stores position of biggest to nonopaque value.
|
||||||
|
for(unsigned i = 0; i < alpha.size(); i++)
|
||||||
|
{
|
||||||
|
transparency[i] = alpha[i];
|
||||||
|
if (alpha[i] < 255)
|
||||||
|
{
|
||||||
|
transparencySize = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (transparencySize > 0)
|
||||||
|
{
|
||||||
|
// Write tRNS chunk.
|
||||||
|
size_t tRNS = startChunk(tRNS_tpl, 8);
|
||||||
|
mz_bool status = tdefl_output_buffer_putter(&transparency[0], transparencySize, buffer);
|
||||||
|
if (status != MZ_TRUE)
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
finishChunk(tRNS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void PNGWriter::writeIDAT(T const& image)
|
||||||
|
{
|
||||||
|
// Write IDAT chunk.
|
||||||
|
size_t IDAT = startChunk(IDAT_tpl, 8);
|
||||||
|
mz_uint8 filter_type = 0;
|
||||||
|
tdefl_status status;
|
||||||
|
|
||||||
|
int bytes_per_pixel = sizeof(typename T::pixel_type);
|
||||||
|
int stride = image.width() * bytes_per_pixel;
|
||||||
|
|
||||||
|
for (unsigned int y = 0; y < image.height(); y++)
|
||||||
|
{
|
||||||
|
// Write filter_type
|
||||||
|
status = tdefl_compress_buffer(compressor, &filter_type, 1, TDEFL_NO_FLUSH);
|
||||||
|
if (status != TDEFL_STATUS_OKAY)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to compress image");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write scanline
|
||||||
|
status = tdefl_compress_buffer(compressor, (mz_uint8 *)image.getRow(y), stride, TDEFL_NO_FLUSH);
|
||||||
|
if (status != TDEFL_STATUS_OKAY)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to compress image");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = tdefl_compress_buffer(compressor, NULL, 0, TDEFL_FINISH);
|
||||||
|
if (status != TDEFL_STATUS_DONE)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to compress image");
|
||||||
|
}
|
||||||
|
|
||||||
|
finishChunk(IDAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PNGWriter::writeIEND()
|
||||||
|
{
|
||||||
|
// Write IEND chunk.
|
||||||
|
size_t IEND = startChunk(IEND_tpl, 8);
|
||||||
|
finishChunk(IEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PNGWriter::toStream(std::ostream& stream)
|
||||||
|
{
|
||||||
|
stream.write((char *)buffer->m_pBuf, buffer->m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mz_uint8 PNGWriter::preamble[] = {
|
||||||
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a
|
||||||
|
};
|
||||||
|
|
||||||
|
const mz_uint8 PNGWriter::IHDR_tpl[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x0D, // chunk length
|
||||||
|
'I', 'H', 'D', 'R', // "IHDR"
|
||||||
|
0x00, 0x00, 0x00, 0x00, // image width (4 bytes)
|
||||||
|
0x00, 0x00, 0x00, 0x00, // image height (4 bytes)
|
||||||
|
0x00, // bit depth (1 byte)
|
||||||
|
0x00, // color type (1 byte)
|
||||||
|
0x00, // compression method (1 byte), has to be 0
|
||||||
|
0x00, // filter method (1 byte)
|
||||||
|
0x00 // interlace method (1 byte)
|
||||||
|
};
|
||||||
|
|
||||||
|
const mz_uint8 PNGWriter::PLTE_tpl[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, // chunk length
|
||||||
|
'P', 'L', 'T', 'E' // "IDAT"
|
||||||
|
};
|
||||||
|
|
||||||
|
const mz_uint8 PNGWriter::tRNS_tpl[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, // chunk length
|
||||||
|
't', 'R', 'N', 'S' // "IDAT"
|
||||||
|
};
|
||||||
|
|
||||||
|
const mz_uint8 PNGWriter::IDAT_tpl[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, // chunk length
|
||||||
|
'I', 'D', 'A', 'T' // "IDAT"
|
||||||
|
};
|
||||||
|
|
||||||
|
const mz_uint8 PNGWriter::IEND_tpl[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, // chunk length
|
||||||
|
'I', 'E', 'N', 'D' // "IEND"
|
||||||
|
};
|
||||||
|
|
||||||
|
template void PNGWriter::writeIDAT<image_data_8>(image_data_8 const& image);
|
||||||
|
template void PNGWriter::writeIDAT<image_view<image_data_8> >(image_view<image_data_8> const& image);
|
||||||
|
template void PNGWriter::writeIDAT<image_data_32>(image_data_32 const& image);
|
||||||
|
template void PNGWriter::writeIDAT<image_view<image_data_32> >(image_view<image_data_32> const& image);
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
Loading…
Reference in a new issue