webp encoding/decoding support - refs #1955

This commit is contained in:
Dane Springmeyer 2013-07-19 01:09:17 -04:00
parent db39528231
commit a141c5c27d
17 changed files with 630 additions and 11 deletions

View file

@ -48,22 +48,26 @@ Mapnik Core depends on:
- regex (optionally built with icu regex support)
- program_options (optionally for mapnik command line programs)
* libicuuc >= 4.0 (ideally >= 4.2) - International Components for Unicode
* libpng >= 1.2.x - PNG graphics
* libjpeg - JPEG graphics
* libtiff - TIFF graphics
* libz - Zlib compression
* libfreetype - Freetype2 for font support (Install requires freetype-config)
* libxml2 - XML parsing (Install requires xml2-config)
* libproj - PROJ.4 projection library
Mapnik Core optionally depends on:
* libpng >= 1.2.x - PNG graphics (Default enabled, if found)
* libjpeg - JPEG graphics (Default enabled, if found)
* libtiff - TIFF graphics (Default enabled, if found)
* libwebp - WEBP graphics (Default enabled, if found)
* libproj - PROJ.4 projection library (Default enabled, if found)
Mapnik Python bindings depend on:
* Python 2.5-2.7 or >= 3.2
* Boost python
Note: Python3k is supported, see: https://github.com/mapnik/mapnik/wiki/Python3k
Note: Python 3.x is supported, see: https://github.com/mapnik/mapnik/wiki/Python3k
Optional dependencies:
Additional optional dependencies:
* Cairo >= 1.6.0 - Graphics library for output formats like PDF, PS, and SVG
- pkg-config - Required for building with cairo support

View file

