/**************************************************************************** * * LoadImage.c 23/09/89 by: Olaf 'Olsen' Barthel * Brabeckstrasse 35 * D-3000 Hannover 71 * * Federal Republic of Germany * * LoadImage 1.9 (C) Copyright 1989 by Olaf Barthel & ED Hannover * * Not even a single character of this wonderful piece of code * may be incorporated into any other program without the * author's consent. Yep, still a splendid time is guaranteed * for all! * ***************************************************************************** * * LoadImage is a kind of IFF-ILBM-reader with some very * remarkable features the authors of other readers did not even * dream of: * * × LoadImage loads anything that looks, smells and tastes like * an IFF-ILBM-file, even EHB & HAM are handled correctly. * * × LoadImage will accept overscanned pictures which require * 1MByte of chip ram. * * × You can scroll around in the bitmap if the picture is larger * in size than the current display. * * × LoadImage takes care of the machine it is running on, i.e. * PAL images will remain PAL images on a PAL Amiga and will * be transformed into NTSC-images on an NTSC Amiga. * * × LoadImage supports colour-cycling (interrupt code, no sick * subtask cycling). * * × LoadImage can display multiple images, i.e. you can enter more * than one file in the command line. If you are a bit lazy you * are hereby granted permission to use UNIX style wildcards * instead of a list of filenames. * * × LoadImage also works from workbench, simply hold down the * shift-key and select the icons of the images you wish to * load, then double-click the LoadImage-icon. * * × LoadImage includes graphicdump functions. Select the area * you wish to print and off it goes! * * LoadImage itself was written over a long period of time. I * started this project when I suddenly came upon an idea how to * resize IFF-ILBM-images. After a week of thought (back in * winter 1988) I rewrote the decompression routines and added * the LoadHeader() function. I continued to work on the program * in late spring 1989, sped up the bitmap reader and thought * about a colour-cycling routine. At last the scrolling was * added (with some help from John Hodgson) to support those very * l a r g e pictures generated by various DTP-scanners. In * version 1.5 I rewrote the chunk reading routine to speed * things up. So you won't have to shave your beard off anymore * when the picture finally appears on the screen. In version * 1.6 soft colour fading was added, a call to SetTaskPri() tries * to make the bitmap scrolling a bit smoother. * * Some routines included in this program are part of a larger * collection of routines which I called ImageTools (please don't * mix them up with the ImageTools utilities by Stephen * Vermeulen). So, please don't be too greedy, some stuff is * copyrighted and almost sold to a german publisher. * * Anyway, have fun with the code. Even though I did not use the * original EA IFF 85 routines ("Why stop now when I'm hating * it?") the reader works quite well and is a bit too tolerant * for the IFF-restrictions Commodore-Amiga, Inc. demands. * * Olsen * ****************************************************************************/ /* End of header, includes start here. */ #include #include #include #include #include #include #include /* Some useful macros. */ #define byte(n) (((n + 15) >> 4) << 1) /* Word aligned width in bytes. */ /* The real BitMapHeader... "I ain't 'fraid of no pic!" */ typedef struct { UWORD w,h; /* Raster width & height. */ UWORD x,y; /* Pixel position. */ UBYTE nPlanes; /* Number of source bitplanes. */ UBYTE masking; /* Masking... good for nothing maybe? */ UBYTE compression; /* Packed or unpacked? */ UBYTE pad1; /* We don't like odd length structures. */ UWORD transparentColor; /* Maybe good for... */ UBYTE xAspect, yAspect; /* Kind of quotient, width / height. */ WORD pageWidth, pageHeight;/* Source page size. */ } BitMapHeader; /* This is a full featured DPaint ColourRange chunk! */ typedef struct { WORD pad1; /* Odd length? Nope. */ WORD rate; /* Speed! */ WORD active; /* What shall we do with a drunken... */ UBYTE low,high; /* That's where we start and stop. */ } CRange; typedef struct { LONG IFF_Type; LONG IFF_Length; } IFF_Chunk; /* The cycling code needs this to be defined externally. */ CRange CycleRange[6]; /* Some forward declarations. */ extern void *AllocMem(); extern struct Library *OpenLibrary(); extern struct Window *OpenWindow(); extern struct Screen *OpenScreen(); extern struct MenuItem *ItemAddress(); extern struct Message *GetMsg(); extern struct MsgPort *CreatePort(); extern struct Task *FindTask(); extern PLANEPTR AllocRaster(); extern struct IORequest *CreateExtIO(); extern char *malloc(); extern char *scdir(); /* Some more fontdata for the AutoRequest soon to come. */ struct TextAttr AboutFont[3] = { {(UBYTE *)"topaz.font",8,FS_NORMAL ,FPF_ROMFONT}, {(UBYTE *)"topaz.font",8,FSF_BOLD ,FPF_ROMFONT}, {(UBYTE *)"topaz.font",8,FSF_ITALIC,FPF_ROMFONT} }; /* The menu text is to follow soon. */ struct IntuiText MenuIntTxt[9] = { {0,1,JAM2,2,1,&AboutFont[0],(UBYTE *)"About LoadImage... ",NULL}, {0,1,JAM1,2,1,&AboutFont[1],(UBYTE *)"________________________",NULL}, {0,1,JAM2,2,1,&AboutFont[0],(UBYTE *)"Title Bar ",NULL}, {0,1,JAM2,2,1,&AboutFont[0],(UBYTE *)"Cycling ",NULL}, {0,1,JAM2,2,1,&AboutFont[0],(UBYTE *)"Print Standard ",NULL}, {0,1,JAM2,2,1,&AboutFont[0],(UBYTE *)" Enlarged ",NULL}, {0,1,JAM1,2,1,&AboutFont[1],(UBYTE *)"________________________",NULL}, {0,1,JAM2,2,1,&AboutFont[0],(UBYTE *)"Next Image ",NULL}, {0,1,JAM2,2,1,&AboutFont[0],(UBYTE *)"Quit LoadImage ",NULL} }; /* Now transform it into a chain of MenuItems. */ struct MenuItem MenuItem[9] = { {&MenuItem[1],4, 0,196,10, 86,0,(APTR)&MenuIntTxt[0],NULL,'?',NULL,NULL}, {&MenuItem[2],4, 3,196,10,210,0,(APTR)&MenuIntTxt[1],NULL, 0,NULL,NULL}, {&MenuItem[3],4,13,196,10, 86,0,(APTR)&MenuIntTxt[2],NULL,'T',NULL,NULL}, {&MenuItem[4],4,23,196,10, 86,0,(APTR)&MenuIntTxt[3],NULL,'C',NULL,NULL}, {&MenuItem[5],4,33,196,10, 86,0,(APTR)&MenuIntTxt[4],NULL,'S',NULL,NULL}, {&MenuItem[6],4,43,196,10, 86,0,(APTR)&MenuIntTxt[5],NULL,'E',NULL,NULL}, {&MenuItem[7],4,46,196,10,210,0,(APTR)&MenuIntTxt[6],NULL, 0,NULL,NULL}, {&MenuItem[8],4,56,196,10, 86,0,(APTR)&MenuIntTxt[7],NULL,'N',NULL,NULL}, {NULL, 4,66,196,10, 86,0,(APTR)&MenuIntTxt[8],NULL,'Q',NULL,NULL} }; /* "This is what you need!" ... a menu definition! */ struct Menu Menu = { NULL,0,0,112,0,257,"LoadImage 1.9",&MenuItem[0] }; /* "Don't wait for me, I'm still typing." */ struct IntuiText AboutTxt[15] = { {3,1,JAM1,8, 4,&AboutFont[1],(UBYTE *)"LoadImage v1.9", &AboutTxt[ 1]}, {2,1,JAM1,8, 16,&AboutFont[0],(UBYTE *)"Was written by Olaf 'Olsen'", &AboutTxt[ 2]}, {2,1,JAM1,8, 24,&AboutFont[0],(UBYTE *)"Barthel of ED Electronic", &AboutTxt[ 3]}, {2,1,JAM1,8, 32,&AboutFont[0],(UBYTE *)"Design Hannover. This", &AboutTxt[ 4]}, {2,1,JAM1,8, 40,&AboutFont[0],(UBYTE *)"version supports EHB & HAM", &AboutTxt[ 5]}, {2,1,JAM1,8, 48,&AboutFont[0],(UBYTE *)"pictures and will also take", &AboutTxt[ 6]}, {2,1,JAM1,8, 56,&AboutFont[0],(UBYTE *)"care of oversized pictures", &AboutTxt[ 7]}, {2,1,JAM1,8, 64,&AboutFont[0],(UBYTE *)"which require 1M of chip ram.", &AboutTxt[ 8]}, {2,1,JAM1,8, 76,&AboutFont[0],(UBYTE *)"Press the left mouse button", &AboutTxt[ 9]}, {2,1,JAM1,8, 84,&AboutFont[0],(UBYTE *)"and move the pointer towards", &AboutTxt[10]}, {2,1,JAM1,8, 92,&AboutFont[0],(UBYTE *)"the borders of the screen to", &AboutTxt[11]}, {2,1,JAM1,8,100,&AboutFont[0],(UBYTE *)"scroll around in overscanned", &AboutTxt[12]}, {2,1,JAM1,8,108,&AboutFont[0],(UBYTE *)"images.", &AboutTxt[13]}, {0,1,JAM1,8,122,&AboutFont[2],(UBYTE *)"(C) Copyright 1989 by...", &AboutTxt[14]}, {0,1,JAM1,8,130,&AboutFont[2],(UBYTE *)"ED Electronic Design Hannover", NULL} }; /* We've got a body text, let's see if there's a negative text * around. */ struct IntuiText Proceed = { 0,1,JAM1,5,3, &AboutFont[0], (UBYTE *)"Understood", NULL }; /* Yes... we will open a CUSTOMBITMAP screen. */ struct BitMap ScreenMap; /* If the picture is smaller than the screen. */ struct BitMap TinyBitMap; /* Must be defined externally since three routines need it. * There could have been an easier way to do this, but it was * me who programmed it and not a "reentrant-code-fanatic". */ BitMapHeader InfoHeader; /* Wait and see what this will be! */ struct NewScreen NewScreen = { 0,0,0,0,0, /* Gets filled in later. */ 0,1, /* Pens. */ NULL, /* ViewModes, get filled in later. */ CUSTOMSCREEN | SCREENBEHIND | CUSTOMBITMAP, &AboutFont[0], /* Just insure that the menu looks cute. */ (STRPTR)"LoadImage 1.9", /* Some title text. */ NULL, /* No gagdets. */ &ScreenMap /* Custom bitmap! */ }; /* What do we need this window for? We've got a menu and * screens don't like menus very much... */ struct NewWindow NewWindow = { 0,0, /* These get filled in later. */ 0,0, 0,1, /* Pens. */ MOUSEBUTTONS | MENUPICK | MENUVERIFY | MOUSEMOVE | RAWKEY, /* We want to know about this. */ BACKDROP | BORDERLESS | RMBTRAP, /* This is how it should look like. */ (struct Gadget *)NULL, /* Don't need a gadget. */ (struct Image *)NULL, /* Don't need a checkmark either. */ (STRPTR)NULL, /* Don't need a title. */ (struct Screen *)NULL, /* This gets filled in later. */ (struct BitMap *)NULL, /* Don't need it, really. */ 0,0, /* Minimum dimensions. */ 0,0, /* Maximum dimensions. */ CUSTOMSCREEN /* No Workbench, this is MY window. */ }; /* External symbols the linker wants. */ struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; struct Screen *Screen; struct Window *Window; struct IntuiMessage *Massage; /* Additional window message data. */ ULONG Class; USHORT Code; /* Macro expressions to set the pointer for our window. */ #define SetPoint(wind) SetPointer(wind,PointData,7,16,-4,-3) #define SetSnooze(wind) SetPointer(wind,Snooze,22,16,-9,-13) #define SetSize(wind) SetPointer(wind,SizeData,13,16,-4,-3) /* Sprite dump for the pointer image. */ USHORT PointData[] = { 0x0000,0x0000, 0x0000,0x1000, 0x1000,0x0000, 0x1000,0x1000, 0x6C00,0xAA00, 0x1000,0x1000, 0x1000,0x0000, 0x0000,0x1000, 0x0000,0x0000 }; /* Sprite dump for the Workbench balloon. */ USHORT Snooze[] = { 0x0000,0x0000, 0x0700,0x0000, 0x0FA0,0x0700, 0x3FF0,0x0FA0, 0x70F8,0x3FF0, 0x7DFC,0x3FF8, 0xFBFC,0x7FF8, 0x70FC,0x3FF8, 0x7FFE,0x3FFC, 0x7F0E,0x3FFC, 0x3FDF,0x1FFE, 0x7FBE,0x3FFC, 0x3F0E,0x1FFC, 0x1FFC,0x07F8, 0x07F8,0x01E0, 0x01E0,0x0080, 0x07C0,0x0340, 0x0FE0,0x07C0, 0x0740,0x0200, 0x0000,0x0000, 0x0070,0x0020, 0x0078,0x0038, 0x0038,0x0010, 0x0000,0x0000 }; USHORT SizeData[] = { 0x0000,0x0000, 0x0000,0x1000, 0x1000,0x0000, 0x1000,0x1000, 0x6C00,0xAA00, 0x1000,0x1000, 0x1000,0x0000, 0x0000,0x1000, 0x0000,0x0000, 0x0D77,0x0D77, 0x1114,0x1114, 0x1D27,0x1D27, 0x0544,0x0544, 0x1977,0x1977, 0x0000,0x0000 }; /* This is where the colours go when the About item is * selected. */ UWORD PrefColours[32]; struct Preferences StandardPrefs; /* These little "quickies" disable the CTRL-C abort function. */ #define BreakCheck() (SetSignal(0,0) & SIGBREAKF_CTRL_C) #define BreakReset() SetSignal(0,SIGBREAKF_CTRL_C) /* Chk_Abort() : * * The system calls this during I/O. Returns 0 to tell * it that there is no CTRL-C around. */ Chk_Abort() { return(0); } /* _wb_parse(pp,wbm) : * * Modified Workbench tool parsing routine. This one * always opens a standard output window if the program * has been started from Workbench. Original version * Copyright (C) 1986,1987 by Manx Software Systems, Inc. */ void _wb_parse(pp,wbm) register struct Process *pp; struct WBStartup *wbm; { register struct FileHandle *FileHandlePtr; /* BCPL window pointer. */ register LONG FileWindow; /* DOS window pointer. */ static char WindowTitle[40 + 18]; /* Title buffer. */ register LONG i,j; /* Counters. */ /* Set up the window title. */ strcpy(WindowTitle,"CON:0/11/640/80/"); i = j = 0; /* Find the end of the window title string. */ while(WindowTitle[i]) i++; /* Append the tool name. */ while((WindowTitle[i++] = wbm -> sm_ArgList -> wa_Name[j++]) && j != 40); /* Window title null termination. */ WindowTitle[40 + 17] = 0; /* Open the window. */ if(FileWindow = (LONG)Open(WindowTitle, MODE_OLDFILE)) { /* Convert window pointer to BPTR. */ FileHandlePtr = (struct FileHandle *)(FileWindow << 2); pp -> pr_ConsoleTask = (APTR)FileHandlePtr -> fh_Type; /* Set up console IO streams. */ pp -> pr_CIS = (BPTR)FileWindow; pp -> pr_COS = (BPTR)Open("*", MODE_OLDFILE); } } /* MakeID(IDString) : * * Transforms a string into a chunk ID. How can we do it * in a macro expression? Just don't know. */ LONG MakeID(IDString) char *IDString; { LONG LongID; LongID = (IDString[0] << 24) | (IDString[1] << 16) | (IDString[2] << 8) | (IDString[3]); return(LongID); } /* FindChunk(ChunkName,FilePointer) : * * Will try to find a chunk ID somewhere in the * file. If it doesn't find any it returns FALSE. * This routine was somewhat inspired by the * Chunk-reader to be found on the disk distributed * along with the FutureSound sound sampling hardware. * Some months later I through the 'crap' out. Instead * of years this will take you only some moments to * load now. */ BOOL FindChunk(ChunkName,FilePointer) register char *ChunkName; register FILE *FilePointer; { IFF_Chunk Chunk; LONG OldPosition; LONG FormType,WeWant; /* Set up the chunk type. */ WeWant = MakeID(ChunkName); /* Remember the initial file position. */ OldPosition = ftell(FilePointer); /* Reset the form type. */ FormType = 0; for(;;) { /* Try to read the chunk. */ if(fread(&Chunk,sizeof(Chunk),1,FilePointer) != 1) { /* If it went wrong, reset the * file position. */ fseek(FilePointer,OldPosition,0); return(FALSE); } /* If this is supposed to be a FORM chunk, * try to figure out the form type. */ if(OldPosition == 0 && FormType == 0 && Chunk . IFF_Type == MakeID("FORM")) { fread(&FormType,sizeof(LONG),1,FilePointer); /* Is it the type we want? */ if(FormType == WeWant) return(TRUE); continue; } /* Is this what we want? */ if(Chunk . IFF_Type == WeWant) return(TRUE); /* Else, skip the length information. */ fseek(FilePointer,Chunk . IFF_Length,1); } } /* LoadHeader(FileName,BMHeader) : * * Does two jobs for us: Initializes the BitMapHeader * and tries to figure out the Amiga ViewModes this * file needs. Returns -1 on failure, so be careful. */ LONG LoadHeader(FileName,BMHeader) char *FileName; BitMapHeader *BMHeader; { register LONG i; FILE *ImgFile; LONG ViewModes = NULL; /* No such file? */ if(!(ImgFile = fopen(FileName,"r"))) return(-1); /* No BMHD-Chunk? */ if(!FindChunk("BMHD",ImgFile)) { fclose(ImgFile); return(-1); } /* Read the header. */ fread(BMHeader,sizeof(BitMapHeader),1,ImgFile); /* Strange values, probably not a picture but a * "mistake" or a CMAP. */ if(BMHeader -> nPlanes < 1 || BMHeader -> nPlanes > 8) { fclose(ImgFile); return(-1); } /* If we don't find a CAMG chunk in the file * we will have to guess the right * ViewModes. This line takes care of the * interlaced display mode. */ if(BMHeader -> pageHeight > GfxBase -> NormalDisplayRows) ViewModes |= LACE; /* Could it be HIRES? */ if(BMHeader -> pageWidth >= 640) ViewModes |= HIRES; /* It is still much more likely to encounter a * HAM picture than an EHB picture. If we are * wrong with this assumption, the CAMG chunk * will tell us (hope so). */ if(BMHeader -> nPlanes == 6) ViewModes |= HAM; /* Hello out there, got any CAMG chunk? */ if(!FindChunk("CAMG",ImgFile)) { fclose(ImgFile); return(ViewModes); } /* Read it then. */ fread(&ViewModes,sizeof(LONG),1,ImgFile); /* Mask out all unwanted bits (thanks, Carolyn!). */ ViewModes &= (~(SPRITES | VP_HIDE | GENLOCK_AUDIO | GENLOCK_VIDEO) | 0xFFFF); /* Finish it. */ fclose(ImgFile); return(ViewModes); } /* LoadCMAP(FileName,ColourMap,MaxCol,BMHeader) : * * Will load an unsigned word array with the * colours to be found in the CMAP chunk. */ LONG LoadCMAP(FileName,ColourMap,MaxCol,BMHeader) char *FileName; UWORD ColourMap[]; LONG MaxCol; BitMapHeader *BMHeader; { register LONG i; register FILE *ColFile; register LONG R,G,B; /* Are you there? */ if(!(ColFile = fopen(FileName,"r"))) return(0); /* Black 'n white or colour TV? */ if(!FindChunk("CMAP",ColFile)) { fclose(ColFile); return(0); } /* Correct it before the reader believes it! */ if(MaxCol < 2) MaxCol = 1 << BMHeader -> nPlanes; /* A bit too large, innit? */ if(MaxCol > 32) MaxCol = 32; /* Read those colours. */ for(i = 0 ; i < MaxCol ; i++) { R = fgetc(ColFile) >> 4; G = fgetc(ColFile) >> 4; B = fgetc(ColFile) >> 4; /* The transformation. */ ColourMap[i] = (R << 8) | (G << 4) | (B); } /* Finish it. */ fclose(ColFile); return(MaxCol); } /* LoadRaster(FileName,BitPlanes,BMHeader) : * * Will decompress the interleaved bitmap data * into a set of bitplanes. */ BOOL LoadRaster(FileName,BitPlanes,BMHeader) char *FileName; PLANEPTR BitPlanes[]; BitMapHeader *BMHeader; { register LONG i,j,k; UBYTE Value,SoFar,Compr,Depth; BYTE ChkVal; LONG Height,Width; register PLANEPTR Planes[9]; /* 9 for possible bitmask. */ FILE *PicFile; /* Clear the planes. */ for(i = 0 ; i < 9 ; i++) Planes[i] = NULL; /* Set up the working copies. */ Width = byte(BMHeader -> w); Height = BMHeader -> h; Depth = BMHeader -> nPlanes; Compr = BMHeader -> compression; /* Is there something wrong in paradise? */ if(Compr > 1 || !BitPlanes) return(FALSE); /* Can we read it, please? */ if(!(PicFile = fopen(FileName,"r"))) return(FALSE); /* No BODY? What is this? */ if(!FindChunk("BODY",PicFile)) { fclose(PicFile); return(FALSE); } /* Copy the bitmap pointers since their * contents will get changed. */ for(i = 0 ; i < Depth ; i++) Planes[i] = BitPlanes[i]; /* Very well, nobody told me that DPaint and Aegis Images * are allowed to save their own home-brewn BODY chunks * if the transparent colour is nonzero or the * stencil/behind function is used. In this case the * interleaved plane data is immediately followed by * a bitmask which is to clear out all unwanted pixels * after the image is drawn. To support this attitude * we increment the depth of the image to give the * reader access to a blank pointer the bitmask will * be sent to. */ if(BMHeader -> masking == 1) Depth++; /* No compression, take the data as is. */ if(Compr == 0) { for(k = 0 ; k < Height ; k++) { for(j = 0 ; j < Depth ; j++) { if(Planes[j]) { fread(Planes[j],Width,1,PicFile); Planes[j] += Width; } else fseek(PicFile,Width,1); } } } /* ByteRun1 compression, efficient but tricky. */ if(Compr == 1) { for(k = 0 ; k < Height ; k++) { for(j = 0 ; j < Depth ; j++) { for(SoFar = 0 ; SoFar < Width ; ) { ChkVal = fgetc(PicFile); /* Read the next bytes. */ if(ChkVal > 0) { if(Planes[j]) { fread(Planes[j],ChkVal + 1,1,PicFile); Planes[j] += ChkVal + 1; } else fseek(PicFile,ChkVal + 1,1); SoFar += ChkVal + 1; } else { /* Set the memory to this * value. */ if(ChkVal != 128) { Value = fgetc(PicFile); for(i = 0 ; i <= -ChkVal ; i++) { if(Planes[j]) { *Planes[j] = Value; Planes[j]++; } SoFar++; } } } } } } } /* Finish it up. */ fclose(PicFile); return(TRUE); } /* InitTinyBitMap() : * * If the picture to be loaded is smaller than the * screen it is to appear on, a custom bitmap is * initialized to receive the image data. */ BOOL InitTinyBitMap() { register short i; /* So the Blitter won't be confused. */ InitBitMap(&TinyBitMap,InfoHeader . nPlanes,InfoHeader . w,InfoHeader . h); /* Try to steal some memory for the tiny bitmap. */ for(i = 0 ; i < InfoHeader . nPlanes ; i++) if(!(TinyBitMap . Planes[i] = (PLANEPTR)AllocRaster(InfoHeader . w,InfoHeader . h))) return(FALSE); InitBitMap(&ScreenMap,InfoHeader . nPlanes,NewScreen . Width,NewScreen . Height); /* Try to steal some memory for the bitplanes. */ for(i = 0 ; i < InfoHeader . nPlanes ; i++) if(!(ScreenMap . Planes[i] = (PLANEPTR)AllocRaster(NewScreen . Width,NewScreen . Height))) return(FALSE); return(TRUE); } /* CloseLibs() : * * Closes the libraries and exits. */ void CloseLibs() { /* Libraries? Who wanted the libraries? */ if(GfxBase); CloseLibrary(GfxBase); if(IntuitionBase) CloseLibrary(IntuitionBase); /* Take care of external signals. */ BreakReset(); /* Return the control to DOS. See what it's doing with * it. */ exit(0); } /* CloseAll() : * * Closes anything that we have opened but the * libraries. */ void CloseAll() { register short i; /* Canst thou hear me? Finish what you have started. */ ClearCycleCode(); /* A window! */ if(Window) { /* Hide the menu. */ Window -> Flags |= RMBTRAP; /* We don't need the menu any more. */ ClearMenuStrip(Window); CloseWindow(Window); Window = NULL; } /* Is there a screen anywhere? */ if(Screen) { CloseScreen(Screen); Screen = NULL; } /* Get rid of the planes. */ for(i = 0 ; i < ScreenMap . Depth ; i++) { if(TinyBitMap . Planes[i]) { FreeRaster(TinyBitMap . Planes[i],InfoHeader . w,InfoHeader . h); TinyBitMap . Planes[i] = NULL; if(ScreenMap . Planes[i]) { FreeRaster(ScreenMap . Planes[i],NewScreen . Width,NewScreen . Height); ScreenMap . Planes[i] = NULL; } continue; } if(ScreenMap . Planes[i]) { FreeRaster(ScreenMap . Planes[i],InfoHeader . w,InfoHeader . h); ScreenMap . Planes[i] = NULL; } } } /* GetPath(InitLock) : * * Fills the PathString with the path name associated * with InitLock. This code fragment is based on the * do_cd() function in Matt Dillon's shell. I shortened * the code a bit, since we don't need to keep the * pathstring in the current pr_CLI -> cli_SetName. * * Or do we? */ char * GetPath(InitLock) struct FileLock *InitLock; { struct FileLock *TempLock; /* "copy of Lock" */ struct FileInfoBlock *FileInfo; /* We need the fib_FileName */ register LONG count,Length; /* Loop counter, string length */ register char *FileName; /* Temporary pointer */ static char PathString[256];/* Buffer to receive full * path name. */ /* Forward declarations. */ struct FileLock *DupLock(),*ParentDir(); /* Longword alignment... */ if(!(FileInfo = (struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC | MEMF_CLEAR))) return(NULL); /* Attempt to duplicate initial lock; I don't expect * this to go wrong, but let's be careful, you can * never know what Commodore's doing with Tripos... */ if(!(TempLock = (struct FileLock *)DupLock(InitLock))) { FreeMem(FileInfo,sizeof(struct FileInfoBlock)); return(NULL); } count = 255; /* As long as a parent directory can be found... */ while(TempLock) { /* To avoid crashes we take a closer look at * the DirEntryType; maybe we will try to * get the parent directory to a file? */ Examine(TempLock,FileInfo); if(FileInfo -> fib_DirEntryType < 0) { UnLock(TempLock); FreeMem(FileInfo,sizeof(struct FileInfoBlock)); return(NULL); } /* Take a look at the parent directory. */ InitLock = (struct FileLock *)ParentDir(TempLock); FileName = FileInfo -> fib_FileName; /* Null string? Probably bug in 1.1/1.2 * Ram-Handler (should be fixed in 1.3, beta * version crashes, though). */ if(!FileName[0]) FileName = "RAM"; Length = strlen(FileName); /* Parent directory available?. */ if(InitLock) { /* Append name of parent directory. */ if(count == 255) { count -= Length; CopyMem(FileName,PathString + count,Length); } else { count -= Length + 1; CopyMem(FileName,PathString + count,Length); PathString[count + Length] = '/'; } } else { count -= Length + 1; CopyMem(FileName,PathString + count,Length); PathString[count + Length] = ':'; } /* Continue with the next directory. */ UnLock(TempLock); TempLock = InitLock; } /* Clean the things up... */ FreeMem(FileInfo,sizeof(struct FileInfoBlock)); CopyMem(PathString + count,PathString,256 - count); /* Let's be safe, append a slash. */ if(PathString[strlen(PathString) - 1] != '/' && PathString[strlen(PathString) - 1] != ':') PathString[strlen(PathString)] = '/'; /* Return a pointer to the path string. */ return(PathString); } /* QuickSort(av,n) : * * Quicksort routine borrowed from Steve Drew's revision * of Matt Dillon's Shell. Please don't ask me how it * works, I forgot it some moments after my Pascal teacher * had explained it to me in detail. */ QuickSort(av,n) register char *av[]; register LONG n; { register LONG b; if(n > 0) { b = QSplit(av,n); QuickSort(av,b); QuickSort(av + b + 1,n - b - 1); } } /* QSplit(av,n) : * * Quicksort subroutine to save stack space. */ QSplit(av,n) register char *av[]; register LONG n; { register LONG i,b; register char *element,*scr; element = av[0]; for(b = 0,i = 1 ; i < n ; ++i) { if(strcmp(av[i],element) < 0) { ++b; scr = av[i]; av[i] = av[b]; av[b] = scr; } } scr = av[0]; av[0] = av[b]; av[b] = scr; return(b); } /* StripPath(s): * * The Lattice support library contains a function like * this one, the Aztec lib doesn't. StripPath does not * modify the contents of the file name it receives * but returns a pointer to the start of the real * file name without path identification. */ char * StripPath(s) char *s; { long i,start = -1; /* Start at the bottom, move up to the top. */ for(i = strlen(s) - 1 ; i >= 0 ; i--) { if(s[i] == '/' || s[i] == ':') break; start = i; } /* Sorry kid... */ if(start == -1) return(NULL); /* Here we go. */ return(s + start); } /* FadeOut(VPort,CMap,Count): * * Fades the colours of a ViewPort out (colour -> black). */ void FadeOut(VPort,CMap,Count) struct ViewPort *VPort; UWORD *CMap; long Count; { UWORD TempMap[32]; BYTE Red,Green,Blue; register long i,j; for(i = 1 ; i < 16 ; i++) { for(j = 0 ; j < Count ; j++) { Red = ((CMap[j] >> 8) & 0x0F) - i; Green = ((CMap[j] >> 4) & 0x0F) - i; Blue = ((CMap[j] ) & 0x0F) - i; if(Red < 0) Red = 0; if(Green < 0) Green = 0; if(Blue < 0) Blue = 0; TempMap[j] = (UWORD)Red << 8 | (UWORD)Green << 4 | (UWORD)Blue; } Delay(2); LoadRGB4(VPort,TempMap,Count); } } /* FadeIn(VPort,CMap,Count): * * Fades the colours of a ViewPort in (black -> colour). */ void FadeIn(VPort,CMap,Count) struct ViewPort *VPort; UWORD *CMap; long Count; { UWORD TempMap[32]; BYTE Red,Green,Blue; register long i,j; for(i = 15 ; i >= 0 ; i--) { for(j = 0 ; j < Count ; j++) { Red = ((CMap[j] >> 8) & 0x0F) - i; Green = ((CMap[j] >> 4) & 0x0F) - i; Blue = ((CMap[j] ) & 0x0F) - i; if(Red < 0) Red = 0; if(Green < 0) Green = 0; if(Blue < 0) Blue = 0; TempMap[j] = (UWORD)Red << 8 | (UWORD)Green << 4 | (UWORD)Blue; } Delay(2); LoadRGB4(VPort,TempMap,Count); } } /* PrintScreen(Large): * * Sends the current LoadImage screen to the printer. */ void PrintScreen(Large) BOOL Large; { struct IODRPReq *PrinterDump; struct MsgPort *PrinterPort; BOOL HasTitle = (Screen -> Flags & SHOWTITLE) ? TRUE : FALSE; BOOL Cycling = IsCycling(); SHORT TopEdge,LeftEdge,BottomEdge,RightEdge; SetSnooze(Window); /* IO Replyport. */ if(PrinterPort = (struct MsgPort *)CreatePort(NULL,0)) { /* Custom RastPort dump structure. */ if(PrinterDump = (struct IODRPReq *)CreateExtIO(PrinterPort,sizeof(struct IODRPReq))) { /* Try to open the device. */ if(!OpenDevice("printer.device",0,PrinterDump,0)) { SetSize(Window); SetDrMd(Window -> RPort,COMPLEMENT); /* Don't let anyone disturb the * LoadImage screen bitmap. */ Window -> Flags |= RMBTRAP; if(HasTitle) ShowTitle(Screen,FALSE); if(Cycling) ToggleCycleCode(); /* Let the user set the initial * point. */ FOREVER { WaitPort(Window -> UserPort); if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort)) { Class = Massage -> Class; Code = Massage -> Code; ReplyMsg(Massage); } if(Class == MOUSEBUTTONS && Code == MENUDOWN) { DisplayBeep(Screen); goto HitAndQuit; } if(Class == MOUSEBUTTONS && Code == SELECTDOWN) { LeftEdge = Window -> MouseX; TopEdge = Window -> MouseY; break; } } BottomEdge = TopEdge; RightEdge = LeftEdge; /* Now let him resize the rubber box * to the right dimensions. */ ReportMouse(TRUE,Window); FOREVER { USHORT Qualifier; Move(Window -> RPort,LeftEdge,TopEdge); Draw(Window -> RPort,RightEdge,TopEdge); Draw(Window -> RPort,RightEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,TopEdge); WaitLoop: WaitPort(Window -> UserPort); if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort)) { Class = Massage -> Class; Code = Massage -> Code; Qualifier = Massage -> Qualifier; ReplyMsg(Massage); } if((Class == MOUSEBUTTONS && Code == MENUDOWN) || Class == RAWKEY) { DisplayBeep(Screen); Move(Window -> RPort,LeftEdge,TopEdge); Draw(Window -> RPort,RightEdge,TopEdge); Draw(Window -> RPort,RightEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,TopEdge); goto HitAndQuit; } if(Class == MOUSEBUTTONS && Code == SELECTUP) { if((RightEdge <= LeftEdge || BottomEdge <= TopEdge) && !(Qualifier & IEQUALIFIER_RBUTTON)) continue; Move(Window -> RPort,LeftEdge,TopEdge); Draw(Window -> RPort,RightEdge,TopEdge); Draw(Window -> RPort,RightEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,TopEdge); if(Qualifier & IEQUALIFIER_RBUTTON) goto HitAndQuit; break; } if(Class == MOUSEMOVE) { Move(Window -> RPort,LeftEdge,TopEdge); Draw(Window -> RPort,RightEdge,TopEdge); Draw(Window -> RPort,RightEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,BottomEdge); Draw(Window -> RPort,LeftEdge,TopEdge); RightEdge = Window -> MouseX; BottomEdge = Window -> MouseY; continue; } goto WaitLoop; } /* Initialize the IO Request for * a RastPort dump. */ PrinterDump -> io_Command = PRD_DUMPRPORT; PrinterDump -> io_RastPort = &Screen -> RastPort; PrinterDump -> io_ColorMap = Screen -> ViewPort . ColorMap; PrinterDump -> io_Modes = Screen -> ViewPort . Modes; PrinterDump -> io_SrcWidth = RightEdge - LeftEdge; PrinterDump -> io_SrcHeight = BottomEdge - TopEdge; PrinterDump -> io_SrcX = LeftEdge; PrinterDump -> io_SrcY = TopEdge; /* Print it as large as possible? */ if(Large) { PrinterDump -> io_DestCols = 0; PrinterDump -> io_Special = SPECIAL_FULLCOLS | SPECIAL_ASPECT; } else { PrinterDump -> io_DestCols = RightEdge - LeftEdge; PrinterDump -> io_Special = SPECIAL_MILCOLS | SPECIAL_ASPECT; /* I sincerely hope that this aspect * doctoring has any sensible effect. */ /* if(!(PrinterDump -> io_Modes & HIRES) && !(PrinterDump -> io_Modes & LACE)) PrinterDump -> io_DestCols *= 10; if(!(PrinterDump -> io_Modes & HIRES) && (PrinterDump -> io_Modes & LACE)) PrinterDump -> io_DestCols *= 20; if((PrinterDump -> io_Modes & HIRES) && !(PrinterDump -> io_Modes & LACE)) PrinterDump -> io_DestCols *= 5; if((PrinterDump -> io_Modes & HIRES) && (PrinterDump -> io_Modes & LACE)) PrinterDump -> io_DestCols *= 10; */ PrinterDump -> io_DestCols *= 11; } SetSnooze(Window); /* Try the dump. */ if(DoIO(PrinterDump)) DisplayBeep(Screen); /* Re-enable the window. */ HitAndQuit: Window -> Flags &= ~RMBTRAP; SetPoint(Window); /* If set, re-enable the screen title. */ if(HasTitle) ShowTitle(Screen,TRUE); if(Cycling) ToggleCycleCode(); ReportMouse(FALSE,Window); CloseDevice(PrinterDump); } else DisplayBeep(Screen); DeleteExtIO(PrinterDump,sizeof(struct IODRPReq)); } else DisplayBeep(Screen); DeletePort(PrinterPort); } else DisplayBeep(Screen); SetPoint(Window); } /* main(argc,argv) : * * This is where all the trouble starts. */ void main(argc,argv) LONG argc; char *argv[]; { UWORD Colours[32]; /* Colour buffer (yes I'm British!) */ register short i,j; /* Loop counters. */ ULONG ViewModes; LONG ColourNumber; /* Number of colours. */ SHORT MaxOffsetX,MaxOffsetY; /* Some scrolling stuff. */ SHORT StartOffsetX,StartOffsetY; SHORT Width,Height; /* Size of the ViewPort */ SHORT JumpY = 1,JumpX = 1; /* Scrolljump steps */ BOOL WeAreScrolling = FALSE; /* Are we? Really? */ BOOL RemakeTheView; /* Push it? */ USHORT MenuNum; /* Oh, we have a menu. */ struct MenuItem *Item; BOOL ForceScroll = FALSE; /* Are we to scroll? */ BOOL ForceLace = FALSE; /* Do we like jittering? */ BOOL IsMouse; /* Scrollmode. */ char *PicName; /* If we are to scroll around. */ char *PrgName; /* Our name. */ short StartAt; /* Item to load first. */ short NumItems; /* Number of items to load. */ BOOL NextPic; /* Proceed with the next picture. */ BOOL MultipleItems = FALSE; /* One or more pictures? */ struct WBArg *WArg; /* Workbench control. */ char PathBuffer[256]; /* Temporary directory string. */ BOOL CycleOnStartup = FALSE; /* Are we to cycle at once? */ BOOL WasCycling; /* What happened? */ BOOL ChangedColours = FALSE; /* Did we change the colours? */ UBYTE *RightMouse = (UBYTE *)0xDFF016; /* The right mousebutton via CIA. */ char *PicList[128]; /* Temporary filename buffer. */ LONG PicsInList = 0; /* Files in the buffer. */ BOOL IsTiny = FALSE; /* Image smaller than screen? */ long TinyX,TinyY; /* Image offsets for tiny bitmap. */ struct Process *ThatsMe = (struct Process *)FindTask(NULL); /* Process identifier. */ long OldPri; /* Old task priority. */ UWORD BlackIsBlack[32]; /* Empty colour information. */ for(i = 0 ; i < 32 ; i++) BlackIsBlack[i] = 0; /* Started via Workbench? */ if(!ThatsMe -> pr_CLI) { extern struct WBStartup *WBenchMsg; WArg = WBenchMsg -> sm_ArgList; PrgName = WArg -> wa_Name; WArg++; if((NumItems = WBenchMsg -> sm_NumArgs - 1) < 1) { printf("\n %s problem:\n\n",PrgName); printf(" Hold down the shift-key, click the picture icons you wish to\n"); printf(" load, then double click the LoadImage icon.\n\n"); printf(" Sorry, no second icon was selected, please press RETURN33[0m."); getchar(); exit(0); } goto Skip; } else { StartAt = 1; NumItems = argc - 1; /* Some capital letters. */ for(i = 0 ; i < strlen(argv[0]) ; i++) argv[0][i] = toupper(argv[0][i]); PrgName = argv[0]; } if(argv[1][0] == '-') { switch(toupper(argv[1][1])) { case 'F': { /* If "-F" is specified, all HIRES and LACE flags * will be ignored. */ ForceScroll = TRUE; break; } case 'I': { /* Forced interlace mode. */ ForceLace = TRUE; break; } case 'C': { /* Colour cycling on startup. */ CycleOnStartup = TRUE; break; } default: { printf("%s: Option \"%s\" not recognized.\n",PrgName,argv[1]); exit(20); } } if(strlen(argv[1]) > 2 && argv[1][0] == '-') if(toupper(argv[1][2]) == 'C') CycleOnStartup = TRUE; StartAt = 2; NumItems--; } /* "Help! I need somebody; Help! not just anybody..." */ if((argv[1][0] == '?' || argc < 2 || (ForceScroll && argc < 3)) && argc) { printf("\n LoadImage © 1989 ED Electronic Design Hannover\n\n"); printf(" Usage is: %s [-F or -I, -FC or -IC] Picture1 Picture2 ... PictureN\n",argv[0]); printf(" UNIX style wildcards are supported!\n\n"); printf(" -F » Force scrollmode (ignore Hi-Res & Interlace flags).\n"); printf(" -I » Force interlaced display mode.\n"); printf(" -C » Force colour cycling on startup.\n\n"); printf(" If you wish to come in contact with me, here's my address:\n\n"); printf("\tOlaf 'Olsen' Barthel\n"); printf("\tBrabeckstrasse 35\n"); printf("\tD-3000 Hannover 71\n\n"); printf("\tFederal Republic of Germany\n\n"); printf(" Have fun with the code ... Olsen\n\n"); exit(0); } Skip: if(NumItems > 1) MultipleItems = TRUE; else { if(argc) { while(PicsInList < 128 && (PicName = scdir(argv[StartAt]))) { if(!(PicList[PicsInList] = malloc(strlen(PicName) + 1))) break; strcpy(PicList[PicsInList++],PicName); if(PicsInList == 2) printf("\nScanning directory for %s...\n",StripPath(argv[StartAt])); } if(!PicsInList) { printf("%s: No files matched with \"%s\".\n",PrgName,argv[StartAt]); exit(20); } StartAt = 0; if((NumItems = PicsInList) > 1) { MultipleItems = TRUE; printf("Sorting entries...\n\n"); QuickSort(PicList,PicsInList); } } } if(!argc) goto Lib; /* The libraries we need. */ Lib: if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",33))) { CloseAll(); CloseLibs(); } if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",33))) { CloseAll(); CloseLibs(); } /* Setup the standard colours for later use. */ GetPrefs(&StandardPrefs,sizeof(struct Preferences)); for(j = StartAt ; j < (StartAt + NumItems) ; j++) { if(!argc) { PicName = GetPath(WArg -> wa_Lock); strcpy(PathBuffer,PicName); strcat(PathBuffer,WArg -> wa_Name); PicName = PathBuffer; WArg++; } else { if(PicsInList) PicName = PicList[j]; else PicName = argv[j]; } /* Finished so soon? */ if(BreakCheck()) { printf("*** Aborting "); getchar(); CloseAll(); CloseLibs(); } /* Got a whole list to display. */ if(MultipleItems || !argc) printf("Picture Nº %03d, \"%s\" loading...\n",j - StartAt + 1,PicName); /* Everything okay with the ViewModes? */ if((ViewModes = LoadHeader(PicName,&InfoHeader)) == -1) { printf("%s: Couldn't load \"%s\"!\n",PrgName,PicName); CloseAll(); continue; } /* Are there any colours out there? */ if(!(ColourNumber = LoadCMAP(PicName,Colours,32,&InfoHeader))) { printf("%s: Couldn't load \"%s\"!\n",PrgName,PicName); CloseAll(); continue; } /* Take care of the size of the ViewPort. */ Width = InfoHeader . pageWidth; Height = InfoHeader . pageHeight; /* Forced scrolling? */ if(ForceScroll) ViewModes &= ~(HIRES | LACE); /* Forced interlaced display mode? */ if(ForceLace) ViewModes |= LACE; /* Take care of exotic screen sizes. */ if(InfoHeader . h > Height) Height = InfoHeader . h; if(InfoHeader . w > Width) Width = InfoHeader . w; /* Hires or lores? */ if(ViewModes & HIRES) { Width = 640; JumpX = 2; } else Width = 320; /* Interlaced or not? This may get funny since * it is possible to scroll the image in steps * of one pixel. In doing so one copper list * (either shortframe or longframe) is ignored * and only half of the image is displayed * correctly. To avoid this the scroll jump * is set to two pixels. */ if(ViewModes & LACE) JumpY = 2; if(Height > GfxBase -> NormalDisplayRows * JumpY) Height = GfxBase -> NormalDisplayRows * JumpY; /* Adjust the information. */ NewScreen . Width = Width; NewScreen . Height = Height; NewScreen . Depth = InfoHeader . nPlanes; NewScreen . ViewModes = ViewModes; /* And don't forget the window. */ NewWindow . Width = Width; NewWindow . Height = Height; /* Anything wrong with the picture? */ if(InfoHeader . w < InfoHeader . pageWidth || InfoHeader . h < InfoHeader . pageHeight) { if(!InitTinyBitMap()) { printf("%s: Not enough memory for secondary bitmap!\n",PrgName); CloseAll(); continue; } IsTiny = TRUE; } else { /* Initialize the bitmap for future use. */ InitBitMap(&ScreenMap,InfoHeader . nPlanes,InfoHeader . w,InfoHeader . h); /* Try to steal some memory for the bitplanes. */ for(i = 0 ; i < InfoHeader . nPlanes ; i++) { NextPic = FALSE; if(!(ScreenMap . Planes[i] = (PLANEPTR)AllocRaster(InfoHeader . w,InfoHeader . h))) { printf("%s: Not enough memory for screen raster!\n",PrgName); CloseAll(); NextPic = TRUE; break; } } if(NextPic) continue; IsTiny = FALSE; } /* Open the screen. */ if(!(Screen = (struct Screen *)OpenScreen(&NewScreen))) { printf("%s: Couldn't open screen!\n",PrgName); CloseAll(); continue; } /* Hide the title bar and prepare the window. */ ShowTitle(Screen,FALSE); NewWindow . Screen = Screen; /* Try to open the window. */ if(!(Window = (struct Window *)OpenWindow(&NewWindow))) { printf("%s: Couldn't open window!\n",PrgName); CloseAll(); continue; } SetSnooze(Window); SetWindowTitles(Window,-1,StripPath(PicName)); for(i = 0 ; i < 32 ; i++) { PrefColours[i] = Colours[i]; if(i >= 1 << NewScreen . Depth) PrefColours[i] = Colours[i] = GetRGB4(Screen -> ViewPort . ColorMap,i); } PrefColours[0] = StandardPrefs . color0; PrefColours[1] = StandardPrefs . color1; PrefColours[2] = StandardPrefs . color2; PrefColours[3] = StandardPrefs . color3; /* Load the colours. */ LoadRGB4(&Screen -> ViewPort,Colours,ColourNumber); /* Just in case we will have to restore it. */ StartOffsetX = Screen -> ViewPort . RasInfo -> RxOffset; StartOffsetY = Screen -> ViewPort . RasInfo -> RyOffset; /* Play it safe --- keep the maximum scroll offset * in reasonable dimensions. */ if((MaxOffsetX = InfoHeader . w - Width) < 0) MaxOffsetX = 0; if((MaxOffsetY = InfoHeader . h - Height) < 0) MaxOffsetY = 0; /* Absolute maximum limit. */ MaxOffsetX += StartOffsetX; MaxOffsetY += StartOffsetY; /* "Here comes the sun..." */ if(IsTiny) { if(!LoadRaster(PicName,TinyBitMap . Planes,&InfoHeader)) { printf("%s: Error while reading image data!\n",PrgName); CloseAll(); continue; } } else { if(!LoadRaster(PicName,ScreenMap . Planes,&InfoHeader)) { printf("%s: Error while reading image data!\n",PrgName); CloseAll(); continue; } } /* Do we allow colour cycling? */ if(LoadCycleRange(PicName,CycleRange,6)) InitCycleCode(&Screen -> ViewPort,Colours,ColourNumber,CycleRange,6); /* Install the menu and the pointer. */ SetMenuStrip(Window,&Menu); SetPoint(Window); /* This one disables the "Next Image" item. */ if(!MultipleItems) OffMenu(Window,0xF8E0); /* And - if necessary - print the tiny image. */ if(IsTiny) { if(InfoHeader . x + InfoHeader . w <= Screen -> Width) TinyX = InfoHeader . x; else TinyX = (NewScreen . Width - InfoHeader . w) >> 1; if(InfoHeader . y + InfoHeader . h <= Screen -> Height) TinyY = InfoHeader . y; else TinyY = (NewScreen . Height - InfoHeader . h) >> 1; /* Woop! what a long line! */ BltBitMap(&TinyBitMap,0,0,Screen -> RastPort . BitMap,TinyX,TinyY,InfoHeader . w,InfoHeader . h,0xC0,0xFF); IsTiny = FALSE; } /* Paint the screen black (for the effect). */ if(!(ViewModes & HAM)) LoadRGB4(&Screen -> ViewPort,BlackIsBlack,ColourNumber); /* Bring the screen to the front. */ ScreenToFront(Screen); ActivateWindow(Window); FreeSprite(0); /* Fade the colours in. */ if(!(ViewModes & HAM)) FadeIn(&Screen -> ViewPort,Colours,ColourNumber); /* Allow the menu to be selected. */ Window -> Flags &= ~RMBTRAP; /* If we are to cycle... */ if(CycleOnStartup) ToggleCycleCode(); /* To make the scrolling a bit smoother, we * raise our own priority. */ OldPri = SetTaskPri(ThatsMe,5); /* "Ewig währt am längsten." : Kurt Schwitters (1887 - 1948). * * Kurt Schwitters was a famous * Hanoverian Dada artist. */ FOREVER { /* Let's be nice and wait for reactions. */ WaitPort(Window -> UserPort); /* Massage the userport. */ if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort)) { Class = Massage -> Class; Code = Massage -> Code; /* If the user presses the menu button * the menu appears. There is nothing * wrong with this, but it might be * possible that the user does not * see the menu because of the raster * offset. So we reposition the ViewPort * before Intuition renders the menu bar. * To keep the menu visible we adjust * the colours if the right mouse * button is pressed. This is done * because of the fact that the * keyboard shortcuts generate the * same MENUVERIFY event. If there is * anyone who doesn't like it this * way he can try to test the keyboard * matrix by tickling the hardware. * I have chosen the easier way. */ if(Class == MENUVERIFY && !(*RightMouse & 4)) { if(WasCycling = IsCycling()) ToggleCycleCode(); LoadRGB4(&Screen -> ViewPort,PrefColours,32); ChangedColours = TRUE; /* Reset the offsets. */ Screen -> ViewPort . RasInfo -> RxOffset = StartOffsetX; Screen -> ViewPort . RasInfo -> RyOffset = StartOffsetY; /* Remake the copper list. */ MakeScreen(Screen); RethinkDisplay(); } ReplyMsg(Massage); } /* If the user presses the select button * we'll assume that he wants to scroll * around in the picture. */ if(Class == MOUSEBUTTONS && Code == SELECTDOWN) { FreeSprite(0); if(ChangedColours) { LoadRGB4(&Screen -> ViewPort,Colours,ColourNumber); ChangedColours = FALSE; if(WasCycling) ToggleCycleCode(); WasCycling = FALSE; } WeAreScrolling = TRUE; IsMouse = TRUE; } /* User pressed the cursor keys and wants * to scroll around in the picture. */ if(Class == RAWKEY && (Code & ~IECODE_UP_PREFIX) >= CURSORUP && (Code & ~IECODE_UP_PREFIX) <= CURSORLEFT) { FreeSprite(0); WeAreScrolling = TRUE; IsMouse = FALSE; } /* User didn't like the menu shortcut * and pressed the Tab key (is he a * DPaint fanatic?). */ if(Class == RAWKEY && Code == 0x42) { FreeSprite(0); ToggleCycleCode(); } NextPic = FALSE; /* The user picked a menu item. */ if(Class == MENUPICK) { if(ChangedColours) { LoadRGB4(&Screen -> ViewPort,Colours,ColourNumber); ChangedColours = FALSE; if(WasCycling) ToggleCycleCode(); WasCycling = FALSE; } MenuNum = Code; /* Until the last event is traced. */ while(MenuNum != MENUNULL) { if(MENUNUM(MenuNum) == 0) { switch(ITEMNUM(MenuNum)) { /* About... */ case 0: if(WasCycling = IsCycling()) ToggleCycleCode(); LoadRGB4(&Screen -> ViewPort,PrefColours,32); SetSnooze(Window); /* Reset the offsets. */ Screen -> ViewPort . RasInfo -> RxOffset = StartOffsetX; Screen -> ViewPort . RasInfo -> RyOffset = StartOffsetY; /* Remake the copper list. */ MakeScreen(Screen); RethinkDisplay(); Window -> Flags |= RMBTRAP; AutoRequest(Window,&AboutTxt[0],NULL,&Proceed,NULL,NULL,274,176); Window -> Flags &= ~RMBTRAP; SetPoint(Window); LoadRGB4(&Screen -> ViewPort,Colours,ColourNumber); if(WasCycling) ToggleCycleCode(); FreeSprite(0); break; /* Toggle the presence of the screen title. */ case 2: FreeSprite(0); if(Screen -> Flags & SHOWTITLE) ShowTitle(Screen,FALSE); else ShowTitle(Screen,TRUE); break; /* Toggle cycling. */ case 3: FreeSprite(0); ToggleCycleCode(); break; /* Print screen (standard size). */ case 4: PrintScreen(FALSE); break; /* Print screen (enlarged). */ case 5: PrintScreen(TRUE); break; /* Next picture. */ case 7: if(IsCycling()) ToggleCycleCode(); WasCycling = FALSE; if(!(ViewModes & HAM)) FadeOut(&Screen -> ViewPort,Colours,ColourNumber); ScreenToBack(Screen); /* Restore original priority. */ SetTaskPri(ThatsMe,OldPri); NextPic = TRUE; CloseAll(); break; /* Quit LoadImage. */ case 8: if(IsCycling()) ToggleCycleCode(); WasCycling = FALSE; if(!(ViewModes & HAM)) FadeOut(&Screen -> ViewPort,Colours,ColourNumber); ScreenToBack(Screen); /* Restore original priority. */ SetTaskPri(ThatsMe,OldPri); NextPic = TRUE; CloseAll(); goto Bye; } } /* See if there is another * menu item around. */ Item = (struct MenuItem *)ItemAddress(&Menu,MenuNum); MenuNum = Item -> NextSelect; } } if(NextPic) break; /* No chance to scroll anywhere, so we'll block * it. */ if(MaxOffsetX == StartOffsetX && MaxOffsetY == StartOffsetY) WeAreScrolling = FALSE; /* This loop will run until the select button * or the cursor key is released. */ while(WeAreScrolling) { if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort)) { Class = Massage -> Class; Code = Massage -> Code; ReplyMsg(Massage); } /* User doesn't want to scroll anymore? */ if(Code & IECODE_UP_PREFIX) { FreeSprite(0); WeAreScrolling = FALSE; } /* Nothing happened. */ RemakeTheView = FALSE; /* Left border, scroll left. */ if(((Window -> MouseX == 0 && IsMouse) || (Code == CURSORLEFT && !IsMouse)) && (Screen -> ViewPort . RasInfo -> RxOffset - JumpX >= StartOffsetX)) { Screen -> ViewPort . RasInfo -> RxOffset -= JumpX; RemakeTheView = TRUE; } /* Top border, scroll up. */ if(((Window -> MouseY == 0 && IsMouse) || (Code == CURSORUP && !IsMouse)) && (Screen -> ViewPort . RasInfo -> RyOffset - JumpY >= StartOffsetY)) { Screen -> ViewPort . RasInfo -> RyOffset -= JumpY; RemakeTheView = TRUE; } /* Right border, scroll right. */ if(((Window -> MouseX == Window -> Width - 1 && IsMouse) || (Code == CURSORRIGHT && !IsMouse)) && (Screen -> ViewPort . RasInfo -> RxOffset + JumpX <= MaxOffsetX)) { Screen -> ViewPort . RasInfo -> RxOffset += JumpX; RemakeTheView = TRUE; } /* Bottom border, scroll down. */ if(((Window -> MouseY == Window -> Height - 1 && IsMouse) || (Code == CURSORDOWN && !IsMouse)) && (Screen -> ViewPort . RasInfo -> RyOffset + JumpY <= MaxOffsetY)) { Screen -> ViewPort . RasInfo -> RyOffset += JumpY; RemakeTheView = TRUE; } /* Are we to scroll the ViewPort? */ if(RemakeTheView) { MakeScreen(Screen); RethinkDisplay(); } } } /* Last image, disable menu item. */ if(j == StartAt + NumItems - 1) OffMenu(Window,0xF8E0); } /* The goodbye message. */ Bye: if(!argc) { printf("\nPlease press RETURN."); getchar(); } /* Give it a newline. */ if(MultipleItems) putchar('\n'); CloseLibs(); } /* End of Code, end of file. */