pgx.cc (4339B)
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/pgx.h" 7 8 #include <jxl/codestream_header.h> 9 #include <string.h> 10 11 #include "lib/extras/packed_image.h" 12 #include "lib/jxl/base/byte_order.h" 13 14 namespace jxl { 15 namespace extras { 16 namespace { 17 18 constexpr size_t kMaxHeaderSize = 200; 19 20 Status EncodeHeader(const JxlBasicInfo& info, char* header, 21 int* chars_written) { 22 if (info.alpha_bits > 0) { 23 return JXL_FAILURE("PGX: can't store alpha"); 24 } 25 if (info.num_color_channels != 1) { 26 return JXL_FAILURE("PGX: must be grayscale"); 27 } 28 // TODO(lode): verify other bit depths: for other bit depths such as 1 or 4 29 // bits, have a test case to verify it works correctly. For bits > 16, we may 30 // need to change the way external_image works. 31 if (info.bits_per_sample != 8 && info.bits_per_sample != 16) { 32 return JXL_FAILURE("PGX: bits other than 8 or 16 not yet supported"); 33 } 34 35 // Use ML (Big Endian), LM may not be well supported by all decoders. 36 *chars_written = snprintf(header, kMaxHeaderSize, "PG ML + %u %u %u\n", 37 info.bits_per_sample, info.xsize, info.ysize); 38 JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) < 39 kMaxHeaderSize); 40 return true; 41 } 42 43 Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info, 44 std::vector<uint8_t>* bytes) { 45 char header[kMaxHeaderSize]; 46 int header_size = 0; 47 JXL_RETURN_IF_ERROR(EncodeHeader(info, header, &header_size)); 48 49 const PackedImage& color = frame.color; 50 const JxlPixelFormat format = color.format; 51 const uint8_t* in = reinterpret_cast<const uint8_t*>(color.pixels()); 52 size_t data_bits_per_sample = PackedImage::BitsPerChannel(format.data_type); 53 size_t bytes_per_sample = data_bits_per_sample / kBitsPerByte; 54 size_t num_samples = info.xsize * info.ysize; 55 56 if (info.bits_per_sample != data_bits_per_sample) { 57 return JXL_FAILURE("Bit depth does not match pixel data type"); 58 } 59 60 std::vector<uint8_t> pixels(num_samples * bytes_per_sample); 61 62 if (format.data_type == JXL_TYPE_UINT8) { 63 memcpy(pixels.data(), in, num_samples * bytes_per_sample); 64 } else if (format.data_type == JXL_TYPE_UINT16) { 65 if (format.endianness != JXL_BIG_ENDIAN) { 66 const uint8_t* p_in = in; 67 uint8_t* p_out = pixels.data(); 68 for (size_t i = 0; i < num_samples; ++i, p_in += 2, p_out += 2) { 69 StoreBE16(LoadLE16(p_in), p_out); 70 } 71 } else { 72 memcpy(pixels.data(), in, num_samples * bytes_per_sample); 73 } 74 } else { 75 return JXL_FAILURE("Unsupported pixel data type"); 76 } 77 78 bytes->resize(static_cast<size_t>(header_size) + pixels.size()); 79 memcpy(bytes->data(), header, static_cast<size_t>(header_size)); 80 memcpy(bytes->data() + header_size, pixels.data(), pixels.size()); 81 82 return true; 83 } 84 85 class PGXEncoder : public Encoder { 86 public: 87 std::vector<JxlPixelFormat> AcceptedFormats() const override { 88 std::vector<JxlPixelFormat> formats; 89 for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) { 90 for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) { 91 formats.push_back(JxlPixelFormat{/*num_channels=*/1, 92 /*data_type=*/data_type, 93 /*endianness=*/endianness, 94 /*align=*/0}); 95 } 96 } 97 return formats; 98 } 99 Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, 100 ThreadPool* pool) const override { 101 JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); 102 encoded_image->icc.assign(ppf.icc.begin(), ppf.icc.end()); 103 encoded_image->bitstreams.clear(); 104 encoded_image->bitstreams.reserve(ppf.frames.size()); 105 for (const auto& frame : ppf.frames) { 106 JXL_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info)); 107 encoded_image->bitstreams.emplace_back(); 108 JXL_RETURN_IF_ERROR( 109 EncodeImagePGX(frame, ppf.info, &encoded_image->bitstreams.back())); 110 } 111 return true; 112 } 113 }; 114 115 } // namespace 116 117 std::unique_ptr<Encoder> GetPGXEncoder() { 118 return jxl::make_unique<PGXEncoder>(); 119 } 120 121 } // namespace extras 122 } // namespace jxl