[古董]通过误差扩散抖动在单显上显示 256 色 GIF 图片的程序

前言:因为在 CSDN 碰到有人问我关于汇编的问题,我因为忘记得差不多了, 只好去找一些以前写的程序来看,结果找出一大堆古董级的程序,我看其中有些可能还有一点价值, 所以就整理了一些拿得出手的东东出来,看了不要笑啊。这些程序我除了对其中一些提示信息字符串和一些注释作了改动外, 未修改任何程序代码部分,尽量保持原样。

这是一个用来在单显(是指那种双频单显--Hercules,还不是 VGA 单显) 上显示 256 色以内的 GIF 图片的程序。至于当年我为什么会写这么一个程序,我不说大家也猜得到啦^_^。 当然,用阀值法把彩色转为单色是很简单的,但效果也是很差的,图像细节严重丢失,用处不大, 所以我在这里用了误差扩散抖动算法作了一些处理,虽然用的是最简单的 Floyd_Steinberg 算法, 但效果已经相当好了。

这个程序最有价值的部分应该算是 GIF 解码的部分和 Floyd_Steinberg 抖动的部分。 为了改善性能,我将 F/S 抖动部分用汇编来写。此外还有就是 PCX 编码/解码的部分。另外, 还有一些为了进一步改善效果而加入的技术,如双向扫描抖动和为提高对比度而加入的灰度映射, 不过后者会造成细节的少量损失。

本程序完成于96年9月到97年3月

/*
Show a monochrome or colour GIF file in a Hercules display card.
Sep.15-96, Oct.21i, Feb.04-97, Mar.02i, Mar.04, Mar.08, Mar.30
*/

#ifndef __HUGE__
#error No huge model !
#endif

#include
#include
#include
#include
#ifdef DEBUG
#include
#endif
#include
#include
#include
#include

#include

#pragma pack(1)

#define S_GREY 6000L
#define S_NOG 5300L
#define S286_16 9520L

#define ABS(x) ((x > 0) ? (x) : (-(x)))

#define FBS 8192
#define HERC 7

#ifndef DEBUG
#define SCR_W 640
#define SCR_D 400
#define SCR_B 80
#define LIM_R 630
#define LIM_B 390
#else
#define SCR_W 720
#define SCR_D 348
#define SCR_B 90
#define LIM_R 710
#define LIM_B 338
#endif

#define H_STEP 40
#define V_STEP 40
#define P_STEP 320

struct {
int idx;
BYTE b[FBS];
BYTE *p;
} fb;

typedef struct tagGLOBAL {
char version[3];
char subversion[3];
WORD screen_width;
WORD screen_depth;
BYTE GlobalFlag;
BYTE BackgroundColor;
BYTE zero;
} GLOBAL;

typedef struct tagLOCAL {
BYTE comma;
WORD Image_Left;
WORD Image_Top;
WORD Image_Width;
WORD Image_Depth;
BYTE LocalFlag;
} LOCAL;

typedef struct tagPCXHEAD {
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
int xmin, ymin;
int xmax, ymax;
int hres, vres;
char palette[48];
char reserved;
char colour_planes;
int bytes_per_line;
int palette_type;
char filler[58];
} PCXHEAD;

BYTE greymaptable[256] = {
/* 00 - 0f */
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 10 - 1f */
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 20 - 2f */
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
/* 30 - 3f */
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
/* 40 - 4f */
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x27, 0x28,
/* 50 - 5f */
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
/* 60 - 6f */
0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
/* 70 - 7f */
0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x50, 0x51, 0x52, 0x53, 0x55, 0x56, 0x57, 0x58,
/* 80 - 8f */
0x59, 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6a, 0x6b,
/* 90 - 9f */
0x6c, 0x6e, 0x70, 0x72, 0x73, 0x74, 0x76, 0x78,
0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88,
/* a0 - af */
0x8a, 0x8c, 0x8f, 0x91, 0x93, 0x95, 0x98, 0x9a,
0x9c, 0x9f, 0xa1, 0xa4, 0xa6, 0xa9, 0xab, 0xae,
/* b0 - bf */
0xb0, 0xb2, 0xb3, 0xb5, 0xb7, 0xb9, 0xba, 0xbc,
0xbd, 0xbe, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca,
/* c0 - cf */
0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd9, 0xdb,
0xdd, 0xe0, 0xe3, 0xe6, 0xe8, 0xeb, 0xed, 0xef,
/* d0 - df */
0xf2, 0xf5, 0xf8, 0xfb, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
/* e0 - ef */
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
/* f0 - ff */
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe };

