diff -r 630d2f34d719 -r 07a122eea281 fax/faxclientandserver/faxio/FAXIO.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fax/faxclientandserver/faxio/FAXIO.CPP Wed Sep 01 12:40:21 2010 +0100 @@ -0,0 +1,763 @@ +// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include +#include "CFAXIO.H" + +#include "FAXSTPAN.H" +#include "FAXUHUFF.H" +#include "FAXHUFF.H" + +// COPIED function from pdrutil + +GLDEF_C void Panic (TFaxStorePanic aPanic) +// Panic the process with ETEXT as the category. + // + +{ + User::Panic (_L ("FaxStore"), aPanic); +} + +// END OF COPIED + +//#define KFaxFileName _L("c:\\temp.fax") + +/********************************************************************/ + +#define RLE_MAKEUP(aTable,aRun) TUint8((aTable)+KRleMakeup+((aRun)>>6<<1)) +#define RLE_RUN(aTable,aRun) TUint8((aTable)+((aRun)<<1)) + +#define WRITE_RLE(ptr,table,run) {if (run>63) {*ptr++=RLE_MAKEUP(table,run);run&=0x3f;} *ptr++=RLE_RUN(table,run);} +#define READ_RLE(ptr,pos) {TInt x=*ptr++;if (x>=KRleMakeup) {pos+=(x&KRleMakeupMask)<<5;x=*ptr++;}pos+=x>>1;} + +LOCAL_C TUint8* RleEncoding(const TUint32 * aScan, TUint8* aRleEncoding) +// +// RLE encode a 1728 pixel scanline into the buffer, and return the end-of-rle data +// The edge detection algorithm is almost optimal for ARM +// + { + // the edge detection looks at multiple pixels at a time + // tests show that for ARM, testing 8 is only 1-2% faster than testing 6 + // testing only 6 makes for a smaller lookup table + + // The FirstBit table is (5 - bitpos) of the least significant + // bit that is set in the array index value + + const TInt KTestBitCount=6; + const TInt KTestBitMask=(1u<> 5); + TUint32 color = ~0u; // white at start + TInt table=KRleWhite; + TInt run = KRunBias; // initialise run length + const TUint8* lookup=KFirstBit; // force the table to be in a register + + nextword: + while (aScan < end) + { + run += 32; + TUint32 pixels = *aScan++ ^ color; + if (pixels) // do no work if there is no edge + { + TInt bit = 31 + KTestBitCount; + for (;;) + { + TUint pix; + do + { + if ((bit-=KTestBitCount) < 0) + goto nextword; // finished processing the word + // now examine the next 6 pixels + // break out if we have found an edge + pix=(pixels>>(31-bit))&KTestBitMask; + } while (pix==0); + // there is an edge, use the table to discover which pixel + bit+=lookup[pix]; + // store the run-length + run-=bit; + WRITE_RLE(aRleEncoding,table,run); + // flip color and look for the next edge + color = ~color; + table=KRleWhite-table; + pixels=~pixels; + run = bit; + } + } + } + // store the final run + run-=KRunBias; + WRITE_RLE(aRleEncoding,table,run); + return aRleEncoding; + } + +LOCAL_C TUint8* RleEncoding (const TUint32 *aScanline,TInt aLength,TUint8* aRleEncoding) +// +// Justify the scanline into a full size buffer before encoding +// + { + __ASSERT_DEBUG(aLength < (KFaxPixelsPerScanLine >> 3),User::Invariant()); +// + TUint32 justified[KFaxPixelsPerScanLine/32]; +// + TInt margin = ((KFaxPixelsPerScanLine >> 3) - aLength) / 2; + Mem::Fill (justified, sizeof(justified), 0xff); // white fill + Mem::Copy ((TUint8*)justified + margin, aScanline, aLength); + return RleEncoding(justified,aRleEncoding); + } + +LOCAL_C TUint8* RleEncoding (const TDesC8& aScanLine, TUint8* aRleEncoding) +// +// Build the RLE encoding for aScanline, handling wrong-sized scanlines +// + { + TInt len = aScanLine.Length (); + const TUint32 *scan = (const TUint32 *) aScanLine.Ptr (); + __ASSERT_DEBUG ((TUint (scan) & 3) == 0, Panic (EFaxEncodeScanlineAlignment)); + if (len >= (KFaxPixelsPerScanLine >> 3)) + return RleEncoding(scan + ((len - (KFaxPixelsPerScanLine >> 3)) >> 3),aRleEncoding); // margin in words + else + return RleEncoding(scan,len,aRleEncoding); + } + +LOCAL_C void EncodeHuffman(TDes8 & anEncodedScanLine,TInt aTagCode,const TUint8* aRleEncoding,const TUint8* aRleEnd) +// +// Starting with the tag code, encode all codes in the rle data using the single huffman table +// + { + TUint8 *t4 = (TUint8 *) anEncodedScanLine.Ptr (); + TUint8 *const e4 = t4; + // start with tag code + TCodeDef huff=KCodes[aTagCode]; + TUint code=HUFFBITS(huff); + TInt bits=HUFFLEN(huff)-16; + while (aRleEncoding>(bits+16); + bits+=HUFFLEN(huff); + if (bits<0) + continue; + *t4++=TUint8(code>>24); + *t4++=TUint8(code>>16); + code<<=16; + bits-=16; + } + if (bits>-16) + { // flush out the remaining bits + *t4++=TUint8(code>>24); + if (bits>-8) + *t4++=TUint8(code>>16); + } + anEncodedScanLine.SetLength (t4 - e4); + } + +/********************************************************************/ + +inline CFaxT4::CFaxT4 () + {PageInitialize(EFaxNormal,EModifiedHuffman);} + +EXPORT_C CFaxT4 *CFaxT4::NewLC () +/** Constructs a CFaxT4 object, which provides utility functions to encode and +decode fax scan lines. + +As is usual in Symbian OS, the only difference between this function and NewL() +is that this variant pushes the object to the cleanup stack. + +The new object is constructed with the default compression and resolution: +EModifiedHuffman and EFaxNormal respectively. + +@leave KErrNoMemory There is insufficient memory to perform the operation. +@return A pointer to the newly created object. +@capability None +*/ + { + CFaxT4 *self = NewL (); + CleanupStack::PushL (self); + return self; + } + +EXPORT_C CFaxT4 *CFaxT4::NewL () +/** Constructs a CFaxT4 object, which provides utility functions to encode and +decode fax scan lines. + +The function is exactly the same as NewLC() except that the new object is +popped from the cleanup stack. + +The new object is constructed with the default compression and resolution: +EModifiedHuffman and EFaxNormal respectively. + +@leave KErrNoMemory There is insufficient memory to perform the operation. +@return A pointer to the newly created object. +@capability None +*/ + { + return new (ELeave) CFaxT4; + } + +EXPORT_C void CFaxT4::PageInitialize (TFaxResolution aResolution, TFaxCompression aCompression, TInt aFlag2) +/** +Initialize fax page, set page parameters. + +@param aResolution defines fax resolution +@param aCompression defines fax compression +@param aFlag2 reserved flag. +@capability None +*/ + { + __ASSERT_ALWAYS (((aCompression == EModifiedHuffman) || (aCompression == EModifiedRead)), Panic (EFaxUnsupportedCompression)); + iCompression = aCompression; + iResolution = aResolution; + iReservedFlag2 = aFlag2; + iK = iResolution == EFaxFine ? 4 : 2; + iLineCount = 1; + // an all-white reference line + iRef[0]=RLE_MAKEUP(KRleWhite,KFaxBytesPerScanLine); + iRef[1]=RLE_RUN(KRleWhite,0); + iEndRef=iRef+2; + } + +EXPORT_C void CFaxT4::EncodeScanLine (const TDesC8 & aScanLine, TDes8 & anEncodedScanLine) +/** Encodes a scan line using either one dimensional Modified Huffman (MH) or two +dimensional Modified Read (MR) encoding. + +The type of encoding used depends on the compression type specified when the +object was initialised - using PageInitialize(). If the object was not initialised, +then the default compression is MH. + +@param aScanLine The raw scan line to be encoded. +@param anEncodedScanLine On return, contains the encoded scan line. +@capability None +*/ + { + if (iCompression == EModifiedRead) + EncodeScanLine2D (aScanLine, anEncodedScanLine); + else + EncodeScanLine1D(aScanLine,anEncodedScanLine); + } + +EXPORT_C void CFaxT4::EncodeScanLine1D (const TDesC8 & aScanLine, TDes8 & anEncodedScanLine) +/** Encodes a scan line using Modified Huffman compression. + +@param aScanLine The scan line to be encoded. +@param anEncodedScanLine On return, contains the MH encoded scan line. +@capability None +*/ + { + iEndRef=RleEncoding(aScanLine,iRef); + EncodeHuffman(anEncodedScanLine,iCompression == EModifiedHuffman ? KRleStd1D : KRleTag1D,iRef,iEndRef); + } + +EXPORT_C void CFaxT4::EncodeScanLine2D (const TDesC8 & aScanLine, TDes8 & anEncodedScanLine) +/** Encodes a scan line using Modified Read compression. + +@param aScanLine The scan line to be encoded. +@param anEncodedScanLine On return, contains the MR encoded scan line. +@capability None +*/ + { + // initialize our own scan line + TInt lc=iLineCount-1; + if (lc==0) + { // 1D reference line + iLineCount=iK; + EncodeScanLine1D(aScanLine,anEncodedScanLine); + } + else + { // 2D line + iLineCount=lc; + DoEncodeScanLine2D(aScanLine,anEncodedScanLine); + } + } + +void CFaxT4::DoEncodeScanLine2D (const TDesC8 & aScanLine, TDes8 & aEncodedScanLine) + { + TUint8 rlebuf[KFaxPixelsPerScanLine*3/2 + 16]; // for output + reference line + + // the buffer is big enough that the 2d coding output into the buffer will not + // catch the reference coding before it is used + + // copy the reference line into the end of the stack buffer + + TInt len=iEndRef-iRef; + TUint8* ref=rlebuf+sizeof(rlebuf)-len; + Mem::Copy(ref,iRef,len); + + // Do the standard RLE encoding of the current line + iEndRef=RleEncoding(aScanLine,iRef); + const TUint8* cur=iRef; + + TUint8* rle=rlebuf; + TInt a0=-1; // previous edge on current line + TInt a1=0; // current edge on current line + TInt b0; // previous edge on reference line + TInt b1=0; // current edge on reference line + TInt b2=0; // look-ahead edge on reference line + TInt color=KRleWhite; // color at a0 (initially white) + + // the reference color is not tracked. Instead the number of reference edges + // traversed is monitored (modulo 2) to ensure that edge b1 is of the same + // color to a1 at "gotB2" + + READ_RLE(cur,a1); // find the first edge + + for (;;) + { + do + { // find the new current and next edges on reference line + b0=b1; + b1=b2; + if (b1==KFaxPixelsPerScanLine) + break; // end of line + READ_RLE(ref,b2); +refMove1: // find just the look-ahead edge on the reference line + b0=b1; + b1=b2; + if (b1==KFaxPixelsPerScanLine) + break; + READ_RLE(ref,b2); + } while(b1<=a0); // ensure that we have the right reference edge + +gotB2: if (b2 < a1) + { // pass mode detected + *rle++=KRlePassMode; + a0=b2; // move along by 2 edges + continue; + } + + if (TUint(b1-a1+3)<=6u) + { // vertical mode + *rle++=TUint8(KRleVertMode0 + (b1 - a1)); + if (a1==KFaxPixelsPerScanLine) + break; // complete + if (b0>a1) + { + // special case of vertical mode edge "cross-over" + // the next edge may match an earlier reference edge than this! + // rewind the reference line by 2 edges + // we know that [b0,b1] is small, and so only uses 1 byte in the rle + // we check for [b1,b2] requiring a makeup byte as well + ref-=2; + if (b2-b1>=64) + --ref; + b2=b0; + b1=0; // no longer know b0, but this cannot happen again without traversing 2 edges + } + a0 = a1; // traverse a single edge + READ_RLE(cur,a1); + color=KRleWhite-color; + goto refMove1; + } + + // we must be in horizontal mode - write out the RLE codes for remainder + // and copy RLE codes for next edge from current coding + + *rle++=KRleHorzMode; + a0=Max(0,a0); // deal with start-effects (a0==-1) + TInt run=a1-a0; + WRITE_RLE(rle,color,run); + // copy the next run + if (a1==KFaxPixelsPerScanLine) + { // complete, need a zero-length, other-color, run to finish + *rle++=RLE_RUN(KRleWhite-color,0); + break; + } + // copy the next RLE code directly from the current line + TInt x=*cur++; + __ASSERT_DEBUG((x&KRleWhite)==KRleWhite-color,User::Invariant()); + if (x>=KRleMakeup) + { + *rle++=TUint8(x); + a1+=(x&KRleMakeupMask)<<5; + x=*cur++; + } + *rle++=TUint8(x); + a1+=x>>1; + if (a1==KFaxPixelsPerScanLine) + break; // complete + a0=a1; + READ_RLE(cur,a1); // traverse another edge + if (b1>a0) + goto gotB2; + } + EncodeHuffman(aEncodedScanLine,KRleTag2D,rlebuf,rle); + } + +EXPORT_C TInt CFaxT4::DecodeScanLine (TDes8 & aScanLine, const TDesC8 & anEncodedScanLine) +/** Decodes a scan line. + +The decoding method depends on the compression type specified when the object +was initialised - using PageInitialize(). If the object was not initialised, +then the scan line is decoded as Modified Huffman. + +The fax client can determine the type of compression used in a fax from its +header, and can hence use PageInitialize() to set the correct decoding method. +KErrUnderflow is returned if the wrong type of compression is specified. + +@param aScanLine On return, contains the decoded scan line. +@param anEncodedScanLine The encoded scan line to be decoded. +@return KErrNone if successful, otherwise another of the system-wide error +codes. +@capability None +*/ + { + if (iCompression == EModifiedHuffman) + return (DecodeScanLine1D (aScanLine, anEncodedScanLine)); + else + return (DecodeScanLine2D (aScanLine, anEncodedScanLine)); + } + +void CFaxT4::DecodeHuffman(const TDesC8 & aEncodedScanLine) + { + // If all goes wrong then the reference line is unchanged and will be + // used for the current line + + const TUint8* t4=aEncodedScanLine.Ptr(); + const TUint8* endt4=t4+aEncodedScanLine.Length(); + TUint bits=0; + + // store the basic RLE data locally, and copy to the member data if the decode + // is successful + + TUint8 rlebuf[KFaxPixelsPerScanLine+4]; + TUint8* rle=rlebuf; + + // Keep track of where we have got to on the reference (previous) line + const TUint8* ref=iRef; + TInt b1=0; // pixel position on the reference line + TInt a0=0; // previous position on the current line + TInt a1=0; // current position on the current line + + // start decoding using the tag-tree, which finds the first 1-bit + // and then determines the encoding (1D or 2D) based on the next bit + const TNode* tree=KTagTree; + + // "color" stores the current line color (in bit 0), the reference line + // color (in bit 1), and the number of white/black codes to expect (x4) + // if color<0, then we are in 2d-tree mode + // initially unused until the encoding type is known + + TInt color=0; + TInt code; + + for (;;) + { + // the structure of the following code maxmises the speed of the + // huffman decoder. Don't change it. + code = 0; // start at the root of the tree +nextBit2d: + if (((bits <<= 1) & 0x80000000)==0) + goto nextByte2d; // run out of bits in the current byte +decode2d: + code = CODE (tree, code, bits & 0x80); + if (ISBRANCH (code)) + goto nextBit2d; // a branch code + + // We have the huffman code + + if (code=0,User::Invariant()); + TInt v=CODEVALUE(code); + a1+=v; + if (a1>KFaxPixelsPerScanLine) + return; // overflow + if (v < 64) + { // a run code (as opposed to make-up code). Emit the RLE + a0=a1-a0; + WRITE_RLE(rle,(color&1),a0); + a0=a1; + color^=KRleWhite; // switch color + color-=4; // one less white/black code + tree=color>=0 ? color&KRleWhite ? KWhiteTree : KBlackTree : KTwoTree; + } + continue; + } + if (code>1))&1)==0) + { + b0=b1; + if (b1!=KFaxPixelsPerScanLine) + READ_RLE(ref,b1); + color^=KRleWhite<<1; + } + + // If the code is below PASSMODE then it is one of the vertical code words + // which are pretty easy to decipher as we have all the data. Vertical mode + // flips the colour and then continues + + if (code==KPassMode) + { + // we need to identify the next reference "edge" + if (b1==KFaxPixelsPerScanLine) + return; // overflow + READ_RLE(ref,b1); + if (b1==KFaxPixelsPerScanLine) + return; // overflow + color^=KRleWhite<<1; + a1=b1; + continue; + } + + __ASSERT_DEBUG(code>=KVtMode3n && code<=KVtMode3p,User::Invariant()); + // vertical mode + a1=b1+(code-(KVtMode0)); + if (a1>KFaxPixelsPerScanLine) + return; // overflow + if (b0>a1) + { + // special case of vertical mode cross-over + // rewind the reference line to the previous "edge" + b1=b0; + --ref; + color^=KRleWhite<<1; + } + a0=a1-a0; + WRITE_RLE(rle,(color&1),a0); + a0=a1; + color^=KRleWhite; + } +nextByte2d: + if (t4 < endt4) + { + bits = 0xff000000u | *t4++; + goto decode2d; + } +eol2d: + if (a0==KFaxPixelsPerScanLine) + iEndRef=Mem::Copy(iRef,rlebuf,rle-rlebuf); + } + +EXPORT_C TInt CFaxT4::DecodeScanLine2D (TDes8 & aScanLine, const TDesC8 & aEncodedScanLine) +/** Decodes a Modified Read encoded scan line. + +@param aScanLine On return, contains the decoded scan line. +@param anEncodedScanLine The 2D encoded scan line to be decoded. +@return KErrNone if successful, KErrUnderflow if the scan line is encoded as +MR, otherwise another of the system-wide error codes. +@capability None +*/ + { + DecodeHuffman(aEncodedScanLine); +// +// decode the RLE into the scanline +// + aScanLine.SetLength (KFaxPixelsPerScanLine / 8); + TUint32 *scan = (TUint32 *) aScanLine.Ptr (); + __ASSERT_DEBUG ((TUint (scan) & 3) == 0, Panic (EFaxDecodeScanlineAlignment)); + + const TUint8* rle=iRef; + const TUint8* const end=iEndRef; + + TUint color = ~0u; // start white + TUint out = 0; // output pixels in accumulation + TInt bits = 0; // this is the number of used bits in out + + while (rle> 1; // add the run length + if (bits < 32) // hasn't completed the word + out -= color << bits; // remove the trailing bits + else + { + *scan++ = out; // write the 32-bits + bits -= 64; + if (bits >= 0) + *scan++ = color; // + another 32 bits + else + bits += 32; // no extra + out = color - (color<>1 is the multiple of 64 bits + out += color << bits; // complete the current 32-bits + *scan++ = out; // output + *scan++ = color; // +32 bits of color + for (run -= KRleMakeup+4;run >= 0;run -= 2) + { // extra multiples of 64 bits + *scan++ = color; + *scan++ = color; + } + out = color - (color<=0) + *scan++=color; + else + bits+=32; + out=color-(color<=0;--code) + { + *scan++=color; + *scan++=color; + } + out = color-(color<