blob: 69e8d148d4bf64aedf3dfdb098cf935eb3ccbe9c [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -----------------------------------------------------------------------------
//
// JPEG XL decode.
//
// Author: Vincent Rabaud (vrabaud@google.com)
#include <cstdio>
#include <cstdlib>
#ifdef HAVE_CONFIG_H
#include "src/wp2/config.h"
#endif
#include "imageio/anim_image_dec.h"
#include "imageio/imageio_util.h"
#include "src/wp2/base.h"
#ifdef WP2_HAVE_JXL
#include <cstdint>
#include "src/utils/utils.h"
#include "third_party/jpegxl/lib/include/jxl/codestream_header.h"
#include "third_party/jpegxl/lib/include/jxl/decode.h"
#include "third_party/jpegxl/lib/include/jxl/decode_cxx.h"
#include "third_party/jpegxl/lib/include/jxl/types.h"
namespace {
//------------------------------------------------------------------------------
// JPEG XL decoding
} // namespace
namespace WP2 {
namespace {
JxlPixelFormat ArgbBufferToJxlPixelFormat(const WP2::ArgbBuffer& image) {
JxlPixelFormat pixel_format;
const uint32_t bytes_per_channel = (WP2Formatbpc(image.format()) + 7) / 8;
pixel_format.num_channels = WP2FormatBpp(image.format()) / bytes_per_channel;
pixel_format.data_type =
WP2Formatbpc(image.format()) == 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16;
// TODO(yguyon): Fix endianness TODO in libwebp2/public/src/wp2/decode.h
pixel_format.endianness = JXL_NATIVE_ENDIAN;
pixel_format.align = image.stride();
return pixel_format;
}
size_t ArgbBufferSize(const WP2::ArgbBuffer& image) {
return static_cast<size_t>(image.height() - 1) * image.stride() +
static_cast<size_t>(image.width()) * WP2FormatBpp(image.format());
}
} // namespace
class ImageReaderJXL : public ImageReader::Impl {
public:
ImageReaderJXL(const uint8_t* data, size_t data_size,
ArgbBuffer* const buffer, LogLevel log_level,
size_t max_num_pixels)
: ImageReader::Impl(buffer, data, data_size, log_level, max_num_pixels) {}
WP2Status ReadFrame(bool* const is_last,
uint32_t* const duration_ms) override {
const JxlDecoderPtr decoder = JxlDecoderMake(nullptr);
WP2_CHECK_ALLOC_OK(decoder != nullptr);
JxlDecoderStatus status = JxlDecoderSubscribeEvents(
decoder.get(), JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE);
WP2_CHECK_OK(status == JXL_DEC_SUCCESS, WP2_STATUS_INVALID_CONFIGURATION);
status = JxlDecoderSetInput(decoder.get(), data_, data_size_);
WP2_CHECK_OK(status == JXL_DEC_SUCCESS, WP2_STATUS_INVALID_CONFIGURATION);
JxlDecoderCloseInput(decoder.get());
status = JxlDecoderProcessInput(decoder.get());
WP2_CHECK_OK(status == JXL_DEC_BASIC_INFO,
WP2_STATUS_INVALID_CONFIGURATION);
JxlBasicInfo info;
status = JxlDecoderGetBasicInfo(decoder.get(), &info);
WP2_CHECK_OK(status == JXL_DEC_SUCCESS, WP2_STATUS_INVALID_CONFIGURATION);
status = JxlDecoderProcessInput(decoder.get());
WP2_CHECK_OK(status == JXL_DEC_NEED_IMAGE_OUT_BUFFER,
WP2_STATUS_INVALID_CONFIGURATION);
ArgbBuffer buffer_tmp;
ArgbBuffer* buffer = buffer_;
if (buffer->format() != (info.alpha_bits > 0 ? WP2_RGBA_32 : WP2_RGB_24)) {
WP2_CHECK_STATUS(
buffer_tmp.SetFormat(info.alpha_bits > 0 ? WP2_RGBA_32 : WP2_RGB_24));
buffer = &buffer_tmp;
}
WP2_CHECK_STATUS(buffer->Resize(info.xsize, info.ysize));
const JxlPixelFormat pixel_format = ArgbBufferToJxlPixelFormat(*buffer);
status =
JxlDecoderSetImageOutBuffer(decoder.get(), &pixel_format,
buffer->GetRow(0), ArgbBufferSize(*buffer));
WP2_CHECK_OK(status == JXL_DEC_SUCCESS, WP2_STATUS_INVALID_CONFIGURATION);
status = JxlDecoderProcessInput(decoder.get());
WP2_CHECK_OK(status == JXL_DEC_FULL_IMAGE,
WP2_STATUS_INVALID_CONFIGURATION);
status = JxlDecoderProcessInput(decoder.get());
WP2_CHECK_OK(status == JXL_DEC_SUCCESS, WP2_STATUS_INVALID_CONFIGURATION);
if (buffer != buffer_) WP2_CHECK_STATUS(buffer_->ConvertFrom(*buffer));
return WP2_STATUS_OK;
}
protected:
JxlDecoderPtr decoder_;
int last_timestamp_ = 0;
size_t frame_index_ = 0;
};
// -----------------------------------------------------------------------------
void ImageReader::SetImplJXL(ArgbBuffer* const buffer, LogLevel log_level,
size_t max_num_pixels) {
impl_.reset(new (WP2Allocable::nothrow) ImageReaderJXL(
data_.bytes, data_.size, buffer, log_level, max_num_pixels));
if (impl_ == nullptr) status_ = WP2_STATUS_OUT_OF_MEMORY;
}
} // namespace WP2
#else // !WP2_HAVE_JXL
void WP2::ImageReader::SetImplJXL(WP2::ArgbBuffer* const buffer,
WP2::LogLevel log_level,
size_t max_num_pixels) {
(void)buffer;
(void)max_num_pixels;
if (log_level >= WP2::LogLevel::DEFAULT) {
fprintf(stderr,
"JPEG XL support not compiled. Please install the libjxl "
"development package before building.\n");
}
status_ = WP2_STATUS_UNSUPPORTED_FEATURE;
}
#endif // WP2_HAVE_JXL