BYTE herc_g[12] = {
0x35, 0x28, 0x2d, 0x07, 0x67, 0x0d, 0x66, 0x66, 0x02, 0x03, 0x0b, 0x0c};
BYTE herc_t[12] = {
0x61, 0x50, 0x52, 0x0f, 0x19, 0x06, 0x19, 0x19, 0x02, 0x0d, 0x0b, 0x0c};

BYTE huge *buffer;
BYTE huge *monobuf;
time_t Tbegin;
int greyflag = 1, cnvtflag = 0, showflag = 0;
BYTE black, white, halfgrey;
WORD ImageWidth, ImageDepth;
int bpl, bpp, first_ch;
int index;
int pass, InterLaced;
int code, oldcode;
int nextcode, nextlim;
int rem, remcnt, reqcnt;
int EOI, CLEAR;
WORD Xoff, Yoff;
int Tincr[4] = {8, 8, 4, 2};
int Tstart[4] = {0, 4, 2, 1};
int CTlink[4096];
BYTE CTfirst[4096];
BYTE CTlast[4096];
BYTE OStack[4096];
BYTE outrow[1284];
BYTE palette[768];
BYTE greypalette[256];
char gname[13];
char pdrv[3], pdir[67], pname[9] = {""};

int detect(void)
{
DWORD ts;
int r;

do ts = *(DWORD far *)0x46c;
while (ts == *(DWORD far *)0x46c);
ts = *(DWORD far *)0x46c;
r = inp(0x3ba) & 0x80;
do if ((inp(0x3ba) & 0x80) != r) return (HERC);
while (ts == *(DWORD far *)0x46c);
return (0);
}

void clrherc(void)
{
memset((BYTE far *)0xb0000000, 0, 65535);
return;
}

#ifndef DEBUG
void init(void)
{
register int i;

outp(0x3bf, 3);
outp(0x3b8, 2); /* Page/Blink/Video/GraphHi */
for (i = 0; i < 12; i++)
{
outp(0x3b4, i);
outp(0x3b5, herc_g[i]);
}
clrherc();
outp(0x3b8, 0x8b); /* page1, enable_video, graphics */
return;
}

void deinit(void)
{
register int i;
union REGS r;

outp(0x3bf, 0);
outp(0x3b8, 2);
for (i = 0; i < 12; i++)
{
outp(0x3b4, i);
outp(0x3b5, herc_t[i]);
}
r.x.ax = 7;
int86(0x10, &r, &r);
return;
}
#else
void init(void)
{
int gd = HERCMONO, gm = HERCMONOHI, e;

initgraph(&gd, &gm, "");
e = graphresult();
if (e != grOk)
{
puts(grapherrormsg(e));
farfree((BYTE far *)buffer);
exit(7);
}
setactivepage(1);
setvisualpage(1);
clrherc();
return;
}

void deinit(void)
{
closegraph();
return;
}
#endif
BYTE far *farptr(BYTE huge *p, long n)
{
WORD seg, ofs;

seg = FP_SEG(p);
ofs = FP_OFF(p);
seg += ((DWORD)ofs + n) >> 4;
ofs = ((DWORD)ofs + n) & 0xf;
return ((BYTE far *)MK_FP(seg, ofs));
}

BYTE far *vp(int x, int y)
{
WORD pass;
DWORD t;

pass = (WORD)(y & 3) << 13;
t = ((((WORD)y >> 2) * SCR_B) | pass) + x;
return ((BYTE far *)MK_FP(0xb800 + (t >> 4), t & 0xf));
}

void showpic(int x, int y)
{
register int i;
int w, h, sx, sy, px, py;
BYTE mask;

if (ImageWidth > SCR_W)
{
if (x < 0) w = SCR_B;
else w = SCR_B - (x >> 3);
}
else w = bpl;
if (ImageDepth > SCR_D)
{
if (y < 0) h = SCR_D;
else h = SCR_D - y;
}
else h = ImageDepth;
if (x < 0)
{
px = 0;
sx = -x >> 3;
}
else
{
px = x >> 3;
sx = 0;
}
if (y < 0)
{
py = 0;
sy = -y;
}
else
{
py = y;
sy = 0;
}
mask = 0xff >> (w * 8 - ImageWidth);
for (i = 0; i < h; i++)
{
memcpy(vp(px, py + i), \
farptr(monobuf, (long)(sy + i) * (long)bpl + (long)sx), w - 1);
vp(px, py + i)[w - 1] = *farptr(monobuf, (long)(sy + i) * (long)bpl \
+ (long)sx + w - 1) & mask;
}
return;
}

