about summary refs log tree commit diff
path: root/urt/rle_putrow.c
blob: 31a596c842dd44e6de80b5febc7340fde58585c7 (plain) (blame)
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
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 *
 *  Modified at BRL 16-May-88 by Mike Muuss to avoid Alliant STDC desire
 *  to have all "void" functions so declared.
 */
/*
 * rle_putrow.c - Save a row of the fb to a file.
 *
 * Author:      Spencer W. Thomas
 *              Computer Science Dept.
 *              University of Utah
 * Date:        1 April 1981
 * Copyright (c) 1981,1986 Spencer W. Thomas
 *
 * $Id: rle_putrow.c,v 3.0.1.2 1992/01/28 18:29:22 spencer Exp $
 */

#include <stdio.h>

#include "rle_put.h"
#include "rle.h"


#define FASTRUNS                /* Faster run finding */

#define FALSE   0
#define TRUE    1

/* Save some typing. */
#define PBRUN the_hdr->priv.put.brun

/*****************************************************************
 * TAG( findruns )
 *
 * Find runs not a given color in the row.
 * Inputs:
 *      row:            Row of pixel values
 *      rowlen:         Number of pixels in the row.
 *      color:          Color to compare against.
 *      nrun:           Number of runs already found (in different colors).
 *      brun:           Runs found in other color channels already.
 * Outputs:
 *      brun:           Modified to reflect merging of runs in this color.
 *      Returns number of runs in brun.
 * Assumptions:
 *
 * Algorithm:
 *      Search for occurrences of pixels not of the given color outside
 *      the runs already found.  When some are found, add a new run or
 *      extend an existing one.  Adjacent runs with fewer than two
 *      pixels intervening are merged.
 */
static int
findruns(rle_pixel * const row,
         int         const rowlen,
         int         const color,
         int         const nrunAlready,
         short    (* const brun)[2]) {

    int i = 0, lower, upper;
    int s, j;
    int nrun;

#ifdef DEBUG
    fprintf( stderr, "findruns( " );
    for ( s = 0; s < rowlen; s++ )
        fprintf( stderr, "%2x.%s", row[s], (s % 20 == 19) ? "\n\t" : "" );
    if ( s % 20 != 0 )
        fprintf( stderr, "\n\t" );
    fprintf( stderr, "%d, %d, %d, \n\t", rowlen, color, nrun );
    for ( j = 0; j < nrun; j++ )
        fprintf( stderr, "(%3d,%3d) %s", brun[j][0], brun[j][1],
                 (j % 6 == 5) ? "\n\t" : "" );
    fprintf( stderr, ")\n" );
#endif

    nrun = nrunAlready;

    while ( i <= nrun )
    {
        /* Assert: 0 <= i <= rowlen
         * brun[i] is the run following the "blank" space being
         * searched.  If i == rowlen, search after brun[i-1].
         */

        /* get lower and upper bounds of search */

        if ( i == 0 )
            lower = 0;
        else
            lower = brun[i-1][1] + 1;

        if ( i == nrun )
            upper = rowlen - 1;
        else
            upper = brun[i][0] - 1;

#ifdef DEBUG
        fprintf( stderr, "Searching before run %d from %d to %d\n",
                 i, lower, upper );
#endif
        /* Search for beginning of run != color */

        for ( s = lower; s <= upper; s++ )
            if ( row[s] != color )
                break;


        if ( s <= upper )       /* found a new run? */
        {
            if ( s > lower + 1 || i == 0 ) /* disjoint from preceding run? */
            {
#ifdef DEBUG
                fprintf( stderr, "Found new run starting at %d\n", s );
#endif
                /* Shift following runs up */
                for ( j = nrun; j > i; j-- )
                {
                    brun[j][0] = brun[j-1][0];
                    brun[j][1] = brun[j-1][1];
                }
                brun[i][0] = s;
                nrun++;
            }
            else
            {
                i--;            /* just add to preceding run */
#ifdef DEBUG
                fprintf( stderr, "Adding to previous run\n" );
#endif
            }

            for ( ; s <= upper; s++ )
                if ( row[s] == color )
                    break;

            brun[i][1] = s - 1;

#ifdef DEBUG
            fprintf( stderr, "Ends at %d", s - 1 );
#endif
            if ( s >= upper && i < nrun - 1 ) /* merge with following run */
            {
                brun[i][1] = brun[i+1][1];
                /* move following runs back down */
                for ( j = i + 2; j < nrun; j++ )
                {
                    brun[j-1][0] = brun[j][0];
                    brun[j-1][1] = brun[j][1];
                }
                nrun--;
#ifdef DEBUG
                fprintf( stderr, ", add to next run" );
#endif
            }
#ifdef DEBUG
            putc( '\n', stderr );
#endif
        }

        /* Search in next space */
        i++;
    }

    return nrun;
}



