blending.cc (6288B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include "lib/jxl/blending.h" 7 8 #include <jxl/memory_manager.h> 9 10 #include <cstddef> 11 #include <cstring> 12 #include <vector> 13 14 #include "lib/jxl/alpha.h" 15 #include "lib/jxl/base/status.h" 16 #include "lib/jxl/dec_patch_dictionary.h" 17 #include "lib/jxl/frame_header.h" 18 #include "lib/jxl/image.h" 19 #include "lib/jxl/image_metadata.h" 20 21 namespace jxl { 22 23 bool NeedsBlending(const FrameHeader& frame_header) { 24 if (!(frame_header.frame_type == FrameType::kRegularFrame || 25 frame_header.frame_type == FrameType::kSkipProgressive)) { 26 return false; 27 } 28 const auto& info = frame_header.blending_info; 29 bool replace_all = (info.mode == BlendMode::kReplace); 30 for (const auto& ec_i : frame_header.extra_channel_blending_info) { 31 if (ec_i.mode != BlendMode::kReplace) { 32 replace_all = false; 33 } 34 } 35 // Replace the full frame: nothing to do. 36 if (!frame_header.custom_size_or_origin && replace_all) { 37 return false; 38 } 39 return true; 40 } 41 42 Status PerformBlending( 43 JxlMemoryManager* memory_manager, const float* const* bg, 44 const float* const* fg, float* const* out, size_t x0, size_t xsize, 45 const PatchBlending& color_blending, const PatchBlending* ec_blending, 46 const std::vector<ExtraChannelInfo>& extra_channel_info) { 47 bool has_alpha = false; 48 size_t num_ec = extra_channel_info.size(); 49 for (size_t i = 0; i < num_ec; i++) { 50 if (extra_channel_info[i].type == jxl::ExtraChannel::kAlpha) { 51 has_alpha = true; 52 break; 53 } 54 } 55 JXL_ASSIGN_OR_RETURN(ImageF tmp, 56 ImageF::Create(memory_manager, xsize, 3 + num_ec)); 57 // Blend extra channels first so that we use the pre-blending alpha. 58 for (size_t i = 0; i < num_ec; i++) { 59 switch (ec_blending[i].mode) { 60 case PatchBlendMode::kAdd: 61 for (size_t x = 0; x < xsize; x++) { 62 tmp.Row(3 + i)[x] = bg[3 + i][x + x0] + fg[3 + i][x + x0]; 63 } 64 continue; 65 66 case PatchBlendMode::kBlendAbove: { 67 size_t alpha = ec_blending[i].alpha_channel; 68 bool is_premultiplied = extra_channel_info[alpha].alpha_associated; 69 PerformAlphaBlending(bg[3 + i] + x0, bg[3 + alpha] + x0, fg[3 + i] + x0, 70 fg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 71 is_premultiplied, ec_blending[i].clamp); 72 continue; 73 } 74 75 case PatchBlendMode::kBlendBelow: { 76 size_t alpha = ec_blending[i].alpha_channel; 77 bool is_premultiplied = extra_channel_info[alpha].alpha_associated; 78 PerformAlphaBlending(fg[3 + i] + x0, fg[3 + alpha] + x0, bg[3 + i] + x0, 79 bg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 80 is_premultiplied, ec_blending[i].clamp); 81 continue; 82 } 83 84 case PatchBlendMode::kAlphaWeightedAddAbove: { 85 size_t alpha = ec_blending[i].alpha_channel; 86 PerformAlphaWeightedAdd(bg[3 + i] + x0, fg[3 + i] + x0, 87 fg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 88 ec_blending[i].clamp); 89 continue; 90 } 91 92 case PatchBlendMode::kAlphaWeightedAddBelow: { 93 size_t alpha = ec_blending[i].alpha_channel; 94 PerformAlphaWeightedAdd(fg[3 + i] + x0, bg[3 + i] + x0, 95 bg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 96 ec_blending[i].clamp); 97 continue; 98 } 99 100 case PatchBlendMode::kMul: 101 PerformMulBlending(bg[3 + i] + x0, fg[3 + i] + x0, tmp.Row(3 + i), 102 xsize, ec_blending[i].clamp); 103 continue; 104 105 case PatchBlendMode::kReplace: 106 if (xsize) memcpy(tmp.Row(3 + i), fg[3 + i] + x0, xsize * sizeof(**fg)); 107 continue; 108 109 case PatchBlendMode::kNone: 110 if (xsize) memcpy(tmp.Row(3 + i), bg[3 + i] + x0, xsize * sizeof(**fg)); 111 continue; 112 } 113 } 114 size_t alpha = color_blending.alpha_channel; 115 116 const auto add = [&]() { 117 for (int p = 0; p < 3; p++) { 118 float* out = tmp.Row(p); 119 for (size_t x = 0; x < xsize; x++) { 120 out[x] = bg[p][x + x0] + fg[p][x + x0]; 121 } 122 } 123 }; 124 125 const auto blend_weighted = [&](const float* const* bottom, 126 const float* const* top) { 127 bool is_premultiplied = extra_channel_info[alpha].alpha_associated; 128 PerformAlphaBlending( 129 {bottom[0] + x0, bottom[1] + x0, bottom[2] + x0, 130 bottom[3 + alpha] + x0}, 131 {top[0] + x0, top[1] + x0, top[2] + x0, top[3 + alpha] + x0}, 132 {tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize, 133 is_premultiplied, color_blending.clamp); 134 }; 135 136 const auto add_weighted = [&](const float* const* bottom, 137 const float* const* top) { 138 for (size_t c = 0; c < 3; c++) { 139 PerformAlphaWeightedAdd(bottom[c] + x0, top[c] + x0, top[3 + alpha] + x0, 140 tmp.Row(c), xsize, color_blending.clamp); 141 } 142 }; 143 144 const auto copy = [&](const float* const* src) { 145 for (size_t p = 0; p < 3; p++) { 146 memcpy(tmp.Row(p), src[p] + x0, xsize * sizeof(**src)); 147 } 148 }; 149 150 switch (color_blending.mode) { 151 case PatchBlendMode::kAdd: 152 add(); 153 break; 154 155 case PatchBlendMode::kAlphaWeightedAddAbove: 156 has_alpha ? add_weighted(bg, fg) : add(); 157 break; 158 159 case PatchBlendMode::kAlphaWeightedAddBelow: 160 has_alpha ? add_weighted(fg, bg) : add(); 161 break; 162 163 case PatchBlendMode::kBlendAbove: 164 has_alpha ? blend_weighted(bg, fg) : copy(fg); 165 break; 166 167 case PatchBlendMode::kBlendBelow: 168 has_alpha ? blend_weighted(fg, bg) : copy(fg); 169 break; 170 171 case PatchBlendMode::kMul: 172 for (int p = 0; p < 3; p++) { 173 PerformMulBlending(bg[p] + x0, fg[p] + x0, tmp.Row(p), xsize, 174 color_blending.clamp); 175 } 176 break; 177 178 case PatchBlendMode::kReplace: 179 copy(fg); 180 break; 181 182 case PatchBlendMode::kNone: 183 copy(bg); 184 } 185 186 for (size_t i = 0; i < 3 + num_ec; i++) { 187 if (xsize != 0) memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out)); 188 } 189 return true; 190 } 191 192 } // namespace jxl