fix
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
// 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 <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#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<uint8_t>& output = *(std::vector<uint8_t>*)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<uint8_t>& 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.height; ++y) {
|
||||
const uint32_t* src =
|
||||
(const uint32_t*)(((const uint8_t*)image.data())
|
||||
+ y*spec.bytes_per_row);
|
||||
uint8_t* dst = row;
|
||||
unsigned int x, c;
|
||||
|
||||
for (x=0; x<spec.width; x++) {
|
||||
c = *(src++);
|
||||
*(dst++) = (c & spec.red_mask ) >> 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<height; ++y)
|
||||
rows[y] = (png_bytep)png_malloc(png, src_bytes_per_row);
|
||||
|
||||
for (int pass=0; pass<number_passes; ++pass)
|
||||
for (y=0; y<height; ++y)
|
||||
png_read_rows(png, rows+y, nullptr, 1);
|
||||
|
||||
for (y=0; y<height; ++y) {
|
||||
const uint8_t* src = rows[y];
|
||||
uint32_t* dst = (uint32_t*)(img.data() + y*spec.bytes_per_row);
|
||||
unsigned int x, r, g, b, a = 0;
|
||||
|
||||
for (x=0; x<width; x++) {
|
||||
r = *(src++);
|
||||
g = *(src++);
|
||||
b = *(src++);
|
||||
if (spec.alpha_mask)
|
||||
a = *(src++);
|
||||
*(dst++) =
|
||||
(r << spec.red_shift) |
|
||||
(g << spec.green_shift) |
|
||||
(b << spec.blue_shift) |
|
||||
(a << spec.alpha_shift);
|
||||
}
|
||||
png_free(png, rows[y]);
|
||||
}
|
||||
png_free(png, rows);
|
||||
|
||||
std::swap(*output_image, img);
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png, &info, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace x11
|
||||
} // namespace clip
|
||||
Reference in New Issue
Block a user