File: MemoryDecoder.c%2B%2B

package info (click to toggle)
hylafax 3%3A6.0.6-8.1
  • links: PTS
  • area: main
  • in suites: buster
  • size: 9,508 kB
  • sloc: sh: 15,371; ansic: 13,242; makefile: 1,545; cpp: 781; awk: 529
file content (538 lines) | stat: -rw-r--r-- 16,131 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
/*	$Id$ */
/*
 * Copyright (c) 1994-1996 Sam Leffler
 * Copyright (c) 1994-1996 Silicon Graphics, Inc.
 * HylaFAX is a trademark of Silicon Graphics
 *
 * 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.
 */
/*
 * Phase C data correction and page chopping support.
 */

#include "MemoryDecoder.h"
#include "G3Encoder.h"
#include "StackBuffer.h"
#include "config.h"

MemoryDecoder::MemoryDecoder(u_char* data, u_long n)
{
    bp = data;
    cc = n;
    endOfData = NULL;
    nblanks = 0;
    runs = NULL;
    rowBuf = NULL;
    rows = 0;
}

MemoryDecoder::MemoryDecoder(u_char* data, u_int wid, u_long n,
                             u_int order, bool twoDim, bool mmr)
{
    bp         = data;
    width      = wid;
    byteWidth  = howmany(width, 8);
    cc         = n;
    rows       = 0;
    
    fillorder  = order;
    is2D       = twoDim;
    isG4       = mmr;

    runs      = new tiff_runlen_t[2*width];      // run arrays for cur+ref rows
    rowBuf    = new u_char[byteWidth];
    setupDecoder(fillorder, is2D, isG4);
    setRuns(runs, runs+width, width);
}

MemoryDecoder::~MemoryDecoder()
{
    if (rowBuf)
	delete rowBuf;
    if (runs)
	delete runs;
}

int
MemoryDecoder::decodeNextByte()
{
    if (cc == 0)
        raiseRTC();                     // XXX don't need to recognize EOF
    cc--;
    return (*bp++);
}

void
MemoryDecoder::invalidCode(const char* type, int x)
{
    printf("Invalid %s code word, x %d\n", type, x);
}        
void
MemoryDecoder::badPixelCount(const char* type, int got, int expected)  
{
    if (!seenRTC())
	printf("Bad %s pixel count, got %d, expected %d\n",
	    type, got, expected);
}
void
MemoryDecoder::badDecodingState(const char* type, int x)
{
    printf("Panic, bad %s decoding state, x %d\n", type, x);
}

static bool
isBlank(tiff_runlen_t* runs, u_int rowpixels)
{
    u_int x = 0;
    for (;;) {
	if ((x += *runs++) >= rowpixels)
	    break;
	if (runs[0] != 0)
	    return (false);
	if ((x += *runs++) >= rowpixels)
	    break;
    }
    return (true);
}

void
MemoryDecoder::scanPageForBlanks(u_int fillorder, const Class2Params& params)
{
    setupDecoder(fillorder,  params.is2D(), (params.df == DF_2DMMR));
    u_int rowpixels = params.pageWidth();	// NB: assume rowpixels <= 4864
    tiff_runlen_t runs[2*4864];			// run arrays for cur+ref rows
    setRuns(runs, runs+4864, rowpixels);

    if (!RTCraised()) {
	/*
	 * Skip a 1" margin at the top of the page before
	 * scanning for trailing white space.  We do this
	 * to ensure that there is always enough space on
	 * the page to image a tag line and to satisfy a
	 * fax machine that is incapable of imaging to the
	 * full extent of the page.
	 */
	u_int topMargin = 1*98;			// 1" at 98 lpi
	switch (params.vr) {
	    case VR_FINE:
	    case VR_200X200:
		topMargin *= 2;			// 196 lpi =>'s twice as many
		break;
	    case VR_300X300:
		topMargin *= 3;
		break;
	    case VR_R8:
	    case VR_R16:
	    case VR_200X400:
		topMargin *= 4;
		break;
	}
	do {
	    (void) decodeRow(NULL, rowpixels);
	} while (--topMargin);
	/*
	 * Scan the remainder of the page data and calculate
	 * the number of blank lines at the bottom.
	 */
	for (;;) {
	    (void) decodeRow(NULL, rowpixels);
	    if (isBlank(lastRuns(), rowpixels)) {
		endOfData = bp;			// include one blank row
		nblanks = 0;
		do {
		    nblanks++;
		    (void) decodeRow(NULL, rowpixels);
		} while (isBlank(lastRuns(), rowpixels));
	    }
	}
    }
}

