Files
leonarski_fandClaude Opus 5.5 d2d8102b66 JPEG: encode with the vendored stb_image_write instead of libjpeg-turbo
WriteJPEGToMem is the only JPEG code in the tree (encode only, no decoding),
and libjpeg-turbo came in as an ExternalProject whose imported target passed
its include dir as -isystem. Any other jpeglib.h on the include path (conda,
the distribution, the CUDA directory) could then win, and a build failed with
"Wrong JPEG library version: library is 62, caller expects 80".

stb_image_write v1.16 (commit 31c1ad3) is vendored in preview/stb/ and
compiled into JFJochJPEG.cpp alone with STB_IMAGE_WRITE_STATIC and
STBI_WRITE_NO_STDIO, so no JPEG symbol or header leaves that file. Output is
baseline JPEG (it was progressive); the API is unchanged. Size limits are
checked up front (JPEG's 65535 per side, stb's int pixel index) and an
encoder failure throws JFJochException. Quality is clamped to 1-100. stb
encodes an 18 Mpx frame in ~350 ms against ~210 ms for the scalar
libjpeg-turbo we built; accepted.

Removed: the ExternalProject and JPEG::JPEG target in preview/, nasm from the
Docker images, licenses/libjpeg-turbo.txt and its manifest row, and the CI
check that counted libjpeg-turbo copies in the viewer (the Qt JPEG/TIFF plugin
check stays, as does the viewer's plugin exclusion). Licence: MIT OR
Unlicense, recorded in licenses/ and THIRD_PARTY_NOTICES.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVeDQqrcrEZy6XBUCMZvyT
2026-11-01 13:49:11 +01:00

59 lines
2.2 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <algorithm>
#include <climits>
#include <cstdint>
#include <string>
#include <vector>
#include "../common/JFJochException.h"
#include "../common/CompressedImage.h"
#include "JFJochJPEG.h"
// The encoder is the vendored stb_image_write, compiled into this file only and with static linkage,
// so no JPEG symbol or header leaves it. File output is compiled out - the image goes to memory.
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#define STBI_WRITE_NO_STDIO
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4244 4505)
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "stb/stb_image_write.h"
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
static void AppendToString(void *context, void *data, int size) {
static_cast<std::string *>(context)->append(static_cast<const char *>(data), size);
}
std::string WriteJPEGToMem(const CompressedImage& image, int quality) {
if (image.GetMode() != CompressedImageMode::RGB)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Only RGB images allowed for JPEG saving");
// 65535 is the JPEG format's limit; stb also indexes the pixels with an int.
const int64_t width = image.GetWidth();
const int64_t height = image.GetHeight();
if (width <= 0 || height <= 0 || width > 65535 || height > 65535 || width * height * 3 > INT_MAX)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Image size not supported by JPEG encoder");
std::vector<uint8_t> buffer;
image.GetUncompressed(buffer);
std::string ret;
if (stbi_write_jpg_to_func(AppendToString, &ret, static_cast<int>(width), static_cast<int>(height), 3,
buffer.data(), std::clamp(quality, 1, 100)) == 0)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "JPEG encoding failed");
return ret;
}