/src/libjxl/lib/extras/enc/encode.cc
Line | Count | Source (jump to first uncovered line) |
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/encode.h" |
7 | | |
8 | | #include <jxl/codestream_header.h> |
9 | | #include <jxl/types.h> |
10 | | |
11 | | #include <algorithm> |
12 | | #include <cctype> |
13 | | #include <cstddef> |
14 | | #include <cstdint> |
15 | | #include <locale> |
16 | | #include <memory> |
17 | | #include <string> |
18 | | #include <vector> |
19 | | |
20 | | #include "lib/extras/enc/apng.h" |
21 | | #include "lib/extras/enc/exr.h" |
22 | | #include "lib/extras/enc/jpg.h" |
23 | | #include "lib/extras/enc/npy.h" |
24 | | #include "lib/extras/enc/pgx.h" |
25 | | #include "lib/extras/enc/pnm.h" |
26 | | #include "lib/extras/packed_image.h" |
27 | | #include "lib/jxl/base/common.h" |
28 | | #include "lib/jxl/base/data_parallel.h" |
29 | | #include "lib/jxl/base/status.h" |
30 | | |
31 | | namespace jxl { |
32 | | namespace extras { |
33 | | |
34 | 0 | Status Encoder::VerifyBasicInfo(const JxlBasicInfo& info) { |
35 | 0 | if (info.xsize == 0 || info.ysize == 0) { |
36 | 0 | return JXL_FAILURE("Empty image"); |
37 | 0 | } |
38 | 0 | if (info.num_color_channels != 1 && info.num_color_channels != 3) { |
39 | 0 | return JXL_FAILURE("Invalid number of color channels"); |
40 | 0 | } |
41 | 0 | if (info.alpha_bits > 0 && info.alpha_bits != info.bits_per_sample) { |
42 | 0 | return JXL_FAILURE("Alpha bit depth does not match image bit depth"); |
43 | 0 | } |
44 | 0 | if (info.orientation != JXL_ORIENT_IDENTITY) { |
45 | 0 | return JXL_FAILURE("Orientation must be identity"); |
46 | 0 | } |
47 | 0 | return true; |
48 | 0 | } |
49 | | |
50 | 0 | Status Encoder::VerifyFormat(const JxlPixelFormat& format) const { |
51 | 0 | for (auto f : AcceptedFormats()) { |
52 | 0 | if (f.num_channels != format.num_channels) continue; |
53 | 0 | if (f.data_type != format.data_type) continue; |
54 | 0 | if (f.data_type == JXL_TYPE_UINT8 || f.endianness == format.endianness) { |
55 | 0 | return true; |
56 | 0 | } |
57 | 0 | } |
58 | 0 | return JXL_FAILURE("Format is not in the list of accepted formats."); |
59 | 0 | } |
60 | | |
61 | | Status Encoder::VerifyBitDepth(JxlDataType data_type, uint32_t bits_per_sample, |
62 | 0 | uint32_t exponent_bits) { |
63 | 0 | if ((data_type == JXL_TYPE_UINT8 && |
64 | 0 | (bits_per_sample == 0 || bits_per_sample > 8 || exponent_bits != 0)) || |
65 | 0 | (data_type == JXL_TYPE_UINT16 && |
66 | 0 | (bits_per_sample <= 8 || bits_per_sample > 16 || exponent_bits != 0)) || |
67 | 0 | (data_type == JXL_TYPE_FLOAT16 && |
68 | 0 | (bits_per_sample > 16 || exponent_bits > 5))) { |
69 | 0 | return JXL_FAILURE( |
70 | 0 | "Incompatible data_type %d and bit depth %u with exponent bits %u", |
71 | 0 | static_cast<int>(data_type), bits_per_sample, exponent_bits); |
72 | 0 | } |
73 | 0 | return true; |
74 | 0 | } |
75 | | |
76 | | Status Encoder::VerifyImageSize(const PackedImage& image, |
77 | 0 | const JxlBasicInfo& info) { |
78 | 0 | if (image.pixels() == nullptr) { |
79 | 0 | return JXL_FAILURE("Invalid image."); |
80 | 0 | } |
81 | 0 | if (image.stride != image.xsize * image.pixel_stride()) { |
82 | 0 | return JXL_FAILURE("Invalid image stride."); |
83 | 0 | } |
84 | 0 | if (image.pixels_size != image.ysize * image.stride) { |
85 | 0 | return JXL_FAILURE("Invalid image size."); |
86 | 0 | } |
87 | 0 | size_t info_num_channels = |
88 | 0 | (info.num_color_channels + (info.alpha_bits > 0 ? 1 : 0)); |
89 | | // TODO(jon): frames do not necessarily have to match image size, |
90 | | // but probably this is still assumed in some encoders |
91 | 0 | if ( // image.xsize != info.xsize || image.ysize != info.ysize || |
92 | 0 | image.format.num_channels != info_num_channels) { |
93 | 0 | return JXL_FAILURE("Frame size does not match image size"); |
94 | 0 | } |
95 | 0 | return true; |
96 | 0 | } |
97 | | |
98 | | Status Encoder::VerifyPackedImage(const PackedImage& image, |
99 | 0 | const JxlBasicInfo& info) const { |
100 | 0 | JXL_RETURN_IF_ERROR(VerifyImageSize(image, info)); |
101 | 0 | JXL_RETURN_IF_ERROR(VerifyFormat(image.format)); |
102 | 0 | JXL_RETURN_IF_ERROR(VerifyBitDepth(image.format.data_type, |
103 | 0 | info.bits_per_sample, |
104 | 0 | info.exponent_bits_per_sample)); |
105 | 0 | return true; |
106 | 0 | } |
107 | | |
108 | | template <int metadata> |
109 | | class MetadataEncoder : public Encoder { |
110 | | public: |
111 | 0 | std::vector<JxlPixelFormat> AcceptedFormats() const override { |
112 | 0 | std::vector<JxlPixelFormat> formats; |
113 | | // empty, i.e. no need for actual pixel data |
114 | 0 | return formats; |
115 | 0 | } Unexecuted instantiation: jxl::extras::MetadataEncoder<0>::AcceptedFormats() const Unexecuted instantiation: jxl::extras::MetadataEncoder<1>::AcceptedFormats() const Unexecuted instantiation: jxl::extras::MetadataEncoder<2>::AcceptedFormats() const |
116 | | |
117 | | Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded, |
118 | 0 | ThreadPool* pool) const override { |
119 | 0 | JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); |
120 | 0 | encoded->icc.clear(); |
121 | 0 | encoded->bitstreams.resize(1); |
122 | 0 | if (metadata == 0) encoded->bitstreams.front() = ppf.metadata.exif; |
123 | 0 | if (metadata == 1) encoded->bitstreams.front() = ppf.metadata.xmp; |
124 | 0 | if (metadata == 2) encoded->bitstreams.front() = ppf.metadata.jumbf; |
125 | 0 | return true; |
126 | 0 | } Unexecuted instantiation: jxl::extras::MetadataEncoder<0>::Encode(jxl::extras::PackedPixelFile const&, jxl::extras::EncodedImage*, jxl::ThreadPool*) const Unexecuted instantiation: jxl::extras::MetadataEncoder<1>::Encode(jxl::extras::PackedPixelFile const&, jxl::extras::EncodedImage*, jxl::ThreadPool*) const Unexecuted instantiation: jxl::extras::MetadataEncoder<2>::Encode(jxl::extras::PackedPixelFile const&, jxl::extras::EncodedImage*, jxl::ThreadPool*) const |
127 | | }; |
128 | | |
129 | 0 | std::unique_ptr<Encoder> Encoder::FromExtension(std::string extension) { |
130 | 0 | std::transform( |
131 | 0 | extension.begin(), extension.end(), extension.begin(), |
132 | 0 | [](char c) { return std::tolower(c, std::locale::classic()); }); |
133 | 0 | if (extension == ".png" || extension == ".apng") return GetAPNGEncoder(); |
134 | 0 | if (extension == ".jpg") return GetJPEGEncoder(); |
135 | 0 | if (extension == ".jpeg") return GetJPEGEncoder(); |
136 | 0 | if (extension == ".npy") return GetNumPyEncoder(); |
137 | 0 | if (extension == ".pgx") return GetPGXEncoder(); |
138 | 0 | if (extension == ".pam") return GetPAMEncoder(); |
139 | 0 | if (extension == ".pgm") return GetPGMEncoder(); |
140 | 0 | if (extension == ".ppm") return GetPPMEncoder(); |
141 | 0 | if (extension == ".pnm") return GetPNMEncoder(); |
142 | 0 | if (extension == ".pfm") return GetPFMEncoder(); |
143 | 0 | if (extension == ".exr") return GetEXREncoder(); |
144 | 0 | if (extension == ".exif") return jxl::make_unique<MetadataEncoder<0>>(); |
145 | 0 | if (extension == ".xmp") return jxl::make_unique<MetadataEncoder<1>>(); |
146 | 0 | if (extension == ".xml") return jxl::make_unique<MetadataEncoder<1>>(); |
147 | 0 | if (extension == ".jumbf") return jxl::make_unique<MetadataEncoder<2>>(); |
148 | 0 | if (extension == ".jumb") return jxl::make_unique<MetadataEncoder<2>>(); |
149 | | |
150 | 0 | return nullptr; |
151 | 0 | } |
152 | | |
153 | 0 | std::string ListOfEncodeCodecs() { |
154 | 0 | std::string list_of_codecs("PPM, PNM, PFM, PAM, PGX"); |
155 | 0 | if (GetAPNGEncoder()) list_of_codecs.append(", PNG, APNG"); |
156 | 0 | if (GetJPEGEncoder()) list_of_codecs.append(", JPEG"); |
157 | 0 | if (GetEXREncoder()) list_of_codecs.append(", EXR"); |
158 | 0 | return list_of_codecs; |
159 | 0 | } |
160 | | |
161 | | } // namespace extras |
162 | | } // namespace jxl |