summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/enc_comparator.cc
blob: cbdd0f78d9f46d4ee738d9e5f717cd1801773c22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 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/enc_comparator.h"

#include <stddef.h>
#include <stdint.h>

#include <algorithm>

#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/profiler.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/enc_gamma_correct.h"
#include "lib/jxl/enc_image_bundle.h"

namespace jxl {
namespace {

// color is linear, but blending happens in gamma-compressed space using
// (gamma-compressed) grayscale background color, alpha image represents
// weights of the sRGB colors in the [0 .. (1 << bit_depth) - 1] interval,
// output image is in linear space.
void AlphaBlend(const Image3F& in, const size_t c, float background_linear,
                const ImageF& alpha, Image3F* out) {
  const float background = LinearToSrgb8Direct(background_linear);

  for (size_t y = 0; y < out->ysize(); ++y) {
    const float* JXL_RESTRICT row_a = alpha.ConstRow(y);
    const float* JXL_RESTRICT row_i = in.ConstPlaneRow(c, y);
    float* JXL_RESTRICT row_o = out->PlaneRow(c, y);
    for (size_t x = 0; x < out->xsize(); ++x) {
      const float a = row_a[x];
      if (a <= 0.f) {
        row_o[x] = background_linear;
      } else if (a >= 1.f) {
        row_o[x] = row_i[x];
      } else {
        const float w_fg = a;
        const float w_bg = 1.0f - w_fg;
        const float fg = w_fg * LinearToSrgb8Direct(row_i[x]);
        const float bg = w_bg * background;
        row_o[x] = Srgb8ToLinearDirect(fg + bg);
      }
    }
  }
}

void AlphaBlend(float background_linear, ImageBundle* io_linear_srgb) {
  // No alpha => all opaque.
  if (!io_linear_srgb->HasAlpha()) return;

  for (size_t c = 0; c < 3; ++c) {
    AlphaBlend(*io_linear_srgb->color(), c, background_linear,
               *io_linear_srgb->alpha(), io_linear_srgb->color());
  }
}

float ComputeScoreImpl(const ImageBundle& rgb0, const ImageBundle& rgb1,
                       Comparator* comparator, ImageF* distmap) {
  JXL_CHECK(comparator->SetReferenceImage(rgb0));
  float score;
  JXL_CHECK(comparator->CompareWith(rgb1, distmap, &score));
  return score;
}

}  // namespace

float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
                   Comparator* comparator, const JxlCmsInterface& cms,
                   ImageF* diffmap, ThreadPool* pool) {
  PROFILER_FUNC;
  // Convert to linear sRGB (unless already in that space)
  ImageMetadata metadata0 = *rgb0.metadata();
  ImageBundle store0(&metadata0);
  const ImageBundle* linear_srgb0;
  JXL_CHECK(TransformIfNeeded(rgb0, ColorEncoding::LinearSRGB(rgb0.IsGray()),
                              cms, pool, &store0, &linear_srgb0));
  ImageMetadata metadata1 = *rgb1.metadata();
  ImageBundle store1(&metadata1);
  const ImageBundle* linear_srgb1;
  JXL_CHECK(TransformIfNeeded(rgb1, ColorEncoding::LinearSRGB(rgb1.IsGray()),
                              cms, pool, &store1, &linear_srgb1));

  // No alpha: skip blending, only need a single call to Butteraugli.
  if (!rgb0.HasAlpha() && !rgb1.HasAlpha()) {
    return ComputeScoreImpl(*linear_srgb0, *linear_srgb1, comparator, diffmap);
  }

  // Blend on black and white backgrounds

  const float black = 0.0f;
  ImageBundle blended_black0 = linear_srgb0->Copy();
  ImageBundle blended_black1 = linear_srgb1->Copy();
  AlphaBlend(black, &blended_black0);
  AlphaBlend(black, &blended_black1);

  const float white = 1.0f;
  ImageBundle blended_white0 = linear_srgb0->Copy();
  ImageBundle blended_white1 = linear_srgb1->Copy();

  AlphaBlend(white, &blended_white0);
  AlphaBlend(white, &blended_white1);

  ImageF diffmap_black, diffmap_white;
  const float dist_black = ComputeScoreImpl(blended_black0, blended_black1,
                                            comparator, &diffmap_black);
  const float dist_white = ComputeScoreImpl(blended_white0, blended_white1,
                                            comparator, &diffmap_white);

  // diffmap and return values are the max of diffmap_black/white.
  if (diffmap != nullptr) {
    const size_t xsize = rgb0.xsize();
    const size_t ysize = rgb0.ysize();
    *diffmap = ImageF(xsize, ysize);
    for (size_t y = 0; y < ysize; ++y) {
      const float* JXL_RESTRICT row_black = diffmap_black.ConstRow(y);
      const float* JXL_RESTRICT row_white = diffmap_white.ConstRow(y);
      float* JXL_RESTRICT row_out = diffmap->Row(y);
      for (size_t x = 0; x < xsize; ++x) {
        row_out[x] = std::max(row_black[x], row_white[x]);
      }
    }
  }
  return std::max(dist_black, dist_white);
}

}  // namespace jxl