/*****************************************************************
 * TAG( rle_putrow )
 * Write a scanline to the output file.
 *
 * Inputs:
 *      rows:           Pointer to vector of pointers to
 *                      rle_pixel arrays containing the pixel information.
 *                      If NULL, rowlen scanlines are skipped.
 *      rowlen:         The number of pixels in the scanline, or the
 *                      number of scanlines to skip (see above).
 * Outputs:
 *      Run length encoded information is written to the_hdr.rle_file.
 * Assumptions:
 *      I'm sure there are lots of assumptions in here.
 * Algorithm:
 *      There are two parts:
 *              1. Find all "sufficiently long" runs of background
 *                 color.  These will not be saved at all.
 *              2. For each run of non-background, for each color
 *                 channel, find runs of identical pixel values
 *                 between "data" segments (of differing pixel
 *                 values).
 *      For part 1, "sufficiently long" is 2 pixels, if the following
 *      data is less than 256 pixels long, otherwise it is 4 pixels.
 *      This is enforced by a post-process merge.
 *
 *      Part 1 can be done in two different ways, depending on whether
 *      FASTRUNS is defined or not.  With FASTRUNS defined, it finds
 *      runs of the background pixel value in each channel
 *      independently, and then merges the results.  With FASTRUNS not
 *      defined, it scans all channels in parallel.
 *
 *      Part 2 uses a state machine.  For each run of non-background
 *      data, it searches for sufficiently long sequences of a single
 *      value (in each channel independently).  Sufficiently long is 4
 *      pixels if the following data is < 256 pixels, 6 pixels
 *      otherwise.  This is because the startup cost for the run is 2
 *      bytes, and the startup cost for a data segment is 2 bytes if
 *      it is < 256 pixels long, 4 bytes otherwise.  Thus a run
 *      shorter than 4 or 6 pixels (respectively) would actually make
 *      the output longer.  An additional pixel is required if the
 *      preceding data is an odd number of pixels long (because a
 *      filler byte will be output at the end of it.)
 */

