/********************************************************************
*   DrvImages.c
*
*   Macintosh device specific images sub-system implementation.
*
*   Copyright (c) 1994-1997, Willows Software Inc.  All rights reserved.
********************************************************************/

/* System Includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <QDOffscreen.h>

/* Driver includes */
#include "DrvHook.h"
#include "DrvImages.h"
#include "DrvSystem.h"
#include "DrvUtils.h"
#include "DrvDP.h"
#include "GdiDC.h"

/* Core includes */
#include "Log.h"


/* Temporary Stuff from Driver.c and Driver .h */
#define USETEMPDRIVER 0

#if USETEMPDRIVER
#include "TempDriver.h"
#define ROPCODESOK 1
#endif

#define DEFAULTCLUT		// To use the systems clut


/* Local prototypes */
static LPDRVIMAGEDATA DrvCreateImage(CREATEIMAGEDATA *lpCreate, BOOL mono);
static void DrvDestroyImage(LPDRVIMAGEDATA);
static BOOL DrvCreateMask(LPDRVIMAGEDATA,LPSTR);
static LPSTR DrvGetBitmapData(LPDRVIMAGEDATA);
static int DrvGetImageCaps(int,DWORD,LPVOID);

static LPVOID DrvCreateBrush(LPDRVIMAGEDATA lpImageData, DWORD dwStyle);
static DWORD DrvDestroyBrush(LPVOID brushPattern);
static DWORD DrvCreateCursor(CREATECURSOR *lpcc);
static DWORD DrvDestroyCursor(void *lpCursor);

/* Local utility prototypes */
static void PixMap2PixPat(PixMapHandle pixmap, PixPatHandle pixpat);
static void FlipHorizontal(void *pixMap);
static void FlipVertical(void *pixMap);
static short MapRasterOp(long rasterOp);		
static void InitPixMapCTable(PixMapHandle pixMap) ;


#define PMROWBYTESMSB 	0x8000	/* The MSB of the rowBytes field needs to be one to signify it is a pixmap */



/********************************************************************
* PrivateImageHook
*	
********************************************************************/
DWORD PrivateImageHook(WORD wFunc, LPARAM dwParam1, LPARAM dwParam2, LPVOID lpStruct)
{
LPDRVIMAGEDATA lpimage = lpStruct;

    switch (wFunc) {
    /* (dwParm)?INITIALIZE:DEINITIALIZE */
	case DSUBSYSTEM_INIT:
		return (DWORD)TRUE;

	case DSUBSYSTEM_GETCAPS:
	case DSUBSYSTEM_EVENTS:
	    return 1L;

	/* dwParm1 - mono bitmap bool */
	/* lpStruct - pointer to IMAGEINFO */
	case PIH_CREATEIMAGE:
		return (DWORD) DrvCreateImage((CREATEIMAGEDATA *)lpStruct, (BOOL) dwParam1);

	/* lpStruct - pointer to IMAGEINFO */
	case PIH_DESTROYIMAGE:
	    DrvDestroyImage(lpimage);
	    return 1L;

	case PIH_UPDATEPIXMAP:
		/* NOP for us, we have not x style server pixmap */
	    return 1L;

	case PIH_GETDEVCAPS:
	    return (DWORD)DrvGetImageCaps((int)dwParam1,dwParam2,lpStruct);

	/* lpStruct - pointer to IMAGEINFO */
	case PIH_GETIMAGEDATA:
	    return (DWORD)DrvGetBitmapData(lpimage);

	/* lpStruct - pointer to IMAGEINFO */
	/* dwParam1 - pointer to area to copy bits into */
	/* dwParam2 - bit depth in pixels */
	case PIH_FETCHBITS:
		return(DrvGetPixmapBits(lpimage->image, (void *)dwParam1));

	case PIH_CREATEBRUSH:
	    return (DWORD)DrvCreateBrush((LPDRVIMAGEDATA)lpStruct, dwParam1);

	case PIH_DESTROYBRUSH:
	    return (DWORD)DrvDestroyBrush(lpStruct);

	case PIH_CREATECURSOR:
	    return (DWORD)DrvCreateCursor((CREATECURSOR *)lpStruct);

	case PIH_DESTROYCURSOR:
	    return (DWORD)DrvDestroyCursor(lpStruct);

	default:
	    return 0;
    }
}




