libheif/codecs/jpeg2000.h (243 lines of code) (raw):
/*
* HEIF JPEG 2000 codec.
* Copyright (c) 2023 Brad Hards <bradh@frogmouth.net>
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBHEIF_JPEG2000_H
#define LIBHEIF_JPEG2000_H
#include "box.h"
#include "file.h"
#include "context.h"
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
/**
* JPEG 2000 Channel Definition box.
*
* This is defined in ITU-800 / IEC 15444-1.
*
* The Channel Definition box provides the type and ordering of the components
* within the codestream. The mapping between actual components from the
* codestream to channels is specified in the Component Mapping box. If the
* header does not contain a Component Mapping box, then a reader shall map
* component
* @code{i} to channel @code{i}, for all components in the codestream.
*
* This box contains an array of channel descriptions. For each description,
* three values are specified: the index of the channel described by that
* association, the type of that channel, and the association of that channel
* with particular colours. This box may specify multiple descriptions for a
* single channel. If a multiple component transform is specified within the
* codestream, the image must be in an RGB colourspace and the red, green and
* blue colours as channels 0, 1 and 2 in the codestream, respectively.
*
* This box is required within the JPEG 2000 header item property (`j2kH`).
*/
class Box_cdef : public Box {
public:
Box_cdef() { set_short_type(fourcc("cdef")); }
std::string dump(Indent &) const override;
Error write(StreamWriter &writer) const override;
struct Channel {
/**
* Channel index (`Cn`).
*
* This field specifies the index of the channel for this description.
* The value of this field represents the index of the channel as
* defined within the Component Mapping box (or the actual component
* from the codestream if the file does not contain a Component Mapping
* box).
*/
uint16_t channel_index;
/**
* Channel type (`Typ`).
*
* Each channel type value shall be equal to 0 (colour), 1 (alpha)
* or 2 (pre-multiplied alpha).
*
* At most one channel type value shall be equal to 1 or 2 (i.e. a
* single alpha channel), and the corresponding association field
* value shall be 0.
*/
uint16_t channel_type;
/**
* Channel association (`Asoc`).
*
* If the channel type value is 0 (colour), then the channel association
* value shall be in the range [1, 2<sup>16</sup> -2].
* Interpretation of this depends on the colourspace.
*/
uint16_t channel_association;
};
/**
* Get the channels in this channel definition box.
*
* @return the channels as a read-only vector.
*/
const std::vector<Channel> &get_channels() const { return m_channels; }
/**
* Add a channel to this channel definition box.
*
* @param channel the channel to add
*/
void add_channel(Channel channel) { m_channels.push_back(channel); }
void set_channels(heif_colorspace colorspace);
protected:
Error parse(BitstreamRange &range) override;
private:
std::vector<Channel> m_channels;
};
/**
* JPEG 2000 Component Mapping box.
*
* The Component Mapping box defines how image channels are identified from the
* actual components decoded from the codestream. This abstraction allows a
* single structure (the Channel Definition box) to specify the colour or type
* of both palettised images and non-palettised images. This box contains an
* array of CMP<sup>i</sup>, MTYP<sup>i</sup> and PCOL<sup>i</sup> fields. Each
* group of these fields represents the definition of one channel in the image.
* The channels are numbered in order starting with zero, and the number of
* channels specified in the Component Mapping box is determined by the length
* of the box.
*
* There shall be at most one Component Mapping box inside a JP2 Header box.
* If the JP2 Header box contains a Palette box, then the JP2 Header box
* shall also contain a Component Mapping box. If the JP2 Header box does
* not contain a Component Mapping box, the components shall be mapped
* directly to channels, such that component @code{i} is mapped to channel
* @code {i}.
*/
class Box_cmap : public Box
{
public:
Box_cmap() { set_short_type(fourcc("cmap")); }
std::string dump(Indent &) const override;
Error write(StreamWriter &writer) const override;
struct Component
{
uint16_t component_index;
uint8_t mapping_type;
uint8_t palette_colour;
};
/**
* Get the components in this component mapping box.
*
* @return the components as a read-only vector.
*/
const std::vector<Component> &get_components() const { return m_components; }
/**
* Add a component to this component mapping box.
*
* @param component the component to add
*/
void add_component(Component component) { m_components.push_back(component); }
protected:
Error parse(BitstreamRange &range) override;
private:
std::vector<Component> m_components;
};
/**
* JPEG 2000 Palette box.
*
* This box defines the palette to be used to create multiple components
* from a single component.
*/
class Box_pclr : public Box
{
public:
Box_pclr()
{
set_short_type(fourcc("pclr"));
}
std::string dump(Indent &) const override;
Error write(StreamWriter &writer) const override;
struct PaletteEntry
{
// JPEG 2000 supports 38 bits and signed/unsigned, but we only
// do up to 16 bit unsigned.
std::vector<uint16_t> columns;
};
/**
* Get the entries in this palette box.
*
* @return the entries as a read-only vector.
*/
const std::vector<PaletteEntry>& get_entries() const
{
return m_entries;
}
/**
* Get the bit depths for the columns in this palette box.
*
* @return the bit depths as a read-only vector.
*/
const std::vector<uint8_t>& get_bit_depths() const
{
return m_bitDepths;
}
const uint8_t get_num_entries() const
{
return (uint8_t)(m_entries.size());
}
const uint8_t get_num_columns() const
{
return (uint8_t)(m_bitDepths.size());
}
void add_entry(const PaletteEntry entry)
{
m_entries.push_back(entry);
}
/**
* Set columns for the palette mapping.
*
* Each column has the same bit depth.
*
* This will reset any existing columns and entries.
*
* @param num_columns the number of columns (e.g. 3 for RGB)
* @param bit_depth the bit depth for each column (e.g. 8 for 24-bit RGB)
*/
void set_columns(uint8_t num_columns, uint8_t bit_depth);
protected:
Error parse(BitstreamRange &range) override;
private:
std::vector<uint8_t> m_bitDepths;
std::vector<PaletteEntry> m_entries;
};
/**
* JPEG 2000 layers box.
*
* The JPEG 2000 layers box declares a list of quality and resolution layers
* for a JPEG 2000 codestream.
*
* @note the JPEG 2000 codestream can contain layers not listed in the JPEG 2000
* layers box.
*/
class Box_j2kL : public FullBox
{
public:
Box_j2kL() { set_short_type(fourcc("j2kL")); }
std::string dump(Indent &) const override;
Error write(StreamWriter &writer) const override;
struct Layer {
/**
* Unique identifier for the layer.
*/
uint16_t layer_id;
/**
* Number of resolution levels of the codestream that can be discarded.
*/
uint8_t discard_levels;
/**
* Minimum number of quality layers of the codestream to be decoded.
*/
uint16_t decode_layers;
};
/**
* Get the layers in this layer box.
*
* @return the layers as a read-only vector.
*/
const std::vector<Layer> &get_layers() const { return m_layers; }
/**
* Add a layer to the layers box.
*
* @param layer the layer to add
*/
void add_layer(Layer layer) { m_layers.push_back(layer); }
protected:
Error parse(BitstreamRange &range) override;
private:
std::vector<Layer> m_layers;
};
class Box_j2kH : public Box
{
public:
Box_j2kH() { set_short_type(fourcc("j2kH")); }
std::string dump(Indent &) const override;
// Default write behaviour for a container is to write children
protected:
Error parse(BitstreamRange &range) override;
};
class Jpeg2000ImageCodec
{
public:
// static Error decode_jpeg2000_image(const HeifContext* context,
// heif_item_id ID,
// std::shared_ptr<HeifPixelImage>& img,
// const std::vector<uint8_t>& uncompressed_data);
static Error encode_jpeg2000_image(const std::shared_ptr<HeifFile>& heif_file,
const std::shared_ptr<HeifPixelImage>& src_image,
void* encoder_struct,
const struct heif_encoding_options& options,
std::shared_ptr<HeifContext::Image>& out_image);
};
struct JPEG2000_SIZ_segment
{
/**
* Decoder capabilities bitmap (Rsiz).
*/
uint16_t decoder_capabilities = 0;
/**
* Width of the reference grid (Xsiz).
*/
uint32_t reference_grid_width = 0;
/**
* Height of the reference grid (Ysiz).
*/
uint32_t reference_grid_height = 0;
/**
* Horizontal offset from reference grid origin to image left side (XOsiz).
*/
uint32_t image_horizontal_offset = 0;
/**
* Vertical offset from reference grid origin to image top size (YOsiz).
*/
uint32_t image_vertical_offset = 0;
/**
* Width of one reference tile with respect to the reference grid (XTsiz).
*/
uint32_t tile_width = 0;
/**
* Height of one reference tile with respect to the reference grid (YTsiz).
*/
uint32_t tile_height = 0;
/**
* Horizontal offset from the origin of the reference grid to left side of first tile (XTOsiz).
*/
uint32_t tile_offset_x = 0;
/**
* Vertical offset from the origin of the reference grid to top side of first tile (YTOsiz).
*/
uint32_t tile_offset_y = 0;
struct component
{
uint8_t h_separation, v_separation;
uint8_t precision;
bool is_signed;
};
std::vector<component> components;
};
class JPEG2000_Extension_Capability {
public:
JPEG2000_Extension_Capability(const uint8_t i) : ident(i) {}
uint8_t getIdent() const {
return ident;
}
uint16_t getValue() const {
return value;
}
void setValue(uint16_t val) {
value = val;
}
private:
const uint8_t ident;
uint16_t value = 0;
};
class JPEG2000_Extension_Capability_HT : public JPEG2000_Extension_Capability
{
public:
static const int IDENT = 15;
JPEG2000_Extension_Capability_HT(): JPEG2000_Extension_Capability(IDENT)
{};
};
class JPEG2000_CAP_segment
{
public:
void push_back(JPEG2000_Extension_Capability ccap) {
extensions.push_back(ccap);
}
bool hasHighThroughputExtension() const {
for (auto &extension : extensions) {
if (extension.getIdent() == 15) {
return true;
}
}
return false;
}
private:
std::vector<JPEG2000_Extension_Capability> extensions;
};
struct JPEG2000MainHeader
{
public:
JPEG2000MainHeader() = default;
Error parseHeader(const HeifFile& file, const heif_item_id imageID);
// Use parseHeader instead - these are mainly for unit testing
Error doParse();
void setHeaderData(std::vector<uint8_t> data)
{
headerData = data;
}
heif_chroma get_chroma_format() const;
int get_precision(uint32_t index) const
{
if (index >= siz.components.size()) {
return -1;
}
// TODO: this is a quick hack. It is more complicated for JPEG2000 because these can be any kind of colorspace (e.g. RGB).
return siz.components[index].precision;
}
bool hasHighThroughputExtension() {
return cap.hasHighThroughputExtension();
}
uint32_t getXSize() const
{
return siz.reference_grid_width;
}
uint32_t getYSize() const
{
return siz.reference_grid_height;
}
const JPEG2000_SIZ_segment get_SIZ()
{
return siz;
}
private:
Error parse_SOC_segment();
Error parse_SIZ_segment();
Error parse_CAP_segment_body();
void parse_Ccap15();
static const int MARKER_LEN = 2;
JPEG2000_SIZ_segment siz;
JPEG2000_CAP_segment cap;
uint8_t read8()
{
uint8_t res = headerData[cursor];
cursor += 1;
return res;
}
uint16_t read16()
{
uint16_t res = (uint16_t)((headerData[cursor] << 8) | headerData[cursor + 1]);
cursor += 2;
return res;
}
uint32_t read32()
{
uint32_t res = (headerData[cursor] << 24) | (headerData[cursor + 1] << 16) | (headerData[cursor + 2] << 8) | headerData[cursor + 3];
cursor += 4;
return res;
}
std::vector<uint8_t> headerData;
size_t cursor;
};
#endif // LIBHEIF_JPEG2000_H