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 <stdio.h>
7 #include <stdlib.h>
8 
9 #include <cmath>
10 
11 #include "lib/extras/codec.h"
12 #include "lib/jxl/base/thread_pool_internal.h"
13 #include "tools/args.h"
14 #include "tools/cmdline.h"
15 
16 namespace jxl {
17 namespace {
18 
GetSystemGamma(const float peak_luminance,const float surround_luminance)19 float GetSystemGamma(const float peak_luminance,
20                      const float surround_luminance) {
21   return 1.2f * std::pow(1.111f, std::log2(peak_luminance / 1000.f)) *
22          std::pow(0.98f, std::log2(surround_luminance / 5));
23 }
24 
HlgOOTF(ImageBundle * ib,const float gamma,ThreadPool * pool)25 Status HlgOOTF(ImageBundle* ib, const float gamma, ThreadPool* pool) {
26   ColorEncoding linear_rec2020;
27   linear_rec2020.SetColorSpace(ColorSpace::kRGB);
28   linear_rec2020.primaries = Primaries::k2100;
29   linear_rec2020.white_point = WhitePoint::kD65;
30   linear_rec2020.tf.SetTransferFunction(TransferFunction::kLinear);
31   JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC());
32   JXL_RETURN_IF_ERROR(ib->TransformTo(linear_rec2020, pool));
33 
34   return RunOnPool(
35       pool, 0, ib->ysize(), ThreadPool::SkipInit(),
36       [&](const int y, const int thread) {
37         float* const JXL_RESTRICT rows[3] = {ib->color()->PlaneRow(0, y),
38                                              ib->color()->PlaneRow(1, y),
39                                              ib->color()->PlaneRow(2, y)};
40         for (size_t x = 0; x < ib->xsize(); ++x) {
41           float& red = rows[0][x];
42           float& green = rows[1][x];
43           float& blue = rows[2][x];
44           const float luminance =
45               0.2627f * red + 0.6780f * green + 0.0593f * blue;
46           const float ratio = std::pow(luminance, gamma - 1);
47           if (std::isfinite(ratio)) {
48             red *= ratio;
49             green *= ratio;
50             blue *= ratio;
51           }
52         }
53       },
54       "HlgOOTF");
55 }
56 
57 }  // namespace
58 }  // namespace jxl
59 
main(int argc,const char ** argv)60 int main(int argc, const char** argv) {
61   jxl::ThreadPoolInternal pool;
62 
63   jpegxl::tools::CommandLineParser parser;
64   float target_nits = 0;
65   auto target_nits_option = parser.AddOptionValue(
66       't', "target_nits", "nits", "peak luminance of the target display",
67       &target_nits, &jpegxl::tools::ParseFloat, 0);
68   float surround_nits = 5;
69   parser.AddOptionValue(
70       's', "surround_nits", "nits",
71       "surround luminance of the viewing environment (default: 5)",
72       &surround_nits, &jpegxl::tools::ParseFloat, 0);
73   bool pq = false;
74   parser.AddOptionFlag('p', "pq",
75                        "write the output with absolute luminance using PQ", &pq,
76                        &jpegxl::tools::SetBooleanTrue, 0);
77   const char* input_filename = nullptr;
78   auto input_filename_option = parser.AddPositionalOption(
79       "input", true, "input image", &input_filename, 0);
80   const char* output_filename = nullptr;
81   auto output_filename_option = parser.AddPositionalOption(
82       "output", true, "output image", &output_filename, 0);
83 
84   if (!parser.Parse(argc, argv)) {
85     fprintf(stderr, "See -h for help.\n");
86     return EXIT_FAILURE;
87   }
88 
89   if (parser.HelpFlagPassed()) {
90     parser.PrintHelp();
91     return EXIT_SUCCESS;
92   }
93 
94   if (!parser.GetOption(target_nits_option)->matched()) {
95     fprintf(stderr,
96             "Missing required argument --target_nits.\nSee -h for help.\n");
97     return EXIT_FAILURE;
98   }
99   if (!parser.GetOption(input_filename_option)->matched()) {
100     fprintf(stderr, "Missing input filename.\nSee -h for help.\n");
101     return EXIT_FAILURE;
102   }
103   if (!parser.GetOption(output_filename_option)->matched()) {
104     fprintf(stderr, "Missing output filename.\nSee -h for help.\n");
105     return EXIT_FAILURE;
106   }
107 
108   jxl::CodecInOut image;
109   jxl::ColorHints color_hints;
110   color_hints.Add("color_space", "RGB_D65_202_Rel_HLG");
111   JXL_CHECK(jxl::SetFromFile(input_filename, color_hints, &image, &pool));
112   const float gamma = jxl::GetSystemGamma(target_nits, surround_nits);
113   fprintf(stderr, "Using a system gamma of %g\n", gamma);
114   JXL_CHECK(jxl::HlgOOTF(&image.Main(), gamma, &pool));
115   image.metadata.m.SetIntensityTarget(target_nits);
116 
117   jxl::ColorEncoding c_out = image.metadata.m.color_encoding;
118   if (pq) {
119     c_out.tf.SetTransferFunction(jxl::TransferFunction::kPQ);
120   } else {
121     c_out.tf.SetTransferFunction(jxl::TransferFunction::kSRGB);
122   }
123   JXL_CHECK(c_out.CreateICC());
124   JXL_CHECK(image.TransformTo(c_out, &pool));
125   image.metadata.m.color_encoding = c_out;
126   JXL_CHECK(jxl::EncodeToFile(image, output_filename, &pool));
127 }
128