265 lines
9 KiB
C++
265 lines
9 KiB
C++
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
#ifndef LIB_JXL_IMAGE_BUNDLE_H_
|
|
#define LIB_JXL_IMAGE_BUNDLE_H_
|
|
|
|
// The main image or frame consists of a bundle of associated images.
|
|
|
|
#include <jxl/cms_interface.h>
|
|
#include <jxl/memory_manager.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "lib/jxl/base/common.h"
|
|
#include "lib/jxl/base/data_parallel.h"
|
|
#include "lib/jxl/base/rect.h"
|
|
#include "lib/jxl/base/status.h"
|
|
#include "lib/jxl/color_encoding_internal.h"
|
|
#include "lib/jxl/common.h" // JPEGXL_ENABLE_TRANSCODE_JPEG
|
|
#include "lib/jxl/frame_header.h"
|
|
#include "lib/jxl/image.h"
|
|
#include "lib/jxl/image_metadata.h"
|
|
#include "lib/jxl/image_ops.h"
|
|
#include "lib/jxl/jpeg/jpeg_data.h"
|
|
|
|
namespace jxl {
|
|
|
|
// A bundle of color/alpha/depth/plane images.
|
|
class ImageBundle {
|
|
public:
|
|
// Uninitialized state for use as output parameter.
|
|
explicit ImageBundle(JxlMemoryManager* memory_manager)
|
|
: memory_manager_(memory_manager), metadata_(nullptr) {}
|
|
// Caller is responsible for setting metadata before calling Set*.
|
|
ImageBundle(JxlMemoryManager* memory_manager, const ImageMetadata* metadata)
|
|
: memory_manager_(memory_manager), metadata_(metadata) {}
|
|
|
|
// Move-only (allows storing in std::vector).
|
|
ImageBundle(ImageBundle&&) = default;
|
|
ImageBundle& operator=(ImageBundle&&) = default;
|
|
|
|
StatusOr<ImageBundle> Copy() const {
|
|
JxlMemoryManager* memory_manager = this->memory_manager();
|
|
ImageBundle copy(memory_manager, metadata_);
|
|
JXL_ASSIGN_OR_RETURN(
|
|
copy.color_,
|
|
Image3F::Create(memory_manager, color_.xsize(), color_.ysize()));
|
|
CopyImageTo(color_, ©.color_);
|
|
copy.c_current_ = c_current_;
|
|
copy.extra_channels_.reserve(extra_channels_.size());
|
|
for (const ImageF& plane : extra_channels_) {
|
|
JXL_ASSIGN_OR_RETURN(
|
|
ImageF ec,
|
|
ImageF::Create(memory_manager, plane.xsize(), plane.ysize()));
|
|
CopyImageTo(plane, &ec);
|
|
copy.extra_channels_.emplace_back(std::move(ec));
|
|
}
|
|
|
|
copy.jpeg_data =
|
|
jpeg_data ? make_unique<jpeg::JPEGData>(*jpeg_data) : nullptr;
|
|
copy.color_transform = color_transform;
|
|
copy.chroma_subsampling = chroma_subsampling;
|
|
|
|
return copy;
|
|
}
|
|
|
|
// -- SIZE
|
|
|
|
size_t xsize() const {
|
|
if (IsJPEG()) return jpeg_data->width;
|
|
if (color_.xsize() != 0) return color_.xsize();
|
|
return extra_channels_.empty() ? 0 : extra_channels_[0].xsize();
|
|
}
|
|
size_t ysize() const {
|
|
if (IsJPEG()) return jpeg_data->height;
|
|
if (color_.ysize() != 0) return color_.ysize();
|
|
return extra_channels_.empty() ? 0 : extra_channels_[0].ysize();
|
|
}
|
|
void ShrinkTo(size_t xsize, size_t ysize);
|
|
|
|
// sizes taking orientation into account
|
|
size_t oriented_xsize() const {
|
|
if (static_cast<uint32_t>(metadata_->GetOrientation()) > 4) {
|
|
return ysize();
|
|
} else {
|
|
return xsize();
|
|
}
|
|
}
|
|
size_t oriented_ysize() const {
|
|
if (static_cast<uint32_t>(metadata_->GetOrientation()) > 4) {
|
|
return xsize();
|
|
} else {
|
|
return ysize();
|
|
}
|
|
}
|
|
|
|
JxlMemoryManager* memory_manager_;
|
|
|
|
// -- COLOR
|
|
|
|
JxlMemoryManager* memory_manager() const { return memory_manager_; }
|
|
|
|
// Whether color() is valid/usable. Returns true in most cases. Even images
|
|
// with spot colors (one example of when !planes().empty()) typically have a
|
|
// part that can be converted to RGB.
|
|
bool HasColor() const { return color_.xsize() != 0; }
|
|
|
|
// For resetting the size when switching from a reference to main frame.
|
|
void RemoveColor() { color_ = Image3F(); }
|
|
|
|
// Do not use if !HasColor().
|
|
const Image3F& color() const {
|
|
// If this fails, Set* was not called - perhaps because decoding failed?
|
|
JXL_DASSERT(HasColor());
|
|
return color_;
|
|
}
|
|
|
|
// Do not use if !HasColor().
|
|
Image3F* color() {
|
|
JXL_DASSERT(HasColor());
|
|
return &color_;
|
|
}
|
|
|
|
// If c_current.IsGray(), all planes must be identical. NOTE: c_current is
|
|
// independent of metadata()->color_encoding, which is the original, whereas
|
|
// a decoder might return pixels in a different c_current.
|
|
// This only sets the color channels, you must also make extra channels
|
|
// match the amount that is in the metadata.
|
|
void SetFromImage(Image3F&& color, const ColorEncoding& c_current);
|
|
|
|
// -- COLOR ENCODING
|
|
|
|
const ColorEncoding& c_current() const { return c_current_; }
|
|
|
|
// Returns whether the color image has identical planes. Once established by
|
|
// Set*, remains unchanged until a subsequent Set* or TransformTo.
|
|
bool IsGray() const { return c_current_.IsGray(); }
|
|
|
|
bool IsSRGB() const { return c_current_.IsSRGB(); }
|
|
bool IsLinearSRGB() const { return c_current_.IsLinearSRGB(); }
|
|
|
|
// Set the c_current profile without doing any transformation, e.g. if the
|
|
// transformation was already applied.
|
|
void OverrideProfile(const ColorEncoding& new_c_current) {
|
|
c_current_ = new_c_current;
|
|
}
|
|
|
|
// TODO(lode): TransformTo and CopyTo are implemented in enc_image_bundle.cc,
|
|
// move these functions out of this header file and class, to
|
|
// enc_image_bundle.h.
|
|
|
|
// Transforms color to c_desired and sets c_current to c_desired. Alpha and
|
|
// metadata remains unchanged.
|
|
Status TransformTo(const ColorEncoding& c_desired, const JxlCmsInterface& cms,
|
|
ThreadPool* pool = nullptr);
|
|
// Copies this:rect, converts to c_desired, and allocates+fills out.
|
|
Status CopyTo(const Rect& rect, const ColorEncoding& c_desired,
|
|
const JxlCmsInterface& cms, Image3F* out,
|
|
ThreadPool* pool = nullptr) const;
|
|
|
|
// Detect 'real' bit depth, which can be lower than nominal bit depth
|
|
// (this is common in PNG), returns 'real' bit depth
|
|
size_t DetectRealBitdepth() const;
|
|
|
|
// -- ALPHA
|
|
|
|
void SetAlpha(ImageF&& alpha);
|
|
bool HasAlpha() const {
|
|
return metadata_->Find(ExtraChannel::kAlpha) != nullptr;
|
|
}
|
|
bool AlphaIsPremultiplied() const {
|
|
const ExtraChannelInfo* eci = metadata_->Find(ExtraChannel::kAlpha);
|
|
return (eci == nullptr) ? false : eci->alpha_associated;
|
|
}
|
|
const ImageF& alpha() const;
|
|
ImageF* alpha();
|
|
|
|
// -- EXTRA CHANNELS
|
|
bool HasBlack() const {
|
|
return metadata_->Find(ExtraChannel::kBlack) != nullptr;
|
|
}
|
|
const ImageF& black() const;
|
|
|
|
// Extra channels of unknown interpretation (e.g. spot colors).
|
|
void SetExtraChannels(std::vector<ImageF>&& extra_channels);
|
|
void ClearExtraChannels() { extra_channels_.clear(); }
|
|
bool HasExtraChannels() const { return !extra_channels_.empty(); }
|
|
const std::vector<ImageF>& extra_channels() const { return extra_channels_; }
|
|
std::vector<ImageF>& extra_channels() { return extra_channels_; }
|
|
|
|
const ImageMetadata* metadata() const { return metadata_; }
|
|
|
|
void VerifyMetadata() const;
|
|
|
|
void SetDecodedBytes(size_t decoded_bytes) { decoded_bytes_ = decoded_bytes; }
|
|
size_t decoded_bytes() const { return decoded_bytes_; }
|
|
|
|
// -- JPEG transcoding:
|
|
|
|
// Returns true if image does or will represent quantized DCT-8 coefficients,
|
|
// stored in 8x8 pixel regions.
|
|
bool IsJPEG() const {
|
|
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
|
return jpeg_data != nullptr;
|
|
#else // JPEGXL_ENABLE_TRANSCODE_JPEG
|
|
return false;
|
|
#endif // JPEGXL_ENABLE_TRANSCODE_JPEG
|
|
}
|
|
|
|
std::unique_ptr<jpeg::JPEGData> jpeg_data;
|
|
// these fields are used to signal the input JPEG color space
|
|
// NOTE: JPEG doesn't actually provide a way to determine whether YCbCr was
|
|
// applied or not.
|
|
ColorTransform color_transform = ColorTransform::kNone;
|
|
YCbCrChromaSubsampling chroma_subsampling;
|
|
|
|
FrameOrigin origin{0, 0};
|
|
|
|
// Animation-related information, corresponding to the timecode and duration
|
|
// fields of the jxl::AnimationFrame of the jxl::FrameHeader.
|
|
// TODO(lode): ImageBundle is used here to carry the information from
|
|
// jxl::FrameHeader, consider instead passing a jxl::FrameHeader directly to
|
|
// EncodeFrame or having a field of that type here.
|
|
uint32_t duration = 0;
|
|
uint32_t timecode = 0;
|
|
|
|
// TODO(lode): these fields do not match the JXL frame header, it should be
|
|
// possible to specify up to 4 (3 if nonzero duration) slots to save this
|
|
// frame as reference (see save_as_reference).
|
|
bool use_for_next_frame = false;
|
|
bool blend = false;
|
|
BlendMode blendmode = BlendMode::kBlend;
|
|
|
|
std::string name;
|
|
|
|
private:
|
|
// Called after any Set* to ensure their sizes are compatible.
|
|
void VerifySizes() const;
|
|
|
|
// Required for TransformTo so that an ImageBundle is self-sufficient. Always
|
|
// points to the same thing, but cannot be const-pointer because that prevents
|
|
// the compiler from generating a move ctor.
|
|
const ImageMetadata* metadata_;
|
|
|
|
// Initialized by Set*:
|
|
Image3F color_; // If empty, planes_ is not; all planes equal if IsGray().
|
|
ColorEncoding c_current_; // of color_
|
|
|
|
// Initialized by SetPlanes; size = ImageMetadata.num_extra_channels
|
|
std::vector<ImageF> extra_channels_;
|
|
|
|
// How many bytes of the input were actually read.
|
|
size_t decoded_bytes_ = 0;
|
|
};
|
|
|
|
} // namespace jxl
|
|
|
|
#endif // LIB_JXL_IMAGE_BUNDLE_H_
|