home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
GRAPHICS
/
mgif.lzh
/
GIF
/
flicker.c
next >
Wrap
Text File
|
1991-12-02
|
21KB
|
1,016 lines
#undef NO_INVERT /* def if black is 0xff */
#define FLICKER /* def to test flicker thing. else just f-s */
#define NEW_RANDOM /* def to new code to add random noise */
#define SHOW_SCREENS /* def to display each screen slowly first */
#define AUTO_SHOW /* def to automatically cycle then proceed */
/*
* this is for screen-switching on mono to simulate grayscale. it
* uses a combination of floyd-steinberg and simulate "planes" which
* get displayed each vblank. the image it works from should be
* grayscale, i.e. each pixel is represented by an 8-bit (0 to 255)
* intensity. since GIF images come with a color table, it is first
* required to map each GIF pixel to the color table which has been
* first changed to grayscale, either by averaging the rgb values
* or by using the NTSC intensity equation.
*
* note that i think the f-s algorithm employed here sweeps each row
* left to right rather than alternating directions. that is why there
* is spurious info in areas otherwise supposed to be white.
*
* also, all points where chars are used in a calculation, they MUST
* be unsigned.
*/
/* written by Klaus Pedersen (micro@imada.dk) */
/* modified by Bill Rosenkranz (rosenkra@convex.com) */
static char *sccsid = "@(#) flicker.c 1.1 91/6/3 rosenkra\0\0 ";
#include <stdio.h>
/* #include <osbind.h> */
#include "gpdefs.h"
#include <modes.h>
#ifdef LOCAL
#undef LOCAL
#endif
#ifdef GLOBAL
#undef GLOBAL
#endif
#define LOCAL /*static*/
#define GLOBAL
#define reg_t register
#define LEVELS 4
#define NUMERRS 1300
#define SCRNSPACE 48256L
/*
* these are the thresholds for quantizing. 0,85,170,255 seem best
*/
LOCAL int _Levels[LEVELS] = { 0, 85, 170, 255};
LOCAL int _Width; /* image size */
LOCAL int _Hight;
LOCAL int _Beta; /* for the Laplace filter */
LOCAL int _MaxRandom; /* for adding random noise */
LOCAL int *_Err1, /* the error propagation arrays */
*_Err2;
LOCAL int _Err1Array[NUMERRS],
_Err2Array[NUMERRS];
LOCAL short _ScrnBuf[SCRNSPACE], /* enuf for 3 screens... */
*_Scrn,
*_Scrn1,
*_Scrn2,
*_Scrn3;
/*
* local functions
*/
LOCAL void _Conv2Gray ();
LOCAL void _SimpConv2Gray ();
LOCAL void _Conv2Grayfile ();
LOCAL int _GenConvPix ();
LOCAL int _Laplace ();
LOCAL void _ClearErr ();
LOCAL int _Rand ();
LOCAL int _Delay ();
/*------------------------------*/
/* flicker */
/*------------------------------*/
GLOBAL int flicker (pimg, width, hight, beta, maxrandom, opt)
unsigned char *pimg; /* -> raster image of intensities */
int width; /* width, pixels */
int hight; /* height, pixels */
int beta; /* for laplace filter */
int maxrandom; /* for random noise */
int opt; /* 0=no flicker, 1=flicker, 2=extended */
{
/*
* this is the main entry point. complete grayscale raster image
* comes from pimg with height and width specified. if beta != 0,
* do Laplace filter with that beta. if maxrandom != 0, add some
* random noise with that value (see below).
*/
register long ii;
/*
* set our globals
*/
_Hight = hight; /* its height... */
_Width = width; /* ...and width */
_Beta = beta; /* Laplace filter coefficient (fixedpoint: */
/* 1=0.25, 2=0.50, 3=0.75... (Try 4) */
_MaxRandom = maxrandom; /* amount of random noise on threshold value */
/* (0 - 255) for 0% to 100% noise. (Try 10) */
for (ii = 0L; ii < SCRNSPACE; ii++) /* clear screens! */
_ScrnBuf[ii] = 0;
#if 0
printf ("\n\nwidth,height,beta,maxrandom = %d %d %d %d\n",
_Width, _Hight, _Beta, _MaxRandom);
printf ("pimg = %ld\nany key...\n", (long) pimg);
_Delay (4000);
if (Bconstat (2))
{
while (Bconstat (2))
Bconin (2);
exit (1);
}
#endif
/*
* do it!
*/
if (opt) {
if (opt == 1)
_Conv2Gray (pimg);
else
_Conv2Grayfile (pimg);
} else
_SimpConv2Gray (pimg);
return;
}
/*------------------------------*/
/* _Conv2Gray */
/*------------------------------*/
LOCAL void _Conv2Gray (pic)
unsigned char *pic;
{
char c;
register int *ps;
register int *pd;
long ii;
int i;
int x,
y;
int w,
h;
long yoffset;
int *TmpErr,
q,
rover1,
rover2;
unsigned short pix, spix;
unsigned char *p;
char *old1;
char *old2;
/*
* first clear the error arrays
*/
_ClearErr ();
/*
* save old screen info, then set logbase to old physbase and
* physbase to first screen buffer. we *should* never see logbase
* in the displayed image since we never display logbase. make
* sure screen is aligned on 256-byte boundary.
*/
old2 = ScreenAddress(0, 0);
old1 = old2;
_Scrn = (int *) (((long) _ScrnBuf + 256L) & 0xFFFFFF00L);
for (ii = 0L; ii < 30720; ii++)
{
old1[ii] = 0;
old2[ii] = 0;
}
/*
* we set physbase to our buffer already so _Scrn1 points to it.
* set the other screen pointers. _Scrn is a buffer large enuf
* to hold all 3 screens
*/
_Scrn1 = _Scrn;
_Scrn2 = _Scrn1 + 16000L;
_Scrn3 = _Scrn2 + 16000L;
/*
* initialize some things. yoffset positions us into each screen
* which are int arrays. each scan line in the screen is thus
* 40 words long.
*/
yoffset = 0L;
rover1 = 0;
rover2 = 0;
spix = 0;
/*
* loop over all rows in image
*/
h = (_Hight > 400) ? 400 : _Hight;
for (y = 1; y < h - 1; y++)
{
/*
* set ptr to this pixel row
*/
p = pic + ((long) y * (long) _Width);
/*
* loop over all pixels in the row
*/
w = (_Width > 640) ? 640 : _Width;
for (x = 0; x < w - 1; x++)
{
pix = (0x8000 >> (x & 0x000f));
/*
* get new pixel value, with f-s error. this is
* actually an index into the _Levels array.
*/
if (y & 1)
q = _GenConvPix (p, (int) x, 0);
else
q = _GenConvPix (p, (int) x, 1);
#ifdef 0
printf (" y=%3d, x=%3d, p+x=%6u, q=%d, rover1,2=%d,%d\n",
(int)y, (int)x,
(unsigned int)(*(unsigned char *)(x+pic+(y*_Width))),
(int)q,
(int)rover1, (int)rover2);
fflush (stdout);
#endif
/*
* set screen pixels based on this index
*/
if (q == 1)
{
switch (rover1)
{
case 0:
_Scrn1[yoffset + (x >> 4)] |= pix;
rover1 = 1;
rover2 = 1;
break;
case 1:
_Scrn2[yoffset + (x >> 4)] |= pix;
rover1 = 2;
rover2 = 2;
break;
case 2:
_Scrn3[yoffset + (x >> 4)] |= pix;
rover1 = 0;
rover2 = 0;
break;
}
}
else if (q == 2)
{
switch (rover2)
{
case 0:
_Scrn1[yoffset + (x >> 4)] |= pix;
_Scrn2[yoffset + (x >> 4)] |= pix;
rover2 = 1;
rover1 = 2;
break;
case 1:
_Scrn2[yoffset + (x >> 4)] |= pix;
_Scrn3[yoffset + (x >> 4)] |= pix;
rover2 = 2;
rover1 = 0;
break;
case 2:
_Scrn1[yoffset + (x >> 4)] |= pix;
_Scrn3[yoffset + (x >> 4)] |= pix;
rover2 = 0;
rover1 = 1;
break;
}
}
else if (q == 3)
{
_Scrn1[yoffset + (x >> 4)] |= pix;
_Scrn2[yoffset + (x >> 4)] |= pix;
_Scrn3[yoffset + (x >> 4)] |= pix;
}
if (q > 1)
spix |= pix;
if ((x & 15) == 15 && y < ScreenLineCount) {
*ScreenAddress(x, y-1) = spix;
spix = 0;
}
}
/*
* get ready for next row by incrementing offset into
* the screens (640 dots = 40 words)
*/
yoffset += 40;
/*
* exchange error arrays
*/
TmpErr = _Err1;
_Err1 = _Err2;
_Err2 = TmpErr;
#if 0
/*
* check for early withdrawal...
*/
if (Bconstat (2))
{
while (Bconstat (2))
Bconin (2);
Setscreen (old1, old2, -1);
return;
}
#endif
}
/*
* with that all done, now we can display the screens. only
* phys screen is displayed (at next vblank). the Vsync waits
* for the vblank. we display screens 1,2,3. any char stops the
* loop. first just show the screens with delay, then do the loop
*/
#ifdef SHOW_SCREENS
do
{ register int i;
register long *q, *z;
for(i=7680, q=_Scrn1, z=old2; i>0; --i) {
*z++ = *q++;
}
tsleep(20);
for(i=7680, q=_Scrn2, z=old2; i>0; --i) {
*z++ = *q++;
}
tsleep(20);
for(i=7680, q=_Scrn3, z=old2; i>0; --i) {
*z++ = *q++;
}
tsleep(20);
} while (_gs_rdy(0) <= 0);
/* clear any key presses */
while (_gs_rdy(0) >= 0)
read(0, &c, 1);
#endif /*SHOW_SCREENS*/
do
{ register int i;
register long *q, *z;
for(i=7680, q=_Scrn1, z=old2; i>0; --i) {
*z++ = *q++;
}
_Delay(4);
for(i=7680, q=_Scrn2, z=old2; i>0; --i) {
*z++ = *q++;
}
_Delay(4);
for(i=7680, q=_Scrn3, z=old2; i>0; --i) {
*z++ = *q++;
}
_Delay(4);
} while (_gs_rdy(0) <= 0);
/*
* clear any waiting keys...
*/
/* clear any key presses */
while (_gs_rdy(0) > 0)
read(0, &c, 1);
}
typedef unsigned short word;
typedef struct {
word version;
word header_length;
word color_planes;
word pattern_length;
word pixel_width;
word pixel_height;
word line_width;
word img_height;
} GemHeader;
elemcpy(e1, e2, groesse)
register char *e1;
register char *e2;
register int groesse;
{
if ((groesse & 1) == 0) {
while ((groesse-=4) >= 0) {
*((long *) e1)++ = *((long *) e2)++;
}
if ((groesse += 4) != 0) {
if ((groesse-=2) > 0) {
*((short *) e1) = *((short *) e2);
e1[2] = e2[2];
} else if (groesse == 0) {
*((short *) e1) = *((short *) e2);
} else {
*e1 = *e2;
}
}
} else {
while (--groesse >= 0) {
*e1++ = *e2++;
}
}
}
/*------------------------------*/
/* _Conv2Grayfile */
/*------------------------------*/
LOCAL void _Conv2Grayfile (pic)
unsigned char *pic;
{
/*
* I have also made a version of "Convert2Gray", that don't use
* flicker - this allows the pictures to be saved, and used in windows,
* and honest - the build-in test-picture looks as good, or even better,
* without the flicker... (this is not true for any *real* pictures
* that I have tested the program with). Here it is :
*/
static unsigned char sl[4*3200];
short line[255];
short line1[255];
GemHeader gh;
long x,
y,
wordpos;
int *TmpErr;
short i, j, w, h, linelen, schwelle;
FILE *fa;
char c;
unsigned short pix, pix1;
unsigned char *p, *p1, *slp;
write(1, "\x1bY!!Schwelle (1,2,3, G)raustufen) : ", 36);
do {
read(0, &c, 1);
c = tolower(c);
} while ((c < '1' || c > '3') && c != 'g');
if (c != 'g')
schwelle = c - '0';
else
schwelle = -1;
if ((fa = fopen("ausgabe.img", "w")) == NULL) {
printf("Cannot open 'ausgabe.img'.\n");
fflush(stdout);
while (_gs_rdy(0) > 0)
read(0, &c, 1);
while (_gs_rdy(0) <= 0)
;
return;
}
fa->_bufsiz = 16384;
w = _Width;
h = _Hight;
/*
* first clear the error arrays
*/
_Width = 2*w;
_Hight = 2*h;
_Levels[1] = 120;
_Levels[2] = 220;
_ClearErr ();
/*
* loop over all rows in image
*/
gh.version = 1;
gh.header_length = 8;
gh.color_planes = 1;
gh.pattern_length = 2;
gh.pixel_width = 250;
gh.pixel_height = 250;
if (schwelle > 0) {
gh.line_width = _Width;
gh.img_height = _Hight;
} else {
gh.line_width = 2*_Width;
gh.img_height = 2*_Hight;
}
fwrite(&gh, sizeof(gh), 1, fa);
for (i=sizeof(sl)-1; i >=0; --i)
sl[i] = 0;
if (schwelle > 0) {
linelen = _Width / 8;
if ((_Width & 7) != 0)
linelen++;
} else {
linelen = _Width / 4;
if ((_Width & 3) != 0)
linelen++;
}
for (y = 1; y < h - 1; y++)
{
/*
* set ptr to this pixel row
*/
p = pic + y * w;
p1 = pic + (y+1) * w;
slp = sl+2*_Width;
for(i = 0; i <= w - 1; i++) {
slp[i*2] = p[i];
}
for(i = 0; i <= w - 2; i++) {
slp[i*2+1] = (p[i]+p[i+1])/2;
}
slp[2*w-1] = p[w-1];
slp += _Width;
for(i = 0; i < w; i++) {
slp[i*2] = (p[i]+p1[i])/2;
}
for(i = 0; i <= w - 2; i++) {
slp[i*2+1] = (p[i]+p[i+1]+p1[i]+p1[i+1])/4;
}
slp[2*w-1] = p[w-1];
/*
* loop over all pixels in the row _Width-1 wg. Laplace
*/
for(j = 0, slp = sl+_Width; j < 2; j++, slp += _Width) {
wordpos = 1;
pix = 0;
pix1 = 0;
if (schwelle > 0) {
for (x = 0L; x < _Width-1; x++)
{
pix <<= 1;
if (_GenConvPix (slp, x, 0) >= schwelle)
pix++;
if ((x & 0xf) == 0xf) {
line[wordpos++] = pix;
if (wordpos < 40) {
*ScreenAddress((wordpos-2) * 16,
(2*y+j) % ScreenLineCount) = pix;
}
}
}
} else {
for (x = 0L; x < _Width-1; x++)
{
i = _GenConvPix (slp, x, 0);
pix <<= 2;
pix1 <<= 2;
switch (i) {
case 1:
pix |= 2;
case 0:
break;
case 3:
pix |= 1;
pix1 |= 2;
case 2:
pix |= 2;
pix1 |= 1;
break;
}
if ((x & 7) == 7) {
line1[wordpos] = pix1;
line[wordpos++] = pix;
if (wordpos < 40) {
*ScreenAddress((wordpos-2) * 16,
(4*y+2*j) % ScreenLineCount) = pix;
*ScreenAddress((wordpos-2) * 16,
(4*y+2*j+1) % ScreenLineCount) = pix1;
}
}
}
}
line[wordpos] = pix;
line1[wordpos] = pix1;
if (linelen < 255) {
line[0] = 0x8000+linelen;
fwrite(line, linelen+2, 1, fa);
if (schwelle < 0) {
line1[0] = 0x8000+linelen;
fwrite(line1, linelen+2, 1, fa);
}
} else {
line[0] = 0x8000+254;
fwrite(line, 256, 1, fa);
line[127] = 0x8000+linelen-254;
fwrite(line+127, linelen+2-254, 1, fa);
if (schwelle < 0) {
line1[0] = 0x8000+254;
fwrite(line1, 256, 1, fa);
line1[127] = 0x8000+linelen-254;
fwrite(line1+127, linelen+2-254, 1, fa);
}
}
/*
* exchange error arrays
*/
TmpErr = _Err1;
_Err1 = _Err2;
_Err2 = TmpErr;
}
elemcpy(sl, sl+2*_Width, 2*_Width);
}
fclose(fa);
_Width = w;
_Hight = h;
_Levels[0] = 0;
_Levels[1] = 85;
_Levels[2] = 170;
_Levels[3] = 255;
/* clear any key presses */
while (_gs_rdy(0) >= 0)
read(0, &c, 1);
}
/*------------------------------*/
/* _SimpConv2Gray */
/*------------------------------*/
LOCAL void _SimpConv2Gray (pic)
unsigned char *pic;
{
/*
* I have also made a version of "Convert2Gray", that don't use
* flicker - this allows the pictures to be saved, and used in windows,
* and honest - the build-in test-picture looks as good, or even better,
* without the flicker... (this is not true for any *real* pictures
* that I have tested the program with). Here it is :
*/
long x,
y,
yoffset,
wordpos;
int *TmpErr;
char c;
unsigned int pix;
unsigned char *p;
/*
* first clear the error arrays
*/
_ClearErr ();
/*
* there is only one screen. we use existing physbase
*/
_Scrn1 = ScreenAddress(0, 0);
/*
* yoffset positions us into the screen which is an int arrays.
* each scan line in the screen is thus 40 words long.
*/
yoffset = 0;
pix = 0;
/*
* loop over all rows in image
*/
for (y = 1; y < _Hight - 1; y++)
{
/*
* set ptr to this pixel row
*/
p = pic + y * _Width;
wordpos = yoffset;
/*
* loop over all pixels in the row
*/
for (x = 0L; x < _Width - 1; x++)
{
pix <<= 1;
if (_GenConvPix (p, x, 0))
pix++;
if ((x & 0xf) == 0xf)
_Scrn1[wordpos++] = pix;
}
/*
* get ready for next row by incrementing offset into
* the screens (640 dots = 40 words)
*/
yoffset += 40;
/*
* exchange error arrays
*/
TmpErr = _Err1;
_Err1 = _Err2;
_Err2 = TmpErr;
}
/*
* displaying the image consists of waiting for a char since we
* already drew it to the screen
*/
while (_gs_rdy(0) < 0)
;
/* clear any key presses */
while (_gs_rdy(0) >= 0)
read(0, &c, 1);
}
/*------------------------------*/
/* _GenConvPix */
/*------------------------------*/
LOCAL int _GenConvPix (pic, x, opt)
unsigned char *pic; /* -> image pixel array */
int x; /* the current pixel */
int opt; /* 0=l to r, 1=r to l */
{
/*
* This rutine converts a point in the 'real' image to
* a point in the screen image. The procedure uses error-
* diffusion to determine the state of the new pixel.
* The Error filter is that of Floyd & Steinberg :
*
* / 1 5 3 \ /
* \ 7 X / / 16
*
* This rutine uses a table of levels to make the convertion.
*
* It returns the index for the pixel from the quantizing Level
* array, i.e. the new pixel intensity is Level[i].
*/
int p,
e,
i,
err,
lev;
#ifdef NEW_RANDOM
/*
The noise is here added to the picture, this means that the noise
isn't cancled out again, and that is NOT intended, the noise should
only act as some kind of *catalysator* (verb. is stolen from
chemistry - something that start a reaction). The correct way to add
noise is :
*/
/*
* first do the weighted average. _Err1 is the current line,
* _Err2 is the previous line.
*/
if (x == 0)
{
# ifdef NO_INVERT
/*!!! next line was actually: */
p = *pic;
/*!!! but that inverted the image. it was assumed that 0xff was black.
we insist that black is 0x00 (low intensity) */
# else
p = 255 - *pic;
# endif
}
else if (_Beta)
{
p = ((7*_Err1[x - 1]
+ _Err2[x - 1] + 5*_Err2[x] + 3*_Err2[x + 1]) >> 4)
+ _Laplace (pic + x);
}
else
{
p = ((7*_Err1[x - 1]
+ _Err2[x - 1] + 5*_Err2[x] + 3*_Err2[x + 1]) >> 4)
# ifdef NO_INVERT
+ *(pic + x);
# else
+ (255 - *(pic + x));
# endif
}
/*
* now calculate the residual errors
*/
err = NUMERRS;
for (i = 0; i < LEVELS; i++)
{
if (_MaxRandom)
e = p + _Rand (_MaxRandom) - _Levels[i];
else
e = p - _Levels[i];
if (e < 0)
e = -e;
if (e < err)
{
err = e;
lev = i;
}
}
_Err1[x] = p - _Levels[lev];
#else /*!NEW_RANDOM*/
if (x == 0)
{
# ifdef NO_INVERT
p = *pic;
# else
p = 255 - *pic;
# endif
}
else if (_Beta)
{
if (_MaxRandom)
p = ((7 * _Err1[x - 1] + _Err2[x - 1]
+ 5 * _Err2[x] + 3 * _Err2[x + 1]) >> 4)
+ _Laplace (pic + x) + _Rand (_MaxRandom);
else
p = ((7 * _Err1[x - 1] + _Err2[x - 1]
+ 5 * _Err2[x] + 3 * _Err2[x + 1]) >> 4)
+ _Laplace (pic + x);
}
else
{
if (_MaxRandom)
{
p = ((7 * _Err1[x - 1] + _Err2[x - 1]
+ 5 * _Err2[x] + 3 * _Err2[x + 1]) >> 4)
# ifdef NO_INVERT
+ *(pic + x) + _Rand (_MaxRandom);
# else
+ (255 - *(pic + x)) + _Rand (_MaxRandom);
# endif
}
else
{
p = ((7 * _Err1[x - 1] + _Err2[x - 1]
+ 5 * _Err2[x] + 3 * _Err2[x + 1]) >> 4)
# ifdef NO_INVERT
+ *(pic + x);
# else
+ (255 - *(pic + x));
# endif
}
}
err = NUMERRS;
for (i = 0; i < LEVELS; i++)
{
e = p - _Levels[i];
if (e < 0)
e = -e;
if (e < err)
{
err = e;
lev = i;
}
}
_Err1[x] = p - _Levels[lev];
#endif /*NEW_RANDOM*/
return (lev);
}
/*------------------------------*/
/* _Laplace */
/*------------------------------*/
LOCAL int _Laplace (Pic)
unsigned char *Pic;
{
/*
* this is a Laplacian filter, a simple edge detector/enhancer.
*
* -1
* -1 4 -1
* -1
*/
register int lp;
/*
* if _Beta 0, formula reduces to just the pixel
*/
#ifdef NO_INVERT
if (_Beta == 0)
return ((int) *Pic);
lp = (*Pic << 2) /* this pixel */
- *(Pic - 1) - *(Pic + 1) /* left/right */
- *(Pic + _Width) - *(Pic - _Width); /* top/bottom */
lp = ((_Beta * lp) >> 2) + *Pic;
#else
if (_Beta == 0)
return ((int) (255 - *Pic));
lp = (255 - *Pic) << 2; /* this pixel */
lp -= (255 - *(Pic - 1)); /* left/right */
lp -= (255 - *(Pic + 1));
lp -= (255 - *(Pic + _Width)); /* top/bottom */
lp -= (255 - *(Pic - _Width));
lp = ((_Beta * lp) >> 2);
lp += (255 - *Pic); /* self */
#endif
/*
* check limits and force result to 8 bits
*/
if (lp < 0)
lp = 0;
if (lp > 255)
lp = 255;
return (lp);
}
/*------------------------------*/
/* _ClearErr */
/*------------------------------*/
LOCAL void _ClearErr ()
{
int i;
_Err1 = _Err1Array;
_Err2 = _Err2Array;
for (i = 0; i < _Width; i++)
_Err1Array[i] = _Err2Array[i] = 0;
}
/*------------------------------*/
/* _Rand */
/*------------------------------*/
LOCAL int _Rand (mx)
int mx;
{
/*
* returns random number 0 <= x < mx
*/
static long Seed = 0;
if (mx == 0)
return (0);
/* Seed = (5 * Seed + 37);*/
Seed += (Seed << 2) + 37; /* same thing but faster */
/* Seed &= 0x00ffffffL;*/ /* should prevent overflow */
return (((int) Seed) % mx);
}
/*------------------------------*/
/* _Delay */
/*------------------------------*/
LOCAL _Delay (ms)
int ms;
{
/*
* delay with 1 ms granularity on normal ST
*/
int i;
for ( ; ms > 0; ms--) for (i = 192; i > 0; i--) ;
}