/src/libjxl/lib/extras/dec/decode.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/dec/decode.h" |
7 | | |
8 | | #include <locale> |
9 | | |
10 | | #include "lib/extras/dec/apng.h" |
11 | | #include "lib/extras/dec/exr.h" |
12 | | #include "lib/extras/dec/gif.h" |
13 | | #include "lib/extras/dec/jpg.h" |
14 | | #include "lib/extras/dec/jxl.h" |
15 | | #include "lib/extras/dec/pgx.h" |
16 | | #include "lib/extras/dec/pnm.h" |
17 | | |
18 | | namespace jxl { |
19 | | namespace extras { |
20 | | namespace { |
21 | | |
22 | | // Any valid encoding is larger (ensures codecs can read the first few bytes) |
23 | | constexpr size_t kMinBytes = 9; |
24 | | |
25 | 0 | std::string GetExtension(const std::string& path) { |
26 | | // Pattern: "name.png" |
27 | 0 | size_t pos = path.find_last_of('.'); |
28 | 0 | if (pos != std::string::npos) { |
29 | 0 | return path.substr(pos); |
30 | 0 | } |
31 | | |
32 | | // Extension not found |
33 | 0 | return ""; |
34 | 0 | } |
35 | | |
36 | | } // namespace |
37 | | |
38 | | Codec CodecFromPath(const std::string& path, |
39 | | size_t* JXL_RESTRICT bits_per_sample, |
40 | 0 | std::string* extension) { |
41 | 0 | std::string ext = GetExtension(path); |
42 | 0 | if (extension) { |
43 | 0 | if (extension->empty()) { |
44 | 0 | *extension = ext; |
45 | 0 | } else { |
46 | 0 | ext = *extension; |
47 | 0 | } |
48 | 0 | } |
49 | 0 | std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { |
50 | 0 | return std::tolower(c, std::locale::classic()); |
51 | 0 | }); |
52 | 0 | if (ext == ".png") return Codec::kPNG; |
53 | | |
54 | 0 | if (ext == ".jpg") return Codec::kJPG; |
55 | 0 | if (ext == ".jpeg") return Codec::kJPG; |
56 | | |
57 | 0 | if (ext == ".pgx") return Codec::kPGX; |
58 | | |
59 | 0 | if (ext == ".pam") return Codec::kPNM; |
60 | 0 | if (ext == ".pnm") return Codec::kPNM; |
61 | 0 | if (ext == ".pgm") return Codec::kPNM; |
62 | 0 | if (ext == ".ppm") return Codec::kPNM; |
63 | 0 | if (ext == ".pfm") { |
64 | 0 | if (bits_per_sample != nullptr) *bits_per_sample = 32; |
65 | 0 | return Codec::kPNM; |
66 | 0 | } |
67 | | |
68 | 0 | if (ext == ".gif") return Codec::kGIF; |
69 | | |
70 | 0 | if (ext == ".exr") return Codec::kEXR; |
71 | | |
72 | 0 | return Codec::kUnknown; |
73 | 0 | } |
74 | | |
75 | 0 | bool CanDecode(Codec codec) { |
76 | 0 | switch (codec) { |
77 | 0 | case Codec::kEXR: |
78 | 0 | return CanDecodeEXR(); |
79 | 0 | case Codec::kGIF: |
80 | 0 | return CanDecodeGIF(); |
81 | 0 | case Codec::kJPG: |
82 | 0 | return CanDecodeJPG(); |
83 | 0 | case Codec::kPNG: |
84 | 0 | return CanDecodeAPNG(); |
85 | 0 | case Codec::kPNM: |
86 | 0 | case Codec::kPGX: |
87 | 0 | case Codec::kJXL: |
88 | 0 | return true; |
89 | 0 | default: |
90 | 0 | return false; |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | 0 | std::string ListOfDecodeCodecs() { |
95 | 0 | std::string list_of_codecs("JXL, PPM, PNM, PFM, PAM, PGX"); |
96 | 0 | if (CanDecode(Codec::kPNG)) list_of_codecs.append(", PNG, APNG"); |
97 | 0 | if (CanDecode(Codec::kGIF)) list_of_codecs.append(", GIF"); |
98 | 0 | if (CanDecode(Codec::kJPG)) list_of_codecs.append(", JPEG"); |
99 | 0 | if (CanDecode(Codec::kEXR)) list_of_codecs.append(", EXR"); |
100 | 0 | return list_of_codecs; |
101 | 0 | } |
102 | | |
103 | | Status DecodeBytes(const Span<const uint8_t> bytes, |
104 | | const ColorHints& color_hints, extras::PackedPixelFile* ppf, |
105 | 0 | const SizeConstraints* constraints, Codec* orig_codec) { |
106 | 0 | if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes"); |
107 | | |
108 | 0 | *ppf = extras::PackedPixelFile(); |
109 | | |
110 | | // Default values when not set by decoders. |
111 | 0 | ppf->info.uses_original_profile = JXL_TRUE; |
112 | 0 | ppf->info.orientation = JXL_ORIENT_IDENTITY; |
113 | |
|
114 | 0 | const auto choose_codec = [&]() -> Codec { |
115 | 0 | if (DecodeImageAPNG(bytes, color_hints, ppf, constraints)) { |
116 | 0 | return Codec::kPNG; |
117 | 0 | } |
118 | 0 | if (DecodeImagePGX(bytes, color_hints, ppf, constraints)) { |
119 | 0 | return Codec::kPGX; |
120 | 0 | } |
121 | 0 | if (DecodeImagePNM(bytes, color_hints, ppf, constraints)) { |
122 | 0 | return Codec::kPNM; |
123 | 0 | } |
124 | 0 | JXLDecompressParams dparams = {}; |
125 | 0 | for (const uint32_t num_channels : {1, 2, 3, 4}) { |
126 | 0 | dparams.accepted_formats.push_back( |
127 | 0 | {num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0}); |
128 | 0 | } |
129 | 0 | dparams.output_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; |
130 | 0 | size_t decoded_bytes; |
131 | 0 | if (DecodeImageJXL(bytes.data(), bytes.size(), dparams, &decoded_bytes, |
132 | 0 | ppf) && |
133 | 0 | ApplyColorHints(color_hints, true, ppf->info.num_color_channels == 1, |
134 | 0 | ppf)) { |
135 | 0 | return Codec::kJXL; |
136 | 0 | } |
137 | 0 | if (DecodeImageGIF(bytes, color_hints, ppf, constraints)) { |
138 | 0 | return Codec::kGIF; |
139 | 0 | } |
140 | 0 | if (DecodeImageJPG(bytes, color_hints, ppf, constraints)) { |
141 | 0 | return Codec::kJPG; |
142 | 0 | } |
143 | 0 | if (DecodeImageEXR(bytes, color_hints, ppf, constraints)) { |
144 | 0 | return Codec::kEXR; |
145 | 0 | } |
146 | 0 | return Codec::kUnknown; |
147 | 0 | }; |
148 | |
|
149 | 0 | Codec codec = choose_codec(); |
150 | 0 | if (codec == Codec::kUnknown) { |
151 | 0 | return JXL_FAILURE("Codecs failed to decode"); |
152 | 0 | } |
153 | 0 | if (orig_codec) *orig_codec = codec; |
154 | |
|
155 | 0 | return true; |
156 | 0 | } |
157 | | |
158 | | } // namespace extras |
159 | | } // namespace jxl |