// MPImage - Amiga Image Conversion
// Copyright (C) © 1996 Mark John Paddock
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// any later version.

// This program is distributed in the hope that it 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 this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// mark@topic.demon.co.uk
// mpaddock@cix.compulink.co.uk

/****** MPImage.library/--background-- ******************************
*
* Version 7.3 - fixed enforcer hit when no progress requester.
*             - Opens locale.library(38) to work on OS3.0.
*
* MPImage.library lets you load save and process images.
*
* Stuctures/Formats:
*
*  RGB Chunky    This is held as seperate Red Green and Blue
*                UBYTE *, with a Width and Height
*  Grey Chunky   This is held as one Grey UBYTE * (often called
*                red), with a Width and a Height.
*  Palette       This is held as one UBYTE[768] (sometimes called
*                red, a Width and Height, and a UBYTE *Palette. The
*                palette holds the red palette in [0] to [255],
*                the green palette in [256] to [511] and the blue
*                palette in [512] to [767]. There may also be a
*                camg (or modeid) and a number of colours, or
*                number of planes. These are required for EHB and
*                HAM images.
*  Bitmap        A normal planar bit map. This also has other
*                fields as for Palette.
*  EGSBitMap     A standard EGSBitMap.
*  MPImage       This holds one of RGB, Grey, Bitmap or EGSBitMap.
*  MPProcess     This holds the RGB pointers and width and height.
*                It is used for some processing functions.
*
* Image Loading:
*
*  LoadMPImage()   Loads an image from disc/clipboard.
*                  It loads various formats.
*                  Returns BitMap, RGB, GreyScale or EGSBitMap.
*
* Image Saving:
*
*  SaveMPImage()   Can save RGB/GreyScale to disc.
*                  It saves in various formats.
*
* ImageScaling:
*
*  RescaleMPImage() Scales a loaded image (any format).
*
* Displaying Images
*
*  SaveMPImage()    Can display RGB/GreyScale Buffers.
*
* Miscellaneous:
*
*  FreeMPImage()         Frees a loaded image.
*  SetMPImageScreen()    Sets the screen for the progress requester.
*  MPProgressHook()      Sets a progress hook for messages.
*  MPImageErrorMessage() Returns the last error message.
*
* Planar/Chunky Conversion:
*
*  MPPlanarToChunky() Converts a BitMap to a chunky buffer.
*  MPChunkyToPlanar() Converts a chunky buffer to a BitMap.
*  SaveMPImage()      Can convert RGB Buffers to a BitMap.
*
* Image processing:
*
*  Image processing is done on chunky buffers; RGB, GreyScale
*  or Palette based. The following convert between these formats.
*
*  MPGreyToPal()  GreyScale to Palette.
*  MPPalToGrey()  Palette to GreyScale.
*  MPPalToPal()   Palette to a different Palette.
*  MPPalToRGB()   Palette to RGB.
*  MPRGBToGrey()  RGB To Grey.
*  MPRGBToPal()   RGB To Palette.
*
*  Buffers can also be scaled:
*
*  MPScaleGrey()  Scales one buffer.
*  MPScaleRGB()   Scales 3 buffers.
*                 Same as 3 calls to MPScaleGrey() but faster.
*
* Summary:
*
* Input                            Output Format
* Format   !Disc! RGB !Gray !Palette!BitMap!EGSBitMap!MPImage!Screen
* ---------!----!-----!-----!-------!------!---------!-------!------
* Disc/Clip!    !1*   !1*   !       !1*    !1*       !1      !
* RGB      !2   !13,3*!10   !11     !2     !         !       !2
* Gray     !2   !x    !12,3*!6      !2     !         !       !2
* Palette  !    !9    !7    !8      !5     !         !       !
* BitMap   !    !     !     !4      !3*,o  !         !       !
* EGSBitMap!    !     !     !       !      !3*       !       !
* MPImage  !2+  !+    !+    !#      !+     !+        !3      !2+
*
*  1 - LoadMPImage()
*  2 - SaveMPImage()
*  3 - RescaleMPImage()
*  4 - MPPlanarToChunky()
*  5 - MPChunkyToPlanar()
*  6 - MPGreyToPal()
*  7 - MPPalToGrey()
*  8 - MPPalToPal()
*  9 - MPPalToRGB()
* 10 - MPRGBToGrey()
* 11 - MPRGBToPal()
* 12 - MPScaleGrey()
* 13 - MPScaleRGB()
*  * - As part of an MPImage
*  + - Depends on parameters to LoadMPImage
*  x - Easy - no function supplied
*  o - See BitMapScale()
*  # - Depends on LoadMPImage, use 11,6,8 or 4
*
*****************************************************************************
*
*/

