/* * OPTICON.C */ #define VERSION "1.5" static char versiontag[] = "$VER: $Id: opticon.c,v 1.5 1994/02/03 04:03:02 tf Exp $"; /****** OptIcon ****************************************** * * NAME * OptIcon -- Optimize icon images for size and speed (V36) * * SYNOPSIS * OptIcon NAME/A/M,DEPTH=PLANES/N,NOEXPAND/S,CRITICAL/S,VERBOSE/S * * FUNCTION * OptIcon reads in given ".info" files and scans the icon image * in order to optimize the PlanePick and PlaneOnOff fields in the * icon Image structure. This is a space-saving mechanism for image * data. * Rather than defining the image data for every plane of the RastPort, * you need define data only for the planes that are not entirely zero * or one. As you define your Imagery, you will often find that most * of the planes ARE just as color selectors. For instance, if you're * designing a two-color icon to use colors one and three, and the icon * will reside in a five-plane display, bit plane zero of your * imagery would be all ones, bit plane one would have data that * describes the imagery, and bit planes two through four would be * all zeroes. Using these flags avoids wasting all that memory in * this way: first, you specify which planes you want your data to * appear in using the PlanePick variable. For each bit set in the * variable, the next "plane" of your image data is blitted to the * display. For each bit clear in this variable, the corresponding bit * in PlaneOnOff is examined. If that bit is clear, a "plane" of zeroes * will be used. If the bit is set, ones will go out instead. * Note that if you want an Image that is only a filled rectangle, you * can get this by setting PlanePick to zero (pick no planes of data) * and set PlaneOnOff to describe the pen color of the rectangle. * * INPUTS * NAME - name of the icon image file. A trailing ".info" * is optional but not required. * DEPTH - maximum number of bitplanes to be saved. * VERBOSE - display input and output information for each icon. * * EXAMPLE * ;Remove all but the first 3 planes of the icon image for the * ;disk in drive DF0: but don't add any planes * opticon df0:disk planes=3 noexpand * * NOTES * Since the IconEdit from Commodore will always save 8 bitplane icons * the above example might be of great use to you. (Note that 3 plane * images are not only smaller but also faster!) * Coming with OptIcon is the script PatchIcons which will recursively * descend all subdirectories of a given path deleting all but the first * 3 planes of all icon images in that path. * * OptIcon now also allows you to expand your 8 or more color icons * for the use on a 16 or more color Workbench. This is important due * to the new color system under OS3.x which always shifts the second * four colors to the end of the system palette. Therefore you might * want to adapt an icon's color depth to the actual screenmode it is * used on. * * EXAMPLE * ;Remap the last 4 of at least 8 colors of the RAM DISK icon * ;to the last 4 colors in a 16 or more colors Workbench palette * opticon ram:disk planes=4 * * BUGS * Commodore's PutDiskObject() currently [icon.library 40.1 (15.2.93)] * re-expands icon images using the PlanePick/PlaneOnOff mechanism and * in fact PutDiskObject() has quite a lot of problems doing so! * For this reason OptIcon will perform the PlanePick/PlaneOnOff * optimization only if the keyword CRITICAL is given in the command * line. * * DISCLAIMER * This file is part of the Icon2C and OptIcon distribution. * * Icon2C and OptIcon are free software; you can redistribute them * and/or modify them under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 1 of * the License, or (at your option) any later version. * * Icon2C and Opticon are distributed in the hope that they will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with these programs; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * SEE ALSO * PatchIcons, Icon2C * ****************************************************************************** * * Compile w/ -DDEBUG to output more information at runtime */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GNUC__ /* suggest parentheses around assignment used as truth value */ #define if(assignment) if( (assignment) ) #endif /* __GNUC__ */ extern struct Library *OpenLibrary(STRPTR, ULONG); extern void CloseLibrary(struct Library *); extern void CopyMem(APTR, APTR, ULONG); extern void *AllocMem(ULONG, ULONG); extern void FreeMem(void *, ULONG); extern ULONG TypeOfMem(void *); extern struct RDArgs *ReadArgs(STRPTR, LONG *, struct RDArgs *); extern LONG IoErr(void); extern BOOL PrintFault(LONG, STRPTR); extern void FreeArgs(struct RDArgs *); extern struct DiskObject *GetDiskObject(char *); extern BOOL PutDiskObject(char *, struct DiskObject *); extern void FreeDiskObject(struct DiskObject *); struct IconBase *IconBase; /*#define CopyMem(a,b,c) printf("CopyMem( 0x%lx, 0x%lx, %ld )\n", a,b,c);*/ void display_version_information(void) { static char license[]= "OptIcon is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published\n" "by the Free Software Foundation; either version 1 of the License,\n" "or (at your option) any later version.\n" "\n" "OptIcon is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with OptIcon; see the file COPYING. If not, write to the\n" "Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n" ; puts("OptIcon Version " VERSION " (compiled " __DATE__ ", " __TIME__ ")\n" "(c)Copyright 1994 by Tobias Ferber, ukjg@dkauni2.bitnet\n"); puts(license); } struct Image *free_image(struct Image *i) { if(i) { long size= i->Depth * i->Height * ((i->Width + 15) / 16) * sizeof(UWORD); if(i->ImageData && size > 0) FreeMem( i->ImageData, size ); FreeMem( i, sizeof(struct Image) ); } return (struct Image *)0L; } #define OPT_NOEXPAND (1<<0) #define OPT_CRITICAL (1<<1) #define OPT_VERBOSE (1<<2) struct Image *optimize_image(struct Image *i, WORD planes, int optimode) { UWORD p16= i->Height * ((i->Width + 15) / 16); /* #of words per plane */ UWORD *idata= i->ImageData; WORD d, dmax, D, P; UBYTE pp= 0; /* plane pick */ UBYTE p10= 0; /* plane on/off */ struct Image *o= i; /* optimized image */ /* prevent silly args from being harmful */ if(!i) return i; if(planes > 8) planes= 8; if(optimode & OPT_VERBOSE) { printf("(depth=%d, pick=%d, onoff=%d)", i->Depth, i->PlanePick, i->PlaneOnOff); fflush(stdout); } /* PRESCAN: Examine dmax planes of i and compute D = the real depth (without trailing 0 planes) pp = the new PlanePick value p10 = the new PlaneOnOff value */ dmax= (0 < planes && planes < i->Depth) ? planes : i->Depth; for(d= D= 0; dPlanePick & (1<PlaneOnOff & (1<PlanePick || p10 != i->PlaneOnOff) ) { printf(" -> (%d,%d,%d)", D,pp,p10); fflush(stdout); } /* compute the #of planes in the output image */ P= ( planes>D && !(optimode & OPT_NOEXPAND) ) ? planes : D; if(P != i->Depth || pp != i->PlanePick || p10 != i->PlaneOnOff) { UWORD p8= p16 * sizeof(UWORD); UWORD *odata; ULONG osize= P * p8; if( o= (struct Image *)AllocMem(sizeof(struct Image),MEMF_CLEAR) ) { if( odata= (UWORD *)AllocMem(osize,TypeOfMem(i->ImageData)|MEMF_CLEAR) ) { CopyMem( (APTR)i, (APTR)o, sizeof(struct Image) ); o->ImageData= odata; idata= i->ImageData; for(d=0; dPlanePick & (1<PlaneOnOff & (1<PlanePick & (1<=3 && D 2, (2) invert the result, (3) AND it with plane 2 and (4) OR the result with all planes > 2 Note: There is no need to expamd the image data if p10 &~ %111 != 0 */ /* move to plane 2 */ idata= i->ImageData; for(d=0; d<2; d++) if(i->PlanePick & (1<PlanePick & (1<<2)) { memcpy((char *)p, (char *)idata, p8); idata= &idata[p16]; } else memset( (char *)p, (p10 & (1L<<2)) ? 0xFF : 0x00, p8 ); /* or planes 3..D, invert them, AND the result with plane 2 */ for(d=3; dPlanePick & (1<PlaneOnOff is 0 --> no-op */ } /* move to plane 3 */ odata= o->ImageData; for(d=0; d<3; d++) if(pp & (1<=D || pp & (1<ImageData; if( p10 &~ (1<<(D-1)) == 0) { memset( (char *)p, 0x00, p8 ); for(d=0; dPlanePick & (1<PlanePick & (1<<(D-1)) ) memand( (char *)p, (char *)idata, p8 ); /* else the last plane is entirely 1 */ } else /* move to the last plane */ { for(d=0; dPlanePick & (1<PlanePick & (1<<(D-1)) ) memcpy( (char *)p, (char *)idata, p8 ); else memset( (char *)p, 0xFF, p8 ); } /* move to plane D */ odata= o->ImageData; for(d=0; d panic! */ { FreeMem(o->ImageData,osize); FreeMem(o,sizeof(struct Image)); o= (struct Image *)0L; } } o->Depth= P; o->PlanePick= pp; o->PlaneOnOff= p10; } else /* !odata */ { FreeMem(o,sizeof(struct Image)); o= (struct Image *)0L; } } } if(optimode & OPT_VERBOSE) { if(o && o!=i) printf(" -> (%d,%d,%d)", o->Depth, o->PlanePick, o->PlaneOnOff); putchar('\n'); } return o; } int main(int argc, char **argv) { struct RDArgs *a; LONG args[5] = { 0,0,0,0,0 }; WORD numplanes= 0; char *whoami= *argv; int rc= RETURN_OK; if( a= ReadArgs("NAME/A/M,DEPTH=PLANES/N,NOEXPAND/S,CRITICAL/S,VERBOSE/S", args, NULL) ) { char **flist= (char **)args[0]; if(args[1]) { if( (numplanes= (WORD)*(LONG *)(args[1])) < 1 ) { fprintf(stderr,"%s: Illegal maximum depth %d\n",whoami,numplanes); rc= RETURN_FAIL; } } if(flist && rc == RETURN_OK) { if( IconBase= (struct IconBase *)OpenLibrary(ICONNAME,36) ) { while(*flist && rc == RETURN_OK) { char *iname= (char *)malloc( strlen(*flist) + 1 ); if(iname) { struct DiskObject *icon; strcpy(iname, *flist); if( (icon= GetDiskObject(iname)) == NULL ) { int x= strlen(iname) - 5; if(x>0 && !stricmp(&(iname[x]),".info")) { iname[x]= '\0'; icon= GetDiskObject(iname); } } if(icon) { struct Gadget *g= &icon->do_Gadget; struct Image *ogr, *osr; int modified= 0; int flags= 0; ogr= osr= (struct Image *)0L; if(args[2]) flags |= OPT_NOEXPAND; if(args[3]) flags |= OPT_CRITICAL; if(args[4]) flags |= OPT_VERBOSE; if(g->GadgetRender && (g->Flags & GFLG_GADGIMAGE)) { struct Image *i= (struct Image *)g->GadgetRender; if(flags & OPT_VERBOSE) printf("Normal "); if( ogr= optimize_image(i,numplanes,flags) ) { if(ogr != i) { g->GadgetRender= (APTR)ogr; ++modified; } else ogr= (struct Image *)0L; /* don't free ogr */ if(g->SelectRender && (g->Flags & GFLG_GADGHIMAGE)) { i= (struct Image *)g->SelectRender; if(flags & OPT_VERBOSE) printf("Selected "); if( osr= optimize_image(i,numplanes, flags) ) { if(osr != i) { g->SelectRender= (APTR)osr; ++modified; } else osr= (struct Image *)0L; /* don't free osr */ } else { fprintf(stderr,"%s: %s.info: not enough free memory to optimize the selected image\n",whoami,iname); rc= RETURN_ERROR; } } } else { fprintf(stderr,"%s: %s.info: not enough free memory to optimize the normal image\n",whoami,iname); rc= RETURN_ERROR; } } if( modified && rc == RETURN_OK ) { if( !PutDiskObject(iname,icon) ) PrintFault(rc= IoErr(), iname); } if(ogr) ogr= free_image(ogr); if(osr) osr= free_image(osr); FreeDiskObject(icon); } else /* !icon */ { fprintf(stderr,"%s: GetDiskObject() failed for %s[.info]\n",whoami,iname); rc= IoErr(); } free(iname); } else /* !iname */ { fprintf(stderr,"%s: out of memory... aaaiiiiiieeeeeeeee!\n",whoami); rc= RETURN_ERROR; } ++flist; } CloseLibrary((struct Library *)IconBase); } else { fprintf(stderr,"%s: You need %s V36+",whoami,ICONNAME); rc= RETURN_ERROR; } } FreeArgs(a); } else /* !ReadArgs */ { if(argc == 1) display_version_information(); if(argc <= 4) printf("%s: required argument missing\n",whoami); else printf("%s: wrong number of arguments\n",whoami); rc= RETURN_FAIL; } exit(rc); }