@ -73,6 +73,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',
'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/',
'z':'Z compression library | more info: http://www.zlib.net/',
'm':'Basic math library, part of C++ stlib',
@ -325,6 +326,9 @@ opts.AddVariables(
BoolVariable('TIFF', 'Build Mapnik with TIFF read and write support', 'True'),
PathVariable('TIFF_INCLUDES', 'Search path for libtiff include files', '/usr/include', PathVariable.PathAccept),
PathVariable('TIFF_LIBS', 'Search path for libtiff library files', '/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
BoolVariable('WEBP', 'Build Mapnik with WEBP read', 'True'),
PathVariable('WEBP_INCLUDES', 'Search path for libwebp include files', '/usr/include', PathVariable.PathAccept),
PathVariable('WEBP_LIBS','Search path for libwebp library files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
BoolVariable('PROJ', 'Build Mapnik with proj4 support to enable transformations between many different projections', 'True'),
PathVariable('PROJ_INCLUDES', 'Search path for PROJ.4 include files', '/usr/include', PathVariable.PathAccept),
PathVariable('PROJ_LIBS', 'Search path for PROJ.4 library files', '/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
@ -1185,7 +1189,7 @@ if not preconfigured:
if env['JPEG']:
env.Append(CPPDEFINES = '-DHAVE_JPEG')
LIBSHEADERS.append(['jpeg', ['stdio.h', 'jpeglib.h'], True,'C'])
LIBSHEADERS.append(['jpeg', ['stdio.h', 'jpeglib.h'], False,'C'])
inc_path = env['%s_INCLUDES' % 'JPEG']
lib_path = env['%s_LIBS' % 'JPEG']
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
@ -1195,7 +1199,7 @@ if not preconfigured:
if env['PROJ']:
env.Append(CPPDEFINES = '-DMAPNIK_USE_PROJ4')
LIBSHEADERS.append(['proj', 'proj_api.h', True,'C'])
LIBSHEADERS.append(['proj', 'proj_api.h', False,'C'])
inc_path = env['%s_INCLUDES' % 'PROJ']
lib_path = env['%s_LIBS' % 'PROJ']
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
@ -1205,7 +1209,7 @@ if not preconfigured:
if env['PNG']:
env.Append(CPPDEFINES = '-DHAVE_PNG')
LIBSHEADERS.append(['png', 'png.h', True,'C'])
LIBSHEADERS.append(['png', 'png.h', False,'C'])
inc_path = env['%s_INCLUDES' % 'PNG']
lib_path = env['%s_LIBS' % 'PNG']
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
@ -1213,9 +1217,19 @@ if not preconfigured:
else:
env['SKIPPED_DEPS'].extend(['png'])
if env['WEBP']:
env.Append(CPPDEFINES = '-DHAVE_WEBP')
LIBSHEADERS.append(['webp', 'webp/decode.h', False,'C'])
inc_path = env['%s_INCLUDES' % 'WEBP']
lib_path = env['%s_LIBS' % 'WEBP']
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
env.AppendUnique(LIBPATH = os.path.realpath(lib_path))
else:
env['SKIPPED_DEPS'].extend(['webp'])
if env['TIFF']:
env.Append(CPPDEFINES = '-DHAVE_TIFF')
LIBSHEADERS.append(['tiff', 'tiff.h', True,'C'])
LIBSHEADERS.append(['tiff', 'tiff.h', False,'C'])
inc_path = env['%s_INCLUDES' % 'TIFF']
lib_path = env['%s_LIBS' % 'TIFF']
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))

View file

@ -147,6 +147,12 @@ inline bool is_ps (std::string const& filename)
return boost::algorithm::iends_with(filename,std::string(".ps"));
}
inline bool is_webp (std::string const& filename)
{
return boost::algorithm::iends_with(filename,std::string(".webp"));
}
inline boost::optional<std::string> type_from_filename(std::string const& filename)
{
@ -157,6 +163,7 @@ inline boost::optional<std::string> type_from_filename(std::string const& filena
if (is_pdf(filename)) return result_type("pdf");
if (is_svg(filename)) return result_type("svg");
if (is_ps(filename)) return result_type("ps");
if (is_webp(filename)) return result_type("webp");
return result_type();
}

View file

@ -88,6 +88,12 @@ public:
{
return data_.getRow(row + y_) + x_;
}
inline char const* getBytes() const
{
return reinterpret_cast<char const*>(&data_);
}
inline T& data()
{
return data_;
@ -107,4 +113,3 @@ private:
}
#endif // MAPNIK_IMAGE_VIEW_HPP

130
include/mapnik/webp_io.hpp Normal file
View file

@ -0,0 +1,130 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 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_WEBP_IO_HPP
#define MAPNIK_WEBP_IO_HPP
#include <webp/encode.h>
#include <ostream>
#include <sstream>
#include <stdexcept>
namespace mapnik {
template <typename T>
int webp_stream_write(const uint8_t* data, size_t data_size, const WebPPicture* picture)
{
T* out = static_cast<T*>(picture->custom_ptr);
out->write(reinterpret_cast<const char*>(data), data_size);
return true;
}
std::string webp_encoding_error(WebPEncodingError error) {
std::ostringstream os;
switch (error) {
case VP8_ENC_ERROR_OUT_OF_MEMORY: os << "memory error allocating objects"; break;
case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY: os << "memory error while flushing bits"; break;
case VP8_ENC_ERROR_NULL_PARAMETER: os << "a pointer parameter is NULL"; break;
case VP8_ENC_ERROR_INVALID_CONFIGURATION: os << "configuration is invalid"; break;
case VP8_ENC_ERROR_BAD_DIMENSION: os << "picture has invalid width/height"; break;
case VP8_ENC_ERROR_PARTITION0_OVERFLOW: os << "partition is bigger than 512k"; break;
case VP8_ENC_ERROR_PARTITION_OVERFLOW: os << "partition is bigger than 16M"; break;
case VP8_ENC_ERROR_BAD_WRITE: os << "error while flushing bytes"; break;
case VP8_ENC_ERROR_FILE_TOO_BIG: os << "file is bigger than 4G"; break;
default: os << "unknown error (" << error << ")"; break;
}
os << " during encoding";
return os.str();
}
template <typename T1, typename T2>
void save_as_webp(T1& file,
float quality,
int method,
int lossless,
int image_hint,
T2 const& image)
{
WebPConfig config;
if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality))
{
throw std::runtime_error("version mismatch");
}
// Add additional tuning
if (method >= 0) config.method = method;
#if (WEBP_ENCODER_ABI_VERSION >> 8) >= 2
config.lossless = lossless;
config.image_hint = static_cast<WebPImageHint>(image_hint);
#else
#ifdef _MSC_VER
#pragma NOTE(compiling against webp that does not support lossless flag)
#else
#warning "compiling against webp that does not support lossless flag"
#endif
#endif
bool valid = WebPValidateConfig(&config);
if (!valid) {
throw std::runtime_error("Invalid configuration");
}
WebPPicture pic;
if (!WebPPictureInit(&pic))
{
throw std::runtime_error("version mismatch");
}
pic.width = image.width();
pic.height = image.height();
#if (WEBP_ENCODER_ABI_VERSION >> 8) >= 2
pic.use_argb = 1;
#endif
if (!WebPPictureAlloc(&pic))
{
throw std::runtime_error("memory error");
}
int stride = sizeof(typename T2::pixel_type) * image.width();
uint8_t const* bytes = reinterpret_cast<uint8_t const*>(image.getBytes());
int ok = WebPPictureImportRGBA(&pic, bytes, stride);
if (!ok)
{
throw std::runtime_error(webp_encoding_error(pic.error_code));
}
pic.writer = webp_stream_write<T1>;
pic.custom_ptr = &file;
ok = WebPEncode(&config, &pic);
WebPPictureFree(&pic);
if (!ok)
{
throw std::runtime_error(webp_encoding_error(pic.error_code));
}
file.flush();
}
}
#endif // MAPNIK_WEBP_IO_HPP