#define CATCOMP_BLOCK
#define CATCOMP_NUMBERS
#include "messages.h"

#include "mpimage.h"

extern struct Catalog *Catalog=NULL;
extern struct Library *LocaleBase = NULL;

extern struct Library *DCTVBase = NULL;

#ifdef CYBER
extern struct Library *CyberGfxBase = NULL;
#endif

extern struct Library *EGSBase = NULL;

extern char ErrorMessage[256] = "";

extern long __oslibversion=39;

const char Version[]="$VER: MPImage.library"
#ifdef MY040
	"_040"
#else
#ifdef _M68060
	"_060"
#else
#ifdef _M68881
	"_881"
#else
#ifdef _M68020
	"_020"
#else
	"_000"
#endif
#endif
#endif
#endif
	" 7.5 (14.3.97)";

char
*GetMg(UWORD message) {
	LONG   *l;
	UWORD  *w;
	STRPTR  builtIn;

   l = (LONG *)CatCompBlock;

	while (*l != message)  {
		w = (UWORD *)((ULONG)l + 4);
		l = (LONG *)((ULONG)l + (ULONG)*w + 6);
	}
	builtIn = (STRPTR)((ULONG)l + 6);
	return(GetCatalogStr(Catalog,message,builtIn));
}

static far int OpenCount = 0;
static far struct SignalSemaphore sema = {0};

static struct Hook *ProgressHook = NULL;

extern void
SetMax(int max) {
	if (ProgressWnd) {
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Progress],ProgressWnd,NULL,
							GTSL_Max,max-1,
							GTSL_Level,0,
							TAG_END);
	}
	if (ProgressHook) {
		CallHookPkt(ProgressHook,MPIP_MAX,(APTR)max);
	}
}

extern void
SetCur(int cur) {
	if (ProgressWnd) {
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Progress],ProgressWnd,NULL,
							GTSL_Level,cur,
							TAG_END);
	}
	if (ProgressHook) {
		CallHookPkt(ProgressHook,MPIP_CURR,(APTR)cur);
	}
}

void AddMessageNo(UWORD no) {
	AddMessage(GetMg(no));
}

int top = 0;
// add a message to the progress list
void
AddMessage(UBYTE *message) {
	struct Node *node;
	if (ProgressWnd) {
		if ((node = (struct Node *)malloc(sizeof(struct Node))) &&
			 (node->ln_Name = malloc(strlen(message)+1))) {
			strcpy(node->ln_Name,message);
			GT_SetGadgetAttrs(ProgressGadgets[GDX_Info],ProgressWnd,NULL,
   	 						 GTLV_Labels, ~0,
   							 TAG_END);
			AddTail(&InfoList,node);
			GT_SetGadgetAttrs(ProgressGadgets[GDX_Info],ProgressWnd,NULL,
    							 GTLV_Labels, &InfoList,
	    						 GTLV_MakeVisible, top,
   							 TAG_END);
  			top++;
  		}
  		HandleProgressIDCMP();
	}
	if (ProgressHook) {
		CallHookPkt(ProgressHook,MPIP_MESSAGE,(APTR)message);
	}
}

int __stdargs
_STI_CreateSema(void) {
#ifdef CYBER
	char buffer[3];
#endif
	Forbid();
	if (!OpenCount) {
		memset(&sema,0,sizeof(sema));
		InitSemaphore(&sema);
		NewList(&InfoList);
	}
	++OpenCount;
	Permit();
	if (!(LocaleBase = OpenLibrary("locale.library",38))) {
		return 1;
	}
	Catalog = OpenCatalog(NULL,
								"mp/mpimage.catalog",
  								TAG_END);
	DCTVBase = OpenLibrary("dctv.library",3);
#ifdef CYBER
	if (GetVar("mpimage/cybergraphics",buffer,1,0) != -1) {							
		CyberGfxBase = OpenLibrary("cybergraphics.library",40);
	}
#endif
	return 0;
}

void __stdargs
_STD_DeleteSema(void) {
	--OpenCount;
	if (LocaleBase) {
		CloseCatalog(Catalog);
		Catalog = NULL;
		CloseLibrary(LocaleBase);
		LocaleBase = NULL;
	}
	if (DCTVBase) {
		CloseLibrary(DCTVBase);
		DCTVBase = NULL;
	}
#ifdef CYBER
	if (CyberGfxBase) {
		CloseLibrary(CyberGfxBase);
		CyberGfxBase = NULL;
	}
#endif
	return;
}

