summaryrefslogtreecommitdiff
path: root/plugins/gimp
diff options
context:
space:
mode:
authorJiyong <jiyong.min@samsung.com>2023-12-26 17:33:03 +0900
committerJiyong <jiyong.min@samsung.com>2023-12-27 08:25:11 +0900
commita6d06c38e46e552195648836052eb909925fe5ff (patch)
tree5b34f3947c8331dc618a5166974e4d9757f8e782 /plugins/gimp
parentf3e519be675ef7922a6c1c3a682232302b55496d (diff)
parent3b773d382e34fcfc7c8995d8bd681a6ef0529b02 (diff)
downloadlibjxl-accepted/tizen_unified_riscv.tar.gz
libjxl-accepted/tizen_unified_riscv.tar.bz2
libjxl-accepted/tizen_unified_riscv.zip
Change-Id: I13b4d2c94ada4853484630800e2a8a5ae90d34c1
Diffstat (limited to 'plugins/gimp')
-rw-r--r--plugins/gimp/common.h4
-rw-r--r--plugins/gimp/file-jxl-load.cc180
-rw-r--r--plugins/gimp/file-jxl-load.h2
-rw-r--r--plugins/gimp/file-jxl-save.cc55
-rw-r--r--plugins/gimp/file-jxl-save.h2
5 files changed, 169 insertions, 74 deletions
diff --git a/plugins/gimp/common.h b/plugins/gimp/common.h
index 95c51bf..3fe63c1 100644
--- a/plugins/gimp/common.h
+++ b/plugins/gimp/common.h
@@ -23,8 +23,8 @@
#undef MIN
#undef CLAMP
-#include "jxl/resizable_parallel_runner.h"
-#include "jxl/resizable_parallel_runner_cxx.h"
+#include <jxl/resizable_parallel_runner.h>
+#include <jxl/resizable_parallel_runner_cxx.h>
namespace jxl {
diff --git a/plugins/gimp/file-jxl-load.cc b/plugins/gimp/file-jxl-load.cc
index b1d1f15..4796c17 100644
--- a/plugins/gimp/file-jxl-load.cc
+++ b/plugins/gimp/file-jxl-load.cc
@@ -5,17 +5,45 @@
#include "plugins/gimp/file-jxl-load.h"
+#include <jxl/decode.h>
+#include <jxl/decode_cxx.h>
+
#define _PROFILE_ORIGIN_ JXL_COLOR_PROFILE_TARGET_ORIGINAL
#define _PROFILE_TARGET_ JXL_COLOR_PROFILE_TARGET_DATA
#define LOAD_PROC "file-jxl-load"
namespace jxl {
+bool SetJpegXlOutBuffer(
+ std::unique_ptr<JxlDecoderStruct, JxlDecoderDestroyStruct> *dec,
+ JxlPixelFormat *format, size_t *buffer_size, gpointer *pixels_buffer_1) {
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderImageOutBufferSize(dec->get(), format, buffer_size)) {
+ g_printerr(LOAD_PROC " Error: JxlDecoderImageOutBufferSize failed\n");
+ return false;
+ }
+ *pixels_buffer_1 = g_malloc(*buffer_size);
+ if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec->get(), format,
+ *pixels_buffer_1,
+ *buffer_size)) {
+ g_printerr(LOAD_PROC " Error: JxlDecoderSetImageOutBuffer failed\n");
+ return false;
+ }
+ return true;
+}
+
bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
+ bool stop_processing = false;
+ JxlDecoderStatus status = JXL_DEC_NEED_MORE_INPUT;
std::vector<uint8_t> icc_profile;
GimpColorProfile *profile_icc = nullptr;
GimpColorProfile *profile_int = nullptr;
bool is_linear = false;
+ unsigned long xsize = 0, ysize = 0;
+ long crop_x0 = 0, crop_y0 = 0;
+ size_t layer_idx = 0;
+ uint32_t frame_duration = 0;
+ double tps_denom = 1.f, tps_numer = 1.f;
gint32 layer;
@@ -28,6 +56,10 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
GimpPrecision precision = GIMP_PRECISION_U16_GAMMA;
JxlBasicInfo info = {};
JxlPixelFormat format = {};
+ JxlAnimationHeader animation = {};
+ JxlBlendMode blend_mode = JXL_BLEND_BLEND;
+ char *frame_name = nullptr; // will be realloced
+ size_t frame_name_len = 0;
format.num_channels = 4;
format.data_type = JXL_TYPE_FLOAT;
@@ -53,9 +85,10 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
auto dec = JxlDecoderMake(nullptr);
if (JXL_DEC_SUCCESS !=
- JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
- JXL_DEC_COLOR_ENCODING |
- JXL_DEC_FULL_IMAGE)) {
+ JxlDecoderSubscribeEvents(
+ dec.get(), JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING |
+ JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME_PROGRESSION |
+ JXL_DEC_FRAME)) {
g_printerr(LOAD_PROC " Error: JxlDecoderSubscribeEvents failed\n");
return false;
}
@@ -66,14 +99,26 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
g_printerr(LOAD_PROC " Error: JxlDecoderSetParallelRunner failed\n");
return false;
}
+ // TODO(user): make this work with coalescing set to false, while handling
+ // frames with duration 0 and references to earlier frames correctly.
+ if (JXL_DEC_SUCCESS != JxlDecoderSetCoalescing(dec.get(), JXL_TRUE)) {
+ g_printerr(LOAD_PROC " Error: JxlDecoderSetCoalescing failed\n");
+ return false;
+ }
// grand decode loop...
JxlDecoderSetInput(dec.get(), compressed.data(), compressed.size());
+ if (JXL_DEC_SUCCESS != JxlDecoderSetProgressiveDetail(
+ dec.get(), JxlProgressiveDetail::kPasses)) {
+ g_printerr(LOAD_PROC " Error: JxlDecoderSetProgressiveDetail failed\n");
+ return false;
+ }
+
while (true) {
gimp_load_progress.update();
- JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
+ if (!stop_processing) status = JxlDecoderProcessInput(dec.get());
if (status == JXL_DEC_BASIC_INFO) {
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
@@ -81,20 +126,26 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
return false;
}
+ xsize = info.xsize;
+ ysize = info.ysize;
+ if (info.have_animation) {
+ animation = info.animation;
+ tps_denom = animation.tps_denominator;
+ tps_numer = animation.tps_numerator;
+ }
+
JxlResizableParallelRunnerSetThreads(
- runner.get(),
- JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
+ runner.get(), JxlResizableParallelRunnerSuggestThreads(xsize, ysize));
} else if (status == JXL_DEC_COLOR_ENCODING) {
// check for ICC profile
size_t icc_size = 0;
JxlColorEncoding color_encoding;
if (JXL_DEC_SUCCESS !=
- JxlDecoderGetColorAsEncodedProfile(
- dec.get(), &format, _PROFILE_ORIGIN_, &color_encoding)) {
+ JxlDecoderGetColorAsEncodedProfile(dec.get(), _PROFILE_ORIGIN_,
+ &color_encoding)) {
// Attempt to load ICC profile when no internal color encoding
- if (JXL_DEC_SUCCESS != JxlDecoderGetICCProfileSize(dec.get(), &format,
- _PROFILE_ORIGIN_,
- &icc_size)) {
+ if (JXL_DEC_SUCCESS != JxlDecoderGetICCProfileSize(
+ dec.get(), _PROFILE_ORIGIN_, &icc_size)) {
g_printerr(LOAD_PROC
" Warning: JxlDecoderGetICCProfileSize failed\n");
}
@@ -102,7 +153,7 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
if (icc_size > 0) {
icc_profile.resize(icc_size);
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
- dec.get(), &format, _PROFILE_ORIGIN_,
+ dec.get(), _PROFILE_ORIGIN_,
icc_profile.data(), icc_profile.size())) {
g_printerr(LOAD_PROC
" Warning: JxlDecoderGetColorAsICCProfile failed\n");
@@ -125,8 +176,8 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
// Internal color profile detection...
if (JXL_DEC_SUCCESS ==
- JxlDecoderGetColorAsEncodedProfile(
- dec.get(), &format, _PROFILE_TARGET_, &color_encoding)) {
+ JxlDecoderGetColorAsEncodedProfile(dec.get(), _PROFILE_TARGET_,
+ &color_encoding)) {
g_printerr(LOAD_PROC " Info: Internal color encoding detected.\n");
// figure out linearity of internal profile
@@ -279,11 +330,11 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
// create new image
if (is_linear) {
- *image_id = gimp_image_new_with_precision(
- info.xsize, info.ysize, image_type, GIMP_PRECISION_FLOAT_LINEAR);
+ *image_id = gimp_image_new_with_precision(xsize, ysize, image_type,
+ GIMP_PRECISION_FLOAT_LINEAR);
} else {
- *image_id = gimp_image_new_with_precision(
- info.xsize, info.ysize, image_type, GIMP_PRECISION_FLOAT_GAMMA);
+ *image_id = gimp_image_new_with_precision(xsize, ysize, image_type,
+ GIMP_PRECISION_FLOAT_GAMMA);
}
if (profile_int) {
@@ -294,22 +345,40 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
// get image from decoder in FLOAT
format.data_type = JXL_TYPE_FLOAT;
- if (JXL_DEC_SUCCESS !=
- JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
- g_printerr(LOAD_PROC " Error: JxlDecoderImageOutBufferSize failed\n");
+ if (!SetJpegXlOutBuffer(&dec, &format, &buffer_size, &pixels_buffer_1))
return false;
- }
- pixels_buffer_1 = g_malloc(buffer_size);
- if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
- pixels_buffer_1,
- buffer_size)) {
- g_printerr(LOAD_PROC " Error: JxlDecoderSetImageOutBuffer failed\n");
- return false;
- }
- } else if (status == JXL_DEC_FULL_IMAGE || status == JXL_DEC_FRAME) {
+ } else if (status == JXL_DEC_FULL_IMAGE) {
// create and insert layer
- layer = gimp_layer_new(*image_id, "Background", info.xsize, info.ysize,
- layer_type, /*opacity=*/100,
+ gchar *layer_name;
+ if (layer_idx == 0 && !info.have_animation) {
+ layer_name = g_strdup_printf("Background");
+ } else {
+ const GString *blend_null_flag = g_string_new("");
+ const GString *blend_replace_flag = g_string_new(" (replace)");
+ const GString *blend_combine_flag = g_string_new(" (combine)");
+ GString *blend;
+ if (blend_mode == JXL_BLEND_REPLACE) {
+ blend = (GString *)blend_replace_flag;
+ } else if (blend_mode == JXL_BLEND_BLEND) {
+ blend = (GString *)blend_combine_flag;
+ } else {
+ blend = (GString *)blend_null_flag;
+ }
+ char *temp_frame_name = nullptr;
+ bool must_free_frame_name = false;
+ if (frame_name_len == 0) {
+ temp_frame_name = g_strdup_printf("Frame %lu", layer_idx + 1);
+ must_free_frame_name = true;
+ } else {
+ temp_frame_name = frame_name;
+ }
+ double fduration = frame_duration * 1000.f * tps_denom / tps_numer;
+ layer_name = g_strdup_printf("%s (%.15gms)%s", temp_frame_name,
+ fduration, blend->str);
+ if (must_free_frame_name) free(temp_frame_name);
+ }
+ layer = gimp_layer_new(*image_id, layer_name, xsize, ysize, layer_type,
+ /*opacity=*/100,
gimp_image_get_default_new_layer_mode(*image_id));
gimp_image_insert_layer(*image_id, layer, /*parent_id=*/-1,
@@ -333,18 +402,57 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
const Babl *source_format = babl_format(babl_format_str.c_str());
babl_process(babl_fish(source_format, destination_format),
- pixels_buffer_1, pixels_buffer_2, info.xsize * info.ysize);
+ pixels_buffer_1, pixels_buffer_2, xsize * ysize);
- gegl_buffer_set(buffer, GEGL_RECTANGLE(0, 0, info.xsize, info.ysize), 0,
- nullptr, pixels_buffer_2, GEGL_AUTO_ROWSTRIDE);
+ gegl_buffer_set(buffer, GEGL_RECTANGLE(0, 0, xsize, ysize), 0, nullptr,
+ pixels_buffer_2, GEGL_AUTO_ROWSTRIDE);
+ gimp_item_transform_translate(layer, crop_x0, crop_y0);
g_clear_object(&buffer);
+ g_free(pixels_buffer_1);
+ g_free(pixels_buffer_2);
+ if (stop_processing) status = JXL_DEC_SUCCESS;
+ g_free(layer_name);
+ layer_idx++;
+ } else if (status == JXL_DEC_FRAME) {
+ JxlFrameHeader frame_header;
+ if (JxlDecoderGetFrameHeader(dec.get(), &frame_header) !=
+ JXL_DEC_SUCCESS) {
+ g_printerr(LOAD_PROC " Error: JxlDecoderSetImageOutBuffer failed\n");
+ return false;
+ }
+ xsize = frame_header.layer_info.xsize;
+ ysize = frame_header.layer_info.ysize;
+ crop_x0 = frame_header.layer_info.crop_x0;
+ crop_y0 = frame_header.layer_info.crop_y0;
+ frame_duration = frame_header.duration;
+ blend_mode = frame_header.layer_info.blend_info.blendmode;
+ if (blend_mode != JXL_BLEND_BLEND && blend_mode != JXL_BLEND_REPLACE) {
+ g_printerr(
+ LOAD_PROC
+ " Warning: JxlDecoderGetFrameHeader: Unhandled blend mode: %d\n",
+ blend_mode);
+ }
+ if ((frame_name_len = frame_header.name_length) > 0) {
+ frame_name = (char *)realloc(frame_name, frame_name_len);
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderGetFrameName(dec.get(), frame_name, frame_name_len)) {
+ g_printerr(LOAD_PROC "Error: JxlDecoderGetFrameName failed");
+ return false;
+ };
+ }
} else if (status == JXL_DEC_SUCCESS) {
// All decoding successfully finished.
// It's not required to call JxlDecoderReleaseInput(dec.get())
// since the decoder will be destroyed.
break;
- } else if (status == JXL_DEC_NEED_MORE_INPUT) {
+ } else if (status == JXL_DEC_NEED_MORE_INPUT ||
+ status == JXL_DEC_FRAME_PROGRESSION) {
+ stop_processing = status != JXL_DEC_FRAME_PROGRESSION;
+ if (JxlDecoderFlushImage(dec.get()) == JXL_DEC_SUCCESS) {
+ status = JXL_DEC_FULL_IMAGE;
+ continue;
+ }
g_printerr(LOAD_PROC " Error: Already provided all input\n");
return false;
} else if (status == JXL_DEC_ERROR) {
diff --git a/plugins/gimp/file-jxl-load.h b/plugins/gimp/file-jxl-load.h
index c9ca6d9..ef5b92f 100644
--- a/plugins/gimp/file-jxl-load.h
+++ b/plugins/gimp/file-jxl-load.h
@@ -6,8 +6,6 @@
#ifndef PLUGINS_GIMP_FILE_JXL_LOAD_H_
#define PLUGINS_GIMP_FILE_JXL_LOAD_H_
-#include "jxl/decode.h"
-#include "jxl/decode_cxx.h"
#include "plugins/gimp/common.h"
namespace jxl {
diff --git a/plugins/gimp/file-jxl-save.cc b/plugins/gimp/file-jxl-save.cc
index 5eb1412..45aaa1f 100644
--- a/plugins/gimp/file-jxl-save.cc
+++ b/plugins/gimp/file-jxl-save.cc
@@ -5,7 +5,11 @@
#include "plugins/gimp/file-jxl-save.h"
+#include <jxl/encode.h>
+#include <jxl/encode_cxx.h>
+
#include <cmath>
+#include <utility>
#include "gobject/gsignal.h"
@@ -116,8 +120,7 @@ bool JpegXlSaveGui::GuiOnChangeQuality(GtkAdjustment* adj_qual,
g_clear_signal_handler(&self->handle_toggle_lossless, self->toggle_lossless);
GtkAdjustment* adj_dist = self->entry_distance;
- jxl_save_opts.quality = gtk_adjustment_get_value(adj_qual);
- jxl_save_opts.UpdateDistance();
+ jxl_save_opts.SetQuality(gtk_adjustment_get_value(adj_qual));
gtk_adjustment_set_value(adj_dist, jxl_save_opts.distance);
self->handle_toggle_lossless = g_signal_connect(
@@ -140,8 +143,7 @@ bool JpegXlSaveGui::GuiOnChangeDistance(GtkAdjustment* adj_dist,
g_clear_signal_handler(&self->handle_entry_quality, self->entry_quality);
g_clear_signal_handler(&self->handle_toggle_lossless, self->toggle_lossless);
- jxl_save_opts.distance = gtk_adjustment_get_value(adj_dist);
- jxl_save_opts.UpdateQuality();
+ jxl_save_opts.SetDistance(gtk_adjustment_get_value(adj_dist));
gtk_adjustment_set_value(adj_qual, jxl_save_opts.quality);
if (!(jxl_save_opts.distance < 0.001)) {
@@ -356,8 +358,6 @@ bool JpegXlSaveGui::SaveDialog() {
gtk_widget_show(separator);
// Advanced Settings Frame
- std::vector<GtkWidget*> advanced_opts;
-
frame_advanced = gtk_frame_new("Advanced Settings");
gimp_help_set_help_data(frame_advanced,
"Some advanced settings may produce malformed files.",
@@ -519,8 +519,8 @@ bool JpegXlSaveOpts::UpdateQuality() {
if (distance < 0.1) {
qual = 100;
- } else if (distance > 6.56) {
- qual = 30 - 5 * log(abs(6.25 * distance - 40)) / log(2.5);
+ } else if (distance > 6.4) {
+ qual = -5.0 / 53.0 * sqrt(6360.0 * distance - 39975.0) + 1725.0 / 53.0;
lossless = false;
} else {
qual = 100 - (distance - 0.1) / 0.09;
@@ -539,15 +539,10 @@ bool JpegXlSaveOpts::UpdateQuality() {
}
bool JpegXlSaveOpts::UpdateDistance() {
- float dist;
- if (quality >= 30) {
- dist = 0.1 + (100 - quality) * 0.09;
- } else {
- dist = 6.4 + pow(2.5, (30 - quality) / 5.0) / 6.25;
- }
+ float dist = JxlEncoderDistanceFromQuality(quality);
- if (dist > 15) {
- distance = 15;
+ if (dist > 25) {
+ distance = 25;
} else {
distance = dist;
}
@@ -602,12 +597,12 @@ bool JpegXlSaveOpts::UpdateBablFormat() {
}
bool JpegXlSaveOpts::SetBablModel(std::string model) {
- babl_model_str = model;
+ babl_model_str = std::move(model);
return UpdateBablFormat();
}
bool JpegXlSaveOpts::SetBablType(std::string type) {
- babl_type_str = type;
+ babl_type_str = std::move(type);
return UpdateBablFormat();
}
@@ -727,6 +722,15 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
return false;
}
+ // this sets some basic_info properties
+ jxl_save_opts.SetModel(jxl_save_opts.is_linear);
+
+ if (JXL_ENC_SUCCESS !=
+ JxlEncoderSetBasicInfo(enc.get(), &jxl_save_opts.basic_info)) {
+ g_printerr(SAVE_PROC " Error: JxlEncoderSetBasicInfo failed\n");
+ return false;
+ }
+
// try to use ICC profile
if (!icc.empty() && !jxl_save_opts.is_gray) {
if (JXL_ENC_SUCCESS ==
@@ -785,15 +789,6 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
JxlEncoderSetFrameDistance(frame_settings, jxl_save_opts.distance);
}
- // this sets some basic_info properties
- jxl_save_opts.SetModel(jxl_save_opts.is_linear);
-
- if (JXL_ENC_SUCCESS !=
- JxlEncoderSetBasicInfo(enc.get(), &jxl_save_opts.basic_info)) {
- g_printerr(SAVE_PROC " Error: JxlEncoderSetBasicInfo failed\n");
- return false;
- }
-
// convert precision and colorspace
if (jxl_save_opts.is_linear &&
jxl_save_opts.basic_info.bits_per_sample < 32) {
@@ -832,11 +827,7 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
g_clear_object(&buffer);
// use babl to fix gamma mismatch issues
- if (jxl_save_opts.icc_attached) {
- jxl_save_opts.SetModel(jxl_save_opts.is_linear);
- } else {
- jxl_save_opts.SetModel(!jxl_save_opts.is_linear);
- }
+ jxl_save_opts.SetModel(jxl_save_opts.is_linear);
jxl_save_opts.pixel_format.data_type = JXL_TYPE_FLOAT;
jxl_save_opts.SetBablType("float");
const Babl* destination_format =
diff --git a/plugins/gimp/file-jxl-save.h b/plugins/gimp/file-jxl-save.h
index 9dfa45c..c9d0e80 100644
--- a/plugins/gimp/file-jxl-save.h
+++ b/plugins/gimp/file-jxl-save.h
@@ -6,8 +6,6 @@
#ifndef PLUGINS_GIMP_FILE_JXL_SAVE_H_
#define PLUGINS_GIMP_FILE_JXL_SAVE_H_
-#include "jxl/encode.h"
-#include "jxl/encode_cxx.h"
#include "plugins/gimp/common.h"
namespace jxl {