int Show(void)
{
int x, y, t;
char c;

if (detect() != HERC)
{
puts("The display card is not a Hercules monochrome graphics card !");
return (5);
}
if (ImageWidth < LIM_R) x = ((SCR_B - bpl) & (~1)) << 2;
else x = 0;
if (ImageDepth < LIM_B) y = (SCR_D - ImageDepth) >> 1;
else y = 0;
init();
showpic(x, y);
do {
c = getch();
if (c == KEY_ESC)
{
deinit();
return (0);
}
if (! c)
{
c = getch();
switch(c) {
case KEY_LEFT :
if (ImageWidth >= SCR_W)
{
if (x == 0) c = 0;
else
{
x += H_STEP;
if (x > 0) x = 0;
}
}
else
{
if (x == 0) c = 0;
else
{
x -= H_STEP;
if (x < 0) x = 0;
}
}
break;
case KEY_UP :
if (ImageDepth >= SCR_D)
{
if (y == 0) c = 0;
else
{
y += V_STEP;
if (y > 0) y = 0;
}
}
else
{
if (y == 0) c = 0;
else
{
y -= V_STEP;
if (y < 0) y = 0;
}
}
break;
case KEY_RIGHT :
t = LIM_R - ImageWidth;
if (x == t) c = 0;
else
{
if (ImageWidth >= SCR_W)
{
x -= H_STEP;
if (x < t) x = t;
}
else
{
x += H_STEP;
if (x > t) x = t;
}
}
break;
case KEY_DOWN :
t = LIM_B - ImageDepth;
if (y == t) c = 0;
else
{
if (ImageDepth >= SCR_D)
{
y -= V_STEP;
if (y < t) y = t;
}
else
{
y += V_STEP;
if (y > t) y = t;
}
}
break;
case KEY_HOME :
y = 0;
break;
case KEY_END :
if (y == LIM_B - ImageDepth) c = 0;
else y = LIM_B - ImageDepth;
break;
case KEY_PGUP :
if (ImageDepth >= SCR_D)
{
if (y == 0) c = 0;
else
{
y += P_STEP;
if (y > 0) y = 0;
}
}
else c = 0;
break;
case KEY_PGDN :
if (ImageDepth >= SCR_D)
{
if (y == (LIM_B - ImageDepth)) c = 0;
else
{
y -= P_STEP;
if ((y + ImageDepth) < LIM_B) y = LIM_B - ImageDepth;
}
}
else c = 0;
break;
default : c = 0;
}
if (c)
{
clrherc();
showpic(x, y);
}
}
} while (1);
}

void greymap(void)
{
register int i, j;
int n;
double f;
BYTE *p;
BYTE huge *pb;

black = 255;
white = 0;
n = 1 << bpp;
p = palette;
for (i = 0; i < n; i++)
{
f = (0.30 * (double)*p++) + (0.59 * (double)*p++) + (0.11 * (double)*p++);
if (f > 255.0) f = 255.0;
/* do grey scale expansion */
if (greyflag) greypalette[i] = greymaptable[(BYTE)f];
else greypalette[i] = (BYTE)f;
/* establish black and white levels */
if (greypalette[i] < black) black = greypalette[i];
if (greypalette[i] > white) white = greypalette[i];
}
pb = buffer;
for (i = 0; i < ImageDepth; i++)
for (j = 0; j < ImageWidth; j++)
{
*pb = greypalette[*pb];
pb++;
}
return;
}

int Dithering(void)
{
time_t Tend;
register int x, y;
int d = 0;

printf("Color convert : 0 %%");
greymap();
gotoxy(wherex() - 5, wherey());
puts("100 %");
printf("Dithering ... : 0 %%");
halfgrey = ((black + white + 1) >> 1);
Floyd_Init(ImageWidth, ImageDepth, halfgrey, white, black, monobuf, \
buffer); /* Function code in FLOYD_S.ASM */
for (y = 0; y < ImageDepth; y++)
{
if (! (y & 15))
{
if (! d)
{
d = 1;
gotoxy(wherex() - 5, wherey());
printf("%3u %%", (long)y * 100L / (long)ImageDepth);
}
}
else d = 0;
if (y & 1)
for (x = ImageWidth - 1; x > 0; --x)
Floyd_S_R(x, y); /* Function code in FLOYD_S.ASM */
else
for (x = 0; x < ImageWidth; ++x)
Floyd_S_L(x, y); /* Function code in FLOYD_S.ASM */
/* Floyd_line(y); / Function code in FLOYD_S.ASM */
}
time(&Tend);
gotoxy(wherex() - 5, wherey());
printf("100 %%\n\n\rElapsed : %u Seconds\n\r", \
(long)difftime(Tend, Tbegin));
return (0);
}

