fune/third_party/jpeg-xl/lib/jxl/blending.cc

163 lines
6.8 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/blending.h"
#include <jxl/memory_manager.h>
#include <cstddef>
#include <cstring>
#include <vector>
#include "lib/jxl/alpha.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/dec_patch_dictionary.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_metadata.h"
namespace jxl {
bool NeedsBlending(const FrameHeader& frame_header) {
if (!(frame_header.frame_type == FrameType::kRegularFrame ||
frame_header.frame_type == FrameType::kSkipProgressive)) {
return false;
}
const auto& info = frame_header.blending_info;
bool replace_all = (info.mode == BlendMode::kReplace);
for (const auto& ec_i : frame_header.extra_channel_blending_info) {
if (ec_i.mode != BlendMode::kReplace) {
replace_all = false;
}
}
// Replace the full frame: nothing to do.
if (!frame_header.custom_size_or_origin && replace_all) {
return false;
}
return true;
}
Status PerformBlending(
JxlMemoryManager* memory_manager, const float* const* bg,
const float* const* fg, float* const* out, size_t x0, size_t xsize,
const PatchBlending& color_blending, const PatchBlending* ec_blending,
const std::vector<ExtraChannelInfo>& extra_channel_info) {
bool has_alpha = false;
size_t num_ec = extra_channel_info.size();
for (size_t i = 0; i < num_ec; i++) {
if (extra_channel_info[i].type == jxl::ExtraChannel::kAlpha) {
has_alpha = true;
break;
}
}
JXL_ASSIGN_OR_RETURN(ImageF tmp,
ImageF::Create(memory_manager, xsize, 3 + num_ec));
// Blend extra channels first so that we use the pre-blending alpha.
for (size_t i = 0; i < num_ec; i++) {
if (ec_blending[i].mode == PatchBlendMode::kAdd) {
for (size_t x = 0; x < xsize; x++) {
tmp.Row(3 + i)[x] = bg[3 + i][x + x0] + fg[3 + i][x + x0];
}
} else if (ec_blending[i].mode == PatchBlendMode::kBlendAbove) {
size_t alpha = ec_blending[i].alpha_channel;
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(bg[3 + i] + x0, bg[3 + alpha] + x0, fg[3 + i] + x0,
fg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
is_premultiplied, ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kBlendBelow) {
size_t alpha = ec_blending[i].alpha_channel;
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(fg[3 + i] + x0, fg[3 + alpha] + x0, bg[3 + i] + x0,
bg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
is_premultiplied, ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kAlphaWeightedAddAbove) {
size_t alpha = ec_blending[i].alpha_channel;
PerformAlphaWeightedAdd(bg[3 + i] + x0, fg[3 + i] + x0,
fg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kAlphaWeightedAddBelow) {
size_t alpha = ec_blending[i].alpha_channel;
PerformAlphaWeightedAdd(fg[3 + i] + x0, bg[3 + i] + x0,
bg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kMul) {
PerformMulBlending(bg[3 + i] + x0, fg[3 + i] + x0, tmp.Row(3 + i), xsize,
ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kReplace) {
memcpy(tmp.Row(3 + i), fg[3 + i] + x0, xsize * sizeof(**fg));
} else if (ec_blending[i].mode == PatchBlendMode::kNone) {
if (xsize) memcpy(tmp.Row(3 + i), bg[3 + i] + x0, xsize * sizeof(**fg));
} else {
JXL_UNREACHABLE("new PatchBlendMode?");
}
}
size_t alpha = color_blending.alpha_channel;
if (color_blending.mode == PatchBlendMode::kAdd ||
(color_blending.mode == PatchBlendMode::kAlphaWeightedAddAbove &&
!has_alpha) ||
(color_blending.mode == PatchBlendMode::kAlphaWeightedAddBelow &&
!has_alpha)) {
for (int p = 0; p < 3; p++) {
float* out = tmp.Row(p);
for (size_t x = 0; x < xsize; x++) {
out[x] = bg[p][x + x0] + fg[p][x + x0];
}
}
} else if (color_blending.mode == PatchBlendMode::kBlendAbove
// blend without alpha is just replace
&& has_alpha) {
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(
{bg[0] + x0, bg[1] + x0, bg[2] + x0, bg[3 + alpha] + x0},
{fg[0] + x0, fg[1] + x0, fg[2] + x0, fg[3 + alpha] + x0},
{tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize,
is_premultiplied, color_blending.clamp);
} else if (color_blending.mode == PatchBlendMode::kBlendBelow
// blend without alpha is just replace
&& has_alpha) {
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(
{fg[0] + x0, fg[1] + x0, fg[2] + x0, fg[3 + alpha] + x0},
{bg[0] + x0, bg[1] + x0, bg[2] + x0, bg[3 + alpha] + x0},
{tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize,
is_premultiplied, color_blending.clamp);
} else if (color_blending.mode == PatchBlendMode::kAlphaWeightedAddAbove) {
JXL_DASSERT(has_alpha);
for (size_t c = 0; c < 3; c++) {
PerformAlphaWeightedAdd(bg[c] + x0, fg[c] + x0, fg[3 + alpha] + x0,
tmp.Row(c), xsize, color_blending.clamp);
}
} else if (color_blending.mode == PatchBlendMode::kAlphaWeightedAddBelow) {
JXL_DASSERT(has_alpha);
for (size_t c = 0; c < 3; c++) {
PerformAlphaWeightedAdd(fg[c] + x0, bg[c] + x0, bg[3 + alpha] + x0,
tmp.Row(c), xsize, color_blending.clamp);
}
} else if (color_blending.mode == PatchBlendMode::kMul) {
for (int p = 0; p < 3; p++) {
PerformMulBlending(bg[p] + x0, fg[p] + x0, tmp.Row(p), xsize,
color_blending.clamp);
}
} else if (color_blending.mode == PatchBlendMode::kReplace ||
color_blending.mode == PatchBlendMode::kBlendAbove ||
color_blending.mode == PatchBlendMode::kBlendBelow) { // kReplace
for (size_t p = 0; p < 3; p++) {
memcpy(tmp.Row(p), fg[p] + x0, xsize * sizeof(**fg));
}
} else if (color_blending.mode == PatchBlendMode::kNone) {
for (size_t p = 0; p < 3; p++) {
memcpy(tmp.Row(p), bg[p] + x0, xsize * sizeof(**fg));
}
} else {
JXL_UNREACHABLE("new PatchBlendMode?");
}
for (size_t i = 0; i < 3 + num_ec; i++) {
if (xsize != 0) memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out));
}
return true;
}
} // namespace jxl