/*
 *  WINFO2ICON      A companion utility to wIconSetter and wIconify.
 *                  wInfo2Icon converts a Workbench .info file into
 *                  an icon file usable by wIconSetter.
 *
 *  Copyright 1990 by Davide P. Cervone, all rights reserved.
 *  You may use this code, provided this copyright notice is kept intact.
 */

#include "wInfo2Icon.h"

static char *program   = PROGRAM;
static char *copyright = COPYRIGHT;

struct IconBase *IconBase;

static struct DiskObject *theInfo;      /* Data read from the .info file */
static long IconFile;                   /* the output icon file */
static struct BitMap theBitMap;         /* a bitmap for the image */
static struct BitMap theMaskMap;        /* a bitmap for the mask image */
static int theWidth,theHeight,theDepth; /* size of the image */
static struct TmpRas theTmpRas;         /* needed for FloodFill */

static char *InfoName;                  /* name of the info file */
static char *IconName;                  /* name of the icon file */

static char *Hex = "0123456789abcdef)!@#$%^&*(ABCDEF";
#define MAXHEX      32
#define MAXLINE     132

#define BLT_INVERT  0x50
#define FLOOD_SAME  1


/*
 *  FreePlanes()
 *
 *  Free each bitplane of the bitmap and free the temporary raster.
 */

static void FreePlanes()
{
   short i;

   for (i=0; i<theDepth; i++)
   {
      if (theBitMap.Planes[i])
         FreeRaster(theBitMap.Planes[i],theWidth,theHeight);
      theBitMap.Planes[i] = NULL;
   }
   if (theTmpRas.RasPtr) FreeRaster(theTmpRas.RasPtr,theWidth,theHeight);
   theTmpRas.RasPtr = NULL;
}


/*
 *  DoExit()
 *
 *  General purpose error-exit routine.  Print an error message if one was
 *  supplied (it can have up to three parameters), and then clean up any
 *  memory, libraries, etc. that need to be handled before exiting.
 */

void DoExit(s,x1,x2,x3)
char *s, *x1, *x2, *x3;
{
   long status = EXIT_OK;
   
   if (s != NULL)
   {
      printf(s,x1,x2,x3);
      printf("\n");
      status = EXIT_ERROR;
   }
   FreePlanes();
   if (IconFile)        fclose(IconFile);
   if (theInfo)         FreeDiskObject(theInfo);
   if (IntuitionBase)   CloseLibrary(IntuitionBase);
   if (GfxBase)         CloseLibrary(GfxBase);
   if (IconBase)        CloseLibrary(IconBase);
   exit(status);
}


/*
 *  CheckLibOpen()
 *
 *  Call OpenLibrary() for the specified library, and check that the 
 *  open succeeded.
 */

static void CheckLibOpen(lib,name,rev)
APTR *lib;
char *name;
int rev;
{
   extern APTR OpenLibrary();

   if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL)
      DoExit("Can't open %s",name);
}


/*
 *  GetPlanes()
 *
 *  Set the size of the image, but add an extra bitplane (for the mask)
 *  Initialize the BitMaps
 *  Get the bitplanes for the BitMap
 *  Make the mask BitMap share the last bitplane
 *  Get a temporary raster (for FloodFill)
 */

static void GetPlanes(w,h,d)
int w,h,d;
{
   short i;

   theWidth = w; theHeight = h; theDepth = d+1;
   InitBitMap(&theBitMap,theDepth,w,h);
   InitBitMap(&theMaskMap,1,w,h);
   for (i=0; i<theDepth; i++)
   {
      theBitMap.Planes[i] = AllocRaster(w,h);
      if (theBitMap.Planes[i] == NULL) DoExit("Can't Get BitPlane %d",i);
   }
   theMaskMap.Planes[0] = theBitMap.Planes[d];
   theTmpRas.RasPtr = (UBYTE *)AllocRaster(w,h);
   if (theTmpRas.RasPtr == NULL) DoExit("Can't Get TmpRas for FloodFill");
   theTmpRas.Size   = RASSIZE(theWidth,theHeight);
}


/*
 *  DoBackFill()
 *
 *  Invert the image's bitmap (so the Mask bitplane becomes all 1's) and flood
 *  the RastPort with 0's starting at the upper left-hand corner.  Since there
 *  is a one pixel border around the actual image data, this does a flood fill
 *  from the outside of the image into the interior.  The Mask bitplane then
 *  has 0's exactly outside the image itself.
 */

static void DoBackFill(theRastPort)
struct RastPort *theRastPort;
{
   BltBitMap(theRastPort->BitMap,0,0,theRastPort->BitMap,0,0,
      theWidth,theHeight,BLT_INVERT,0xFF,NULL);
   SetAPen(theRastPort,0L);
   SetDrMd(theRastPort,JAM1);
   Flood(theRastPort,FLOOD_SAME,0,0);
}


/*
 *  WriteBitMap()
 *
 *  Clip the width to the maximum we can write
 *  For each row in the image
 *    And each column in each row
 *      Get the pen color of the pixel
 *      Store the HEX digit in the line buffer
 *    End the line and print it to the file
 */

