148 lines
5.3 KiB
C++
148 lines
5.3 KiB
C++
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
#include <string.h>
|
|
|
|
#include "lib/jpegli/encode.h"
|
|
#include "lib/jpegli/error.h"
|
|
#include "lib/jpegli/memory_manager.h"
|
|
|
|
namespace jpegli {
|
|
|
|
constexpr size_t kDestBufferSize = 64 << 10;
|
|
|
|
struct StdioDestinationManager {
|
|
jpeg_destination_mgr pub;
|
|
FILE* f;
|
|
uint8_t* buffer;
|
|
|
|
static void init_destination(j_compress_ptr cinfo) {
|
|
auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
|
|
dest->pub.next_output_byte = dest->buffer;
|
|
dest->pub.free_in_buffer = kDestBufferSize;
|
|
}
|
|
|
|
static boolean empty_output_buffer(j_compress_ptr cinfo) {
|
|
auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
|
|
if (fwrite(dest->buffer, 1, kDestBufferSize, dest->f) != kDestBufferSize) {
|
|
JPEGLI_ERROR("Failed to write to output stream.");
|
|
}
|
|
dest->pub.next_output_byte = dest->buffer;
|
|
dest->pub.free_in_buffer = kDestBufferSize;
|
|
return TRUE;
|
|
}
|
|
|
|
static void term_destination(j_compress_ptr cinfo) {
|
|
auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
|
|
size_t bytes_left = kDestBufferSize - dest->pub.free_in_buffer;
|
|
if (bytes_left &&
|
|
fwrite(dest->buffer, 1, bytes_left, dest->f) != bytes_left) {
|
|
JPEGLI_ERROR("Failed to write to output stream.");
|
|
}
|
|
fflush(dest->f);
|
|
if (ferror(dest->f)) {
|
|
JPEGLI_ERROR("Failed to write to output stream.");
|
|
}
|
|
}
|
|
};
|
|
|
|
struct MemoryDestinationManager {
|
|
jpeg_destination_mgr pub;
|
|
// Output buffer supplied by the application
|
|
uint8_t** output;
|
|
unsigned long* output_size; // NOLINT
|
|
// Output buffer allocated by us.
|
|
uint8_t* temp_buffer;
|
|
// Current output buffer (either application supplied or allocated by us).
|
|
uint8_t* current_buffer;
|
|
size_t buffer_size;
|
|
|
|
static void init_destination(j_compress_ptr cinfo) {}
|
|
|
|
static boolean empty_output_buffer(j_compress_ptr cinfo) {
|
|
auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest);
|
|
uint8_t* next_buffer =
|
|
reinterpret_cast<uint8_t*>(malloc(dest->buffer_size * 2));
|
|
memcpy(next_buffer, dest->current_buffer, dest->buffer_size);
|
|
if (dest->temp_buffer != nullptr) {
|
|
free(dest->temp_buffer);
|
|
}
|
|
dest->temp_buffer = next_buffer;
|
|
dest->current_buffer = next_buffer;
|
|
*dest->output = next_buffer;
|
|
*dest->output_size = dest->buffer_size;
|
|
dest->pub.next_output_byte = next_buffer + dest->buffer_size;
|
|
dest->pub.free_in_buffer = dest->buffer_size;
|
|
dest->buffer_size *= 2;
|
|
return TRUE;
|
|
}
|
|
|
|
static void term_destination(j_compress_ptr cinfo) {
|
|
auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest);
|
|
*dest->output_size = dest->buffer_size - dest->pub.free_in_buffer;
|
|
}
|
|
};
|
|
|
|
} // namespace jpegli
|
|
|
|
void jpegli_stdio_dest(j_compress_ptr cinfo, FILE* outfile) {
|
|
if (outfile == nullptr) {
|
|
JPEGLI_ERROR("jpegli_stdio_dest: Invalid destination.");
|
|
}
|
|
if (cinfo->dest && cinfo->dest->init_destination !=
|
|
jpegli::StdioDestinationManager::init_destination) {
|
|
JPEGLI_ERROR("jpegli_stdio_dest: a different dest manager was already set");
|
|
}
|
|
if (!cinfo->dest) {
|
|
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(
|
|
jpegli::Allocate<jpegli::StdioDestinationManager>(cinfo, 1));
|
|
}
|
|
auto* dest = reinterpret_cast<jpegli::StdioDestinationManager*>(cinfo->dest);
|
|
dest->f = outfile;
|
|
dest->buffer = jpegli::Allocate<uint8_t>(cinfo, jpegli::kDestBufferSize);
|
|
dest->pub.next_output_byte = dest->buffer;
|
|
dest->pub.free_in_buffer = jpegli::kDestBufferSize;
|
|
dest->pub.init_destination =
|
|
jpegli::StdioDestinationManager::init_destination;
|
|
dest->pub.empty_output_buffer =
|
|
jpegli::StdioDestinationManager::empty_output_buffer;
|
|
dest->pub.term_destination =
|
|
jpegli::StdioDestinationManager::term_destination;
|
|
}
|
|
|
|
void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer,
|
|
unsigned long* outsize /* NOLINT */) {
|
|
if (outbuffer == nullptr || outsize == nullptr) {
|
|
JPEGLI_ERROR("jpegli_mem_dest: Invalid destination.");
|
|
}
|
|
if (cinfo->dest && cinfo->dest->init_destination !=
|
|
jpegli::MemoryDestinationManager::init_destination) {
|
|
JPEGLI_ERROR("jpegli_mem_dest: a different dest manager was already set");
|
|
}
|
|
if (!cinfo->dest) {
|
|
auto* dest = jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1);
|
|
dest->temp_buffer = nullptr;
|
|
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(dest);
|
|
}
|
|
auto* dest = reinterpret_cast<jpegli::MemoryDestinationManager*>(cinfo->dest);
|
|
dest->pub.init_destination =
|
|
jpegli::MemoryDestinationManager::init_destination;
|
|
dest->pub.empty_output_buffer =
|
|
jpegli::MemoryDestinationManager::empty_output_buffer;
|
|
dest->pub.term_destination =
|
|
jpegli::MemoryDestinationManager::term_destination;
|
|
dest->output = outbuffer;
|
|
dest->output_size = outsize;
|
|
if (*outbuffer == nullptr || *outsize == 0) {
|
|
dest->temp_buffer =
|
|
reinterpret_cast<uint8_t*>(malloc(jpegli::kDestBufferSize));
|
|
*outbuffer = dest->temp_buffer;
|
|
*outsize = jpegli::kDestBufferSize;
|
|
}
|
|
dest->current_buffer = *outbuffer;
|
|
dest->buffer_size = *outsize;
|
|
dest->pub.next_output_byte = dest->current_buffer;
|
|
dest->pub.free_in_buffer = dest->buffer_size;
|
|
}
|