Blender V5.0
format_jpeg.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* This little block needed for linking to Blender... */
10#include <algorithm>
11#include <csetjmp>
12#include <cstdio>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_fileops.h"
17#include "BLI_listbase.h"
18#include "BLI_string.h"
19#include "BLI_string_utf8.h"
20#include "BLI_utildefines.h"
21
22#include "BKE_idprop.hh"
23
24#include "DNA_ID.h" /* ID property definitions. */
25
27#include "IMB_filetype.hh"
28#include "IMB_imbuf.hh"
29#include "IMB_imbuf_types.hh"
30#include "IMB_metadata.hh"
31
32#include "CLG_log.h"
33
34#include <cstring>
35#include <jerror.h>
36#include <jpeglib.h>
37
38static CLG_LogRef LOG = {"image.jpeg"};
39
40/* the types are from the jpeg lib */
41static void jpeg_error(j_common_ptr cinfo) ATTR_NORETURN;
42static void init_source(j_decompress_ptr cinfo);
43static boolean fill_input_buffer(j_decompress_ptr cinfo);
44static void skip_input_data(j_decompress_ptr cinfo, long num_bytes);
45static void term_source(j_decompress_ptr cinfo);
46static void memory_source(j_decompress_ptr cinfo, const uchar *buffer, size_t size);
47static boolean handle_app1(j_decompress_ptr cinfo);
49 jpeg_decompress_struct *cinfo, int flags, int max_size, size_t *r_width, size_t *r_height);
50
51static const uchar jpeg_default_quality = 75;
53
54bool imb_is_a_jpeg(const uchar *mem, const size_t size)
55{
56 const char magic[2] = {0xFF, 0xD8};
57 if (size < sizeof(magic)) {
58 return false;
59 }
60 return memcmp(mem, magic, sizeof(magic)) == 0;
61}
62
63/*----------------------------------------------------------
64 * JPG ERROR HANDLING
65 *---------------------------------------------------------- */
66
68 jpeg_error_mgr pub; /* "public" fields */
69
70 jmp_buf setjmp_buffer; /* for return to caller */
71};
72
74
75static void jpeg_error(j_common_ptr cinfo)
76{
77 my_error_ptr err = (my_error_ptr)cinfo->err;
78
79 /* Always display the message */
80 (*cinfo->err->output_message)(cinfo);
81
82 /* Let the memory manager delete any temp files before we die */
83 jpeg_destroy(cinfo);
84
85 /* return control to the setjmp point */
86 longjmp(err->setjmp_buffer, 1);
87}
88
89/*----------------------------------------------------------
90 * INPUT HANDLER FROM MEMORY
91 *---------------------------------------------------------- */
92
94 jpeg_source_mgr pub; /* public fields */
95
96 const uchar *buffer;
97 int size;
98 JOCTET terminal[2];
99};
100
102
103static void init_source(j_decompress_ptr cinfo)
104{
105 (void)cinfo; /* unused */
106}
107
108static boolean fill_input_buffer(j_decompress_ptr cinfo)
109{
110 my_src_ptr src = (my_src_ptr)cinfo->src;
111
112 /* Since we have given all we have got already
113 * we simply fake an end of file
114 */
115
116 src->pub.next_input_byte = src->terminal;
117 src->pub.bytes_in_buffer = 2;
118 src->terminal[0] = (JOCTET)0xFF;
119 src->terminal[1] = (JOCTET)JPEG_EOI;
120
121 return true;
122}
123
124static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
125{
126 my_src_ptr src = (my_src_ptr)cinfo->src;
127
128 if (num_bytes > 0) {
129 /* prevent skipping over file end */
130 size_t skip_size = size_t(num_bytes) <= src->pub.bytes_in_buffer ? num_bytes :
131 src->pub.bytes_in_buffer;
132
133 src->pub.next_input_byte = src->pub.next_input_byte + skip_size;
134 src->pub.bytes_in_buffer = src->pub.bytes_in_buffer - skip_size;
135 }
136}
137
138static void term_source(j_decompress_ptr cinfo)
139{
140 (void)cinfo; /* unused */
141}
142
143static void memory_source(j_decompress_ptr cinfo, const uchar *buffer, size_t size)
144{
145 my_src_ptr src;
146
147 if (cinfo->src == nullptr) { /* first time for this JPEG object? */
148 cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)(
149 (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr));
150 }
151
152 src = (my_src_ptr)cinfo->src;
153 src->pub.init_source = init_source;
154 src->pub.fill_input_buffer = fill_input_buffer;
155 src->pub.skip_input_data = skip_input_data;
156 src->pub.resync_to_restart = jpeg_resync_to_restart;
157 src->pub.term_source = term_source;
158
159 src->pub.bytes_in_buffer = size;
160 src->pub.next_input_byte = buffer;
161
162 src->buffer = buffer;
163 src->size = size;
164}
165
166#define MAKESTMT(stuff) \
167 do { \
168 stuff \
169 } while (0)
170
171#define INPUT_VARS(cinfo) \
172 jpeg_source_mgr *datasrc = (cinfo)->src; \
173 const JOCTET *next_input_byte = datasrc->next_input_byte; \
174 size_t bytes_in_buffer = datasrc->bytes_in_buffer
175
176/* Unload the local copies --- do this only at a restart boundary */
177#define INPUT_SYNC(cinfo) \
178 (datasrc->next_input_byte = next_input_byte, datasrc->bytes_in_buffer = bytes_in_buffer)
179
180/* Reload the local copies --- seldom used except in MAKE_BYTE_AVAIL */
181#define INPUT_RELOAD(cinfo) \
182 (next_input_byte = datasrc->next_input_byte, bytes_in_buffer = datasrc->bytes_in_buffer)
183
184/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available.
185 * Note we do *not* do INPUT_SYNC before calling fill_input_buffer,
186 * but we must reload the local copies after a successful fill.
187 */
188#define MAKE_BYTE_AVAIL(cinfo, action) \
189 if (bytes_in_buffer == 0) { \
190 if (!(*datasrc->fill_input_buffer)(cinfo)) { \
191 action; \
192 } \
193 INPUT_RELOAD(cinfo); \
194 } \
195 (void)0
196
197/* Read a byte into variable V.
198 * If must suspend, take the specified action (typically "return false").
199 */
200#define INPUT_BYTE(cinfo, V, action) \
201 MAKESTMT(MAKE_BYTE_AVAIL(cinfo, action); bytes_in_buffer--; V = GETJOCTET(*next_input_byte++);)
202
203/* As above, but read two bytes interpreted as an unsigned 16-bit integer.
204 * V should be declared `uint` or perhaps INT32.
205 */
206#define INPUT_2BYTES(cinfo, V, action) \
207 MAKESTMT(MAKE_BYTE_AVAIL(cinfo, action); bytes_in_buffer--; \
208 V = uint(GETJOCTET(*next_input_byte++)) << 8; \
209 MAKE_BYTE_AVAIL(cinfo, action); \
210 bytes_in_buffer--; \
211 V += GETJOCTET(*next_input_byte++);)
212
219BLI_STATIC_ASSERT(sizeof(NeoGeo_Word) == 4, "Must be 4 bytes");
220
221static boolean handle_app1(j_decompress_ptr cinfo)
222{
223 INT32 length; /* initialized by the macro */
224 INT32 i;
225 char neogeo[128];
226
227 INPUT_VARS(cinfo);
228
229 INPUT_2BYTES(cinfo, length, return false);
230 length -= 2;
231
232 if (length < 16) {
233 for (i = 0; i < length; i++) {
234 INPUT_BYTE(cinfo, neogeo[i], return false);
235 }
236 length = 0;
237 if (STRPREFIX(neogeo, "NeoGeo")) {
238 NeoGeo_Word *neogeo_word = (NeoGeo_Word *)(neogeo + 6);
239 ibuf_quality = neogeo_word->quality;
240 }
241 }
242 INPUT_SYNC(cinfo); /* do before skip_input_data */
243 if (length > 0) {
244 (*cinfo->src->skip_input_data)(cinfo, length);
245 }
246 return true;
247}
248
250 jpeg_decompress_struct *cinfo, int flags, int max_size, size_t *r_width, size_t *r_height)
251{
252 JSAMPARRAY row_pointer;
253 JSAMPLE *buffer = nullptr;
254 int row_stride;
255 int x, y, depth, r, g, b, k;
256 ImBuf *ibuf = nullptr;
257 uchar *rect;
258 jpeg_saved_marker_ptr marker;
259 char *str, *key, *value;
260
261 /* install own app1 handler */
263 jpeg_set_marker_processor(cinfo, 0xe1, handle_app1);
264 cinfo->dct_method = JDCT_FLOAT;
265 jpeg_save_markers(cinfo, JPEG_COM, 0xffff);
266
267 if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) {
268 depth = cinfo->num_components;
269
270 if (cinfo->jpeg_color_space == JCS_YCCK) {
271 cinfo->out_color_space = JCS_CMYK;
272 }
273
274 if (r_width) {
275 *r_width = cinfo->image_width;
276 }
277 if (r_height) {
278 *r_height = cinfo->image_height;
279 }
280
281 if (max_size > 0) {
282 /* `libjpeg` can more quickly decompress while scaling down to 1/2, 1/4, 1/8,
283 * while `libjpeg-turbo` can also do 3/8, 5/8, etc. But max is 1/8. */
284 float scale = float(max_size) / std::max(cinfo->image_width, cinfo->image_height);
285 cinfo->scale_denom = 8;
286 cinfo->scale_num = max_uu(1, min_uu(8, ceill(scale * float(cinfo->scale_denom))));
287 cinfo->dct_method = JDCT_FASTEST;
288 cinfo->dither_mode = JDITHER_ORDERED;
289 }
290
291 jpeg_start_decompress(cinfo);
292
293 x = cinfo->output_width;
294 y = cinfo->output_height;
295
296 if (flags & IB_test) {
297 jpeg_abort_decompress(cinfo);
298 ibuf = IMB_allocImBuf(x, y, 8 * depth, 0);
299 }
300 else if ((ibuf = IMB_allocImBuf(x, y, 8 * depth, IB_byte_data | IB_uninitialized_pixels)) ==
301 nullptr)
302 {
303 jpeg_abort_decompress(cinfo);
304 }
305 else {
306 row_stride = cinfo->output_width * depth;
307
308 row_pointer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1);
309
310 for (y = ibuf->y - 1; y >= 0; y--) {
311 jpeg_read_scanlines(cinfo, row_pointer, 1);
312 rect = ibuf->byte_buffer.data + 4 * y * size_t(ibuf->x);
313 buffer = row_pointer[0];
314
315 switch (depth) {
316 case 1:
317 for (x = ibuf->x; x > 0; x--) {
318 rect[3] = 255;
319 rect[0] = rect[1] = rect[2] = *buffer++;
320 rect += 4;
321 }
322 break;
323 case 3:
324 for (x = ibuf->x; x > 0; x--) {
325 rect[3] = 255;
326 rect[0] = *buffer++;
327 rect[1] = *buffer++;
328 rect[2] = *buffer++;
329 rect += 4;
330 }
331 break;
332 case 4:
333 for (x = ibuf->x; x > 0; x--) {
334 r = *buffer++;
335 g = *buffer++;
336 b = *buffer++;
337 k = *buffer++;
338
339 r = (r * k) / 255;
340 g = (g * k) / 255;
341 b = (b * k) / 255;
342
343 rect[3] = 255;
344 rect[2] = b;
345 rect[1] = g;
346 rect[0] = r;
347 rect += 4;
348 }
349 break;
350 }
351 }
352
353 marker = cinfo->marker_list;
354 while (marker) {
355 if (marker->marker != JPEG_COM) {
356 goto next_stamp_marker;
357 }
358
359 /*
360 * JPEG marker strings are not meant to be null-terminated,
361 * create a null-terminated copy before going further.
362 *
363 * Files saved from Blender pre v4.0 were null terminated,
364 * use `BLI_strnlen` to prevent assertion on passing in too short a string. */
365 str = BLI_strdupn((const char *)marker->data,
366 BLI_strnlen((const char *)marker->data, marker->data_length));
367
368 /*
369 * Because JPEG format don't support the
370 * pair "key/value" like PNG, we store the
371 * stamp-info in a single "encode" string:
372 * "Blender:key:value"
373 *
374 * That is why we need split it to the
375 * common key/value here.
376 */
377 if (!STRPREFIX(str, "Blender")) {
378 /*
379 * Maybe the file have text that
380 * we don't know "what it's", in that
381 * case we keep the text (with a
382 * key "None").
383 * This is only for don't "lose"
384 * the information when we write
385 * it back to disk.
386 */
388 IMB_metadata_set_field(ibuf->metadata, "None", str);
389 ibuf->flags |= IB_metadata;
390 MEM_freeN(str);
391 goto next_stamp_marker;
392 }
393
394 key = strchr(str, ':');
395 /*
396 * A little paranoid, but the file maybe
397 * is broken... and a "extra" check is better
398 * then segfault ;)
399 */
400 if (!key) {
401 MEM_freeN(str);
402 goto next_stamp_marker;
403 }
404
405 key++;
406 value = strchr(key, ':');
407 if (!value) {
408 MEM_freeN(str);
409 goto next_stamp_marker;
410 }
411
412 *value = '\0'; /* need finish the key string */
413 value++;
415 IMB_metadata_set_field(ibuf->metadata, key, value);
416 ibuf->flags |= IB_metadata;
417 MEM_freeN(str);
418 next_stamp_marker:
419 marker = marker->next;
420 }
421
422 jpeg_finish_decompress(cinfo);
423 }
424
425 if (ibuf) {
426 /* Density_unit may be 0 for unknown, 1 for dots/inch, or 2 for dots/cm. */
427 if (cinfo->density_unit == 1) {
428 /* Convert inches to meters. */
429 ibuf->ppm[0] = double(cinfo->X_density) / 0.0254;
430 ibuf->ppm[1] = double(cinfo->Y_density) / 0.0254;
431 }
432 else if (cinfo->density_unit == 2) {
433 ibuf->ppm[0] = double(cinfo->X_density) * 100.0;
434 ibuf->ppm[1] = double(cinfo->Y_density) * 100.0;
435 }
436
437 ibuf->ftype = IMB_FTYPE_JPG;
438 ibuf->foptions.quality = std::min<char>(ibuf_quality, 100);
439 }
440 jpeg_destroy((j_common_ptr)cinfo);
441 }
442
443 return ibuf;
444}
445
446ImBuf *imb_load_jpeg(const uchar *buffer,
447 size_t size,
448 int flags,
449 ImFileColorSpace & /*r_colorspace*/)
450{
451 jpeg_decompress_struct _cinfo, *cinfo = &_cinfo;
452 my_error_mgr jerr;
453 ImBuf *ibuf;
454
455 if (!imb_is_a_jpeg(buffer, size)) {
456 return nullptr;
457 }
458
459 cinfo->err = jpeg_std_error(&jerr.pub);
460 jerr.pub.error_exit = jpeg_error;
461
462 /* Establish the setjmp return context for my_error_exit to use. */
463 if (setjmp(jerr.setjmp_buffer)) {
464 /* If we get here, the JPEG code has signaled an error.
465 * We need to clean up the JPEG object, close the input file, and return.
466 */
467 jpeg_destroy_decompress(cinfo);
468 return nullptr;
469 }
470
471 jpeg_create_decompress(cinfo);
472 memory_source(cinfo, buffer, size);
473
474 ibuf = ibJpegImageFromCinfo(cinfo, flags, -1, nullptr, nullptr);
475
476 return ibuf;
477}
478
479/* Defines for JPEG Header markers and segment size. */
480#define JPEG_MARKER_MSB (0xFF)
481#define JPEG_MARKER_SOI (0xD8)
482#define JPEG_MARKER_APP1 (0xE1)
483#define JPEG_APP1_MAX (1 << 16)
484
485ImBuf *imb_thumbnail_jpeg(const char *filepath,
486 const int flags,
487 const size_t max_thumb_size,
488 ImFileColorSpace &r_colorspace,
489 size_t *r_width,
490 size_t *r_height)
491{
492 jpeg_decompress_struct _cinfo, *cinfo = &_cinfo;
493 my_error_mgr jerr;
494 FILE *infile = nullptr;
495
496 cinfo->err = jpeg_std_error(&jerr.pub);
497 jerr.pub.error_exit = jpeg_error;
498
499 /* Establish the setjmp return context for my_error_exit to use. */
500 if (setjmp(jerr.setjmp_buffer)) {
501 /* If we get here, the JPEG code has signaled an error.
502 * We need to clean up the JPEG object, close the input file, and return.
503 */
504 jpeg_destroy_decompress(cinfo);
505 return nullptr;
506 }
507
508 if ((infile = BLI_fopen(filepath, "rb")) == nullptr) {
509 CLOG_ERROR(&LOG, "Cannot open \"%s\"", filepath);
510 return nullptr;
511 }
512
513 /* If file contains an embedded thumbnail, let's return that instead. */
514
515 if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) &&
516 (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1))
517 {
518 /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */
520 /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */
521 while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) &&
522 !feof(infile) && i--)
523 {
524 }
525 if (i > 0 && !feof(infile)) {
526 /* We found a JPEG thumbnail inside this image. */
527 ImBuf *ibuf = nullptr;
528 uchar *buffer = MEM_calloc_arrayN<uchar>(JPEG_APP1_MAX, "thumbbuffer");
529 /* Just put SOI directly in buffer rather than seeking back 2 bytes. */
530 buffer[0] = JPEG_MARKER_MSB;
531 buffer[1] = JPEG_MARKER_SOI;
532 if (fread(buffer + 2, JPEG_APP1_MAX - 2, 1, infile) == 1) {
533 ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, r_colorspace);
534 }
535 MEM_SAFE_FREE(buffer);
536 if (ibuf) {
537 fclose(infile);
538 return ibuf;
539 }
540 }
541 }
542
543 /* No embedded thumbnail found, so let's create a new one. */
544
545 fseek(infile, 0, SEEK_SET);
546 jpeg_create_decompress(cinfo);
547
548 jpeg_stdio_src(cinfo, infile);
549 ImBuf *ibuf = ibJpegImageFromCinfo(cinfo, flags, max_thumb_size, r_width, r_height);
550 fclose(infile);
551
552 return ibuf;
553}
554
555#undef JPEG_MARKER_MSB
556#undef JPEG_MARKER_SOI
557#undef JPEG_MARKER_APP1
558#undef JPEG_APP1_MAX
559
560static void write_jpeg(jpeg_compress_struct *cinfo, ImBuf *ibuf)
561{
562 JSAMPLE *buffer = nullptr;
563 JSAMPROW row_pointer[1];
564 uchar *rect;
565 int x, y;
566 char neogeo[128];
567 NeoGeo_Word *neogeo_word;
568
569 jpeg_start_compress(cinfo, true);
570
571 STRNCPY_UTF8(neogeo, "NeoGeo");
572 neogeo_word = (NeoGeo_Word *)(neogeo + 6);
573 memset(neogeo_word, 0, sizeof(*neogeo_word));
574 neogeo_word->quality = ibuf->foptions.quality;
575 jpeg_write_marker(cinfo, 0xe1, (JOCTET *)neogeo, 10);
576 if (ibuf->metadata) {
577
578 /* Static storage array for the short metadata. */
579 char static_text[1024];
580 const size_t static_text_size = ARRAY_SIZE(static_text);
581 LISTBASE_FOREACH (IDProperty *, prop, &ibuf->metadata->data.group) {
582 if (prop->type == IDP_STRING) {
583 size_t text_len;
584 if (STREQ(prop->name, "None")) {
585 jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)IDP_string_get(prop), prop->len);
586 }
587
588 char *text = static_text;
589 size_t text_size = static_text_size;
590 /* 7 is for Blender, 2 colon separators, length of property
591 * name and property value, followed by the nullptr-terminator
592 * which isn't needed by JPEG but #BLI_snprintf_rlen requires it. */
593 const size_t text_length_required = 7 + 2 + strlen(prop->name) +
594 strlen(IDP_string_get(prop)) + 1;
595 if (text_length_required > static_text_size) {
596 text = MEM_malloc_arrayN<char>(text_length_required, "jpeg metadata field");
597 text_size = text_length_required;
598 }
599
600 /*
601 * The JPEG format don't support a pair "key/value"
602 * like PNG, so we "encode" the stamp in a
603 * single string:
604 * "Blender:key:value"
605 *
606 * The first "Blender" is a simple identify to help
607 * in the read process.
608 */
609 text_len = BLI_snprintf_utf8_rlen(
610 text, text_size, "Blender:%s:%s", prop->name, IDP_string_get(prop));
611 /* Don't write the null byte (not expected by the JPEG format). */
612 jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)text, uint(text_len));
613
614 /* TODO(sergey): Ideally we will try to re-use allocation as
615 * much as possible. In practice, such long fields don't happen
616 * often. */
617 if (text != static_text) {
618 MEM_freeN(text);
619 }
620 }
621 }
622 }
623
624 /* Write ICC profile if there is one associated with the colorspace. */
625 const ColorSpace *colorspace = ibuf->byte_buffer.colorspace;
626 if (colorspace) {
628 if (!icc_profile.is_empty()) {
629 icc_profile.prepend({'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', 0, 0, 1});
630 jpeg_write_marker(cinfo,
631 JPEG_APP0 + 2,
632 reinterpret_cast<const JOCTET *>(icc_profile.data()),
633 icc_profile.size());
634 }
635 }
636
638 size_t(cinfo->input_components) * size_t(cinfo->image_width), "jpeg row_pointer");
639
640 for (y = ibuf->y - 1; y >= 0; y--) {
641 rect = ibuf->byte_buffer.data + 4 * y * size_t(ibuf->x);
642 buffer = row_pointer[0];
643
644 switch (cinfo->in_color_space) {
645 case JCS_RGB:
646 for (x = 0; x < ibuf->x; x++) {
647 *buffer++ = rect[0];
648 *buffer++ = rect[1];
649 *buffer++ = rect[2];
650 rect += 4;
651 }
652 break;
653 case JCS_GRAYSCALE:
654 for (x = 0; x < ibuf->x; x++) {
655 *buffer++ = rect[0];
656 rect += 4;
657 }
658 break;
659 case JCS_UNKNOWN:
660 memcpy(buffer, rect, 4 * ibuf->x);
661 break;
662 /* default was missing... intentional ? */
663 default:
664 /* do nothing */
665 break;
666 }
667
668 jpeg_write_scanlines(cinfo, row_pointer, 1);
669 }
670
671 jpeg_finish_compress(cinfo);
672 MEM_freeN(row_pointer[0]);
673}
674
675static int init_jpeg(FILE *outfile, jpeg_compress_struct *cinfo, ImBuf *ibuf)
676{
677 int quality;
678
679 quality = ibuf->foptions.quality;
680 if (quality <= 0) {
681 quality = jpeg_default_quality;
682 }
683 quality = std::min(quality, 100);
684
685 jpeg_create_compress(cinfo);
686 jpeg_stdio_dest(cinfo, outfile);
687
688 cinfo->image_width = ibuf->x;
689 cinfo->image_height = ibuf->y;
690
691 cinfo->in_color_space = JCS_RGB;
692 if (ibuf->planes == 8) {
693 cinfo->in_color_space = JCS_GRAYSCALE;
694 }
695#if 0
696 /* just write RGBA as RGB,
697 * unsupported feature only confuses other s/w */
698
699 if (ibuf->planes == 32) {
700 cinfo->in_color_space = JCS_UNKNOWN;
701 }
702#endif
703 switch (cinfo->in_color_space) {
704 case JCS_RGB:
705 cinfo->input_components = 3;
706 break;
707 case JCS_GRAYSCALE:
708 cinfo->input_components = 1;
709 break;
710 case JCS_UNKNOWN:
711 cinfo->input_components = 4;
712 break;
713 /* default was missing... intentional ? */
714 default:
715 /* do nothing */
716 break;
717 }
718 jpeg_set_defaults(cinfo);
719
720 /* own settings */
721
722 cinfo->dct_method = JDCT_FLOAT;
723 jpeg_set_quality(cinfo, quality, true);
724
725 return 0;
726}
727
728static bool save_stdjpeg(const char *filepath, ImBuf *ibuf)
729{
730 FILE *outfile;
731 jpeg_compress_struct _cinfo, *cinfo = &_cinfo;
732 my_error_mgr jerr;
733
734 if ((outfile = BLI_fopen(filepath, "wb")) == nullptr) {
735 return false;
736 }
737
738 cinfo->err = jpeg_std_error(&jerr.pub);
739 jerr.pub.error_exit = jpeg_error;
740
741 /* Establish the setjmp return context for jpeg_error to use. */
742 if (setjmp(jerr.setjmp_buffer)) {
743 /* If we get here, the JPEG code has signaled an error.
744 * We need to clean up the JPEG object, close the input file, and return.
745 */
746 jpeg_destroy_compress(cinfo);
747 fclose(outfile);
748 remove(filepath);
749 return false;
750 }
751
752 init_jpeg(outfile, cinfo, ibuf);
753
754 write_jpeg(cinfo, ibuf);
755
756 fclose(outfile);
757 jpeg_destroy_compress(cinfo);
758
759 return true;
760}
761
762bool imb_savejpeg(ImBuf *ibuf, const char *filepath, int flags)
763{
764
765 ibuf->flags = flags;
766 return save_stdjpeg(filepath, ibuf);
767}
#define IDP_string_get(prop)
blender::ocio::ColorSpace ColorSpace
Definition BLF_api.hh:38
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
#define ATTR_NORETURN
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define LISTBASE_FOREACH(type, var, list)
MINLINE uint min_uu(uint a, uint b)
MINLINE uint max_uu(uint a, uint b)
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
int char char int int int int size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:913
size_t BLI_snprintf_utf8_rlen(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
unsigned char uchar
unsigned int uint
#define STRPREFIX(a, b)
#define ARRAY_SIZE(arr)
#define STREQ(a, b)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
ID and Library types, which are fundamental for SDNA.
@ IDP_STRING
blender::Vector< char > IMB_colormanagement_space_to_icc_profile(const ColorSpace *colorspace)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
@ IMB_FTYPE_JPG
@ IB_byte_data
@ IB_uninitialized_pixels
@ IB_metadata
@ IB_test
void IMB_metadata_set_field(IDProperty *metadata, const char *key, const char *value)
Definition metadata.cc:68
void IMB_metadata_ensure(IDProperty **metadata)
Definition metadata.cc:23
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
void prepend(const T &value)
bool is_empty() const
nullptr float
#define str(s)
#define JPEG_MARKER_APP1
#define INPUT_BYTE(cinfo, V, action)
static const uchar jpeg_default_quality
bool imb_is_a_jpeg(const uchar *mem, const size_t size)
static void memory_source(j_decompress_ptr cinfo, const uchar *buffer, size_t size)
#define JPEG_MARKER_MSB
my_source_mgr * my_src_ptr
bool imb_savejpeg(ImBuf *ibuf, const char *filepath, int flags)
ImBuf * imb_load_jpeg(const uchar *buffer, size_t size, int flags, ImFileColorSpace &)
#define JPEG_MARKER_SOI
#define INPUT_2BYTES(cinfo, V, action)
#define INPUT_VARS(cinfo)
static int init_jpeg(FILE *outfile, jpeg_compress_struct *cinfo, ImBuf *ibuf)
static ImBuf * ibJpegImageFromCinfo(jpeg_decompress_struct *cinfo, int flags, int max_size, size_t *r_width, size_t *r_height)
static void term_source(j_decompress_ptr cinfo)
static uchar ibuf_quality
static boolean handle_app1(j_decompress_ptr cinfo)
#define INPUT_SYNC(cinfo)
my_error_mgr * my_error_ptr
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
ImBuf * imb_thumbnail_jpeg(const char *filepath, const int flags, const size_t max_thumb_size, ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height)
static void init_source(j_decompress_ptr cinfo)
static void write_jpeg(jpeg_compress_struct *cinfo, ImBuf *ibuf)
static void jpeg_error(j_common_ptr cinfo) ATTR_NORETURN
static boolean fill_input_buffer(j_decompress_ptr cinfo)
#define JPEG_APP1_MAX
static bool save_stdjpeg(const char *filepath, ImBuf *ibuf)
float length(VecOp< float, D >) RET
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ListBase group
Definition DNA_ID.h:143
IDPropertyData data
Definition DNA_ID.h:169
const ColorSpace * colorspace
ImbFormatOptions foptions
ImBufByteBuffer byte_buffer
unsigned char planes
enum eImbFileType ftype
IDProperty * metadata
double ppm[2]
jmp_buf setjmp_buffer
jpeg_error_mgr pub
const uchar * buffer
jpeg_source_mgr pub
JOCTET terminal[2]
i
Definition text_draw.cc:230
static int magic(const Tex *tex, const float texvec[3], TexResult *texres)