tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pnm.cc (12419B)


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