image_utils.h (6529B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #ifndef TOOLS_HDR_IMAGE_UTILS_H_ 7 #define TOOLS_HDR_IMAGE_UTILS_H_ 8 9 #include <jxl/cms.h> 10 #include <jxl/cms_interface.h> 11 12 #include "lib/extras/enc/apng.h" 13 #include "lib/extras/enc/encode.h" 14 #include "lib/extras/enc/exr.h" 15 #include "lib/extras/enc/jpg.h" 16 #include "lib/extras/enc/pgx.h" 17 #include "lib/extras/enc/pnm.h" 18 #include "lib/extras/packed_image.h" 19 #include "lib/extras/packed_image_convert.h" 20 #include "lib/jxl/base/status.h" 21 #include "lib/jxl/codec_in_out.h" 22 #include "lib/jxl/image_bundle.h" 23 24 namespace jpegxl { 25 namespace tools { 26 27 static inline jxl::Status TransformCodecInOutTo( 28 jxl::CodecInOut& io, const jxl::ColorEncoding& c_desired, 29 jxl::ThreadPool* pool) { 30 const JxlCmsInterface& cms = *JxlGetDefaultCms(); 31 if (io.metadata.m.have_preview) { 32 JXL_RETURN_IF_ERROR(io.preview_frame.TransformTo(c_desired, cms, pool)); 33 } 34 for (jxl::ImageBundle& ib : io.frames) { 35 JXL_RETURN_IF_ERROR(ib.TransformTo(c_desired, cms, pool)); 36 } 37 return true; 38 } 39 40 static inline jxl::Status Encode(const jxl::CodecInOut& io, 41 const jxl::extras::Codec codec, 42 const jxl::ColorEncoding& c_desired, 43 size_t bits_per_sample, 44 std::vector<uint8_t>* bytes, 45 jxl::ThreadPool* pool) { 46 bytes->clear(); 47 JXL_CHECK(!io.Main().c_current().ICC().empty()); 48 JXL_CHECK(!c_desired.ICC().empty()); 49 io.CheckMetadata(); 50 if (io.Main().IsJPEG()) { 51 JXL_WARNING("Writing JPEG data as pixels"); 52 } 53 JxlPixelFormat format = { 54 0, // num_channels is ignored by the converter 55 bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 56 0}; 57 const bool floating_point = bits_per_sample > 16; 58 std::unique_ptr<jxl::extras::Encoder> encoder; 59 std::ostringstream os; 60 switch (codec) { 61 case jxl::extras::Codec::kPNG: 62 encoder = jxl::extras::GetAPNGEncoder(); 63 if (encoder) { 64 break; 65 } else { 66 return JXL_FAILURE("JPEG XL was built without (A)PNG support"); 67 } 68 case jxl::extras::Codec::kJPG: 69 format.data_type = JXL_TYPE_UINT8; 70 encoder = jxl::extras::GetJPEGEncoder(); 71 if (encoder) { 72 os << io.jpeg_quality; 73 encoder->SetOption("q", os.str()); 74 break; 75 } else { 76 return JXL_FAILURE("JPEG XL was built without JPEG support"); 77 } 78 case jxl::extras::Codec::kPNM: 79 if (io.Main().HasAlpha()) { 80 encoder = jxl::extras::GetPAMEncoder(); 81 } else if (io.Main().IsGray()) { 82 encoder = jxl::extras::GetPGMEncoder(); 83 } else if (!floating_point) { 84 encoder = jxl::extras::GetPPMEncoder(); 85 } else { 86 format.data_type = JXL_TYPE_FLOAT; 87 format.endianness = JXL_LITTLE_ENDIAN; 88 encoder = jxl::extras::GetPFMEncoder(); 89 } 90 break; 91 case jxl::extras::Codec::kPGX: 92 encoder = jxl::extras::GetPGXEncoder(); 93 break; 94 case jxl::extras::Codec::kGIF: 95 return JXL_FAILURE("Encoding to GIF is not implemented"); 96 case jxl::extras::Codec::kEXR: 97 format.data_type = JXL_TYPE_FLOAT; 98 encoder = jxl::extras::GetEXREncoder(); 99 if (encoder) { 100 break; 101 } else { 102 return JXL_FAILURE("JPEG XL was built without OpenEXR support"); 103 } 104 case jxl::extras::Codec::kJXL: 105 // TODO(user): implement 106 return JXL_FAILURE("Codec::kJXL is not supported yet"); 107 108 case jxl::extras::Codec::kUnknown: 109 return JXL_FAILURE("Cannot encode using Codec::kUnknown"); 110 } 111 112 if (!encoder) { 113 return JXL_FAILURE("Invalid codec."); 114 } 115 116 jxl::extras::PackedPixelFile ppf; 117 JXL_RETURN_IF_ERROR( 118 ConvertCodecInOutToPackedPixelFile(io, format, c_desired, pool, &ppf)); 119 ppf.info.bits_per_sample = bits_per_sample; 120 if (format.data_type == JXL_TYPE_FLOAT) { 121 ppf.info.bits_per_sample = 32; 122 ppf.info.exponent_bits_per_sample = 8; 123 } 124 jxl::extras::EncodedImage encoded_image; 125 JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded_image, pool)); 126 JXL_ASSERT(encoded_image.bitstreams.size() == 1); 127 *bytes = encoded_image.bitstreams[0]; 128 129 return true; 130 } 131 132 static inline jxl::Status Encode(const jxl::CodecInOut& io, 133 const jxl::ColorEncoding& c_desired, 134 size_t bits_per_sample, 135 const std::string& pathname, 136 std::vector<uint8_t>* bytes, 137 jxl::ThreadPool* pool) { 138 std::string extension; 139 const jxl::extras::Codec codec = 140 jxl::extras::CodecFromPath(pathname, &bits_per_sample, &extension); 141 142 // Warn about incorrect usage of PGM/PGX/PPM - only the latter supports 143 // color, but CodecFromPath lumps them all together. 144 if (codec == jxl::extras::Codec::kPNM && extension != ".pfm") { 145 if (io.Main().HasAlpha() && extension != ".pam") { 146 JXL_WARNING( 147 "For images with alpha, the filename should end with .pam.\n"); 148 } else if (!io.Main().IsGray() && extension == ".pgm") { 149 JXL_WARNING("For color images, the filename should end with .ppm.\n"); 150 } else if (io.Main().IsGray() && extension == ".ppm") { 151 JXL_WARNING( 152 "For grayscale images, the filename should not end with .ppm.\n"); 153 } 154 if (bits_per_sample > 16) { 155 JXL_WARNING("PPM only supports up to 16 bits per sample"); 156 bits_per_sample = 16; 157 } 158 } else if (codec == jxl::extras::Codec::kPGX && !io.Main().IsGray()) { 159 JXL_WARNING("Storing color image to PGX - use .ppm extension instead.\n"); 160 } 161 if (bits_per_sample > 16 && codec == jxl::extras::Codec::kPNG) { 162 JXL_WARNING("PNG only supports up to 16 bits per sample"); 163 bits_per_sample = 16; 164 } 165 166 return Encode(io, codec, c_desired, bits_per_sample, bytes, pool); 167 } 168 169 static inline jxl::Status Encode(const jxl::CodecInOut& io, 170 const std::string& pathname, 171 std::vector<uint8_t>* bytes, 172 jxl::ThreadPool* pool) { 173 // TODO(lode): need to take the floating_point_sample field into account 174 return Encode(io, io.metadata.m.color_encoding, 175 io.metadata.m.bit_depth.bits_per_sample, pathname, bytes, pool); 176 } 177 178 } // namespace tools 179 } // namespace jpegxl 180 181 #endif // TOOLS_HDR_IMAGE_UTILS_H_