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