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
[4];
43 memcpy(little_endian
, &endian_test
, 4);
45 if (fseek(file
, 0, SEEK_END
) != 0) {
50 long size
= ftell(file
);
51 // Avoid invalid file or directory.
52 if (size
>= LONG_MAX
|| size
< 0) {
57 if (fseek(file
, 0, SEEK_SET
) != 0) {
62 std::vector
<char> data
;
65 size_t readsize
= fread(data
.data(), 1, size
, file
);
66 if ((long)readsize
!= size
) {
70 if (fclose(file
) != 0) {
74 std::stringstream datastream
;
75 std::string
datastream_content(data
.data(), data
.size());
76 datastream
.str(datastream_content
);
79 getline(datastream
, pf_token
, '\n');
80 if (pf_token
!= "PF") {
82 "%s doesn't seem to be a 3 channel Portable FloatMap file (missing "
89 std::string xsize_token
;
90 getline(datastream
, xsize_token
, ' ');
91 *xsize
= std::stoi(xsize_token
);
93 std::string ysize_token
;
94 getline(datastream
, ysize_token
, '\n');
95 *ysize
= std::stoi(ysize_token
);
97 std::string endianness_token
;
98 getline(datastream
, endianness_token
, '\n');
99 bool input_little_endian
;
100 if (endianness_token
== "1.0") {
101 input_little_endian
= false;
102 } else if (endianness_token
== "-1.0") {
103 input_little_endian
= true;
106 "%s doesn't seem to be a Portable FloatMap file (endianness token "
107 "isn't '1.0' or '-1.0').\n",
112 size_t offset
= pf_token
.size() + 1 + xsize_token
.size() + 1 +
113 ysize_token
.size() + 1 + endianness_token
.size() + 1;
115 if (data
.size() != *ysize
* *xsize
* 3 * 4 + offset
) {
117 "%s doesn't seem to be a Portable FloatMap file (pixel data bytes "
118 "are %d, but expected %d * %d * 3 * 4 + %d (%d).\n",
119 filename
, (int)data
.size(), (int)*ysize
, (int)*xsize
, (int)offset
,
120 (int)(*ysize
* *xsize
* 3 * 4 + offset
));
124 if (!!little_endian
[0] != input_little_endian
) {
126 "%s has a different endianness than we do, conversion is not "
132 pixels
->resize(*ysize
* *xsize
* 3);
134 for (int y
= *ysize
- 1; y
>= 0; y
--) {
135 for (int x
= 0; x
< (int)*xsize
; x
++) {
136 for (int c
= 0; c
< 3; c
++) {
137 memcpy(pixels
->data() + (y
* *xsize
+ x
) * 3 + c
, data
.data() + offset
,
139 offset
+= sizeof(float);
148 * Compresses the provided pixels.
150 * @param pixels input pixels
151 * @param xsize width of the input image
152 * @param ysize height of the input image
153 * @param compressed will be populated with the compressed bytes
155 bool EncodeJxlOneshot(const std::vector
<float>& pixels
, const uint32_t xsize
,
156 const uint32_t ysize
, std::vector
<uint8_t>* compressed
) {
157 auto enc
= JxlEncoderMake(/*memory_manager=*/nullptr);
158 auto runner
= JxlThreadParallelRunnerMake(
159 /*memory_manager=*/nullptr,
160 JxlThreadParallelRunnerDefaultNumWorkerThreads());
161 if (JXL_ENC_SUCCESS
!= JxlEncoderSetParallelRunner(enc
.get(),
162 JxlThreadParallelRunner
,
164 fprintf(stderr
, "JxlEncoderSetParallelRunner failed\n");
168 JxlPixelFormat pixel_format
= {3, JXL_TYPE_FLOAT
, JXL_NATIVE_ENDIAN
, 0};
170 JxlBasicInfo basic_info
;
171 JxlEncoderInitBasicInfo(&basic_info
);
172 basic_info
.xsize
= xsize
;
173 basic_info
.ysize
= ysize
;
174 basic_info
.bits_per_sample
= 32;
175 basic_info
.exponent_bits_per_sample
= 8;
176 basic_info
.uses_original_profile
= JXL_FALSE
;
177 if (JXL_ENC_SUCCESS
!= JxlEncoderSetBasicInfo(enc
.get(), &basic_info
)) {
178 fprintf(stderr
, "JxlEncoderSetBasicInfo failed\n");
182 JxlColorEncoding color_encoding
= {};
183 JxlColorEncodingSetToSRGB(&color_encoding
,
184 /*is_gray=*/pixel_format
.num_channels
< 3);
185 if (JXL_ENC_SUCCESS
!=
186 JxlEncoderSetColorEncoding(enc
.get(), &color_encoding
)) {
187 fprintf(stderr
, "JxlEncoderSetColorEncoding failed\n");
191 JxlEncoderFrameSettings
* frame_settings
=
192 JxlEncoderFrameSettingsCreate(enc
.get(), nullptr);
194 if (JXL_ENC_SUCCESS
!=
195 JxlEncoderAddImageFrame(frame_settings
, &pixel_format
,
196 (void*)pixels
.data(),
197 sizeof(float) * pixels
.size())) {
198 fprintf(stderr
, "JxlEncoderAddImageFrame failed\n");
201 JxlEncoderCloseInput(enc
.get());
203 compressed
->resize(64);
204 uint8_t* next_out
= compressed
->data();
205 size_t avail_out
= compressed
->size() - (next_out
- compressed
->data());
206 JxlEncoderStatus process_result
= JXL_ENC_NEED_MORE_OUTPUT
;
207 while (process_result
== JXL_ENC_NEED_MORE_OUTPUT
) {
208 process_result
= JxlEncoderProcessOutput(enc
.get(), &next_out
, &avail_out
);
209 if (process_result
== JXL_ENC_NEED_MORE_OUTPUT
) {
210 size_t offset
= next_out
- compressed
->data();
211 compressed
->resize(compressed
->size() * 2);
212 next_out
= compressed
->data() + offset
;
213 avail_out
= compressed
->size() - offset
;
216 compressed
->resize(next_out
- compressed
->data());
217 if (JXL_ENC_SUCCESS
!= process_result
) {
218 fprintf(stderr
, "JxlEncoderProcessOutput failed\n");
226 * Writes bytes to file.
228 bool WriteFile(const std::vector
<uint8_t>& bytes
, const char* filename
) {
229 FILE* file
= fopen(filename
, "wb");
231 fprintf(stderr
, "Could not open %s for writing\n", filename
);
234 if (fwrite(bytes
.data(), sizeof(uint8_t), bytes
.size(), file
) !=
236 fprintf(stderr
, "Could not write bytes to %s\n", filename
);
240 if (fclose(file
) != 0) {
241 fprintf(stderr
, "Could not close %s\n", filename
);
247 int main(int argc
, char* argv
[]) {
250 "Usage: %s <pfm> <jxl>\n"
252 " pfm = input Portable FloatMap image filename\n"
253 " jxl = output JPEG XL image filename\n"
254 "Output files will be overwritten.\n",
259 const char* pfm_filename
= argv
[1];
260 const char* jxl_filename
= argv
[2];
262 std::vector
<float> pixels
;
265 if (!ReadPFM(pfm_filename
, &pixels
, &xsize
, &ysize
)) {
266 fprintf(stderr
, "Couldn't load %s\n", pfm_filename
);
270 std::vector
<uint8_t> compressed
;
271 if (!EncodeJxlOneshot(pixels
, xsize
, ysize
, &compressed
)) {
272 fprintf(stderr
, "Couldn't encode jxl\n");
276 if (!WriteFile(compressed
, jxl_filename
)) {
277 fprintf(stderr
, "Couldn't write jxl file\n");