fax/faxclientandserver/faxio/FAXIO.CPP
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:41:59 +0200
changeset 0 3553901f7fa8
permissions -rw-r--r--
Revision: 201005 Kit: 201005

// 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 <s32mem.h>
#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<<KTestBitCount)-1;
	const TInt KRunBias=KTestBitCount-2;
	static const TUint8 KFirstBit[64]=
		{
		0,5,4,5,3,5,4,5,2,5,4,5,3,5,4,5,1,5,4,5,3,5,4,5,2,5,4,5,3,5,4,5,
		0,5,4,5,3,5,4,5,2,5,4,5,3,5,4,5,1,5,4,5,3,5,4,5,2,5,4,5,3,5,4,5
		};

	const TUint32 *end = aScan + (KFaxPixelsPerScanLine >> 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<aRleEnd)
		{
		__ASSERT_DEBUG (bits < 0, User::Invariant ());
		TUint8 c=*aRleEncoding++;
		TCodeDef huff=KCodes[c];
		code|=HUFFBITS(huff)>>(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<KOurEol)
			{	// the code was a white/black length code
			__ASSERT_DEBUG(color>=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<KPassMode)
			{
			if (code == KHorzMode)
				{	// expect two white/black codes to follow
				color+=8;
				tree = color&KRleWhite ? KWhiteTree : KBlackTree;
				continue;
				}
			if (code==KTag1D)
				{	// 1d decoding: set color to maximum positive value
					// as all codes are standard white/black code.
				color=KMaxTInt;		// current and reference color both 1 (white)
				tree=KWhiteTree;
				continue;
				}
			if (code==KTag2D)
				{	// 2d decoding: set color negative to indicate 2d-tree
				color=-1;			// current and reference color both 1 (white)
				tree=KTwoTree;
				continue;
				}
			if (code==KOurEol)
				goto eol2d;
			__ASSERT_DEBUG(code == KBadRun,User::Invariant());
			return;			// bad run, give up
			}

		// The remaining 2D possibilities all require that we know the various 2D determinants
		// so we proceed as follows :

		// b0 tracks the previous "edge" in the reference line
		TInt b0=0;
		// find the next "edge" on the reference line after a1
		// if we've just started decoding (rle==rlebuf), b1 boundary at 0 is correct
		if (rle!=rlebuf)
			{
			while (b1<=a1)
				{
				color^=KRleWhite<<1;
				b0=b1;
				if (b1!=KFaxPixelsPerScanLine)
					READ_RLE(ref,b1);
				}
			}
		// the b1 "edge" must match the colors of a1, so move one more edge if
		// the a0 color (color&1) is the same as the b1 color (color&2)
		if (((color^(color>>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<end)
		{
		TInt run=*rle++;
		__ASSERT_DEBUG(TUint((run&1))==(color&1),User::Invariant());	// rle-data correct
		if (run<KRleMakeup)
			{	// run length code (x2)
			out += color << bits;		// complete the current 32-bits
			bits += run >> 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<<bits);	// bits remaining
				}
			color = ~color;				// swap color
			}
		else
			{
			// make-up code. (run-KRleMakeup)>>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<<bits);	// remainder bits
			}
		}
	return KErrNone;
	}

EXPORT_C TInt CFaxT4::DecodeScanLine1D (TDes8 & aScanLine, const TDesC8 & anEncodedScanLine)
//
// This could be done through DecodeScanLine2D, but this is an optimized version for 1D
// The intermediate rle encoding is skipped
//
/** Decodes a Modified Huffman encoded scan line.

@param aScanLine On return, contains the decoded scan line. 
@param anEncodedScanLine The MH 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
*/
	{
	const TUint8 *t4 = anEncodedScanLine.Ptr ();
	const TUint8 *const endt4 = t4 + anEncodedScanLine.Length ();
	//
	aScanLine.SetLength (KFaxPixelsPerScanLine / 8);
	TUint32 *scan = (TUint32 *) aScanLine.Ptr ();
	__ASSERT_DEBUG ((TUint (scan) & 3) == 0, Panic (EFaxDecodeScanlineAlignment));
	TUint out = 0;
	TInt bits = 0;               // this is the number ofused bits
	//
	TInt dotsleft = KFaxPixelsPerScanLine;
	const TNode *tree = KSynchTree;
	TUint octet=0;
	TUint color = 0xffffffffu;

nextCode1d:
	TInt code = 0;
nextBit1d:
	if (((octet <<= 1) & 0x80000000)==0)
		goto nextByte1d;
decode1d:
	code = CODE (tree, code, octet & 0x80);
	if (ISBRANCH (code))
		goto nextBit1d;

	// end of huffman code

	if (code<KEndCode+0x40)
		{
		code-=KEndCode;
		if ((dotsleft -= code) < 0)
			return KErrOverflow;
		out += color<<bits;
		bits+=code;
		if (bits<32)
			out -= color<<bits;	// output word not full
		else
			{	// out is full
			*scan++=out;
			bits-=64;
			if (bits>=0)
				*scan++=color;
			else
				bits+=32;
			out=color-(color<<bits);
			}
		color = ~color;
		if (color)
			tree=KWhiteTree;
		else
			tree=KBlackTree;
		goto nextCode1d;
		}
	else if (code<=KEndCode+KMaxRun)
		{	// makeup (multiple of 64)
		code-=KEndCode+0x40;
		if ((dotsleft -= code<<6) < 0)
			return KErrOverflow;
		out += color << bits;
		*scan++=out;
		*scan++=color;
		for (code-=2;code>=0;--code)
			{
			*scan++=color;
			*scan++=color;
			}
		out = color-(color<<bits);
		goto nextCode1d;
		}
	else if (code == KOurEol)
		goto eol1d;
	else if (code == KStd1D)
		{
		tree=KWhiteTree;
		goto nextCode1d;
		}
	else
		{
		__ASSERT_DEBUG (code == KBadRun, User::Invariant ());
		return KErrCorrupt;
		}
nextByte1d:
	if (t4 < endt4)
		{
		octet = 0xff000000u | *t4++;
		goto decode1d;
		}
eol1d:
	__ASSERT_DEBUG (dotsleft || (TUint8 *) scan - aScanLine.Ptr () == KFaxPixelsPerScanLine / 8, User::Invariant ());
	return dotsleft ? KErrUnderflow : KErrNone;
	}