8000 Lower libjxl version requirement to 0.7.x · libvips/libvips@afb6edf · GitHub
[go: up one dir, main page]

Skip to content

Commit afb6edf

Browse files
committed
Lower libjxl version requirement to 0.7.x
1 parent c423bbf commit afb6edf

File tree

4 files changed

+179
-6
lines changed

4 files changed

+179
-6
lines changed

ChangeLog

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ master
2929
- deprecate VipsSaveable, add VipsForeignSaveable
3030
- move vips_image_preeval(), vips_image_eval(), vips_image_posteval() into the
3131
public API
32-
- jxlsave writes in chunks, for lower memory use on large images (but we now
33-
need libjxl 0.9 at minimum)
32+
- jxlsave: add support for chunked save (requires libjxl 0.9.0+)
33+
- increase minimum version of libjxl dependency to 0.7.0
3434
- increase minimum version of libheif dependency to 1.11.0
3535
- improve scaling of hough_line feature space [ecbypi]
3636
- heifload: limit per-image memory usage to 2GB (requires libheif 1.20.0+)

libvips/foreign/jxlload.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,9 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
947947

948948
case JXL_DEC_COLOR_ENCODING:
949949
if (JxlDecoderGetICCProfileSize(jxl->decoder,
950+
#ifndef HAVE_LIBJXL_0_9
951+
&jxl->format,
952+
#endif
950953
JXL_COLOR_PROFILE_TARGET_DATA, &jxl->icc_size)) {
951954
vips_foreign_load_jxl_error(jxl, "JxlDecoderGetICCProfileSize");
952955
return -1;
@@ -960,6 +963,9 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
960963
return -1;
961964

962965
if (JxlDecoderGetColorAsICCProfile(jxl->decoder,
966+
#ifndef HAVE_LIBJXL_0_9
967+
&jxl->format,
968+
#endif
963969
JXL_COLOR_PROFILE_TARGET_DATA,
964970
jxl->icc_data, jxl->icc_size)) {
965971
vips_foreign_load_jxl_error(jxl,

libvips/foreign/jxlsave.c

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ typedef struct _VipsForeignSaveJxl {
8484
gboolean lossless;
8585
int Q;
8686

87+
#ifdef HAVE_LIBJXL_0_9
8788
gboolean error;
89+
#endif
8890

8991
/* JXL multipage and animated images are the same, but multipage has
9092
* all the frame delays set to -1 (duration 0xffffffff).
@@ -118,6 +120,7 @@ typedef struct _VipsForeignSaveJxl {
118120
*/
119121
uint8_t output_buffer[OUTPUT_BUFFER_SIZE];
120122

123+
#ifdef HAVE_LIBJXL_0_9
121124
/* Chunk reader.
122125
*/
123126
struct JxlChunkedFrameInputSource input_source;
@@ -135,14 +138,21 @@ typedef struct _VipsForeignSaveJxl {
135138
/* Track number of pixels saved here for eval reporting.
136139
*/
137140
guint64 processed;
138-
141+
#else /*!defined(HAVE_LIBJXL_0_9)*/
142+
/* Buffer scanlines to make a line of libjxl tiles.
143+
*/
144+
VipsPel *scanline_buffer;
145+
size_t scanline_size;
146+
int scanline_y;
147+
#endif /*defined(HAVE_LIBJXL_0_9)*/
139148
} VipsForeignSaveJxl;
140149

141150
typedef VipsForeignSaveClass VipsForeignSaveJxlClass;
142151

143152
G_DEFINE_ABSTRACT_TYPE(VipsForeignSaveJxl, vips_foreign_save_jxl,
144153
VIPS_TYPE_FOREIGN_SAVE);
145154

155+
#ifdef HAVE_LIBJXL_0_9
146156
static void *
147157
vips_foreign_save_jxl_get_buffer(void *opaque, size_t *size)
148158
{
@@ -317,6 +327,7 @@ vips_foreign_save_jxl_set_input_source(VipsForeignSaveJxl *jxl)
317327
.release_buffer = vips_foreign_save_jxl_input_release_buffer
318328
};
319329
}
330+
#endif /*defined(HAVE_LIBJXL_0_9)*/
320331

321332
static void
322333
vips_foreign_save_jxl_error(VipsForeignSaveJxl *jxl, const char *details)
@@ -437,7 +448,9 @@ vips_foreign_save_jxl_finalize(GObject *gobject)
437448
VIPS_FREEF(JxlThreadParallelRunnerDestroy, jxl->runner);
438449
VIPS_FREEF(JxlEncoderDestroy, jxl->encoder);
439450

451+
#ifdef HAVE_LIBJXL_0_9
440452
g_mutex_clear(&jxl->tile_lock);
453+
#endif
441454

442455
G_OBJECT_CLASS(vips_foreign_save_jxl_parent_class)->finalize(gobject);
443456
}
@@ -448,7 +461,9 @@ vips_foreign_save_jxl_dispose(GObject *gobject)
448461
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) gobject;
449462

450463
VIPS_UNREF(jxl->target);
464+
#ifdef HAVE_LIBJXL_0_9
451465
VIPS_FREEF(g_hash_table_destroy, jxl->tile_hash);
466+
#endif
452467

453468
G_OBJECT_CLASS(vips_foreign_save_jxl_parent_class)->dispose(gobject);
454469
}
@@ -674,6 +689,7 @@ vips_foreign_save_jxl_get_delay(VipsForeignSaveJxl *jxl, int page_number)
674689
return delay <= 10 ? 100 : delay;
675690
}
676691