/********************************************************************
* DrvCreateImage
*
*  Dispose of the image pixmap structure.
*  The memory for the bit stream contained in the pixmap will be
*	disposed above in the calling routine.
********************************************************************/
static LPDRVIMAGEDATA DrvCreateImage(CREATEIMAGEDATA *lpcreate, BOOL mono)
{
    LPDRVIMAGEDATA image;
	void *pixmap;
	
		/* Call the wrapper function to put the pixmap structure into lpimage */
		pixmap = DrvCreatePixmap(lpcreate->lpData, 
	   											lpcreate->nWidth, 
	    										lpcreate->nHeight, 
	    										lpcreate->nWidthBytes,
	    										lpcreate->nBitsPixel,
	    										FALSE);
	    						
	    if (pixmap == NULL)
	    	return NULL;										/* Pixmap allocation failed, return null */
	
	image = DrvMalloc(sizeof(DRVIMAGEDATA));	/* Allocate the image structure */

	if (image == NULL)
		return NULL;
		
    image->nWidth 			= lpcreate->nWidth;
    image->nHeight 		= lpcreate->nHeight;
    image->nLinePad		= lpcreate->nLinePad;
    image->nBitsPixel 	= lpcreate->nBitsPixel;
    image->nWidthBytes = lpcreate->nWidthBytes;
    image->fMono 			= mono;
    image->fInvalid 		= FALSE;
    image->image 			= pixmap;
	
	return image;
}




/********************************************************************
* DrvDestroyImage
*
*  Dispose of the image pixmap structure.
*  The memory for the bit stream contained in the pixmap will be
*	disposed above in the calling routine.
********************************************************************/
static void DrvDestroyImage(LPDRVIMAGEDATA lpimage)
{
	/* Make sure a valid image exists */
	if (!lpimage)
		return;
		
	/* Use wrapper function to destroy the pixmap structure */ 
	/* We free bit stream here*/
	if (lpimage->image)
		DrvDestroyPixmap(lpimage->image, TRUE);		
	
	DrvFree(lpimage);						/* Free the image structure */
}




/********************************************************************
* DrvGetBitmapData
*
*  Return a pointer to the bitmaps data.
********************************************************************/
static LPSTR DrvGetBitmapData(LPDRVIMAGEDATA lpimage)
{
	/* Make sure the image is valid */
    if ((lpimage == 0) || (lpimage->image == 0))
		return NULL;

	return((**(lpimage->image)).baseAddr);

}




/********************************************************************/
static int DrvGetImageCaps(int nFunction, DWORD dwParam, LPVOID lpStruct)
{
    int nBitsPixel, nImageBitsPixel;
    DRVIMAGEDATA *lpimage;

    switch (nFunction) {
	case DRV_QUERYDEPTH:
	/* Ask driver if it can convert given image (passed in lpStruct)*/
	/* to given depth (passed in dwParam); This X driver supports  */
	/* only mono to mono (1 bit deep) and default depth;  */
	/* Return value is the supported depth (may be different from	*/
	/* the one asked for);  passing 0 as format queries default	*/
	/* value of bits per pixel	*/
	    if ((lpimage = (DRVIMAGEDATA *)lpStruct) != 0)
			nImageBitsPixel = lpimage->nBitsPixel;
	    else
			nImageBitsPixel = 0;
	    nBitsPixel = (int)dwParam;

	    if ((nBitsPixel == 1) && (nImageBitsPixel == 1))
			return 1;
	    else
			return (8);

	case DRV_QUERYBITSPIXEL:
	    return (gDspl->screenDepth);

	case DRV_BITORDER:
		return(MSBFIRST);
		
	default:
	    return 0;
    }
}


