1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
6 // This example encodes a file containing a floating point image to another
7 // file containing JPEG XL image with a single frame.
9 #include <jxl/codestream_header.h>
10 #include <jxl/color_encoding.h>
11 #include <jxl/encode.h>
12 #include <jxl/encode_cxx.h>
13 #include <jxl/thread_parallel_runner.h>
14 #include <jxl/thread_parallel_runner_cxx.h>
15 #include <jxl/types.h>
26 * Reads from .pfm file (Portable FloatMap)
28 * @param filename name of the file to read
29 * @param pixels vector to fill with loaded pixels as 32-bit floating point with
31 * @param xsize set to width of loaded image
32 * @param ysize set to height of loaded image
34 bool ReadPFM(const char* filename
, std::vector
<float>* pixels
, uint32_t* xsize
,
36 FILE* file
= fopen(filename
, "rb");
38 fprintf(stderr
, "Could not open %s for reading.\n", filename
);
41 uint32_t endian_test
= 1;
42 uint8_t little_endian_check
[4];
43 memcpy(little_endian_check
, &endian_test
, 4);
44 bool little_endian
= (little_endian_check
[0] == 1);
46 if (fseek(file
, 0, SEEK_END
) != 0) {
51 long size
= ftell(file
); // NOLINT
52 // Avoid invalid file or directory.
53 if (size
>= LONG_MAX
|| size
< 0) {
58 if (fseek(file
, 0, SEEK_SET
) != 0) {
63 std::vector
<char> data
;
66 size_t readsize
= fread(data
.data(), 1, size
, file
);
67 if (static_cast<long>(readsize
) != size
) { // NOLINT
71 if (fclose(file
) != 0) {
75 std::stringstream datastream
;
76 std::string
datastream_content(data
.data(), data
.size());
77 datastream
.str(datastream_content
);
80 getline(datastream
, pf_token
, '\n');
81 if (pf_token
!= "PF") {
83 "%s doesn't seem to be a 3 channel Portable FloatMap file (missing "
90 std::string xsize_token
;
91 getline(datastream
, xsize_token
, ' ');
92 *xsize
= std::stoi(xsize_token
);
94 std::string ysize_token
;
95 getline(datastream
, ysize_token
, '\n');
96 *ysize
= std::stoi(ysize_token
);
98 std::string endianness_token
;
99 getline(datastream
, endianness_token
, '\n');
100 bool input_little_endian
;
101 if (endianness_token
== "1.0") {
102 input_little_endian
= false;
103 } else if (endianness_token
== "-1.0") {
104 input_little_endian
= true;
107 "%s doesn't seem to be a Portable FloatMap file (endianness token "
108 "isn't '1.0' or '-1.0').\n",
113 size_t offset
= pf_token
.size() + 1 + xsize_token
.size() + 1 +
114 ysize_token
.size() + 1 + endianness_token
.size() + 1;
116 if (data
.size() != *ysize
* *xsize
* 3 * 4 + offset
) {
118 "%s doesn't seem to be a Portable FloatMap file (pixel data bytes "
119 "are %d, but expected %d * %d * 3 * 4 + %d (%d).\n",
120 filename
, static_cast<int>(data
.size()), static_cast<int>(*ysize
),
121 static_cast<int>(*xsize
), static_cast<int>(offset
),
122 static_cast<int>(*ysize
* *xsize
* 3 * 4 + offset
));
126 if (little_endian
!= input_little_endian
) {
128 "%s has a different endianness than we do, conversion is not "
134 pixels
->resize(*ysize
* *xsize
* 3);
136 for (int y
= *ysize
- 1; y
>= 0; y
--) {
137 for (int x
= 0; x
< static_cast<int>(*xsize
); x
++) {
138 for (int c
= 0; c
< 3; c
++) {
139 memcpy(pixels
->data() + (y
* *xsize
+ x
) * 3 + c
, data
.data() + offset
,
141 offset
+= sizeof(float);
150 * Compresses the provided pixels.
152 * @param pixels input pixels
153 * @param xsize width of the input image
154 * @param ysize height of the input image
155 * @param compressed will be populated with the compressed bytes
157 bool EncodeJxlOneshot(const std::vector
<float>& pixels
, const uint32_t xsize
,
158 const uint32_t ysize
, std::vector
<uint8_t>* compressed
) {
159 auto enc
= JxlEncoderMake(/*memory_manager=*/nullptr);
160 auto runner
= JxlThreadParallelRunnerMake(
161 /*memory_manager=*/nullptr,
162 JxlThreadParallelRunnerDefaultNumWorkerThreads());
163 if (JXL_ENC_SUCCESS
!= JxlEncoderSetParallelRunner(enc
.get(),
164 JxlThreadParallelRunner
,
166 fprintf(stderr
, "JxlEncoderSetParallelRunner failed\n");
170 JxlPixelFormat pixel_format
= {3, JXL_TYPE_FLOAT
, JXL_NATIVE_ENDIAN
, 0};
172 JxlBasicInfo basic_info
;
173 JxlEncoderInitBasicInfo(&basic_info
);
174 basic_info
.xsize
= xsize
;
175 basic_info
.ysize
= ysize
;
176 basic_info
.bits_per_sample
= 32;
177 basic_info
.exponent_bits_per_sample
= 8;
178 basic_info
.uses_original_profile
= JXL_FALSE
;
179 if (JXL_ENC_SUCCESS
!= JxlEncoderSetBasicInfo(enc
.get(), &basic_info
)) {
180 fprintf(stderr
, "JxlEncoderSetBasicInfo failed\n");
184 JxlColorEncoding color_encoding
= {};
185 JXL_BOOL is_gray
= TO_JXL_BOOL(pixel_format
.num_channels
< 3);
186 JxlColorEncodingSetToSRGB(&color_encoding
, is_gray
);
187 if (JXL_ENC_SUCCESS
!=
188 JxlEncoderSetColorEncoding(enc
.get(), &color_encoding
)) {
189 fprintf(stderr
, "JxlEncoderSetColorEncoding failed\n");
193 JxlEncoderFrameSettings
* frame_settings
=
194 JxlEncoderFrameSettingsCreate(enc
.get(), nullptr);
196 if (JXL_ENC_SUCCESS
!=
197 JxlEncoderAddImageFrame(frame_settings
, &pixel_format
,
198 static_cast<const void*>(pixels
.data()),
199 sizeof(float) * pixels
.size())) {
200 fprintf(stderr
, "JxlEncoderAddImageFrame failed\n");
203 JxlEncoderCloseInput(enc
.get());
205 compressed
->resize(64);
206 uint8_t* next_out
= compressed
->data();
207 size_t avail_out
= compressed
->size() - (next_out
- compressed
->data());
208 JxlEncoderStatus process_result
= JXL_ENC_NEED_MORE_OUTPUT
;
209 while (process_result
== JXL_ENC_NEED_MORE_OUTPUT
) {
210 process_result
= JxlEncoderProcessOutput(enc
.get(), &next_out
, &avail_out
);
211 if (process_result
== JXL_ENC_NEED_MORE_OUTPUT
) {
212 size_t offset
= next_out
- compressed
->data();
213 compressed
->resize(compressed
->size() * 2);
214 next_out
= compressed
->data() + offset
;
215 avail_out
= compressed
->size() - offset
;
218 compressed
->resize(next_out
- compressed
->data());
219 if (JXL_ENC_SUCCESS
!= process_result
) {
220 fprintf(stderr
, "JxlEncoderProcessOutput failed\n");
228 * Writes bytes to file.
230 bool WriteFile(const std::vector
<uint8_t>& bytes
, const char* filename
) {
231 FILE* file
= fopen(filename
, "wb");
233 fprintf(stderr
, "Could not open %s for writing\n", filename
);
236 if (fwrite(bytes
.data(), sizeof(uint8_t), bytes
.size(), file
) !=
238 fprintf(stderr
, "Could not write bytes to %s\n", filename
);
242 if (fclose(file
) != 0) {
243 fprintf(stderr
, "Could not close %s\n", filename
);
249 int main(int argc
, char* argv
[]) {
252 "Usage: %s <pfm> <jxl>\n"
254 " pfm = input Portable FloatMap image filename\n"
255 " jxl = output JPEG XL image filename\n"
256 "Output files will be overwritten.\n",
261 const char* pfm_filename
= argv
[1];
262 const char* jxl_filename
= argv
[2];
264 std::vector
<float> pixels
;
267 if (!ReadPFM(pfm_filename
, &pixels
, &xsize
, &ysize
)) {
268 fprintf(stderr
, "Couldn't load %s\n", pfm_filename
);
272 std::vector
<uint8_t> compressed
;
273 if (!EncodeJxlOneshot(pixels
, xsize
, ysize
, &compressed
)) {
274 fprintf(stderr
, "Couldn't encode jxl\n");
278 if (!WriteFile(compressed
, jxl_filename
)) {
279 fprintf(stderr
, "Couldn't write jxl file\n");