// Clip Library // Copyright (c) 2018-2021 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. #include "clip.h" #include #include #include "png.h" namespace clip { namespace x11 { ////////////////////////////////////////////////////////////////////// // Functions to convert clip::image into png data to store it in the // clipboard. void write_data_fn(png_structp png, png_bytep buf, png_size_t len) { std::vector& output = *(std::vector*)png_get_io_ptr(png); const size_t i = output.size(); output.resize(i+len); std::copy(buf, buf+len, output.begin()+i); } bool write_png(const image& image, std::vector& output) { png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png) return false; png_infop info = png_create_info_struct(png); if (!info) { png_destroy_write_struct(&png, nullptr); return false; } if (setjmp(png_jmpbuf(png))) { png_destroy_write_struct(&png, &info); return false; } png_set_write_fn(png, (png_voidp)&output, write_data_fn, nullptr); // No need for a flush function const image_spec& spec = image.spec(); int color_type = (spec.alpha_mask ? PNG_COLOR_TYPE_RGB_ALPHA: PNG_COLOR_TYPE_RGB); png_set_IHDR(png, info, spec.width, spec.height, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png, info); png_set_packing(png); png_bytep row = (png_bytep)png_malloc(png, png_get_rowbytes(png, info)); for (png_uint_32 y=0; y> spec.red_shift; *(dst++) = (c & spec.green_mask) >> spec.green_shift; *(dst++) = (c & spec.blue_mask ) >> spec.blue_shift; if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) *(dst++) = (c & spec.alpha_mask) >> spec.alpha_shift; } png_write_rows(png, &row, 1); } png_free(png, row); png_write_end(png, info); png_destroy_write_struct(&png, &info); return true; } ////////////////////////////////////////////////////////////////////// // Functions to convert png data stored in the clipboard to a // clip::image. struct read_png_io { const uint8_t* buf; size_t len; size_t pos; }; void read_data_fn(png_structp png, png_bytep buf, png_size_t len) { read_png_io& io = *(read_png_io*)png_get_io_ptr(png); if (io.pos < io.len) { size_t n = std::min(len, io.len-io.pos); if (n > 0) { std::copy(io.buf+io.pos, io.buf+io.pos+n, buf); io.pos += n; } } } bool read_png(const uint8_t* buf, const size_t len, image* output_image, image_spec* output_spec) { png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png) return false; png_infop info = png_create_info_struct(png); if (!info) { png_destroy_read_struct(&png, nullptr, nullptr); return false; } if (setjmp(png_jmpbuf(png))) { png_destroy_read_struct(&png, &info, nullptr); return false; } read_png_io io = { buf, len, 0 }; png_set_read_fn(png, (png_voidp)&io, read_data_fn); png_read_info(png, info); png_uint_32 width, height; int bit_depth, color_type, interlace_type; png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type, &interlace_type, nullptr, nullptr); image_spec spec; spec.width = width; spec.height = height; spec.bits_per_pixel = 32; // Don't use png_get_rowbytes(png, info) here because this is the // bytes_per_row of the output clip::image (the png file could // contain 24bpp but we want to return a 32bpp anyway with alpha=255 // in that case). spec.bytes_per_row = 4*width; spec.red_mask = 0x000000ff; spec.green_mask = 0x0000ff00; spec.blue_mask = 0x00ff0000; spec.red_shift = 0; spec.green_shift = 8; spec.blue_shift = 16; if ((color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) { spec.alpha_mask = 0xff000000; spec.alpha_shift = 24; } else { spec.alpha_mask = 0; spec.alpha_shift = 0; } if (output_spec) *output_spec = spec; if (output_image && width > 0 && height > 0) { image img(spec); // We want RGB 24-bit or RGBA 32-bit as a result png_set_strip_16(png); // Down to 8-bit (TODO we might support 16-bit values) png_set_packing(png); // Use one byte if color depth < 8-bit png_set_expand_gray_1_2_4_to_8(png); png_set_palette_to_rgb(png); png_set_gray_to_rgb(png); png_set_tRNS_to_alpha(png); int number_passes = png_set_interlace_handling(png); png_read_update_info(png, info); const int src_bytes_per_row = png_get_rowbytes(png, info); png_bytepp rows = (png_bytepp)png_malloc(png, sizeof(png_bytep)*height); png_uint_32 y; for (y=0; y