extern void LockSema(void) {
	ObtainSemaphore(&sema);
}
extern void UnlockSema(void) {
	ReleaseSemaphore(&sema);
}

extern void
PlanarToChunky(struct p2cStruct *p2c) {
	AddMessageNo(MSG_P2C);
	ObtainSemaphore(&sema);
	PlanarToChunkyAsm(p2c);
	ReleaseSemaphore(&sema);
}

extern void
ChunkyToPlanar(struct c2pStruct *c2p) {
	AddMessageNo(MSG_C2P);
	ObtainSemaphore(&sema);
	ChunkyToPlanarAsm(c2p);
	ReleaseSemaphore(&sema);
}

/****** MPImage.library/MPChunkyToPlanar ******************************
*
*   NAME   
*  MPChunkyToPlanar -- Convert a chunky buffer to a BitMap (V7)
*
*   SYNOPSIS
*  MPChunkyToPlanar( chunky, bitmap, width, height )
*                    A0      A1      D0     D1
*
*  void MPPlanarToChunky( UBYTE *, struct BitMap *, UWORD, UWORD);
*
*   FUNCTION
*  Converts a chunky buffer to a BitMap.
*
*   INPUTS
*  chunky - Pointer to a chunky buffer of sufficient size.
*  bitmap - Pointer to a standard bitmap (of sufficient size).
*  width  - Width of buffer.
*  height - Height of buffer.
*
*   RESULT
*  None.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*  MPPlanarToChunky().
*
*****************************************************************************
*
*/
void __asm __saveds
MPChunkyToPlanar(register __a0 UBYTE *chunky,register __a1 struct BitMap *bitmap,
						register __d0 UWORD width, register __d1 UWORD height) {
	struct c2pStruct c2p;
	c2p.bmap = bitmap;
	c2p.startX = 0;
	c2p.startY = 0;
	c2p.width = width;
	c2p.height = height;
	c2p.chunkybuffer = chunky;
	ObtainSemaphore(&sema);
	ChunkyToPlanarAsm(&c2p);
	ReleaseSemaphore(&sema);
}

struct MyBitMap *
MyAllocBitMap(UWORD wide,UWORD high, UWORD deep, UBYTE *filename) {
	struct MyBitMap *bitmap;
	int k;
	BOOL error = 0;

	if (!filename || !*filename) {
		return (struct MyBitMap *)AllocBitMap(wide,high,deep,BMF_CLEAR|BMF_DISPLAYABLE,NULL);
	}
	if (bitmap = AllocMem(sizeof(struct MyBitMap),MEMF_CLEAR)) {
		InitBitMap((struct BitMap *)bitmap,deep,wide,high);
		for (k=0; k<deep && (!error); k++) {
			if (!(bitmap->BitMap.Planes[k] = AllocVec(RASSIZE(wide,high),MEMF_CLEAR))) {
				error = 1;
			}
		}
		if(error) {
			MyFreeBitMap(bitmap,filename);
			bitmap = NULL;
		}
	}
	return bitmap;
}

void
MyFreeBitMap(struct MyBitMap *bitmap,UBYTE *filename) {
	int k;
	if (!filename || !*filename) {
		FreeBitMap(bitmap);
		return;
	}
	for(k=0; k< bitmap->BitMap.Depth; k++) {
		if (bitmap->BitMap.Planes[k]) {
			FreeVec(bitmap->BitMap.Planes[k]);
		}
	}
	FreeMem(bitmap,sizeof(struct MyBitMap));
}

void __asm __saveds FreeMPImage(register __a0 struct MPImage *MPi);

char * __asm __saveds MPImageErrorMessage(void);

/****** MPImage.library/MPPlanarToChunky ******************************
*
*   NAME   
*  MPPlanarToChunky -- Convert a bitmap to a chunky buffer (V5)
*
*   SYNOPSIS
*  MPPlanarToChunky( bitmap, chunky, width, height )
*                    A0      A1      D0     D1
*
*  void MPPlanarToChunky( struct BitMap *, UBYTE *, UWORD, UWORD);
*
*   FUNCTION
*  Converts a BitMap to a chunky buffer.
*
*   INPUTS
*  bitmap - Pointer to a standard bitmap.
*  chunky - Pointer to a chunky buffer of sufficient size.
*  width  - Width of bitmap.
*  height - Height of bitmap.
*
*   RESULT
*  None.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*  MPChunkyToPlanar().
*
*****************************************************************************
*
*/
void __asm __saveds
MPPlanarToChunky(register __a0 struct BitMap *bitmap,register __a1 UBYTE *chunky,
						register __d0 UWORD width, register __d1 UWORD height) {
	struct p2cStruct p2c;
	p2c.bmap = bitmap;
	p2c.startX = 0;
	p2c.startY = 0;
	p2c.width = width;
	p2c.height = height;
	p2c.chunkybuffer = chunky;
	ObtainSemaphore(&sema);
	PlanarToChunkyAsm(&p2c);
	ReleaseSemaphore(&sema);
}


