djpegli.cc (5939B)
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 <stdint.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 10 #include <string> 11 #include <vector> 12 13 #include "lib/extras/dec/jpegli.h" 14 #include "lib/extras/enc/apng.h" 15 #include "lib/extras/enc/encode.h" 16 #include "lib/extras/time.h" 17 #include "lib/jxl/base/printf_macros.h" 18 #include "tools/cmdline.h" 19 #include "tools/file_io.h" 20 #include "tools/speed_stats.h" 21 22 namespace jpegxl { 23 namespace tools { 24 namespace { 25 26 struct Args { 27 void AddCommandLineOptions(CommandLineParser* cmdline) { 28 std::string output_help("The output can be "); 29 if (jxl::extras::GetAPNGEncoder()) { 30 output_help.append("PNG, "); 31 } 32 output_help.append("PFM or PPM/PGM/PNM"); 33 cmdline->AddPositionalOption("INPUT", /* required = */ true, 34 "The JPG input file.", &file_in); 35 36 cmdline->AddPositionalOption("OUTPUT", /* required = */ true, output_help, 37 &file_out); 38 cmdline->AddOptionFlag('\0', "disable_output", 39 "No output file will be written (for benchmarking)", 40 &disable_output, &SetBooleanTrue); 41 42 cmdline->AddOptionValue('\0', "bitdepth", "8|16", 43 "Sets the output bitdepth for integer based " 44 "formats, can be 8 (default) " 45 "or 16. Has no impact on PFM output.", 46 &bitdepth, &ParseUnsigned); 47 48 cmdline->AddOptionValue('\0', "num_reps", "N", 49 "Sets the number of times to decompress the image. " 50 "Used for benchmarking, the default is 1.", 51 &num_reps, &ParseUnsigned); 52 53 cmdline->AddOptionFlag('\0', "quiet", "Silence output (except for errors).", 54 &quiet, &SetBooleanTrue); 55 } 56 57 const char* file_in = nullptr; 58 const char* file_out = nullptr; 59 bool disable_output = false; 60 size_t bitdepth = 8; 61 size_t num_reps = 1; 62 bool quiet = false; 63 }; 64 65 bool ValidateArgs(const Args& args) { 66 if (args.bitdepth != 8 && args.bitdepth != 16) { 67 fprintf(stderr, "Invalid --bitdepth argument\n"); 68 return false; 69 } 70 return true; 71 } 72 73 void SetDecompressParams(const Args& args, const std::string& extension, 74 jxl::extras::JpegDecompressParams* params) { 75 if (extension == ".pfm") { 76 params->output_data_type = JXL_TYPE_FLOAT; 77 params->output_endianness = JXL_BIG_ENDIAN; 78 } else if (args.bitdepth == 16) { 79 params->output_data_type = JXL_TYPE_UINT16; 80 params->output_endianness = JXL_BIG_ENDIAN; 81 } 82 if (extension == ".pgm") { 83 params->force_grayscale = true; 84 } else if (extension == ".ppm") { 85 params->force_rgb = true; 86 } 87 } 88 89 int DJpegliMain(int argc, const char* argv[]) { 90 Args args; 91 CommandLineParser cmdline; 92 args.AddCommandLineOptions(&cmdline); 93 94 if (!cmdline.Parse(argc, const_cast<const char**>(argv))) { 95 // Parse already printed the actual error cause. 96 fprintf(stderr, "Use '%s -h' for more information.\n", argv[0]); 97 return EXIT_FAILURE; 98 } 99 100 if (cmdline.HelpFlagPassed() || !args.file_in) { 101 cmdline.PrintHelp(); 102 return EXIT_SUCCESS; 103 } 104 105 if (!args.file_out && !args.disable_output) { 106 fprintf(stderr, 107 "No output file specified and --disable_output flag not passed.\n"); 108 return EXIT_FAILURE; 109 } 110 111 if (args.disable_output && !args.quiet) { 112 fprintf(stderr, 113 "Decoding will be performed, but the result will be discarded.\n"); 114 } 115 116 if (!ValidateArgs(args)) { 117 return EXIT_FAILURE; 118 } 119 120 std::vector<uint8_t> jpeg_bytes; 121 if (!ReadFile(args.file_in, &jpeg_bytes)) { 122 fprintf(stderr, "Failed to read input image %s\n", args.file_in); 123 return EXIT_FAILURE; 124 } 125 126 if (!args.quiet) { 127 fprintf(stderr, "Read %" PRIuS " compressed bytes.\n", jpeg_bytes.size()); 128 } 129 130 std::string filename_out; 131 std::string extension; 132 if (args.file_out) { 133 filename_out = std::string(args.file_out); 134 size_t pos = filename_out.find_last_of('.'); 135 if (pos >= filename_out.size()) { 136 fprintf(stderr, "Unrecognized output extension.\n"); 137 return EXIT_FAILURE; 138 } 139 extension = filename_out.substr(pos); 140 } 141 142 jxl::extras::JpegDecompressParams dparams; 143 SetDecompressParams(args, extension, &dparams); 144 145 jxl::extras::PackedPixelFile ppf; 146 jpegxl::tools::SpeedStats stats; 147 for (size_t num_rep = 0; num_rep < args.num_reps; ++num_rep) { 148 const double t0 = jxl::Now(); 149 if (!jxl::extras::DecodeJpeg(jpeg_bytes, dparams, nullptr, &ppf)) { 150 fprintf(stderr, "jpegli decoding failed\n"); 151 return EXIT_FAILURE; 152 } 153 const double t1 = jxl::Now(); 154 stats.NotifyElapsed(t1 - t0); 155 stats.SetImageSize(ppf.info.xsize, ppf.info.ysize); 156 } 157 158 if (!args.quiet) { 159 stats.Print(1); 160 } 161 162 if (args.disable_output) { 163 return EXIT_SUCCESS; 164 } 165 166 if (extension == ".pnm") { 167 extension = ppf.info.num_color_channels == 3 ? ".ppm" : ".pgm"; 168 } 169 170 std::unique_ptr<jxl::extras::Encoder> encoder = 171 jxl::extras::Encoder::FromExtension(extension); 172 if (encoder == nullptr) { 173 fprintf(stderr, "Can't decode to the file extension '%s'\n", 174 extension.c_str()); 175 return EXIT_FAILURE; 176 } 177 jxl::extras::EncodedImage encoded_image; 178 if (!encoder->Encode(ppf, &encoded_image, nullptr) || 179 encoded_image.bitstreams.empty()) { 180 fprintf(stderr, "Encode failed\n"); 181 return EXIT_FAILURE; 182 } 183 if (!WriteFile(filename_out, encoded_image.bitstreams[0])) { 184 fprintf(stderr, "Failed to write output file %s\n", filename_out.c_str()); 185 return EXIT_FAILURE; 186 } 187 188 return EXIT_SUCCESS; 189 } 190 191 } // namespace 192 } // namespace tools 193 } // namespace jpegxl 194 195 int main(int argc, const char* argv[]) { 196 return jpegxl::tools::DJpegliMain(argc, argv); 197 }