/********************************************************************
* DrvCreateBrush
*
*  Create a new PixPatHandle or PatPtr with the PixMapHandle to be used with
*    the FillCRect or FillRect QD calls respectively.
*  Returns PixPatHandle if isPixMapis true, else returns PatPtr.
********************************************************************/
static LPVOID DrvCreateBrush(LPDRVIMAGEDATA lpImageData, DWORD dwStyle)
{
PixPatHandle pixpat;
PatPtr penpat;
short patIndex = 4;

	if (dwStyle == BFP_PIXMAP) {
#if 1
		/* Create the PixPatHandle */
		pixpat = NewPixPat();
		if (!pixpat)
			return(NULL);
			
		/* Copy the pixmap into the pixpat */
		PixMap2PixPat(lpImageData->image, pixpat );

		PixPatChanged(pixpat); 
		return(pixpat);

#else
		pixpat = GetPixPat(131);		/* Get the pixmap from resource */
#endif		
	}
	else {
		penpat = DrvMalloc(sizeof(Pattern));
		if (!penpat)
			return(NULL);
		GetIndPattern(penpat, sysPatListID, patIndex);
		return(penpat);
	}

}


/********************************************************************
* DrvDestroyBrush
*
*  Destroys the brush structure.
********************************************************************/
static DWORD DrvDestroyBrush(LPVOID brushPattern)
{
BOOL isPixMap = TRUE;		// LATER account for PatPtr's
char cMemTags;

	cMemTags = HGetState(brushPattern);
	if (MemError())
		isPixMap = FALSE;

	if (isPixMap) {
		/* Hopefully this will free the patData handle as well */
//TEMPORARY		DisposPixPat(brushPattern);
	}
	else {
		DrvFree(brushPattern);
	}

	return TRUE;
}


/********************************************************************
* PixMapToCursor
*
*  Convert a 32x32 1bit deep pixmap into a Cursors data or mask.
********************************************************************/
static void PixMapToCursor(PixMapHandle pixmap, short *cursData)
{
long *srcData = (long *)(**pixmap).baseAddr;
long srcBits;
int index, bit;

	/* Index each element of the cursor data */
	for (index=0; index <16; index++) {
		srcBits = *srcData;										/* Store bits in shift register */
		*cursData = 0;												/* Clear the bits */
		/* Index each bit of the cursor data */
		for (bit=0; bit<16; bit++) {
			*cursData += (srcBits & 0x01) << bit;		/* Take LSB */ 
			srcBits >>= 2;											/* Take every other bit */
		}
		
		++cursData;
		srcData += 2;		/* take every other long word */
	}

} 


/********************************************************************
* DrvCreateCursor
*
*  
********************************************************************/
static DWORD DrvCreateCursor(CREATECURSOR *lpcc)
{
LPDRVIMAGEDATA lpXORData, lpANDData;
int nWidth,nHeight,index;
Cursor *lpCursor;

	/* Get the data and mask images */
    lpXORData = (LPDRVIMAGEDATA)lpcc->lpXORData;		/* Data */
    lpANDData = (LPDRVIMAGEDATA)lpcc->lpANDData;		/* Mask */
	
	nWidth = lpXORData->nWidth;
	nHeight = lpXORData->nHeight;
	/* Make sure mask and data have same dimentions */
    if ((nWidth  != lpANDData->nWidth) ||	(nHeight != lpANDData->nHeight))
		return 0L;

	/* Color cursors -- can it ever happen??? */
	/* If so, deal with it LATER */
    if (lpXORData->nBitsPixel != 1)
		return 0L;

	/* Create the cursor structure */
	lpCursor = DrvMalloc(sizeof(Cursor));

	/* Set the cursor hot spot */
	lpCursor->hotSpot.h = lpcc->ptHot.x / 2;
	lpCursor->hotSpot.v = lpcc->ptHot.y / 2;
		
	/* Copy and scale the data into the cursor's data structures */
	PixMapToCursor(lpXORData->image, lpCursor->data);
	PixMapToCursor(lpANDData->image, lpCursor->mask);

	/* Modify the Cursor data from Windows logic to Mac logic. 	*/
	/* Windows = 	(bg AND andData) XOR xorData 						*/
	/* Mac =			(bg XOR mask) OR data									*/
	for (index=0; index<16; index++) {
		lpCursor->mask[index] = ~lpCursor->mask[index];
		lpCursor->data[index] = ~lpCursor->data[index] & lpCursor->mask[index];
	}
	
	return((DWORD)lpCursor);
}


