/src/libjxl/lib/extras/packed_image.cc
Line | Count | Source |
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 | | // Helper class for storing external (int or float, interleaved) images. This is |
7 | | // the common format used by other libraries and in the libjxl API. |
8 | | |
9 | | #include "packed_image.h" |
10 | | |
11 | | #include <jxl/codestream_header.h> |
12 | | #include <jxl/encode.h> |
13 | | #include <jxl/types.h> |
14 | | |
15 | | #include <algorithm> |
16 | | #include <cstddef> |
17 | | #include <cstdint> |
18 | | #include <cstdlib> |
19 | | #include <cstring> |
20 | | #include <functional> |
21 | | #include <memory> |
22 | | #include <string> |
23 | | #include <utility> |
24 | | #include <vector> |
25 | | |
26 | | #include "lib/jxl/base/byte_order.h" |
27 | | #include "lib/jxl/base/common.h" |
28 | | #include "lib/jxl/base/status.h" |
29 | | |
30 | | namespace jxl { |
31 | | namespace extras { |
32 | | |
33 | | // Class representing an interleaved image with a bunch of channels. |
34 | | StatusOr<PackedImage> PackedImage::Create(size_t xsize, size_t ysize, |
35 | 64.1k | const JxlPixelFormat& format) { |
36 | 64.1k | JXL_ASSIGN_OR_RETURN(size_t stride, CalcStride(format, xsize)); |
37 | 64.1k | size_t pixels_size = ysize * stride; |
38 | 64.1k | if ((pixels_size / stride) != ysize) { |
39 | 0 | return JXL_FAILURE("Image too big"); |
40 | 0 | } |
41 | 64.1k | PackedImage image(xsize, ysize, format, stride); |
42 | 64.1k | if (!image.pixels()) { |
43 | | // TODO(szabadka): use specialized OOM error code |
44 | 0 | return JXL_FAILURE("Failed to allocate memory for image"); |
45 | 0 | } |
46 | 64.1k | return image; |
47 | 64.1k | } |
48 | | |
49 | 0 | StatusOr<PackedImage> PackedImage::Copy() const { |
50 | | // Resulting copy_stride have to be less or equal to original -> always ok. |
51 | 0 | JXL_ASSIGN_OR_RETURN(size_t copy_stride, CalcStride(format, xsize)); |
52 | 0 | PackedImage copy(xsize, ysize, format, copy_stride); |
53 | 0 | const uint8_t* orig_pixels = reinterpret_cast<const uint8_t*>(pixels()); |
54 | 0 | uint8_t* copy_pixels = reinterpret_cast<uint8_t*>(copy.pixels()); |
55 | 0 | if (stride == copy_stride) { |
56 | | // Same stride -> copy in one go. |
57 | 0 | memcpy(copy_pixels, orig_pixels, ysize * stride); |
58 | 0 | } else { |
59 | | // Otherwise, copy row-wise. |
60 | 0 | JXL_DASSERT(copy_stride < stride); |
61 | 0 | for (size_t y = 0; y < ysize; ++y) { |
62 | 0 | memcpy(copy_pixels + y * copy_stride, orig_pixels + y * stride, |
63 | 0 | copy_stride); |
64 | 0 | } |
65 | 0 | } |
66 | 0 | return copy; |
67 | 0 | } |
68 | | |
69 | 16.4k | Status PackedImage::ValidateDataType(JxlDataType data_type) { |
70 | 16.4k | if ((data_type != JXL_TYPE_UINT8) && (data_type != JXL_TYPE_UINT16) && |
71 | 16.4k | (data_type != JXL_TYPE_FLOAT) && (data_type != JXL_TYPE_FLOAT16)) { |
72 | 0 | return JXL_FAILURE("Unhandled data type: %d", static_cast<int>(data_type)); |
73 | 0 | } |
74 | 16.4k | return true; |
75 | 16.4k | } |
76 | | |
77 | 144k | size_t PackedImage::BitsPerChannel(JxlDataType data_type) { |
78 | 144k | switch (data_type) { |
79 | 118 | case JXL_TYPE_UINT8: |
80 | 118 | return 8; |
81 | 147 | case JXL_TYPE_UINT16: |
82 | 147 | return 16; |
83 | 144k | case JXL_TYPE_FLOAT: |
84 | 144k | return 32; |
85 | 0 | case JXL_TYPE_FLOAT16: |
86 | 0 | return 16; |
87 | 0 | default: |
88 | 0 | JXL_DEBUG_ABORT("Unreachable"); |
89 | 0 | return 0; |
90 | 144k | } |
91 | 144k | } |
92 | | |
93 | | // Logical resize; use Copy() for storage reallocation, if necessary. |
94 | 0 | Status PackedImage::ShrinkTo(size_t new_xsize, size_t new_ysize) { |
95 | 0 | if (new_xsize > xsize || new_ysize > ysize) { |
96 | 0 | return JXL_FAILURE("Cannot shrink PackedImage to a larger size"); |
97 | 0 | } |
98 | 0 | xsize = new_xsize; |
99 | 0 | ysize = new_ysize; |
100 | 0 | return true; |
101 | 0 | } |
102 | | |
103 | | PackedImage::PackedImage(size_t xsize, size_t ysize, |
104 | | const JxlPixelFormat& format, size_t stride) |
105 | 64.1k | : xsize(xsize), |
106 | 64.1k | ysize(ysize), |
107 | 64.1k | stride(stride), |
108 | 64.1k | format(format), |
109 | 64.1k | pixels_size(ysize * stride), |
110 | 64.1k | pixels_(malloc(std::max<size_t>(1, pixels_size)), free) { |
111 | 64.1k | bytes_per_channel_ = BitsPerChannel(format.data_type) / jxl::kBitsPerByte; |
112 | 64.1k | pixel_stride_ = format.num_channels * bytes_per_channel_; |
113 | 64.1k | swap_endianness_ = SwapEndianness(format.endianness); |
114 | 64.1k | } |
115 | | |
116 | | StatusOr<size_t> PackedImage::CalcStride(const JxlPixelFormat& format, |
117 | 64.1k | size_t xsize) { |
118 | 64.1k | size_t multiplier = (BitsPerChannel(format.data_type) * format.num_channels / |
119 | 64.1k | jxl::kBitsPerByte); |
120 | 64.1k | size_t stride; |
121 | 64.1k | if (!SafeMul(xsize, multiplier, stride)) { |
122 | 0 | return JXL_FAILURE("Image too big"); |
123 | 0 | } |
124 | 64.1k | if (!SafeRoundUpTo(stride, format.align, stride)) { |
125 | 0 | return JXL_FAILURE("Image too big"); |
126 | 0 | } |
127 | 64.1k | return stride; |
128 | 64.1k | } |
129 | | |
130 | 11.4k | PackedFrame::PackedFrame(PackedImage&& image) : color(std::move(image)) {} |
131 | | |
132 | 43.5k | PackedFrame::PackedFrame(PackedFrame&& other) = default; |
133 | | |
134 | 0 | PackedFrame& PackedFrame::operator=(PackedFrame&& other) = default; |
135 | | |
136 | 55.0k | PackedFrame::~PackedFrame() = default; |
137 | | |
138 | | StatusOr<PackedFrame> PackedFrame::Create(size_t xsize, size_t ysize, |
139 | 11.3k | const JxlPixelFormat& format) { |
140 | 11.3k | JXL_ASSIGN_OR_RETURN(PackedImage image, |
141 | 11.3k | PackedImage::Create(xsize, ysize, format)); |
142 | 11.3k | PackedFrame frame(std::move(image)); |
143 | 11.3k | return frame; |
144 | 11.3k | } |
145 | | |
146 | 0 | StatusOr<PackedFrame> PackedFrame::Copy() const { |
147 | 0 | JXL_ASSIGN_OR_RETURN( |
148 | 0 | PackedFrame copy, |
149 | 0 | PackedFrame::Create(color.xsize, color.ysize, color.format)); |
150 | 0 | copy.frame_info = frame_info; |
151 | 0 | copy.name = name; |
152 | 0 | JXL_ASSIGN_OR_RETURN(copy.color, color.Copy()); |
153 | 0 | for (const auto& ec : extra_channels) { |
154 | 0 | JXL_ASSIGN_OR_RETURN(PackedImage ec_copy, ec.Copy()); |
155 | 0 | copy.extra_channels.emplace_back(std::move(ec_copy)); |
156 | 0 | } |
157 | 0 | return copy; |
158 | 0 | } |
159 | | |
160 | | // Logical resize; use Copy() for storage reallocation, if necessary. |
161 | 0 | Status PackedFrame::ShrinkTo(size_t new_xsize, size_t new_ysize) { |
162 | 0 | JXL_RETURN_IF_ERROR(color.ShrinkTo(new_xsize, new_ysize)); |
163 | 0 | for (auto& ec : extra_channels) { |
164 | 0 | JXL_RETURN_IF_ERROR(ec.ShrinkTo(new_xsize, new_ysize)); |
165 | 0 | } |
166 | 0 | frame_info.layer_info.xsize = new_xsize; |
167 | 0 | frame_info.layer_info.ysize = new_ysize; |
168 | 0 | return true; |
169 | 0 | } |
170 | | |
171 | | ChunkedPackedFrame::ChunkedPackedFrame( |
172 | | size_t xsize, size_t ysize, |
173 | | std::function<JxlChunkedFrameInputSource()> get_input_source) |
174 | 0 | : xsize(xsize), |
175 | 0 | ysize(ysize), |
176 | 0 | get_input_source_(std::move(get_input_source)) { |
177 | 0 | const auto input_source = get_input_source_(); |
178 | 0 | input_source.get_color_channels_pixel_format(input_source.opaque, &format); |
179 | 0 | } |
180 | | |
181 | 37.4k | PackedPixelFile::PackedPixelFile() { JxlEncoderInitBasicInfo(&info); }; |
182 | | |
183 | 0 | Status PackedPixelFile::ShrinkTo(size_t new_xsize, size_t new_ysize) { |
184 | 0 | for (auto& frame : frames) { |
185 | 0 | JXL_RETURN_IF_ERROR(frame.ShrinkTo(new_xsize, new_ysize)); |
186 | 0 | } |
187 | 0 | info.xsize = new_xsize; |
188 | 0 | info.ysize = new_ysize; |
189 | 0 | return true; |
190 | 0 | } |
191 | | |
192 | | } // namespace extras |
193 | | } // namespace jxl |