IMBUF: Faster JPEG Thumbnails
Make preview thumbnails of JPEG files in less time and with less RAM. See D14727 for more details. Differential Revision: https://developer.blender.org/D14727 Reviewed by Brecht Van Lommel
This commit is contained in:
parent
ed0964c976
commit
8960c6e060
@ -107,6 +107,14 @@ struct ImBuf *IMB_testiffname(const char *filepath, int flags);
|
|||||||
*/
|
*/
|
||||||
struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]);
|
struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \attention Defined in readimage.c
|
||||||
|
*/
|
||||||
|
struct ImBuf *IMB_thumb_load_image(const char *filepath,
|
||||||
|
const size_t max_thumb_size,
|
||||||
|
char colorspace[IM_MAX_SPACE]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* \attention Defined in allocimbuf.c
|
* \attention Defined in allocimbuf.c
|
||||||
|
@ -36,6 +36,15 @@ typedef struct ImFileType {
|
|||||||
char colorspace[IM_MAX_SPACE]);
|
char colorspace[IM_MAX_SPACE]);
|
||||||
/** Load an image from a file. */
|
/** Load an image from a file. */
|
||||||
struct ImBuf *(*load_filepath)(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]);
|
struct ImBuf *(*load_filepath)(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]);
|
||||||
|
/** Load/Create a thumbnail image from a filepath. `max_thumb_size` is maximum size of either
|
||||||
|
* dimension, so can return less on either or both. Should, if possible and performant, return
|
||||||
|
* dimensions of the full-size image in width_r & height_r. */
|
||||||
|
struct ImBuf *(*load_filepath_thumbnail)(const char *filepath,
|
||||||
|
const int flags,
|
||||||
|
const size_t max_thumb_size,
|
||||||
|
size_t *width_r,
|
||||||
|
size_t *height_r,
|
||||||
|
char colorspace[IM_MAX_SPACE]);
|
||||||
/** Save to a file (or memory if #IB_mem is set in `flags` and the format supports it). */
|
/** Save to a file (or memory if #IB_mem is set in `flags` and the format supports it). */
|
||||||
bool (*save)(struct ImBuf *ibuf, const char *filepath, int flags);
|
bool (*save)(struct ImBuf *ibuf, const char *filepath, int flags);
|
||||||
void (*load_tile)(struct ImBuf *ibuf,
|
void (*load_tile)(struct ImBuf *ibuf,
|
||||||
@ -143,6 +152,12 @@ struct ImBuf *imb_load_jpeg(const unsigned char *buffer,
|
|||||||
size_t size,
|
size_t size,
|
||||||
int flags,
|
int flags,
|
||||||
char colorspace[IM_MAX_SPACE]);
|
char colorspace[IM_MAX_SPACE]);
|
||||||
|
struct ImBuf *imb_thumbnail_jpeg(const char *filepath,
|
||||||
|
const int flags,
|
||||||
|
const size_t max_thumb_size,
|
||||||
|
size_t *width_r,
|
||||||
|
size_t *height_r,
|
||||||
|
char colorspace[IM_MAX_SPACE]);
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_jpeg,
|
.is_a = imb_is_a_jpeg,
|
||||||
.load = imb_load_jpeg,
|
.load = imb_load_jpeg,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = imb_thumbnail_jpeg,
|
||||||
.save = imb_savejpeg,
|
.save = imb_savejpeg,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -45,6 +46,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_png,
|
.is_a = imb_is_a_png,
|
||||||
.load = imb_loadpng,
|
.load = imb_loadpng,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_savepng,
|
.save = imb_savepng,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -57,6 +59,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_bmp,
|
.is_a = imb_is_a_bmp,
|
||||||
.load = imb_bmp_decode,
|
.load = imb_bmp_decode,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_savebmp,
|
.save = imb_savebmp,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -69,6 +72,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_targa,
|
.is_a = imb_is_a_targa,
|
||||||
.load = imb_loadtarga,
|
.load = imb_loadtarga,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_savetarga,
|
.save = imb_savetarga,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -81,6 +85,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_iris,
|
.is_a = imb_is_a_iris,
|
||||||
.load = imb_loadiris,
|
.load = imb_loadiris,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_saveiris,
|
.save = imb_saveiris,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -94,6 +99,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_dpx,
|
.is_a = imb_is_a_dpx,
|
||||||
.load = imb_load_dpx,
|
.load = imb_load_dpx,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_save_dpx,
|
.save = imb_save_dpx,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = IM_FTYPE_FLOAT,
|
.flag = IM_FTYPE_FLOAT,
|
||||||
@ -106,6 +112,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_cineon,
|
.is_a = imb_is_a_cineon,
|
||||||
.load = imb_load_cineon,
|
.load = imb_load_cineon,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_save_cineon,
|
.save = imb_save_cineon,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = IM_FTYPE_FLOAT,
|
.flag = IM_FTYPE_FLOAT,
|
||||||
@ -120,6 +127,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_tiff,
|
.is_a = imb_is_a_tiff,
|
||||||
.load = imb_loadtiff,
|
.load = imb_loadtiff,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_savetiff,
|
.save = imb_savetiff,
|
||||||
.load_tile = imb_loadtiletiff,
|
.load_tile = imb_loadtiletiff,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -134,6 +142,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_hdr,
|
.is_a = imb_is_a_hdr,
|
||||||
.load = imb_loadhdr,
|
.load = imb_loadhdr,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_savehdr,
|
.save = imb_savehdr,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = IM_FTYPE_FLOAT,
|
.flag = IM_FTYPE_FLOAT,
|
||||||
@ -148,6 +157,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_openexr,
|
.is_a = imb_is_a_openexr,
|
||||||
.load = imb_load_openexr,
|
.load = imb_load_openexr,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_save_openexr,
|
.save = imb_save_openexr,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = IM_FTYPE_FLOAT,
|
.flag = IM_FTYPE_FLOAT,
|
||||||
@ -162,6 +172,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_jp2,
|
.is_a = imb_is_a_jp2,
|
||||||
.load = imb_load_jp2,
|
.load = imb_load_jp2,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_save_jp2,
|
.save = imb_save_jp2,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = IM_FTYPE_FLOAT,
|
.flag = IM_FTYPE_FLOAT,
|
||||||
@ -176,6 +187,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_dds,
|
.is_a = imb_is_a_dds,
|
||||||
.load = imb_load_dds,
|
.load = imb_load_dds,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = NULL,
|
.save = NULL,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -190,6 +202,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_photoshop,
|
.is_a = imb_is_a_photoshop,
|
||||||
.load = NULL,
|
.load = NULL,
|
||||||
.load_filepath = imb_load_photoshop,
|
.load_filepath = imb_load_photoshop,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = NULL,
|
.save = NULL,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = IM_FTYPE_FLOAT,
|
.flag = IM_FTYPE_FLOAT,
|
||||||
@ -204,6 +217,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.is_a = imb_is_a_webp,
|
.is_a = imb_is_a_webp,
|
||||||
.load = imb_loadwebp,
|
.load = imb_loadwebp,
|
||||||
.load_filepath = NULL,
|
.load_filepath = NULL,
|
||||||
|
.load_filepath_thumbnail = NULL,
|
||||||
.save = imb_savewebp,
|
.save = imb_savewebp,
|
||||||
.load_tile = NULL,
|
.load_tile = NULL,
|
||||||
.flag = 0,
|
.flag = 0,
|
||||||
@ -211,7 +225,7 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||||||
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
|
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0},
|
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ImFileType *IMB_FILE_TYPES_LAST = &IMB_FILE_TYPES[ARRAY_SIZE(IMB_FILE_TYPES) - 1];
|
const ImFileType *IMB_FILE_TYPES_LAST = &IMB_FILE_TYPES[ARRAY_SIZE(IMB_FILE_TYPES) - 1];
|
||||||
|
@ -39,7 +39,11 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes);
|
|||||||
static void term_source(j_decompress_ptr cinfo);
|
static void term_source(j_decompress_ptr cinfo);
|
||||||
static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size);
|
static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size);
|
||||||
static boolean handle_app1(j_decompress_ptr cinfo);
|
static boolean handle_app1(j_decompress_ptr cinfo);
|
||||||
static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags);
|
static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo,
|
||||||
|
int flags,
|
||||||
|
int max_size,
|
||||||
|
size_t *width_r,
|
||||||
|
size_t *height_r);
|
||||||
|
|
||||||
static const uchar jpeg_default_quality = 75;
|
static const uchar jpeg_default_quality = 75;
|
||||||
static uchar ibuf_quality;
|
static uchar ibuf_quality;
|
||||||
@ -246,7 +250,11 @@ static boolean handle_app1(j_decompress_ptr cinfo)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags)
|
static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo,
|
||||||
|
int flags,
|
||||||
|
int max_size,
|
||||||
|
size_t *width_r,
|
||||||
|
size_t *height_r)
|
||||||
{
|
{
|
||||||
JSAMPARRAY row_pointer;
|
JSAMPARRAY row_pointer;
|
||||||
JSAMPLE *buffer = NULL;
|
JSAMPLE *buffer = NULL;
|
||||||
@ -264,16 +272,34 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla
|
|||||||
jpeg_save_markers(cinfo, JPEG_COM, 0xffff);
|
jpeg_save_markers(cinfo, JPEG_COM, 0xffff);
|
||||||
|
|
||||||
if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) {
|
if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) {
|
||||||
x = cinfo->image_width;
|
|
||||||
y = cinfo->image_height;
|
|
||||||
depth = cinfo->num_components;
|
depth = cinfo->num_components;
|
||||||
|
|
||||||
if (cinfo->jpeg_color_space == JCS_YCCK) {
|
if (cinfo->jpeg_color_space == JCS_YCCK) {
|
||||||
cinfo->out_color_space = JCS_CMYK;
|
cinfo->out_color_space = JCS_CMYK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (width_r) {
|
||||||
|
*width_r = cinfo->image_width;
|
||||||
|
}
|
||||||
|
if (height_r) {
|
||||||
|
*height_r = cinfo->image_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_size > 0) {
|
||||||
|
/* libjpeg can more quickly decompress while scaling down to 1/2, 1/4, 1/8,
|
||||||
|
* while libjpeg-turbo can also do 3/8, 5/8, etc. But max is 1/8. */
|
||||||
|
float scale = (float)max_size / MAX2(cinfo->image_width, cinfo->image_height);
|
||||||
|
cinfo->scale_denom = 8;
|
||||||
|
cinfo->scale_num = MAX2(1, MIN2(8, ceill(scale * (float)cinfo->scale_denom)));
|
||||||
|
cinfo->dct_method = JDCT_FASTEST;
|
||||||
|
cinfo->dither_mode = JDITHER_ORDERED;
|
||||||
|
}
|
||||||
|
|
||||||
jpeg_start_decompress(cinfo);
|
jpeg_start_decompress(cinfo);
|
||||||
|
|
||||||
|
x = cinfo->output_width;
|
||||||
|
y = cinfo->output_height;
|
||||||
|
|
||||||
if (flags & IB_test) {
|
if (flags & IB_test) {
|
||||||
jpeg_abort_decompress(cinfo);
|
jpeg_abort_decompress(cinfo);
|
||||||
ibuf = IMB_allocImBuf(x, y, 8 * depth, 0);
|
ibuf = IMB_allocImBuf(x, y, 8 * depth, 0);
|
||||||
@ -449,11 +475,92 @@ ImBuf *imb_load_jpeg(const unsigned char *buffer,
|
|||||||
jpeg_create_decompress(cinfo);
|
jpeg_create_decompress(cinfo);
|
||||||
memory_source(cinfo, buffer, size);
|
memory_source(cinfo, buffer, size);
|
||||||
|
|
||||||
ibuf = ibJpegImageFromCinfo(cinfo, flags);
|
ibuf = ibJpegImageFromCinfo(cinfo, flags, -1, NULL, NULL);
|
||||||
|
|
||||||
return ibuf;
|
return ibuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Defines for JPEG Header markers and segment size. */
|
||||||
|
#define JPEG_MARKER_MSB (0xFF)
|
||||||
|
#define JPEG_MARKER_SOI (0xD8)
|
||||||
|
#define JPEG_MARKER_APP1 (0xE1)
|
||||||
|
#define JPEG_APP1_MAX (1 << 16)
|
||||||
|
|
||||||
|
struct ImBuf *imb_thumbnail_jpeg(const char *filepath,
|
||||||
|
const int flags,
|
||||||
|
const size_t max_thumb_size,
|
||||||
|
size_t *width_r,
|
||||||
|
size_t *height_r,
|
||||||
|
char colorspace[IM_MAX_SPACE])
|
||||||
|
{
|
||||||
|
struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo;
|
||||||
|
struct my_error_mgr jerr;
|
||||||
|
FILE *infile = NULL;
|
||||||
|
|
||||||
|
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
|
||||||
|
|
||||||
|
cinfo->err = jpeg_std_error(&jerr.pub);
|
||||||
|
jerr.pub.error_exit = jpeg_error;
|
||||||
|
|
||||||
|
/* Establish the setjmp return context for my_error_exit to use. */
|
||||||
|
if (setjmp(jerr.setjmp_buffer)) {
|
||||||
|
/* If we get here, the JPEG code has signaled an error.
|
||||||
|
* We need to clean up the JPEG object, close the input file, and return.
|
||||||
|
*/
|
||||||
|
jpeg_destroy_decompress(cinfo);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((infile = BLI_fopen(filepath, "rb")) == NULL) {
|
||||||
|
fprintf(stderr, "can't open %s\n", filepath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If file contains an embedded thumbnail, let's return that instead. */
|
||||||
|
|
||||||
|
if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) &&
|
||||||
|
(fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) {
|
||||||
|
/* This is a JPEG in Exif format (SOI + APP1), not JFIF (SOI + APP0). */
|
||||||
|
unsigned int i = JPEG_APP1_MAX;
|
||||||
|
/* All Exif data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */
|
||||||
|
while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) &&
|
||||||
|
!feof(infile) && i--)
|
||||||
|
;
|
||||||
|
if (i > 0 && !feof(infile)) {
|
||||||
|
/* We found a JPEG thumbnail inside this image. */
|
||||||
|
ImBuf *ibuf = NULL;
|
||||||
|
unsigned char *buffer = (char *)MEM_callocN(JPEG_APP1_MAX, "thumbbuffer");
|
||||||
|
/* Just put SOI directly in buffer rather than seeking back 2 bytes. */
|
||||||
|
buffer[0] = JPEG_MARKER_MSB;
|
||||||
|
buffer[1] = JPEG_MARKER_SOI;
|
||||||
|
if (fread(buffer + 2, JPEG_APP1_MAX - 2, 1, infile) == 1) {
|
||||||
|
ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, colorspace);
|
||||||
|
}
|
||||||
|
MEM_SAFE_FREE(buffer);
|
||||||
|
if (ibuf) {
|
||||||
|
fclose(infile);
|
||||||
|
return ibuf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No embedded thumbnail found, so let's create a new one. */
|
||||||
|
|
||||||
|
fseek(infile, 0, SEEK_SET);
|
||||||
|
jpeg_create_decompress(cinfo);
|
||||||
|
|
||||||
|
jpeg_stdio_src(cinfo, infile);
|
||||||
|
ImBuf *ibuf = ibJpegImageFromCinfo(cinfo, flags, max_thumb_size, width_r, height_r);
|
||||||
|
fclose(infile);
|
||||||
|
|
||||||
|
return ibuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef JPEG_MARKER_MSB
|
||||||
|
#undef JPEG_MARKER_SOI
|
||||||
|
#undef JPEG_MARKER_APP1
|
||||||
|
#undef JPEG_APP1_MAX
|
||||||
|
|
||||||
static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf)
|
static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf)
|
||||||
{
|
{
|
||||||
JSAMPLE *buffer = NULL;
|
JSAMPLE *buffer = NULL;
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include "IMB_filetype.h"
|
#include "IMB_filetype.h"
|
||||||
#include "IMB_imbuf.h"
|
#include "IMB_imbuf.h"
|
||||||
#include "IMB_imbuf_types.h"
|
#include "IMB_imbuf_types.h"
|
||||||
|
#include "IMB_metadata.h"
|
||||||
|
#include "IMB_thumbs.h"
|
||||||
#include "imbuf.h"
|
#include "imbuf.h"
|
||||||
|
|
||||||
#include "IMB_colormanagement.h"
|
#include "IMB_colormanagement.h"
|
||||||
@ -234,6 +236,61 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S
|
|||||||
return ibuf;
|
return ibuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ImBuf *IMB_thumb_load_image(const char *filepath,
|
||||||
|
size_t max_thumb_size,
|
||||||
|
char colorspace[IM_MAX_SPACE])
|
||||||
|
{
|
||||||
|
const ImFileType *type = IMB_file_type_from_ftype(IMB_ispic_type(filepath));
|
||||||
|
if (type == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImBuf *ibuf = NULL;
|
||||||
|
int flags = IB_rect | IB_metadata;
|
||||||
|
/* Size of the original image. */
|
||||||
|
size_t width = 0;
|
||||||
|
size_t height = 0;
|
||||||
|
|
||||||
|
char effective_colorspace[IM_MAX_SPACE] = "";
|
||||||
|
if (colorspace) {
|
||||||
|
BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->load_filepath_thumbnail) {
|
||||||
|
ibuf = type->load_filepath_thumbnail(
|
||||||
|
filepath, flags, max_thumb_size, &width, &height, colorspace);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Skip images of other types if over 100MB. */
|
||||||
|
const size_t file_size = BLI_file_size(filepath);
|
||||||
|
if (file_size != -1 && file_size > THUMB_SIZE_MAX) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ibuf = IMB_loadiffname(filepath, flags, colorspace);
|
||||||
|
if (ibuf) {
|
||||||
|
width = ibuf->x;
|
||||||
|
height = ibuf->y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ibuf) {
|
||||||
|
imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace);
|
||||||
|
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
/* Save dimensions of original image into the thumbnail metadata. */
|
||||||
|
char cwidth[40] = "0";
|
||||||
|
char cheight[40] = "0";
|
||||||
|
BLI_snprintf(cwidth, sizeof(cwidth), "%d", width);
|
||||||
|
BLI_snprintf(cheight, sizeof(cheight), "%d", height);
|
||||||
|
IMB_metadata_ensure(&ibuf->metadata);
|
||||||
|
IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Width", cwidth);
|
||||||
|
IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Height", cheight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ibuf;
|
||||||
|
}
|
||||||
|
|
||||||
ImBuf *IMB_testiffname(const char *filepath, int flags)
|
ImBuf *IMB_testiffname(const char *filepath, int flags)
|
||||||
{
|
{
|
||||||
ImBuf *ibuf;
|
ImBuf *ibuf;
|
||||||
|
@ -319,11 +319,7 @@ static ImBuf *thumb_create_ex(const char *file_path,
|
|||||||
char tdir[FILE_MAX];
|
char tdir[FILE_MAX];
|
||||||
char temp[FILE_MAX];
|
char temp[FILE_MAX];
|
||||||
char mtime[40] = "0"; /* in case we can't stat the file */
|
char mtime[40] = "0"; /* in case we can't stat the file */
|
||||||
char cwidth[40] = "0"; /* in case images have no data */
|
|
||||||
char cheight[40] = "0";
|
|
||||||
short tsize = 128;
|
short tsize = 128;
|
||||||
short ex, ey;
|
|
||||||
float scaledx, scaledy;
|
|
||||||
BLI_stat_t info;
|
BLI_stat_t info;
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
@ -340,15 +336,6 @@ static ImBuf *thumb_create_ex(const char *file_path,
|
|||||||
return NULL; /* unknown size */
|
return NULL; /* unknown size */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* exception, skip images over 100mb */
|
|
||||||
if (source == THB_SOURCE_IMAGE) {
|
|
||||||
const size_t file_size = BLI_file_size(file_path);
|
|
||||||
if (file_size != -1 && file_size > THUMB_SIZE_MAX) {
|
|
||||||
// printf("file too big: %d, skipping %s\n", (int)size, file_path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_thumb_dir(tdir, size)) {
|
if (get_thumb_dir(tdir, size)) {
|
||||||
BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb);
|
BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb);
|
||||||
// thumb[8] = '\0'; /* shorten for tempname, not needed anymore */
|
// thumb[8] = '\0'; /* shorten for tempname, not needed anymore */
|
||||||
@ -368,7 +355,7 @@ static ImBuf *thumb_create_ex(const char *file_path,
|
|||||||
if (img == NULL) {
|
if (img == NULL) {
|
||||||
switch (source) {
|
switch (source) {
|
||||||
case THB_SOURCE_IMAGE:
|
case THB_SOURCE_IMAGE:
|
||||||
img = IMB_loadiffname(file_path, IB_rect | IB_metadata, NULL);
|
img = IMB_thumb_load_image(file_path, tsize, NULL);
|
||||||
break;
|
break;
|
||||||
case THB_SOURCE_BLEND:
|
case THB_SOURCE_BLEND:
|
||||||
img = IMB_thumb_load_blend(file_path, blen_group, blen_id);
|
img = IMB_thumb_load_blend(file_path, blen_group, blen_id);
|
||||||
@ -385,8 +372,6 @@ static ImBuf *thumb_create_ex(const char *file_path,
|
|||||||
if (BLI_stat(file_path, &info) != -1) {
|
if (BLI_stat(file_path, &info) != -1) {
|
||||||
BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime);
|
BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime);
|
||||||
}
|
}
|
||||||
BLI_snprintf(cwidth, sizeof(cwidth), "%d", img->x);
|
|
||||||
BLI_snprintf(cheight, sizeof(cheight), "%d", img->y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (THB_SOURCE_MOVIE == source) {
|
else if (THB_SOURCE_MOVIE == source) {
|
||||||
@ -411,28 +396,20 @@ static ImBuf *thumb_create_ex(const char *file_path,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (img->x > img->y) {
|
if (img->x > tsize || img->y > tsize) {
|
||||||
scaledx = (float)tsize;
|
float scale = MIN2((float)tsize / (float)img->x, (float)tsize / (float)img->y);
|
||||||
scaledy = ((float)img->y / (float)img->x) * tsize;
|
/* Scaling down must never assign zero width/height, see: T89868. */
|
||||||
}
|
short ex = MAX2(1, (short)(img->x * scale));
|
||||||
else {
|
short ey = MAX2(1, (short)(img->y * scale));
|
||||||
scaledy = (float)tsize;
|
/* Save some time by only scaling byte buf */
|
||||||
scaledx = ((float)img->x / (float)img->y) * tsize;
|
if (img->rect_float) {
|
||||||
}
|
if (img->rect == NULL) {
|
||||||
/* Scaling down must never assign zero width/height, see: T89868. */
|
IMB_rect_from_float(img);
|
||||||
ex = MAX2(1, (short)scaledx);
|
}
|
||||||
ey = MAX2(1, (short)scaledy);
|
imb_freerectfloatImBuf(img);
|
||||||
|
|
||||||
/* save some time by only scaling byte buf */
|
|
||||||
if (img->rect_float) {
|
|
||||||
if (img->rect == NULL) {
|
|
||||||
IMB_rect_from_float(img);
|
|
||||||
}
|
}
|
||||||
|
IMB_scaleImBuf(img, ex, ey);
|
||||||
imb_freerectfloatImBuf(img);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IMB_scaleImBuf(img, ex, ey);
|
|
||||||
}
|
}
|
||||||
BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri);
|
BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri);
|
||||||
IMB_metadata_ensure(&img->metadata);
|
IMB_metadata_ensure(&img->metadata);
|
||||||
@ -443,10 +420,6 @@ static ImBuf *thumb_create_ex(const char *file_path,
|
|||||||
if (use_hash) {
|
if (use_hash) {
|
||||||
IMB_metadata_set_field(img->metadata, "X-Blender::Hash", hash);
|
IMB_metadata_set_field(img->metadata, "X-Blender::Hash", hash);
|
||||||
}
|
}
|
||||||
if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) {
|
|
||||||
IMB_metadata_set_field(img->metadata, "Thumb::Image::Width", cwidth);
|
|
||||||
IMB_metadata_set_field(img->metadata, "Thumb::Image::Height", cheight);
|
|
||||||
}
|
|
||||||
img->ftype = IMB_FTYPE_PNG;
|
img->ftype = IMB_FTYPE_PNG;
|
||||||
img->planes = 32;
|
img->planes = 32;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user