Coverage Report

Created: 2026-09-28 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/enc_comparator.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/enc_comparator.h"
7
8
#include <jxl/cms_interface.h>
9
#include <jxl/memory_manager.h>
10
11
#include <algorithm>
12
#include <cstddef>
13
#include <memory>
14
15
#include "lib/jxl/base/common.h"
16
#include "lib/jxl/base/compiler_specific.h"
17
#include "lib/jxl/base/data_parallel.h"
18
#include "lib/jxl/base/status.h"
19
#include "lib/jxl/color_encoding_internal.h"
20
#include "lib/jxl/enc_gamma_correct.h"
21
#include "lib/jxl/enc_image_bundle.h"
22
#include "lib/jxl/image.h"
23
#include "lib/jxl/image_bundle.h"
24
#include "lib/jxl/image_metadata.h"
25
26
namespace jxl {
27
namespace {
28
29
// color is linear, but blending happens in gamma-compressed space using
30
// (gamma-compressed) grayscale background color, alpha image represents
31
// weights of the sRGB colors in the [0 .. (1 << bit_depth) - 1] interval,
32
// output image is in linear space.
33
void AlphaBlend(const Image3F& in, const size_t c, float background_linear,
34
0
                const ImageF& alpha, Image3F* out) {
35
0
  const float background = LinearToSrgb8Direct(background_linear);
36
37
0
  for (size_t y = 0; y < out->ysize(); ++y) {
38
0
    const float* JXL_RESTRICT row_a = alpha.ConstRow(y);
39
0
    const float* JXL_RESTRICT row_i = in.ConstPlaneRow(c, y);
40
0
    float* JXL_RESTRICT row_o = out->PlaneRow(c, y);
41
0
    for (size_t x = 0; x < out->xsize(); ++x) {
42
0
      const float a = row_a[x];
43
0
      if (a <= 0.f) {
44
0
        row_o[x] = background_linear;
45
0
      } else if (a >= 1.f) {
46
0
        row_o[x] = row_i[x];
47
0
      } else {
48
0
        const float w_fg = a;
49
0
        const float w_bg = 1.0f - w_fg;
50
0
        const float fg = w_fg * LinearToSrgb8Direct(row_i[x]);
51
0
        const float bg = w_bg * background;
52
0
        row_o[x] = Srgb8ToLinearDirect(fg + bg);
53
0
      }
54
0
    }
55
0
  }
56
0
}
57
58
0
void AlphaBlend(float background_linear, ImageBundle* io_linear_srgb) {
59
  // No alpha => all opaque.
60
0
  if (!io_linear_srgb->HasAlpha()) return;
61
62
0
  for (size_t c = 0; c < 3; ++c) {
63
0
    AlphaBlend(*io_linear_srgb->color(), c, background_linear,
64
0
               *io_linear_srgb->alpha(), io_linear_srgb->color());
65
0
  }
66
0
}
67
68
Status ComputeScoreImpl(const ImageBundle& rgb0, const ImageBundle& rgb1,
69
0
                        Comparator* comparator, ImageF* distmap, float& score) {
70
0
  JXL_RETURN_IF_ERROR(comparator->SetReferenceImage(rgb0));
71
0
  JXL_RETURN_IF_ERROR(comparator->CompareWith(rgb1, distmap, &score));
72
0
  return true;
73
0
}
74
75
}  // namespace
76
77
Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
78
                    Comparator* comparator, const JxlCmsInterface& cms,
79
                    float* score, ImageF* diffmap, ThreadPool* pool,
80
0
                    bool ignore_alpha) {
81
0
  JxlMemoryManager* memory_manager = rgb0.memory_manager();
82
  // Convert to linear sRGB (unless already in that space)
83
0
  auto metadata0 = jxl::make_unique<ImageMetadata>();
84
0
  *metadata0 = *rgb0.metadata();
85
0
  auto store0 = jxl::make_unique<ImageBundle>(memory_manager, metadata0.get());
86
0
  const ImageBundle* linear_srgb0;
87
0
  JXL_RETURN_IF_ERROR(
88
0
      TransformIfNeeded(rgb0, ColorEncoding::LinearSRGB(rgb0.IsGray()), cms,
89
0
                        pool, store0.get(), &linear_srgb0));
90
0
  auto metadata1 = jxl::make_unique<ImageMetadata>();
91
0
  *metadata1 = *rgb1.metadata();
92
0
  auto store1 = jxl::make_unique<ImageBundle>(memory_manager, metadata1.get());
93
0
  const ImageBundle* linear_srgb1;
94
0
  JXL_RETURN_IF_ERROR(
95
0
      TransformIfNeeded(rgb1, ColorEncoding::LinearSRGB(rgb1.IsGray()), cms,
96
0
                        pool, store1.get(), &linear_srgb1));
97
98
  // No alpha: skip blending, only need a single call to Butteraugli.
99
0
  if (ignore_alpha || (!rgb0.HasAlpha() && !rgb1.HasAlpha())) {
100
0
    JXL_RETURN_IF_ERROR(ComputeScoreImpl(*linear_srgb0, *linear_srgb1,
101
0
                                         comparator, diffmap, *score));
102
0
    return true;
103
0
  }
104
105
  // Blend on black and white backgrounds
106
107
0
  ImageF diffmap_black;
108
0
  float dist_black;
109
0
  {
110
0
    const float black = 0.0f;
111
0
    JXL_ASSIGN_OR_RETURN(ImageBundle blended_black0, linear_srgb0->Copy());
112
0
    JXL_ASSIGN_OR_RETURN(ImageBundle blended_black1, linear_srgb1->Copy());
113
0
    AlphaBlend(black, &blended_black0);
114
0
    AlphaBlend(black, &blended_black1);
115
0
    JXL_RETURN_IF_ERROR(ComputeScoreImpl(blended_black0, blended_black1,
116
0
                                         comparator, &diffmap_black,
117
0
                                         dist_black));
118
0
  }
119
120
0
  ImageF diffmap_white;
121
0
  float dist_white;
122
0
  {
123
0
    const float white = 1.0f;
124
0
    JXL_ASSIGN_OR_RETURN(ImageBundle blended_white0, linear_srgb0->Copy());
125
0
    JXL_ASSIGN_OR_RETURN(ImageBundle blended_white1, linear_srgb1->Copy());
126
0
    AlphaBlend(white, &blended_white0);
127
0
    AlphaBlend(white, &blended_white1);
128
0
    JXL_RETURN_IF_ERROR(ComputeScoreImpl(blended_white0, blended_white1,
129
0
                                         comparator, &diffmap_white,
130
0
                                         dist_white));
131
0
  }
132
133
  // diffmap and return values are the max of diffmap_black/white.
134
0
  if (diffmap != nullptr) {
135
0
    const size_t xsize = rgb0.xsize();
136
0
    const size_t ysize = rgb0.ysize();
137
0
    JXL_ASSIGN_OR_RETURN(*diffmap,
138
0
                         ImageF::Create(memory_manager, xsize, ysize));
139
0
    for (size_t y = 0; y < ysize; ++y) {
140
0
      const float* JXL_RESTRICT row_black = diffmap_black.ConstRow(y);
141
0
      const float* JXL_RESTRICT row_white = diffmap_white.ConstRow(y);
142
0
      float* JXL_RESTRICT row_out = diffmap->Row(y);
143
0
      for (size_t x = 0; x < xsize; ++x) {
144
0
        row_out[x] = std::max(row_black[x], row_white[x]);
145
0
      }
146
0
    }
147
0
  }
148
0
  *score = std::max(dist_black, dist_white);
149
0
  return true;
150
0
}
151
152
}  // namespace jxl