View file

@ -68,6 +68,9 @@ if env['PNG']:
if env['TIFF']:
lib_env['LIBS'].append('tiff')
if env['WEBP']:
lib_env['LIBS'].append('webp')
if env['JPEG']:
lib_env['LIBS'].append('jpeg')
@ -269,6 +272,12 @@ if env['PNG']:
png_reader.cpp
""")
if env['WEBP']:
source += Split(
"""
webp_reader.cpp
""")
# agg backend
source += Split(
"""

View file

@ -59,6 +59,14 @@ inline boost::optional<std::string> type_from_bytes(char const* data, size_t siz
}
}
if (size>=12)
{
if (data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F' &&
data[8] == 'W' && data[9] == 'E' && data[10] == 'B' && data[11] == 'P')
{
return result_type("webp");
}
}
return result_type();
}

View file

@ -40,6 +40,10 @@ extern "C"
#include <mapnik/jpeg_io.hpp>
#endif
#if defined(HAVE_WEBP)
#include <mapnik/webp_io.hpp>
#endif
#include <mapnik/image_util.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/graphics.hpp>
@ -237,6 +241,73 @@ void handle_png_options(std::string const& type,
}
#endif
#if defined(HAVE_WEBP)
void handle_webp_options(std::string const& type,
double & quality,
int & method,
int & lossless,
int & image_hint
)
{
if (type == "webp")
{
return;
}
if (type.length() > 4){
boost::char_separator<char> sep(":");
boost::tokenizer< boost::char_separator<char> > tokens(type, sep);
BOOST_FOREACH(std::string t, tokens)
{
if (boost::algorithm::starts_with(t, "quality="))
{
std::string val = t.substr(8);
if (!val.empty())
{
if (!mapnik::util::string2double(val,quality) || quality < 0.0 || quality > 100.0)
{
throw ImageWriterException("invalid webp quality: '" + val + "'");
}
}
}
else if (boost::algorithm::starts_with(t, "method="))
{
std::string val = t.substr(7);
if (!val.empty())
{
if (!mapnik::util::string2int(val,method) || method < 0 || method > 6)
{
throw ImageWriterException("invalid webp method: '" + val + "'");
}
}
}
else if (boost::algorithm::starts_with(t, "lossless="))
{
std::string val = t.substr(9);
if (!val.empty())
{
if (!mapnik::util::string2int(val,lossless) || lossless < 0 || lossless > 1)
{
throw ImageWriterException("invalid webp lossless: '" + val + "'");
}
}
}
else if (boost::algorithm::starts_with(t, "image_hint="))
{
std::string val = t.substr(11);
if (!val.empty())
{
if (!mapnik::util::string2int(val,image_hint) || image_hint < 0 || image_hint > 3)
{
throw ImageWriterException("invalid webp image_hint: '" + val + "'");
}
}
}
}
}
}
#endif
template <typename T>
void save_to_stream(T const& image,
std::ostream & stream,
@ -369,6 +440,26 @@ void save_to_stream(T const& image,
save_as_jpeg(stream, quality, image);
#else
throw ImageWriterException("jpeg output is not enabled in your build of Mapnik");
#endif
}
else if (boost::algorithm::starts_with(t, "webp"))
{
#if defined(HAVE_WEBP)
double quality = 90.0; // 0 lowest, 100 highest
int method = 3; // 0 if fastest, 6 slowest
int lossless = 0; // Lossless encoding (0=lossy(default), 1=lossless).
int image_hint = 3; // used when lossless=1
/*
WEBP_HINT_DEFAULT = 0, // default preset.
WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting
WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc).
WEBP_HINT_LAST
*/
handle_webp_options(t,quality,method,lossless, image_hint);
save_as_webp(stream, static_cast<float>(quality), method, lossless, image_hint, image);
#else
throw ImageWriterException("webp output is not enabled in your build of Mapnik");
#endif
}
else throw ImageWriterException("unknown file type: " + type);

239
src/webp_reader.cpp Normal file
View file

@ -0,0 +1,239 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 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/debug.hpp>
#include <mapnik/image_reader.hpp>
extern "C"
{
#include <webp/types.h>
#include <webp/decode.h>
}
// boost
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
// stl
#include <fstream>
namespace mapnik
{
struct external_buffer_policy
{
external_buffer_policy( uint8_t const* data, std::size_t size)
: data_(data),
size_(size) {}
uint8_t const* data() const
{
return data_;
}
std::size_t size() const
{
return size_;
}
uint8_t const* data_;
std::size_t size_;
};
struct internal_buffer_policy
{
internal_buffer_policy(std::size_t size)
: data_((size!=0) ? static_cast<uint8_t*>(::operator new(sizeof(uint8_t) * size)) : 0),
size_(size)
{}
uint8_t * data() const
{
return data_;
}
std::size_t size() const
{
return size_;
}
~internal_buffer_policy()
{
::operator delete(data_), data_=0;
}
uint8_t * data_;
std::size_t size_;
};
template <typename T>
class webp_reader : public image_reader
{
typedef T buffer_policy_type;
private:
struct config_guard
{
config_guard(WebPDecoderConfig & config)
: config_(config) {}
~config_guard()
{
WebPFreeDecBuffer(&config_.output);
}
WebPDecoderConfig & config_;
};
std::auto_ptr<buffer_policy_type> buffer_;
size_t size_;
unsigned width_;
unsigned height_;
public:
explicit webp_reader(char const* data, std::size_t size);
explicit webp_reader(std::string const& filename);
~webp_reader();
unsigned width() const;
unsigned height() const;
bool premultiplied_alpha() const { return false; }
void read(unsigned x,unsigned y,image_data_32& image);
private:
void init();
};
namespace
{
image_reader* create_webp_reader(char const * data, std::size_t size)
{
return new webp_reader<external_buffer_policy>(data, size);
}
image_reader* create_webp_reader2(std::string const& filename)
{
return new webp_reader<internal_buffer_policy>(filename);
}
const bool registered = register_image_reader("webp", create_webp_reader);
const bool registered2 = register_image_reader("webp", create_webp_reader2);
}
// ctor
template <typename T>
webp_reader<T>::webp_reader(char const* data, std::size_t size)
: buffer_(new buffer_policy_type(reinterpret_cast<uint8_t const*>(data), size)),
width_(0),
height_(0)
{
init();
}
template <typename T>
webp_reader<T>::webp_reader(std::string const& filename)
: buffer_(),
size_(0),
width_(0),
height_(0)
{
std::ifstream file(filename.c_str(), std::ios::binary);
if (!file)
{
throw image_reader_exception("WEBP: Can't read file:" + filename);
}
std::streampos beg = file.tellg();
file.seekg (0, std::ios::end);
std::streampos end = file.tellg();
std::size_t file_size = end - beg;
file.seekg (0, std::ios::beg);
buffer_ = std::auto_ptr<buffer_policy_type>(new buffer_policy_type(file_size));
file.read(reinterpret_cast<char*>(buffer_->data()), buffer_->size());
if (!file)
{
throw image_reader_exception("WEBP: Failed to read:" + filename);
}
init();
}
// dtor
template <typename T>
webp_reader<T>::~webp_reader()
{
//
}
template <typename T>
void webp_reader<T>::init()
{
int width, height;
if (!WebPGetInfo(buffer_->data(), buffer_->size(), &width, &height))
{
throw image_reader_exception("WEBP reader: WebPGetInfo failed");
}
width_ = width;
height_ = height;
}
template <typename T>
unsigned webp_reader<T>::width() const
{
return width_;
}
template <typename T>
unsigned webp_reader<T>::height() const
{
return height_;
}
template <typename T>
void webp_reader<T>::read(unsigned x0, unsigned y0,image_data_32& image)
{
WebPDecoderConfig config;
config_guard guard(config);
if (!WebPInitDecoderConfig(&config))
{
throw image_reader_exception("WEBP reader: WebPInitDecoderConfig failed");
}
config.options.use_cropping = 1;
config.options.crop_left = x0;
config.options.crop_top = y0;
config.options.crop_width = std::min(width_ - x0, image.width());
config.options.crop_height = std::min(height_ - y0, image.height());
if (WebPGetFeatures(buffer_->data(), buffer_->size(), &config.input) != VP8_STATUS_OK)
{
throw image_reader_exception("WEBP reader: WebPGetFeatures failed");
}
config.output.colorspace = MODE_RGBA;
config.output.u.RGBA.rgba = (uint8_t *)image.getBytes();
config.output.u.RGBA.stride = 4 * image.width();
config.output.u.RGBA.size = image.width() * image.height() * 4;
config.output.is_external_memory = 1;
if (WebPDecode(buffer_->data(), buffer_->size(), &config) != VP8_STATUS_OK)
{
throw image_reader_exception("WEBP reader: WebPDecode failed");
}
}
}

View file

View file

@ -67,6 +67,20 @@ int main(int argc, char** argv)
BOOST_TEST( true );
}
should_throw = "./tests/cpp_tests/data/blank.webp";
BOOST_TEST( mapnik::util::exists( should_throw ) );
type = mapnik::type_from_filename(should_throw);
BOOST_TEST( type );
try
{
std::auto_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(should_throw,*type));
BOOST_TEST( false );
}
catch (std::exception const&)
{
BOOST_TEST( true );
}
should_throw = "./tests/data/images/xcode-CgBI.png";
BOOST_TEST( mapnik::util::exists( should_throw ) );
type = mapnik::type_from_filename(should_throw);

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