#ifdef roundup
#undef roundup
#endif
#define roundup(a,b)    ((((a)+((b)-1))/(b))*(b))

/*
 * TIFF Class F specs say:
 *
 * "As illustrated in FIGURE 1/T.4 in Recommendation T.4 (the Red
 * Book, page 20), facsimile documents begin with an EOL (which in
 * Class F is byte-aligned)..."
 *
 * This is wrong! "Byte-aligned" first EOL means extra zero bits
 * which are not allowed by T.4. Reencode first row to fix this
 * "byte-alignment".
 */
void MemoryDecoder::fixFirstEOL()
{
    fxStackBuffer result;
    G3Encoder enc(result);
    enc.setupEncoder(fillorder, is2D, isG4);
    
    memset(rowBuf, 0, byteWidth*sizeof(u_char)); // clear row to white
    if(!RTCraised()) {
        u_char* start = current();
        (void)decodeRow(rowBuf, width);
        /*
         * syncronize to the next EOL and calculate pointer to it
         * (see detailed explanation of look_ahead in encodeTagLine())
         */
        (void)isNextRow1D();
        u_int look_ahead = roundup(getPendingBits(),8) / 8;
        u_int decoded = current() - look_ahead - start;

        enc.encode(rowBuf, width, 1);
	enc.encoderCleanup();
        u_int encoded = result.getLength();
            
        while( encoded < decoded ){
            result.put((char) 0);
            encoded++;
        }
        if( encoded == decoded ){
            memcpy(start, (const char*)result, encoded);
        }
    }
}

/*
 * TIFF Class F specs say:
 *
 * "Aside from EOL's, TIFF Class F files contain only image data. This
 * means that the Return To Control sequence (RTC) is specifically
 * prohibited..."
 *
 * Nethertheless Ghostscript and possibly other TIFF Class F writers
 * append RTC or single EOL to the last encoded line. Remove them.
 */
u_char* MemoryDecoder::cutExtraRTC()
{
    /*
     * We expect RTC near the end of data and thus
     * do not check all image to save processing time.
     * It's safe because we will resync on the first 
     * encountered EOL.
     *
     * NB: We expect G3Decoder::data==0 and
     * G3Decoder::bit==0 (no data in the accumulator).
     * As we cannot explicitly clear the accumulator
     * (bit and data are private), cutExtraRTC()
     * should be called immediately after
     * MemoryDecoder() constructing.
     */
    const u_long CheckArea = 20;
    if( cc > CheckArea ){
        bp += (cc-CheckArea);
        cc = CheckArea;
    }
        
    endOfData = NULL;
    rows = 0;
    if(!RTCraised()) {
        /*
         * syncronize to the next EOL and calculate pointer to it
         * (see detailed explanation of look_ahead in encodeTagLine())
         */
        (void)isNextRow1D();
        u_int look_ahead = roundup(getPendingBits(),8) / 8;
        endOfData = current() - look_ahead;
        for (;;) {
            if( decodeRow(NULL, width) ){
                /*
                 * endOfData is now after last good row. Thus we correctly handle
                 * RTC, single EOL in the end, or no RTC/EOL at all
                 */
                endOfData = current();
            }
            if( seenRTC() )
                break;
	    rows++;
        }
    }
    return endOfData;
}

u_char* MemoryDecoder::cutExtraEOFB()
{
    /*
     * MMR requires us to decode the entire image...
     */
    endOfData = NULL;
    rows = 0;
    if(!RTCraised()) {
	endOfData = current();
        for (;;) {
            if( decodeRow(NULL, width) ){
                endOfData = current();
            }
            if( seenRTC() )
                break;
	    rows++;
        }
    }
    /*
     * The loop above will leave the endOfData pointer somewhere inside of EOFB.
     * Make sure that endOfData points to the last byte containing real image data.
     * So trim any whole bytes containing EOFB data.
     */
    if (seenRTC()) {
	bool trimmed;
	u_int searcharea;
	u_short i;
	do {
	    while (*(endOfData - 1) == 0x00) endOfData--;
	    searcharea =  (*(endOfData - 1) << 16) | (*(endOfData - 2) << 8) | *(endOfData - 3);
	    trimmed = false;
	    for (i = 0; i < 13; i++) {
		if (((searcharea >> i) & 0xFFF) == 0x800) {
		    endOfData--;
		    trimmed = true;
		    break;
		}
	    }
	} while (trimmed);
    }
    return endOfData;
}

