/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 |