/********************************************************************
* DrvDestroyCursor
*
*  
********************************************************************/
static DWORD DrvDestroyCursor(void *lpCursor)
{

	DrvFree(lpCursor);

	return(TRUE);
}


/********************************************************************
* PixMap2PixPat
*
*  Copies a pix pat from a pix map.
********************************************************************/
static void PixMap2PixPat(PixMapHandle pixmap, PixPatHandle pixpat)
{
Handle 	image;
PixMapHandle pixmapCopy;
long 		imageSize = 64, actualRowBytes;
Ptr dataCopy;

	/* Calculate the image size, we will be making a copy of the data */
	actualRowBytes = (**pixmap).rowBytes & 0x7FFF;	
	imageSize = actualRowBytes * ((**pixmap).bounds.bottom - (**pixmap).bounds.top);

	/* Make a copy of the source pixmap */	
	pixmapCopy = NewPixMap();
	CopyPixMap(pixmap, pixmapCopy);
	dataCopy = NewPtr(imageSize);
	BlockMove((**pixmap).baseAddr, dataCopy, imageSize);
	(**pixmapCopy).baseAddr = dataCopy;

	/* Point the patMap (PixMapHandle) to the source pixmap */
	(**pixpat).patMap = pixmap;

	/* Create another copy of the data and place in a new handle */				
	PtrToHand( (**pixmap).baseAddr, &image, imageSize );
	/* Save the new handle in the patData of the PixPat */
	(**pixpat).patData = image;
}


/********************************************************************
* CreatePixmapFromRect
*
*  Returns a new pixmap with the size given in the rect parameter.
********************************************************************/
PixMapHandle CreatePixmapFromRect(Rect srcRect)
{
short widthBytes;
PixMapHandle newPixMap = NULL;

	/* make sure rowbytes is a multiple of 4 */
	widthBytes = (((RECTWIDTH(&srcRect) * TEMPPIXMAPDEPTH) + 31) / 32) * 4;		

	/* Create the new pix map, look carefully at the parameters! */
	/* Pass null as the data, driver will not copy any bits */
	/* Pass TRUE to copyData, which creates memory, but since */
	/*  we passes NULL data, it will be undefined data (garbage) */
	/* Rect could be flipped, thus the abs value is needed */	
	newPixMap = DrvCreatePixmap(NULL, 											
								RECTWIDTH(&srcRect),	
								RECTHEIGHT(&srcRect),
								widthBytes, 
								TEMPPIXMAPDEPTH,
								TRUE);			

	return(newPixMap);
}

/********************************************************************
* DrvCreatePixmapFromPixmap
*
*  Returns a new pixmap from an old one.
*  While doing so, copy, stretch, and flip the source 
*	image into the new pixmap.
*
*  Input: newRect, the size of the desired new pixmap.
*  Output: newRect, normalized rect, with positive coords (no flip).
*
********************************************************************/
PixMapHandle DrvCreatePixmapFromPixmap(PixMapHandle srcPixMap, Rect srcRect, Rect *newRect)
{
short widthBytes;
PixMapHandle newPixMap = NULL;
	
	*newRect = srcRect;
	
	/* make sure rowbytes is a multiple of 4 */
	widthBytes = (((AbsSizeOfRect(*newRect, TRUE) * TEMPPIXMAPDEPTH) + 31) / 32) * 4;		


	/* Create the new pix map, look carefully at the parameters! */
	/* Pass null as the data, driver will not copy any bits */
	/* Pass TRUE to copyData, which creates memory, but since */
	/*  we passes NULL data, it will be undefined data (garbage) */
	/* Rect could be flipped, thus the abs value is needed */	
	newPixMap = DrvCreatePixmap(NULL, 											
								AbsSizeOfRect(*newRect, TRUE),	
								AbsSizeOfRect(*newRect, FALSE),
								widthBytes, 
								TEMPPIXMAPDEPTH,
								TRUE);			

	/* Check allocation prior to continuing */
	if(!newPixMap)
		return(FALSE);			/* Allocation failed, abondon ship! */

	/* The destRect needs to be normalized for transfering into 		*/
	/*  the new srcPixMap since it is just large enough for the image. 	*/
	/* Set the corner to be zero, zero */
	NormalizeRect(newRect, FALSE);								

	/* Now make a copy of the original, streching and flipping as necessary */
	DrvCopyPixMap(srcPixMap, newPixMap, srcRect, *newRect, srcCopy);

	/* We have flipped the image, remove flip information */
	ResetRect(newRect);			

	return(newPixMap);
}					  




