libheif/plugins/encoder_openjph.cc (665 lines of code) (raw):
/*
* OpenJPH codec.
* Copyright (c) 2023 Devon Sookhoo
* Copyright (c) 2023 Dirk Farin <dirk.farin@gmail.com>
* Copyright (C) 2024 Brad Hards
*
* 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/>.
*/
// Portions of this code adapted from OpenJPH's ojph_compress.cpp
// which is under the following license:
//***************************************************************************/
// This software is released under the 2-Clause BSD license, included
// below.
//
// Copyright (c) 2019, Aous Naman
// Copyright (c) 2019, Kakadu Software Pty Ltd, Australia
// Copyright (c) 2019, The University of New South Wales, Australia
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//***************************************************************************/
#include "libheif/heif.h"
#include "libheif/heif_plugin.h"
#include "encoder_openjph.h"
#include "openjph/ojph_mem.h"
#include "openjph/ojph_defs.h"
#include "openjph/ojph_file.h"
#include "openjph/ojph_codestream.h"
#include "openjph/ojph_params.h"
#include "openjph/ojph_version.h"
#include <string.h>
#include <cassert>
#include <limits>
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
static const int OJPH_PLUGIN_PRIORITY = 80;
struct encoder_struct_ojph
{
// We do this for API reasons. Has no effect at this stage.
int quality = 70;
heif_chroma chroma = heif_chroma_undefined;
// Context
ojph::codestream codestream;
std::string comment;
// --- output
bool data_read = false;
ojph::mem_outfile outfile;
};
#define MAX_NPARAMETERS 10
static struct heif_encoder_parameter ojph_encoder_params[MAX_NPARAMETERS];
const static struct heif_encoder_parameter* ojph_encoder_parameter_ptrs[MAX_NPARAMETERS + 1];
static const char* kParam_chroma = "chroma";
static const char* const kParam_chroma_valid_values[] = {
"420", "422", "444", nullptr
};
static const char* kParam_num_decompositions = "num_decompositions";
static const int NUM_DECOMPOSITIONS_MIN = 0;
static const int NUM_DECOMPOSITIONS_MAX = 32;
static const char* kParam_progression_order = "progression_order";
static const char* const kParam_progression_order_valid_values[] = {
"LRCP", "RLCP", "RPCL", "PCRL", "CPRL", nullptr
};
static const char* kParam_tlm_marker = "tlm_marker";
static const char* kParam_codestream_comment = "codestream_comment";
static const char* kParam_tile_size = "tile_size";
static const char* kParam_tilepart_division = "tilepart_division";
static const char* const kParam_tilepart_division_valid_values[] = {
"none", "resolution", "component", "both", nullptr
};
static const char* kParam_block_dimensions = "block_dimensions";
static void ojph_init_encoder_parameters()
{
struct heif_encoder_parameter* p = ojph_encoder_params;
const struct heif_encoder_parameter** d = ojph_encoder_parameter_ptrs;
int i = 0;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = heif_encoder_parameter_name_lossless;
p->type = heif_encoder_parameter_type_boolean;
p->boolean.default_value = false;
p->has_default = true;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_chroma;
p->type = heif_encoder_parameter_type_string;
p->string.default_value = "444";
p->has_default = true;
p->string.valid_values = kParam_chroma_valid_values;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_num_decompositions;
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 5;
p->integer.have_minimum_maximum = true;
p->integer.minimum = NUM_DECOMPOSITIONS_MIN;
p->integer.maximum = NUM_DECOMPOSITIONS_MAX;
p->integer.valid_values = NULL;
p->integer.num_valid_values = 0;
p->has_default = true;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_progression_order;
p->type = heif_encoder_parameter_type_string;
p->string.default_value = "RPCL";
p->has_default = true;
p->string.valid_values = kParam_progression_order_valid_values;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_tlm_marker;
p->type = heif_encoder_parameter_type_boolean;
p->boolean.default_value = false;
p->has_default = true;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_codestream_comment;
p->type = heif_encoder_parameter_type_string;
p->string.default_value = nullptr;
p->has_default = false;
p->string.valid_values = nullptr;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_tile_size;
p->type = heif_encoder_parameter_type_string;
p->string.default_value = "0,0";
p->has_default = true;
p->string.valid_values = nullptr;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_tilepart_division;
p->type = heif_encoder_parameter_type_string;
p->string.default_value = "none";
p->has_default = true;
p->string.valid_values = kParam_tilepart_division_valid_values;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_block_dimensions;
p->type = heif_encoder_parameter_type_string;
p->string.default_value = "64,64";
p->has_default = true;
p->string.valid_values = nullptr;
d[i++] = p++;
d[i++] = nullptr;
}
void ojph_init_plugin()
{
ojph_init_encoder_parameters();
}
void ojph_cleanup_plugin()
{
}
//////////// Integer parameter setters
// Note quality is part of the plugin API.
struct heif_error ojph_set_parameter_quality(void* encoder_raw, int quality)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
encoder->quality = quality;
return heif_error_ok;
}
static const heif_error &ojph_set_num_decompositions(int value, encoder_struct_ojph *encoder)
{
if ((value < NUM_DECOMPOSITIONS_MIN) || (value > NUM_DECOMPOSITIONS_MAX)) {
return heif_error_invalid_parameter_value;
}
encoder->codestream.access_cod().set_num_decomposition(value);
return heif_error_ok;
}
struct heif_error ojph_set_parameter_integer(void *encoder_raw, const char *name, int value)
{
struct encoder_struct_ojph* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
return ojph_set_parameter_quality(encoder, value);
} else if (strcmp(name, kParam_num_decompositions) == 0) {
return ojph_set_num_decompositions(value, encoder);
} else {
return heif_error_unsupported_parameter;
}
}
//////////// Integer parameter getters
// Note quality is part of the plugin API
struct heif_error ojph_get_parameter_quality(void* encoder_raw, int* quality)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
*quality = encoder->quality;
return heif_error_ok;
}
const heif_error &ojph_get_parameter_num_decompositions(encoder_struct_ojph *encoder, int *value)
{
*value = encoder->codestream.access_cod().get_num_decompositions();
return heif_error_ok;
}
struct heif_error ojph_get_parameter_integer(void *encoder_raw, const char *name, int *value)
{
struct encoder_struct_ojph* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
return ojph_get_parameter_quality(encoder, value);
} else if (strcmp(name, kParam_num_decompositions) == 0) {
return ojph_get_parameter_num_decompositions(encoder, value);
} else {
return heif_error_unsupported_parameter;
}
}
//////////// Boolean parameter setters
// Note lossless is part of the plugin API
struct heif_error ojph_set_parameter_lossless(void* encoder_raw, int lossless)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
encoder->codestream.access_cod().set_reversible(lossless);
return heif_error_ok;
}
const heif_error &ojph_set_tlm_marker_requested(encoder_struct_ojph *encoder, int value)
{
encoder->codestream.request_tlm_marker(value);
return heif_error_ok;
}
struct heif_error ojph_set_parameter_boolean(void *encoder_raw, const char *name, int value)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
return ojph_set_parameter_lossless(encoder, value);
} else if (strcmp(name, kParam_tlm_marker) == 0) {
return ojph_set_tlm_marker_requested(encoder, value);
}
return heif_error_unsupported_parameter;
}
//////////// Boolean parameter getters
// Note lossless is part of the plugin API
struct heif_error ojph_get_parameter_lossless(void* encoder_raw, int* lossless)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
*lossless = encoder->codestream.access_cod().is_reversible();
return heif_error_ok;
}
const heif_error &ojph_get_parameter_tlm_marker(encoder_struct_ojph *encoder, int *value)
{
*value = encoder->codestream.is_tlm_requested();
return heif_error_ok;
}
struct heif_error ojph_get_parameter_boolean(void *encoder_raw, const char *name, int *value)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
return ojph_get_parameter_lossless(encoder, value);
} else if (strcmp(name, kParam_tlm_marker) == 0) {
return ojph_get_parameter_tlm_marker(encoder, value);
} else {
return heif_error_unsupported_parameter;
}
}
//////////// String parameter getters
static void safe_strcpy(char* dst, int dst_size, const char* src)
{
strncpy(dst, src, dst_size - 1);
dst[dst_size - 1] = 0;
}
const heif_error &ojph_get_parameter_chroma(encoder_struct_ojph *encoder, char *value, int value_size)
{
switch (encoder->chroma) {
case heif_chroma_420:
safe_strcpy(value, value_size, "420");
break;
case heif_chroma_422:
safe_strcpy(value, value_size, "422");
break;
case heif_chroma_444:
safe_strcpy(value, value_size, "444");
break;
case heif_chroma_undefined:
safe_strcpy(value, value_size, "undefined");
break;
default:
assert(false);
return heif_error_invalid_parameter_value;
}
return heif_error_ok;
}
const heif_error &ojph_get_parameter_progression_order(encoder_struct_ojph *encoder, char *value, int value_size)
{
safe_strcpy(value, value_size, encoder->codestream.access_cod().get_progression_order_as_string());
return heif_error_ok;
}
const heif_error &ojph_get_parameter_codestream_comment(encoder_struct_ojph *encoder, char *value, int value_size)
{
safe_strcpy(value, value_size, encoder->comment.c_str());
return heif_error_ok;
}
const heif_error &ojph_get_parameter_tile_size(encoder_struct_ojph *encoder, char *value, int value_size)
{
ojph::size tile_size = encoder->codestream.access_siz().get_tile_size();
std::stringstream stringStream;
stringStream << tile_size.w << "," << tile_size.h;
safe_strcpy(value, value_size, stringStream.str().c_str());
return heif_error_ok;
}
const heif_error &ojph_get_parameter_tilepart_division(encoder_struct_ojph *encoder, char *value, int value_size)
{
bool res = encoder->codestream.is_tilepart_division_at_resolutions();
bool comp = encoder->codestream.is_tilepart_division_at_components();
if (res && comp) {
safe_strcpy(value, value_size, "both");
} else if (res) {
safe_strcpy(value, value_size, "resolution");
} else if (comp) {
safe_strcpy(value, value_size, "component");
} else {
safe_strcpy(value, value_size, "none");
}
return heif_error_ok;
}
const heif_error &ojph_get_parameter_block_dimensions(encoder_struct_ojph *encoder, char *value, int value_size)
{
ojph::size block_dims = encoder->codestream.access_cod().get_block_dims();
std::stringstream stringStream;
stringStream << block_dims.w << "," << block_dims.h;
safe_strcpy(value, value_size, stringStream.str().c_str());
return heif_error_ok;
}
struct heif_error ojph_get_parameter_string(void *encoder_raw, const char *name, char *value, int value_size)
{
struct encoder_struct_ojph* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (strcmp(name, kParam_chroma) == 0) {
return ojph_get_parameter_chroma(encoder, value, value_size);
} else if (strcmp(name, kParam_progression_order) == 0) {
return ojph_get_parameter_progression_order(encoder, value, value_size);
} else if (strcmp(name, kParam_codestream_comment) == 0) {
return ojph_get_parameter_codestream_comment(encoder, value, value_size);
} else if (strcmp(name, kParam_tile_size) == 0) {
return ojph_get_parameter_tile_size(encoder, value, value_size);
} else if (strcmp(name, kParam_tilepart_division) == 0) {
return ojph_get_parameter_tilepart_division(encoder, value, value_size);
} else if (strcmp(name, kParam_block_dimensions) == 0) {
return ojph_get_parameter_block_dimensions(encoder, value, value_size);
} else {
return heif_error_unsupported_parameter;
}
}
//////////// String parameter setters
static const heif_error &ojph_set_chroma(encoder_struct_ojph *encoder, const char *value)
{
if (strcmp(value, "420") == 0) {
encoder->chroma = heif_chroma_420;
return heif_error_ok;
} else if (strcmp(value, "422") == 0) {
encoder->chroma = heif_chroma_422;
return heif_error_ok;
} else if (strcmp(value, "444") == 0) {
encoder->chroma = heif_chroma_444;
return heif_error_ok;
} else {
return heif_error_invalid_parameter_value;
}
}
static bool string_list_contains(const char* const* values_list, const char* value)
{
for (int i = 0; values_list[i]; i++) {
if (strcmp(values_list[i], value) == 0) {
return true;
}
}
return false;
}
static const heif_error &ojph_set_progression_order(encoder_struct_ojph *encoder, const char *value)
{
if (string_list_contains(kParam_progression_order_valid_values, value)) {
encoder->codestream.access_cod().set_progression_order(value);
return heif_error_ok;
} else {
return heif_error_invalid_parameter_value;
}
}
static const heif_error &ojph_set_codestream_comment(encoder_struct_ojph *encoder, const char *value)
{
if (value != nullptr) {
encoder->comment = std::string(value);
}
return heif_error_ok;
}
static const heif_error &ojph_set_tile_size(encoder_struct_ojph *encoder, const char *value)
{
std::string valueStr(value);
size_t commaOffset = valueStr.find(",");
if (commaOffset == std::string::npos) {
return heif_error_invalid_parameter_value;
}
std::string xTSizText = valueStr.substr(0, commaOffset);
unsigned long xTSiz = std::stoul(xTSizText);
std::string yTSizText = valueStr.substr(commaOffset + 1);
unsigned long yTSiz = std::stoul(yTSizText);
if ((xTSiz < 1)
|| (xTSiz > std::numeric_limits<unsigned int>::max())
|| (yTSiz < 1)
|| (yTSiz > std::numeric_limits<unsigned int>::max())) {
return heif_error_invalid_parameter_value;
}
encoder->codestream.access_siz().set_tile_size(ojph::size((ojph::ui32)xTSiz, (ojph::ui32)yTSiz));
return heif_error_ok;
}
static const heif_error &ojph_set_tilepart_division(encoder_struct_ojph *encoder, const char *value)
{
if (strcmp(value, "none") == 0) {
encoder->codestream.set_tilepart_divisions(false, false);
return heif_error_ok;
} else if (strcmp(value, "resolution") == 0) {
encoder->codestream.set_tilepart_divisions(true, false);
return heif_error_ok;
} else if (strcmp(value, "component") == 0) {
encoder->codestream.set_tilepart_divisions(false, true);
return heif_error_ok;
} else if (strcmp(value, "both") == 0) {
encoder->codestream.set_tilepart_divisions(true, true);
return heif_error_ok;
} else {
return heif_error_invalid_parameter_value;
}
}
// Get the base 2 logarithm for code block sizes. See ITU-T T.800 (11/2015) Table A.18
// Values are encoded 0 to 8 (i.e. its -2)
static const int log_base_2(unsigned long v)
{
switch (v) {
case 4:
return 2 - 2;
case 8:
return 3 - 2;
case 16:
return 4 - 2;
case 32:
return 5 - 2;
case 64:
return 6 - 2;
case 128:
return 7 - 2;
case 256:
return 8 - 2;
case 512:
return 9 - 2;
case 1024:
return 10 - 2;
default:
// any other value is invalid
return -1;
}
}
static const heif_error &ojph_set_block_dimensions(encoder_struct_ojph *encoder, const char *value)
{
std::string valueStr(value);
size_t commaOffset = valueStr.find(",");
if (commaOffset == std::string::npos) {
return heif_error_invalid_parameter_value;
}
std::string widthText = valueStr.substr(0, commaOffset);
unsigned long width = std::stoul(widthText);
std::string heightText = valueStr.substr(commaOffset + 1);
unsigned long height = std::stoul(heightText);
int xcb = log_base_2(width);
int ycb = log_base_2(height);
if ((xcb == -1) || (ycb == -1) || (xcb + ycb > 12)) {
return heif_error_invalid_parameter_value;
}
encoder->codestream.access_cod().set_block_dims((ojph::ui32)width, (ojph::ui32)height);
return heif_error_ok;
}
struct heif_error ojph_set_parameter_string(void *encoder_raw, const char *name, const char *value)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (strcmp(name, kParam_chroma) == 0) {
return ojph_set_chroma(encoder, value);
} else if (strcmp(name, kParam_progression_order) == 0) {
return ojph_set_progression_order(encoder, value);
} else if (strcmp(name, kParam_codestream_comment) == 0) {
return ojph_set_codestream_comment(encoder, value);
} else if (strcmp(name, kParam_tile_size) == 0) {
return ojph_set_tile_size(encoder, value);
} else if (strcmp(name, kParam_tilepart_division) == 0) {
return ojph_set_tilepart_division(encoder, value);
} else if (strcmp(name, kParam_block_dimensions) == 0) {
return ojph_set_block_dimensions(encoder, value);
} else {
return heif_error_unsupported_parameter;
}
}
static void ojph_set_default_parameters(void* encoder_raw)
{
struct encoder_struct_ojph* encoder = (struct encoder_struct_ojph*) encoder_raw;
for (const struct heif_encoder_parameter** p = ojph_encoder_parameter_ptrs; *p; p++) {
const struct heif_encoder_parameter* param = *p;
if (param->has_default) {
switch (param->type) {
case heif_encoder_parameter_type_integer:
ojph_set_parameter_integer(encoder, param->name, param->integer.default_value);
break;
case heif_encoder_parameter_type_boolean:
ojph_set_parameter_boolean(encoder, param->name, param->boolean.default_value);
break;
case heif_encoder_parameter_type_string:
ojph_set_parameter_string(encoder, param->name, param->string.default_value);
break;
}
}
}
}
///// Actual encoding functionality
struct heif_error ojph_new_encoder(void** encoder_out)
{
struct encoder_struct_ojph* encoder = new encoder_struct_ojph();
encoder->outfile.open();
*encoder_out = encoder;
ojph_set_default_parameters(encoder);
return heif_error_ok;
}
void ojph_free_encoder(void* encoder_raw)
{
struct encoder_struct_ojph* encoder = (struct encoder_struct_ojph*) encoder_raw;
encoder->codestream.close();
delete encoder;
}
struct heif_error ojph_set_parameter_logging_level(void* encoder, int logging)
{
// No logging level options in OpenJPH
return heif_error_ok;
}
struct heif_error ojph_get_parameter_logging_level(void* encoder, int* logging)
{
// No logging level options in OpenJPH
return heif_error_ok;
}
const struct heif_encoder_parameter** ojph_list_parameters(void* encoder_raw)
{
return ojph_encoder_parameter_ptrs;
}
void ojph_query_input_colorspace(enum heif_colorspace* inout_colorspace, enum heif_chroma* inout_chroma)
{
// Replace the input colorspace/chroma with the one that is supported by the encoder and that
// comes as close to the input colorspace/chroma as possible.
if (*inout_colorspace == heif_colorspace_monochrome) {
*inout_colorspace = heif_colorspace_monochrome;
*inout_chroma = heif_chroma_monochrome;
}
else {
*inout_colorspace = heif_colorspace_YCbCr;
*inout_chroma = heif_chroma_444;
}
}
void ojph_query_input_colorspace2(void* encoder_raw, enum heif_colorspace* inout_colorspace, enum heif_chroma* inout_chroma)
{
auto* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (*inout_colorspace == heif_colorspace_monochrome) {
*inout_colorspace = heif_colorspace_monochrome;
*inout_chroma = heif_chroma_monochrome;
}
else {
*inout_colorspace = heif_colorspace_YCbCr;
if (encoder->chroma != heif_chroma_undefined) {
*inout_chroma = encoder->chroma;
}
else {
*inout_chroma = heif_chroma_444;
}
}
}
std::vector<heif_channel> build_SIZ(encoder_struct_ojph *encoder, const heif_image *image)
{
std::vector<heif_channel> sourceChannels;
ojph::param_siz siz = encoder->codestream.access_siz();
int width = heif_image_get_primary_width(image);
int height = heif_image_get_primary_height(image);
siz.set_image_extent(ojph::point(width, height));
heif_chroma chroma = heif_image_get_chroma_format(image);
encoder->codestream.set_planar(true);
sourceChannels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr};
siz.set_num_components((ojph::ui32)sourceChannels.size());
for (ojph::ui32 i = 0; i < siz.get_num_components(); i++) {
int bit_depth = heif_image_get_bits_per_pixel_range(image, sourceChannels[i]);
if (sourceChannels[i] == heif_channel_Y) {
siz.set_component(i, ojph::point(1, 1), bit_depth, false);
} else { // Cb or Cr
if (chroma == heif_chroma_444) {
siz.set_component(i, ojph::point(1, 1), bit_depth, false);
} else if (chroma == heif_chroma_422) {
siz.set_component(i, ojph::point(2, 1), bit_depth, false);
} else {
siz.set_component(i, ojph::point(2, 2), bit_depth, false);
}
}
}
siz.set_image_offset(ojph::point(0, 0));
siz.set_tile_offset(ojph::point(0, 0));
return sourceChannels;
}
void build_COD(encoder_struct_ojph *encoder)
{
ojph::param_cod cod = encoder->codestream.access_cod();
cod.set_color_transform(false);
}
struct heif_error ojph_encode_image(void *encoder_raw, const struct heif_image *image, enum heif_image_input_class image_class)
{
struct encoder_struct_ojph* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (heif_image_get_colorspace(image) != heif_colorspace_YCbCr) {
return heif_error{heif_error_Encoding_error,
heif_suberror_Unspecified,
"OpenJPH encoder plugin received image with invalid colorspace."};
}
std::vector<heif_channel> sourceChannels = build_SIZ(encoder, image);
build_COD(encoder);
bool hasComment = (encoder->comment.length() > 0);
ojph::comment_exchange com_ex;
if (hasComment) {
com_ex.set_string(encoder->comment.c_str());
}
encoder->codestream.write_headers(&(encoder->outfile), &com_ex, hasComment ? 1 : 0);
ojph::ui32 next_comp;
ojph::line_buf* cur_line = encoder->codestream.exchange(NULL, next_comp);
for (const auto& sourceChannel : sourceChannels) {
int stride;
const uint8_t *data = heif_image_get_plane_readonly(image, sourceChannel, &stride);
uint32_t component_height = heif_image_get_height(image, sourceChannel);
for (uint32_t y = 0; y < component_height; y++) {
const uint8_t *sourceLine = data + y * stride;
size_t outputWidth = cur_line->size;
ojph::si32 *targetLine = cur_line->i32;
for (uint32_t x = 0; x < outputWidth; x++) {
targetLine[x] = sourceLine[x];
}
cur_line = encoder->codestream.exchange(cur_line, next_comp);
}
}
encoder->codestream.flush();
return heif_error_ok;
}
struct heif_error ojph_get_compressed_data(void* encoder_raw, uint8_t** data, int* size, enum heif_encoded_data_type* type)
{
struct encoder_struct_ojph* encoder = (struct encoder_struct_ojph*) encoder_raw;
if (encoder->data_read) {
*size = 0;
*data = nullptr;
}
else {
*size = (int) encoder->outfile.tell();
*data = (uint8_t*) encoder->outfile.get_data();
encoder->data_read = true;
}
return heif_error_ok;
}
static const int MAX_PLUGIN_NAME_LENGTH = 80;
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
const char* ojph_plugin_name()
{
snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH,
"OpenJPH %s.%s.%s",
OJPH_INT_TO_STRING(OPENJPH_VERSION_MAJOR),
OJPH_INT_TO_STRING(OPENJPH_VERSION_MINOR),
OJPH_INT_TO_STRING(OPENJPH_VERSION_PATCH)
);
plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0;
return plugin_name;
}
static const struct heif_encoder_plugin encoder_plugin_openjph {
/* plugin_api_version */ 3,
/* compression_format */ heif_compression_HTJ2K,
/* id_name */ "openjph",
/* priority */ OJPH_PLUGIN_PRIORITY,
/* supports_lossy_compression */ true,
/* supports_lossless_compression */ true,
/* get_plugin_name */ ojph_plugin_name,
/* init_plugin */ ojph_init_plugin,
/* cleanup_plugin */ ojph_cleanup_plugin,
/* new_encoder */ ojph_new_encoder,
/* free_encoder */ ojph_free_encoder,
/* set_parameter_quality */ ojph_set_parameter_quality,
/* get_parameter_quality */ ojph_get_parameter_quality,
/* set_parameter_lossless */ ojph_set_parameter_lossless,
/* get_parameter_lossless */ ojph_get_parameter_lossless,
/* set_parameter_logging_level */ ojph_set_parameter_logging_level,
/* get_parameter_logging_level */ ojph_get_parameter_logging_level,
/* list_parameters */ ojph_list_parameters,
/* set_parameter_integer */ ojph_set_parameter_integer,
/* get_parameter_integer */ ojph_get_parameter_integer,
/* set_parameter_boolean */ ojph_set_parameter_boolean,
/* get_parameter_boolean */ ojph_get_parameter_boolean,
/* set_parameter_string */ ojph_set_parameter_string,
/* get_parameter_string */ ojph_get_parameter_string,
/* query_input_colorspace */ ojph_query_input_colorspace,
/* encode_image */ ojph_encode_image,
/* get_compressed_data */ ojph_get_compressed_data,
/* query_input_colorspace (v2) */ ojph_query_input_colorspace2,
/* query_encoded_size (v3) */ nullptr
};
const struct heif_encoder_plugin* get_encoder_plugin_openjph()
{
return &encoder_plugin_openjph;
}
#if PLUGIN_OPENJPH_ENCODER
heif_plugin_info plugin_info {
1,
heif_plugin_type_encoder,
&encoder_plugin_openjph
};
#endif