692+
#ifdef HAVE_LIBJXL_0_9
677693
static int
678694
vips_foreign_save_jxl_save_page(VipsForeignSaveJxl *jxl,
679695
int n, VipsImage *page)
@@ -747,6 +763,142 @@ vips_foreign_save_jxl_save(VipsForeignSaveJxl *jxl, VipsImage *in)
747763

748764
return 0;
749765
}
766+
#else /*!defined(HAVE_LIBJXL_0_9)*/
767+
/* Output for sequential write.
768+
*/
769+
static int
770+
vips_foreign_save_jxl_process_output(VipsForeignSaveJxl *jxl)
771+
{
772+
JxlEncoderStatus status;
773+
774+
do {
775+
uint8_t *out = jxl->output_buffer;
776+
size_t avail_out = OUTPUT_BUFFER_SIZE;
777+
status = JxlEncoderProcessOutput(jxl->encoder, &out, &avail_out);
778+
779+
switch (status) {
780+
case JXL_ENC_SUCCESS:
781+
case JXL_ENC_NEED_MORE_OUTPUT:
782+
if (OUTPUT_BUFFER_SIZE > avail_out &&
783+
vips_target_write(jxl->target, jxl->output_buffer,
784+
OUTPUT_BUFFER_SIZE - avail_out))
785+
return -1;
786+
break;
787+
788+
default:
789+
vips_foreign_save_jxl_error(jxl, "JxlEncoderProcessOutput");
790+
#ifdef DEBUG
791+
vips_foreign_save_jxl_print_status(status);
792+
#endif /*DEBUG*/
793+
return -1;
794+
}
795+
} while (status != JXL_ENC_SUCCESS);
796+
797+
return 0;
798+
}
799+
800+
static int
801+
vips_foreign_save_jxl_add_frame(VipsForeignSaveJxl *jxl)
802+
{
803+
JxlEncoderFrameSettings *frame_settings =
804+
JxlEncoderFrameSettingsCreate(jxl->encoder, NULL);
805+
JxlEncoderFrameSettingsSetOption(frame_settings,
806+
JXL_ENC_FRAME_SETTING_DECODING_SPEED, jxl->tier);
807+
JxlEncoderSetFrameDistance(frame_settings, jxl->distance);
808+
JxlEncoderFrameSettingsSetOption(frame_settings,
809+
JXL_ENC_FRAME_SETTING_EFFORT, jxl->effort);
810+
JxlEncoderSetFrameLossless(frame_settings, jxl->lossless);
811+
812+
if (jxl->info.have_animation) {
813+
JxlFrameHeader header = { 0 };
814+
815+
if (!jxl->is_animated)
816+
header.duration = 0xffffffff;
817+
else
818+
header.duration =
819+
vips_foreign_save_jxl_get_delay(jxl, jxl->page_number);
820+
821+
JxlEncoderSetFrameHeader(frame_settings, &header);
822+
}
823+
824+
if (JxlEncoderAddImageFrame(frame_settings, &jxl->format,
825+
jxl->scanline_buffer, jxl->scanline_size)) {
826+
vips_foreign_save_jxl_error(jxl, "JxlEncoderAddImageFrame");
827+
return -1;
828+
}
829+
830+
jxl->page_number += 1;
831+
832+
/* We should close frames before processing the output
833+
* if we have written the last frame.
834+
*/
835+
if (jxl->page_number == jxl->page_count)
836+
JxlEncoderCloseFrames(jxl->encoder);
837+
838+
return vips_foreign_save_jxl_process_output(jxl);
839+
}
840+
841+
/* Another chunk of pixels have arrived from the pipeline. Add to frame, and
842+
* if the frame completes, compress and write to the target.
843+
*/
844+
static int
845+
vips_foreign_save_jxl_sink_disc(VipsRegion *region, VipsRect *area, void *a)
846+
{
847+
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) a;
848+
size_t sz = VIPS_IMAGE_SIZEOF_PEL(region->im) * area->width;
849+
850+
/* Write the new pixels into the frame.
851+
*/
852+
for (int i = 0; i < area->height; i++) {
853+
memcpy(jxl->scanline_buffer + sz * jxl->scanline_y,
854+
VIPS_REGION_ADDR(region, 0, area->top + i),
855+
sz);
856+
857+
jxl->scanline_y += 1;
858+
859+
/* If we've filled the frame, add it to the encoder.
860+
*/
861+
if (jxl->scanline_y == jxl->page_height) {
862+
if (vips_foreign_save_jxl_add_frame(jxl))
863+
return -1;
864+
865+
jxl->scanline_y = 0;
866+
}
867+
}
868+
869+
return 0;
870+
}
871+
872+
static int
873+
vips_foreign_save_jxl_save_sequential(VipsForeignSaveJxl *jxl, VipsImage *in)
874+
{
875+
/* Write the header.
876+
*/
877+
if (vips_foreign_save_jxl_process_output(jxl))
878+
return -1;
879+
880+
/* RGB(A) frame as a contiguous buffer.
881+
*/
882+
jxl->scanline_size = VIPS_IMAGE_SIZEOF_LINE(in) * jxl->page_height;
883+
if (!(jxl->scanline_buffer = vips_tracked_malloc(jxl->scanline_size)))
884+
return -1;
885+
886+
if (vips_sink_disc(in, vips_foreign_save_jxl_sink_disc, jxl))
887+
return -1;
888+
889+
/* This function must be called after the final frame and/or box,
890+
* otherwise the codestream will not be encoded correctly.
891+
*/
892+
JxlEncoderCloseInput(jxl->encoder);
893+
894+
/* Flush any remaining libjxl writes.
895+
*/
896+
if (vips_foreign_save_jxl_process_output(jxl))
897+
return -1;
898+
899+
return 0;
900+
}
901+
#endif /*defined(HAVE_LIBJXL_0_9)*/
750902

