libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

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_