/********************************************************************
* DrvCreatePixmap
*
*  Create a new pixmap (image) with the specified data.
*  If copyData, allocate our own bitstream and copy the data into it.
*  Returns NULL if any allocation failed.
********************************************************************/
PixMapHandle DrvCreatePixmap(void *data, short width, short height, short rowBytes, short bitsPerPix, short copyData)
{
PixMapHandle pixMapH = NULL;
long sizeOfBits;
Ptr myBits = NULL;
Rect boundsRect;

	/* Allocate the new pixmap structure */
	if((pixMapH = NewPixMap()) == NULL)
		return(NULL);

	/* Determine if we must allocate storage and make a copy of the data*/
	if(copyData)
	{
		/* Allocate memory for a new bit stream */
		sizeOfBits = rowBytes * (long)height;
		myBits = DrvMalloc(sizeOfBits + 64);				/* use memory manager to trap errors */

		/* See if allocation passes, hit the silk if it failed */
		if(!myBits)
		{
			DisposePixMap(pixMapH);
			return(NULL);			
		}
		
		/* Copy the data passes in to our bit stream */
		/* Makes sure there is source and destination memory allocated */
		if(data && myBits)
			BlockMove(data, myBits, sizeOfBits);	/* analogous to the memcpy function (only faster) */

		/* Substitute our bit stream for the one passed in */
		data = myBits;								
	}
	
	/* Put raw image data into pix map base data addr ptr */
	(**pixMapH).baseAddr = data;

	/* The MSB of rowbytes has to be set, else it is treated as a bit map */
	(**pixMapH).rowBytes = rowBytes | PMROWBYTESMSB;

	SetRect(&boundsRect, 0, 0, width, height); 
	(**pixMapH).bounds = boundsRect;

	(**pixMapH).cmpSize = bitsPerPix;
	(**pixMapH).pixelSize = bitsPerPix;

	InitPixMapCTable(pixMapH);
	
	return(pixMapH);
}



/********************************************************************
* DrvDestroyPixmap
*
*  Destroy a pixmap (image).
*  If freeData, dispose of the memory in the baseAddr bit stream.
********************************************************************/
void DrvDestroyPixmap(PixMapHandle pixMap, short freeData)
{
	/* Be save and check to make sure there is something there */
	if (pixMap == NULL)
		return;
		
	/* Free the bit stream if desired. */
	if(freeData)
		DrvFree((**pixMap).baseAddr);
	
	/* This disposes the the PixMap record and it's color table */
	/* Make sure not to dispose a PixMap whose color table is same as current device's CLUT */
	DisposePixMap(pixMap);

}




/********************************************************************
* DrvGetPixmapBits
*
*  Copy the pixmap's data.  Return the number of bytes per row.
********************************************************************/
long DrvGetPixmapBits(PixMapHandle pixMap, void *data)
{
long actualRowBytes, byteCount, height;

	if (!data || !pixMap)
		return(0);
		
	/* Mask off the MSB of rowBytes to get actual amount */
	actualRowBytes = (**pixMap).rowBytes | !(PMROWBYTESMSB);

	/* Calculate the number of bytes to move */
	height = (**pixMap).bounds.bottom - (**pixMap).bounds.top;
	byteCount = actualRowBytes * height;
	
	/* Copy the bit stream */
	BlockMove((**pixMap).baseAddr, data, byteCount);

	return(actualRowBytes);
}