751903
static int
752904
vips_foreign_save_jxl_build(VipsObject *object)
@@ -768,7 +920,13 @@ vips_foreign_save_jxl_build(VipsObject *object)
768920
* value.
769921
*/
770922
if (!vips_object_argument_isset(object, "distance"))
923+
#ifdef HAVE_LIBJXL_0_9
771924
jxl->distance = JxlEncoderDistanceFromQuality((float) jxl->Q);
925+
#else
926+
jxl->distance = jxl->Q >= 30
927+
? 0.1 + (100 - jxl->Q) * 0.09
928+
: 53.0 / 3000.0 * jxl->Q * jxl->Q - 23.0 / 20.0 * jxl->Q + 25.0;
929+
#endif
772930

773931
/* Distance 0 is lossless. libjxl will fail for lossy distance 0.
774932
*/
@@ -863,6 +1021,7 @@ vips_foreign_save_jxl_build(VipsObject *object)
8631021
printf(" lossless = %d\n", jxl->lossless);
8641022
#endif /*DEBUG*/
8651023

1024+
#ifdef HAVE_LIBJXL_0_9
8661025
/* _save() is not a vips_sink_*() iterator, so we must emit
8671026
* the various signals by hand.
8681027
*/
@@ -876,6 +1035,9 @@ vips_foreign_save_jxl_build(VipsObject *object)
8761035

8771036
if (jxl->error)
8781037
return -1;
1038+
#else /*!defined(HAVE_LIBJXL_0_9)*/
1039+
int result = vips_foreign_save_jxl_save_sequential(jxl, in);
1040+
#endif /*defined(HAVE_LIBJXL_0_9)*/
8791041

8801042
if (vips_target_end(jxl->target))
8811043
return -1;
@@ -966,7 +1128,9 @@ vips_foreign_save_jxl_init(VipsForeignSaveJxl *jxl)
9661128
jxl->distance = 1.0;
9671129
jxl->effort = 7;
9681130
jxl->Q = 75;
1131+
#ifdef HAVE_LIBJXL_0_9
9691132
g_mutex_init(&jxl->tile_lock);
1133+
#endif
9701134
}
9711135

9721136
typedef struct _VipsForeignSaveJxlFile {

meson.build

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,8 @@ if libheif_dep.found()
506506
cfg_var.set('HAVE_HEIF_MAX_TOTAL_MEMORY', cpp.has_member('struct heif_security_limits', 'max_total_memory', prefix: '#include <libheif/heif.h>', dependencies: libheif_dep))
507507
endif
508508

509-
# need v0.9+ for JxlEncoderAddChunkedFrame()
510-
libjxl_dep = dependency('libjxl', version: '>=0.9', required: get_option('jpeg-xl'))
511-
libjxl_threads_dep = dependency('libjxl_threads', version: '>=0.9', required: get_option('jpeg-xl'))
509+
libjxl_dep = dependency('libjxl', version: '>=0.7', required: get_option('jpeg-xl'))
510+
libjxl_threads_dep = dependency('libjxl_threads', version: '>=0.7', required: get_option('jpeg-xl'))
512511
libjxl_found = libjxl_dep.found() and libjxl_threads_dep.found()
513512
libjxl_module = false
514513
if libjxl_found
@@ -522,6 +521,10 @@ if libjxl_found
522521
external_deps += libjxl_threads_dep
523522
endif
524523
cfg_var.set('HAVE_LIBJXL', true)
524+
# need v0.9+ for chunked write
525+
if libjxl_dep.version().version_compare('>=0.9')
526+
cfg_var.set('HAVE_LIBJXL_0_9', '1')
527+
endif
525528
endif
526529

527530
# only if pdfium not found

0 commit comments

Comments
 (0)
0