u_char* MemoryDecoder::encodeTagLine(u_long* raster, u_int th, u_int slop)
{
    /*
     * Decode (and discard) the top part of the page where
     * the tag line is to be imaged.  Note that we assume
     * the strip of raw data has enough scanlines in it
     * to satisfy our needs (caller is responsible).
     *
     * ... and then...
     *
     * Encode the result according to the parameters of
     * the outgoing page.  Note that the encoded data is
     * written in the bit order of the page data since
     * it must be merged back with it below.
     */
    fxStackBuffer result;
    G3Encoder enc(result);
    enc.setupEncoder(fillorder, is2D, isG4);

    decode(NULL, width, th);		// discard decoded data
    if (!isG4) {
	/*
	 * If the source is 2D-encoded and the decoding done
	 * above leaves us at a row that is 2D-encoded, then
	 * our re-encoding below will generate a decoding
	 * error if we don't fix things up.  Thus we discard
	 * up to the next 1D-encoded scanline.  (We could
	 * instead decode the rows and re-encoded them below
	 * but to do that would require decoding above instead
	 * of skipping so that the reference line for the
	 * 2D-encoded rows is available.)
	 */
	u_int n;
	for (n = 0; n < 4 && !isNextRow1D(); n++)
	    decodeRow(NULL, width);
	th += n;			// compensate for discarded rows
	/*
	 * Things get tricky trying to identify the last byte in
	 * the decoded data that we want to replace.  The decoder
	 * must potentially look ahead to see the zeros that
	 * makeup the EOL that marks the end of the data we want
	 * to skip over.  Consequently current() must be
	 * adjusted by the look ahead, a factor of the number of
	 * bits pending in the G3 decoder's bit accumulator.
	 */
	u_int look_ahead = roundup(getPendingBits(),8) / 8;
	u_int decoded = current() - look_ahead - bp;
	enc.encode(raster, width, th);
	enc.encoderCleanup();
	delete raster;
	/*
	 * To properly join the newly encoded data and the previous
	 * data we need to insert two bytes of zero-fill prior to
	 * the start of the old data to ensure 11 bits of zero exist
	 * prior to the EOL code in the first line of data that
	 * follows what we skipped over above.  Note that this
	 * assumes the G3 decoder always stops decoding prior to
	 * an EOL code and that we've adjusted the byte count to the
	 * start of the old data so that the leading bitstring is
	 * some number of zeros followed by a 1.
	 */
	result.put((char) 0);
	result.put((char) 0);
	/*
	 * Copy the encoded raster with the tag line back to
	 * the front of the buffer that was passed in.  The
	 * caller has preallocated a hunk of space for us to
	 * do this and we also reuse space occupied by the
	 * original encoded raster image.  If insufficient space
	 * exists for the newly encoded tag line, then we jam
	 * as much as will fit w/o concern for EOL markers;
	 * this will cause at most one bad row to be received
	 * at the receiver (got a better idea?).
	 */
	u_int encoded = result.getLength();
	if (encoded > slop + decoded)
	    encoded = slop + decoded;
	u_char* dst = bp + (int)(decoded-encoded);
	memcpy(dst, (const unsigned char*) result, encoded);
	return (dst);
    } else {
	u_char* refrow = new u_char[byteWidth*sizeof(u_char)];	// reference row
	memset(refrow, 0, byteWidth*sizeof(u_char));		// clear to white
	enc.encode(raster, width, th, (unsigned char*) refrow);
	/*
	 * refrow does not need to be altered now to match the 
	 * last line of raster because the raster contains MARGIN_BOT
	 * blank lines.
	 */
	delete raster;
	if (!RTCraised()) {
	    for (;;) {
		(void) decodeRow(rowBuf, width);
		if(seenRTC())
		    break;
		enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
		memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
	    }
	}
	enc.encoderCleanup();
	cc = result.getLength();
	u_char* dst = new u_char[cc];
	memcpy(dst, (const unsigned char*) result, cc);
	return (dst);
    }
}

