mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-20 09:04:43 +00:00
231 lines
6.2 KiB
C
231 lines
6.2 KiB
C
|
// 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
|