mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-29 03:23:48 +02:00
1078 lines
31 KiB
C
1078 lines
31 KiB
C
/*
|
|
* Decompressor.c
|
|
*
|
|
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
#include "Decompressor.h"
|
|
#include "LZMA.h"
|
|
#include "../ClangAnalyser.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
|
|
//
|
|
// Constructor and destructor.
|
|
//
|
|
|
|
WinZipJPEGDecompressor *AllocWinZipJPEGDecompressor(WinZipJPEGReadFunction *readfunc,void *inputcontext)
|
|
{
|
|
WinZipJPEGDecompressor *self=calloc(sizeof(WinZipJPEGDecompressor),1);
|
|
if(!self) return NULL;
|
|
|
|
self->readfunc=readfunc;
|
|
self->inputcontext=inputcontext;
|
|
|
|
self->metadatalength=0;
|
|
self->metadatabytes=NULL;
|
|
|
|
self->isfirstbundle=true;
|
|
self->reachedend=false;
|
|
|
|
self->slicesavailable=false;
|
|
|
|
memset(self->blocks,0,sizeof(self->blocks));
|
|
|
|
self->mcusavailable=false;
|
|
|
|
InitializeFixedWinZipJPEGContext(&self->fixedcontext);
|
|
|
|
return self;
|
|
}
|
|
|
|
void FreeWinZipJPEGDecompressor(WinZipJPEGDecompressor *self)
|
|
{
|
|
if(!self) return;
|
|
|
|
free(self->metadatabytes);
|
|
for(int i=0;i<4;i++) free(self->blocks[i]);
|
|
free(self);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Header and bundle reading.
|
|
//
|
|
|
|
// Little endian integer parsing functions.
|
|
static inline uint16_t LittleEndianUInt16(uint8_t *ptr) { return ptr[0]|(ptr[1]<<8); }
|
|
static inline uint32_t LittleEndianUInt32(uint8_t *ptr) { return ptr[0]|(ptr[1]<<8)|(ptr[2]<<16)|(ptr[3]<<24); }
|
|
|
|
// Allocator functions for LZMA.
|
|
static void *Alloc(void *p,size_t size) { return malloc(size); }
|
|
static void Free(void *p,void *address) { return free(address); }
|
|
static ISzAlloc lzmaallocator={Alloc,Free};
|
|
|
|
// Helper functions for reading from the input stream.
|
|
static int FullRead(WinZipJPEGDecompressor *self,uint8_t *buffer,size_t length);
|
|
static int SkipBytes(WinZipJPEGDecompressor *self,size_t length);
|
|
|
|
int ReadWinZipJPEGHeader(WinZipJPEGDecompressor *self)
|
|
{
|
|
// Read 4-byte header.
|
|
uint8_t header[4];
|
|
int error=FullRead(self,header,sizeof(header));
|
|
if(error) return error;
|
|
|
|
// Sanity check the header, and make sure it contains only versions we can handle.
|
|
if(header[0]<4) return WinZipJPEGInvalidHeaderError;
|
|
if(header[1]!=0x10) return WinZipJPEGInvalidHeaderError;
|
|
if(header[2]!=0x01) return WinZipJPEGInvalidHeaderError;
|
|
if(header[3]&0xe0) return WinZipJPEGInvalidHeaderError;
|
|
|
|
// The header can possibly be bigger than 4 bytes, so skip the rest.
|
|
// (Unlikely to happen).
|
|
if(header[0]>4)
|
|
{
|
|
int error=SkipBytes(self,header[0]-4);
|
|
if(error) return error;
|
|
}
|
|
|
|
// Parse slice value.
|
|
self->slicevalue=header[3]&0x1f;
|
|
|
|
return WinZipJPEGNoError;
|
|
}
|
|
|
|
int ReadNextWinZipJPEGBundle(WinZipJPEGDecompressor *self)
|
|
{
|
|
// Free and clear any old metadata.
|
|
free(self->metadatabytes);
|
|
self->metadatalength=0;
|
|
self->metadatabytes=NULL;
|
|
|
|
// Free and clear old slices.
|
|
for(int i=0;i<4;i++) free(self->blocks[i]);
|
|
memset(self->blocks,0,sizeof(self->blocks));
|
|
|
|
// Read bundle header.
|
|
uint8_t header[4];
|
|
int error=FullRead(self,header,sizeof(header));
|
|
if(error) return error;
|
|
|
|
// Parse metadata sizes from header.
|
|
uint32_t uncompressedsize=LittleEndianUInt16(&header[0]);
|
|
uint32_t compressedsize=LittleEndianUInt16(&header[2]);
|
|
|
|
// If the sizes do not fit in 16 bits, both are set to 0xffff and
|
|
// an 8-byte 32-bit header is appended.
|
|
if(uncompressedsize==0xffff && compressedsize==0xffff)
|
|
{
|
|
uint8_t header[8];
|
|
int error=FullRead(self,header,sizeof(header));
|
|
if(error) return error;
|
|
|
|
uncompressedsize=LittleEndianUInt32(&header[0]);
|
|
compressedsize=LittleEndianUInt32(&header[4]);
|
|
}
|
|
|
|
// Allocate space for the uncompressed metadata.
|
|
self->metadatabytes=malloc(uncompressedsize);
|
|
if(!self->metadatabytes) return WinZipJPEGOutOfMemoryError;
|
|
self->metadatalength=uncompressedsize;
|
|
|
|
// NOTE: The spec does not mention this, but a compressed
|
|
// size of 0 means uncompressed data is stored.
|
|
if(compressedsize)
|
|
{
|
|
// Allocate temporary space for the compressed metadata, and read it.
|
|
uint8_t *compressedbytes=malloc(compressedsize);
|
|
if(!compressedbytes) return WinZipJPEGOutOfMemoryError;
|
|
|
|
error=FullRead(self,compressedbytes,compressedsize);
|
|
if(error) { free(compressedbytes); return error; }
|
|
|
|
// Calculate the dictionary size used for the LZMA coding.
|
|
int dictionarysize=(uncompressedsize+511)&~511;
|
|
if(dictionarysize<1024) dictionarysize=1024; // Silly - LZMA enforces a lower limit of 4096.
|
|
if(dictionarysize>512*1024) dictionarysize=512*1024;
|
|
|
|
// Create properties chunk for LZMA, using the dictionary size and default settings (lc=3, lp=0, pb=2).
|
|
uint8_t properties[5]={3+0*9+2*5*9,dictionarysize,dictionarysize>>8,dictionarysize>>16,dictionarysize>>24};
|
|
|
|
// Run LZMA decompressor.
|
|
SizeT destlen=uncompressedsize,srclen=compressedsize;
|
|
ELzmaStatus status;
|
|
SRes res=LzmaDecode(self->metadatabytes,&destlen,compressedbytes,&srclen,
|
|
properties,sizeof(properties),LZMA_FINISH_END,&status,&lzmaallocator);
|
|
|
|
// Free temporary buffer.
|
|
free(compressedbytes);
|
|
|
|
// Check if LZMA decoding succeeded.
|
|
if(res!=SZ_OK) return WinZipJPEGLZMAError;
|
|
}
|
|
else
|
|
{
|
|
// Read uncompressed metadata.
|
|
error=FullRead(self,self->metadatabytes,uncompressedsize);
|
|
if(error) return error;
|
|
}
|
|
|
|
// Parse the JPEG structure. If this is the first bundle,
|
|
// we have to first find the start marker.
|
|
const uint8_t *metadatastart;
|
|
if(self->isfirstbundle)
|
|
{
|
|
metadatastart=FindStartOfWinZipJPEGImage(self->metadatabytes,self->metadatalength);
|
|
if(!metadatastart) return WinZipJPEGParseError;
|
|
|
|
self->isfirstbundle=false;
|
|
}
|
|
else
|
|
{
|
|
metadatastart=self->metadatabytes;
|
|
}
|
|
|
|
int parseres=ParseWinZipJPEGMetadata(&self->jpeg,metadatastart,
|
|
self->metadatabytes+self->metadatalength-metadatastart);
|
|
if(parseres==WinZipJPEGMetadataParsingFailed) return WinZipJPEGParseError;
|
|
|
|
// If we encountered an End Of Image marker, there will be
|
|
// no further scans or bundles, so set a flag and return.
|
|
if(parseres==WinZipJPEGMetadataFoundEndOfImage)
|
|
{
|
|
self->reachedend=true;
|
|
return WinZipJPEGNoError;
|
|
}
|
|
|
|
// Initialize arithmetic decoder contexts.
|
|
InitializeWinZipJPEGContexts(&self->eobbins[0][0][0],sizeof(self->eobbins));
|
|
InitializeWinZipJPEGContexts(&self->zerobins[0][0][0][0],sizeof(self->zerobins));
|
|
InitializeWinZipJPEGContexts(&self->pivotbins[0][0][0][0],sizeof(self->pivotbins));
|
|
InitializeWinZipJPEGContexts(&self->acmagnitudebins[0][0][0][0][0],sizeof(self->acmagnitudebins));
|
|
InitializeWinZipJPEGContexts(&self->acremainderbins[0][0][0][0],sizeof(self->acremainderbins));
|
|
InitializeWinZipJPEGContexts(&self->acsignbins[0][0][0][0],sizeof(self->acsignbins));
|
|
InitializeWinZipJPEGContexts(&self->dcmagnitudebins[0][0][0],sizeof(self->dcmagnitudebins));
|
|
InitializeWinZipJPEGContexts(&self->dcremainderbins[0][0][0],sizeof(self->dcremainderbins));
|
|
InitializeWinZipJPEGContexts(&self->dcsignbins[0][0][0][0],sizeof(self->dcsignbins));
|
|
|
|
// Calculate slize size, if any.
|
|
if(self->slicevalue)
|
|
{
|
|
int64_t pow2size=1LL<<(self->slicevalue+6);
|
|
int64_t div1=pow2size/self->jpeg.horizontalmcus;
|
|
if(div1<1) div1=1;
|
|
int64_t div2=(self->jpeg.verticalmcus+div1-1)/div1;
|
|
self->sliceheight=(unsigned int)((self->jpeg.verticalmcus+div2-1)/div2);
|
|
}
|
|
else
|
|
{
|
|
self->sliceheight=self->jpeg.verticalmcus;
|
|
}
|
|
|
|
// Allocate memory for each component in a slice.
|
|
for(int i=0;i<self->jpeg.numscancomponents;i++)
|
|
{
|
|
self->blocks[i]=malloc(self->jpeg.horizontalmcus*self->sliceheight*
|
|
self->jpeg.scancomponents[i].component->horizontalfactor*
|
|
self->jpeg.scancomponents[i].component->verticalfactor*
|
|
sizeof(WinZipJPEGBlock));
|
|
|
|
if(!self->blocks[i]) return WinZipJPEGOutOfMemoryError;
|
|
}
|
|
|
|
self->slicesavailable=true;
|
|
self->finishedrows=0;
|
|
|
|
self->mcucounter=0;
|
|
self->restartmarkerindex=0;
|
|
self->writerestartmarker=false;
|
|
|
|
memset(self->predicted,0,sizeof(self->predicted));
|
|
|
|
self->bitstring=0;
|
|
self->bitlength=0;
|
|
self->needsstuffing=false;
|
|
|
|
return WinZipJPEGNoError;
|
|
}
|
|
|
|
// Helper function that makes sure to read as much data as requested, even
|
|
// if the read function returns short buffers, and reports an error if it
|
|
// reaches EOF prematurely.
|
|
static int FullRead(WinZipJPEGDecompressor *self,uint8_t *buffer,size_t length)
|
|
{
|
|
size_t totalread=0;
|
|
while(totalread<length)
|
|
{
|
|
size_t actual=self->readfunc(self->inputcontext,&buffer[totalread],length-totalread);
|
|
if(actual==0) return WinZipJPEGEndOfStreamError;
|
|
totalread+=actual;
|
|
}
|
|
|
|
return WinZipJPEGNoError;
|
|
}
|
|
|
|
// Helper function to skip data by reading and discarding.
|
|
static int SkipBytes(WinZipJPEGDecompressor *self,size_t length)
|
|
{
|
|
uint8_t buffer[1024];
|
|
|
|
size_t totalread=0;
|
|
while(totalread<length)
|
|
{
|
|
size_t numbytes=length-totalread;
|
|
if(numbytes>sizeof(buffer)) numbytes=sizeof(buffer);
|
|
size_t actual=self->readfunc(self->inputcontext,buffer,numbytes);
|
|
if(actual==0) return WinZipJPEGEndOfStreamError;
|
|
totalread+=actual;
|
|
}
|
|
|
|
return WinZipJPEGNoError;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Block decoding.
|
|
//
|
|
|
|
// Decoder functions.
|
|
static void DecodeBlock(WinZipJPEGDecompressor *self,int comp,
|
|
WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization);
|
|
|
|
static int DecodeACComponent(WinZipJPEGDecompressor *self,int comp,unsigned int k,bool canbezero,
|
|
const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization);
|
|
|
|
static int DecodeACSign(WinZipJPEGDecompressor *self,int comp,unsigned int k,int absvalue,
|
|
const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization);
|
|
|
|
static int DecodeDCComponent(WinZipJPEGDecompressor *self,int comp,
|
|
const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization);
|
|
|
|
static unsigned int DecodeBinarization(WinZipJPEGArithmeticDecoder *decoder,
|
|
WinZipJPEGContext *magnitudebins,WinZipJPEGContext *remainderbins,int maxbits,int cap);
|
|
|
|
// Coefficient zig-zag ordering functions.
|
|
static bool IsFirstRow(unsigned int k);
|
|
static bool IsFirstColumn(unsigned int k);
|
|
static bool IsFirstRowOrColumn(unsigned int k);
|
|
static bool IsSecondRow(unsigned int k);
|
|
static bool IsSecondColumn(unsigned int k);
|
|
|
|
static unsigned int Left(unsigned int k);
|
|
static unsigned int Up(unsigned int k);
|
|
static unsigned int UpAndLeft(unsigned int k);
|
|
static unsigned int Right(unsigned int k);
|
|
static unsigned int Down(unsigned int k);
|
|
|
|
static unsigned int ZigZag(unsigned int row,unsigned int column);
|
|
static unsigned int Row(unsigned int k);
|
|
static unsigned int Column(unsigned int k);
|
|
|
|
// Compression primitive functions.
|
|
static int Min(int a,int b);
|
|
static int Abs(int x);
|
|
static int Sign(int x);
|
|
static unsigned int Category(unsigned int val);
|
|
|
|
static int Sum(unsigned int k,const WinZipJPEGBlock *block);
|
|
static int Average(unsigned int k,
|
|
const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization);
|
|
static int BDR(unsigned int k,const WinZipJPEGBlock *current,
|
|
const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization);
|
|
|
|
static const WinZipJPEGBlock ZeroBlock={0};
|
|
|
|
int ReadNextWinZipJPEGSlice(WinZipJPEGDecompressor *self)
|
|
{
|
|
self->currheight=self->sliceheight;
|
|
if(self->finishedrows+self->currheight>=self->jpeg.verticalmcus)
|
|
{
|
|
self->currheight=self->jpeg.verticalmcus-self->finishedrows;
|
|
self->slicesavailable=false;
|
|
}
|
|
|
|
for(int i=0;i<self->jpeg.numscancomponents;i++)
|
|
{
|
|
InitializeWinZipJPEGArithmeticDecoder(&self->decoder,self->readfunc,self->inputcontext);
|
|
|
|
int hblocks=self->jpeg.scancomponents[i].component->horizontalfactor;
|
|
int vblocks=self->jpeg.scancomponents[i].component->verticalfactor;
|
|
int blocksperrow=self->jpeg.horizontalmcus*hblocks;
|
|
|
|
const WinZipJPEGQuantizationTable *quantization=self->jpeg.scancomponents[i].component->quantizationtable;
|
|
|
|
// NOTE: Blocks are processed in cartesian order, not MCU order.
|
|
for(int y=0;y<self->currheight*vblocks;y++)
|
|
for(int x=0;x<self->jpeg.horizontalmcus*hblocks;x++)
|
|
{
|
|
WinZipJPEGBlock *currblock=&self->blocks[i][x+y*blocksperrow];
|
|
|
|
const WinZipJPEGBlock *northblock;
|
|
if(y!=0) northblock=&self->blocks[i][x+(y-1)*blocksperrow];
|
|
else if(self->finishedrows!=0) northblock=&self->blocks[i][x+(self->sliceheight*vblocks-1)*blocksperrow];
|
|
else northblock=NULL;
|
|
|
|
const WinZipJPEGBlock *westblock;
|
|
if(x!=0) westblock=&self->blocks[i][x-1+y*blocksperrow];
|
|
else westblock=NULL;
|
|
|
|
DecodeBlock(self,i,currblock,northblock,westblock,quantization);
|
|
|
|
if(WinZipJPEGArithmeticDecoderEncounteredEOF(&self->decoder)) return WinZipJPEGEndOfStreamError;
|
|
}
|
|
|
|
FlushWinZipJPEGArithmeticDecoder(&self->decoder);
|
|
}
|
|
|
|
self->finishedrows+=self->currheight;
|
|
|
|
// Initialize writer state.
|
|
self->mcusavailable=true;
|
|
self->currblock=self->blocks[0];
|
|
|
|
self->mcurow=0;
|
|
self->mcucol=0;
|
|
self->mcucomp=0;
|
|
self->mcux=0;
|
|
self->mcuy=0;
|
|
self->mcucoeff=0;
|
|
|
|
// TODO: Error handling.
|
|
return WinZipJPEGNoError;
|
|
}
|
|
|
|
static void DecodeBlock(WinZipJPEGDecompressor *self,int comp,
|
|
WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization)
|
|
{
|
|
// Decode End Of Block value to find out how many AC components there are. (5.6.5)
|
|
|
|
// Calculate EOB context. (5.6.5.2)
|
|
int average;
|
|
if(!north&&!west) average=0;
|
|
else if(!north) average=Sum(0,west);
|
|
else if(!west) average=Sum(0,north);
|
|
else average=(Sum(0,north)+Sum(0,west)+1)/2;
|
|
|
|
int eobcontext=Min(Category(average),12);
|
|
|
|
// Decode EOB bits using binary tree. (5.6.5.1)
|
|
unsigned int bitstring=1;
|
|
for(int i=0;i<6;i++)
|
|
{
|
|
bitstring=(bitstring<<1)|NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,
|
|
&self->eobbins[comp][eobcontext][bitstring-1]);
|
|
}
|
|
unsigned int eob=bitstring&0x3f;
|
|
current->eob=eob;
|
|
|
|
// Fill out the elided block entries with 0.
|
|
for(unsigned int k=eob+1;k<=63;k++) current->c[k]=0;
|
|
|
|
// Decode AC components in decreasing order, if any. (5.6.6)
|
|
for(unsigned int k=eob;k>=1;k--)
|
|
{
|
|
current->c[k]=DecodeACComponent(self,comp,k,k!=eob,current,north,west,quantization);
|
|
}
|
|
|
|
// Decode DC component.
|
|
current->c[0]=DecodeDCComponent(self,comp,current,north,west,quantization);
|
|
}
|
|
|
|
static int DecodeACComponent(WinZipJPEGDecompressor *self,int comp,unsigned int k,bool canbezero,
|
|
const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization)
|
|
{
|
|
if(!north) north=&ZeroBlock;
|
|
if(!west) west=&ZeroBlock;
|
|
|
|
int val1;
|
|
if(IsFirstRowOrColumn(k)) val1=Abs(BDR(k,current,north,west,quantization));
|
|
else val1=Average(k,north,west,quantization);
|
|
|
|
int val2=Sum(k,current);
|
|
|
|
if(canbezero)
|
|
{
|
|
// Decode zero/non-zero bit. (5.6.6.1)
|
|
int zerocontext1=Min(Category(val1),2);
|
|
int zerocontext2=Min(Category(val2),5);
|
|
|
|
int nonzero=NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,
|
|
&self->zerobins[comp][k-1][zerocontext1][zerocontext2]);
|
|
|
|
// If this component is zero, there is no need to decode further parameters.
|
|
if(!nonzero) return 0;
|
|
}
|
|
|
|
// This component is not zero. Proceed with decoding absolute value.
|
|
int absvalue;
|
|
|
|
// Decode pivot (abs>=2). (5.6.6.2)
|
|
int pivotcontext1=Min(Category(val1),4);
|
|
int pivotcontext2=Min(Category(val2),6);
|
|
|
|
int pivot=NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,
|
|
&self->pivotbins[comp][k-1][pivotcontext1][pivotcontext2]);
|
|
|
|
if(!pivot)
|
|
{
|
|
// The absolute of this component is not >=2. It must therefore be 1,
|
|
// and there is no need to decode the value.
|
|
absvalue=1;
|
|
}
|
|
else
|
|
{
|
|
// The absolute of this component is >=2. Proceed with decoding
|
|
// the absolute value. (5.6.6.3)
|
|
int val3,n;
|
|
if(IsFirstRow(k)) { val3=Column(k)-1; n=0; }
|
|
else if(IsFirstColumn(k)) { val3=Row(k)-1; n=1; }
|
|
else { val3=Category(k-4); n=2; }
|
|
|
|
int magnitudecontext1=Min(Category(val1),8);
|
|
int magnitudecontext2=Min(Category(val2),8);
|
|
int remaindercontext=val3;
|
|
|
|
// Decode absolute value.
|
|
absvalue=DecodeBinarization(&self->decoder,
|
|
self->acmagnitudebins[comp][n][magnitudecontext1][magnitudecontext2],
|
|
self->acremainderbins[comp][n][remaindercontext],
|
|
14,9)+2;
|
|
}
|
|
|
|
if(DecodeACSign(self,comp,k,absvalue,current,north,west,quantization)) return -absvalue;
|
|
else return absvalue;
|
|
}
|
|
|
|
static int DecodeACSign(WinZipJPEGDecompressor *self,int comp,unsigned int k,int absvalue,
|
|
const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization)
|
|
{
|
|
// Decode sign. (5.6.6.4)
|
|
|
|
// Calculate sign context, or decode with fixed probability. (5.6.6.4.1)
|
|
int predictedsign;
|
|
if(IsFirstRowOrColumn(k))
|
|
{
|
|
int bdr=BDR(k,current,north,west,quantization);
|
|
|
|
if(bdr==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext);
|
|
|
|
predictedsign=(bdr<0);
|
|
}
|
|
else if(k==4)
|
|
{
|
|
int sign1=Sign(north->c[k]);
|
|
int sign2=Sign(west->c[k]);
|
|
|
|
if(sign1+sign2==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext);
|
|
|
|
predictedsign=(sign1+sign2<0);
|
|
}
|
|
else if(IsSecondRow(k))
|
|
{
|
|
if(north->c[k]==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext);
|
|
|
|
predictedsign=(north->c[k]<0);
|
|
}
|
|
else if(IsSecondColumn(k))
|
|
{
|
|
if(west->c[k]==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext);
|
|
|
|
predictedsign=(west->c[k]<0);
|
|
}
|
|
else
|
|
{
|
|
return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext);
|
|
}
|
|
|
|
static const int n_for_k[64]={
|
|
0,
|
|
0, 1,
|
|
2, 3, 4,
|
|
5, 6, 7, 8,
|
|
9,10, 0,11,12,
|
|
13,14, 0, 0,15,16,
|
|
17,18, 0, 0, 0,19,20,
|
|
21,22, 0, 0, 0, 0,23,24,
|
|
25, 0, 0, 0, 0, 0,26,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0,
|
|
0, 0,
|
|
0,
|
|
};
|
|
int n=n_for_k[k];
|
|
|
|
int signcontext1=Min(Category(absvalue)/2,2);
|
|
|
|
return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,
|
|
&self->acsignbins[comp][n][signcontext1][predictedsign]);
|
|
}
|
|
|
|
static int DecodeDCComponent(WinZipJPEGDecompressor *self,int comp,
|
|
const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization)
|
|
{
|
|
// Decode DC component. (5.6.7)
|
|
|
|
// DC prediction. (5.6.7.1)
|
|
int predicted;
|
|
if(!north&&!west)
|
|
{
|
|
predicted=0;
|
|
}
|
|
else if(!north)
|
|
{
|
|
// NOTE: Spec says west[1]-current[1].
|
|
int t1=west->c[0]*10000-11038*quantization->c[1]*(west->c[1]+current->c[1])/quantization->c[0];
|
|
int p1=((t1<0)?(t1-5000):(t1+5000))/10000;
|
|
predicted=p1;
|
|
}
|
|
else if(!west)
|
|
{
|
|
// NOTE: Spec says north->c[2]-current->c[2].
|
|
int t0=north->c[0]*10000-11038*quantization->c[2]*(north->c[2]+current->c[2])/quantization->c[0];
|
|
int p0=((t0<0)?(t0-5000):(t0+5000))/10000;
|
|
predicted=p0;
|
|
}
|
|
else
|
|
{
|
|
// NOTE: Spec says north[2]-current[2] and west[1]-current[1].
|
|
int t0=north->c[0]*10000-11038*quantization->c[2]*(north->c[2]+current->c[2])/quantization->c[0];
|
|
int p0=((t0<0)?(t0-5000):(t0+5000))/10000;
|
|
|
|
int t1=west->c[0]*10000-11038*quantization->c[1]*(west->c[1]+current->c[1])/quantization->c[0];
|
|
int p1=((t1<0)?(t1-5000):(t1+5000))/10000;
|
|
|
|
// Prediction refinement. (5.6.7.2)
|
|
int d0=0,d1=0;
|
|
for(int i=1;i<8;i++)
|
|
{
|
|
// Note: Spec says Abs(Abs(north->c[ZigZag(i,0)])-
|
|
// Abs(current->c[ZigZag(i,0)])) and similarly for west.
|
|
d0+=Abs(north->c[ZigZag(i,0)]-current->c[ZigZag(i,0)]);
|
|
d1+=Abs(west->c[ZigZag(0,i)]-current->c[ZigZag(0,i)]);
|
|
}
|
|
|
|
if(d0>d1)
|
|
{
|
|
int64_t weight=1LL<<Min(d0-d1,31);
|
|
predicted=(int)((weight*(int64_t)p1+(int64_t)p0)/(1+weight));
|
|
}
|
|
else
|
|
{
|
|
int64_t weight=1LL<<Min(d1-d0,31);
|
|
predicted=(int)((weight*(int64_t)p0+(int64_t)p1)/(1+weight));
|
|
}
|
|
}
|
|
|
|
// Decode DC residual. (5.6.7.3)
|
|
|
|
// Decode absolute value. (5.6.7.3.1)
|
|
int absvalue;
|
|
int sum=Sum(0,current);
|
|
int valuecontext=Min(Category(sum),12);
|
|
|
|
absvalue=DecodeBinarization(&self->decoder,
|
|
self->dcmagnitudebins[comp][valuecontext],
|
|
self->dcremainderbins[comp][valuecontext],
|
|
15,10);
|
|
if(absvalue==0) return predicted;
|
|
|
|
// Decode sign. (5.6.7.3.2)
|
|
// NOTE: Spec says north[0]<0 and west[0]<0.
|
|
if(!north) north=&ZeroBlock;
|
|
if(!west) west=&ZeroBlock;
|
|
int northsign=(north->c[0]<predicted);
|
|
int westsign=(west->c[0]<predicted);
|
|
int predictedsign=(predicted<0);
|
|
|
|
int sign=NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,
|
|
&self->dcsignbins[comp][northsign][westsign][predictedsign]);
|
|
|
|
if(sign) return predicted-absvalue;
|
|
else return predicted+absvalue;
|
|
}
|
|
|
|
static unsigned int DecodeBinarization(WinZipJPEGArithmeticDecoder *decoder,
|
|
WinZipJPEGContext *magnitudebins,WinZipJPEGContext *remainderbins,int maxbits,int cap)
|
|
{
|
|
// Decode binarization. (5.6.4, and additional reverse engineering
|
|
// as the spec does not describe the process in sufficient detail.)
|
|
|
|
// Decode unary magnitude.
|
|
int ones=0;
|
|
while(ones<maxbits)
|
|
{
|
|
int context=ones;
|
|
if(context>=cap) context=cap-1;
|
|
|
|
int unary=NextBitFromWinZipJPEGArithmeticDecoder(decoder,&magnitudebins[context]);
|
|
if(unary==1) ones++;
|
|
else break;
|
|
}
|
|
|
|
// Decode remainder bits, if any.
|
|
if(ones==0) return 0;
|
|
else if(ones==1) return 1;
|
|
else
|
|
{
|
|
int numbits=ones-1;
|
|
int val=1<<numbits;
|
|
|
|
for(int i=numbits-1;i>=0;i--)
|
|
{
|
|
int bit=NextBitFromWinZipJPEGArithmeticDecoder(decoder,&remainderbins[i]);
|
|
val|=bit<<i;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
}
|
|
|
|
static bool IsFirstRow(unsigned int k) { return Row(k)==0; }
|
|
static bool IsFirstColumn(unsigned int k) { return Column(k)==0; }
|
|
static bool IsFirstRowOrColumn(unsigned int k) { return IsFirstRow(k)||IsFirstColumn(k); }
|
|
static bool IsSecondRow(unsigned int k) { return Row(k)==1; }
|
|
static bool IsSecondColumn(unsigned int k) { return Column(k)==1; }
|
|
|
|
static unsigned int Left(unsigned int k) { return ZigZag(Row(k),Column(k)-1); }
|
|
static unsigned int Up(unsigned int k) { return ZigZag(Row(k)-1,Column(k)); }
|
|
static unsigned int UpAndLeft(unsigned int k) { return ZigZag(Row(k)-1,Column(k)-1); }
|
|
static unsigned int Right(unsigned int k) { return ZigZag(Row(k),Column(k)+1); }
|
|
static unsigned int Down(unsigned int k) { return ZigZag(Row(k)+1,Column(k)); }
|
|
|
|
static unsigned int ZigZag(unsigned int row,unsigned int column)
|
|
{
|
|
if(row>=8||column>=8) return 0; // Can't happen.
|
|
static const int table[8][8]=
|
|
{
|
|
{ 0, 1, 5, 6,14,15,27,28, },
|
|
{ 2, 4, 7,13,16,26,29,42, },
|
|
{ 3, 8,12,17,25,30,41,43, },
|
|
{ 9,11,18,24,31,40,44,53, },
|
|
{ 10,19,23,32,39,45,52,54, },
|
|
{ 20,22,33,38,46,51,55,60, },
|
|
{ 21,34,37,47,50,56,59,61, },
|
|
{ 35,36,48,49,57,58,62,63, },
|
|
};
|
|
return table[row][column];
|
|
}
|
|
|
|
static unsigned int Row(unsigned int k)
|
|
{
|
|
if(k>=64) return 0; // Can't happen.
|
|
static const int table[64]=
|
|
{
|
|
0,0,1,2,1,0,0,1,2,3,4,3,2,1,0,0,
|
|
1,2,3,4,5,6,5,4,3,2,1,0,0,1,2,3,
|
|
4,5,6,7,7,6,5,4,3,2,1,2,3,4,5,6,
|
|
7,7,6,5,4,3,4,5,6,7,7,6,5,6,7,7,
|
|
};
|
|
return table[k];
|
|
}
|
|
|
|
static unsigned int Column(unsigned int k)
|
|
{
|
|
if(k>=64) return 0; // Can't happen.
|
|
static const int table[64]=
|
|
{
|
|
0,1,0,0,1,2,3,2,1,0,0,1,2,3,4,5,
|
|
4,3,2,1,0,0,1,2,3,4,5,6,7,6,5,4,
|
|
3,2,1,0,1,2,3,4,5,6,7,7,6,5,4,3,
|
|
2,3,4,5,6,7,7,6,5,4,5,6,7,7,6,7,
|
|
};
|
|
return table[k];
|
|
}
|
|
|
|
static int Min(int a,int b)
|
|
{
|
|
if(a<b) return a;
|
|
else return b;
|
|
}
|
|
|
|
static int Abs(int x)
|
|
{
|
|
if(x>=0) return x;
|
|
else return -x;
|
|
}
|
|
|
|
static int Sign(int x)
|
|
{
|
|
if(x>0) return 1;
|
|
else if(x<0) return -1;
|
|
else return 0;
|
|
}
|
|
|
|
// CAT (5.6.3)
|
|
static unsigned int Category(unsigned int val)
|
|
{
|
|
if(val==0) return 0;
|
|
|
|
unsigned int cat=0;
|
|
if(val&0xffff0000) { val>>=16; cat|=16; }
|
|
if(val&0xff00) { val>>=8; cat|=8; }
|
|
if(val&0xf0) { val>>=4; cat|=4; }
|
|
if(val&0xc) { val>>=2; cat|=2; }
|
|
if(val&0x2) { /*val>>=1;*/ cat|=1; }
|
|
return cat+1;
|
|
}
|
|
|
|
// SUM (5.6.2.1)
|
|
static int Sum(unsigned int k,const WinZipJPEGBlock *block)
|
|
{
|
|
int sum=0;
|
|
for(unsigned int i=0;i<64;i++)
|
|
{
|
|
if(i!=k && Row(i)>=Row(k) && Column(i)>=Column(k))
|
|
sum+=Abs(block->c[i]);
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
// AVG (5.6.2.2)
|
|
// NOTE: This assumes that the expression given for 'sum' is incorrect, and that
|
|
// Bw[k] should actually be Bw[x]. Also, the spec does not explicitly mention
|
|
// that the DC component never contributes.
|
|
static int Average(unsigned int k,
|
|
const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization)
|
|
{
|
|
if(k==0||k==1||k==2)
|
|
{
|
|
return (Abs(north->c[k])+Abs(west->c[k])+1)/2;
|
|
}
|
|
else if(IsFirstRow(k))
|
|
{
|
|
return (
|
|
(Abs(north->c[Left(k)])+Abs(west->c[Left(k)]))*quantization->c[Left(k)]/quantization->c[k]+
|
|
Abs(north->c[k])+Abs(west->c[k])+
|
|
2
|
|
)/(2*2);
|
|
}
|
|
else if(IsFirstColumn(k))
|
|
{
|
|
return (
|
|
(Abs(north->c[Up(k)])+Abs(west->c[Up(k)]))*quantization->c[Up(k)]/quantization->c[k]+
|
|
Abs(north->c[k])+Abs(west->c[k])+
|
|
2
|
|
)/(2*2);
|
|
}
|
|
else if(k==4)
|
|
{
|
|
return (
|
|
(Abs(north->c[Up(k)])+Abs(west->c[Up(k)]))*quantization->c[Up(k)]/quantization->c[k]+
|
|
(Abs(north->c[Left(k)])+Abs(west->c[Left(k)]))*quantization->c[Left(k)]/quantization->c[k]+
|
|
Abs(north->c[k])+Abs(west->c[k])+
|
|
3
|
|
)/(2*3);
|
|
}
|
|
else
|
|
{
|
|
return (
|
|
(Abs(north->c[Up(k)])+Abs(west->c[Up(k)]))*quantization->c[Up(k)]/quantization->c[k]+
|
|
(Abs(north->c[Left(k)])+Abs(west->c[Left(k)]))*quantization->c[Left(k)]/quantization->c[k]+
|
|
(Abs(north->c[UpAndLeft(k)])+Abs(west->c[UpAndLeft(k)]))*quantization->c[UpAndLeft(k)]/quantization->c[k]+
|
|
Abs(north->c[k])+Abs(west->c[k])+
|
|
4
|
|
)/(2*4);
|
|
}
|
|
}
|
|
|
|
// BDR (5.6.2.3)
|
|
static int BDR(unsigned int k,const WinZipJPEGBlock *current,
|
|
const WinZipJPEGBlock *north,const WinZipJPEGBlock *west,
|
|
const WinZipJPEGQuantizationTable *quantization)
|
|
{
|
|
if(IsFirstRow(k))
|
|
{
|
|
return north->c[k]-(north->c[Down(k)]+current->c[Down(k)])*quantization->c[Down(k)]/quantization->c[k];
|
|
}
|
|
else if(IsFirstColumn(k))
|
|
{
|
|
return west->c[k]-(west->c[Right(k)]+current->c[Right(k)])*quantization->c[Right(k)]/quantization->c[k];
|
|
}
|
|
else return 0; // Can't happen.
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Block encoding.
|
|
//
|
|
|
|
static void PushEncodedValue(WinZipJPEGDecompressor *self,WinZipJPEGHuffmanTable *table,
|
|
int value,unsigned int highbits);
|
|
static void PushHuffmanCode(WinZipJPEGDecompressor *self,WinZipJPEGHuffmanTable *table,unsigned int code);
|
|
static void PushBitString(WinZipJPEGDecompressor *self,uint32_t bitstring,unsigned int length);
|
|
|
|
size_t EncodeWinZipJPEGBlocksToBuffer(WinZipJPEGDecompressor *self,void *bytes,size_t length)
|
|
{
|
|
uint8_t *start=bytes;
|
|
uint8_t *ptr=bytes;
|
|
uint8_t *end=ptr+length;
|
|
|
|
while(ptr<end)
|
|
{
|
|
if(self->needsstuffing)
|
|
{
|
|
// If we need to add a byte of stuffing, do so.
|
|
*ptr++=0x00;
|
|
self->needsstuffing=false;
|
|
}
|
|
else if(self->bitlength>=8)
|
|
{
|
|
// If there are enough buffered bits, output one byte.
|
|
uint8_t byte=self->bitstring>>56LL;
|
|
*ptr++=byte;
|
|
self->bitstring<<=8;
|
|
self->bitlength-=8;
|
|
|
|
if(byte==0xff) self->needsstuffing=true;
|
|
}
|
|
else if(self->writerestartmarker)
|
|
{
|
|
// Output the first half of a restart marker. This has to be done
|
|
// separately, to avoid stuffing.
|
|
*ptr++=0xff;
|
|
|
|
// Next, push the rest of the marker into the bitstream as usual, as it
|
|
// will not trigger bit stuffing.
|
|
PushBitString(self,0xd0+self->restartmarkerindex,8);
|
|
|
|
// Cycle the restart marker indexes, reset the MCU counter, and clear predictors.
|
|
self->restartmarkerindex=(self->restartmarkerindex+1)&7;
|
|
self->mcucounter=0;
|
|
memset(self->predicted,0,sizeof(self->predicted));
|
|
|
|
self->writerestartmarker=false;
|
|
}
|
|
else if(self->jpeg.restartinterval && self->mcucounter==self->jpeg.restartinterval &&
|
|
(self->mcusavailable || self->slicesavailable))
|
|
{
|
|
// If it is time for a restart marker, and if we have not reached the very
|
|
// end of the scan, start outputting one.
|
|
|
|
// First, pad with ones to a byte border if needed.
|
|
if(self->bitlength)
|
|
{
|
|
int n=8-self->bitlength;
|
|
PushBitString(self,(1<<n)-1,n);
|
|
}
|
|
|
|
// Then set a flag to start writing a restart marker. This has to be done
|
|
// separately, to avoid bit stuffing.
|
|
self->writerestartmarker=true;
|
|
}
|
|
else if(self->mcusavailable)
|
|
{
|
|
// If there are still MCUs left to process output either a DC or AC coefficient as appropriate.
|
|
if(self->mcucoeff==0)
|
|
{
|
|
// Output DC coefficient.
|
|
int diff=self->currblock->c[0]-self->predicted[self->mcucomp];
|
|
|
|
PushEncodedValue(self,self->jpeg.scancomponents[self->mcucomp].dctable,diff,0);
|
|
|
|
self->predicted[self->mcucomp]=self->currblock->c[0];
|
|
self->mcucoeff=1;
|
|
}
|
|
else if(self->mcucoeff>self->currblock->eob && self->currblock->eob!=63)
|
|
{
|
|
// Output EOB marker.
|
|
PushHuffmanCode(self,self->jpeg.scancomponents[self->mcucomp].actable,0x00);
|
|
|
|
self->mcucoeff=64;
|
|
}
|
|
else
|
|
{
|
|
// Output AC coefficient.
|
|
|
|
// Find the next non-zero coefficient.
|
|
int firstcoeff=self->mcucoeff;
|
|
int endrun=self->mcucoeff+15;
|
|
while(self->mcucoeff<63 && self->mcucoeff<endrun &&
|
|
self->currblock->c[self->mcucoeff]==0) self->mcucoeff++;
|
|
|
|
int zeroes=self->mcucoeff-firstcoeff;
|
|
int val=self->currblock->c[self->mcucoeff];
|
|
|
|
PushEncodedValue(self,self->jpeg.scancomponents[self->mcucomp].actable,val,zeroes);
|
|
|
|
self->mcucoeff++;
|
|
}
|
|
|
|
// If we have output all coefficients, update position.
|
|
if(self->mcucoeff>=64)
|
|
{
|
|
int hblocks=self->jpeg.scancomponents[self->mcucomp].component->horizontalfactor;
|
|
int vblocks=self->jpeg.scancomponents[self->mcucomp].component->verticalfactor;
|
|
|
|
self->mcucoeff=0; self->mcux++;
|
|
if(self->mcux>=hblocks)
|
|
{
|
|
self->mcux=0; self->mcuy++;
|
|
if(self->mcuy>=vblocks)
|
|
{
|
|
self->mcuy=0; self->mcucomp++;
|
|
if(self->mcucomp>=self->jpeg.numscancomponents)
|
|
{
|
|
self->mcucomp=0; self->mcucol++;
|
|
if(self->mcucol>=self->jpeg.horizontalmcus)
|
|
{
|
|
self->mcucol=0; self->mcurow++;
|
|
if(self->mcurow>=self->currheight)
|
|
{
|
|
self->mcusavailable=false;
|
|
|
|
if(!self->slicesavailable)
|
|
{
|
|
// If we reached the very end, pad with ones
|
|
// to a byte boundary to finish the stream.
|
|
int n=(-self->bitlength)&7;
|
|
PushBitString(self,(1<<n)-1,n);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count up towards the restart interval.
|
|
self->mcucounter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the new block.
|
|
hblocks=self->jpeg.scancomponents[self->mcucomp].component->horizontalfactor;
|
|
vblocks=self->jpeg.scancomponents[self->mcucomp].component->verticalfactor;
|
|
int blocksperrow=self->jpeg.horizontalmcus*hblocks;
|
|
|
|
int blockx=self->mcucol*hblocks+self->mcux;
|
|
int blocky=self->mcurow*vblocks+self->mcuy;
|
|
|
|
self->currblock=&self->blocks[self->mcucomp][blockx+blocky*blocksperrow];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Nothing left to do. Return the partial length of output data.
|
|
return ptr-start;
|
|
}
|
|
}
|
|
return length;
|
|
}
|
|
|
|
static void PushEncodedValue(WinZipJPEGDecompressor *self,WinZipJPEGHuffmanTable *table,
|
|
int value,unsigned int highbits)
|
|
{
|
|
unsigned int category,bitstring;
|
|
if(value>=0)
|
|
{
|
|
category=Category(value);
|
|
analyser_assert(category>=1 && category<=32);
|
|
unsigned int mask=(1ull<<category)-1;
|
|
bitstring=value&mask;
|
|
}
|
|
else
|
|
{
|
|
category=Category(-value);
|
|
analyser_assert(category>=1 && category<=32);
|
|
unsigned int mask=(1ull<<category)-1;
|
|
bitstring=(value&mask)-1;
|
|
}
|
|
|
|
PushHuffmanCode(self,table,category|(highbits<<4));
|
|
PushBitString(self,bitstring,category);
|
|
}
|
|
|
|
|
|
static void PushHuffmanCode(WinZipJPEGDecompressor *self,WinZipJPEGHuffmanTable *table,unsigned int code)
|
|
{
|
|
PushBitString(self,table->codes[code].code,table->codes[code].length);
|
|
}
|
|
|
|
static void PushBitString(WinZipJPEGDecompressor *self,uint32_t bitstring,unsigned int length)
|
|
{
|
|
self->bitstring|=(uint64_t)bitstring<<(64-self->bitlength-length);
|
|
self->bitlength+=length;
|
|
}
|