// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.
for (const ImageBundle& ib : io->frames) { // Remember original encoding, will be returned by decoder.
original_metadata_encodings.push_back(ib.metadata()->color_encoding); // c_current should not change during encoding.
original_current_encodings.push_back(ib.c_current());
}
for (const ImageBundle& ib1 : io->frames) {
metadata_encodings_1.push_back(ib1.metadata()->color_encoding);
}
// Should still be in the same color space after encoding.
CheckSameEncodings(metadata_encodings_1, original_metadata_encodings, "original vs after encoding", failures);
for (const ImageBundle& ib2 : io2->frames) {
metadata_encodings_2.push_back(ib2.metadata()->color_encoding);
current_encodings_2.push_back(ib2.c_current());
}
// We always produce the original color encoding if a color transform hook is // set.
CheckSameEncodings(current_encodings_2, original_current_encodings, "current: original vs decoded", failures);
// Decoder returns the originals passed to the encoder.
CheckSameEncodings(metadata_encodings_2, original_metadata_encodings, "metadata: original vs decoded", failures);
if (compressed_size) {
*compressed_size = compressed.size();
}
if (format.data_type == JXL_TYPE_UINT8) { // Multiplier to bring to 0-1.0 range double mul = factor > 0.0 ? factor : 1.0 / 255.0; for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) {
size_t j = (y * xsize + x) * 4;
size_t i = y * stride + x * num_channels; double r = pixels[i]; double g = gray ? r : pixels[i + 1]; double b = gray ? r : pixels[i + 2]; double a = alpha ? pixels[i + num_channels - 1] : 255;
result[j + 0] = r * mul;
result[j + 1] = g * mul;
result[j + 2] = b * mul;
result[j + 3] = a * mul;
}
}
} elseif (format.data_type == JXL_TYPE_UINT16) {
Check(endianness != JXL_NATIVE_ENDIAN); // Multiplier to bring to 0-1.0 range double mul = factor > 0.0 ? factor : 1.0 / 65535.0; for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) {
size_t j = (y * xsize + x) * 4;
size_t i = y * stride + x * num_channels * 2; double r; double g; double b; double a; if (endianness == JXL_BIG_ENDIAN) {
r = (pixels[i + 0] << 8) + pixels[i + 1];
g = gray ? r : (pixels[i + 2] << 8) + pixels[i + 3];
b = gray ? r : (pixels[i + 4] << 8) + pixels[i + 5];
a = alpha ? (pixels[i + num_channels * 2 - 2] << 8) +
pixels[i + num_channels * 2 - 1]
: 65535;
} else {
r = (pixels[i + 1] << 8) + pixels[i + 0];
g = gray ? r : (pixels[i + 3] << 8) + pixels[i + 2];
b = gray ? r : (pixels[i + 5] << 8) + pixels[i + 4];
a = alpha ? (pixels[i + num_channels * 2 - 1] << 8) +
pixels[i + num_channels * 2 - 2]
: 65535;
}
result[j + 0] = r * mul;
result[j + 1] = g * mul;
result[j + 2] = b * mul;
result[j + 3] = a * mul;
}
}
} elseif (format.data_type == JXL_TYPE_FLOAT) {
Check(endianness != JXL_NATIVE_ENDIAN); for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) {
size_t j = (y * xsize + x) * 4;
size_t i = y * stride + x * num_channels * 4; double r; double g; double b; double a; if (endianness == JXL_BIG_ENDIAN) {
r = LoadBEFloat(pixels + i);
g = gray ? r : LoadBEFloat(pixels + i + 4);
b = gray ? r : LoadBEFloat(pixels + i + 8);
a = alpha ? LoadBEFloat(pixels + i + num_channels * 4 - 4) : 1.0;
} else {
r = LoadLEFloat(pixels + i);
g = gray ? r : LoadLEFloat(pixels + i + 4);
b = gray ? r : LoadLEFloat(pixels + i + 8);
a = alpha ? LoadLEFloat(pixels + i + num_channels * 4 - 4) : 1.0;
}
result[j + 0] = r;
result[j + 1] = g;
result[j + 2] = b;
result[j + 3] = a;
}
}
} elseif (format.data_type == JXL_TYPE_FLOAT16) {
Check(endianness != JXL_NATIVE_ENDIAN); for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) {
size_t j = (y * xsize + x) * 4;
size_t i = y * stride + x * num_channels * 2; double r; double g; double b; double a; if (endianness == JXL_BIG_ENDIAN) {
r = LoadBEFloat16(pixels + i);
g = gray ? r : LoadBEFloat16(pixels + i + 2);
b = gray ? r : LoadBEFloat16(pixels + i + 4);
a = alpha ? LoadBEFloat16(pixels + i + num_channels * 2 - 2) : 1.0;
} else {
r = LoadLEFloat16(pixels + i);
g = gray ? r : LoadLEFloat16(pixels + i + 2);
b = gray ? r : LoadLEFloat16(pixels + i + 4);
a = alpha ? LoadLEFloat16(pixels + i + num_channels * 2 - 2) : 1.0;
}
result[j + 0] = r;
result[j + 1] = g;
result[j + 2] = b;
result[j + 3] = a;
}
}
} else {
Check(false); // Unsupported type
} return result;
}
size_t ComparePixels(const uint8_t* a, const uint8_t* b, size_t xsize,
size_t ysize, const JxlPixelFormat& format_a, const JxlPixelFormat& format_b, double threshold_multiplier) { // Convert both images to equal full precision for comparison.
std::vector<double> a_full = ConvertToRGBA32(a, xsize, ysize, format_a);
std::vector<double> b_full = ConvertToRGBA32(b, xsize, ysize, format_b); bool gray_a = format_a.num_channels < 3; bool gray_b = format_b.num_channels < 3; bool alpha_a = ((format_a.num_channels & 1) == 0); bool alpha_b = ((format_b.num_channels & 1) == 0);
size_t bits_a = GetPrecision(format_a.data_type);
size_t bits_b = GetPrecision(format_b.data_type);
size_t bits = std::min(bits_a, bits_b); // How much distance is allowed in case of pixels with lower bit depths, given // that the double precision float images use range 0-1.0. // E.g. in case of 1-bit this is 0.5 since 0.499 must map to 0 and 0.501 must // map to 1. double precision = 0.5 * threshold_multiplier / ((1ull << bits) - 1ull); if (format_a.data_type == JXL_TYPE_FLOAT16 ||
format_b.data_type == JXL_TYPE_FLOAT16) { // Lower the precision for float16, because it currently looks like the // scalar and wasm implementations of hwy have 1 less bit of precision // than the x86 implementations. // TODO(lode): Set the required precision back to 11 bits when possible.
precision = 0.5 * threshold_multiplier / ((1ull << (bits - 1)) - 1ull);
} if (format_b.data_type == JXL_TYPE_UINT8) { // Increase the threshold by the maximum difference introduced by dithering.
precision += 63.0 / 128.0;
}
size_t numdiff = 0; for (size_t y = 0; y < ysize; y++) { for (size_t x = 0; x < xsize; x++) {
size_t i = (y * xsize + x) * 4; bool ok = true; if (gray_a || gray_b) { if (!Near(a_full[i + 0], b_full[i + 0], precision)) ok = false; // If the input was grayscale and the output not, then the output must // have all channels equal. if (gray_a && b_full[i + 0] != b_full[i + 1] &&
b_full[i + 2] != b_full[i + 2]) {
ok = false;
}
} else { if (!Near(a_full[i + 0], b_full[i + 0], precision) ||
!Near(a_full[i + 1], b_full[i + 1], precision) ||
!Near(a_full[i + 2], b_full[i + 2], precision)) {
ok = false;
}
} if (alpha_a && alpha_b) { if (!Near(a_full[i + 3], b_full[i + 3], precision)) ok = false;
} else { // If the input had no alpha channel, the output should be opaque // after roundtrip. if (alpha_b && !Near(1.0, b_full[i + 3], precision)) ok = false;
} if (!ok) numdiff++;
}
} return numdiff;
}
double DistanceRMS(const uint8_t* a, const uint8_t* b, size_t xsize,
size_t ysize, const JxlPixelFormat& format) { // Convert both images to equal full precision for comparison.
std::vector<double> a_full = ConvertToRGBA32(a, xsize, ysize, format);
std::vector<double> b_full = ConvertToRGBA32(b, xsize, ysize, format); double sum = 0.0; for (size_t y = 0; y < ysize; y++) { double row_sum = 0.0; for (size_t x = 0; x < xsize; x++) {
size_t i = (y * xsize + x) * 4; for (size_t c = 0; c < format.num_channels; ++c) { double diff = a_full[i + c] - b_full[i + c];
row_sum += diff * diff;
}
}
sum += row_sum;
}
sum /= (xsize * ysize); return sqrt(sum);
}
// Keep ICC profile in lossless modes because a reconstructed profile may be // slightly different (quantization). // Also keep ICC in JPEG reconstruction mode as we need byte-exact profiles. if (!cparams.IsLossless() && !io->Main().IsJPEG() && cparams.cms_set) {
metadata->m.color_encoding.DecideIfWantICC(cparams.cms);
}
// TODO(firsching): move this EncodeFile to test_utils / re-implement this // using API functions returntrue;
}
Status EncodePreview(const CompressParams& cparams, ImageBundle& ib, const CodecMetadata* metadata, const JxlCmsInterface& cms,
ThreadPool* pool, BitWriter* JXL_RESTRICT writer) {
JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
BitWriter preview_writer{memory_manager}; // TODO(janwas): also support generating preview by downsampling if (ib.HasColor()) {
AuxOut aux_out; // TODO(lode): check if we want all extra channels and matching xyb_encoded // for the preview, such that using the main ImageMetadata object for // encoding this frame is warrented.
FrameInfo frame_info;
frame_info.is_preview = true;
JXL_RETURN_IF_ERROR(EncodeFrame(memory_manager, cparams, frame_info,
metadata, ib, cms, pool, &preview_writer,
&aux_out));
preview_writer.ZeroPadToByte();
}
if (preview_writer.BitsWritten() != 0) {
writer->ZeroPadToByte();
JXL_RETURN_IF_ERROR(writer->AppendByteAligned(preview_writer.GetSpan()));
}
CompressParams cparams = params; if (io->Main().color_transform != ColorTransform::kNone) { // Set the color transform to YCbCr or XYB if the original image is such.
cparams.color_transform = io->Main().color_transform;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.