libjxl

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

pnm.cc (12394B)


      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 #include "lib/extras/enc/pnm.h"
      7 
      8 #include <string.h>
      9 
     10 #include <string>
     11 #include <vector>
     12 
     13 #include "lib/extras/packed_image.h"
     14 #include "lib/jxl/base/byte_order.h"
     15 #include "lib/jxl/base/compiler_specific.h"
     16 #include "lib/jxl/base/printf_macros.h"
     17 #include "lib/jxl/base/status.h"
     18 #include "lib/jxl/dec_external_image.h"
     19 #include "lib/jxl/enc_external_image.h"
     20 #include "lib/jxl/enc_image_bundle.h"
     21 #include "lib/jxl/fields.h"  // AllDefault
     22 #include "lib/jxl/image.h"
     23 #include "lib/jxl/image_bundle.h"
     24 
     25 namespace jxl {
     26 namespace extras {
     27 namespace {
     28 
     29 constexpr size_t kMaxHeaderSize = 200;
     30 
     31 class BasePNMEncoder : public Encoder {
     32  public:
     33   Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
     34                 ThreadPool* pool) const override {
     35     JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info));
     36     if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() ||
     37         !ppf.metadata.jumbf.empty() || !ppf.metadata.xmp.empty()) {
     38       JXL_WARNING("PNM encoder ignoring metadata - use a different codec");
     39     }
     40     encoded_image->icc = ppf.icc;
     41     encoded_image->bitstreams.clear();
     42     encoded_image->bitstreams.reserve(ppf.frames.size());
     43     for (const auto& frame : ppf.frames) {
     44       JXL_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info));
     45       encoded_image->bitstreams.emplace_back();
     46       JXL_RETURN_IF_ERROR(
     47           EncodeFrame(ppf, frame, &encoded_image->bitstreams.back()));
     48     }
     49     for (size_t i = 0; i < ppf.extra_channels_info.size(); ++i) {
     50       const auto& ec_info = ppf.extra_channels_info[i].ec_info;
     51       encoded_image->extra_channel_bitstreams.emplace_back();
     52       auto& ec_bitstreams = encoded_image->extra_channel_bitstreams.back();
     53       for (const auto& frame : ppf.frames) {
     54         ec_bitstreams.emplace_back();
     55         JXL_RETURN_IF_ERROR(EncodeExtraChannel(frame.extra_channels[i],
     56                                                ec_info.bits_per_sample,
     57                                                &ec_bitstreams.back()));
     58       }
     59     }
     60     return true;
     61   }
     62 
     63  protected:
     64   virtual Status EncodeFrame(const PackedPixelFile& ppf,
     65                              const PackedFrame& frame,
     66                              std::vector<uint8_t>* bytes) const = 0;
     67   virtual Status EncodeExtraChannel(const PackedImage& image,
     68                                     size_t bits_per_sample,
     69                                     std::vector<uint8_t>* bytes) const = 0;
     70 };
     71 
     72 class PNMEncoder : public BasePNMEncoder {
     73  public:
     74   static const std::vector<JxlPixelFormat> kAcceptedFormats;
     75 
     76   std::vector<JxlPixelFormat> AcceptedFormats() const override {
     77     return kAcceptedFormats;
     78   }
     79 
     80   Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame,
     81                      std::vector<uint8_t>* bytes) const override {
     82     return EncodeImage(frame.color, ppf.info.bits_per_sample, bytes);
     83   }
     84   Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample,
     85                             std::vector<uint8_t>* bytes) const override {
     86     return EncodeImage(image, bits_per_sample, bytes);
     87   }
     88 
     89  private:
     90   Status EncodeImage(const PackedImage& image, size_t bits_per_sample,
     91                      std::vector<uint8_t>* bytes) const {
     92     uint32_t maxval = (1u << bits_per_sample) - 1;
     93     char type = image.format.num_channels == 1 ? '5' : '6';
     94     char header[kMaxHeaderSize];
     95     size_t header_size =
     96         snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%u\n",
     97                  type, image.xsize, image.ysize, maxval);
     98     JXL_RETURN_IF_ERROR(header_size < kMaxHeaderSize);
     99     bytes->resize(header_size + image.pixels_size);
    100     memcpy(bytes->data(), header, header_size);
    101     memcpy(bytes->data() + header_size,
    102            reinterpret_cast<uint8_t*>(image.pixels()), image.pixels_size);
    103     return true;
    104   }
    105 };
    106 
    107 class PGMEncoder : public PNMEncoder {
    108  public:
    109   static const std::vector<JxlPixelFormat> kAcceptedFormats;
    110 
    111   std::vector<JxlPixelFormat> AcceptedFormats() const override {
    112     return kAcceptedFormats;
    113   }
    114 };
    115 
    116 const std::vector<JxlPixelFormat> PGMEncoder::kAcceptedFormats = {
    117     JxlPixelFormat{1, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0},
    118     JxlPixelFormat{1, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}};
    119 
    120 class PPMEncoder : public PNMEncoder {
    121  public:
    122   static const std::vector<JxlPixelFormat> kAcceptedFormats;
    123 
    124   std::vector<JxlPixelFormat> AcceptedFormats() const override {
    125     return kAcceptedFormats;
    126   }
    127 };
    128 
    129 const std::vector<JxlPixelFormat> PPMEncoder::kAcceptedFormats = {
    130     JxlPixelFormat{3, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0},
    131     JxlPixelFormat{3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}};
    132 
    133 const std::vector<JxlPixelFormat> PNMEncoder::kAcceptedFormats = [] {
    134   std::vector<JxlPixelFormat> combined = PPMEncoder::kAcceptedFormats;
    135   combined.insert(combined.end(), PGMEncoder::kAcceptedFormats.begin(),
    136                   PGMEncoder::kAcceptedFormats.end());
    137   return combined;
    138 }();
    139 
    140 class PFMEncoder : public BasePNMEncoder {
    141  public:
    142   std::vector<JxlPixelFormat> AcceptedFormats() const override {
    143     std::vector<JxlPixelFormat> formats;
    144     for (const uint32_t num_channels : {1, 3}) {
    145       for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
    146         formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
    147                                          /*data_type=*/JXL_TYPE_FLOAT,
    148                                          /*endianness=*/endianness,
    149                                          /*align=*/0});
    150       }
    151     }
    152     return formats;
    153   }
    154   Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame,
    155                      std::vector<uint8_t>* bytes) const override {
    156     return EncodeImage(frame.color, bytes);
    157   }
    158   Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample,
    159                             std::vector<uint8_t>* bytes) const override {
    160     return EncodeImage(image, bytes);
    161   }
    162 
    163  private:
    164   Status EncodeImage(const PackedImage& image,
    165                      std::vector<uint8_t>* bytes) const {
    166     char type = image.format.num_channels == 1 ? 'f' : 'F';
    167     double scale = image.format.endianness == JXL_LITTLE_ENDIAN ? -1.0 : 1.0;
    168     char header[kMaxHeaderSize];
    169     size_t header_size =
    170         snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%.1f\n",
    171                  type, image.xsize, image.ysize, scale);
    172     JXL_RETURN_IF_ERROR(header_size < kMaxHeaderSize);
    173     bytes->resize(header_size + image.pixels_size);
    174     memcpy(bytes->data(), header, header_size);
    175     const uint8_t* in = reinterpret_cast<const uint8_t*>(image.pixels());
    176     uint8_t* out = bytes->data() + header_size;
    177     for (size_t y = 0; y < image.ysize; ++y) {
    178       size_t y_out = image.ysize - 1 - y;
    179       const uint8_t* row_in = &in[y * image.stride];
    180       uint8_t* row_out = &out[y_out * image.stride];
    181       memcpy(row_out, row_in, image.stride);
    182     }
    183     return true;
    184   }
    185 };
    186 
    187 class PAMEncoder : public BasePNMEncoder {
    188  public:
    189   std::vector<JxlPixelFormat> AcceptedFormats() const override {
    190     std::vector<JxlPixelFormat> formats;
    191     for (const uint32_t num_channels : {1, 2, 3, 4}) {
    192       for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) {
    193         formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
    194                                          /*data_type=*/data_type,
    195                                          /*endianness=*/JXL_BIG_ENDIAN,
    196                                          /*align=*/0});
    197       }
    198     }
    199     return formats;
    200   }
    201   Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame,
    202                      std::vector<uint8_t>* bytes) const override {
    203     const PackedImage& color = frame.color;
    204     const auto& ec_info = ppf.extra_channels_info;
    205     JXL_RETURN_IF_ERROR(frame.extra_channels.size() == ec_info.size());
    206     for (const auto& ec : frame.extra_channels) {
    207       if (ec.xsize != color.xsize || ec.ysize != color.ysize) {
    208         return JXL_FAILURE("Extra channel and color size mismatch.");
    209       }
    210       if (ec.format.data_type != color.format.data_type ||
    211           ec.format.endianness != color.format.endianness) {
    212         return JXL_FAILURE("Extra channel and color format mismatch.");
    213       }
    214     }
    215     if (ppf.info.alpha_bits &&
    216         (ppf.info.bits_per_sample != ppf.info.alpha_bits)) {
    217       return JXL_FAILURE("Alpha bit depth does not match image bit depth");
    218     }
    219     for (const auto& it : ec_info) {
    220       if (it.ec_info.bits_per_sample != ppf.info.bits_per_sample) {
    221         return JXL_FAILURE(
    222             "Extra channel bit depth does not match image bit depth");
    223       }
    224     }
    225     const char* kColorTypes[4] = {"GRAYSCALE", "GRAYSCALE_ALPHA", "RGB",
    226                                   "RGB_ALPHA"};
    227     uint32_t maxval = (1u << ppf.info.bits_per_sample) - 1;
    228     uint32_t depth = color.format.num_channels + ec_info.size();
    229     char header[kMaxHeaderSize];
    230     size_t pos = 0;
    231     pos += snprintf(header + pos, kMaxHeaderSize - pos,
    232                     "P7\nWIDTH %" PRIuS "\nHEIGHT %" PRIuS
    233                     "\nDEPTH %u\n"
    234                     "MAXVAL %u\nTUPLTYPE %s\n",
    235                     color.xsize, color.ysize, depth, maxval,
    236                     kColorTypes[color.format.num_channels - 1]);
    237     JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize);
    238     for (const auto& info : ec_info) {
    239       pos += snprintf(header + pos, kMaxHeaderSize - pos, "TUPLTYPE %s\n",
    240                       ExtraChannelTypeName(info.ec_info.type).c_str());
    241       JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize);
    242     }
    243     pos += snprintf(header + pos, kMaxHeaderSize - pos, "ENDHDR\n");
    244     JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize);
    245     size_t total_size = color.pixels_size;
    246     for (const auto& ec : frame.extra_channels) {
    247       total_size += ec.pixels_size;
    248     }
    249     bytes->resize(pos + total_size);
    250     memcpy(bytes->data(), header, pos);
    251     // If we have no extra channels, just copy color pixel data over.
    252     if (frame.extra_channels.empty()) {
    253       memcpy(bytes->data() + pos, reinterpret_cast<uint8_t*>(color.pixels()),
    254              color.pixels_size);
    255       return true;
    256     }
    257     // Interleave color and extra channels.
    258     const uint8_t* in = reinterpret_cast<const uint8_t*>(color.pixels());
    259     std::vector<const uint8_t*> ec_in(frame.extra_channels.size());
    260     for (size_t i = 0; i < frame.extra_channels.size(); ++i) {
    261       ec_in[i] =
    262           reinterpret_cast<const uint8_t*>(frame.extra_channels[i].pixels());
    263     }
    264     uint8_t* out = bytes->data() + pos;
    265     size_t pwidth = PackedImage::BitsPerChannel(color.format.data_type) / 8;
    266     for (size_t y = 0; y < color.ysize; ++y) {
    267       for (size_t x = 0; x < color.xsize; ++x) {
    268         memcpy(out, in, color.pixel_stride());
    269         out += color.pixel_stride();
    270         in += color.pixel_stride();
    271         for (auto& p : ec_in) {
    272           memcpy(out, p, pwidth);
    273           out += pwidth;
    274           p += pwidth;
    275         }
    276       }
    277     }
    278     return true;
    279   }
    280   Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample,
    281                             std::vector<uint8_t>* bytes) const override {
    282     return true;
    283   }
    284 
    285  private:
    286   static std::string ExtraChannelTypeName(JxlExtraChannelType type) {
    287     switch (type) {
    288       case JXL_CHANNEL_ALPHA:
    289         return std::string("Alpha");
    290       case JXL_CHANNEL_DEPTH:
    291         return std::string("Depth");
    292       case JXL_CHANNEL_SPOT_COLOR:
    293         return std::string("SpotColor");
    294       case JXL_CHANNEL_SELECTION_MASK:
    295         return std::string("SelectionMask");
    296       case JXL_CHANNEL_BLACK:
    297         return std::string("Black");
    298       case JXL_CHANNEL_CFA:
    299         return std::string("CFA");
    300       case JXL_CHANNEL_THERMAL:
    301         return std::string("Thermal");
    302       default:
    303         return std::string("UNKNOWN");
    304     }
    305   }
    306 };
    307 
    308 }  // namespace
    309 
    310 std::unique_ptr<Encoder> GetPPMEncoder() {
    311   return jxl::make_unique<PPMEncoder>();
    312 }
    313 
    314 std::unique_ptr<Encoder> GetPNMEncoder() {
    315   return jxl::make_unique<PNMEncoder>();
    316 }
    317 
    318 std::unique_ptr<Encoder> GetPFMEncoder() {
    319   return jxl::make_unique<PFMEncoder>();
    320 }
    321 
    322 std::unique_ptr<Encoder> GetPGMEncoder() {
    323   return jxl::make_unique<PGMEncoder>();
    324 }
    325 
    326 std::unique_ptr<Encoder> GetPAMEncoder() {
    327   return jxl::make_unique<PAMEncoder>();
    328 }
    329 
    330 }  // namespace extras
    331 }  // namespace jxl