diff options
author | Aaron Carlisle <carlisle.b3d@gmail.com> | 2022-03-25 01:24:06 +0300 |
---|---|---|
committer | Aaron Carlisle <carlisle.b3d@gmail.com> | 2022-03-25 01:24:06 +0300 |
commit | 4fd0a69d7ba86e92390c421a745c6f32f1050c31 (patch) | |
tree | e98005f5a099665d09296569fbee1ef5e72b1d1e /source/blender/imbuf/intern | |
parent | 07846b31f34caa88244d192ee7d3aa6c057ac602 (diff) |
ImBuf: Add support for WebP image format
Currently only supports single image frames (no animation possible).
If quality slider is set to 100 then lossless compression will be used,
otherwise lossy compression is used.
Gives about 35% reduction of filesize save when re-saving splash screens with lossless
compression.
Also saves much faster, up to 15x faster than PNG with a better compression ratio as a plus.
Note, this is currently left disabled until we have WebP libs (see T95206)
For testing precompiled libs can be downloaded from Google:
https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html
Differential Revision: https://developer.blender.org/D1598
Diffstat (limited to 'source/blender/imbuf/intern')
-rw-r--r-- | source/blender/imbuf/intern/IMB_filetype.h | 13 | ||||
-rw-r--r-- | source/blender/imbuf/intern/filetype.c | 14 | ||||
-rw-r--r-- | source/blender/imbuf/intern/util.c | 13 | ||||
-rw-r--r-- | source/blender/imbuf/intern/webp.c | 129 |
4 files changed, 164 insertions, 5 deletions
diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 035c5b10c60..31f8b3a9505 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -238,3 +238,16 @@ void imb_loadtiletiff( bool imb_savetiff(struct ImBuf *ibuf, const char *filepath, int flags); /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: TIFF (#IMB_FTYPE_WEBP) + * \{ */ + +bool imb_is_a_webp(const unsigned char *buf, size_t size); +struct ImBuf *imb_loadwebp(const unsigned char *mem, + size_t size, + int flags, + char colorspace[IM_MAX_SPACE]); +bool imb_savewebp(struct ImBuf *ibuf, const char *name, int flags); + +/** \} */ diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 60442f97885..548bc9e120c 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -197,6 +197,20 @@ const ImFileType IMB_FILE_TYPES[] = { .default_save_role = COLOR_ROLE_DEFAULT_FLOAT, }, #endif +#ifdef WITH_WEBP + { + .init = NULL, + .exit = NULL, + .is_a = imb_is_a_webp, + .load = imb_loadwebp, + .load_filepath = NULL, + .save = imb_savewebp, + .load_tile = NULL, + .flag = 0, + .filetype = IMB_FTYPE_WEBP, + .default_save_role = COLOR_ROLE_DEFAULT_BYTE, + }, +#endif {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, }; diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 241f1a736f4..45b50c866fe 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -41,12 +41,12 @@ #define UTIL_DEBUG 0 const char *imb_ext_image[] = { - ".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb", ".rgba", + ".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb", ".rgba", #ifdef WITH_TIFF - ".tif", ".tiff", ".tx", + ".tif", ".tiff", ".tx", #endif #ifdef WITH_OPENJPEG - ".jp2", ".j2c", + ".jp2", ".j2c", #endif #ifdef WITH_HDR ".hdr", @@ -55,13 +55,16 @@ const char *imb_ext_image[] = { ".dds", #endif #ifdef WITH_CINEON - ".dpx", ".cin", + ".dpx", ".cin", #endif #ifdef WITH_OPENEXR ".exr", #endif #ifdef WITH_OPENIMAGEIO - ".psd", ".pdd", ".psb", + ".psd", ".pdd", ".psb", +#endif +#ifdef WITH_WEBP + ".webp", #endif NULL, }; diff --git a/source/blender/imbuf/intern/webp.c b/source/blender/imbuf/intern/webp.c new file mode 100644 index 00000000000..1070920e458 --- /dev/null +++ b/source/blender/imbuf/intern/webp.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file blender/imbuf/intern/webp.c + * \ingroup imbuf + */ + +#include <stdio.h> +#include <stdlib.h> +#include <webp/decode.h> +#include <webp/encode.h> + +#include "BLI_fileops.h" +#include "BLI_utildefines.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 unsigned char *buf, size_t size) +{ + if (WebPGetInfo(buf, size, NULL, NULL)) { + return true; + } + return false; +} + +ImBuf *imb_loadwebp(const unsigned char *mem, + size_t size, + int flags, + char colorspace[IM_MAX_SPACE]) +{ + if (!imb_is_a_webp(mem, size)) { + return NULL; + } + + 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 NULL; + } + + const int planes = features.has_alpha ? 32 : 24; + ImBuf *ibuf = IMB_allocImBuf(features.width, features.height, planes, 0); + + if (ibuf == NULL) { + fprintf(stderr, "WebP: Failed to allocate image memory\n"); + return NULL; + } + + if ((flags & IB_test) == 0) { + ibuf->ftype = IMB_FTYPE_WEBP; + imb_addrectImBuf(ibuf); + /* Flip the image during decoding to match Blender. */ + unsigned char *last_row = (unsigned char *)(ibuf->rect + (ibuf->y - 1) * ibuf->x); + if (WebPDecodeRGBAInto(mem, size, last_row, (size_t)(ibuf->x) * ibuf->y * 4, -4 * ibuf->x) == + NULL) { + fprintf(stderr, "WebP: Failed to decode image\n"); + } + } + + return ibuf; +} + +bool imb_savewebp(struct ImBuf *ibuf, const char *name, int UNUSED(flags)) +{ + const int bytesperpixel = (ibuf->planes + 7) >> 3; + unsigned char *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 = (uint8_t *)ibuf->rect; + uint8_t *rgb_rect = 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 = (unsigned char *)(rgb_rect + (ibuf->y - 1) * ibuf->x * 3); + MEM_freeN(rgb_rect); + + 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); + } + } + else if (bytesperpixel == 4) { + last_row = (unsigned char *)(ibuf->rect + (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, name); + return false; + } + + if (encoded_data != NULL) { + FILE *fp = BLI_fopen(name, "wb"); + if (!fp) { + free(encoded_data); + fprintf(stderr, "WebP: Cannot open file for writing: '%s'\n", name); + return false; + } + fwrite(encoded_data, encoded_data_size, 1, fp); + free(encoded_data); + fclose(fp); + } + + return true; +} |