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