Coverage Report

Created: 2025-10-12 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/blending.cc
Line
Count
Source
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
30.4k
bool NeedsBlending(const FrameHeader& frame_header) {
24
30.4k
  if (!(frame_header.frame_type == FrameType::kRegularFrame ||
25
15.2k
        frame_header.frame_type == FrameType::kSkipProgressive)) {
26
13.0k
    return false;
27
13.0k
  }
28
17.3k
  const auto& info = frame_header.blending_info;
29
17.3k
  bool replace_all = (info.mode == BlendMode::kReplace);
30
17.3k
  for (const auto& ec_i : frame_header.extra_channel_blending_info) {
31
9.48k
    if (ec_i.mode != BlendMode::kReplace) {
32
3.32k
      replace_all = false;
33
3.32k
    }
34
9.48k
  }
35
  // Replace the full frame: nothing to do.
36
17.3k
  if (!frame_header.custom_size_or_origin && replace_all) {
37
8.93k
    return false;
38
8.93k
  }
39
8.42k
  return true;
40
17.3k
}
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
30.5M
    const std::vector<ExtraChannelInfo>& extra_channel_info) {
47
30.5M
  bool has_alpha = false;
48
30.5M
  size_t num_ec = extra_channel_info.size();
49
30.5M
  for (size_t i = 0; i < num_ec; i++) {
50
135k
    if (extra_channel_info[i].type == jxl::ExtraChannel::kAlpha) {
51
100k
      has_alpha = true;
52
100k
      break;
53
100k
    }
54
135k
  }
55
30.5M
  JXL_ASSIGN_OR_RETURN(ImageF tmp,
56
30.5M
                       ImageF::Create(memory_manager, xsize, 3 + num_ec));
57
  // Blend extra channels first so that we use the pre-blending alpha.
58
30.8M
  for (size_t i = 0; i < num_ec; i++) {
59
291k
    switch (ec_blending[i].mode) {
60
15.9k
      case PatchBlendMode::kAdd:
61
1.17M
        for (size_t x = 0; x < xsize; x++) {
62
1.16M
          tmp.Row(3 + i)[x] = bg[3 + i][x + x0] + fg[3 + i][x + x0];
63
1.16M
        }
64
15.9k
        continue;
65
66
59.8k
      case PatchBlendMode::kBlendAbove: {
67
59.8k
        size_t alpha = ec_blending[i].alpha_channel;
68
59.8k
        bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
69
59.8k
        PerformAlphaBlending(bg[3 + i] + x0, bg[3 + alpha] + x0, fg[3 + i] + x0,
70
59.8k
                             fg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
71
59.8k
                             is_premultiplied, ec_blending[i].clamp);
72
59.8k
        continue;
73
0
      }
74
75
0
      case PatchBlendMode::kBlendBelow: {
76
0
        size_t alpha = ec_blending[i].alpha_channel;
77
0
        bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
78
0
        PerformAlphaBlending(fg[3 + i] + x0, fg[3 + alpha] + x0, bg[3 + i] + x0,
79
0
                             bg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
80
0
                             is_premultiplied, ec_blending[i].clamp);
81
0
        continue;
82
0
      }
83
84
15.0k
      case PatchBlendMode::kAlphaWeightedAddAbove: {
85
15.0k
        size_t alpha = ec_blending[i].alpha_channel;
86
15.0k
        PerformAlphaWeightedAdd(bg[3 + i] + x0, fg[3 + i] + x0,
87
15.0k
                                fg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
88
15.0k
                                ec_blending[i].clamp);
89
15.0k
        continue;
90
0
      }
91
92
0
      case PatchBlendMode::kAlphaWeightedAddBelow: {
93
0
        size_t alpha = ec_blending[i].alpha_channel;
94
0
        PerformAlphaWeightedAdd(fg[3 + i] + x0, bg[3 + i] + x0,
95
0
                                bg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
96
0
                                ec_blending[i].clamp);
97
0
        continue;
98
0
      }
99
100
60.1k
      case PatchBlendMode::kMul:
101
60.1k
        PerformMulBlending(bg[3 + i] + x0, fg[3 + i] + x0, tmp.Row(3 + i),
102
60.1k
                           xsize, ec_blending[i].clamp);
103
60.1k
        continue;
104
105
140k
      case PatchBlendMode::kReplace:
106
140k
        if (xsize) memcpy(tmp.Row(3 + i), fg[3 + i] + x0, xsize * sizeof(**fg));
107
140k
        continue;
108
109
173
      case PatchBlendMode::kNone:
110
173
        if (xsize) memcpy(tmp.Row(3 + i), bg[3 + i] + x0, xsize * sizeof(**fg));
111
173
        continue;
112
291k
    }
113
291k
  }
114
30.5M
  size_t alpha = color_blending.alpha_channel;
115
116
30.5M
  const auto add = [&]() {
117
120M
    for (int p = 0; p < 3; p++) {
118
90.3M
      float* out = tmp.Row(p);
119
3.21G
      for (size_t x = 0; x < xsize; x++) {
120
3.12G
        out[x] = bg[p][x + x0] + fg[p][x + x0];
121
3.12G
      }
122
90.3M
    }
123
30.1M
  };
124
125
30.5M
  const auto blend_weighted = [&](const float* const* bottom,
126
30.5M
                                  const float* const* top) {
127
21.5k
    bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
128
21.5k
    PerformAlphaBlending(
129
21.5k
        {bottom[0] + x0, bottom[1] + x0, bottom[2] + x0,
130
21.5k
         bottom[3 + alpha] + x0},
131
21.5k
        {top[0] + x0, top[1] + x0, top[2] + x0, top[3 + alpha] + x0},
132
21.5k
        {tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize,
133
21.5k
        is_premultiplied, color_blending.clamp);
134
21.5k
  };
135
136
30.5M
  const auto add_weighted = [&](const float* const* bottom,
137
30.5M
                                const float* const* top) {
138
84.0k
    for (size_t c = 0; c < 3; c++) {
139
63.0k
      PerformAlphaWeightedAdd(bottom[c] + x0, top[c] + x0, top[3 + alpha] + x0,
140
63.0k
                              tmp.Row(c), xsize, color_blending.clamp);
141
63.0k
    }
142
21.0k
  };
143
144
30.5M
  const auto copy = [&](const float* const* src) {
145
1.41M
    for (size_t p = 0; p < 3; p++) {
146
1.05M
      memcpy(tmp.Row(p), src[p] + x0, xsize * sizeof(**src));
147
1.05M
    }
148
352k
  };
149
150
30.5M
  switch (color_blending.mode) {
151
30.0M
    case PatchBlendMode::kAdd:
152
30.0M
      add();
153
30.0M
      break;
154
155
104k
    case PatchBlendMode::kAlphaWeightedAddAbove:
156
104k
      has_alpha ? add_weighted(bg, fg) : add();
157
104k
      break;
158
159
1.64k
    case PatchBlendMode::kAlphaWeightedAddBelow:
160
1.64k
      has_alpha ? add_weighted(fg, bg) : add();
161
1.64k
      break;
162
163
290k
    case PatchBlendMode::kBlendAbove:
164
290k
      has_alpha ? blend_weighted(bg, fg) : copy(fg);
165
290k
      break;
166
167
1.45k
    case PatchBlendMode::kBlendBelow:
168
1.45k
      has_alpha ? blend_weighted(fg, bg) : copy(fg);
169
1.45k
      break;
170
171
13.2k
    case PatchBlendMode::kMul:
172
53.1k
      for (int p = 0; p < 3; p++) {
173
39.8k
        PerformMulBlending(bg[p] + x0, fg[p] + x0, tmp.Row(p), xsize,
174
39.8k
                           color_blending.clamp);
175
39.8k
      }
176
13.2k
      break;
177
178
33.2k
    case PatchBlendMode::kReplace:
179
33.2k
      copy(fg);
180
33.2k
      break;
181
182
49.2k
    case PatchBlendMode::kNone:
183
49.2k
      copy(bg);
184
30.5M
  }
185
186
122M
  for (size_t i = 0; i < 3 + num_ec; i++) {
187
91.9M
    if (xsize != 0) memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out));
188
91.9M
  }
189
30.5M
  return true;
190
30.5M
}
191
192
}  // namespace jxl