void
rle_putrow(rows, rowlen, the_hdr)
rle_pixel *rows[];
int rowlen;
rle_hdr * the_hdr;
{
    int i, j;
    int nrun;
    rle_pixel *row;
    int mask;
    char bits[256];
    short   state,              /* State of run-finding state machine. */
            dstart,             /* Starting point for current data segment. */
            dend,               /* Ending point of current data segment. */
            rstart = 0,         /* Starting point of current run. */
            runval = 0;         /* Data value for current run. */

    if (rows == NULL)
    {
        the_hdr->priv.put.nblank += rowlen;
        return;
    }
    /*
     * If not done already, allocate space to remember runs of
     * non-background color.  A run of bg color must be at least 2
     * bytes long to count, so there can be at most rowlen/3 of them.
     */
    if ( PBRUN == NULL )
    {
        PBRUN = (short (*)[2])malloc(
            (unsigned)((rowlen/3 + 1) * 2 * sizeof(short)) );
        if ( PBRUN == NULL )
        {
            fprintf( stderr, "%s: Malloc failed in rle_putrow, writing %s\n",
                     the_hdr->cmd, the_hdr->file_name );
            exit(1);
        }
    }
    /* Unpack bitmask in the_hdr struct */
    for ( i=0; i < the_hdr->ncolors; i++ )
        bits[i] = RLE_BIT( *the_hdr, i );
    bits[255] = RLE_BIT( *the_hdr, -1 );

    /*
     * If saving only non-background pixels, find runs of them.  Note
     * that the alpha channel is considered to be background iff it is
     * zero.
     */
#ifdef  FASTRUNS
    if ( the_hdr->background )
    {
        /*
         * Find runs in each color individually, merging them as we go.
         */
        nrun = 0;               /* start out with no runs */
        /* Alpha channel first */
        if ( the_hdr->alpha )
            nrun = findruns( rows[-1], rowlen, 0, nrun, PBRUN );
        /* Now the color channels */
        for ( i = 0; i < the_hdr->ncolors; i++ )
            if ( bits[i] )
                nrun = findruns( rows[i], rowlen, the_hdr->bg_color[i],
                                 nrun, PBRUN );
    }
    else
    {
        PBRUN[0][0] = 0;
        PBRUN[0][1] = rowlen-1;
        nrun = 1;
    }
#else                           /* FASTRUNS */
    if (the_hdr->background)    /* find non-background runs */
    {
        j = 0;
        for (i=0; i<rowlen; i++)
            if (!same_color( i, rows, the_hdr->bg_color,
                             the_hdr->ncolors, bits ) ||
                (the_hdr->alpha && rows[-1][i] != 0))
            {
                if (j > 0 && i - PBRUN[j-1][1] <= 2)
                    j--;
                else
                    PBRUN[j][0] = i; /* start of run */
                for ( i++;
                      i < rowlen &&
                        ( !same_color( i, rows, the_hdr->bg_color,
                                         the_hdr->ncolors, bits ) ||
                          (the_hdr->alpha && rows[-1][i] != 0) );
                      i++)
                    ;                   /* find the end of this run */
                PBRUN[j][1] = i-1;    /* last in run */
                j++;
            }
        nrun = j;
    }
    else
    {
        PBRUN[0][0] = 0;
        PBRUN[0][1] = rowlen-1;
        nrun = 1;
    }
#endif                          /* FASTRUNS */
    /* One final pass merges runs with fewer than 4 intervening pixels
     * if the second run is longer than 255 pixels.  This is because
     * the startup cost for such a run is 4 bytes.
     */
    if ( nrun > 1 )
    {
        for ( i = nrun - 1; i > 0; i-- )
        {
            if ( PBRUN[i][1] - PBRUN[i][0] > 255 &&
                 PBRUN[i-1][1] + 4 > PBRUN[i][0] )
            {
                PBRUN[i-1][1] = PBRUN[i][1];
                for ( j = i; j < nrun - 1; j++ )
                {
                    PBRUN[j][0] = PBRUN[j+1][0];
                    PBRUN[j][1] = PBRUN[j+1][1];
                }
                nrun--;
            }
        }
    }

    if (nrun > 0)
    {
        if (the_hdr->priv.put.nblank > 0)
        {
            SkipBlankLines(the_hdr->priv.put.nblank);
            the_hdr->priv.put.nblank = 0;
        }
        for ( mask = (the_hdr->alpha ? -1 : 0);
              mask < the_hdr->ncolors;
              mask++)                   /* do all colors */
        {
            if ( ! bits[mask & 0xff] )
            {
                continue;
            }
            row = rows[mask];
            SetColor(mask);
            if (PBRUN[0][0] > 0)
            {
                SkipPixels(PBRUN[0][0], FALSE, FALSE);
            }
            for (j=0; j<nrun; j++)
            {
                state = DATA;
                dstart = PBRUN[j][0];
                dend = PBRUN[j][1];
                for (i=dstart; i<=dend; i++)
                {
                    switch(state)
                    {
                    case DATA:
                        if (i > dstart && runval == row[i])
                        {
                            /* 2 in a row may be a run. */
                            /* If odd data length, start with RUN1 */
                            if ( ((i - dstart) % 2) == 0)
                                state = RUN1;
                            else
                                state = RUN2;
                        }
                        else
                        {
                            runval = row[i];    /* maybe a run starts here? */
                            rstart = i;
                        }
                        break;

                    case RUN4:
                        if (runval == row[i])
                        {
                            /* If the following data might be longer
                             * than 255 pixels then look for 8 in a
                             * row, otherwise, 6 in a row is
                             * sufficient.  Fake this by skipping to
                             * state RUN5.
                             */
                            if ( dend - i > 255 )
                                state  = RUN5;  /* Need some more. */
                            else
                                state = RUN7;   /* Next one makes a run. */

                        }
                        else
                        {
                            state = DATA;       /* Nope, back to data */
                            runval = row[i];    /* but maybe a new run here? */
                            rstart = i;
                        }
                        break;

                    case RUN1:
                    case RUN2:
                    case RUN3:
                    case RUN5:
                    case RUN6:
                        if (runval == row[i])
                        {
                            /* Move to the next state. */
                            state++;
                        }
                        else
                        {
                            state = DATA;       /* Nope, back to data */
                            runval = row[i];    /* but maybe a new run here? */
                            rstart = i;
                        }
                        break;


                    case RUN7:
                        if (runval == row[i])   /* enough in a row for a run */
                        {
                            state = INRUN;
                            putdata(row + dstart, rstart - dstart);
#ifdef FASTRUNS
                            while ( row[++i] == runval && i <= dend)
                                ; /* not quite so good, but not bad */
                            i--;
#endif /* FASTRUNS */
                        }
                        else
                        {
                            state = DATA;               /* not a run, */
                            runval = row[i];    /* but may this starts one */
                            rstart = i;
                        }
                        break;

                    case INRUN:
                        if (runval != row[i])   /* if run out */
                        {
                            state = DATA;
                            putrun(runval, i - rstart, FALSE);
                            runval = row[i];    /* who knows, might be more */
                            rstart = i;
                            dstart = i; /* starting a new 'data' run */
                        }
                        break;
                    }
                }
                if (state == INRUN)
                    putrun(runval, i - rstart, TRUE);   /* last bit */
                else
                    putdata(row + dstart, i - dstart);

                if (j < nrun-1)
                    SkipPixels(
                            PBRUN[j+1][0] - dend - 1,
                            FALSE, state == INRUN);
                else
                {
                    if (rowlen - dend > 0)
                        SkipPixels(
                            rowlen - dend - 1,
                            TRUE, state == INRUN);
                }
            }

            if ( mask != the_hdr->ncolors - 1 )
                NewScanLine(FALSE);
        }
    }

    /* Increment to next scanline */
    the_hdr->priv.put.nblank++;

    /* flush every scanline */
    fflush( the_hdr->rle_file );
}