View file

@ -0,0 +1,98 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os, mapnik
from timeit import Timer, time
from nose.tools import *
from utilities import execution_path, run_all
def setup():
# All of the paths used are relative, if we run the tests
# from another directory we need to chdir()
os.chdir(execution_path('.'))
tmp_dir = '/tmp/mapnik-webp/'
if not os.path.exists(tmp_dir):
os.makedirs(tmp_dir)
opts = [
'webp',
'webp:q=64',
]
def gen_filepath(name,format):
return os.path.join('images/support/encoding-opts',name+'-'+format.replace(":","+")+'.webp')
def test_quality_threshold():
im = mapnik.Image(256,256)
im.tostring('webp:quality=99.99000')
im.tostring('webp:quality=0')
im.tostring('webp:quality=0.001')
@raises(RuntimeError)
def test_quality_threshold_invalid():
im = mapnik.Image(256,256)
im.tostring('webp:quality=101')
@raises(RuntimeError)
def test_quality_threshold_invalid2():
im = mapnik.Image(256,256)
im.tostring('webp:quality=-1')
generate = False
def test_expected_encodings():
im = mapnik.Image(256,256)
for opt in opts:
expected = gen_filepath('solid',opt)
actual = os.path.join(tmp_dir,os.path.basename(expected))
if generate or not os.path.exists(expected):
print 'generating expected image %s' % expected
im.save(expected,opt)
im.save(actual,opt)
eq_(mapnik.Image.open(actual).tostring(),
mapnik.Image.open(expected).tostring(),
'%s (actual) not == to %s (expected)' % (actual,expected))
for opt in opts:
expected = gen_filepath('blank',opt)
actual = os.path.join(tmp_dir,os.path.basename(expected))
if generate or not os.path.exists(expected):
print 'generating expected image %s' % expected
im.save(expected,opt)
im.save(actual,opt)
eq_(mapnik.Image.open(actual).tostring(),
mapnik.Image.open(expected).tostring(),
'%s (actual) not == to %s (expected)' % (actual,expected))
def test_transparency_levels():
# create partial transparency image
im = mapnik.Image(256,256)
im.background = mapnik.Color('rgba(255,255,255,.5)')
c2 = mapnik.Color('rgba(255,255,0,.2)')
c3 = mapnik.Color('rgb(0,255,255)')
for y in range(0,im.height()/2):
for x in range(0,im.width()/2):
im.set_pixel(x,y,c2)
for y in range(im.height()/2,im.height()):
for x in range(im.width()/2,im.width()):
im.set_pixel(x,y,c3)
t0 = tmp_dir + 'white0.webp'
# octree
format = 'webp'
expected = 'images/support/transparency/white0.webp'
if generate or not os.path.exists(expected):
im.save('images/support/transparency/white0.webp')
im.save(t0,format)
im_in = mapnik.Image.open(t0)
t0_len = len(im_in.tostring(format))
eq_(t0_len,len(mapnik.Image.open(expected).tostring(format)))
if __name__ == "__main__":
setup()
run_all(eval(x) for x in dir() if x.startswith("test_"))