#ifdef HAVE_JBIG
extern "C" {
#include "jbig.h"
}
fxStackBuffer resultBuffer;

void bufferJBIGData(unsigned char *start, size_t len, void *file)
{
    resultBuffer.put((const char*) start, len);
}
#endif /* HAVE_JBIG */

u_char* MemoryDecoder::convertDataFormat(const Class2Params& params)
{
    /*
     * Convert data to the format specified in params.  The decoder has already
     * been set up, and we don't need to worry about decoder operation here.  
     * These params are for the encoder to use.
     */
    rows = 0;
    if (params.df <= DF_2DMMR) {
	fxStackBuffer result;
	G3Encoder enc(result);
	enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));

	u_char* refrow = new u_char[byteWidth*sizeof(u_char)];	// reference row
	memset(refrow, 0, byteWidth*sizeof(u_char));		// clear to white

	/*
	 * For MR we encode a 1-D or 2-D line according to T.4 4.2.1.
	 * We understand that "standard" resolution means VR_NORMAL.
	 */
	u_short k = 0;

	if (!RTCraised()) {
	    for (;;) {
		(void) decodeRow(rowBuf, width);
		if(seenRTC())
		    break;
		rows++;
		// encode the line specific to the desired format
		if (params.df == DF_2DMMR) {
		    enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
		} else if (params.df == DF_2DMR) {
		    if (k) {
			enc.encode(rowBuf, width, 1, (unsigned char*) refrow);	// 2-D
		    } else {
			enc.encode(rowBuf, width, 1);				// 1-D
			k = (params.vr == VR_NORMAL || params.vr == VR_200X100) ? 2 : 4;
		    }
		    k--;
		} else {	// DF_1DMH
		    enc.encode(rowBuf, width, 1);
		}
		memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
	    }
	}
	enc.encoderCleanup();
	cc = result.getLength();
	u_char* dst = new u_char[cc];
	memcpy(dst, (const unsigned char*) result, cc);
	return (dst);
    } else if (params.df == DF_JBIG) {
#ifdef HAVE_JBIG
	char* decodedrow = new char[byteWidth];
	fxStackBuffer raster;
	resultBuffer = raster;		// initialize resultBuffer
	if (!RTCraised()) {
	    for (;;) {
		(void) decodeRow(decodedrow, width);
		if(seenRTC())
		    break;
		raster.put(decodedrow, byteWidth*sizeof(u_char));
		rows++;
	    }
	}
	delete decodedrow;
	// bitmap raster is prepared, pass through JBIG encoding...
	cc = raster.getLength();
	u_char* rasterdst = new u_char[cc];
	memcpy(rasterdst, (const unsigned char*) raster, cc);
	unsigned char *pmap[1];
	pmap[0] = rasterdst;
	struct jbg_enc_state jbigstate;
	jbg_enc_init(&jbigstate, width, rows, 1, pmap, bufferJBIGData, NULL);
	/*
	 * T.85 requires "single-progressive sequential coding" and thus:
	 *
	 * Dl = 0, D = 0, P = 1 are required
	 * L0 = 128 is suggested (and appears to be standard among senders)
	 * Mx = 0 to 127 (and 0 appears to be standard)
	 * My = 0, HITOLO = 0, SEQ = 0, ILEAVE = 0, SMID = 0
	 * TPDON = 0, DPON = 0, DPPRIV = 0, DPLAST = 0
	 *
	 * As these settings vary from the library's defaults, we carefully
	 * specify all of them.
	 */
	jbg_enc_options(&jbigstate, 0, 0, 128, 0, 0);
	jbg_enc_out(&jbigstate);
	jbg_enc_free(&jbigstate);
	delete rasterdst;
	// image is now encoded into JBIG
	//resultBuffer[19] |= 0x20;	// set VLENGTH = 1, if someday we want to transmit NEWLEN
	cc = resultBuffer.getLength();
	u_char* dst = new u_char[cc];
	memcpy(dst, (const unsigned char*) resultBuffer, cc);
	return (dst);
#else
	printf("Attempt to convert Phase C data to JBIG without JBIG support.  This should not happen.\n");
	return (NULL);
#endif /* HAVE_JBIG */
    } else {
	printf("Attempt to convert Phase C data to an unsupported format.  This should not happen.\n");
	return (NULL);
    }
}