123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /*
- * 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 "lzma.h"
- #include "tif_predict.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_t *bp, tmsize_t cc, uint16_t s);
- static int LZMADecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t 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 "unidentified 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_t 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)
- {
- TIFFErrorExtR(tif, 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)
- {
- TIFFErrorExtR(tif, module, "Error initializing the stream decoder, %s",
- LZMAStrerror(ret));
- return 0;
- }
- return 1;
- }
- static int LZMADecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t 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)
- {
- TIFFErrorExtR(tif, 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)
- {
- TIFFErrorExtR(tif, 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)
- {
- TIFFErrorExtR(tif, module,
- "Decoding error at scanline %" PRIu32 ", %s",
- tif->tif_row, LZMAStrerror(ret));
- break;
- }
- } while (sp->stream.avail_out > 0);
- if (sp->stream.avail_out != 0)
- {
- TIFFErrorExtR(tif, module,
- "Not enough data at scanline %" PRIu32
- " (short %" TIFF_SIZE_FORMAT " bytes)",
- tif->tif_row, sp->stream.avail_out);
- return 0;
- }
- tif->tif_rawcp = (uint8_t *)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_t s)
- {
- static const char module[] = "LZMAPreEncode";
- LZMAState *sp = EncoderState(tif);
- lzma_ret ret;
- (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)
- {
- TIFFErrorExtR(tif, module,
- "Liblzma cannot deal with buffers this size");
- return 0;
- }
- ret = lzma_stream_encoder(&sp->stream, sp->filters, sp->check);
- if (ret != LZMA_OK)
- {
- TIFFErrorExtR(tif, module, "Error in lzma_stream_encoder(): %s",
- LZMAStrerror(ret));
- return 0;
- }
- return 1;
- }
- /*
- * Encode a chunk of pixels.
- */
- static int LZMAEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t 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)
- {
- TIFFErrorExtR(tif, module,
- "Liblzma cannot deal with buffers this size");
- return 0;
- }
- do
- {
- lzma_ret ret = lzma_code(&sp->stream, LZMA_RUN);
- if (ret != LZMA_OK)
- {
- TIFFErrorExtR(tif, module,
- "Encoding error at scanline %" PRIu32 ", %s",
- tif->tif_row, LZMAStrerror(ret));
- return 0;
- }
- if (sp->stream.avail_out == 0)
- {
- tif->tif_rawcc = tif->tif_rawdatasize;
- if (!TIFFFlushData1(tif))
- return 0;
- 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;
- if (!TIFFFlushData1(tif))
- return 0;
- 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:
- TIFFErrorExtR(tif, 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;
- }
- _TIFFfreeExt(tif, sp);
- tif->tif_data = NULL;
- _TIFFSetDefaultCompressionState(tif);
- }
- static int LZMAVSetField(TIFF *tif, uint32_t 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)
- {
- TIFFErrorExtR(tif, module, "Liblzma error: %s",
- LZMAStrerror(ret));
- }
- }
- return 1;
- default:
- return (*sp->vsetparent)(tif, tag, ap);
- }
- /*NOTREACHED*/
- }
- static int LZMAVGetField(TIFF *tif, uint32_t 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;
- (void)scheme;
- assert(scheme == COMPRESSION_LZMA);
- /*
- * Merge codec-specific tag information.
- */
- if (!_TIFFMergeFields(tif, lzmaFields, TIFFArrayCount(lzmaFields)))
- {
- TIFFErrorExtR(tif, module, "Merging LZMA2 codec-specific tags failed");
- return 0;
- }
- /*
- * Allocate state block so tag methods have storage to record values.
- */
- tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, 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:
- TIFFErrorExtR(tif, module, "No space for LZMA2 state block");
- return 0;
- }
- #endif /* LZMA_SUPPORT */
|