void composite(T1 & im, T2 & im2, composite_mode_e mode, float opacity, int dx, int dy, bool premultiply_src, bool premultiply_dst) { typedef agg::rgba8 color; typedef agg::order_rgba order; typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba<color, order> blender_type; typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_type; typedef agg::renderer_base<pixfmt_type> renderer_type; agg::rendering_buffer source(im.getBytes(),im.width(),im.height(),im.width() * 4); agg::rendering_buffer mask(im2.getBytes(),im2.width(),im2.height(),im2.width() * 4); pixfmt_type pixf(source); pixf.comp_op(static_cast<agg::comp_op_e>(mode)); agg::pixfmt_rgba32 pixf_mask(mask); if (premultiply_src) pixf_mask.premultiply(); if (premultiply_dst) pixf.premultiply(); renderer_type ren(pixf); // TODO - is this really opacity, or agg::cover? ren.blend_from(pixf_mask,0, dx,dy,unsigned(255*opacity)); }
inline int import_image_data(T2 const& image, WebPPicture & pic, bool alpha) { // Reason for copy: https://github.com/mapnik/mapnik/issues/2024 // TODO - figure out way to pass view pixels directly to webp importer image_data_32 im(image.width(),image.height()); for (unsigned y = 0; y < image.height(); ++y) { typename T2::pixel_type const * row_from = image.getRow(y); image_data_32::pixel_type * row_to = im.getRow(y); for (unsigned x = 0; x < image.width(); ++x) { row_to[x] = row_from[x]; } } int stride = sizeof(typename T2::pixel_type) * im.width(); if (alpha) { return WebPPictureImportRGBA(&pic, im.getBytes(), stride); } else { #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 return WebPPictureImportRGBX(&pic, im.getBytes(), stride); #else return WebPPictureImportRGBA(&pic, im.getBytes(), stride); #endif } }
inline int import_image_data(T2 const& image, WebPPicture & pic, bool alpha) { ImageData<typename T2::pixel_type> const& data = image.data(); int stride = sizeof(typename T2::pixel_type) * image.width(); if (data.width() == image.width() && data.height() == image.height()) { if (alpha) { return WebPPictureImportRGBA(&pic, data.getBytes(), stride); } else { #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 return WebPPictureImportRGBX(&pic, data.getBytes(), stride); #else return WebPPictureImportRGBA(&pic, data.getBytes(), stride); #endif } } else { // need to copy: https://github.com/mapnik/mapnik/issues/2024 image_data_32 im(image.width(),image.height()); for (unsigned y = 0; y < image.height(); ++y) { typename T2::pixel_type const * row_from = image.getRow(y); image_data_32::pixel_type * row_to = im.getRow(y); std::memcpy(row_to,row_from,stride); } if (alpha) { return WebPPictureImportRGBA(&pic, im.getBytes(), stride); } else { #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 return WebPPictureImportRGBX(&pic, im.getBytes(), stride); #else return WebPPictureImportRGBA(&pic, im.getBytes(), stride); #endif } } }
void save_as_png8(T1 & file, T2 const& image, T3 const & tree, std::vector<mapnik::rgb> const& palette, std::vector<unsigned> const& alphaTable, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY) { unsigned width = image.width(); unsigned height = image.height(); if (palette.size() > 16 ) { // >16 && <=256 colors -> write 8-bit color depth image_data_8 reduced_image(width, height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); for (unsigned x = 0; x < width; ++x) { row_out[x] = tree.quantize(row[x]); } } save_as_png(file, palette, reduced_image, width, height, 8, compression, strategy, alphaTable); } else if (palette.size() == 1) { // 1 color image -> write 1-bit color depth PNG unsigned image_width = width > 7 ? (int(0.125*width) + 1)&~1 : 1; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); reduced_image.set(0); save_as_png(file, palette, reduced_image, width, height, 1, compression, strategy, alphaTable); } else { // <=16 colors -> write 4-bit color depth PNG unsigned image_width = width > 3 ? (int(0.5*width) + 3)&~3 : 4; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); byte index = 0; for (unsigned x = 0; x < width; ++x) { index = tree.quantize(row[x]); if (x%2 == 0) index = index<<4; row_out[x>>1] |= index; } } save_as_png(file, palette, reduced_image, width, height, 4, compression, strategy, alphaTable); } }
void save_as_jpeg(T1 & file,int quality, T2 const& image) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; int width=image.width(); int height=image.height(); cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); cinfo.dest = (struct jpeg_destination_mgr *)(*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, sizeof(dest_mgr)); dest_mgr * dest = (dest_mgr*) cinfo.dest; dest->pub.init_destination = init_destination; dest->pub.empty_output_buffer = empty_output_buffer; dest->pub.term_destination = term_destination; dest->out = &file; //jpeg_stdio_dest(&cinfo, fp); cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality,1); jpeg_start_compress(&cinfo, 1); JSAMPROW row_pointer[1]; JSAMPLE* row=reinterpret_cast<JSAMPLE*>( ::operator new (sizeof(JSAMPLE) * width*3)); while (cinfo.next_scanline < cinfo.image_height) { const unsigned* imageRow=image.getRow(cinfo.next_scanline); int index=0; for (int i=0;i<width;++i) { #ifdef MAPNIK_BIG_ENDIAN row[index++]=(imageRow[i]>>24)&0xff; row[index++]=(imageRow[i]>>16)&0xff; row[index++]=(imageRow[i]>>8)&0xff; #else row[index++]=(imageRow[i])&0xff; row[index++]=(imageRow[i]>>8)&0xff; row[index++]=(imageRow[i]>>16)&0xff; #endif } row_pointer[0] = &row[0]; (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); } ::operator delete(row); jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); }
void composite(T1 & dst, T2 & src, composite_mode_e mode, float opacity, int dx, int dy, bool premultiply_src) { using color = agg::rgba8; using order = agg::order_rgba; using blender_type = agg::comp_op_adaptor_rgba_pre<color, order>; using pixfmt_type = agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer>; using renderer_type = agg::renderer_base<pixfmt_type>; agg::rendering_buffer dst_buffer(dst.getBytes(),dst.width(),dst.height(),dst.width() * 4); agg::rendering_buffer src_buffer(src.getBytes(),src.width(),src.height(),src.width() * 4); pixfmt_type pixf(dst_buffer); pixf.comp_op(static_cast<agg::comp_op_e>(mode)); agg::pixfmt_rgba32 pixf_mask(src_buffer); if (premultiply_src) pixf_mask.premultiply(); renderer_type ren(pixf); ren.blend_from(pixf_mask,0,dx,dy,unsigned(255*opacity)); }
void save_as_png(T1 & file, T2 const& image, png_options const& opts) { png_voidp error_ptr=0; png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr,0, 0); if (!png_ptr) return; // switch on optimization only if supported #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) && defined(PNG_MMX_CODE_SUPPORTED) png_uint_32 mask, flags; flags = png_get_asm_flags(png_ptr); mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,static_cast<png_infopp>(0)); return; } jmp_buf* jmp_context = static_cast<jmp_buf*>(png_get_error_ptr(png_ptr)); if (jmp_context) { png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_set_write_fn (png_ptr, &file, &write_data<T1>, &flush_data<T1>); png_set_compression_level(png_ptr, opts.compression); png_set_compression_strategy(png_ptr, opts.strategy); png_set_compression_buffer_size(png_ptr, 32768); png_set_IHDR(png_ptr, info_ptr,image.width(),image.height(),8, (opts.trans_mode == 0) ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); const std::unique_ptr<png_bytep[]> row_pointers(new png_bytep[image.height()]); for (unsigned int i = 0; i < image.height(); i++) { row_pointers[i] = const_cast<png_bytep>(reinterpret_cast<const unsigned char *>(image.get_row(i))); } png_set_rows(png_ptr, info_ptr, row_pointers.get()); png_write_png(png_ptr, info_ptr, (opts.trans_mode == 0) ? PNG_TRANSFORM_STRIP_FILLER_AFTER : PNG_TRANSFORM_IDENTITY, nullptr); png_destroy_write_struct(&png_ptr, &info_ptr); }
inline int import_image(T2 const& im_in, WebPPicture & pic, bool alpha) { image<typename T2::pixel> const& data = im_in.data(); std::size_t width = im_in.width(); std::size_t height = im_in.height(); std::size_t stride = sizeof(typename T2::pixel_type) * width; if (data.width() == width && data.height() == height) { if (alpha) { return WebPPictureImportRGBA(&pic, data.bytes(), static_cast<int>(stride)); } else { #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 return WebPPictureImportRGBX(&pic, data.bytes(), static_cast<int>(stride)); #else return WebPPictureImportRGBA(&pic, data.bytes(), static_cast<int>(stride)); #endif } } else { // need to copy: https://github.com/mapnik/mapnik/issues/2024 image_rgba8 im(width,height); for (unsigned y = 0; y < height; ++y) { typename T2::pixel_type const * row_from = im_in.get_row(y); image_rgba8::pixel_type * row_to = im.get_row(y); std::copy(row_from, row_from + width, row_to); } if (alpha) { return WebPPictureImportRGBA(&pic, im.bytes(), static_cast<int>(stride)); } else { #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 return WebPPictureImportRGBX(&pic, im.bytes(), static_cast<int>(stride)); #else return WebPPictureImportRGBA(&pic, im.bytes(), static_cast<int>(stride)); #endif } } }
void save_as_png(T1 & file , T2 const& image, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY) { png_voidp error_ptr=0; png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr,0, 0); if (!png_ptr) return; // switch on optimization only if supported #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) && defined(PNG_MMX_CODE_SUPPORTED) png_uint_32 mask, flags; flags = png_get_asm_flags(png_ptr); mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,(png_infopp)0); return; } jmp_buf* jmp_context = (jmp_buf*) png_get_error_ptr(png_ptr); if (jmp_context) { png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_set_write_fn (png_ptr, &file, &write_data<T1>, &flush_data<T1>); png_set_compression_level(png_ptr, compression); png_set_compression_strategy(png_ptr, strategy); png_set_compression_buffer_size(png_ptr, 32768); png_set_IHDR(png_ptr, info_ptr,image.width(),image.height(),8, PNG_COLOR_TYPE_RGB_ALPHA,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); for (unsigned i=0;i<image.height();i++) { png_write_row(png_ptr,(png_bytep)image.getRow(i)); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); }
void save_as_webp(T1& file, T2 const& image, WebPConfig const& config, bool alpha) { 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(); int ok = 0; #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 pic.use_argb = !!config.lossless; // lossless fast track if (pic.use_argb) { pic.colorspace = static_cast<WebPEncCSP>(pic.colorspace | WEBP_CSP_ALPHA_BIT); if (WebPPictureAlloc(&pic)) { ok = 1; const int width = pic.width; const int height = pic.height; for (int y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.getRow(y); for (int x = 0; x < width; ++x) { const unsigned rgba = row[x]; unsigned a = (rgba >> 24) & 0xff; unsigned r = rgba & 0xff; unsigned g = (rgba >> 8 ) & 0xff; unsigned b = (rgba >> 16) & 0xff; const uint32_t argb = (a << 24) | (r << 16) | (g << 8) | (b); pic.argb[x + y * pic.argb_stride] = argb; } } }
void save_as_png8_hex(T1 & file, T2 const& image, int colors = 256, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY, int trans_mode = -1, double gamma = 2.0) { unsigned width = image.width(); unsigned height = image.height(); // structure for color quantization hextree<mapnik::rgba> tree(colors); if (trans_mode >= 0) tree.setTransMode(trans_mode); if (gamma > 0) tree.setGamma(gamma); for (unsigned y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.getRow(y); for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; tree.insert(mapnik::rgba(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val))); } } //transparency values per palette index std::vector<mapnik::rgba> pal; tree.create_palette(pal); assert(int(pal.size()) <= colors); std::vector<mapnik::rgb> palette; std::vector<unsigned> alphaTable; for(unsigned i=0; i<pal.size(); i++) { palette.push_back(rgb(pal[i].r, pal[i].g, pal[i].b)); alphaTable.push_back(pal[i].a); } save_as_png8<T1, T2, hextree<mapnik::rgba> >(file, image, tree, palette, alphaTable, compression, strategy); }
void save_as_png8_oct(T1 & file, T2 const& image, png_options const& opts) { // 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 const unsigned TRANSPARENCY_LEVELS = (opts.trans_mode==2||opts.trans_mode<0)?MAX_OCTREE_LEVELS:2; unsigned width = image.width(); unsigned height = image.height(); unsigned alphaHist[256];//transparency histogram unsigned semiCount = 0;//sum of semitransparent pixels unsigned meanAlpha = 0; if (opts.trans_mode == 0) { meanAlpha = 255; } else { for(int i=0; i<256; i++) { alphaHist[i] = 0; } for (unsigned y = 0; y < height; ++y) { for (unsigned x = 0; x < width; ++x) { unsigned val = U2ALPHA(static_cast<unsigned>(image.get_row(y)[x])); alphaHist[val]++; meanAlpha += val; if (val>0 && val<255) { semiCount++; } } } meanAlpha /= width*height; } // transparency ranges division points unsigned limits[MAX_OCTREE_LEVELS+1]; limits[0] = 0; limits[1] = (opts.trans_mode!=0 && alphaHist[0]>0)?1:0; limits[TRANSPARENCY_LEVELS] = 256; for(unsigned j=2; j<TRANSPARENCY_LEVELS; j++) { limits[j] = limits[1]; } if (opts.trans_mode != 0) { unsigned alphaHistSum = 0; for(unsigned i=1; i<256; i++) { alphaHistSum += alphaHist[i]; for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { if (alphaHistSum<semiCount*(j)/4) { limits[j] = i; } } } } // avoid too wide full transparent range if (limits[1]>256/(TRANSPARENCY_LEVELS-1)) { limits[1]=256/(TRANSPARENCY_LEVELS-1); } // avoid too wide full opaque range if (limits[TRANSPARENCY_LEVELS-1]<212) { limits[TRANSPARENCY_LEVELS-1]=212; } if (TRANSPARENCY_LEVELS==2) { limits[1]=127; } // estimated number of colors from palette assigned to chosen ranges unsigned cols[MAX_OCTREE_LEVELS]; // count colors if (opts.trans_mode == 0) { for (unsigned j=0; j<TRANSPARENCY_LEVELS; j++) { cols[j] = 0; } cols[TRANSPARENCY_LEVELS-1] = width * height; } else { for (unsigned j=0; j<TRANSPARENCY_LEVELS; j++) { cols[j] = 0; for (unsigned i=limits[j]; i<limits[j+1]; i++) { cols[j] += alphaHist[i]; } } } unsigned divCoef = width*height-cols[0]; if (divCoef==0) { divCoef = 1; } cols[0] = cols[0]>0?1:0; // fully transparent color (one or not at all) if (opts.colors>=64) { // give chance less populated but not empty cols to have at least few colors(12) unsigned minCols = (12+1)*divCoef/(opts.colors-cols[0]); for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { if (cols[j]>12 && cols[j]<minCols) { divCoef += minCols-cols[j]; cols[j] = minCols; } } } unsigned usedColors = cols[0]; for(unsigned j=1; j<TRANSPARENCY_LEVELS-1; j++) { cols[j] = cols[j]*(opts.colors-cols[0])/divCoef; usedColors += cols[j]; } // use rest for most opaque group of pixels cols[TRANSPARENCY_LEVELS-1] = opts.colors-usedColors; //no transparency if (opts.trans_mode == 0) { limits[1] = 0; cols[0] = 0; cols[1] = opts.colors; } // octree table for separate alpha range with 1-based index (0 is fully transparent: no color) octree<rgb> trees[MAX_OCTREE_LEVELS]; for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { trees[j].setMaxColors(cols[j]); } for (unsigned y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.get_row(y); for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; // insert to proper tree based on alpha range for(unsigned j=TRANSPARENCY_LEVELS-1; j>0; j--) { if (cols[j]>0 && U2ALPHA(val)>=limits[j]) { trees[j].insert(mapnik::rgb(U2RED(val), U2GREEN(val), U2BLUE(val))); break; } } } } unsigned leftovers = 0; std::vector<rgb> palette; palette.reserve(opts.colors); if (cols[0]) { palette.push_back(rgb(0,0,0)); } for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { if (cols[j]>0) { if (leftovers>0) { cols[j] += leftovers; trees[j].setMaxColors(cols[j]); leftovers = 0; } std::vector<rgb> pal; trees[j].setOffset( static_cast<unsigned>(palette.size())); trees[j].create_palette(pal); leftovers = cols[j] - static_cast<unsigned>(pal.size()); cols[j] = static_cast<unsigned>(pal.size()); palette.insert(palette.end(), pal.begin(), pal.end()); } } //transparency values per palette index std::vector<unsigned> alphaTable; //alphaTable.resize(palette.size());//allow semitransparency also in almost opaque range if (opts.trans_mode != 0) { alphaTable.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]); } if (palette.size() > 16 ) { // >16 && <=256 colors -> write 8-bit color depth image_gray8 reduced_image(width,height); reduce_8(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable); save_as_png(file,palette,reduced_image,width,height,8,alphaTable,opts); } else if (palette.size() == 1) { // 1 color image -> write 1-bit color depth PNG unsigned image_width = ((width + 15) >> 3) & ~1U; // 1-bit image, round up to 16-bit boundary unsigned image_height = height; image_gray8 reduced_image(image_width,image_height); reduce_1(image,reduced_image,trees, limits, alphaTable); if (meanAlpha<255 && cols[0]==0) { alphaTable.resize(1); alphaTable[0] = meanAlpha; } save_as_png(file,palette,reduced_image,width,height,1,alphaTable,opts); }
void save_as_png(T1 & file, T2 const& image, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY, int alpha = false, bool use_miniz = false) { if (use_miniz) { MiniZ::PNGWriter writer(compression); if (alpha) { writer.writeIHDR(image.width(), image.height(), 32); writer.writeIDAT(image); } else { writer.writeIHDR(image.width(), image.height(), 24); writer.writeIDATStripAlpha(image); } writer.writeIEND(); writer.toStream(file); return; } png_voidp error_ptr=0; png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr,0, 0); if (!png_ptr) return; // switch on optimization only if supported #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) && defined(PNG_MMX_CODE_SUPPORTED) png_uint_32 mask, flags; flags = png_get_asm_flags(png_ptr); mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,(png_infopp)0); return; } jmp_buf* jmp_context = (jmp_buf*) png_get_error_ptr(png_ptr); if (jmp_context) { png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_set_write_fn (png_ptr, &file, &write_data<T1>, &flush_data<T1>); png_set_compression_level(png_ptr, compression); png_set_compression_strategy(png_ptr, strategy); png_set_compression_buffer_size(png_ptr, 32768); png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), 8, alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_bytep row_pointers[image.height()]; for (unsigned int i = 0; i < image.height(); i++) { row_pointers[i] = (png_bytep)image.getRow(i); } png_set_rows(png_ptr, info_ptr, (png_bytepp)&row_pointers); png_write_png(png_ptr, info_ptr, alpha ? PNG_TRANSFORM_IDENTITY : PNG_TRANSFORM_STRIP_FILLER_AFTER, NULL); png_destroy_write_struct(&png_ptr, &info_ptr); }
void save_as_webp(T1& file, float quality, int method, int lossless, int image_hint, bool alpha, 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) >= 1 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(); int ok = 0; #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 pic.use_argb = !!lossless; // lossless fast track if (pic.use_argb) { pic.colorspace = static_cast<WebPEncCSP>(pic.colorspace | WEBP_CSP_ALPHA_BIT); if (WebPPictureAlloc(&pic)) { ok = 1; const int width = pic.width; const int height = pic.height; for (int y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.getRow(y); for (int x = 0; x < width; ++x) { const unsigned rgba = row[x]; unsigned a = (rgba >> 24) & 0xff; unsigned r = rgba & 0xff; unsigned g = (rgba >> 8 ) & 0xff; unsigned b = (rgba >> 16) & 0xff; const uint32_t argb = (a << 24) | (r << 16) | (g << 8) | (b); pic.argb[x + y * pic.argb_stride] = argb; } } }
void composite(T1 & im, T2 & im2, composite_mode_e mode) { typedef agg::rgba8 color; typedef agg::order_bgra order; typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba<color, order> blender_type; typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_type; typedef agg::renderer_base<pixfmt_type> renderer_type; typedef agg::comp_op_adaptor_rgba<color, order> blender_type; typedef agg::renderer_base<pixfmt_type> renderer_type; agg::rendering_buffer source(im.getBytes(),im.width(),im.height(),im.width() * 4); agg::rendering_buffer mask(im2.getBytes(),im2.width(),im2.height(),im2.width() * 4); agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixf(source); agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixf_mask(mask); switch(mode) { case clear : pixf.comp_op(agg::comp_op_clear); break; case src: pixf.comp_op(agg::comp_op_src); break; case dst: pixf.comp_op(agg::comp_op_dst); break; case src_over: pixf.comp_op(agg::comp_op_src_over); break; case dst_over: pixf.comp_op(agg::comp_op_dst_over); break; case src_in: pixf.comp_op(agg::comp_op_src_in); break; case dst_in: pixf.comp_op(agg::comp_op_dst_in); break; case src_out: pixf.comp_op(agg::comp_op_src_out); break; case dst_out: pixf.comp_op(agg::comp_op_dst_out); break; case src_atop: pixf.comp_op(agg::comp_op_src_atop); break; case dst_atop: pixf.comp_op(agg::comp_op_dst_atop); break; case _xor: pixf.comp_op(agg::comp_op_xor); break; case plus: pixf.comp_op(agg::comp_op_plus); break; case minus: pixf.comp_op(agg::comp_op_minus); break; case multiply: pixf.comp_op(agg::comp_op_multiply); break; case screen: pixf.comp_op(agg::comp_op_screen); break; case overlay: pixf.comp_op(agg::comp_op_overlay); break; case darken: pixf.comp_op(agg::comp_op_darken); break; case lighten: pixf.comp_op(agg::comp_op_lighten); break; case color_dodge: pixf.comp_op(agg::comp_op_color_dodge); break; case color_burn: pixf.comp_op(agg::comp_op_color_burn); break; case hard_light: pixf.comp_op(agg::comp_op_hard_light); break; case soft_light: pixf.comp_op(agg::comp_op_soft_light); break; case difference: pixf.comp_op(agg::comp_op_difference); break; case exclusion: pixf.comp_op(agg::comp_op_exclusion); break; case contrast: pixf.comp_op(agg::comp_op_contrast); break; case invert: pixf.comp_op(agg::comp_op_invert); break; case invert_rgb: pixf.comp_op(agg::comp_op_invert_rgb); break; default: break; } renderer_type ren(pixf); agg::renderer_base<pixfmt_type> rb(pixf); rb.blend_from(pixf_mask,0,0,0,255); }
void save_as_png256_hex(T1 & file, T2 const& image, int colors = 256, int trans_mode = -1, double gamma = 2.0) { unsigned width = image.width(); unsigned height = image.height(); // structure for color quantization hextree<mapnik::rgba> tree(colors); if (trans_mode >= 0) tree.setTransMode(trans_mode); if (gamma > 0) tree.setGamma(gamma); for (unsigned y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.getRow(y); for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; tree.insert(mapnik::rgba(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val))); } } //transparency values per palette index std::vector<mapnik::rgba> pal; tree.create_palette(pal); assert(int(pal.size()) <= colors); std::vector<mapnik::rgb> palette; std::vector<unsigned> alphaTable; for(unsigned i=0; i<pal.size(); i++) { palette.push_back(rgb(pal[i].r, pal[i].g, pal[i].b)); alphaTable.push_back(pal[i].a); } if (palette.size() > 16 ) { // >16 && <=256 colors -> write 8-bit color depth image_data_8 reduced_image(width, height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); row_out[x] = tree.quantize(c); } } save_as_png(file, palette, reduced_image, width, height, 8, alphaTable); } else if (palette.size() == 1) { // 1 color image -> write 1-bit color depth PNG unsigned image_width = (int(0.125*width) + 7)&~7; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); reduced_image.set(0); save_as_png(file, palette, reduced_image, width, height, 1, alphaTable); } else { // <=16 colors -> write 4-bit color depth PNG unsigned image_width = (int(0.5*width) + 3)&~3; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); byte index = 0; for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); index = tree.quantize(c); if (x%2 == 0) index = index<<4; row_out[x>>1] |= index; } } save_as_png(file, palette, reduced_image, width, height, 4, alphaTable); } }