destination_manager.cc (5412B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include <string.h> 7 8 #include "lib/jpegli/encode.h" 9 #include "lib/jpegli/error.h" 10 #include "lib/jpegli/memory_manager.h" 11 12 namespace jpegli { 13 14 constexpr size_t kDestBufferSize = 64 << 10; 15 16 struct StdioDestinationManager { 17 jpeg_destination_mgr pub; 18 FILE* f; 19 uint8_t* buffer; 20 21 static void init_destination(j_compress_ptr cinfo) { 22 auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); 23 dest->pub.next_output_byte = dest->buffer; 24 dest->pub.free_in_buffer = kDestBufferSize; 25 } 26 27 static boolean empty_output_buffer(j_compress_ptr cinfo) { 28 auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); 29 if (fwrite(dest->buffer, 1, kDestBufferSize, dest->f) != kDestBufferSize) { 30 JPEGLI_ERROR("Failed to write to output stream."); 31 } 32 dest->pub.next_output_byte = dest->buffer; 33 dest->pub.free_in_buffer = kDestBufferSize; 34 return TRUE; 35 } 36 37 static void term_destination(j_compress_ptr cinfo) { 38 auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); 39 size_t bytes_left = kDestBufferSize - dest->pub.free_in_buffer; 40 if (bytes_left && 41 fwrite(dest->buffer, 1, bytes_left, dest->f) != bytes_left) { 42 JPEGLI_ERROR("Failed to write to output stream."); 43 } 44 fflush(dest->f); 45 if (ferror(dest->f)) { 46 JPEGLI_ERROR("Failed to write to output stream."); 47 } 48 } 49 }; 50 51 struct MemoryDestinationManager { 52 jpeg_destination_mgr pub; 53 // Output buffer supplied by the application 54 uint8_t** output; 55 unsigned long* output_size; 56 // Output buffer allocated by us. 57 uint8_t* temp_buffer; 58 // Current output buffer (either application supplied or allocated by us). 59 uint8_t* current_buffer; 60 size_t buffer_size; 61 62 static void init_destination(j_compress_ptr cinfo) {} 63 64 static boolean empty_output_buffer(j_compress_ptr cinfo) { 65 auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest); 66 uint8_t* next_buffer = 67 reinterpret_cast<uint8_t*>(malloc(dest->buffer_size * 2)); 68 memcpy(next_buffer, dest->current_buffer, dest->buffer_size); 69 if (dest->temp_buffer != nullptr) { 70 free(dest->temp_buffer); 71 } 72 dest->temp_buffer = next_buffer; 73 dest->current_buffer = next_buffer; 74 *dest->output = next_buffer; 75 *dest->output_size = dest->buffer_size; 76 dest->pub.next_output_byte = next_buffer + dest->buffer_size; 77 dest->pub.free_in_buffer = dest->buffer_size; 78 dest->buffer_size *= 2; 79 return TRUE; 80 } 81 82 static void term_destination(j_compress_ptr cinfo) { 83 auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest); 84 *dest->output_size = dest->buffer_size - dest->pub.free_in_buffer; 85 } 86 }; 87 88 } // namespace jpegli 89 90 void jpegli_stdio_dest(j_compress_ptr cinfo, FILE* outfile) { 91 if (outfile == nullptr) { 92 JPEGLI_ERROR("jpegli_stdio_dest: Invalid destination."); 93 } 94 if (cinfo->dest && cinfo->dest->init_destination != 95 jpegli::StdioDestinationManager::init_destination) { 96 JPEGLI_ERROR("jpegli_stdio_dest: a different dest manager was already set"); 97 } 98 if (!cinfo->dest) { 99 cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>( 100 jpegli::Allocate<jpegli::StdioDestinationManager>(cinfo, 1)); 101 } 102 auto* dest = reinterpret_cast<jpegli::StdioDestinationManager*>(cinfo->dest); 103 dest->f = outfile; 104 dest->buffer = jpegli::Allocate<uint8_t>(cinfo, jpegli::kDestBufferSize); 105 dest->pub.next_output_byte = dest->buffer; 106 dest->pub.free_in_buffer = jpegli::kDestBufferSize; 107 dest->pub.init_destination = 108 jpegli::StdioDestinationManager::init_destination; 109 dest->pub.empty_output_buffer = 110 jpegli::StdioDestinationManager::empty_output_buffer; 111 dest->pub.term_destination = 112 jpegli::StdioDestinationManager::term_destination; 113 } 114 115 void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer, 116 unsigned long* outsize) { 117 if (outbuffer == nullptr || outsize == nullptr) { 118 JPEGLI_ERROR("jpegli_mem_dest: Invalid destination."); 119 } 120 if (cinfo->dest && cinfo->dest->init_destination != 121 jpegli::MemoryDestinationManager::init_destination) { 122 JPEGLI_ERROR("jpegli_mem_dest: a different dest manager was already set"); 123 } 124 if (!cinfo->dest) { 125 auto* dest = jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1); 126 dest->temp_buffer = nullptr; 127 cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(dest); 128 } 129 auto* dest = reinterpret_cast<jpegli::MemoryDestinationManager*>(cinfo->dest); 130 dest->pub.init_destination = 131 jpegli::MemoryDestinationManager::init_destination; 132 dest->pub.empty_output_buffer = 133 jpegli::MemoryDestinationManager::empty_output_buffer; 134 dest->pub.term_destination = 135 jpegli::MemoryDestinationManager::term_destination; 136 dest->output = outbuffer; 137 dest->output_size = outsize; 138 if (*outbuffer == nullptr || *outsize == 0) { 139 dest->temp_buffer = 140 reinterpret_cast<uint8_t*>(malloc(jpegli::kDestBufferSize)); 141 *outbuffer = dest->temp_buffer; 142 *outsize = jpegli::kDestBufferSize; 143 } 144 dest->current_buffer = *outbuffer; 145 dest->buffer_size = *outsize; 146 dest->pub.next_output_byte = dest->current_buffer; 147 dest->pub.free_in_buffer = dest->buffer_size; 148 }