libheif/color-conversion/rgb2rgb.cc (574 lines of code) (raw):
/*
* HEIF codec.
* Copyright (c) 2023 Dirk Farin <dirk.farin@gmail.com>
*
* 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/>.
*/
#include <algorithm>
#include <cassert>
#include <memory>
#include <vector>
#include "rgb2rgb.h"
std::vector<ColorStateWithCost>
Op_RGB_to_RGB24_32::state_after_conversion(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
if (input_state.colorspace != heif_colorspace_RGB ||
input_state.chroma != heif_chroma_444 ||
input_state.bits_per_pixel != 8) {
return {};
}
std::vector<ColorStateWithCost> states;
ColorState output_state;
//ColorConversionCosts costs;
// --- convert to RGBA (with alpha)
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGBA;
output_state.has_alpha = true;
output_state.bits_per_pixel = 8;
states.push_back({output_state, SpeedCosts_Unoptimized});
// --- convert to RGB (without alpha)
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGB;
output_state.has_alpha = false;
output_state.bits_per_pixel = 8;
states.push_back({output_state, SpeedCosts_Unoptimized});
return states;
}
std::shared_ptr<HeifPixelImage>
Op_RGB_to_RGB24_32::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
bool has_alpha = input->has_channel(heif_channel_Alpha);
bool want_alpha = target_state.has_alpha;
if (input->get_bits_per_pixel(heif_channel_R) != 8 ||
input->get_bits_per_pixel(heif_channel_G) != 8 ||
input->get_bits_per_pixel(heif_channel_B) != 8) {
return nullptr;
}
if (has_alpha && input->get_bits_per_pixel(heif_channel_Alpha) != 8) {
return nullptr;
}
auto outimg = std::make_shared<HeifPixelImage>();
int width = input->get_width();
int height = input->get_height();
outimg->create(width, height, heif_colorspace_RGB,
want_alpha ? heif_chroma_interleaved_32bit : heif_chroma_interleaved_24bit);
if (!outimg->add_plane(heif_channel_interleaved, width, height, 8)) {
return nullptr;
}
const uint8_t* in_r, * in_g, * in_b, * in_a = nullptr;
int in_r_stride = 0, in_g_stride = 0, in_b_stride = 0, in_a_stride = 0;
uint8_t* out_p;
int out_p_stride = 0;
in_r = input->get_plane(heif_channel_R, &in_r_stride);
in_g = input->get_plane(heif_channel_G, &in_g_stride);
in_b = input->get_plane(heif_channel_B, &in_b_stride);
out_p = outimg->get_plane(heif_channel_interleaved, &out_p_stride);
if (has_alpha) {
in_a = input->get_plane(heif_channel_Alpha, &in_a_stride);
}
int x, y;
for (y = 0; y < height; y++) {
if (has_alpha && want_alpha) {
for (x = 0; x < width; x++) {
out_p[y * out_p_stride + 4 * x + 0] = in_r[x + y * in_r_stride];
out_p[y * out_p_stride + 4 * x + 1] = in_g[x + y * in_g_stride];
out_p[y * out_p_stride + 4 * x + 2] = in_b[x + y * in_b_stride];
out_p[y * out_p_stride + 4 * x + 3] = in_a[x + y * in_a_stride];
}
}
else if (!want_alpha) {
for (x = 0; x < width; x++) {
out_p[y * out_p_stride + 3 * x + 0] = in_r[x + y * in_r_stride];
out_p[y * out_p_stride + 3 * x + 1] = in_g[x + y * in_g_stride];
out_p[y * out_p_stride + 3 * x + 2] = in_b[x + y * in_b_stride];
}
}
else {
assert(want_alpha && !has_alpha);
for (x = 0; x < width; x++) {
out_p[y * out_p_stride + 4 * x + 0] = in_r[x + y * in_r_stride];
out_p[y * out_p_stride + 4 * x + 1] = in_g[x + y * in_g_stride];
out_p[y * out_p_stride + 4 * x + 2] = in_b[x + y * in_b_stride];
out_p[y * out_p_stride + 4 * x + 3] = 0xFF;
}
}
}
return outimg;
}
std::vector<ColorStateWithCost>
Op_RGB_HDR_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
// Note: no input alpha channel required. It will be filled up with 0xFF.
if (input_state.colorspace != heif_colorspace_RGB ||
input_state.chroma != heif_chroma_444 ||
input_state.bits_per_pixel == 8) {
return {};
}
std::vector<ColorStateWithCost> states;
ColorState output_state;
// --- convert to RRGGBB_BE
if (input_state.has_alpha == false) {
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBB_BE;
output_state.has_alpha = false;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
}
// --- convert to RRGGBBAA_BE
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBBAA_BE;
output_state.has_alpha = true;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
return states;
}
std::shared_ptr<HeifPixelImage>
Op_RGB_HDR_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
if (input->get_bits_per_pixel(heif_channel_R) == 8 ||
input->get_bits_per_pixel(heif_channel_G) == 8 ||
input->get_bits_per_pixel(heif_channel_B) == 8) {
return nullptr;
}
bool input_has_alpha = input->has_channel(heif_channel_Alpha);
bool output_has_alpha = input_has_alpha || target_state.has_alpha;
if (input_has_alpha) {
if (input->get_bits_per_pixel(heif_channel_Alpha) == 8) {
return nullptr;
}
if (input->get_width(heif_channel_Alpha) != input->get_width(heif_channel_G) ||
input->get_height(heif_channel_Alpha) != input->get_height(heif_channel_G)) {
return nullptr;
}
}
int bpp = input->get_bits_per_pixel(heif_channel_R);
if (bpp <= 0) return nullptr;
auto outimg = std::make_shared<HeifPixelImage>();
int width = input->get_width();
int height = input->get_height();
outimg->create(width, height, heif_colorspace_RGB,
output_has_alpha ? heif_chroma_interleaved_RRGGBBAA_BE : heif_chroma_interleaved_RRGGBB_BE);
if (!outimg->add_plane(heif_channel_interleaved, width, height, bpp)) {
return nullptr;
}
const uint16_t* in_r, * in_g, * in_b, * in_a = nullptr;
int in_r_stride = 0, in_g_stride = 0, in_b_stride = 0, in_a_stride = 0;
uint8_t* out_p;
int out_p_stride = 0;
in_r = (uint16_t*) input->get_plane(heif_channel_R, &in_r_stride);
in_g = (uint16_t*) input->get_plane(heif_channel_G, &in_g_stride);
in_b = (uint16_t*) input->get_plane(heif_channel_B, &in_b_stride);
out_p = outimg->get_plane(heif_channel_interleaved, &out_p_stride);
if (input_has_alpha) {
in_a = (uint16_t*) input->get_plane(heif_channel_Alpha, &in_a_stride);
}
in_r_stride /= 2;
in_g_stride /= 2;
in_b_stride /= 2;
in_a_stride /= 2;
const int pixelsize = (output_has_alpha ? 8 : 6);
int x, y;
uint16_t alpha_max = static_cast<uint16_t>((1 << bpp) - 1);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
uint16_t r = in_r[x + y * in_r_stride];
uint16_t g = in_g[x + y * in_g_stride];
uint16_t b = in_b[x + y * in_b_stride];
out_p[y * out_p_stride + pixelsize * x + 0] = (uint8_t)(r >> 8);
out_p[y * out_p_stride + pixelsize * x + 1] = (uint8_t)(r & 0xFF);
out_p[y * out_p_stride + pixelsize * x + 2] = (uint8_t)(g >> 8);
out_p[y * out_p_stride + pixelsize * x + 3] = (uint8_t)(g & 0xFF);
out_p[y * out_p_stride + pixelsize * x + 4] = (uint8_t)(b >> 8);
out_p[y * out_p_stride + pixelsize * x + 5] = (uint8_t)(b & 0xFF);
if (output_has_alpha) {
uint16_t a = input_has_alpha ? in_a[x + y * in_a_stride] : alpha_max;
out_p[y * out_p_stride + pixelsize * x + 6] = (uint8_t)(a >> 8);
out_p[y * out_p_stride + pixelsize * x + 7] = (uint8_t)(a & 0xFF);
}
}
}
return outimg;
}
std::vector<ColorStateWithCost>
Op_RGB_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
// Note: no input alpha channel required. It will be filled up with 0xFF.
if (input_state.colorspace != heif_colorspace_RGB ||
input_state.chroma != heif_chroma_444 ||
input_state.bits_per_pixel != 8) {
return {};
}
std::vector<ColorStateWithCost> states;
ColorState output_state;
// --- convert to RRGGBB_BE
if (input_state.has_alpha == false) {
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBB_BE;
output_state.has_alpha = false;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
}
// --- convert to RRGGBBAA_BE
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBBAA_BE;
output_state.has_alpha = true;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
return states;
}
std::shared_ptr<HeifPixelImage>
Op_RGB_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
if (input->get_bits_per_pixel(heif_channel_R) != 8 ||
input->get_bits_per_pixel(heif_channel_G) != 8 ||
input->get_bits_per_pixel(heif_channel_B) != 8) {
return nullptr;
}
//int bpp = input->get_bits_per_pixel(heif_channel_R);
bool input_has_alpha = input->has_channel(heif_channel_Alpha);
bool output_has_alpha = input_has_alpha || target_state.has_alpha;
if (input_has_alpha && input->get_bits_per_pixel(heif_channel_Alpha) != 8) {
return nullptr;
}
auto outimg = std::make_shared<HeifPixelImage>();
int width = input->get_width();
int height = input->get_height();
outimg->create(width, height, heif_colorspace_RGB,
output_has_alpha ? heif_chroma_interleaved_RRGGBBAA_BE : heif_chroma_interleaved_RRGGBB_BE);
if (!outimg->add_plane(heif_channel_interleaved, width, height, input->get_bits_per_pixel(heif_channel_R))) {
return nullptr;
}
const uint8_t* in_r, * in_g, * in_b, * in_a = nullptr;
int in_r_stride = 0, in_g_stride = 0, in_b_stride = 0, in_a_stride = 0;
uint8_t* out_p;
int out_p_stride = 0;
in_r = input->get_plane(heif_channel_R, &in_r_stride);
in_g = input->get_plane(heif_channel_G, &in_g_stride);
in_b = input->get_plane(heif_channel_B, &in_b_stride);
out_p = outimg->get_plane(heif_channel_interleaved, &out_p_stride);
if (input_has_alpha) {
in_a = input->get_plane(heif_channel_Alpha, &in_a_stride);
}
const int pixelsize = (output_has_alpha ? 8 : 6);
int x, y;
for (y = 0; y < height; y++) {
if (input_has_alpha) {
for (x = 0; x < width; x++) {
out_p[y * out_p_stride + 8 * x + 0] = 0;
out_p[y * out_p_stride + 8 * x + 1] = in_r[x + y * in_r_stride];
out_p[y * out_p_stride + 8 * x + 2] = 0;
out_p[y * out_p_stride + 8 * x + 3] = in_g[x + y * in_g_stride];
out_p[y * out_p_stride + 8 * x + 4] = 0;
out_p[y * out_p_stride + 8 * x + 5] = in_b[x + y * in_b_stride];
out_p[y * out_p_stride + 8 * x + 6] = 0;
out_p[y * out_p_stride + 8 * x + 7] = in_a[x + y * in_a_stride];
}
}
else {
for (x = 0; x < width; x++) {
out_p[y * out_p_stride + pixelsize * x + 0] = 0;
out_p[y * out_p_stride + pixelsize * x + 1] = in_r[x + y * in_r_stride];
out_p[y * out_p_stride + pixelsize * x + 2] = 0;
out_p[y * out_p_stride + pixelsize * x + 3] = in_g[x + y * in_g_stride];
out_p[y * out_p_stride + pixelsize * x + 4] = 0;
out_p[y * out_p_stride + pixelsize * x + 5] = in_b[x + y * in_b_stride];
if (output_has_alpha) {
out_p[y * out_p_stride + pixelsize * x + 6] = 0;
out_p[y * out_p_stride + pixelsize * x + 7] = 0xFF;
}
}
}
}
return outimg;
}
std::vector<ColorStateWithCost>
Op_RRGGBBaa_BE_to_RGB_HDR::state_after_conversion(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
// Note: no input alpha channel required. It will be filled up with 0xFF.
if (input_state.colorspace != heif_colorspace_RGB ||
(input_state.chroma != heif_chroma_interleaved_RRGGBB_BE &&
input_state.chroma != heif_chroma_interleaved_RRGGBBAA_BE) ||
input_state.bits_per_pixel == 8) {
return {};
}
std::vector<ColorStateWithCost> states;
ColorState output_state;
// --- convert to RRGGBB_BE
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_444;
output_state.has_alpha = target_state.has_alpha;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
return states;
}
std::shared_ptr<HeifPixelImage>
Op_RRGGBBaa_BE_to_RGB_HDR::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
bool has_alpha = (input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE ||
input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE);
bool want_alpha = target_state.has_alpha;
auto outimg = std::make_shared<HeifPixelImage>();
int width = input->get_width();
int height = input->get_height();
int bpp = input->get_bits_per_pixel(heif_channel_interleaved);
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_444);
if (!outimg->add_plane(heif_channel_R, width, height, bpp) ||
!outimg->add_plane(heif_channel_G, width, height, bpp) ||
!outimg->add_plane(heif_channel_B, width, height, bpp)) {
return nullptr;
}
if (want_alpha) {
if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp)) {
return nullptr;
}
}
const uint8_t* in_p;
int in_p_stride = 0;
int in_pix_size = has_alpha ? 8 : 6;
uint16_t* out_r, * out_g, * out_b, * out_a = nullptr;
int out_r_stride = 0, out_g_stride = 0, out_b_stride = 0, out_a_stride = 0;
in_p = input->get_plane(heif_channel_interleaved, &in_p_stride);
out_r = (uint16_t*) outimg->get_plane(heif_channel_R, &out_r_stride);
out_g = (uint16_t*) outimg->get_plane(heif_channel_G, &out_g_stride);
out_b = (uint16_t*) outimg->get_plane(heif_channel_B, &out_b_stride);
if (want_alpha) {
out_a = (uint16_t*) outimg->get_plane(heif_channel_Alpha, &out_a_stride);
}
out_r_stride /= 2;
out_g_stride /= 2;
out_b_stride /= 2;
out_a_stride /= 2;
uint16_t alpha_max = static_cast<uint16_t>((1 << bpp) - 1);
int x, y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
uint16_t r = (uint16_t) ((in_p[y * in_p_stride + in_pix_size * x + 0] << 8) |
in_p[y * in_p_stride + in_pix_size * x + 1]);
uint16_t g = (uint16_t) ((in_p[y * in_p_stride + in_pix_size * x + 2] << 8) |
in_p[y * in_p_stride + in_pix_size * x + 3]);
uint16_t b = (uint16_t) ((in_p[y * in_p_stride + in_pix_size * x + 4] << 8) |
in_p[y * in_p_stride + in_pix_size * x + 5]);
out_r[x + y * out_r_stride] = r;
out_g[x + y * out_g_stride] = g;
out_b[x + y * out_b_stride] = b;
if (want_alpha) {
// in_pix_size is always 8 when we have alpha channel
uint16_t a = has_alpha ? (uint16_t) ((in_p[y * in_p_stride + 8 * x + 6] << 8) |
in_p[y * in_p_stride + 8 * x + 7]) : alpha_max;
out_a[x + y * out_a_stride] = a;
}
}
}
return outimg;
}
std::vector<ColorStateWithCost>
Op_RGB24_32_to_RGB::state_after_conversion(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
// Note: no input alpha channel required. It will be filled up with 0xFF.
if (input_state.colorspace != heif_colorspace_RGB ||
(input_state.chroma != heif_chroma_interleaved_RGB &&
input_state.chroma != heif_chroma_interleaved_RGBA) ||
input_state.bits_per_pixel != 8) {
return {};
}
std::vector<ColorStateWithCost> states;
ColorState output_state;
// --- convert to planar RGB
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_444;
output_state.has_alpha = target_state.has_alpha;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
return states;
}
std::shared_ptr<HeifPixelImage>
Op_RGB24_32_to_RGB::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
bool has_alpha = input->get_chroma_format() == heif_chroma_interleaved_RGBA;
bool want_alpha = target_state.has_alpha;
auto outimg = std::make_shared<HeifPixelImage>();
int width = input->get_width();
int height = input->get_height();
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_444);
if (!outimg->add_plane(heif_channel_R, width, height, 8) ||
!outimg->add_plane(heif_channel_G, width, height, 8) ||
!outimg->add_plane(heif_channel_B, width, height, 8)) {
return nullptr;
}
if (want_alpha) {
if (!outimg->add_plane(heif_channel_Alpha, width, height, 8)) {
return nullptr;
}
}
const uint8_t* in_p;
int in_p_stride = 0;
int in_pix_size = has_alpha ? 4 : 3;
uint8_t* out_r, * out_g, * out_b, * out_a = nullptr;
int out_r_stride = 0, out_g_stride = 0, out_b_stride = 0, out_a_stride = 0;
in_p = input->get_plane(heif_channel_interleaved, &in_p_stride);
out_r = outimg->get_plane(heif_channel_R, &out_r_stride);
out_g = outimg->get_plane(heif_channel_G, &out_g_stride);
out_b = outimg->get_plane(heif_channel_B, &out_b_stride);
if (want_alpha) {
out_a = outimg->get_plane(heif_channel_Alpha, &out_a_stride);
}
int x, y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
out_r[x + y * out_r_stride] = in_p[y * in_p_stride + in_pix_size * x + 0];
out_g[x + y * out_g_stride] = in_p[y * in_p_stride + in_pix_size * x + 1];
out_b[x + y * out_b_stride] = in_p[y * in_p_stride + in_pix_size * x + 2];
if (want_alpha) {
uint8_t a = has_alpha ? in_p[y * in_p_stride + in_pix_size * x + 3] : 0xff;
out_a[x + y * out_a_stride] = a;
}
}
}
return outimg;
}
std::vector<ColorStateWithCost>
Op_RRGGBBaa_swap_endianness::state_after_conversion(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
// Note: no input alpha channel required. It will be filled up with 0xFF.
if (input_state.colorspace != heif_colorspace_RGB ||
(input_state.chroma != heif_chroma_interleaved_RRGGBB_LE &&
input_state.chroma != heif_chroma_interleaved_RRGGBB_BE &&
input_state.chroma != heif_chroma_interleaved_RRGGBBAA_LE &&
input_state.chroma != heif_chroma_interleaved_RRGGBBAA_BE)) {
return {};
}
std::vector<ColorStateWithCost> states;
ColorState output_state;
// --- swap RRGGBB
if (input_state.chroma == heif_chroma_interleaved_RRGGBB_LE ||
input_state.chroma == heif_chroma_interleaved_RRGGBB_BE) {
output_state.colorspace = heif_colorspace_RGB;
if (input_state.chroma == heif_chroma_interleaved_RRGGBB_LE) {
output_state.chroma = heif_chroma_interleaved_RRGGBB_BE;
}
else {
output_state.chroma = heif_chroma_interleaved_RRGGBB_LE;
}
output_state.has_alpha = false;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
}
// --- swap RRGGBBAA
if (input_state.chroma == heif_chroma_interleaved_RRGGBBAA_LE ||
input_state.chroma == heif_chroma_interleaved_RRGGBBAA_BE) {
output_state.colorspace = heif_colorspace_RGB;
if (input_state.chroma == heif_chroma_interleaved_RRGGBBAA_LE) {
output_state.chroma = heif_chroma_interleaved_RRGGBBAA_BE;
}
else {
output_state.chroma = heif_chroma_interleaved_RRGGBBAA_LE;
}
output_state.has_alpha = true;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Unoptimized});
}
return states;
}
std::shared_ptr<HeifPixelImage>
Op_RRGGBBaa_swap_endianness::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
auto outimg = std::make_shared<HeifPixelImage>();
int width = input->get_width();
int height = input->get_height();
switch (input->get_chroma_format()) {
case heif_chroma_interleaved_RRGGBB_LE:
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RRGGBB_BE);
break;
case heif_chroma_interleaved_RRGGBB_BE:
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RRGGBB_LE);
break;
case heif_chroma_interleaved_RRGGBBAA_LE:
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RRGGBBAA_BE);
break;
case heif_chroma_interleaved_RRGGBBAA_BE:
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RRGGBBAA_LE);
break;
default:
return nullptr;
}
if (!outimg->add_plane(heif_channel_interleaved, width, height,
input->get_bits_per_pixel(heif_channel_interleaved))) {
return nullptr;
}
const uint8_t* in_p = nullptr;
int in_p_stride = 0;
uint8_t* out_p;
int out_p_stride = 0;
in_p = input->get_plane(heif_channel_interleaved, &in_p_stride);
out_p = outimg->get_plane(heif_channel_interleaved, &out_p_stride);
int n_bytes = std::min(in_p_stride, out_p_stride);
int x, y;
for (y = 0; y < height; y++) {
for (x = 0; x < n_bytes; x += 2) {
out_p[y * out_p_stride + x + 0] = in_p[y * in_p_stride + x + 1];
out_p[y * out_p_stride + x + 1] = in_p[y * in_p_stride + x + 0];
}
}
return outimg;
}
template<class Pixel>
std::vector<ColorStateWithCost>
Op_RGBA_GENERAL_to_RGB_GENTRAL<Pixel>::state_after_conversion(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
// Note: no input alpha channel required. It will be filled up with 0xFF.
if (input_state.colorspace != heif_colorspace_RGB ||
input_state.chroma != heif_chroma_interleaved_RGBA ||
!input_state.has_alpha) {
return {};
}
std::vector<ColorStateWithCost> states;
ColorState output_state;
// --- convert to RGB
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGB;
output_state.has_alpha = false;
output_state.bits_per_pixel = input_state.bits_per_pixel;
states.push_back({output_state, SpeedCosts_Trivial});
return states;
}
template<class Pixel>
std::shared_ptr<HeifPixelImage>
Op_RGBA_GENERAL_to_RGB_GENTRAL<Pixel>::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options) const
{
bool hdr = !std::is_same<Pixel, uint8_t>::value;
int bpp = input->get_bits_per_pixel(heif_channel_interleaved);
if ((bpp != 8) != hdr) {
return nullptr;
}
int width = input->get_width();
int height = input->get_height();
auto outimg = std::make_shared<HeifPixelImage>();
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RGB);
outimg->add_plane(heif_channel_interleaved, width, height, bpp);
int input_stride = 0;
const Pixel* in_data = (const Pixel*) input->get_plane(heif_channel_interleaved, &input_stride);
int output_stride = 0;
Pixel* out_data = (Pixel*)outimg->get_plane(heif_channel_interleaved, &output_stride);
int x, y;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
out_data[y * output_stride + 3 * x + 0] = in_data[y * input_stride + 4 * x + 0];
out_data[y * output_stride + 3 * x + 1] = in_data[y * input_stride + 4 * x + 1];
out_data[y * output_stride + 3 * x + 2] = in_data[y * input_stride + 4 * x + 2];
}
}
return outimg;
}
template class Op_RGBA_GENERAL_to_RGB_GENTRAL<uint8_t>;
template class Op_RGBA_GENERAL_to_RGB_GENTRAL<uint16_t>;