void bufcpy(void)
{
register int i, x, y;
BYTE huge *pd;
BYTE huge *ps;
BYTE c;

ps = buffer;
pd = monobuf;
for (y = 0; y < ImageDepth; y++)
for (x = 0; x < bpl; x++)
{
for (i = 0; i < 8; i++)
*pd = (*pd << 1) | (*ps++ & 1);
pd++;
}
return;
}

void PackPCX(FILE *fp)
{
BYTE p[1024];
register int i, t, y;

for (y = 0; y < ImageDepth; y++)
{
memcpy((BYTE far *)p, (BYTE far *)(monobuf + (long)y * (long)bpl), bpl);
t = 0;
do {
i = 0;
while ((p[t + i] == p[t + i + 1]) && ((t + i) < bpl) && (i < 63))
++i;
if (i > 0)
{
fputc(i | 0xc0, fp);
fputc(p[t], fp);
t += i;
}
else
{
if ((p[t] & 0xc0) == 0xc0)
fputc(0xc1, fp);
fputc(p[t++], fp);
}
} while (t < bpl);
}
return;
}

void SavePCXfile(void)
{
FILE *fp;
char name[81];
PCXHEAD ph;

fnmerge(name, pdrv, pdir, gname, ".PCX");
if ((fp = fopen(name, "wb")) == NULL)
{
printf("File %s creat error ! No save !\n\r", name);
return;
}
memset((char *)&ph, 0, sizeof(PCXHEAD));
ph.manufacturer = 0x0a;
ph.version = 5;
ph.encoding = 1;
ph.xmin = ph.ymin = 0;
ph.xmax = ImageWidth - 1;
ph.ymax = ImageDepth - 1;
ph.hres = ph.vres = 0;
ph.palette_type = 1;
ph.bits_per_pixel = 1;
ph.colour_planes = 1;
ph.bytes_per_line = (ImageWidth + 7) >> 3;
strcpy((char *)ph.palette, "HaingZhang KeeCh");
fwrite(&ph, sizeof(PCXHEAD), 1, fp);
PackPCX(fp);
fclose(fp);
return;
}

int DealPixel(BYTE pix)
{
if (Yoff > ImageDepth) return(1);
outrow[Xoff++] = pix;
if (Xoff == ImageWidth)
{
memcpy(farptr(buffer, (long)ImageWidth * (long)Yoff), outrow, ImageWidth);
if (! InterLaced)
Yoff++;
else
{
Yoff += Tincr[pass];
if (Yoff >= ImageDepth)
{
pass++;
Yoff = Tstart[pass];
}
}
Xoff = 0;
}
return (0);
}

int putx(int code)
{
int i = 0;

while (code != -1)
{
OStack[i++] = CTlast[code];
code = CTlink[code];
}
for (i--; i >= 0; i--)
if (DealPixel(OStack[i])) return (1);
return (0);
}

int InsertTable(int code, int oldcode)
{
CTlink[nextcode] = oldcode;
CTlast[nextcode] = CTfirst[code];
CTfirst[nextcode] = CTfirst[oldcode];
if (++nextcode == nextlim)
{
if (reqcnt < 12)
{
reqcnt++;
nextlim <<= 1;
}
}
}

int InitTable(int clear)
{
register int i;

nextcode = clear + 2;
for (i = 0; i < clear; i++)
{
CTfirst[i] = i;
CTlast[i] = i;
CTlink[i] = -1;
}
for (i = clear; i < 4096; i++)
CTlink[i] = -2;
reqcnt = first_ch + 1;
nextlim = 1 << reqcnt;
}

BYTE xgetc(FILE *fp)
{
if (! fb.idx)
{
gotoxy(wherex() - 5, wherey());
printf("%3u %%", (long)Yoff * 100L / (long)ImageDepth);
fb.idx = fread(fb.b, sizeof(BYTE), FBS, fp);
fb.p = fb.b;
}
fb.idx--;
return (*fb.p++);
}

int GetCode(FILE *fp, int req)
{
int value = 0, mask, start = 0, used;

while (req > 0)
{
if (remcnt == 0)
{
while (index == 0) index = (int)xgetc(fp);
index--;
rem = xgetc(fp);
remcnt = 8;
}
used = (req > remcnt) ? remcnt : req;
remcnt -= used;
mask = (0xff >> 8 - used); /* remcnt); */
mask &= rem;
rem >>= used;
mask <<= start;
start += used;
value |= mask;
req -= used;
}
return (value);
}

