diff options
Diffstat (limited to 'kimgio/webp.cpp')
-rw-r--r-- | kimgio/webp.cpp | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/kimgio/webp.cpp b/kimgio/webp.cpp new file mode 100644 index 000000000..ea50ee3b6 --- /dev/null +++ b/kimgio/webp.cpp @@ -0,0 +1,148 @@ +// WebP read support +// © 2024 Alexander Hajnal +// Based loosely on jp2.cpp +// +// If implementing write support it's suggested to use lossless mode with exact +// mode enabled when the quality setting is 100 and for other qualities to use +// lossy mode with the default settings. +// +// This library is distributed under the conditions of the GNU LGPL. +#include "config.h" + +#include <tdetempfile.h> +#include <tqfile.h> +#include <tqimage.h> + +#include <webp/decode.h> +#include <cstdlib> + +#ifdef __cplusplus +extern "C" { +#endif + +TDE_EXPORT void kimgio_webp_read( TQImageIO* io ) +{ + int width, height; + FILE* in; + + // === Read the source file === + // Based on code in jp2.cpp + + // for QIODevice's other than TQFile, a temp. file is used. + KTempFile* tempf = 0; + + TQFile* qf = 0; + if( ( qf = dynamic_cast<TQFile*>( io->ioDevice() ) ) ) { + // great, it's a TQFile. Let's just take the filename. + in = fopen( TQFile::encodeName( qf->name() ), "rb" ); + } else { + // not a TQFile. Copy the whole data to a temp. file. + tempf = new KTempFile(); + if( tempf->status() != 0 ) { + delete tempf; + return; + } // if + tempf->setAutoDelete( true ); + TQFile* out = tempf->file(); + // 4096 (=4k) is a common page size. + TQByteArray b( 4096 ); + TQ_LONG size; + // 0 or -1 is EOF / error + while( ( size = io->ioDevice()->readBlock( b.data(), 4096 ) ) > 0 ) { + // in case of a write error, still give the decoder a try + if( ( out->writeBlock( b.data(), size ) ) == -1 ) break; + } // while + // flush everything out to disk + out->flush(); + + in = fopen( TQFile::encodeName( tempf->name() ), "rb" ); + } // else + if( ! in ) { + delete tempf; + return; + } // if + + // File is now open + + // === Load compressed data === + + // Find file's size + fseek(in, 0L, SEEK_END); // Seek to end of file + long size = ftell(in); // Get position (i.e. the file size) + fseek(in, 0L, SEEK_SET); // Seek back to start of file + + // Sanity check + if ( size > SIZE_MAX ) { + // File size is larger than a size_t can hold + fclose( in ); + delete tempf; + return; + } + + // Allocate a buffer for the compressed data + uint8_t* compressed_image = (uint8_t*)malloc(size); + if( ! compressed_image ) { + // malloc failed + fclose( in ); + delete tempf; + return; + } // if + + // Read compressed image into buffer + size_t bytes_read = fread( compressed_image, sizeof(uint8_t), size, in ); + + // Close the compressed image file + fclose( in ); + delete tempf; + + if ( bytes_read < size ) { + // Read failed + free( compressed_image ); + return; + } + + // === Decompress image === + + // Get image dimensions + if ( ! WebPGetInfo( compressed_image, size, &width, &height ) ) { + // Error + free( compressed_image ); + return; + } + + // Create an appropriately sized image + TQImage image; + if( ! image.create( width, height, 32 ) ) { + // Error + free( compressed_image ); + return; + } + + // Enable alpha channel + image.setAlphaBuffer(true); + + // Get the image buffer + uint32_t* data = (uint32_t*)image.bits(); + + // Decompress the image +#ifdef WORDS_BIGENDIAN + if ( ! WebPDecodeARGBInto( compressed_image, size, (uint8_t*)data, width*height*4, width*4) ) { +#else + if ( ! WebPDecodeBGRAInto( compressed_image, size, (uint8_t*)data, width*height*4, width*4) ) { +#endif + // Error + free( compressed_image ); + return; + } + + // Free the compressed image buffer + free( compressed_image ); + + // Finalize load + io->setImage( image ); + io->setStatus( 0 ); +} // kimgio_webp_read + +#ifdef __cplusplus +} +#endif |