Coverage Report

Created: 2025-06-16 07:00

/src/libjxl/lib/jxl/image_bundle.cc
Line
Count
Source (jump to first uncovered line)
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/image_bundle.h"
7
8
#include <cstddef>
9
#include <cstdint>
10
#include <utility>
11
#include <vector>
12
13
#include "lib/jxl/base/printf_macros.h"
14
#include "lib/jxl/base/status.h"
15
#include "lib/jxl/color_encoding_internal.h"
16
#include "lib/jxl/image.h"
17
#include "lib/jxl/image_metadata.h"
18
19
namespace jxl {
20
21
0
Status ImageBundle::ShrinkTo(size_t xsize, size_t ysize) {
22
0
  if (HasColor()) {
23
0
    JXL_RETURN_IF_ERROR(color_.ShrinkTo(xsize, ysize));
24
0
  }
25
0
  for (ImageF& ec : extra_channels_) {
26
0
    JXL_RETURN_IF_ERROR(ec.ShrinkTo(xsize, ysize));
27
0
  }
28
0
  return true;
29
0
}
30
31
// Called by all other SetFrom*.
32
Status ImageBundle::SetFromImage(Image3F&& color,
33
32.0k
                                 const ColorEncoding& c_current) {
34
32.0k
  JXL_ENSURE(color.xsize() != 0 && color.ysize() != 0);
35
32.0k
  JXL_ENSURE(metadata_->color_encoding.IsGray() == c_current.IsGray());
36
32.0k
  color_ = std::move(color);
37
32.0k
  c_current_ = c_current;
38
32.0k
  JXL_RETURN_IF_ERROR(VerifySizes());
39
32.0k
  return true;
40
32.0k
}
41
42
0
Status ImageBundle::VerifyMetadata() const {
43
0
  JXL_ENSURE(!c_current_.ICC().empty());
44
0
  JXL_ENSURE(metadata_->color_encoding.IsGray() == IsGray());
45
46
0
  if (metadata_->HasAlpha()) {
47
0
    const ImageF* a = alpha();
48
0
    if (a->xsize() == 0) {
49
0
      return JXL_UNREACHABLE("MD alpha_bits %u IB alpha %" PRIuS " x %" PRIuS
50
0
                             "\n",
51
0
                             metadata_->GetAlphaBits(), a->xsize(), a->ysize());
52
0
    }
53
0
  }
54
0
  const uint32_t alpha_bits = metadata_->GetAlphaBits();
55
0
  JXL_ENSURE(alpha_bits <= 32);
56
57
  // metadata_->num_extra_channels may temporarily differ from
58
  // extra_channels_.size(), e.g. after SetAlpha. They are synced by the next
59
  // call to VisitFields.
60
0
  return true;
61
0
}
62
63
32.0k
Status ImageBundle::VerifySizes() const {
64
32.0k
  const size_t xs = xsize();
65
32.0k
  const size_t ys = ysize();
66
67
32.0k
  if (HasExtraChannels()) {
68
0
    JXL_ENSURE(xs != 0 && ys != 0);
69
0
    for (const ImageF& ec : extra_channels_) {
70
0
      JXL_ENSURE(ec.xsize() == xs);
71
0
      JXL_ENSURE(ec.ysize() == ys);
72
0
    }
73
0
  }
74
32.0k
  return true;
75
32.0k
}
76
77
0
size_t ImageBundle::DetectRealBitdepth() const {
78
0
  return metadata_->bit_depth.bits_per_sample;
79
80
  // TODO(lode): let this function return lower bit depth if possible, e.g.
81
  // return 8 bits in case the original image came from a 16-bit PNG that
82
  // was in fact representable as 8-bit PNG. Ensure that the implementation
83
  // returns 16 if e.g. two consecutive 16-bit values appeared in the original
84
  // image (such as 32768 and 32769), take into account that e.g. the values
85
  // 3-bit can represent is not a superset of the values 2-bit can represent,
86
  // and there may be slight imprecisions in the floating point image.
87
0
}
88
89
0
const ImageF* ImageBundle::black() const {
90
0
  if (!HasBlack()) return nullptr;
91
0
  const size_t ec = metadata_->Find(ExtraChannel::kBlack) -
92
0
                    metadata_->extra_channel_info.data();
93
0
  JXL_DASSERT(ec < extra_channels_.size());
94
0
  return &extra_channels_[ec];
95
0
}
96
0
const ImageF* ImageBundle::alpha() const {
97
0
  if (!HasAlpha()) return nullptr;
98
0
  const size_t ec = metadata_->Find(ExtraChannel::kAlpha) -
99
0
                    metadata_->extra_channel_info.data();
100
0
  JXL_DASSERT(ec < extra_channels_.size());
101
0
  return &extra_channels_[ec];
102
0
}
103
0
ImageF* ImageBundle::alpha() {
104
0
  if (!HasAlpha()) return nullptr;
105
0
  const size_t ec = metadata_->Find(ExtraChannel::kAlpha) -
106
0
                    metadata_->extra_channel_info.data();
107
0
  JXL_DASSERT(ec < extra_channels_.size());
108
0
  return &extra_channels_[ec];
109
0
}
110
111
0
Status ImageBundle::SetAlpha(ImageF&& alpha) {
112
0
  const ExtraChannelInfo* eci = metadata_->Find(ExtraChannel::kAlpha);
113
  // Must call SetAlphaBits first, otherwise we don't know which channel index
114
0
  JXL_ENSURE(eci != nullptr);
115
0
  JXL_ENSURE(alpha.xsize() != 0 && alpha.ysize() != 0);
116
0
  if (extra_channels_.size() < metadata_->extra_channel_info.size()) {
117
    // TODO(jon): get rid of this case
118
0
    extra_channels_.insert(
119
0
        extra_channels_.begin() + (eci - metadata_->extra_channel_info.data()),
120
0
        std::move(alpha));
121
0
  } else {
122
0
    extra_channels_[eci - metadata_->extra_channel_info.data()] =
123
0
        std::move(alpha);
124
0
  }
125
  // num_extra_channels is automatically set in visitor
126
0
  JXL_RETURN_IF_ERROR(VerifySizes());
127
0
  return true;
128
0
}
129
130
0
Status ImageBundle::SetExtraChannels(std::vector<ImageF>&& extra_channels) {
131
0
  for (const ImageF& plane : extra_channels) {
132
0
    JXL_ENSURE(plane.xsize() != 0 && plane.ysize() != 0);
133
0
  }
134
0
  extra_channels_ = std::move(extra_channels);
135
0
  JXL_RETURN_IF_ERROR(VerifySizes());
136
0
  return true;
137
0
}
138
}  // namespace jxl