1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
// Copyright 2023 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
#include "convert_command.h"
#include "avif/avif_cxx.h"
#include "avifutil.h"
#include "imageio.h"
#include "swapbase_command.h"
namespace avif {
ConvertCommand::ConvertCommand()
: ProgramCommand("convert", "Convert a JPEG with a gain map to AVIF",
"If features like --swap-base are not needed, avifenc can "
"also be used to convert JPEGs to AVIF.") {
argparse_.add_argument(arg_input_filename_, "input_filename.jpg");
argparse_.add_argument(arg_output_filename_, "output_image.avif");
argparse_.add_argument(arg_swap_base_, "--swap-base")
.help("Make the HDR image the base image")
.action(argparse::Action::STORE_TRUE)
.default_value("false");
argparse_.add_argument(arg_gain_map_quality_, "--qgain-map")
.help("Quality for the gain map (0-100, where 100 is lossless)")
.default_value("60");
argparse_.add_argument<CicpValues, CicpConverter>(arg_cicp_, "--cicp")
.help(
"Set the CICP values for the input image, expressed as "
"P/T/M where P = color primaries, T = transfer characteristics, "
"M = matrix coefficients.");
argparse_
.add_argument<avifContentLightLevelInformationBox, ClliConverter>(
arg_clli_, "--clli")
.help(
"Set the content light level information of the alternate image, "
"expressed as: MaxCLL,MaxPALL.");
arg_image_encode_.Init(argparse_, /*can_have_alpha=*/false);
arg_image_read_.Init(argparse_);
}
avifResult ConvertCommand::Run() {
#if !defined(AVIF_ENABLE_JPEG_GAIN_MAP_CONVERSION)
std::cout << "JPEG gainmap conversion unavailable because avifgainmaputil "
"was not built with libxml2.\n";
return AVIF_RESULT_NOT_IMPLEMENTED;
#else
const avifPixelFormat pixel_format =
static_cast<avifPixelFormat>(arg_image_read_.pixel_format.value());
ImagePtr image(avifImageCreateEmpty());
if (image == nullptr) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
if (arg_cicp_.provenance() == argparse::Provenance::SPECIFIED) {
image->colorPrimaries = arg_cicp_.value().color_primaries;
image->transferCharacteristics = arg_cicp_.value().transfer_characteristics;
image->matrixCoefficients = arg_cicp_.value().matrix_coefficients;
}
avifResult result =
ReadImage(image.get(), arg_input_filename_.value(), pixel_format,
arg_image_read_.depth, arg_image_read_.ignore_profile,
/*ignore_gain_map*/ false);
if (result != AVIF_RESULT_OK) {
std::cout << "Failed to decode image: " << arg_input_filename_;
return result;
}
if (image->gainMap && image->gainMap->altICC.size == 0) {
if (image->gainMap->altColorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED) {
// Assume the alternate image has the same primaries as the base image.
image->gainMap->altColorPrimaries = image->colorPrimaries;
}
if (image->gainMap->altTransferCharacteristics ==
AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) {
// Assume the alternate image is PQ HDR.
image->gainMap->altTransferCharacteristics =
AVIF_TRANSFER_CHARACTERISTICS_PQ;
}
}
if (image->gainMap == nullptr || image->gainMap->image == nullptr) {
std::cerr << "Input image " << arg_input_filename_
<< " does not contain a gain map\n";
return AVIF_RESULT_INVALID_ARGUMENT;
}
image->gainMap->altCLLI = arg_clli_.value();
if (arg_swap_base_) {
int depth = arg_image_read_.depth;
if (depth == 0) {
depth = image->gainMap->alternateHdrHeadroom.n == 0 ? 8 : 10;
}
ImagePtr new_base(avifImageCreateEmpty());
if (new_base == nullptr) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
result = ChangeBase(*image, depth, image->yuvFormat, new_base.get());
if (result != AVIF_RESULT_OK) {
return result;
}
std::swap(image, new_base);
}
EncoderPtr encoder(avifEncoderCreate());
if (encoder == nullptr) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
encoder->quality = arg_image_encode_.quality;
encoder->qualityAlpha = arg_image_encode_.quality_alpha;
encoder->qualityGainMap = arg_gain_map_quality_;
encoder->speed = arg_image_encode_.speed;
result = WriteAvifGrid(image.get(), arg_image_encode_.grid.value().grid_cols,
arg_image_encode_.grid.value().grid_rows,
encoder.get(), arg_output_filename_);
if (result != AVIF_RESULT_OK) {
std::cout << "Failed to encode image: " << avifResultToString(result)
<< " (" << encoder->diag.error << ")\n";
return result;
}
return AVIF_RESULT_OK;
#endif // !defined(AVIF_ENABLE_JPEG_GAIN_MAP_CONVERSION)
}
} // namespace avif
|