mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
495 lines
13 KiB
495 lines
13 KiB
/* $Id: tif_lzma.c,v 1.4 2011-12-22 00:29:29 bfriesen Exp $ */ |
|
|
|
/* |
|
* Copyright (c) 2010, Andrey Kiselev <dron@ak4719.spb.edu> |
|
* |
|
* Permission to use, copy, modify, distribute, and sell this software and |
|
* its documentation for any purpose is hereby granted without fee, provided |
|
* that (i) the above copyright notices and this permission notice appear in |
|
* all copies of the software and related documentation, and (ii) the names of |
|
* Sam Leffler and Silicon Graphics may not be used in any advertising or |
|
* publicity relating to the software without the specific, prior written |
|
* permission of Sam Leffler and Silicon Graphics. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, |
|
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY |
|
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. |
|
* |
|
* IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR |
|
* ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, |
|
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
|
* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF |
|
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
|
* OF THIS SOFTWARE. |
|
*/ |
|
|
|
#include "tiffiop.h" |
|
#ifdef LZMA_SUPPORT |
|
/* |
|
* TIFF Library. |
|
* |
|
* LZMA2 Compression Support |
|
* |
|
* You need an LZMA2 SDK to link with. See http://tukaani.org/xz/ for details. |
|
* |
|
* The codec is derived from ZLIB codec (tif_zip.c). |
|
*/ |
|
|
|
#include "tif_predict.h" |
|
#include "lzma.h" |
|
|
|
#include <stdio.h> |
|
|
|
/* |
|
* State block for each open TIFF file using LZMA2 compression/decompression. |
|
*/ |
|
typedef struct { |
|
TIFFPredictorState predict; |
|
lzma_stream stream; |
|
lzma_filter filters[LZMA_FILTERS_MAX + 1]; |
|
lzma_options_delta opt_delta; /* delta filter options */ |
|
lzma_options_lzma opt_lzma; /* LZMA2 filter options */ |
|
int preset; /* compression level */ |
|
lzma_check check; /* type of the integrity check */ |
|
int state; /* state flags */ |
|
#define LSTATE_INIT_DECODE 0x01 |
|
#define LSTATE_INIT_ENCODE 0x02 |
|
|
|
TIFFVGetMethod vgetparent; /* super-class method */ |
|
TIFFVSetMethod vsetparent; /* super-class method */ |
|
} LZMAState; |
|
|
|
#define LState(tif) ((LZMAState*) (tif)->tif_data) |
|
#define DecoderState(tif) LState(tif) |
|
#define EncoderState(tif) LState(tif) |
|
|
|
static int LZMAEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s); |
|
static int LZMADecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s); |
|
|
|
static const char * |
|
LZMAStrerror(lzma_ret ret) |
|
{ |
|
switch (ret) { |
|
case LZMA_OK: |
|
return "operation completed successfully"; |
|
case LZMA_STREAM_END: |
|
return "end of stream was reached"; |
|
case LZMA_NO_CHECK: |
|
return "input stream has no integrity check"; |
|
case LZMA_UNSUPPORTED_CHECK: |
|
return "cannot calculate the integrity check"; |
|
case LZMA_GET_CHECK: |
|
return "integrity check type is now available"; |
|
case LZMA_MEM_ERROR: |
|
return "cannot allocate memory"; |
|
case LZMA_MEMLIMIT_ERROR: |
|
return "memory usage limit was reached"; |
|
case LZMA_FORMAT_ERROR: |
|
return "file format not recognized"; |
|
case LZMA_OPTIONS_ERROR: |
|
return "invalid or unsupported options"; |
|
case LZMA_DATA_ERROR: |
|
return "data is corrupt"; |
|
case LZMA_BUF_ERROR: |
|
return "no progress is possible (stream is truncated or corrupt)"; |
|
case LZMA_PROG_ERROR: |
|
return "programming error"; |
|
default: |
|
return "unindentified liblzma error"; |
|
} |
|
} |
|
|
|
static int |
|
LZMAFixupTags(TIFF* tif) |
|
{ |
|
(void) tif; |
|
return 1; |
|
} |
|
|
|
static int |
|
LZMASetupDecode(TIFF* tif) |
|
{ |
|
LZMAState* sp = DecoderState(tif); |
|
|
|
assert(sp != NULL); |
|
|
|
/* if we were last encoding, terminate this mode */ |
|
if (sp->state & LSTATE_INIT_ENCODE) { |
|
lzma_end(&sp->stream); |
|
sp->state = 0; |
|
} |
|
|
|
sp->state |= LSTATE_INIT_DECODE; |
|
return 1; |
|
} |
|
|
|
/* |
|
* Setup state for decoding a strip. |
|
*/ |
|
static int |
|
LZMAPreDecode(TIFF* tif, uint16 s) |
|
{ |
|
static const char module[] = "LZMAPreDecode"; |
|
LZMAState* sp = DecoderState(tif); |
|
lzma_ret ret; |
|
|
|
(void) s; |
|
assert(sp != NULL); |
|
|
|
if( (sp->state & LSTATE_INIT_DECODE) == 0 ) |
|
tif->tif_setupdecode(tif); |
|
|
|
sp->stream.next_in = tif->tif_rawdata; |
|
sp->stream.avail_in = (size_t) tif->tif_rawcc; |
|
if ((tmsize_t)sp->stream.avail_in != tif->tif_rawcc) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Liblzma cannot deal with buffers this size"); |
|
return 0; |
|
} |
|
|
|
/* |
|
* Disable memory limit when decoding. UINT64_MAX is a flag to disable |
|
* the limit, we are passing (uint64_t)-1 which should be the same. |
|
*/ |
|
ret = lzma_stream_decoder(&sp->stream, (uint64_t)-1, 0); |
|
if (ret != LZMA_OK) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Error initializing the stream decoder, %s", |
|
LZMAStrerror(ret)); |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
static int |
|
LZMADecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) |
|
{ |
|
static const char module[] = "LZMADecode"; |
|
LZMAState* sp = DecoderState(tif); |
|
|
|
(void) s; |
|
assert(sp != NULL); |
|
assert(sp->state == LSTATE_INIT_DECODE); |
|
|
|
sp->stream.next_in = tif->tif_rawcp; |
|
sp->stream.avail_in = (size_t) tif->tif_rawcc; |
|
|
|
sp->stream.next_out = op; |
|
sp->stream.avail_out = (size_t) occ; |
|
if ((tmsize_t)sp->stream.avail_out != occ) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Liblzma cannot deal with buffers this size"); |
|
return 0; |
|
} |
|
|
|
do { |
|
/* |
|
* Save the current stream state to properly recover from the |
|
* decoding errors later. |
|
*/ |
|
const uint8_t *next_in = sp->stream.next_in; |
|
size_t avail_in = sp->stream.avail_in; |
|
|
|
lzma_ret ret = lzma_code(&sp->stream, LZMA_RUN); |
|
if (ret == LZMA_STREAM_END) |
|
break; |
|
if (ret == LZMA_MEMLIMIT_ERROR) { |
|
lzma_ret r = lzma_stream_decoder(&sp->stream, |
|
lzma_memusage(&sp->stream), 0); |
|
if (r != LZMA_OK) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Error initializing the stream decoder, %s", |
|
LZMAStrerror(r)); |
|
break; |
|
} |
|
sp->stream.next_in = next_in; |
|
sp->stream.avail_in = avail_in; |
|
continue; |
|
} |
|
if (ret != LZMA_OK) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Decoding error at scanline %lu, %s", |
|
(unsigned long) tif->tif_row, LZMAStrerror(ret)); |
|
break; |
|
} |
|
} while (sp->stream.avail_out > 0); |
|
if (sp->stream.avail_out != 0) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Not enough data at scanline %lu (short %lu bytes)", |
|
(unsigned long) tif->tif_row, (unsigned long) sp->stream.avail_out); |
|
return 0; |
|
} |
|
|
|
tif->tif_rawcp = (uint8 *)sp->stream.next_in; /* cast away const */ |
|
tif->tif_rawcc = sp->stream.avail_in; |
|
|
|
return 1; |
|
} |
|
|
|
static int |
|
LZMASetupEncode(TIFF* tif) |
|
{ |
|
LZMAState* sp = EncoderState(tif); |
|
|
|
assert(sp != NULL); |
|
if (sp->state & LSTATE_INIT_DECODE) { |
|
lzma_end(&sp->stream); |
|
sp->state = 0; |
|
} |
|
|
|
sp->state |= LSTATE_INIT_ENCODE; |
|
return 1; |
|
} |
|
|
|
/* |
|
* Reset encoding state at the start of a strip. |
|
*/ |
|
static int |
|
LZMAPreEncode(TIFF* tif, uint16 s) |
|
{ |
|
static const char module[] = "LZMAPreEncode"; |
|
LZMAState *sp = EncoderState(tif); |
|
|
|
(void) s; |
|
assert(sp != NULL); |
|
if( sp->state != LSTATE_INIT_ENCODE ) |
|
tif->tif_setupencode(tif); |
|
|
|
sp->stream.next_out = tif->tif_rawdata; |
|
sp->stream.avail_out = (size_t)tif->tif_rawdatasize; |
|
if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Liblzma cannot deal with buffers this size"); |
|
return 0; |
|
} |
|
return (lzma_stream_encoder(&sp->stream, sp->filters, sp->check) == LZMA_OK); |
|
} |
|
|
|
/* |
|
* Encode a chunk of pixels. |
|
*/ |
|
static int |
|
LZMAEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) |
|
{ |
|
static const char module[] = "LZMAEncode"; |
|
LZMAState *sp = EncoderState(tif); |
|
|
|
assert(sp != NULL); |
|
assert(sp->state == LSTATE_INIT_ENCODE); |
|
|
|
(void) s; |
|
sp->stream.next_in = bp; |
|
sp->stream.avail_in = (size_t) cc; |
|
if ((tmsize_t)sp->stream.avail_in != cc) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Liblzma cannot deal with buffers this size"); |
|
return 0; |
|
} |
|
do { |
|
lzma_ret ret = lzma_code(&sp->stream, LZMA_RUN); |
|
if (ret != LZMA_OK) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Encoding error at scanline %lu, %s", |
|
(unsigned long) tif->tif_row, LZMAStrerror(ret)); |
|
return 0; |
|
} |
|
if (sp->stream.avail_out == 0) { |
|
tif->tif_rawcc = tif->tif_rawdatasize; |
|
TIFFFlushData1(tif); |
|
sp->stream.next_out = tif->tif_rawdata; |
|
sp->stream.avail_out = (size_t)tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in LZMAPreEncode */ |
|
} |
|
} while (sp->stream.avail_in > 0); |
|
return 1; |
|
} |
|
|
|
/* |
|
* Finish off an encoded strip by flushing the last |
|
* string and tacking on an End Of Information code. |
|
*/ |
|
static int |
|
LZMAPostEncode(TIFF* tif) |
|
{ |
|
static const char module[] = "LZMAPostEncode"; |
|
LZMAState *sp = EncoderState(tif); |
|
lzma_ret ret; |
|
|
|
sp->stream.avail_in = 0; |
|
do { |
|
ret = lzma_code(&sp->stream, LZMA_FINISH); |
|
switch (ret) { |
|
case LZMA_STREAM_END: |
|
case LZMA_OK: |
|
if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) { |
|
tif->tif_rawcc = |
|
tif->tif_rawdatasize - sp->stream.avail_out; |
|
TIFFFlushData1(tif); |
|
sp->stream.next_out = tif->tif_rawdata; |
|
sp->stream.avail_out = (size_t)tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in ZIPPreEncode */ |
|
} |
|
break; |
|
default: |
|
TIFFErrorExt(tif->tif_clientdata, module, "Liblzma error: %s", |
|
LZMAStrerror(ret)); |
|
return 0; |
|
} |
|
} while (ret != LZMA_STREAM_END); |
|
return 1; |
|
} |
|
|
|
static void |
|
LZMACleanup(TIFF* tif) |
|
{ |
|
LZMAState* sp = LState(tif); |
|
|
|
assert(sp != 0); |
|
|
|
(void)TIFFPredictorCleanup(tif); |
|
|
|
tif->tif_tagmethods.vgetfield = sp->vgetparent; |
|
tif->tif_tagmethods.vsetfield = sp->vsetparent; |
|
|
|
if (sp->state) { |
|
lzma_end(&sp->stream); |
|
sp->state = 0; |
|
} |
|
_TIFFfree(sp); |
|
tif->tif_data = NULL; |
|
|
|
_TIFFSetDefaultCompressionState(tif); |
|
} |
|
|
|
static int |
|
LZMAVSetField(TIFF* tif, uint32 tag, va_list ap) |
|
{ |
|
static const char module[] = "LZMAVSetField"; |
|
LZMAState* sp = LState(tif); |
|
|
|
switch (tag) { |
|
case TIFFTAG_LZMAPRESET: |
|
sp->preset = (int) va_arg(ap, int); |
|
lzma_lzma_preset(&sp->opt_lzma, sp->preset); |
|
if (sp->state & LSTATE_INIT_ENCODE) { |
|
lzma_ret ret = lzma_stream_encoder(&sp->stream, |
|
sp->filters, |
|
sp->check); |
|
if (ret != LZMA_OK) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Liblzma error: %s", |
|
LZMAStrerror(ret)); |
|
} |
|
} |
|
return 1; |
|
default: |
|
return (*sp->vsetparent)(tif, tag, ap); |
|
} |
|
/*NOTREACHED*/ |
|
} |
|
|
|
static int |
|
LZMAVGetField(TIFF* tif, uint32 tag, va_list ap) |
|
{ |
|
LZMAState* sp = LState(tif); |
|
|
|
switch (tag) { |
|
case TIFFTAG_LZMAPRESET: |
|
*va_arg(ap, int*) = sp->preset; |
|
break; |
|
default: |
|
return (*sp->vgetparent)(tif, tag, ap); |
|
} |
|
return 1; |
|
} |
|
|
|
static const TIFFField lzmaFields[] = { |
|
{ TIFFTAG_LZMAPRESET, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, |
|
FIELD_PSEUDO, TRUE, FALSE, "LZMA2 Compression Preset", NULL }, |
|
}; |
|
|
|
int |
|
TIFFInitLZMA(TIFF* tif, int scheme) |
|
{ |
|
static const char module[] = "TIFFInitLZMA"; |
|
LZMAState* sp; |
|
lzma_stream tmp_stream = LZMA_STREAM_INIT; |
|
|
|
assert( scheme == COMPRESSION_LZMA ); |
|
|
|
/* |
|
* Merge codec-specific tag information. |
|
*/ |
|
if (!_TIFFMergeFields(tif, lzmaFields, TIFFArrayCount(lzmaFields))) { |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"Merging LZMA2 codec-specific tags failed"); |
|
return 0; |
|
} |
|
|
|
/* |
|
* Allocate state block so tag methods have storage to record values. |
|
*/ |
|
tif->tif_data = (uint8*) _TIFFmalloc(sizeof(LZMAState)); |
|
if (tif->tif_data == NULL) |
|
goto bad; |
|
sp = LState(tif); |
|
memcpy(&sp->stream, &tmp_stream, sizeof(lzma_stream)); |
|
|
|
/* |
|
* Override parent get/set field methods. |
|
*/ |
|
sp->vgetparent = tif->tif_tagmethods.vgetfield; |
|
tif->tif_tagmethods.vgetfield = LZMAVGetField; /* hook for codec tags */ |
|
sp->vsetparent = tif->tif_tagmethods.vsetfield; |
|
tif->tif_tagmethods.vsetfield = LZMAVSetField; /* hook for codec tags */ |
|
|
|
/* Default values for codec-specific fields */ |
|
sp->preset = LZMA_PRESET_DEFAULT; /* default comp. level */ |
|
sp->check = LZMA_CHECK_NONE; |
|
sp->state = 0; |
|
|
|
/* Data filters. So far we are using delta and LZMA2 filters only. */ |
|
sp->opt_delta.type = LZMA_DELTA_TYPE_BYTE; |
|
/* |
|
* The sample size in bytes seems to be reasonable distance for delta |
|
* filter. |
|
*/ |
|
sp->opt_delta.dist = (tif->tif_dir.td_bitspersample % 8) ? |
|
1 : tif->tif_dir.td_bitspersample / 8; |
|
sp->filters[0].id = LZMA_FILTER_DELTA; |
|
sp->filters[0].options = &sp->opt_delta; |
|
|
|
lzma_lzma_preset(&sp->opt_lzma, sp->preset); |
|
sp->filters[1].id = LZMA_FILTER_LZMA2; |
|
sp->filters[1].options = &sp->opt_lzma; |
|
|
|
sp->filters[2].id = LZMA_VLI_UNKNOWN; |
|
sp->filters[2].options = NULL; |
|
|
|
/* |
|
* Install codec methods. |
|
*/ |
|
tif->tif_fixuptags = LZMAFixupTags; |
|
tif->tif_setupdecode = LZMASetupDecode; |
|
tif->tif_predecode = LZMAPreDecode; |
|
tif->tif_decoderow = LZMADecode; |
|
tif->tif_decodestrip = LZMADecode; |
|
tif->tif_decodetile = LZMADecode; |
|
tif->tif_setupencode = LZMASetupEncode; |
|
tif->tif_preencode = LZMAPreEncode; |
|
tif->tif_postencode = LZMAPostEncode; |
|
tif->tif_encoderow = LZMAEncode; |
|
tif->tif_encodestrip = LZMAEncode; |
|
tif->tif_encodetile = LZMAEncode; |
|
tif->tif_cleanup = LZMACleanup; |
|
/* |
|
* Setup predictor setup. |
|
*/ |
|
(void) TIFFPredictorInit(tif); |
|
return 1; |
|
bad: |
|
TIFFErrorExt(tif->tif_clientdata, module, |
|
"No space for LZMA2 state block"); |
|
return 0; |
|
} |
|
#endif /* LZMA_SUPORT */ |
|
|
|
/* vim: set ts=8 sts=8 sw=8 noet: */
|
|
|