forked from mirrors/gecko-dev
268 lines
10 KiB
C++
268 lines
10 KiB
C++
// 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.
|
|
|
|
#include "lib/jxl/dec_cache.h"
|
|
|
|
#include <jxl/memory_manager.h>
|
|
|
|
#include "lib/jxl/base/status.h"
|
|
#include "lib/jxl/blending.h"
|
|
#include "lib/jxl/common.h" // JXL_HIGH_PRECISION
|
|
#include "lib/jxl/render_pipeline/stage_blending.h"
|
|
#include "lib/jxl/render_pipeline/stage_chroma_upsampling.h"
|
|
#include "lib/jxl/render_pipeline/stage_cms.h"
|
|
#include "lib/jxl/render_pipeline/stage_epf.h"
|
|
#include "lib/jxl/render_pipeline/stage_from_linear.h"
|
|
#include "lib/jxl/render_pipeline/stage_gaborish.h"
|
|
#include "lib/jxl/render_pipeline/stage_noise.h"
|
|
#include "lib/jxl/render_pipeline/stage_patches.h"
|
|
#include "lib/jxl/render_pipeline/stage_splines.h"
|
|
#include "lib/jxl/render_pipeline/stage_spot.h"
|
|
#include "lib/jxl/render_pipeline/stage_to_linear.h"
|
|
#include "lib/jxl/render_pipeline/stage_tone_mapping.h"
|
|
#include "lib/jxl/render_pipeline/stage_upsampling.h"
|
|
#include "lib/jxl/render_pipeline/stage_write.h"
|
|
#include "lib/jxl/render_pipeline/stage_xyb.h"
|
|
#include "lib/jxl/render_pipeline/stage_ycbcr.h"
|
|
|
|
namespace jxl {
|
|
|
|
Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header,
|
|
const ImageMetadata* metadata,
|
|
ImageBundle* decoded,
|
|
PipelineOptions options) {
|
|
JxlMemoryManager* memory_manager = this->memory_manager();
|
|
size_t num_c = 3 + frame_header.nonserialized_metadata->m.num_extra_channels;
|
|
if (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0) {
|
|
num_c += 3;
|
|
}
|
|
|
|
if (frame_header.CanBeReferenced()) {
|
|
// Necessary so that SetInputSizes() can allocate output buffers as needed.
|
|
frame_storage_for_referencing = ImageBundle(memory_manager, metadata);
|
|
}
|
|
|
|
RenderPipeline::Builder builder(memory_manager, num_c);
|
|
|
|
if (options.use_slow_render_pipeline) {
|
|
builder.UseSimpleImplementation();
|
|
}
|
|
|
|
if (!frame_header.chroma_subsampling.Is444()) {
|
|
for (size_t c = 0; c < 3; c++) {
|
|
if (frame_header.chroma_subsampling.HShift(c) != 0) {
|
|
builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/true));
|
|
}
|
|
if (frame_header.chroma_subsampling.VShift(c) != 0) {
|
|
builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/false));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (frame_header.loop_filter.gab) {
|
|
builder.AddStage(GetGaborishStage(frame_header.loop_filter));
|
|
}
|
|
|
|
{
|
|
const LoopFilter& lf = frame_header.loop_filter;
|
|
if (lf.epf_iters >= 3) {
|
|
builder.AddStage(GetEPFStage(lf, sigma, 0));
|
|
}
|
|
if (lf.epf_iters >= 1) {
|
|
builder.AddStage(GetEPFStage(lf, sigma, 1));
|
|
}
|
|
if (lf.epf_iters >= 2) {
|
|
builder.AddStage(GetEPFStage(lf, sigma, 2));
|
|
}
|
|
}
|
|
|
|
bool late_ec_upsample = frame_header.upsampling != 1;
|
|
for (auto ecups : frame_header.extra_channel_upsampling) {
|
|
if (ecups != frame_header.upsampling) {
|
|
// If patches are applied, either frame_header.upsampling == 1 or
|
|
// late_ec_upsample is true.
|
|
late_ec_upsample = false;
|
|
}
|
|
}
|
|
|
|
if (!late_ec_upsample) {
|
|
for (size_t ec = 0; ec < frame_header.extra_channel_upsampling.size();
|
|
ec++) {
|
|
if (frame_header.extra_channel_upsampling[ec] != 1) {
|
|
builder.AddStage(GetUpsamplingStage(
|
|
frame_header.nonserialized_metadata->transform_data, 3 + ec,
|
|
CeilLog2Nonzero(frame_header.extra_channel_upsampling[ec])));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((frame_header.flags & FrameHeader::kPatches) != 0) {
|
|
builder.AddStage(
|
|
GetPatchesStage(&shared->image_features.patches,
|
|
3 + shared->metadata->m.num_extra_channels));
|
|
}
|
|
if ((frame_header.flags & FrameHeader::kSplines) != 0) {
|
|
builder.AddStage(GetSplineStage(&shared->image_features.splines));
|
|
}
|
|
|
|
if (frame_header.upsampling != 1) {
|
|
size_t nb_channels =
|
|
3 +
|
|
(late_ec_upsample ? frame_header.extra_channel_upsampling.size() : 0);
|
|
for (size_t c = 0; c < nb_channels; c++) {
|
|
builder.AddStage(GetUpsamplingStage(
|
|
frame_header.nonserialized_metadata->transform_data, c,
|
|
CeilLog2Nonzero(frame_header.upsampling)));
|
|
}
|
|
}
|
|
if (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0) {
|
|
builder.AddStage(GetConvolveNoiseStage(num_c - 3));
|
|
builder.AddStage(GetAddNoiseStage(shared->image_features.noise_params,
|
|
shared->cmap, num_c - 3));
|
|
}
|
|
if (frame_header.dc_level != 0) {
|
|
builder.AddStage(GetWriteToImage3FStage(
|
|
memory_manager, &shared_storage.dc_frames[frame_header.dc_level - 1]));
|
|
}
|
|
|
|
if (frame_header.CanBeReferenced() &&
|
|
frame_header.save_before_color_transform) {
|
|
builder.AddStage(GetWriteToImageBundleStage(&frame_storage_for_referencing,
|
|
output_encoding_info));
|
|
}
|
|
|
|
bool has_alpha = false;
|
|
size_t alpha_c = 0;
|
|
for (size_t i = 0; i < metadata->extra_channel_info.size(); i++) {
|
|
if (metadata->extra_channel_info[i].type == ExtraChannel::kAlpha) {
|
|
has_alpha = true;
|
|
alpha_c = 3 + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fast_xyb_srgb8_conversion) {
|
|
#if !JXL_HIGH_PRECISION
|
|
JXL_ASSERT(!NeedsBlending(frame_header));
|
|
JXL_ASSERT(!frame_header.CanBeReferenced() ||
|
|
frame_header.save_before_color_transform);
|
|
JXL_ASSERT(!options.render_spotcolors ||
|
|
!metadata->Find(ExtraChannel::kSpotColor));
|
|
bool is_rgba = (main_output.format.num_channels == 4);
|
|
uint8_t* rgb_output = reinterpret_cast<uint8_t*>(main_output.buffer);
|
|
builder.AddStage(GetFastXYBTosRGB8Stage(rgb_output, main_output.stride,
|
|
width, height, is_rgba, has_alpha,
|
|
alpha_c));
|
|
#endif
|
|
} else {
|
|
bool linear = false;
|
|
if (frame_header.color_transform == ColorTransform::kYCbCr) {
|
|
builder.AddStage(GetYCbCrStage());
|
|
} else if (frame_header.color_transform == ColorTransform::kXYB) {
|
|
builder.AddStage(GetXYBStage(output_encoding_info));
|
|
if (output_encoding_info.color_encoding.GetColorSpace() !=
|
|
ColorSpace::kXYB) {
|
|
linear = true;
|
|
}
|
|
} // Nothing to do for kNone.
|
|
|
|
if (options.coalescing && NeedsBlending(frame_header)) {
|
|
if (linear) {
|
|
builder.AddStage(GetFromLinearStage(output_encoding_info));
|
|
linear = false;
|
|
}
|
|
builder.AddStage(GetBlendingStage(frame_header, this,
|
|
output_encoding_info.color_encoding));
|
|
}
|
|
|
|
if (options.coalescing && frame_header.CanBeReferenced() &&
|
|
!frame_header.save_before_color_transform) {
|
|
if (linear) {
|
|
builder.AddStage(GetFromLinearStage(output_encoding_info));
|
|
linear = false;
|
|
}
|
|
builder.AddStage(GetWriteToImageBundleStage(
|
|
&frame_storage_for_referencing, output_encoding_info));
|
|
}
|
|
|
|
if (options.render_spotcolors &&
|
|
frame_header.nonserialized_metadata->m.Find(ExtraChannel::kSpotColor)) {
|
|
for (size_t i = 0; i < metadata->extra_channel_info.size(); i++) {
|
|
// Don't use Find() because there may be multiple spot color channels.
|
|
const ExtraChannelInfo& eci = metadata->extra_channel_info[i];
|
|
if (eci.type == ExtraChannel::kSpotColor) {
|
|
builder.AddStage(GetSpotColorStage(3 + i, eci.spot_color));
|
|
}
|
|
}
|
|
}
|
|
|
|
auto tone_mapping_stage = GetToneMappingStage(output_encoding_info);
|
|
if (tone_mapping_stage) {
|
|
if (!linear) {
|
|
auto to_linear_stage = GetToLinearStage(output_encoding_info);
|
|
if (!to_linear_stage) {
|
|
if (!output_encoding_info.cms_set) {
|
|
return JXL_FAILURE("Cannot tonemap this colorspace without a CMS");
|
|
}
|
|
auto cms_stage = GetCmsStage(output_encoding_info);
|
|
if (cms_stage) {
|
|
builder.AddStage(std::move(cms_stage));
|
|
}
|
|
} else {
|
|
builder.AddStage(std::move(to_linear_stage));
|
|
}
|
|
linear = true;
|
|
}
|
|
builder.AddStage(std::move(tone_mapping_stage));
|
|
}
|
|
|
|
if (linear) {
|
|
const size_t channels_src =
|
|
(output_encoding_info.orig_color_encoding.IsCMYK()
|
|
? 4
|
|
: output_encoding_info.orig_color_encoding.Channels());
|
|
const size_t channels_dst =
|
|
output_encoding_info.color_encoding.Channels();
|
|
bool mixing_color_and_grey = (channels_dst != channels_src);
|
|
if ((output_encoding_info.color_encoding_is_original) ||
|
|
(!output_encoding_info.cms_set) || mixing_color_and_grey) {
|
|
// in those cases we only need a linear stage in other cases we attempt
|
|
// to obtain a cms stage: the cases are
|
|
// - output_encoding_info.color_encoding_is_original: no cms stage
|
|
// needed because it would be a no-op
|
|
// - !output_encoding_info.cms_set: can't use the cms, so no point in
|
|
// trying to add a cms stage
|
|
// - mixing_color_and_grey: cms stage can't handle that
|
|
// TODO(firsching): remove "mixing_color_and_grey" condition after
|
|
// adding support for greyscale to cms stage.
|
|
builder.AddStage(GetFromLinearStage(output_encoding_info));
|
|
} else {
|
|
if (!output_encoding_info.linear_color_encoding.CreateICC()) {
|
|
return JXL_FAILURE("Failed to create ICC");
|
|
}
|
|
auto cms_stage = GetCmsStage(output_encoding_info);
|
|
if (cms_stage) {
|
|
builder.AddStage(std::move(cms_stage));
|
|
}
|
|
}
|
|
linear = false;
|
|
}
|
|
(void)linear;
|
|
|
|
if (main_output.callback.IsPresent() || main_output.buffer) {
|
|
builder.AddStage(GetWriteToOutputStage(
|
|
main_output, width, height, has_alpha, unpremul_alpha, alpha_c,
|
|
undo_orientation, extra_output, memory_manager));
|
|
} else {
|
|
builder.AddStage(
|
|
GetWriteToImageBundleStage(decoded, output_encoding_info));
|
|
}
|
|
}
|
|
JXL_ASSIGN_OR_RETURN(render_pipeline,
|
|
std::move(builder).Finalize(shared->frame_dim));
|
|
return render_pipeline->IsInitialized();
|
|
}
|
|
|
|
} // namespace jxl
|