void InitializeData(void)
{
index = 0;
pass = 0;
code = 0;
oldcode = 0;
nextcode = 0;
nextlim = 0;
rem = 0;
remcnt = 0;
reqcnt = 0;
Xoff = 0;
Yoff = 0;
memset(CTlink, 0, 4096 * sizeof(int));
memset(CTfirst, 0, 4096);
memset(CTlast, 0, 4096);
memset(OStack, 0, 4096);
memset(outrow, 0, 1284);
return;
}

int UnpackGIF(FILE *fp)
{
InitializeData();
first_ch = getc(fp);
CLEAR = 1 << first_ch;
EOI = CLEAR + 1;
reqcnt = first_ch + 1;
fb.idx = 0; /* Init xgetc */
while ((code = GetCode(fp, reqcnt)) != EOI)
{
if (code == CLEAR)
{
InitTable(CLEAR);
oldcode = -1;
}
else
{
if (CTlink[code] == -2)
InsertTable(oldcode, oldcode);
else if (oldcode != -1)
InsertTable(code, oldcode);
if (putx(code))
return(1);
oldcode = code;
}
}
return (0);
}

long getspeed(void)
{
register DWORD i;
DWORD k = 0;
volatile DWORD far *t = (DWORD far *)0x46c;

i = *t;
while (i == *t);
i = *t;
while (i == *t) k++;
return (k);
}


int LoadGIFfile(void)
{
FILE *fp;
GLOBAL g;
LOCAL l;
WORD ColorNum;
int e;
long estimated;
char name[81];

fnmerge(name, pdrv, pdir, gname, ".GIF");
if ((fp = fopen(name, "rb")) == NULL)
{
printf("File %s no found !\n\r", name);
return (3);
}
fread(&g, sizeof(GLOBAL), 1, fp);
if (strnicmp(g.version, "GIF87a", 6) || g.zero != 0)
{
printf("File %s is an invalide GIF file !\n\r", name);
return (3);
}
bpp = (g.GlobalFlag & 7) + 1;
ColorNum = 1 << bpp;
if (fread(palette, 3, ColorNum, fp) != ColorNum)
{
printf("File %s read palette error !\n\r", name);
return (3);
}
fread(&l, sizeof(LOCAL), 1, fp);
printf("File format : %u x %u, %u Colours\n\r", l.Image_Width, \
l.Image_Depth, ColorNum);
estimated = (long)l.Image_Width * (long)l.Image_Depth;
if (greyflag)
estimated /= getspeed() * S_GREY / S286_16;
else
estimated /= getspeed() * S_NOG / S286_16;
printf("\n\rEstimated : %u Seconds\n\r", estimated);
time(&Tbegin);
ImageWidth = l.Image_Width;
ImageDepth = l.Image_Depth;
InterLaced = l.LocalFlag & 0x40;
bpl = (ImageWidth + 7) >> 3;
printf("\n\rAvaliable memory : %ld\n\r", farcoreleft());
printf("Require memory : %ld\n\r", \
(DWORD)ImageWidth * (DWORD)ImageDepth);
if ((buffer = (BYTE huge *)farmalloc((long)ImageWidth * \
(long)ImageDepth)) == NULL)
{
puts("\n\rNo enough memory !");
return (4);
}
printf("\n\rUnpacking GIF : 0 %");
if (UnpackGIF(fp))
{
printf("\n\rUnpack %s fail !\n\r", name);
return (3);
}
gotoxy(wherex() - 5, wherey());
puts("100 %");
fclose(fp);
return (0);
}

void UnpackPCX(FILE *fp)
{
register int i, j;
int n;
BYTE huge *p;
BYTE c;

p = monobuf;
for (i = 0; i < ImageDepth; i++)
{
n = 0;
do {
c = fgetc(fp);
if ((c & 0xc0) == 0xc0)
{
j = c & 0x3f;
c = fgetc(fp);
while (j--) p[n++] = c;
}
else p[n++] = c;
} while (n < bpl);
p += (long)bpl;
}
return;
}

