Blame |
Last modification |
View Log
| RSS feed
/*
* fs2.c --
*
* Procedures dealing with Floyd-Steinberg dithering with 2 error
* values propagated.
*
*/
/*
* Copyright (c) 1995 The Regents of the University of California.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#include "video.h"
#include "dither.h"
#include "fs2.h"
#include "proto.h"
#ifdef __STDC__
#include <stdlib.h>
#else
#include <malloc.h> /* member of stdlib.h included in header.h */
#endif
/* Structures for precomputed error propogation values. */
static FS2DithVal lum_index
[256];
static FS2DithVal cr_index
[256];
static FS2DithVal cb_index
[256];
/*
*--------------------------------------------------------------
*
* InitFS2Dither --
*
* Initializes structures for precomputed 2 error f-s dithering.
* The value field of the structure contains the pixel component
* of the particular channel in question. Thus the addition of
* the value field of a structure in the luminance index, a
* structure in the Cr index, and a structure in the Cb index will
* yeild a color number. This color number can then be transformed
* into a pixel value to be displayed. Each channel can then be
* processed (i.e. dithered) separately, with the results being
* added up and remapped to yield a final pixel value.
*
* Results:
* None.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
void InitFS2Dither
()
{
int i
;
/* For each possible pixel value, precompute propogated error and
store in array.
*/
for (i
=0; i
<256; i
++) {
lum_index
[i
].
value = (i
* LUM_RANGE
) / 256;
lum_index
[i
].
e1 = (i
-lum_values
[lum_index
[i
].
value]) / 2;
lum_index
[i
].
e3 = (i
- lum_values
[lum_index
[i
].
value]) - lum_index
[i
].
e1;
lum_index
[i
].
value *= LUM_BASE
;
cr_index
[i
].
value = (i
* CR_RANGE
) / 256;
cr_index
[i
].
e1 = (i
- cr_values
[cr_index
[i
].
value]) / 2;
cr_index
[i
].
e3 = (i
- cr_values
[cr_index
[i
].
value]) - cr_index
[i
].
e1 ;
cr_index
[i
].
value *= CR_BASE
;
cb_index
[i
].
value = (i
* CB_RANGE
) / 256;
cb_index
[i
].
e1 = (i
- cb_values
[cb_index
[i
].
value]) / 2;
cb_index
[i
].
e3 = (i
- cb_values
[cb_index
[i
].
value]) - cb_index
[i
].
e1;
cb_index
[i
].
value *= CB_BASE
;
}
}
/*
*--------------------------------------------------------------
*
* DitherImage --
*
* Converts lum, cr, cb image planes into fixed colormap
* space.
*
* Results:
* the display plane is replaced by 8-bit colormap space
* image.
*
* Side effects:
* Hopefully, none.
*
*--------------------------------------------------------------
*/
void FS2DitherImage
(lum
, cr
, cb
, disp
, rows
, cols
)
unsigned char *lum
, *cr
, *cb
, *disp
;
int rows
, cols
;
{
static char *cur_row_error
, *next_row_error
;
static int first
= 1;
char *cur_row_err_mark
, *next_row_err_mark
;
char *temp
;
int i
, j
, pixsum
, c_cols
;
unsigned char *cur_row
, *channel
, *dest_row
;
FS2DithVal
*chan_index
;
/* Allocate error arrays. */
if (first
) {
cur_row_error
= (char *) malloc((unsigned int) cols
+2);
next_row_error
= (char *) malloc((unsigned int) cols
+2);
first
= 0;
}
/* Initialize error arrays. */
memset(cur_row_error
, 0, cols
+2);
memset(next_row_error
, 0, cols
+2);
/* Use luminance values first. */
/* For each two rows, do... */
for(i
=0; i
<rows
; i
+=2) {
/* Establish pointer to current source and destination rows. */
cur_row
= lum
+ (i
*cols
);
dest_row
= disp
+ (i
*cols
);
/* Establish pointers to error arrays. */
cur_row_err_mark
= cur_row_error
+ 1;
next_row_err_mark
= next_row_error
+ 1;
/* For each column within first row do... */
for (j
=0; j
<cols
; j
++) {
/* Calculate pixel value with error. */
pixsum
= *cur_row
+ *cur_row_err_mark
;
/* Bounds check. */
if (pixsum
< 0) pixsum
= 0;
else if (pixsum
> 255) pixsum
= 255;
/* Establish dest value, propogate errors. */
*dest_row
= lum_index
[pixsum
].
value;
*(cur_row_err_mark
+1) += lum_index
[pixsum
].
e1;
*next_row_err_mark
+= lum_index
[pixsum
].
e3;
/* Advance pointers. */
cur_row
++;
dest_row
++;
cur_row_err_mark
++;
next_row_err_mark
++;
}
/* Switch error arrays, so next row errors are now current row errors, and
vice versa.
*/
temp
= cur_row_error
;
cur_row_error
= next_row_error
;
next_row_error
= temp
;
/* Reset next row errors. */
memset(next_row_error
, 0, cols
+2);
/* Establish pointers for second row. This one will be processed right to
left to establish serpantine motion.
*/
cur_row
+= cols
-1;
dest_row
+= cols
-1;
cur_row_err_mark
= cur_row_error
+ cols
;
next_row_err_mark
= next_row_error
+ cols
;
/* Process each column... */
for (j
=0; j
<cols
; j
++) {
pixsum
= *cur_row
+ *cur_row_err_mark
;
if (pixsum
< 0) pixsum
= 0;
else if (pixsum
> 255) pixsum
= 255;
*dest_row
= lum_index
[pixsum
].
value;
*(cur_row_err_mark
-1) += lum_index
[pixsum
].
e1;
*next_row_err_mark
+= lum_index
[pixsum
].
e3;
cur_row
--;
dest_row
--;
cur_row_err_mark
--;
next_row_err_mark
--;
}
/* Switch error arrays. */
temp
= cur_row_error
;
cur_row_error
= next_row_error
;
next_row_error
= temp
;
/* Reset next row errors. */
memset(next_row_error
, 0, cols
+2);
}
/* Reset error arrays. */
memset(cur_row_error
, 0, cols
+2);
/* Establish column length divided by two. */
c_cols
= cols
>> 1;
/* Set channel to Cr. Use Cr index. */
channel
= cr
;
chan_index
= cr_index
;
repeat
:
/* Process each row of chrominance data... */
for (i
=0; i
< rows
; i
+=2) {
/* Establish pointers. */
cur_row
= channel
+ ((i
>>1)*c_cols
);
dest_row
= disp
+ (i
*cols
);
cur_row_err_mark
= cur_row_error
+1;
next_row_err_mark
= next_row_error
+1;
/* For each column in row... */
for (j
=0; j
<cols
; j
++) {
int p_val
;
/* Get pixel value as twos bit complement. */
p_val
= *cur_row
;
/* Add error term. */
pixsum
= *cur_row_err_mark
+ p_val
;
/* Bounds check. */
if (pixsum
< 0) pixsum
= 0;
else if (pixsum
> 255) pixsum
= 255;
/* Increment dest value. */
*dest_row
+= chan_index
[pixsum
].
value;
/* Propogate error values. */
*(cur_row_err_mark
+1) += chan_index
[pixsum
].
e1;
*next_row_err_mark
+= chan_index
[pixsum
].
e3;
/* If count is odd, advance source pointer (Cr and Cb channels are 2:1
subsampled.
*/
if (j
&1) cur_row
++;
/* Advance destination and error pointers. */
dest_row
++;
cur_row_err_mark
++;
next_row_err_mark
++;
}
/* Switch error arrays. */
temp
= cur_row_error
;
cur_row_error
= next_row_error
;
next_row_error
= temp
;
/* Reset next row errors. */
memset(next_row_error
, 0, cols
+2);
/* Re-establish pointers. */
cur_row
+= c_cols
-1;
dest_row
+= cols
-1;
cur_row_err_mark
= cur_row_error
+cols
;
next_row_err_mark
= next_row_error
+cols
;
/* Process second row right to left. */
for (j
=0; j
<cols
; j
++) {
int p_val
;
/* Get source value as twos bit complement. */
p_val
= *cur_row
;
/* Add error. */
pixsum
= *cur_row_err_mark
+ p_val
;
/* Bounds check. */
if (pixsum
< 0) pixsum
= 0;
else if (pixsum
> 255) pixsum
= 255;
/* Increment dest value. */
*dest_row
+= chan_index
[pixsum
].
value;
/* Propogate errors. */
*(cur_row_err_mark
-1) += chan_index
[pixsum
].
e1;
*next_row_err_mark
+= chan_index
[pixsum
].
e3;
/* If column counters is odd, decrement source pointer. */
if (j
&1) cur_row
--;
/* Decrement dest and error pointers. */
dest_row
--;
cur_row_err_mark
--;
next_row_err_mark
--;
}
/* Switch error arrays. */
temp
= cur_row_error
;
cur_row_error
= next_row_error
;
next_row_error
= temp
;
/* Reinitialize next row errors. */
memset(next_row_error
, 0, cols
+2);
}
/* If Cr channel completed, set channel to Cb and Cb index and repeat. */
if (channel
== cr
) {
channel
= cb
;
chan_index
= cb_index
;
memset(cur_row_error
, 0, cols
+2);
goto repeat
;
}
/* Establish pointer to start of display frame. */
dest_row
= disp
;
/* Transform all display values to pixel values. */
for (i
=0; i
<rows
; i
++) {
for (j
=0; j
<cols
; j
++) {
*dest_row
= pixel
[*dest_row
];
dest_row
++;
}
}
}