diff options
Diffstat (limited to 'lib/extras/packed_image.h')
-rw-r--r-- | lib/extras/packed_image.h | 198 |
1 files changed, 183 insertions, 15 deletions
diff --git a/lib/extras/packed_image.h b/lib/extras/packed_image.h index 1296472..d3ba9ce 100644 --- a/lib/extras/packed_image.h +++ b/lib/extras/packed_image.h @@ -9,20 +9,24 @@ // Helper class for storing external (int or float, interleaved) images. This is // the common format used by other libraries and in the libjxl API. +#include <jxl/codestream_header.h> +#include <jxl/encode.h> +#include <jxl/types.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <algorithm> +#include <cmath> #include <memory> +#include <set> #include <string> #include <vector> -#include "jxl/codestream_header.h" -#include "jxl/encode.h" -#include "jxl/types.h" -#include "lib/jxl/common.h" +#include "lib/jxl/base/byte_order.h" +#include "lib/jxl/base/common.h" +#include "lib/jxl/base/status.h" namespace jxl { namespace extras { @@ -33,9 +37,26 @@ class PackedImage { PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format) : PackedImage(xsize, ysize, format, CalcStride(format, xsize)) {} + PackedImage Copy() const { + PackedImage copy(xsize, ysize, format); + memcpy(reinterpret_cast<uint8_t*>(copy.pixels()), + reinterpret_cast<const uint8_t*>(pixels()), pixels_size); + return copy; + } + // The interleaved pixels as defined in the storage format. void* pixels() const { return pixels_.get(); } + uint8_t* pixels(size_t y, size_t x, size_t c) const { + return (reinterpret_cast<uint8_t*>(pixels_.get()) + y * stride + + x * pixel_stride_ + c * bytes_per_channel_); + } + + const uint8_t* const_pixels(size_t y, size_t x, size_t c) const { + return (reinterpret_cast<const uint8_t*>(pixels_.get()) + y * stride + + x * pixel_stride_ + c * bytes_per_channel_); + } + // The image size in pixels. size_t xsize; size_t ysize; @@ -47,10 +68,7 @@ class PackedImage { JxlPixelFormat format; size_t pixels_size; - size_t pixel_stride() const { - return (BitsPerChannel(format.data_type) * format.num_channels / - jxl::kBitsPerByte); - } + size_t pixel_stride() const { return pixel_stride_; } static size_t BitsPerChannel(JxlDataType data_type) { switch (data_type) { @@ -67,6 +85,52 @@ class PackedImage { } } + float GetPixelValue(size_t y, size_t x, size_t c) const { + const uint8_t* data = const_pixels(y, x, c); + switch (format.data_type) { + case JXL_TYPE_UINT8: + return data[0] * (1.0f / 255); + case JXL_TYPE_UINT16: { + uint16_t val; + memcpy(&val, data, 2); + return (swap_endianness_ ? JXL_BSWAP16(val) : val) * (1.0f / 65535); + } + case JXL_TYPE_FLOAT: { + float val; + memcpy(&val, data, 4); + return swap_endianness_ ? BSwapFloat(val) : val; + } + default: + JXL_ABORT("Unhandled JxlDataType"); + } + } + + void SetPixelValue(size_t y, size_t x, size_t c, float val) { + uint8_t* data = pixels(y, x, c); + switch (format.data_type) { + case JXL_TYPE_UINT8: + data[0] = Clamp1(std::round(val * 255), 0.0f, 255.0f); + break; + case JXL_TYPE_UINT16: { + uint16_t val16 = Clamp1(std::round(val * 65535), 0.0f, 65535.0f); + if (swap_endianness_) { + val16 = JXL_BSWAP16(val16); + } + memcpy(data, &val16, 2); + break; + } + case JXL_TYPE_FLOAT: { + if (swap_endianness_) { + val = BSwapFloat(val); + } + memcpy(data, &val, 4); + break; + } + default: + JXL_ABORT("Unhandled JxlDataType"); + } + } + private: PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format, size_t stride) @@ -75,7 +139,11 @@ class PackedImage { stride(stride), format(format), pixels_size(ysize * stride), - pixels_(malloc(std::max<size_t>(1, pixels_size)), free) {} + pixels_(malloc(std::max<size_t>(1, pixels_size)), free) { + bytes_per_channel_ = BitsPerChannel(format.data_type) / jxl::kBitsPerByte; + pixel_stride_ = format.num_channels * bytes_per_channel_; + swap_endianness_ = SwapEndianness(format.endianness); + } static size_t CalcStride(const JxlPixelFormat& format, size_t xsize) { size_t stride = xsize * (BitsPerChannel(format.data_type) * @@ -86,6 +154,9 @@ class PackedImage { return stride; } + size_t bytes_per_channel_; + size_t pixel_stride_; + bool swap_endianness_; std::unique_ptr<void, decltype(free)*> pixels_; }; @@ -98,6 +169,18 @@ class PackedFrame { template <typename... Args> explicit PackedFrame(Args&&... args) : color(std::forward<Args>(args)...) {} + PackedFrame Copy() const { + PackedFrame copy(color.xsize, color.ysize, color.format); + copy.frame_info = frame_info; + copy.name = name; + copy.color = color.Copy(); + for (size_t i = 0; i < extra_channels.size(); ++i) { + PackedImage ec = extra_channels[i].Copy(); + copy.extra_channels.emplace_back(std::move(ec)); + } + return copy; + } + // The Frame metadata. JxlFrameHeader frame_info = {}; std::string name; @@ -108,6 +191,85 @@ class PackedFrame { std::vector<PackedImage> extra_channels; }; +class ChunkedPackedFrame { + public: + typedef void (*ReadLine)(void* opaque, size_t xpos, size_t ypos, size_t xsize, + uint8_t* buffer, size_t len); + ChunkedPackedFrame(size_t xsize, size_t ysize, const JxlPixelFormat& format, + void* opaque, ReadLine read_line) + : xsize(xsize), + ysize(ysize), + format(format), + opaque_(opaque), + read_line_(read_line) {} + + JxlChunkedFrameInputSource GetInputSource() { + return JxlChunkedFrameInputSource{this, + GetColorChannelsPixelFormat, + GetColorChannelDataAt, + GetExtraChannelPixelFormat, + GetExtraChannelDataAt, + ReleaseCurrentData}; + } + + // The Frame metadata. + JxlFrameHeader frame_info = {}; + std::string name; + + size_t xsize; + size_t ysize; + JxlPixelFormat format; + + private: + static void GetColorChannelsPixelFormat(void* opaque, + JxlPixelFormat* pixel_format) { + ChunkedPackedFrame* self = reinterpret_cast<ChunkedPackedFrame*>(opaque); + *pixel_format = self->format; + } + + static const void* GetColorChannelDataAt(void* opaque, size_t xpos, + size_t ypos, size_t xsize, + size_t ysize, size_t* row_offset) { + ChunkedPackedFrame* self = reinterpret_cast<ChunkedPackedFrame*>(opaque); + size_t bytes_per_channel = + PackedImage::BitsPerChannel(self->format.data_type) / jxl::kBitsPerByte; + size_t bytes_per_pixel = bytes_per_channel * self->format.num_channels; + *row_offset = xsize * bytes_per_pixel; + uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(ysize * (*row_offset))); + for (size_t y = 0; y < ysize; ++y) { + self->read_line_(self->opaque_, xpos, ypos + y, xsize, + &buffer[y * (*row_offset)], *row_offset); + } + self->buffers_.insert(buffer); + return buffer; + } + + static void GetExtraChannelPixelFormat(void* opaque, size_t ec_index, + JxlPixelFormat* pixel_format) { + JXL_ABORT("Not implemented"); + } + + static const void* GetExtraChannelDataAt(void* opaque, size_t ec_index, + size_t xpos, size_t ypos, + size_t xsize, size_t ysize, + size_t* row_offset) { + JXL_ABORT("Not implemented"); + } + + static void ReleaseCurrentData(void* opaque, const void* buffer) { + ChunkedPackedFrame* self = reinterpret_cast<ChunkedPackedFrame*>(opaque); + auto iter = self->buffers_.find(const_cast<void*>(buffer)); + if (iter != self->buffers_.end()) { + free(*iter); + self->buffers_.erase(iter); + } + } + + void* opaque_; + ReadLine read_line_; + std::set<void*> buffers_; +}; + // Optional metadata associated with a file class PackedMetadata { public: @@ -117,17 +279,18 @@ class PackedMetadata { std::vector<uint8_t> xmp; }; +// The extra channel metadata information. +struct PackedExtraChannel { + JxlExtraChannelInfo ec_info; + size_t index; + std::string name; +}; + // Helper class representing a JXL image file as decoded to pixels from the API. class PackedPixelFile { public: JxlBasicInfo info = {}; - // The extra channel metadata information. - struct PackedExtraChannel { - JxlExtraChannelInfo ec_info; - size_t index; - std::string name; - }; std::vector<PackedExtraChannel> extra_channels_info; // Color information of the decoded pixels. @@ -139,9 +302,14 @@ class PackedPixelFile { std::unique_ptr<PackedFrame> preview_frame; std::vector<PackedFrame> frames; + mutable std::vector<ChunkedPackedFrame> chunked_frames; PackedMetadata metadata; PackedPixelFile() { JxlEncoderInitBasicInfo(&info); }; + + size_t num_frames() const { + return chunked_frames.empty() ? frames.size() : chunked_frames.size(); + } }; } // namespace extras |