int LoadPCXfile(void)
{
FILE *fp;
long fs;
int e;
char name[81];
PCXHEAD ph;

fnmerge(name, pdrv, pdir, gname, ".PCX");
if ((fp = fopen(name, "rb")) == NULL) return (3);
if (fread(&ph, sizeof(PCXHEAD), 1, fp) != 1) return (3);
if (ph.manufacturer != 0x0a || ph.bits_per_pixel != 1 || ph.encoding != \
1 || ph.colour_planes != 1)
return (3);
ImageWidth = ph.xmax - ph.xmin + 1;
ImageDepth = ph.ymax - ph.ymin + 1;
bpl = (ImageWidth + 7) >> 3;
if (bpl != ph.bytes_per_line) return (3);
fs = (long)bpl * (long)ImageDepth;
if ((monobuf = (BYTE huge *)farmalloc(fs)) == NULL)
{
puts("\n\rNo enough memory !");
return (4);
}
printf("File format : %u x %u\n\n\r", ImageWidth, ImageDepth);
UnpackPCX(fp);
fclose(fp);
return (0);
}

void help(void)
{
puts("Show a monochrome or colour GIF file by a Hercules display card.");
puts("Set Hercules display card at 640 * 400 monochrome graphics mode.");
puts("Dithering by Floyd/Steinberg error filter.");
puts("Serpentine raster scan. And grey level convert or not.\n");
puts("Usage : GIFXMONO filename [/c | /s] [/n]\n");
puts("Option :");
puts("/c -- Convert a colour GIF file to a monochrome PCX file.");
puts("/s -- Convert and show it.");
puts("/n -- Not grey convert.");
return;
}

int showPCX(void)
{
int e;
char name[81], *p;
struct ffblk f;

fnmerge(name, pdrv, pdir, pname, ".PCX");
if (findfirst(name, &f, FA_ARCH))
{
puts("File not found !");
return (1);
}
do {
strcpy(gname, f.ff_name);
printf("File : %s\n\r", gname);
p = strrchr(gname, '.');
*p = 0;
if ((e = LoadPCXfile()) != 0)
{
if (e == 3) puts("Invalid PCX file !");
return (e);
}
printf("Hit to abort... other key to show...");
if ((BYTE)getch() == KEY_CR)
{
farfree((BYTE far *)monobuf);
return (6);
}
if ((e = Show()) != 0) return (e);
farfree((BYTE far *)monobuf);
} while (! findnext(&f));
return (0);
}

int convertGIF(void)
{
int e;
char name[80];

strcpy(gname, pname);
e = LoadPCXfile();
if (e == 3)
{
if ((e = LoadGIFfile()) != 0)
{
farfree((BYTE far *)buffer);
return (e);
}
monobuf = buffer;
if (bpp == 1)
bufcpy();
else if ((e = Dithering()) != 0)
{
farfree((BYTE far *)buffer);
return (e);
}
if (cnvtflag || showflag) SavePCXfile();
if (! cnvtflag)
{
printf("Hit any key to show the picture...");
getch();
if ((e = Show()) != 0)
{
farfree((BYTE far *)buffer);
return (e);
}
}
farfree((BYTE far *)buffer);
}
else
{
if (cnvtflag)
{
fnmerge(name, pdrv, pdir, gname, ".PCX");
printf("PCX file %s exist .\n\r", name);
}
else
{
printf("Hit any key to show the picture...");
getch();
if ((e = Show()) != 0)
{
farfree((BYTE far *)monobuf);
return (e);
}
farfree((BYTE far *)monobuf);
}
}
return (0);
}

#ifdef DEBUG
main()
#else
main(int argc, char *argv[])
#endif
{
int e;
char pext[5];
char s[80];

puts("GIFXMONO. Ver 0.96");
puts("Copyright (c) 1996, 97 by Raptor, Sep.15-96, Mar.30-97\n\r");
#ifdef DEBUG
fnsplit("VV1S", pdrv, pdir, pname, pext);
#else
if (argc == 1)
{
help();
return (1);
}
for (e = 1; e < argc; e++)
{
strcpy(s, argv[e]);
strupr(s);
if (strstr(s, "/C")) cnvtflag = 1;
else if (strstr(s, "/S")) showflag = 1;
else if (strstr(s, "/N")) greyflag = 0;
else if (strstr(s, "/?") || strstr(s, "/H"))
{
help();
return (1);
}
else if (! (strchr(s, '/') || pname[0]))
fnsplit(s, pdrv, pdir, pname, pext);
else
{
printf("Invalid parameter : %s\n\r", argv[e]);
return (2);
}
}
if (! pname[0])
{
help();
return (1);
}
e = 0;
#endif
if (! stricmp(pext, ".PCX"))
{
if ((e = showPCX()) != 0) return (e);
}
else
{
if ((e = convertGIF()) != 0) return (e);
}
return (0);
}

