Skip to content

Instantly share code, notes, and snippets.

@yllan
Last active February 1, 2021 06:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yllan/0da653b9898090bb5173bbd32eb15201 to your computer and use it in GitHub Desktop.
Save yllan/0da653b9898090bb5173bbd32eb15201 to your computer and use it in GitHub Desktop.
dc
// Copyright (c) the JPEG XL Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This C++ example decodes a JPEG XL image in one shot (all input bytes
// available at once). The example outputs the pixels and color information to a
// floating point image and an ICC profile on disk.
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
#include "jxl/thread_parallel_runner.h"
#include "jxl/thread_parallel_runner_cxx.h"
/** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are
* stored as floating point, as interleaved RGBA (4 floating point values per
* pixel), line per line from top to bottom. Pixel values have nominal range
* 0..1 but may go beyond this range for HDR or wide gamut. The ICC profile
* describes the color format of the pixel data.
*/
bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
std::vector<float>* pixels, std::vector<float>* dc, size_t* xsize,
size_t* ysize, std::vector<uint8_t>* icc_profile) {
// Multi-threaded parallel runner.
auto runner = JxlThreadParallelRunnerMake(
nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
auto dec = JxlDecoderMake(nullptr);
if (JXL_DEC_SUCCESS !=
JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
JXL_DEC_COLOR_ENCODING |
JXL_DEC_FULL_IMAGE |
JXL_DEC_DC_IMAGE)) {
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
return false;
}
if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
JxlThreadParallelRunner,
runner.get())) {
fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
return false;
}
JxlBasicInfo info;
JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
JxlDecoderSetInput(dec.get(), jxl, size);
for (;;) {
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
if (status == JXL_DEC_ERROR) {
fprintf(stderr, "Decoder error\n");
return false;
} else if (status == JXL_DEC_NEED_MORE_INPUT) {
fprintf(stderr, "Error, already provided all input\n");
return false;
} else if (status == JXL_DEC_BASIC_INFO) {
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
return false;
}
*xsize = info.xsize;
*ysize = info.ysize;
} else if (status == JXL_DEC_COLOR_ENCODING) {
// Get the ICC color profile of the pixel data
size_t icc_size;
if (JXL_DEC_SUCCESS !=
JxlDecoderGetICCProfileSize(
dec.get(), &format, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size)) {
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
return false;
}
icc_profile->resize(icc_size);
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
dec.get(), &format,
JXL_COLOR_PROFILE_TARGET_DATA,
icc_profile->data(), icc_profile->size())) {
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
return false;
}
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
size_t buffer_size;
if (JXL_DEC_SUCCESS !=
JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
return false;
}
if (buffer_size != *xsize * *ysize * 16) {
fprintf(stderr, "Invalid out buffer size %zu %zu\n", buffer_size,
*xsize * *ysize * 16);
return false;
}
pixels->resize(*xsize * *ysize * 4);
void* pixels_buffer = (void*)pixels->data();
size_t pixels_buffer_size = pixels->size() * sizeof(float);
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
pixels_buffer,
pixels_buffer_size)) {
fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
return false;
}
} else if (status == JXL_DEC_NEED_DC_OUT_BUFFER) {
size_t dc_size;
if (JXL_DEC_SUCCESS !=
JxlDecoderDCOutBufferSize(dec.get(), &format, &dc_size)) {
fprintf(stderr, "JxlDecoderDCOutBufferSize failed\n");
return false;
}
size_t dc_x = (*xsize + 7) / 8;
size_t dc_y = (*ysize + 7) / 8;
if (dc_size != dc_x * dc_y * 16) {
fprintf(stderr, "Invalid out dc size %zu %zu\n", dc_size,
dc_x * dc_y * 16);
return false;
}
dc->resize(dc_x * dc_y * 4);
void* dc_buffer = (void*)dc->data();
size_t dc_buffer_size = dc->size() * sizeof(float);
if (JXL_DEC_SUCCESS != JxlDecoderSetDCOutBuffer(dec.get(), &format,
dc_buffer,
dc_buffer_size)) {
fprintf(stderr, "JxlDecoderSetDCOutBuffer failed\n");
return false;
}
} else if (status == JXL_DEC_DC_IMAGE) {
fprintf(stderr, "JXL_DEC_DC_IMAGE\n");
} else if (status == JXL_DEC_FULL_IMAGE) {
// Nothing to do. Do not yet return. If the image is an animation, more
// full frames may be decoded. This example only keeps the last one.
} else if (status == JXL_DEC_SUCCESS) {
// All decoding successfully finished.
// It's not required to call JxlDecoderReleaseInput(dec.get()) here since
// the decoder will be destroyed.
return true;
} else {
fprintf(stderr, "Unknown decoder status\n");
return false;
}
}
}
/** Writes to .pfm file (Portable FloatMap). Gimp, tev viewer and ImageMagick
* support viewing this format.
* The input pixels are given as 32-bit floating point with 4-channel RGBA.
* The alpha channel will not be written since .pfm does not support it.
*/
bool WritePFM(const char* filename, const float* pixels, size_t xsize,
size_t ysize) {
FILE* file = fopen(filename, "wb");
if (!file) {
fprintf(stderr, "Could not open %s for writing", filename);
return false;
}
uint32_t endian_test = 1;
uint8_t little_endian[4];
memcpy(little_endian, &endian_test, 4);
fprintf(file, "PF\n%d %d\n%s\n", (int)xsize, (int)ysize,
little_endian[0] ? "-1.0" : "1.0");
for (int y = ysize - 1; y >= 0; y--) {
for (size_t x = 0; x < xsize; x++) {
for (size_t c = 0; c < 3; c++) {
const float* f = &pixels[(y * xsize + x) * 4 + c];
fwrite(f, 4, 1, file);
}
}
}
if (fclose(file) != 0) {
return false;
}
return true;
}
bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
FILE* file = fopen(filename, "rb");
if (!file) {
return false;
}
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return false;
}
long size = ftell(file);
// Avoid invalid file or directory.
if (size >= LONG_MAX || size < 0) {
fclose(file);
return false;
}
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return false;
}
out->resize(size);
size_t readsize = fread(out->data(), 1, size, file);
if (fclose(file) != 0) {
return false;
}
return readsize == static_cast<size_t>(size);
}
bool WriteFile(const char* filename, const uint8_t* data, size_t size) {
FILE* file = fopen(filename, "wb");
if (!file) {
fprintf(stderr, "Could not open %s for writing", filename);
return false;
}
fwrite(data, 1, size, file);
if (fclose(file) != 0) {
return false;
}
return true;
}
int main(int argc, char* argv[]) {
if (argc != 4) {
fprintf(stderr,
"Usage: %s <jxl> <pfm> <icc>\n"
"Where:\n"
" jxl = input JPEG XL image filename\n"
" pfm = output Portable FloatMap image filename\n"
" icc = output ICC color profile filename\n"
"Output files will be overwritten.\n",
argv[0]);
return 1;
}
const char* jxl_filename = argv[1];
const char* pfm_filename = argv[2];
const char* icc_filename = argv[3];
std::string dc_filename = std::string(pfm_filename) + "-dc.pfm";
std::vector<uint8_t> jxl;
if (!LoadFile(jxl_filename, &jxl)) {
fprintf(stderr, "couldn't load %s\n", jxl_filename);
return 1;
}
std::vector<float> pixels;
std::vector<float> dc;
std::vector<uint8_t> icc_profile;
size_t xsize = 0, ysize = 0;
if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &dc, &xsize, &ysize,
&icc_profile)) {
fprintf(stderr, "Error while decoding the jxl file\n");
return 1;
}
if (!WritePFM(pfm_filename, pixels.data(), xsize, ysize)) {
fprintf(stderr, "Error while writing the PFM image file\n");
return 1;
}
if (!WritePFM(dc_filename.c_str(), dc.data(), (xsize + 7)/8, (ysize + 7)/8)) {
fprintf(stderr, "Error while writing the DC PFM image file\n");
return 1;
}
if (!WriteFile(icc_filename, icc_profile.data(), icc_profile.size())) {
fprintf(stderr, "Error while writing the ICC profile file\n");
return 1;
}
printf("Successfully wrote %s and %s\n", pfm_filename, icc_filename);
return 0;
}
This file has been truncated, but you can view the full file.
PF
504 378
-1.0
View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment