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 <limits>
9 #include <utility>
10
11 #include "lib/jxl/alpha.h"
12 #include "lib/jxl/base/byte_order.h"
13 #include "lib/jxl/base/padded_bytes.h"
14 #include "lib/jxl/base/profiler.h"
15 #include "lib/jxl/codec_in_out.h"
16 #include "lib/jxl/color_management.h"
17 #include "lib/jxl/fields.h"
18 #include "lib/jxl/luminance.h"
19
20 namespace jxl {
21
ShrinkTo(size_t xsize,size_t ysize)22 void ImageBundle::ShrinkTo(size_t xsize, size_t ysize) {
23 if (HasColor()) color_.ShrinkTo(xsize, ysize);
24 for (ImageF& ec : extra_channels_) {
25 ec.ShrinkTo(xsize, ysize);
26 }
27 }
28
29 // Called by all other SetFrom*.
SetFromImage(Image3F && color,const ColorEncoding & c_current)30 void ImageBundle::SetFromImage(Image3F&& color,
31 const ColorEncoding& c_current) {
32 JXL_CHECK(color.xsize() != 0 && color.ysize() != 0);
33 JXL_CHECK(metadata_->color_encoding.IsGray() == c_current.IsGray());
34 color_ = std::move(color);
35 c_current_ = c_current;
36 VerifySizes();
37 }
38
VerifyMetadata() const39 void ImageBundle::VerifyMetadata() const {
40 JXL_CHECK(!c_current_.ICC().empty());
41 JXL_CHECK(metadata_->color_encoding.IsGray() == IsGray());
42
43 if (metadata_->HasAlpha() && alpha().xsize() == 0) {
44 JXL_ABORT("MD alpha_bits %u IB alpha %zu x %zu\n",
45 metadata_->GetAlphaBits(), alpha().xsize(), alpha().ysize());
46 }
47 const uint32_t alpha_bits = metadata_->GetAlphaBits();
48 JXL_CHECK(alpha_bits <= 32);
49
50 // metadata_->num_extra_channels may temporarily differ from
51 // extra_channels_.size(), e.g. after SetAlpha. They are synced by the next
52 // call to VisitFields.
53 }
54
VerifySizes() const55 void ImageBundle::VerifySizes() const {
56 const size_t xs = xsize();
57 const size_t ys = ysize();
58
59 if (HasExtraChannels()) {
60 JXL_CHECK(xs != 0 && ys != 0);
61 for (const ImageF& ec : extra_channels_) {
62 JXL_CHECK(ec.xsize() == xs);
63 JXL_CHECK(ec.ysize() == ys);
64 }
65 }
66 }
67
DetectRealBitdepth() const68 size_t ImageBundle::DetectRealBitdepth() const {
69 return metadata_->bit_depth.bits_per_sample;
70
71 // TODO(lode): let this function return lower bit depth if possible, e.g.
72 // return 8 bits in case the original image came from a 16-bit PNG that
73 // was in fact representable as 8-bit PNG. Ensure that the implementation
74 // returns 16 if e.g. two consecutive 16-bit values appeared in the original
75 // image (such as 32768 and 32769), take into account that e.g. the values
76 // 3-bit can represent is not a superset of the values 2-bit can represent,
77 // and there may be slight imprecisions in the floating point image.
78 }
79
alpha() const80 const ImageF& ImageBundle::alpha() const {
81 JXL_ASSERT(HasAlpha());
82 const size_t ec = metadata_->Find(ExtraChannel::kAlpha) -
83 metadata_->extra_channel_info.data();
84 JXL_ASSERT(ec < extra_channels_.size());
85 return extra_channels_[ec];
86 }
alpha()87 ImageF* ImageBundle::alpha() {
88 JXL_ASSERT(HasAlpha());
89 const size_t ec = metadata_->Find(ExtraChannel::kAlpha) -
90 metadata_->extra_channel_info.data();
91 JXL_ASSERT(ec < extra_channels_.size());
92 return &extra_channels_[ec];
93 }
94
depth() const95 const ImageF& ImageBundle::depth() const {
96 JXL_ASSERT(HasDepth());
97 const size_t ec = metadata_->Find(ExtraChannel::kDepth) -
98 metadata_->extra_channel_info.data();
99 JXL_ASSERT(ec < extra_channels_.size());
100 return extra_channels_[ec];
101 }
102
SetAlpha(ImageF && alpha,bool alpha_is_premultiplied)103 void ImageBundle::SetAlpha(ImageF&& alpha, bool alpha_is_premultiplied) {
104 const ExtraChannelInfo* eci = metadata_->Find(ExtraChannel::kAlpha);
105 // Must call SetAlphaBits first, otherwise we don't know which channel index
106 JXL_CHECK(eci != nullptr);
107 JXL_CHECK(alpha.xsize() != 0 && alpha.ysize() != 0);
108 JXL_CHECK(eci->alpha_associated == alpha_is_premultiplied);
109 extra_channels_.insert(
110 extra_channels_.begin() + (eci - metadata_->extra_channel_info.data()),
111 std::move(alpha));
112 // num_extra_channels is automatically set in visitor
113 VerifySizes();
114 }
PremultiplyAlpha()115 void ImageBundle::PremultiplyAlpha() {
116 if (!HasAlpha()) return;
117 if (!HasColor()) return;
118 const ExtraChannelInfo* eci = metadata_->Find(ExtraChannel::kAlpha);
119 if (eci->alpha_associated) return; // already premultiplied
120 JXL_CHECK(color_.ysize() == alpha()->ysize());
121 JXL_CHECK(color_.xsize() == alpha()->xsize());
122 for (size_t y = 0; y < color_.ysize(); y++) {
123 ::jxl::PremultiplyAlpha(color_.PlaneRow(0, y), color_.PlaneRow(1, y),
124 color_.PlaneRow(2, y), alpha()->Row(y),
125 color_.xsize());
126 }
127 }
UnpremultiplyAlpha()128 void ImageBundle::UnpremultiplyAlpha() {
129 if (!HasAlpha()) return;
130 if (!HasColor()) return;
131 const ExtraChannelInfo* eci = metadata_->Find(ExtraChannel::kAlpha);
132 if (!eci->alpha_associated) return; // already unpremultiplied
133 JXL_CHECK(color_.ysize() == alpha()->ysize());
134 JXL_CHECK(color_.xsize() == alpha()->xsize());
135 for (size_t y = 0; y < color_.ysize(); y++) {
136 ::jxl::UnpremultiplyAlpha(color_.PlaneRow(0, y), color_.PlaneRow(1, y),
137 color_.PlaneRow(2, y), alpha()->Row(y),
138 color_.xsize());
139 }
140 }
141
SetExtraChannels(std::vector<ImageF> && extra_channels)142 void ImageBundle::SetExtraChannels(std::vector<ImageF>&& extra_channels) {
143 for (const ImageF& plane : extra_channels) {
144 JXL_CHECK(plane.xsize() != 0 && plane.ysize() != 0);
145 }
146 extra_channels_ = std::move(extra_channels);
147 VerifySizes();
148 }
149 } // namespace jxl
150