; 以下为 F/S 抖动部分的汇编程序
; File : floyd_s.asm
; Floyd_Steinberg error filter dithering procedure. Ver 0.96
; Copyright (c) 1997 by Raptor, Mar.30-97
;
; Command line : TASM /mx floyd_s

.286
.Model Huge
.Code
assume cs : @code, ds : @data
;
; Prototype : void Floyd_Init(WORD ImageWidth, WORD ImageDepth, WORD h, \
; WORD white, WORD black, BYTE far *monobuf, BYTE far *buffer)
_Floyd_Init proc
ARG iw:word, id:word, h:word, wh:word, blk:word, dst:dword, src:dword
push bp
mov bp, sp
push ds
mov ax, @data
mov ds, ax
mov ax, [iw] ; Get Image Width
mov ImageWidth, ax
add ax, 7
shr ax, 3
mov bpl, al ; Calc. BPL(Byte per line)
mov ax, [id] ; Get Image Depth
mov ImageDepth, ax
mov ax, [h] ; Get halfgrey
mov halfgrey, al
mov ax, [wh] ; Get white
mov white, al
mov ax, [blk] ; Get black
mov black, al
push es
les ax, [dst] ; Get monobuf
mov word ptr monobuf, ax
mov word ptr monobuf + 2, es
les ax, [src] ; Get buffer
mov word ptr buffer, ax
mov word ptr buffer + 2, es
pop es
pop ds
pop bp
ret
_Floyd_Init endp
;
; Prototype : PASCAL BYTE far *PixelAddr(WORD x, WORD y)
PixelAddr proc near
ARG x : word, y : word
push bp
mov bp, sp
mov ax, ImageWidth
mul [y] ; dx:ax = ImageWidth * y
push dx
push ax
mov ax, [x]
xor dx, dx ; dx:ax = x
pop bx
pop cx
add bx, ax
adc cx, dx ; bx:cx = y * ImageWidth + x
mov dx, word ptr buffer + 2
mov ax, word ptr buffer
call far ptr PADD@ ; dx:ax = &buffer[y * ImageWidth + x]
pop bp
ret 4
PixelAddr endp
;
; Prototype : PASCAL void bitPixel(WORD x, WORD y, WORD b)
bitPixel proc near
ARG x : word, y : word, b : word
push bp
mov bp, sp
push ds
push es
mov al, bpl
mov ah, 0
mul [y] ; ax = y * bpl
mov bx, [x]
shr bx, 3
add bx, ax ; bx = y * bpl + (x >> 3)
xor cx, cx
mov dx, word ptr monobuf + 2
mov ax, word ptr monobuf
call far ptr PADD@ ; dx:ax = &monobuf[y * bpl + (x >> 3)]
mov bx, ax
mov es, dx
mov cx, [x]
and cx, 7
cmp [b], 0
je @bP2
mov al, 80h ; Set pixel white
ror al, cl
or byte ptr es : bx, al
jmp short @bP1
@bP2 :
mov al, 7fh ; Set pixel black
ror al, cl
and byte ptr es : bx, al
@bP1 :
pop es
pop ds
pop bp
ret 6
bitPixel endp
;
; Prototype : PASCAL void filterPixel(DWORD PixelPtr, int e);
; = setPixel(x, y, getPixel(x, y) + e);
filterPixel proc near
ARG p : dword, e : word
push bp
mov bp, sp
push ds
push es
les bx, [p]
mov al, byte ptr es : [bx]; ax = buffer[y * ImageWidth + x]
xor ah, ah
add ax, [e]
mov cl, black
xor ch, ch
cmp ax, cx
jge @fP_white
mov ax, cx ; if (ax < black) ax = black
jmp short @fP_set
@fP_white :
mov cl, white
cmp ax, cx
jle @fP_set
mov ax, cx ; if (ax > white) ax = white
@fP_set :
mov byte ptr es : [bx], al; buffer[y * ImageWidth + x] += e
pop es
pop ds
pop bp
ret 6
filterPixel endp