/********************************************************************
* InitPixMapCTable
*
*  When NewPixMap is used, a handle to a color table is allocated but no initialized.
********************************************************************/
static void InitPixMapCTable(PixMapHandle pixMap) 
{
CTabHandle	ourCMHandle;
Handle h;
GDHandle	curDevice;
OSErr	err;
short index;

	if ((**pixMap).pixelSize == 1) {
		ourCMHandle = GetCTable((**pixMap).pixelSize);
	}
	else {
#ifdef DEFAULTCLUT
		curDevice = GetGDevice();									/* save theGDevice so we */

		ourCMHandle = (**(**curDevice).gdPMap).pmTable;	/* Get the old handle */
		h = (Handle) ourCMHandle;			/* make a copy */
		err = HandToHand(& h);			/* create the new handle (overwrites original, thus the copy is used) */
		ourCMHandle = (CTabHandle) h;	/* Now set to our variable */

		/* Real programs do error checking here */
		for (index = 0; index <= (**ourCMHandle ).ctSize; ++index)
			(**ourCMHandle ).ctTable[index].value = index;
		(**ourCMHandle ).ctFlags &= 0x7FFF;
		/* This code is necessary for converting GDevice cluts to Pixmap cluts */

#else
		ourCMHandle = GetCTable(128);
#endif

//	(**ourCMHandle ).ctFlags |= 0x4000;		//Try assigning to the pallette

	}


//	(**ourCMHandle ).ctSeed = GetCTSeed();
	CTabChanged(ourCMHandle);
	(**(pixMap)).pmTable = ourCMHandle;
}


/********************************************************************
* DrvCopyPixMap
*
*  Copy the pixmap's data from source to destination.
*  This function will stretch and flip as necessary.
********************************************************************/
short DrvCopyPixMap(PixMapHandle srcPixMap, PixMapHandle destPixMap, 
					Rect srcRect, Rect destRect, 
					long rasterOp)
{
short doFlip;
short xferMode;
int err = noErr;
Rect resultRect;
BitMap scrn = qd.screenBits;
PixMapHandle tempPixmap = NULL;

	/* CopyBits will handle the strech image function for us, so we		*/
	/*  need to handle the horizontal and vertical flipping operations.	*/

	/* get the flips that need to be performed */
	doFlip = IsFlipped(&srcRect, &destRect);
	
	/* Reset any flipping that is present in the rectangles */
	ResetRect(&srcRect);
	ResetRect(&destRect);

	/* Convert library raster op to platform raster op */
	xferMode = rasterOp;
	
	/* See if we have a special case where we have overlapping areas in the same pixmap*/
	if ((srcPixMap == destPixMap) && SectRect(&srcRect, &destRect, &resultRect)) {
 		tempPixmap = DrvCreatePixmapFromPixmap(srcPixMap, srcRect, &srcRect);
		srcPixMap = tempPixmap;		/* Substitute the src pixmap with the temporary one */
	} 

	/* Lock down the pixmaps, copybits WILL move memory */
	HLock((Handle) srcPixMap);
	HLock((Handle) destPixMap);

	/* First perform the copy/strech into the destination */
	CopyBits((BitMapPtr)*srcPixMap, (BitMapPtr)*destPixMap, &srcRect, &destRect, xferMode, nil);
	err = QDError();
	
	HUnlock((Handle) srcPixMap);
	HUnlock((Handle) destPixMap);

	/* Since the flipping is done in-place, do it on the destination */
	if(doFlip & FLIPH)
		FlipHorizontal(destPixMap);
	
	if(doFlip & FLIPV)
		FlipVertical(destPixMap);

	if (tempPixmap)
		DrvDestroyPixmap(tempPixmap, TRUE);	

	return(0);
}




/********************************************************************
* FlipHorizontal
*
*  Flips the entire image around the horizontal axis.
********************************************************************/
static void FlipHorizontal(void *pixMap)
{
#pragma unused(pixMap)		/* LATER: Implement FlipHorizontal()*/
		
}



/********************************************************************
* FlipVertical
*
*  Flips the entire image around the vertical axis.
********************************************************************/
static void FlipVertical(void *pixMap)
{
#pragma unused(pixMap)		/* LATER: Implement FlipHorizontal()*/
		
		
}



/********************************************************************
* MapRasterOp
*
*  Convert library raster ops to platform raster ops.
*  Temporary until table is devised.
********************************************************************/
static short MapRasterOp(long rasterOp)
{
	switch(rasterOp)
	{
	case SRCAND:
		return(srcBic);
	case SRCCOPY:
	default:
		return(srcCopy);
	}
	
}

