blending.cc (6653B)
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 "lib/jxl/alpha.h" 9 10 namespace jxl { 11 12 bool NeedsBlending(const FrameHeader& frame_header) { 13 if (!(frame_header.frame_type == FrameType::kRegularFrame || 14 frame_header.frame_type == FrameType::kSkipProgressive)) { 15 return false; 16 } 17 const auto& info = frame_header.blending_info; 18 bool replace_all = (info.mode == BlendMode::kReplace); 19 for (const auto& ec_i : frame_header.extra_channel_blending_info) { 20 if (ec_i.mode != BlendMode::kReplace) { 21 replace_all = false; 22 } 23 } 24 // Replace the full frame: nothing to do. 25 if (!frame_header.custom_size_or_origin && replace_all) { 26 return false; 27 } 28 return true; 29 } 30 31 Status PerformBlending( 32 const float* const* bg, const float* const* fg, float* const* out, 33 size_t x0, size_t xsize, const PatchBlending& color_blending, 34 const PatchBlending* ec_blending, 35 const std::vector<ExtraChannelInfo>& extra_channel_info) { 36 bool has_alpha = false; 37 size_t num_ec = extra_channel_info.size(); 38 for (size_t i = 0; i < num_ec; i++) { 39 if (extra_channel_info[i].type == jxl::ExtraChannel::kAlpha) { 40 has_alpha = true; 41 break; 42 } 43 } 44 JXL_ASSIGN_OR_RETURN(ImageF tmp, ImageF::Create(xsize, 3 + num_ec)); 45 // Blend extra channels first so that we use the pre-blending alpha. 46 for (size_t i = 0; i < num_ec; i++) { 47 if (ec_blending[i].mode == PatchBlendMode::kAdd) { 48 for (size_t x = 0; x < xsize; x++) { 49 tmp.Row(3 + i)[x] = bg[3 + i][x + x0] + fg[3 + i][x + x0]; 50 } 51 } else if (ec_blending[i].mode == PatchBlendMode::kBlendAbove) { 52 size_t alpha = ec_blending[i].alpha_channel; 53 bool is_premultiplied = extra_channel_info[alpha].alpha_associated; 54 PerformAlphaBlending(bg[3 + i] + x0, bg[3 + alpha] + x0, fg[3 + i] + x0, 55 fg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 56 is_premultiplied, ec_blending[i].clamp); 57 } else if (ec_blending[i].mode == PatchBlendMode::kBlendBelow) { 58 size_t alpha = ec_blending[i].alpha_channel; 59 bool is_premultiplied = extra_channel_info[alpha].alpha_associated; 60 PerformAlphaBlending(fg[3 + i] + x0, fg[3 + alpha] + x0, bg[3 + i] + x0, 61 bg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 62 is_premultiplied, ec_blending[i].clamp); 63 } else if (ec_blending[i].mode == PatchBlendMode::kAlphaWeightedAddAbove) { 64 size_t alpha = ec_blending[i].alpha_channel; 65 PerformAlphaWeightedAdd(bg[3 + i] + x0, fg[3 + i] + x0, 66 fg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 67 ec_blending[i].clamp); 68 } else if (ec_blending[i].mode == PatchBlendMode::kAlphaWeightedAddBelow) { 69 size_t alpha = ec_blending[i].alpha_channel; 70 PerformAlphaWeightedAdd(fg[3 + i] + x0, bg[3 + i] + x0, 71 bg[3 + alpha] + x0, tmp.Row(3 + i), xsize, 72 ec_blending[i].clamp); 73 } else if (ec_blending[i].mode == PatchBlendMode::kMul) { 74 PerformMulBlending(bg[3 + i] + x0, fg[3 + i] + x0, tmp.Row(3 + i), xsize, 75 ec_blending[i].clamp); 76 } else if (ec_blending[i].mode == PatchBlendMode::kReplace) { 77 memcpy(tmp.Row(3 + i), fg[3 + i] + x0, xsize * sizeof(**fg)); 78 } else if (ec_blending[i].mode == PatchBlendMode::kNone) { 79 if (xsize) memcpy(tmp.Row(3 + i), bg[3 + i] + x0, xsize * sizeof(**fg)); 80 } else { 81 JXL_UNREACHABLE("new PatchBlendMode?"); 82 } 83 } 84 size_t alpha = color_blending.alpha_channel; 85 86 if (color_blending.mode == PatchBlendMode::kAdd || 87 (color_blending.mode == PatchBlendMode::kAlphaWeightedAddAbove && 88 !has_alpha) || 89 (color_blending.mode == PatchBlendMode::kAlphaWeightedAddBelow && 90 !has_alpha)) { 91 for (int p = 0; p < 3; p++) { 92 float* out = tmp.Row(p); 93 for (size_t x = 0; x < xsize; x++) { 94 out[x] = bg[p][x + x0] + fg[p][x + x0]; 95 } 96 } 97 } else if (color_blending.mode == PatchBlendMode::kBlendAbove 98 // blend without alpha is just replace 99 && has_alpha) { 100 bool is_premultiplied = extra_channel_info[alpha].alpha_associated; 101 PerformAlphaBlending( 102 {bg[0] + x0, bg[1] + x0, bg[2] + x0, bg[3 + alpha] + x0}, 103 {fg[0] + x0, fg[1] + x0, fg[2] + x0, fg[3 + alpha] + x0}, 104 {tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize, 105 is_premultiplied, color_blending.clamp); 106 } else if (color_blending.mode == PatchBlendMode::kBlendBelow 107 // blend without alpha is just replace 108 && has_alpha) { 109 bool is_premultiplied = extra_channel_info[alpha].alpha_associated; 110 PerformAlphaBlending( 111 {fg[0] + x0, fg[1] + x0, fg[2] + x0, fg[3 + alpha] + x0}, 112 {bg[0] + x0, bg[1] + x0, bg[2] + x0, bg[3 + alpha] + x0}, 113 {tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize, 114 is_premultiplied, color_blending.clamp); 115 } else if (color_blending.mode == PatchBlendMode::kAlphaWeightedAddAbove) { 116 JXL_DASSERT(has_alpha); 117 for (size_t c = 0; c < 3; c++) { 118 PerformAlphaWeightedAdd(bg[c] + x0, fg[c] + x0, fg[3 + alpha] + x0, 119 tmp.Row(c), xsize, color_blending.clamp); 120 } 121 } else if (color_blending.mode == PatchBlendMode::kAlphaWeightedAddBelow) { 122 JXL_DASSERT(has_alpha); 123 for (size_t c = 0; c < 3; c++) { 124 PerformAlphaWeightedAdd(fg[c] + x0, bg[c] + x0, bg[3 + alpha] + x0, 125 tmp.Row(c), xsize, color_blending.clamp); 126 } 127 } else if (color_blending.mode == PatchBlendMode::kMul) { 128 for (int p = 0; p < 3; p++) { 129 PerformMulBlending(bg[p] + x0, fg[p] + x0, tmp.Row(p), xsize, 130 color_blending.clamp); 131 } 132 } else if (color_blending.mode == PatchBlendMode::kReplace || 133 color_blending.mode == PatchBlendMode::kBlendAbove || 134 color_blending.mode == PatchBlendMode::kBlendBelow) { // kReplace 135 for (size_t p = 0; p < 3; p++) { 136 memcpy(tmp.Row(p), fg[p] + x0, xsize * sizeof(**fg)); 137 } 138 } else if (color_blending.mode == PatchBlendMode::kNone) { 139 for (size_t p = 0; p < 3; p++) { 140 memcpy(tmp.Row(p), bg[p] + x0, xsize * sizeof(**fg)); 141 } 142 } else { 143 JXL_UNREACHABLE("new PatchBlendMode?"); 144 } 145 for (size_t i = 0; i < 3 + num_ec; i++) { 146 if (xsize != 0) memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out)); 147 } 148 return true; 149 } 150 151 } // namespace jxl