static void WriteBitMap(theRastPort,Width,Height,x,y)
struct RastPort *theRastPort;
int Width,Height,x,y;
{
   short h,w;
   int pen;
   char Line[MAXLINE];

   if (Width > MAXLINE) Width = MAXLINE;

   for (h=0; h<Height; h++)
   {
      for (w=0; w<Width; w++)
      {
         pen = ReadPixel(theRastPort,w+x,h+y) % MAXHEX;
         Line[w] = Hex[pen];
      }
      Line[w] = 0;
      fprintf(IconFile,"\n   ");
      fprintf(IconFile,Line);
   }
   fprintf(IconFile,"\n");
}


/*
 *  WriteImage()
 *
 *  If there is an image to write,
 *    Free any bitplanes that we already got
 *    Get the number of planes we need to write and count the depth
 *    Get the bitplanes for a slightly larger image (leave a border around it)
 *    Set up the RastPort to use the BitMap and TmpRas
 *    Clear the bitmap and temporarily remove the extra bitplane
 *    Draw the image into the prepared bitmap, leaving a 1-pixel border
 *    Write the image to the file
 *    If we are also writing the mask,
 *      Put back the extra bitplane
 *      Do the backfill stuff to get the mask
 *      Write the MASK command
 *      Use the mask bitmap and write the mask to the file
 */

static void WriteImage(theImage,MaskIt)
struct Image *theImage;
int MaskIt;
{
   int depth;
   int mask;
   struct RastPort theRastPort;

   if (theImage)
   {
      FreePlanes();
      mask = theImage->PlanePick | theImage->PlaneOnOff;
      for (depth=0; mask; depth++) mask >>= 1;
      if (depth > 5) depth = 5;
      GetPlanes(theImage->Width+2,theImage->Height+2,depth);
      InitRastPort(&theRastPort);
      theRastPort.BitMap = &theBitMap;
      theRastPort.TmpRas = &theTmpRas;
      SetRast(&theRastPort,0L); theBitMap.Depth--;
      DrawImage(&theRastPort,theImage,1,1);
      WriteBitMap(&theRastPort,theImage->Width,theImage->Height,1,1);
      if (MaskIt)
      {
         theBitMap.Depth++;
         DoBackFill(&theRastPort);
         fprintf(IconFile,"\nMASK:\n");
         theRastPort.BitMap = &theMaskMap; 
         WriteBitMap(&theRastPort,theImage->Width,theImage->Height,1,1);
      }
   }
}


/*
 *  WriteIcon()
 *
 *  Get the WB icon's Gadget structure
 *  Write the identification header to the file
 *  If the gadget has an image,
 *    Write the IMAGE command to the file
 *    Write the image itself, and write the mask if it is a BACKFILL icon
 *  Now check the highlight bits:
 *    GADGHIMAGE:
 *      If there is a select image,
 *        Write the SELECT command, and write the image data and mask
 *    GADGHNONE:
 *      If the gadget had an image,
 *        Write it as the select image (this way it looks the same selected
 *        as unselected) and write the mask as well, for good measure
 *        (the WB doesn't do this, but we will, since it's easy)
 *    Don't write anything else for the other highlight types
 *    (we've already taken care of GADGHBACKFILL, and GADGHCOMP is the
 *     default function of wIconify).
 */

static void WriteIcon()
{
   struct Gadget *theGadget = &(theInfo->do_Gadget);
   
   fprintf(IconFile,"/*\n *  %s\n *\n",IconName);
   fprintf(IconFile," *  An Icon created by %s\n",program);
   fprintf(IconFile," *  From the Workbench icon %s.info\n",InfoName);
   fprintf(IconFile," */\n");

   if ((theGadget->Flags & GADGIMAGE) && theGadget->GadgetRender)
   {
      fprintf(IconFile,"\nIMAGE:\n");
      WriteImage(theGadget->GadgetRender,
         (theGadget->Flags & GADGHIGHBITS) == GADGBACKFILL);
   }
   switch(theGadget->Flags & GADGHIGHBITS)
   {
      case GADGHIMAGE:
         if (theGadget->SelectRender)
         {
            fprintf(IconFile,"\nSELECT:\n");
            WriteImage(theGadget->SelectRender,TRUE);
         }
         break;

      case GADGHNONE:
         if ((theGadget->Flags & GADGIMAGE) && theGadget->GadgetRender)
         {
            fprintf(IconFile,"\nSELECT:\n");
            WriteImage(theGadget->GadgetRender,TRUE);
         }
         break;

      case GADGBACKFILL:
      case GADGHCOMP:
         break;
   }
}


/*
 *  main()
 *
 *  If run from the WB, sinply exit
 *  Show the usage if there are not the right number of arguments
 *  Otherwise, get the file names
 *  Open the needed libraries
 *  Get the info from the .info file, if possible
 *  Open the icon file for writing, if possible
 *  Write the icon to the file
 */

void main(argc,argv)
int argc;
char **argv;
{
   if (argc == 0) DoExit(NULL);
   if (argc != 3) DoExit("Usage:  %s",USAGE);
   InfoName = argv[1];
   IconName = argv[2];

   CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
   CheckLibOpen(&GfxBase,"graphics.library",GRAPHICS_REV);
   CheckLibOpen(&IconBase,"icon.library",ICON_REV);

   theInfo = GetDiskObject(InfoName);
   if (theInfo == NULL) DoExit("Can't read Info file '%s.info'",InfoName);
   IconFile = fopen(IconName,"w");
   if (IconFile == NULL) DoExit("Can't write Icon file '%s'",IconName);
   WriteIcon();

   DoExit(NULL);
}