/****** MPImage.library/FreeMPImage ***********************************
*
*   NAME   
*  FreeMPImage -- Free an image loaded using LoadMPImage() (V3)
*
*   SYNOPSIS
*  FreeMPImage( MPi )
*               A0
*
*  void FreeMPImage( struct MPImage * );
*
*   FUNCTION
*  Frees all data associated loaded with an image using LoadMPImage().
*
*   INPUTS
*  MPi - structure returned by LoadMPImage().
*
*   RESULT
*  None.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*  LoadMPImage()
*
*****************************************************************************
*
*/
void __asm __saveds
FreeMPImage(register __a0 struct MPImage *MPi) {
	if (MPi) {
		if (MPi->Red) {
			FreeVec(MPi->Red);
		}
		if (!MPi->GreyScale) {
			if (MPi->Blue) {
				FreeVec(MPi->Blue);
			}
			if (MPi->Green) {
				FreeVec(MPi->Green);
			}
		}
		if (MPi->Object) {	// Dispose object (frees bit map as well...)
			DisposeDTObject(MPi->Object);
		}
		else {
			if (MPi->BitMap) {
				FreeBitMap(MPi->BitMap);
			}
			if (MPi->EGS_BitMap) {
				if (EGSBase = OpenLibrary((char *)"egs.library",0)) {
					E_DisposeBitMap(MPi->EGS_BitMap);
					CloseLibrary(EGSBase);
				}
			}
		}
		FreeMem(MPi,sizeof(struct MPImage));
	}
}

/****** MPImage.library/MPImageErrorMessage *********************************
*
*   NAME   
*  MPImageErrorMessage -- Return the last error set by MPImage.library. (V3)
*
*   SYNOPSIS
*  msg = MPImageErrorMessage()
*  D0
*
*  char *MPImageErrorMessage( void );
*
*   FUNCTION
*  Returns the last error message set by this opener of MPImage.library.
*
*   INPUTS
*  None.
*
*   RESULT
*  Formatted Error Message.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
*****************************************************************************
*
*/
char * __asm __saveds
MPImageErrorMessage(void) {
	return ErrorMessage;
}

extern BOOL ShowProgress;

/****** MPImage.library/SetMPImageScreen ************************************
*
*   NAME   
*  SetMPImageScreen -- Sets the Screen name for progress requesters (V3)
*
*   SYNOPSIS
*  SetMPImageScreen(ScreenName,Flags)
*                   A0         D0
*
*  void SetMPImageScreen(char *, ULONG);
*
*   FUNCTION
*  Sets the Screen Name for progress requesters for this opener.
*
*   INPUTS
*  ScreenName - Name of Public Screen, NULL for default
*  Flags      - 0 - Do not show progress requesters (default)
*             - MPIF_PROGRESS - Do show progress requesters
*                               (except for non remapped bitmaps).
*
*   RESULT
*  None.
*
*   EXAMPLE
*
*   NOTES
*  ScreenName must remain valid while MPImage.library is open.
*
*   BUGS
*
*   SEE ALSO
*
*****************************************************************************
*
*/
void __asm __saveds
SetMPImageScreen(register __a0 char *ScreenName, register __d0 ULONG Flags) {
	PubScreenName = ScreenName;
	if (Flags & MPIF_PROGRESS) {
		ShowProgress = TRUE;
	}
	else {
		ShowProgress = FALSE;
	}
}

/****** MPImage.library/MPProgressHook ************************************
*
*   NAME   
*  MPProgressHook -- Sets the Progress Hook (V6)
*
*   SYNOPSIS
*  MPProgressHook(Hook)
*                 A0
*
*  void MPProgressHook(struct Hook*);
*
*   FUNCTION
*  Sets the Hook to call for progress messages.
*
*   INPUTS
*  Hook - Hook to call
*
*   RESULT
*  None.
*
*   EXAMPLE
*
*   NOTES
*  Called with...
* object = MPIP_MAX, (ULONG)message = max-progress
* object = MPIP_MAX, (ULONG)message = curr-progress
* object = MPIP_CURR,(UBYTE *)message = Progress-Message
*
*   BUGS
*
*   SEE ALSO
*
*****************************************************************************
*
*/
void __asm __saveds
MPProgressHook(register __a0 struct Hook *Hook) {
	ProgressHook = Hook;
}
