942 lines
27 KiB
C
942 lines
27 KiB
C
/*****************************************************************************
|
|
* "Gif-Lib" - Yet another gif library. *
|
|
* *
|
|
* Written by: Gershon Elber Ver 0.1, Jul. 1989 *
|
|
******************************************************************************
|
|
* Program to display GIF file using the BGI device indepedent routines *
|
|
* Options: *
|
|
* -q : quite printing mode. *
|
|
* -d BGI path : specify the directory where to look for bgi drivers. *
|
|
* -u BGIUserDriverName.Mode : use user driver instead of auto detection. *
|
|
* -z factor : zoom the pixels by the given factor. *
|
|
* -b : beeps disabled. *
|
|
* -h : on line help. *
|
|
* *
|
|
* In this file Screen refers to GIF file screen, while Device to BGI size. *
|
|
******************************************************************************
|
|
* History: *
|
|
* 31 Jul 90 - Version 1.0 by Gershon Elber. *
|
|
*****************************************************************************/
|
|
|
|
#include <graphics.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <conio.h>
|
|
#include <ctype.h>
|
|
#include <alloc.h>
|
|
#include <string.h>
|
|
#include <io.h>
|
|
#include <dos.h>
|
|
#include <bios.h>
|
|
#include <fcntl.h>
|
|
#include "gif_lib.h"
|
|
#include "getarg.h"
|
|
|
|
#define PROGRAM_NAME "Gif2BGI"
|
|
|
|
#define KEY_LEFT 256 /* Key Codes returned for operational keys */
|
|
#define KEY_RIGHT 257 /* as return by the GetKey routine. */
|
|
#define KEY_UP 258
|
|
#define KEY_DOWN 259
|
|
#define KEY_RETURN 260
|
|
#define KEY_DELETE 261
|
|
#define KEY_INSERT 262
|
|
#define KEY_BSPACE 263
|
|
#define KEY_ESC 264
|
|
#define KEY_HOME 265
|
|
#define KEY_END 266
|
|
#define KEY_PGUP 267
|
|
#define KEY_PGDN 268
|
|
|
|
#define SET_POSITION_RESET 0 /* Situations need positionings: */
|
|
#define SET_POSITION_ZOOM_U 1
|
|
#define SET_POSITION_ZOOM_D 2
|
|
#define SET_POSITION_PAN 3
|
|
|
|
#define CURSOR_TEXT_X 120
|
|
|
|
#define BGI_USER_INSTALL 999 /* BGI User installed driver device. */
|
|
|
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
extern unsigned int
|
|
_stklen = 16384; /* Increase default stack size. */
|
|
|
|
static char
|
|
*VersionStr =
|
|
PROGRAM_NAME
|
|
GIF_LIB_VERSION
|
|
" Gershon Elber, "
|
|
__DATE__ ", " __TIME__ "\n"
|
|
"(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
|
|
static char
|
|
*CtrlStr =
|
|
PROGRAM_NAME
|
|
" q%- d%-BGI|Directory!s u%-UserBGIDrv.Mode!s z%-ZoomFactor!d b%- h%- GifFile!*s";
|
|
static char
|
|
*GifFileName,
|
|
*BGIPath = "",
|
|
*BGIUserDriverName = NULL;
|
|
|
|
/* Make some variables global, so we could access them faster: */
|
|
static int
|
|
ImageNum = 0,
|
|
BackGround = 0,
|
|
ForeGround = 1, /* As close to white as possible. */
|
|
BeepsDisabled = FALSE,
|
|
ZoomFactor = 1,
|
|
MaximumScreenHeight = 1,
|
|
BGIUserDriverMode = -1,
|
|
DeviceMaxX = 640, DeviceMaxY = 400, /* Physical device dimensions. */
|
|
ScreenWidth = 320, ScreenHeight = 200, /* Gif image screen size. */
|
|
InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
|
|
InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
|
|
static GifColorType
|
|
*ColorMap;
|
|
|
|
static int huge detectVGA(void);
|
|
static void BGIInstallUserDriver(char *BGIUserDriverNameMode);
|
|
static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile);
|
|
static void PrintSettingStatus(GifFileType *GifFile);
|
|
static void CPrintStr(char *Str, int y);
|
|
static void SetPosition(int Why,
|
|
int *ScreenLeft, int *ScreenTop,
|
|
int *DeviceLeft, int *DeviceTop,
|
|
int MoveX, int MoveY);
|
|
static void DrawScreen(GifRowType *ScreenBuffer,
|
|
int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop);
|
|
static void DoCursorMode(GifRowType *ScreenBuffer,
|
|
int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop);
|
|
static int MyKbHit(void);
|
|
static int MyGetCh(void);
|
|
static int GetKey(void);
|
|
static void Tone(int Frequency, int Time);
|
|
|
|
/******************************************************************************
|
|
* Interpret the command line and scan the given GIF file. *
|
|
******************************************************************************/
|
|
void main(int argc, char **argv)
|
|
{
|
|
int i, j, k, Error, NumFiles, Size, Row, Col, Width, Height, ExtCode,
|
|
Count, ColorMapSize, GraphDriver, GraphMode, Sum,
|
|
HelpFlag = FALSE,
|
|
BGIPathFlag = FALSE,
|
|
BGIUserDriverFlag = FALSE,
|
|
ZoomFlag = FALSE;
|
|
GifRecordType RecordType;
|
|
GifByteType *Extension;
|
|
char Str[80], *BGIUserDriverNameMode,
|
|
**FileName = NULL;
|
|
GifRowType *ScreenBuffer;
|
|
GifFileType *GifFile;
|
|
struct text_info TextInfo; /* So we can restore starting text mode. */
|
|
|
|
if ((Error = GAGetArgs(argc, argv, CtrlStr,
|
|
&GifQuitePrint, &BGIPathFlag, &BGIPath,
|
|
&BGIUserDriverFlag, &BGIUserDriverNameMode,
|
|
&ZoomFlag, &ZoomFactor,
|
|
&BeepsDisabled, &HelpFlag,
|
|
&NumFiles, &FileName)) != FALSE ||
|
|
(NumFiles > 1 && !HelpFlag)) {
|
|
if (Error)
|
|
GAPrintErrMsg(Error);
|
|
else if (NumFiles > 1)
|
|
GIF_MESSAGE("Error in command line parsing - one GIF file please.");
|
|
GAPrintHowTo(CtrlStr);
|
|
exit(1);
|
|
}
|
|
|
|
if (HelpFlag) {
|
|
fprintf(stderr, VersionStr);
|
|
GAPrintHowTo(CtrlStr);
|
|
exit(0);
|
|
}
|
|
|
|
if (BGIUserDriverFlag) {
|
|
/* Use the driver supplied by the user! */
|
|
BGIInstallUserDriver(BGIUserDriverNameMode);
|
|
installuserdriver(BGIUserDriverName, detectVGA);
|
|
GraphDriver = BGI_USER_INSTALL;
|
|
}
|
|
else {
|
|
/* Sense type of display we have and attempt to load right driver. */
|
|
detectgraph(&GraphDriver, &GraphMode);
|
|
if (GraphDriver < 0)
|
|
GIF_EXIT("BGI Auto detect: No graphics device detected.");
|
|
}
|
|
|
|
/* Put in the following any graphic driver specific setup: */
|
|
switch (GraphDriver) {
|
|
case CGA:
|
|
GraphMode = CGAHI;
|
|
break;
|
|
case EGA:
|
|
GraphMode = EGAHI;
|
|
break;
|
|
case EGA64:
|
|
GraphMode = EGA64HI;
|
|
break;
|
|
case EGAMONO:
|
|
GraphMode = EGAMONOHI;
|
|
break;
|
|
case HERCMONO:
|
|
GraphMode = HERCMONOHI;
|
|
break;
|
|
case VGA:
|
|
GraphMode = VGAHI;
|
|
break;
|
|
case BGI_USER_INSTALL:
|
|
GraphDriver = DETECT;
|
|
GraphMode = BGIUserDriverMode;
|
|
break;
|
|
default:
|
|
GIF_EXIT("Requested graphic device is not supported.");
|
|
break;
|
|
}
|
|
|
|
if (NumFiles == 1) {
|
|
GifFileName = *FileName;
|
|
if ((GifFile = DGifOpenFileName(*FileName)) == NULL) {
|
|
PrintGifError();
|
|
exit(-1);
|
|
}
|
|
}
|
|
else {
|
|
/* Use the stdin instead: */
|
|
GifFileName = "Stdin";
|
|
setmode(0, O_BINARY);
|
|
if ((GifFile = DGifOpenFileHandle(0)) == NULL) {
|
|
PrintGifError();
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
/* Allocate the screen as vector of column of rows. We cannt allocate */
|
|
/* the all screen at once, as this broken minded CPU can allocate up to */
|
|
/* 64k at a time and our image can be bigger than that: */
|
|
/* Note this screen is device independent - its the screen as defined by */
|
|
/* the GIF file parameters itself. */
|
|
if ((ScreenBuffer = (GifRowType *)
|
|
malloc(GifFile -> SHeight * sizeof(GifRowType *))) == NULL)
|
|
GIF_EXIT("Failed to allocate memory required, aborted.");
|
|
|
|
Size = GifFile -> SWidth * sizeof(GifPixelType);/* Size in bytes of one row.*/
|
|
if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
|
|
GIF_EXIT("Failed to allocate memory required, aborted.");
|
|
|
|
for (i = 0; i < GifFile -> SWidth; i++) /* Set its color to BackGround. */
|
|
ScreenBuffer[0][i] = GifFile -> SBackGroundColor;
|
|
MaximumScreenHeight = GifFile -> SHeight - 1;
|
|
for (i = 1; i < GifFile -> SHeight; i++) {
|
|
/* Allocate the other rows, and set their color to background too: */
|
|
if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL) {
|
|
if (i > 30) {
|
|
/* Free some memory for the BGI driver and auxilary. */
|
|
for (j = 1; j < 28; j++)
|
|
free((char *) ScreenBuffer[i - j]);
|
|
MaximumScreenHeight = i - 28;
|
|
fprintf(stderr, "\n%s: Failed to allocate all memory required, last line %d.\n",
|
|
PROGRAM_NAME, MaximumScreenHeight);
|
|
break;
|
|
}
|
|
else
|
|
GIF_EXIT("Failed to allocate memory required, aborted.");
|
|
}
|
|
|
|
memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
|
|
}
|
|
|
|
/* Scan the content of the GIF file and load the image(s) in: */
|
|
do {
|
|
if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
|
|
PrintGifError();
|
|
break;
|
|
}
|
|
switch (RecordType) {
|
|
case IMAGE_DESC_RECORD_TYPE:
|
|
if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
|
|
PrintGifError();
|
|
exit(-1);
|
|
}
|
|
|
|
Row = GifFile -> ITop; /* Image Position relative to Screen. */
|
|
Col = GifFile -> ILeft;
|
|
Width = GifFile -> IWidth;
|
|
Height = GifFile -> IHeight;
|
|
GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ",
|
|
PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
|
|
if (GifFile -> ILeft + GifFile -> IWidth > GifFile -> SWidth ||
|
|
GifFile -> ITop + GifFile -> IHeight > GifFile -> SHeight) {
|
|
fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n");
|
|
exit(-2);
|
|
}
|
|
if (GifFile -> IInterlace) {
|
|
/* Need to perform 4 passes on the images: */
|
|
for (Count = i = 0; i < 4; i++)
|
|
for (j = Row + InterlacedOffset[i]; j < Row + Height;
|
|
j += InterlacedJumps[i]) {
|
|
GifQprintf("\b\b\b\b%-4d", Count++);
|
|
if (DGifGetLine(GifFile,
|
|
&ScreenBuffer[MIN(j, MaximumScreenHeight)][Col],
|
|
Width) == GIF_ERROR) {
|
|
PrintGifError();
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < Height; i++, Row++) {
|
|
GifQprintf("\b\b\b\b%-4d", i);
|
|
if (DGifGetLine(GifFile, &ScreenBuffer[MIN(Row, MaximumScreenHeight)][Col],
|
|
Width) == GIF_ERROR) {
|
|
PrintGifError();
|
|
MaximumScreenHeight = MIN(i - 1, MaximumScreenHeight);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EXTENSION_RECORD_TYPE:
|
|
/* Skip any extension blocks in file: */
|
|
if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
|
|
PrintGifError();
|
|
exit(-1);
|
|
}
|
|
while (Extension != NULL) {
|
|
if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
|
|
PrintGifError();
|
|
exit(-1);
|
|
}
|
|
}
|
|
break;
|
|
case TERMINATE_RECORD_TYPE:
|
|
break;
|
|
default: /* Should be traps by DGifGetRecordType. */
|
|
break;
|
|
}
|
|
}
|
|
while (RecordType != TERMINATE_RECORD_TYPE);
|
|
|
|
/* Lets display it - set the global variables required and do it: */
|
|
BackGround = GifFile -> SBackGroundColor;
|
|
ColorMap = (GifFile -> IColorMap ? GifFile -> IColorMap :
|
|
GifFile -> SColorMap);
|
|
ColorMapSize = 1 << (GifFile -> IColorMap ? GifFile -> IBitsPerPixel :
|
|
GifFile -> SBitsPerPixel);
|
|
|
|
gettextinfo(&TextInfo); /* Save current mode so we can recover. */
|
|
|
|
initgraph(&GraphDriver, &GraphMode, BGIPath);
|
|
if (graphresult() != grOk) /* Error occured during init. */
|
|
GIF_EXIT("Graphics System Error, failed to initialize driver.");
|
|
|
|
if (getmaxcolor() + 1 < ColorMapSize) {
|
|
sprintf(Str, "GIF Image color map (%d) is too big for device (%d).\n",
|
|
ColorMapSize, getmaxcolor() + 1);
|
|
closegraph();
|
|
GIF_EXIT(Str);
|
|
}
|
|
|
|
/* Initialize hardware pallete and also select fore/background color. */
|
|
BackGround = ForeGround = 1;
|
|
Sum = ((int) ColorMap[1].Red) +
|
|
((int) ColorMap[1].Green) +
|
|
((int) ColorMap[1].Blue);
|
|
j = k = Sum;
|
|
for (i = 0; i < ColorMapSize; i++) {
|
|
setrgbpalette(i, ColorMap[i].Red >> 2,
|
|
ColorMap[i].Green >> 2,
|
|
ColorMap[i].Blue >> 2);
|
|
|
|
Sum = ((int) ColorMap[i].Red) +
|
|
((int) ColorMap[i].Green) +
|
|
((int) ColorMap[i].Blue);
|
|
if (i != 0 && Sum > j) { /* Dont use color 0. */
|
|
ForeGround = i;
|
|
j = Sum;
|
|
}
|
|
if (i != 0 && Sum <= k) { /* Dont use color 0. */
|
|
BackGround = i;
|
|
k = Sum;
|
|
}
|
|
}
|
|
|
|
DeviceMaxX = getmaxx(); /* Read size of physical screen. */
|
|
DeviceMaxY = getmaxy();
|
|
ScreenWidth = GifFile -> SWidth;
|
|
ScreenHeight = MIN(GifFile -> SHeight, MaximumScreenHeight);
|
|
|
|
Tone(500, 10);
|
|
DisplayScreen(ScreenBuffer, GifFile);
|
|
|
|
if (DGifCloseFile(GifFile) == GIF_ERROR) {
|
|
PrintGifError();
|
|
closegraph();
|
|
exit(-1);
|
|
}
|
|
|
|
closegraph();
|
|
|
|
textmode(TextInfo.currmode);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Routine to be called for the user installed driver: *
|
|
****************************************************************************/
|
|
static int huge detectVGA(void)
|
|
{
|
|
return BGIUserDriverMode;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Routine to install the user BGI driver information. *
|
|
****************************************************************************/
|
|
static void BGIInstallUserDriver(char *BGIUserDriverNameMode)
|
|
{
|
|
char *p;
|
|
|
|
if ((p = strrchr(BGIUserDriverNameMode, '/')) != NULL ||
|
|
(p = strrchr(BGIUserDriverNameMode, '\\')) != NULL) {
|
|
p[0] = 0;
|
|
BGIPath = strdup(BGIUserDriverNameMode);
|
|
p++;
|
|
}
|
|
|
|
p = strtok(p, ".");
|
|
BGIUserDriverName = strdup(p);
|
|
|
|
p = strtok(NULL, ".");
|
|
if (sscanf(p, "%d", &BGIUserDriverMode) != 1 || BGIUserDriverName == NULL)
|
|
GIF_EXIT("User [-u] BGI specification has wrong format.");
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Given the screen buffer, display it: *
|
|
* The following commands are available (case insensitive). *
|
|
* 1. Four arrow to move along the screen (only if ScreenBuffer > physical *
|
|
* screen in that direction. *
|
|
* 2. C - goto cursor mode - print current color & position in GIF screen *
|
|
* of the current pixel cursor is on. *
|
|
* 3. D - zoom out by factor of 2. *
|
|
* 4. R - redraw current image. *
|
|
* 5. S - Print Current status/options. *
|
|
* 6. U - zoom in by factor of 2. *
|
|
* 7. ' ' - stop drawing current image. *
|
|
* 8. ESC - to quit. *
|
|
******************************************************************************/
|
|
static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile)
|
|
{
|
|
int DeviceTop, DeviceLeft, /* Where ScreenBuffer is to mapped to ours. */
|
|
ScreenTop, ScreenLeft, /* Porsion of ScreenBuffer to start display. */
|
|
XPanning, YPanning, /* Amount to move using the arrows. */
|
|
GetK, DrawIt = TRUE;
|
|
|
|
XPanning = DeviceMaxX / 2;
|
|
YPanning = DeviceMaxY / 2;
|
|
|
|
SetPosition(SET_POSITION_RESET,
|
|
&ScreenLeft, &ScreenTop,
|
|
&DeviceLeft, &DeviceTop,
|
|
0, 0);
|
|
|
|
do {
|
|
if (DrawIt && !MyKbHit()) {
|
|
DrawScreen(ScreenBuffer, ScreenLeft, ScreenTop,
|
|
DeviceLeft, DeviceTop);
|
|
Tone(2000, 200);
|
|
}
|
|
DrawIt = TRUE;
|
|
switch (GetK = GetKey()) {
|
|
case 'C':
|
|
DoCursorMode(ScreenBuffer, ScreenLeft, ScreenTop,
|
|
DeviceLeft, DeviceTop);
|
|
DrawIt = TRUE;
|
|
break;
|
|
case 'D':
|
|
if (ZoomFactor > 1) {
|
|
ZoomFactor >>= 1;
|
|
SetPosition(SET_POSITION_ZOOM_D,
|
|
&ScreenLeft, &ScreenTop,
|
|
&DeviceLeft, &DeviceTop,
|
|
0, 0);
|
|
}
|
|
else {
|
|
Tone(1000, 100);
|
|
DrawIt = FALSE;
|
|
}
|
|
break;
|
|
case 'R':
|
|
break;
|
|
case 'S':
|
|
PrintSettingStatus(GifFile);
|
|
break;
|
|
case 'U':
|
|
if (ZoomFactor < 256) {
|
|
ZoomFactor <<= 1;
|
|
SetPosition(SET_POSITION_ZOOM_U,
|
|
&ScreenLeft, &ScreenTop,
|
|
&DeviceLeft, &DeviceTop,
|
|
0, 0);
|
|
}
|
|
else {
|
|
Tone(1000, 100);
|
|
DrawIt = FALSE;
|
|
}
|
|
break;
|
|
case KEY_ESC:
|
|
break;
|
|
case KEY_LEFT:
|
|
SetPosition(SET_POSITION_PAN,
|
|
&ScreenLeft, &ScreenTop,
|
|
&DeviceLeft, &DeviceTop,
|
|
-XPanning, 0);
|
|
break;
|
|
case KEY_RIGHT:
|
|
SetPosition(SET_POSITION_PAN,
|
|
&ScreenLeft, &ScreenTop,
|
|
&DeviceLeft, &DeviceTop,
|
|
XPanning, 0);
|
|
break;
|
|
case KEY_UP:
|
|
SetPosition(SET_POSITION_PAN,
|
|
&ScreenLeft, &ScreenTop,
|
|
&DeviceLeft, &DeviceTop,
|
|
0, -YPanning);
|
|
break;
|
|
case KEY_DOWN:
|
|
SetPosition(SET_POSITION_PAN,
|
|
&ScreenLeft, &ScreenTop,
|
|
&DeviceLeft, &DeviceTop,
|
|
0, YPanning);
|
|
break;
|
|
default:
|
|
DrawIt = FALSE;
|
|
Tone(800, 100);
|
|
Tone(300, 200);
|
|
break;
|
|
}
|
|
}
|
|
while (GetK != KEY_ESC);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Routine to print (in text mode), current program status. *
|
|
******************************************************************************/
|
|
static void PrintSettingStatus(GifFileType *GifFile)
|
|
{
|
|
char s[80];
|
|
|
|
setcolor(ForeGround);
|
|
|
|
CPrintStr(PROGRAM_NAME, 1);
|
|
|
|
sprintf(s, "GIF File - %s.", GifFileName);
|
|
CPrintStr(s, 10);
|
|
|
|
sprintf(s, "Gif Screen Size = [%d, %d]. Contains %d image(s).",
|
|
GifFile -> SWidth, GifFile -> SHeight, ImageNum);
|
|
CPrintStr(s, 20);
|
|
|
|
if (GifFile -> SColorMap)
|
|
sprintf(s,
|
|
"Has Screen Color map of %d bits. BackGround = [%d, %d, %d].",
|
|
GifFile -> SBitsPerPixel,
|
|
GifFile -> SColorMap[GifFile -> SBackGroundColor].Red,
|
|
GifFile -> SColorMap[GifFile -> SBackGroundColor].Green,
|
|
GifFile -> SColorMap[GifFile -> SBackGroundColor].Blue);
|
|
else
|
|
sprintf(s, "No Screen color map.");
|
|
CPrintStr(s, 30);
|
|
|
|
if (GifFile -> IColorMap)
|
|
sprintf(s, "Has Image map of %d bits (last image). Image is %s.",
|
|
GifFile -> IBitsPerPixel,
|
|
(GifFile -> IInterlace ? "interlaced" : "non interlaced"));
|
|
else
|
|
sprintf(s, "No Image color map.");
|
|
CPrintStr(s, 40);
|
|
|
|
CPrintStr("Press anything to continue:", 60);
|
|
MyGetCh();
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Routine to cprintf given string centered at given Y level, and attr: *
|
|
******************************************************************************/
|
|
static void CPrintStr(char *Str, int y)
|
|
{
|
|
setfillstyle(SOLID_FILL, BackGround);
|
|
bar(0, y, textwidth(Str) + 2, y + textheight(Str) + 2);
|
|
|
|
setcolor(ForeGround);
|
|
outtextxy(1, y + 1, Str);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Routine to set the position of Screen in Device, and what porsion of the *
|
|
* screen should be visible: *
|
|
* MoveX, MoveY are the panning factors (if both zero - initialize). *
|
|
******************************************************************************/
|
|
static void SetPosition(int Why,
|
|
int *ScreenLeft, int *ScreenTop,
|
|
int *DeviceLeft, int *DeviceTop,
|
|
int MoveX, int MoveY)
|
|
{
|
|
|
|
MoveX /= ZoomFactor; /* Make sure move move same amount independent. */
|
|
MoveY /= ZoomFactor; /* of what ZommFactor is. */
|
|
|
|
/* Figure out position of GIF file in real device X axis: */
|
|
if (ScreenWidth * ZoomFactor <= DeviceMaxX + 1) {
|
|
/* Device is big enough to hold all the image X axis: */
|
|
*ScreenLeft = 0;
|
|
*DeviceLeft = (DeviceMaxX - ScreenWidth * ZoomFactor) / 2;
|
|
}
|
|
else {
|
|
/* Device is too small to hold all the image X axis: */
|
|
switch (Why) {
|
|
case SET_POSITION_RESET:
|
|
*ScreenLeft = 0;
|
|
break;
|
|
case SET_POSITION_ZOOM_U:
|
|
*ScreenLeft += DeviceMaxX / (2 * ZoomFactor);
|
|
break;
|
|
case SET_POSITION_ZOOM_D:
|
|
*ScreenLeft -= DeviceMaxX / (4 * ZoomFactor);
|
|
break;
|
|
case SET_POSITION_PAN:
|
|
if (MoveX != 0) *ScreenLeft += MoveX;
|
|
break;
|
|
}
|
|
if (*ScreenLeft < 0) *ScreenLeft = 0;
|
|
if ((ScreenWidth - *ScreenLeft) * ZoomFactor < DeviceMaxX + 1)
|
|
*ScreenLeft = (ScreenWidth * ZoomFactor -
|
|
DeviceMaxX + 1) / ZoomFactor;
|
|
*DeviceLeft = 0;
|
|
}
|
|
|
|
/* Figure out position of GIF file in real device Y axis: */
|
|
if (ScreenHeight * ZoomFactor <= DeviceMaxY + 1) {
|
|
/* Device is big enough to hold all the image Y axis: */
|
|
*ScreenTop = 0;
|
|
*DeviceTop = (DeviceMaxY - ScreenHeight * ZoomFactor) / 2;
|
|
}
|
|
else {
|
|
/* Device is too small to hold all the image Y axis: */
|
|
switch (Why) {
|
|
case SET_POSITION_RESET:
|
|
*ScreenTop = 0;
|
|
break;
|
|
case SET_POSITION_ZOOM_U:
|
|
*ScreenTop += DeviceMaxY / (2 * ZoomFactor);
|
|
break;
|
|
case SET_POSITION_ZOOM_D:
|
|
*ScreenTop -= DeviceMaxY / (4 * ZoomFactor);
|
|
break;
|
|
case SET_POSITION_PAN:
|
|
if (MoveY != 0) *ScreenTop += MoveY;
|
|
break;
|
|
}
|
|
if (*ScreenTop < 0) *ScreenTop = 0;
|
|
if ((ScreenHeight - *ScreenTop) * ZoomFactor < DeviceMaxY + 1)
|
|
*ScreenTop = (ScreenHeight * ZoomFactor -
|
|
DeviceMaxY - 1) / ZoomFactor;
|
|
*DeviceTop = 0;
|
|
}
|
|
|
|
/* Make sure the position is on Byte boundary (8 pixels per byte): */
|
|
*DeviceLeft &= 0xfff8;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* The real drawing of the image is performed here. Few things are taken into *
|
|
* account: *
|
|
* 1. The zoom factor. If > 1 each pixel is multiplied this amount vertically *
|
|
* and horizontally. *
|
|
* The image is drawn from ScreenBuffer ScreenTop/Left in the bottom/right *
|
|
* directions, onto the Device DeviceTop/Left in the bottom/right direction *
|
|
* Pressing space during drawing will abort this routine. *
|
|
******************************************************************************/
|
|
static void DrawScreen(GifRowType *ScreenBuffer,
|
|
int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop)
|
|
{
|
|
int i, j, k, l, CountZoomJ, CountZoomI,
|
|
DeviceWidth, DeviceRight, ScreenBottom;
|
|
unsigned char *ImageBuffer, *p;
|
|
GifPixelType *Line;
|
|
|
|
/* Make sure we start from scratch. Note cleardevice() uses color 0 even */
|
|
/* if it may be non black. */
|
|
cleardevice();
|
|
|
|
if (getmaxcolor() + 1 == 256) {
|
|
/* Optimize this case - one byte per pixel. */
|
|
DeviceWidth = ScreenWidth * ZoomFactor;
|
|
if (DeviceWidth + DeviceLeft > DeviceMaxX)
|
|
DeviceWidth = DeviceMaxX - DeviceLeft;
|
|
DeviceRight = DeviceLeft + DeviceWidth - 1;
|
|
ScreenBottom = ScreenTop + ScreenHeight - 1;
|
|
|
|
if ((ImageBuffer = malloc(imagesize(DeviceLeft, DeviceTop,
|
|
DeviceRight, DeviceTop))) == NULL)
|
|
GIF_EXIT("Failed to allocate memory required, aborted.");
|
|
getimage(DeviceLeft, DeviceTop, DeviceRight, DeviceTop, ImageBuffer);
|
|
|
|
for (k = DeviceTop; k < DeviceMaxY && ScreenTop <= ScreenBottom;) {
|
|
Line = ScreenBuffer[ScreenTop++];
|
|
p = ImageBuffer + 4; /* point on first pixel in bitmap. */
|
|
if (ZoomFactor == 1)
|
|
memcpy(p, &Line[ScreenLeft], DeviceWidth);
|
|
else {
|
|
for (i = 0, j = ScreenLeft, CountZoomI = ZoomFactor;
|
|
i < DeviceWidth;
|
|
i++) {
|
|
*p++ = Line[j];
|
|
if (--CountZoomI == 0) {
|
|
CountZoomI = ZoomFactor;
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Abort drawing if space bar was pressed: */
|
|
if (MyKbHit() && GetKey() == ' ') break;
|
|
|
|
for (i = 0; i < ZoomFactor; i++)
|
|
putimage(DeviceLeft, k++, ImageBuffer, COPY_PUT);
|
|
}
|
|
|
|
free((char *) ImageBuffer);
|
|
}
|
|
else {
|
|
for (CountZoomJ = ZoomFactor, j = ScreenTop, l = DeviceTop;
|
|
j < ScreenHeight && l <= DeviceMaxY; l++) {
|
|
Line = ScreenBuffer[j];
|
|
|
|
/* Abort drawing if space bar was pressed: */
|
|
if (MyKbHit() && GetKey() == ' ') break;
|
|
|
|
for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft;
|
|
i < ScreenWidth && k <= DeviceMaxX;) {
|
|
putpixel(k++, l, Line[i]);
|
|
|
|
if (!--CountZoomI) {
|
|
/* Go to next column: */
|
|
i++;
|
|
CountZoomI = ZoomFactor;
|
|
}
|
|
}
|
|
|
|
if (!--CountZoomJ) {
|
|
/* Go to next row: */
|
|
j++;
|
|
CountZoomJ = ZoomFactor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Walks along the current image, while printing pixel value and position. *
|
|
* 4 arrows may be used, and any other key will abort this operation *
|
|
******************************************************************************/
|
|
static void DoCursorMode(GifRowType *ScreenBuffer,
|
|
int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop)
|
|
{
|
|
int GetK, DeviceRight, DeviceBottom, x, y, Step;
|
|
GifPixelType Pixel;
|
|
char s[80];
|
|
|
|
DeviceRight = DeviceLeft + (ScreenWidth - ScreenLeft) * ZoomFactor;
|
|
if (DeviceRight > DeviceMaxX) DeviceRight = DeviceMaxX;
|
|
|
|
DeviceBottom = DeviceTop + (ScreenHeight - ScreenTop) * ZoomFactor;
|
|
if (DeviceBottom > DeviceMaxY) DeviceBottom = DeviceMaxY;
|
|
|
|
x = (DeviceLeft + DeviceRight) / 2;
|
|
y = (DeviceTop + DeviceBottom) / 2;
|
|
|
|
setwritemode(XOR_PUT);
|
|
|
|
while (TRUE) {
|
|
Pixel = ScreenBuffer[ScreenTop + (y - DeviceTop) / ZoomFactor]
|
|
[ScreenLeft + (x - DeviceLeft) / ZoomFactor];
|
|
sprintf(s, "Color = %3d [%3d, %3d, %3d], X = %3d, Y = %3d",
|
|
Pixel,
|
|
ColorMap[Pixel].Red,
|
|
ColorMap[Pixel].Green,
|
|
ColorMap[Pixel].Blue,
|
|
(x - DeviceLeft) / ZoomFactor,
|
|
(y - DeviceTop) / ZoomFactor);
|
|
|
|
setfillstyle(SOLID_FILL, BackGround);
|
|
bar(0, 0, textwidth(s) + 2, textheight(s) + 2);
|
|
|
|
setcolor(ForeGround);
|
|
outtextxy(1, 1, s);
|
|
|
|
line(0, y, DeviceMaxX, y);
|
|
line(x, 0, x, DeviceMaxY);
|
|
GetK = GetKey();
|
|
line(0, y, DeviceMaxX, y);
|
|
line(x, 0, x, DeviceMaxY);
|
|
|
|
Step = 10;
|
|
switch (GetK) {
|
|
case '1':
|
|
GetK = KEY_END;
|
|
break;
|
|
case '2':
|
|
GetK = KEY_DOWN;
|
|
break;
|
|
case '3':
|
|
GetK = KEY_PGDN;
|
|
break;
|
|
case '4':
|
|
GetK = KEY_LEFT;
|
|
break;
|
|
case '6':
|
|
GetK = KEY_RIGHT;
|
|
break;
|
|
case '7':
|
|
GetK = KEY_HOME;
|
|
break;
|
|
case '8':
|
|
GetK = KEY_UP;
|
|
break;
|
|
case '9':
|
|
GetK = KEY_PGUP;
|
|
break;
|
|
default:
|
|
Step = 1;
|
|
}
|
|
|
|
switch (GetK) {
|
|
case KEY_LEFT:
|
|
x -= Step;
|
|
break;
|
|
case KEY_RIGHT:
|
|
x += Step;
|
|
break;
|
|
case KEY_UP:
|
|
y -= Step;
|
|
break;
|
|
case KEY_DOWN:
|
|
y += Step;
|
|
break;
|
|
case KEY_PGUP:
|
|
y -= Step;
|
|
x += Step;
|
|
break;
|
|
case KEY_PGDN:
|
|
y += Step;
|
|
x += Step;
|
|
break;
|
|
case KEY_HOME:
|
|
y -= Step;
|
|
x -= Step;
|
|
break;
|
|
case KEY_END:
|
|
y += Step;
|
|
x -= Step;
|
|
break;
|
|
default:
|
|
setwritemode(COPY_PUT);
|
|
return;
|
|
}
|
|
if (x < DeviceLeft) x = DeviceLeft;
|
|
if (x >= DeviceRight) x = DeviceRight;
|
|
if (y < DeviceTop) y = DeviceTop;
|
|
if (y >= DeviceBottom) y = DeviceBottom;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Return non zero value if at list one character exists in keyboard queue. *
|
|
* This routine emulates kbhit() which do uses stdin and useless for us. *
|
|
******************************************************************************/
|
|
static int MyKbHit(void)
|
|
{
|
|
return bioskey(1);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Get a key from keyboard directly (bypass stdin as we might redirect it). *
|
|
* This routine emulates getch() which do uses stdin and useless for us. *
|
|
******************************************************************************/
|
|
static int MyGetCh(void)
|
|
{
|
|
static int Extended = 0;
|
|
int c;
|
|
|
|
if (Extended) {
|
|
c = Extended;
|
|
Extended = 0;
|
|
return c;
|
|
}
|
|
else {
|
|
c = bioskey(0);
|
|
if (c & 0x0ff)
|
|
return c;
|
|
else {
|
|
Extended = c >> 8;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Get a key from keyboard, and translating operational keys into special *
|
|
* codes (>255). Lower case characters are upercased. *
|
|
******************************************************************************/
|
|
static int GetKey(void)
|
|
{
|
|
char c;
|
|
|
|
while (TRUE) switch (c = MyGetCh()) {
|
|
case 0: /* Extended code - get the next extended char. */
|
|
switch (MyGetCh()) {
|
|
case 75: return KEY_LEFT;
|
|
case 77: return KEY_RIGHT;
|
|
case 72: return KEY_UP;
|
|
case 80: return KEY_DOWN;
|
|
case 71: return KEY_HOME;
|
|
case 79: return KEY_END;
|
|
case 73: return KEY_PGUP;
|
|
case 81: return KEY_PGDN;
|
|
case 83: return KEY_DELETE;
|
|
case 82: return KEY_INSERT;
|
|
}
|
|
break;
|
|
case 8:
|
|
return KEY_BSPACE;
|
|
case 10:
|
|
case 13:
|
|
return KEY_RETURN;
|
|
case 27:
|
|
return KEY_ESC;
|
|
default:
|
|
if (isprint(c)) {
|
|
if (islower(c))
|
|
return toupper(c);
|
|
else
|
|
return c;
|
|
}
|
|
else {
|
|
Tone(800, 100);
|
|
Tone(300, 200);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Routine to make some sound with given Frequency, Time milliseconds: *
|
|
******************************************************************************/
|
|
static void Tone(int Frequency, int Time)
|
|
{
|
|
if (BeepsDisabled) return;
|
|
|
|
sound(Frequency);
|
|
delay(Time);
|
|
nosound();
|
|
}
|