Files
test/source/blender/imbuf/intern/webp.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

225 lines
6.4 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
*/
#ifdef _WIN32
# include <io.h>
#else
# include <unistd.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <webp/decode.h>
#include <webp/encode.h>
#include "BLI_fileops.h"
#include "BLI_mmap.h"
#include "BLI_utildefines.h"
#include "IMB_allocimbuf.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
#include "IMB_filetype.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "MEM_guardedalloc.h"
bool imb_is_a_webp(const uchar *buf, size_t size)
{
if (WebPGetInfo(buf, size, nullptr, nullptr)) {
return true;
}
return false;
}
ImBuf *imb_loadwebp(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
if (!imb_is_a_webp(mem, size)) {
return nullptr;
}
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
WebPBitstreamFeatures features;
if (WebPGetFeatures(mem, size, &features) != VP8_STATUS_OK) {
fprintf(stderr, "WebP: Failed to parse features\n");
return nullptr;
}
const int planes = features.has_alpha ? 32 : 24;
ImBuf *ibuf = IMB_allocImBuf(features.width, features.height, planes, 0);
if (ibuf == nullptr) {
fprintf(stderr, "WebP: Failed to allocate image memory\n");
return nullptr;
}
if ((flags & IB_test) == 0) {
ibuf->ftype = IMB_FTYPE_WEBP;
imb_addrectImBuf(ibuf);
/* Flip the image during decoding to match Blender. */
uchar *last_row = ibuf->byte_buffer.data + 4 * (ibuf->y - 1) * ibuf->x;
if (WebPDecodeRGBAInto(mem, size, last_row, size_t(ibuf->x) * ibuf->y * 4, -4 * ibuf->x) ==
nullptr)
{
fprintf(stderr, "WebP: Failed to decode image\n");
}
}
return ibuf;
}
ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath,
const int /*flags*/,
const size_t max_thumb_size,
char colorspace[],
size_t *r_width,
size_t *r_height)
{
const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
if (file == -1) {
return nullptr;
}
const size_t data_size = BLI_file_descriptor_size(file);
imb_mmap_lock();
BLI_mmap_file *mmap_file = BLI_mmap_open(file);
imb_mmap_unlock();
close(file);
if (mmap_file == nullptr) {
return nullptr;
}
const uchar *data = static_cast<const uchar *>(BLI_mmap_get_pointer(mmap_file));
WebPDecoderConfig config;
if (!data || !WebPInitDecoderConfig(&config) ||
WebPGetFeatures(data, data_size, &config.input) != VP8_STATUS_OK)
{
fprintf(stderr, "WebP: Invalid file\n");
imb_mmap_lock();
BLI_mmap_free(mmap_file);
imb_mmap_unlock();
return nullptr;
}
/* Return full size of the image. */
*r_width = size_t(config.input.width);
*r_height = size_t(config.input.height);
const float scale = float(max_thumb_size) / MAX2(config.input.width, config.input.height);
const int dest_w = MAX2(int(config.input.width * scale), 1);
const int dest_h = MAX2(int(config.input.height * scale), 1);
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rect);
if (ibuf == nullptr) {
fprintf(stderr, "WebP: Failed to allocate image memory\n");
imb_mmap_lock();
BLI_mmap_free(mmap_file);
imb_mmap_unlock();
return nullptr;
}
config.options.no_fancy_upsampling = 1;
config.options.use_scaling = 1;
config.options.scaled_width = dest_w;
config.options.scaled_height = dest_h;
config.options.bypass_filtering = 1;
config.options.use_threads = 0;
config.options.flip = 1;
config.output.is_external_memory = 1;
config.output.colorspace = MODE_RGBA;
config.output.u.RGBA.rgba = ibuf->byte_buffer.data;
config.output.u.RGBA.stride = 4 * ibuf->x;
config.output.u.RGBA.size = size_t(config.output.u.RGBA.stride * ibuf->y);
if (WebPDecode(data, data_size, &config) != VP8_STATUS_OK) {
fprintf(stderr, "WebP: Failed to decode image\n");
imb_mmap_lock();
BLI_mmap_free(mmap_file);
imb_mmap_unlock();
return nullptr;
}
/* Free the output buffer. */
WebPFreeDecBuffer(&config.output);
imb_mmap_lock();
BLI_mmap_free(mmap_file);
imb_mmap_unlock();
return ibuf;
}
bool imb_savewebp(ImBuf *ibuf, const char *filepath, int /*flags*/)
{
const int bytesperpixel = (ibuf->planes + 7) >> 3;
uchar *encoded_data, *last_row;
size_t encoded_data_size;
if (bytesperpixel == 3) {
/* We must convert the ImBuf RGBA buffer to RGB as WebP expects a RGB buffer. */
const size_t num_pixels = ibuf->x * ibuf->y;
const uint8_t *rgba_rect = ibuf->byte_buffer.data;
uint8_t *rgb_rect = static_cast<uint8_t *>(
MEM_mallocN(sizeof(uint8_t) * num_pixels * 3, "webp rgb_rect"));
for (int i = 0; i < num_pixels; i++) {
rgb_rect[i * 3 + 0] = rgba_rect[i * 4 + 0];
rgb_rect[i * 3 + 1] = rgba_rect[i * 4 + 1];
rgb_rect[i * 3 + 2] = rgba_rect[i * 4 + 2];
}
last_row = (uchar *)(rgb_rect + (ibuf->y - 1) * ibuf->x * 3);
if (ibuf->foptions.quality == 100.0f) {
encoded_data_size = WebPEncodeLosslessRGB(
last_row, ibuf->x, ibuf->y, -3 * ibuf->x, &encoded_data);
}
else {
encoded_data_size = WebPEncodeRGB(
last_row, ibuf->x, ibuf->y, -3 * ibuf->x, ibuf->foptions.quality, &encoded_data);
}
MEM_freeN(rgb_rect);
}
else if (bytesperpixel == 4) {
last_row = ibuf->byte_buffer.data + 4 * (ibuf->y - 1) * ibuf->x;
if (ibuf->foptions.quality == 100.0f) {
encoded_data_size = WebPEncodeLosslessRGBA(
last_row, ibuf->x, ibuf->y, -4 * ibuf->x, &encoded_data);
}
else {
encoded_data_size = WebPEncodeRGBA(
last_row, ibuf->x, ibuf->y, -4 * ibuf->x, ibuf->foptions.quality, &encoded_data);
}
}
else {
fprintf(
stderr, "WebP: Unsupported bytes per pixel: %d for file: '%s'\n", bytesperpixel, filepath);
return false;
}
if (encoded_data != nullptr) {
FILE *fp = BLI_fopen(filepath, "wb");
if (!fp) {
free(encoded_data);
fprintf(stderr, "WebP: Cannot open file for writing: '%s'\n", filepath);
return false;
}
fwrite(encoded_data, encoded_data_size, 1, fp);
free(encoded_data);
fclose(fp);
}
return true;
}