/***************************************************************************** * "Gif-Lib" - Yet another gif library. * * * * Written by: Gershon Elber Ver 0.1, Jul. 1989 * ****************************************************************************** * Program to display GIF file under X11 window system. * * Options: * * -q : quite printing mode. * * -p PosX PosY : defines the position where to put the image. * * -d Display : what display should go to. * * -f : force attempt to allocate the exact colors. This usually look bad... * * -h : on line help. * ****************************************************************************** * History: * * 28 Dec 89 - Version 1.0 by Gershon Elber, color allocation is based on the * * xgif program by John Bradley, bradley@cis.ipenn.edu. * *****************************************************************************/ #ifdef __MSDOS__ #include #include #include #include #include #include #endif /* __MSDOS__ */ #include #include #include #include #include #include #include #include "gif_lib.h" #include "getarg.h" #define PROGRAM_NAME "Gif2X11" #define ICON_SIZE 60 #define ABS(x) ((x) > 0 ? (x) : (-(x))) #ifdef __MSDOS__ extern unsigned int _stklen = 16384; /* Increase default stack size. */ #endif /* __MSDOS__ */ #ifdef SYSV static char *VersionStr = "Gif library module,\t\tGershon Elber\n\ (C) Copyright 1989 Gershon Elber, Non commercial use only.\n"; static char *CtrlStr = "Gif2X11 q%- p%-PosX|PosY!d!d d%-Display!s f%- h%- GifFile!*s"; #else 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%- p%-PosX|PosY!d!d d%-Display!s f%- h%- GifFile!*s"; #endif /* SYSV */ /* Make some variables global, so we could access them faster: */ static int PosFlag = FALSE, HelpFlag = FALSE, DisplayFlag = FALSE, ForceFlag = FALSE, ColorMapSize = 0, BackGround = 0, XPosX = 0, XPosY = 0, InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */ InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */ static char *DisplayName = NULL; static GifColorType *ColorMap; /* X specific staff goes here. XColorTable will hold the GIF image colors, */ /* while XPixelTable will hold the pixel number so we can redirect through */ /* it when forming the image bitmap in X format. */ /* Note the table has 256 entry which is the maximum allowed in GIF format. */ static XColor XColorTable[256]; static unsigned long XPixelTable[256]; static Display *XDisplay; static int XScreen; static Window Xroot, XImageWndw; static Colormap XColorMap; static GC XGraphContext; static Visual *XVisual; static XImage *XImageBuffer; static Pixmap XIcon; static Cursor XCursor; static void Screen2X(int argc, char **argv, GifRowType *ScreenBuffer, int ScreenWidth, int ScreenHeight); static void AllocateColors1(void); static void AllocateColors2(void); /****************************************************************************** * Interpret the command line and scan the given GIF file. * ******************************************************************************/ void main(int argc, char **argv) { int i, j, Error, NumFiles, ImageNum = 0, Size, Row, Col, Width, Height, ExtCode, Count; GifRecordType RecordType; GifByteType *Extension; char **FileName = NULL; GifRowType *ScreenBuffer; GifFileType *GifFile; if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifQuitePrint, &PosFlag, &XPosX, &XPosY, &DisplayFlag, &DisplayName, &ForceFlag, &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 (NumFiles == 1) { if ((GifFile = DGifOpenFileName(*FileName)) == NULL) { PrintGifError(); exit(-1); } } else { /* Use the stdin instead: */ #ifdef __MSDOS__ setmode(0, O_BINARY); #endif /* __MSDOS__ */ if ((GifFile = DGifOpenFileHandle(0)) == NULL) { PrintGifError(); exit(-1); } } /* Lets see if we can get access to the X server before we even start: */ if ((XDisplay = (Display *) XOpenDisplay(DisplayName)) == NULL) GIF_EXIT("Failed to access X server, abored."); XScreen = DefaultScreen(XDisplay); Xroot = RootWindow(XDisplay, XScreen); XColorMap = DefaultColormap(XDisplay, XScreen); XGraphContext = DefaultGC(XDisplay, XScreen); XVisual = DefaultVisual(XDisplay, XScreen); XSetBackground(XDisplay, XGraphContext, BlackPixel(XDisplay, XScreen)); XSetForeground(XDisplay, XGraphContext, WhitePixel(XDisplay, XScreen)); /* 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 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; 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) 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(); exit(-1); } 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 SBackGroundColor; ColorMap = (GifFile -> IColorMap ? GifFile -> IColorMap : GifFile -> SColorMap); ColorMapSize = 1 << (GifFile -> IColorMap ? GifFile -> IBitsPerPixel : GifFile -> SBitsPerPixel); Screen2X(argc, argv, ScreenBuffer, GifFile -> SWidth, GifFile -> SHeight); if (DGifCloseFile(GifFile) == GIF_ERROR) { PrintGifError(); exit(-1); } GifQprintf("\n"); } /****************************************************************************** * The real display routine. * ******************************************************************************/ static void Screen2X(int argc, char **argv, GifRowType *ScreenBuffer, int ScreenWidth, int ScreenHeight) { int i, j, c, Size, x, y, MinIntensity, MaxIntensity, AvgIntensity, IconSizeX, IconSizeY; char *XImageData, *XIconData, KeyBuffer[81]; double Aspect; GifByteType *OutLine, Data; unsigned long ValueMask; GifPixelType *Line; GifRowType *DitherBuffer; GifColorType *ColorMapEntry = ColorMap; XSetWindowAttributes SetWinAttr; XSizeHints Hints; XEvent Event; XExposeEvent *EEvent; XComposeStatus Stat; KeySym KS; /* Let find out what are the intensities in the color map: */ MaxIntensity = 0; MinIntensity = 256 * 100; for (i = 0; i < ColorMapSize; i++) { c = ColorMapEntry[i].Red * 30 + ColorMapEntry[i].Green * 59 + ColorMapEntry[i].Blue * 11; if (c > MaxIntensity) MaxIntensity = c; if (c < MinIntensity) MinIntensity = c; } AvgIntensity = (MinIntensity + MaxIntensity) / 2; /* The big trick here is to select the colors so lets do this first: */ if (ForceFlag) AllocateColors2(); else AllocateColors1(); SetWinAttr.background_pixel = BlackPixel( XDisplay, XScreen ); SetWinAttr.border_pixel = WhitePixel( XDisplay, XScreen ); ValueMask = CWBackPixel | CWBorderPixel; Hints.flags = PSize | PMinSize | PMaxSize; Hints.x = Hints.y = 1; Hints.width = Hints.min_width = Hints.max_width = ScreenWidth; Hints.height = Hints.min_height = Hints.max_height = ScreenHeight; if (PosFlag) { Hints.flags |= USPosition; Hints.x = XPosX; Hints.y = XPosY; } XImageWndw = XCreateWindow(XDisplay, Xroot, XPosX, XPosY, ScreenWidth, ScreenHeight, 1, 0, CopyFromParent, CopyFromParent, ValueMask, &SetWinAttr); /* Set up the icon bit map to be a shrinked BW version of the image: */ if (ScreenWidth > ScreenHeight) { IconSizeX = (ICON_SIZE / 8) * 8; IconSizeY = (ScreenHeight * ICON_SIZE) / ScreenWidth; } else { IconSizeY = ICON_SIZE; IconSizeX = (((ScreenWidth * ICON_SIZE) / ScreenHeight) / 8) * 8; } XIconData = (char *) malloc(IconSizeX * IconSizeY / 8); memset(XIconData, 0, IconSizeX * IconSizeY / 8); for (i = 0; i < IconSizeY; i++) { y = (i * ScreenHeight / IconSizeY); Size = i * IconSizeX / 8; for (j = 0; j < IconSizeX; j++) { x = j * ScreenWidth / IconSizeX; c = ScreenBuffer[y][x]; c = ColorMapEntry[c].Red * 30 + ColorMapEntry[c].Green * 59 + ColorMapEntry[c].Blue * 11 > AvgIntensity; XIconData[Size + j / 8] |= c << (j % 8); } } XIcon = XCreateBitmapFromData(XDisplay, XImageWndw, XIconData, IconSizeX, IconSizeY); XSetStandardProperties(XDisplay, XImageWndw, PROGRAM_NAME, PROGRAM_NAME, XIcon, argv, argc, &Hints); XSelectInput(XDisplay, XImageWndw, ExposureMask | KeyPressMask); /* Set out own cursor: */ XCursor = XCreateFontCursor(XDisplay, XC_diamond_cross); XDefineCursor(XDisplay, XImageWndw, XCursor); XMapWindow(XDisplay, XImageWndw); /* Create the image in X format: */ if ((XImageData = (char *) malloc(ScreenWidth * ScreenHeight)) == NULL) GIF_EXIT("Failed to allocate memory required, aborted."); for (i = 0; i < ScreenHeight; i++) { y = i * ScreenWidth; for (j = 0; j < ScreenWidth; j++) XImageData[y + j] = XPixelTable[ScreenBuffer[i][j]]; } XImageBuffer = XCreateImage(XDisplay, XVisual, 8, ZPixmap, 0, XImageData, ScreenWidth, ScreenHeight, 8, ScreenWidth); while (TRUE) { XNextEvent(XDisplay, &Event); switch (Event.type) { case Expose: EEvent = (XExposeEvent *) &Event; XPutImage(XDisplay, XImageWndw, XGraphContext, XImageBuffer, EEvent -> x, EEvent -> y, EEvent -> x, EEvent -> y, EEvent -> width, EEvent -> height); break; case KeyPress: XLookupString(&Event, KeyBuffer, 80, &KS, &Stat); if (KeyBuffer[0] == 3) return; break; } } } /****************************************************************************** * Routine to allocate the requested colors from the X server. * * Colors are allocated until success by stripping off the least bits of the * * colors. * ******************************************************************************/ static void AllocateColors1(void) { int Strip, Msk, i, j; char Msg[80]; for (i = 0; i < 256; i++) XPixelTable[i] = 0; /* Put reasonable color for out of range. */ for (Strip = 0, Msk = 0xff; Strip < 8; Strip++, Msk <<= 1) { for (i = 0; i < ColorMapSize; i++) { /* Prepere color entry in X format. */ XColorTable[i].red = (ColorMap[i].Red & Msk) << 8; XColorTable[i].green = (ColorMap[i].Green & Msk) << 8; XColorTable[i].blue = (ColorMap[i].Blue & Msk) << 8; XColorTable[i].flags = DoRed | DoGreen | DoBlue; if (XAllocColor(XDisplay, XColorMap, &XColorTable[i])) XPixelTable[i] = XColorTable[i].pixel; else break; } if (i < ColorMapSize) XFreeColors(XDisplay, XColorMap, XPixelTable, i, 0L); else break; } if (Strip == 8) GIF_EXIT("Can not display the image - not enough colors available."); if (Strip != 0) { sprintf(Msg, "%d bits were stripped off the color map.", Strip); GIF_MESSAGE(Msg); } } /****************************************************************************** * Routine to allocate the requested colors from the X server. * * Two stages are performed: * * 1. Colors are requested directly. * * 2. If not enough colors can be allocated, the closest current color * * in current table is selected instead. * * This allocation is not optimal as when fail to allocate all colors one * * should pick the right colors to do allocate in order to minimize the * * closest distance from the unallocated ones under some norm (what is a good * * norm for the RGB space?). Improve it if you are bored. * ******************************************************************************/ static void AllocateColors2(void) { int i, j, Index = 0, Count = 0, XNumOfColors; char Msg[80]; unsigned long D, Distance, AvgDistance = 0, Red, Green, Blue; GifBooleanType Failed = FALSE; XColor *XOldColorTable; for (i = 0; i < 256; i++) { if (i < ColorMapSize) { /* Prepere color entry in X format. */ XColorTable[i].red = ColorMap[i].Red << 8; XColorTable[i].green = ColorMap[i].Green << 8; XColorTable[i].blue = ColorMap[i].Blue << 8; XColorTable[i].flags = DoRed | DoGreen | DoBlue; XPixelTable[i] = -1; /* Not allocated yet. */ } else XPixelTable[i] = 0; /* Put reasonable color for out of range. */ } for (i = 0; i < ColorMapSize; i++) /* Allocate the colors from X: */ if (XAllocColor(XDisplay, XColorMap, &XColorTable[i])) XPixelTable[i] = XColorTable[i].pixel; else Failed = TRUE; if (Failed) { XNumOfColors = DisplayCells(XDisplay, XScreen); XOldColorTable = (XColor *) malloc(sizeof(XColor) * XNumOfColors); for (i = 0; i < XNumOfColors; i++) XOldColorTable[i].pixel = i; XQueryColors(XDisplay, XColorMap, XOldColorTable, XNumOfColors); for (i = 0; i < ColorMapSize; i++) { /* Allocate closest colors from X: */ if (XPixelTable[i] == -1) { /* Failed to allocate this one. */ Distance = 0xffffffff; Red = XColorTable[i].red; Green = XColorTable[i].green; Blue = XColorTable[i].blue; for (j = 0; j < XNumOfColors; j++) { /* Find the closest color in 3D RGB space using L1 norm. */ if ((D = ABS(Red - XOldColorTable[j].red) + ABS(Green - XOldColorTable[j].green) + ABS(Blue - XOldColorTable[j].blue)) < Distance) { Distance = D; Index = j; } } XPixelTable[i] = Index; AvgDistance += Distance; Count++; } } free(XOldColorTable); sprintf(Msg, "Colors will be approximated (average error = %d).\n", AvgDistance / Count); GIF_MESSAGE(Msg); } }