; Prototype : PASCAL BYTE far *Floyd_S_Init(WORD x, WORD y)
; Caution : modify : ds = data segment, bx, cx; return : es:di, si
Floyd_S_Init proc near
ARG x : word, y : word
push bp
mov bp, sp
mov ax, @data ; Set data segment
mov ds, ax
push [y]
push [x]
call PixelAddr ; return pixel address in buffer to dx:ax
mov es, dx
mov di, ax ; es:di -> pixel(x, y)
mov al, es : [di]
xor ah, ah
mov si, ax ; si = getPixel(x, y) = Pixel
mov al, halfgrey
cmp ax, si
ja @FSI_black ; it's black then goto @FSI_black
mov al, white
sub si, ax ; si = n - white = err
push 1 ; bitWhite
jmp short @FSI_bit
@FSI_black :
mov al, black
sub si, ax ; si = n - black = err
push 0 ; bitBlack
@FSI_bit :
push [y]
push [x]
call bitPixel ; bitPixel(x, y, bit)
pop bp
ret 4
Floyd_S_Init endp

; Prototype : void Floyd_S_L(WORD x, WORD y);
_Floyd_S_L proc
ARG x : word, y : word
push bp
mov bp, sp
push ds
push es
push si
push di
push [y]
push [x]
call Floyd_S_Init
or si, si
jz @FSL_end
; setPixel(x + 1, y, getPixel(x + 1, y) + ((7 * e + 8) >> 4))
mov ax, [x] ; x range check
inc ax
cmp ax, ImageWidth
jge @FSL_p2
mov ax, si
imul ax, 7
add ax, 8
sar ax, 4
push ax
push es
inc di
push di
call filterPixel
; setPixel(x - 1, y + 1, getPixel(x - 1, y + 1) + ((3 * e + 8) >> 4))
@FSL_p2 :
mov ax, [y] ; y range check
inc ax
cmp ax, ImageDepth
jge @FSL_end
cmp [x], 0 ; x range check
jz @FSL_p3
mov ax, si
imul ax, 3
add ax, 8
sar ax, 4
push ax
push es
add di, ImageWidth
sub di, 2
push di
call filterPixel
; setPixel(x, y + 1, getPixel(x, y + 1) + ((5 * e + 8) >> 4))
@FSL_p3 :
mov ax, si
imul ax, 5
add ax, 8
sar ax, 4
push ax
push es
inc di
push di
call filterPixel
; setPixel(x + 1, y + 1, getPixel(x + 1, y + 1) + ((e + 8) >> 4))
mov ax, [x] ; x range check
inc ax
cmp ax, ImageWidth
jge @FSL_end
add si, 8
sar si, 4
push si
push es
inc di
push di
call filterPixel
@FSL_end :
pop di
pop si
pop es
pop ds
pop bp
ret
_Floyd_S_L endp

; Prototype : void Floyd_S_R(WORD x, WORD y);
_Floyd_S_R proc
ARG x : word, y : word
push bp
mov bp, sp
push ds
push es
push si
push di
push [y]
push [x]
call Floyd_S_Init
or si, si
jz @FSR_end
; setPixel(x - 1, y, getPixel(x - 1, y) + ((7 * e + 8) >> 4))
cmp [x], 0 ; x range check
jz @FSR_p2
mov ax, si
imul ax, 7
add ax, 8
sar ax, 4
push ax
push es
dec di
push di
call filterPixel
; setPixel(x + 1, y + 1, getPixel(x + 1, y + 1) + ((3 * e + 8) >> 4))
@FSR_p2 :
mov ax, [y] ; y range check
inc ax
cmp ax, ImageDepth
jge @FSR_end
mov ax, [x]
inc ax
cmp ax, ImageWidth ; x range check
jge @FSR_p3
mov ax, si
imul ax, 3
add ax, 8
sar ax, 4
push ax
push es
add di, ImageWidth
add di, 2
push di
call filterPixel
; setPixel(x, y + 1, getPixel(x, y + 1) + ((5 * e + 8) >> 4))
@FSR_p3 :
mov ax, si
imul ax, 5
add ax, 8
sar ax, 4
push ax
push es
dec di
push di
call filterPixel
; setPixel(x - 1, y + 1, getPixel(x - 1, y + 1) + ((e + 8) >> 4))
cmp [x], 0 ; x range check
jz @FSR_end
add si, 8
sar si, 4
push si
push es
dec di
push di
call filterPixel
@FSR_end :
pop di
pop si
pop es
pop ds
pop bp
ret
_Floyd_S_R endp
;
@curseg ends
;
public _Floyd_Init
public _Floyd_S_L
public _Floyd_S_R
extrn PADD@
;
.Data
ImageWidth dw 0
ImageDepth dw 0
bpl db 0
halfgrey db 80h
white db 0ffh
black db 0
monobuf label dword
buffer dd 0
;
end

难得老兄能找到这些古董,看着这些东西,真怀念以前求学的日子

天蝎蝴蝶 at 2005-10-08T14:38:00+08:00