/*****************************************************************
 * TAG( rle_put_init )
 *
 * Initialize the header structure for writing scanlines.
 * Inputs:
 *      [None]
 * Outputs:
 *      the_hdr:        Private portions initialized for output.
 * Assumptions:
 *      [None]
 * Algorithm:
 *      [None]
 */
void
rle_put_init( the_hdr )
rle_hdr *the_hdr;
{
    the_hdr->dispatch = RUN_DISPATCH;

    if ( the_hdr->is_init != RLE_INIT_MAGIC )
    {
        the_hdr->cmd = "Urt";
        the_hdr->file_name = "some file";
    }
    the_hdr->priv.put.nblank = 0;       /* Reinit static vars */
    /* Would like to be able to free previously allocated storage,
     * but can't count on a non-NULL value being a valid pointer.
     */
    PBRUN = NULL;
    the_hdr->priv.put.fileptr = 0;

    /* Only save alpha if alpha AND alpha channel bit are set. */
    if ( the_hdr->alpha )
        the_hdr->alpha = (RLE_BIT( *the_hdr, -1 ) != 0);
    else
        RLE_CLR_BIT( *the_hdr, -1 );
}



/*****************************************************************
 * TAG( rle_put_setup )
 *
 * Initialize for writing RLE, and write header to output file.
 * Inputs:
 *      the_hdr:        Describes output image.
 * Outputs:
 *      the_hdr:        Initialized.
 * Assumptions:
 *      Lots of them.
 * Algorithm:
 *      [None]
 */
void
rle_put_setup( the_hdr )
rle_hdr * the_hdr;
{
    rle_put_init( the_hdr );
    the_hdr->img_num++;         /* Count output images. */
    Setup();
}



void
DefaultBlockHook(rle_hdr * the_hdr)
{
                                        /* Do nothing */
}



/*****************************************************************
 * TAG( rle_puteof )
 * Write an EOF code into the output file.
 */
void
rle_puteof( the_hdr )
rle_hdr * the_hdr;
{
    /* Don't puteof twice. */
    if ( the_hdr->dispatch == NO_DISPATCH )
        return;
    PutEof();
    fflush( the_hdr->rle_file );
    /* Free storage allocated by rle_put_init. */
    if ( PBRUN != NULL )
    {
        free( PBRUN );
        PBRUN = NULL;
    }
    /* Signal that puteof has been called. */
    the_hdr->dispatch = NO_DISPATCH;
}



#ifndef FASTRUNS
/*****************************************************************
 * TAG( same_color )
 *
 * Determine if the color at the given index position in the scan rows
 * is the same as the background color.
 * Inputs:
 *      index:      Index to the pixel position in each row.
 *      rows:       array of pointers to the scanlines
 *      bg_color:   the background color
 *      ncolors:    number of color elements/pixel
 * Outputs:
 *      TRUE if the color at row[*][i] is the same as bg_color[*].
 * Assumptions:
 *      [None]
 * Algorithm:
 *      [None]
 */
static int
same_color( index, rows, bg_color, ncolors, bits )
rle_pixel *rows[];
int bg_color[];
char *bits;
{
    int i;

    for ( i = 0; i < ncolors; i++, bits++ )
        if ( *bits &&
             rows[i][index] != bg_color[i] )
            return 0;
    return 1;                           /* all the same */
}
#endif /* !FASTRUNS */