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