/* See <URL:
http://wiki.tcl.tk/3440.html > for details */
#include "DimgImage.h"
char *g_aszOptions[] = { "-height", "-width", "-mask", "-rgb", (char*)NULL };
char *g_aszModes[] = { "disable",
"BLACKNESS", "DSTINVERT", "MERGECOPY", "MERGEPAINT", "NOTSRCCOPY", "NOTSRCERASE",
"PATCOPY", "PATINVERT", "PATPAINT", "SRCAND", "SRCCOPY", "SRCERASE", "SRCINVERT", "SRCPAINT",
"WHITENESS", (char*)NULL };
DWORD g_adwModes[] = { 0,
BLACKNESS, DSTINVERT, MERGECOPY, MERGEPAINT, NOTSRCCOPY, NOTSRCERASE,
PATCOPY, PATINVERT, PATPAINT, SRCAND, SRCCOPY, SRCERASE, SRCINVERT, SRCPAINT,
WHITENESS };
RGBQUAD g_aDefaultColorTable[]
= { 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 255, 0,
255, 0, 0, 0, 255, 0, 255, 0, 255, 255, 0, 0, 255, 255, 255, 0 };
Tk_ImageType g_stDimgDesc;
/*
*----------------------------------------------------------------------
*
* DimgConfigureCmd --
*
* This procedure is called when a Dimg image is created or
* reconfigured. It processes configuration options and resets
* any instances of the image.
*
* Results:
* A standard Tcl return value.
*
* Side effects:
* Existing instances of the image will be redisplayed to match
* the new configuration options.
*
*----------------------------------------------------------------------
-height height of image widget
-width width of image widget
-mask mode for mask bitmap
-rgb mode for rgb bitmap
*/
int DimgConfigureCmd( Tcl_Interp *pInterp, DimgMaster *pMaster, int nObj, Tcl_Obj * CONST aObj[] )
{
Tcl_Obj *CONST *pObject;
Tcl_Obj *pResult = Tcl_GetObjResult(pInterp);
BITMAPCOREHEADER stBMCoreHeader;
int iOption;
int iMode;
char szBuffer[512];
int iBuffer = 0;
bool bSizeChange = false;
if ( nObj == 0 )
{
/*
return all configuration parameters
*/
iBuffer += sprintf( szBuffer, "{-height %d -width %d ", pMaster->nHeight, pMaster->nWidth );
for ( iMode = 0; g_adwModes[iMode] != pMaster->dwImageMode; iMode++ );
iBuffer += sprintf( szBuffer + iBuffer, "-rgb %s ", g_aszModes[iMode] );
for ( iMode = 0; g_adwModes[iMode] != pMaster->dwMaskMode; iMode++ );
iBuffer += sprintf( szBuffer + iBuffer, "-mask %s}", g_aszModes[iMode] );
Tcl_AppendStringsToObj( pResult, szBuffer, NULL );
return TCL_OK;
}
pObject = aObj;
for( ; nObj >= 2; nObj -= 2, pObject += 2 )
{
if ( Tcl_GetIndexFromObj( pInterp, pObject[0], (const char**)g_aszOptions, "option", 0, &iOption ) != TCL_OK )
{
return TCL_ERROR;
}
switch( iOption )
{
case 0: // height
if ( Tcl_GetIntFromObj( pInterp, pObject[1], &pMaster->nHeight ) != TCL_OK )
{
return TCL_ERROR;
}
bSizeChange = true;
break;
case 1: // width
if ( Tcl_GetIntFromObj( pInterp, pObject[1], &pMaster->nWidth ) != TCL_OK )
{
return TCL_ERROR;
}
pMaster->nMaskPitch = ( ( ( pMaster->nWidth - 1 ) >> 2 ) + 1 ) << 2;
pMaster->nImagePitch = pMaster->nWidth << 2;
bSizeChange = true;
break;
case 2: case 3: // mask, rgb
if ( Tcl_GetIndexFromObj( pInterp, pObject[1], (const char**)g_aszModes, "mode", 0, &iMode ) != TCL_OK )
{
return TCL_ERROR;
}
if ( iOption == 3 )
{
pMaster->dwImageMode = g_adwModes[iMode];
}
else
{
pMaster->dwMaskMode = g_adwModes[iMode];
}
break;
}
}
if ( ( bSizeChange || !pMaster->dwImageMode ) && pMaster->bmImage )
{
DeleteObject( pMaster->bmImage );
pMaster->bmImage = NULL;
}
if ( ( bSizeChange || !pMaster->dwMaskMode ) && pMaster->bmMask )
{
DeleteObject( pMaster->bmMask );
pMaster->bmMask = NULL;
}
if ( pMaster->nWidth && pMaster->nHeight )
{
stBMCoreHeader.bcHeight = pMaster->nHeight;
stBMCoreHeader.bcWidth = pMaster->nWidth;
stBMCoreHeader.bcPlanes = 1;
if ( pMaster->dwImageMode && !pMaster->bmImage )
{
stBMCoreHeader.bcSize = sizeof(BITMAPCOREHEADER);
stBMCoreHeader.bcBitCount = 32;
pMaster->bmImage = CreateDIBSection( GetDC(0), (BITMAPINFO*)&stBMCoreHeader, DIB_RGB_COLORS, (void**)&pMaster->pImageData, 0, 0 );
if ( !pMaster->bmImage )
{
Tcl_AppendResult( pInterp, "dimg image error: Can't allocate rgb bitmap", 0 ) ;
return TCL_ERROR;
}
}
if ( pMaster->dwMaskMode && !pMaster->bmImage )
{
stBMCoreHeader.bcSize = sizeof(BITMAPCOREHEADER);
stBMCoreHeader.bcBitCount = 8;
pMaster->bmMask = CreateDIBSection( GetDC(0), (BITMAPINFO*)&stBMCoreHeader, DIB_PAL_COLORS, (void**)&pMaster->pMaskData, 0, 0 );
if ( !pMaster->bmMask )
{
Tcl_AppendResult( pInterp, "dimg image error: Can't allocate mask bitmap", 0 ) ;
return TCL_ERROR;
}
}
GdiFlush();
}
Tk_ImageChanged( pMaster->tkMaster, 0, 0, pMaster->nWidth, pMaster->nHeight, pMaster->nWidth, pMaster->nHeight );
return (TCL_OK);
}
/*
*----------------------------------------------------------------------
*
* DimgCgetCmd --
*
* Return one or all configuration options.
*
* Results:
* A standard Tcl return value. If TCL_ERROR is returned then
* an error rawImgDisplayMessage is left in masterPtr->interp->result.
*
* Side effects:
* none
*
*----------------------------------------------------------------------
-height height of image widget
-width width of image widget
-mask mode for mask bitmap
-rgb mode for rgb bitmap
*/
int DimgCgetCmd( Tcl_Interp *pInterp, DimgMaster *pMaster, int nObj, Tcl_Obj * CONST aObj[] )
{
char szBuffer[512];
int iOption;
int iMode;
DWORD dwMode;
if ( Tcl_GetIndexFromObj( pInterp, aObj[0], (const char**)g_aszOptions, "option", 0, &iOption ) != TCL_OK )
{
return TCL_ERROR;
}
switch( iOption )
{
case 0: // height
sprintf( szBuffer, "%d", pMaster->nHeight );
break;
case 1: // width
sprintf( szBuffer, "%d", pMaster->nWidth );
break;
case 2: case 3: // mask, rgb
if ( iOption == 3 )
{
dwMode = pMaster->dwImageMode;
}
else
{
dwMode = pMaster->dwMaskMode;
}
for ( iMode = 0; g_adwModes[iMode] != dwMode; iMode++ );
strcpy( szBuffer, g_aszModes[iMode] );
break;
}
Tcl_AppendResult( pInterp, szBuffer, NULL );
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* DimgFillOrOvalCmd --
*
* Fills the entire area or the largest possible oval
* inside the rgb or mask image with a specific color or index.
* The prototyped functions below do the actual work.
*
* Results:
* A standard Tcl return value.
*
* Side effects:
* none
*
*----------------------------------------------------------------------
oval [rgb|mask] value
fill [rgb|mask] value
*/
void DimgFillRgb( int nX, int nY, int nPitch, int iColor, char * pData );
void DimgOvalRgb( int nX, int nY, int nPitch, int iColor, char * pData );
void DimgFillMask( int nX, int nY, int nPitch, int iColor, char * pData );
void DimgOvalMask( int nX, int nY, int nPitch, int iColor, char * pData );
int DimgFillOrOvalCmd( int iAction, Tcl_Interp *pInterp, DimgMaster *pMaster, int nObj, Tcl_Obj * CONST aObj[] )
{
int iResult;
XColor stColor;
const char *szColor;
int iColor;
static char *aszTargets[] = { "rgb", "mask", (char*)NULL };
int iTarget;
if ( Tcl_GetIndexFromObj( pInterp, aObj[0], (const char **)aszTargets, "target", 0, &iTarget ) != TCL_OK )
{
return TCL_ERROR;
}
switch ( iTarget )
{
case 0: //rgb
if ( !pMaster->bmImage )
{
Tcl_AppendResult( pInterp, "no rgb bitmap assigned", NULL );
return TCL_ERROR;
}
szColor = Tcl_GetStringFromObj( aObj[1], 0 );
iResult = XParseColor( 0, 0, szColor, &stColor );
/* the first two arguments are not available here -
but they are anyway currently not used in the
implementation of XParseColor */
if ( !iResult )
{
Tcl_AppendResult( pInterp, "invalid color name \"", szColor, "\"", NULL );
return TCL_ERROR;
}
iColor = (stColor.blue >> 8) +
(stColor.green & 0x00ff00 ) +
((stColor.red & 0x00ff00 ) << 8 );
if ( iAction )
{
DimgOvalRgb( pMaster->nWidth, pMaster->nHeight, pMaster->nImagePitch, iColor, pMaster->pImageData );
}
else
{
DimgFillRgb( pMaster->nWidth, pMaster->nHeight, pMaster->nImagePitch, iColor, pMaster->pImageData );
}
break;
case 1: //mask
if ( !pMaster->bmMask )
{
Tcl_AppendResult( pInterp, "no mask bitmap assigned", NULL );
return TCL_ERROR;
}
iResult = Tcl_GetIntFromObj( pInterp, aObj[1], &iColor );
if ( iResult != TCL_OK )
{
return TCL_ERROR;
}
if ( iColor < 0 || iColor > 7 )
{
Tcl_AppendResult( pInterp, "color index out of range", NULL );
return TCL_ERROR;
}
if ( iAction )
{
DimgOvalMask( pMaster->nWidth, pMaster->nHeight, pMaster->nMaskPitch, iColor, pMaster->pMaskData );
}
else
{
DimgFillMask( pMaster->nWidth, pMaster->nHeight, pMaster->nMaskPitch, iColor, pMaster->pMaskData );
}
break;
}
Tk_ImageChanged( pMaster->tkMaster, 0, 0, pMaster->nWidth, pMaster->nHeight, pMaster->nWidth, pMaster->nHeight );
return TCL_OK;
}
void DimgOvalRgb( int nX, int nY, int nPitch, int iColor, char * pData )
{
int iX;
int iY;
int iX0;
int iX1;
double fA = (double) nX / 2.0;
double fB = (double) nY / 2.0;
int *piData;
for ( iY = 0; iY < nY; iY++ )
{
iX0 = (int)( ( ( fB - sqrt( (double)iY * ( 2.0 * fB - (double)iY ) ) ) * fA ) / fB );
iX1 = nX - iX0;
piData = (int*)( pData + iY * nPitch );
for ( iX = iX0; iX < iX1; iX++ )
{
piData[iX] = iColor;
}
}
}
void DimgFillRgb( int nX, int nY, int nPitch, int iColor, char * pData )
{
int iX;
int iY;
int *piData;
for ( iY = 0; iY < nY; iY++ )
{
piData = (int*)( pData + iY * nPitch );
for ( iX = 0; iX < nX; iX++ )
{
piData[iX] = iColor;
}
}
}
void DimgOvalMask( int nX, int nY, int nPitch, int iColor, char * pData )
{
int iY;
int iX0;
int iX1;
double fA = (double) nX / 2.0;
double fB = (double) nY / 2.0;
for ( iY = 0; iY < nY; iY++ )
{
iX0 = (int)( ( ( fB - sqrt( (double)iY * ( 2.0 * fB - (double)iY ) ) ) * fA ) / fB );
iX1 = nX - iX0;
memset( pData + iY * nPitch + iX0, iColor, iX1 - iX0 );
}
}
void DimgFillMask( int nX, int nY, int nPitch, int iColor, char * pData )
{
int iY;
for ( iY = 0; iY < nY; iY++ )
{
memset( pData + iY * nPitch, iColor, nX );
}
}
/*
*----------------------------------------------------------------------
*
* DimgDispatchCommand --
*
* Tcl command connected with image name
* Valid subcommands are:
* $img configure ?-option value?
*
* Results:
* None.
*
* Side effects:
* The image is deleted.
*
*----------------------------------------------------------------------
*/
int DimgDispatchCommand( ClientData clientData, Tcl_Interp *pInterp, int nObj, Tcl_Obj *CONST aObj[] )
{
static char *aszSubcommands[] = { "configure", "cget", "fill", "oval", (char*)NULL };
int iSubcommand;
DimgMaster *pMaster = (DimgMaster *) clientData;
Tcl_Obj *pResult = Tcl_GetObjResult(pInterp);
int iResult = TCL_OK;
if ( nObj == 1 )
{
Tcl_WrongNumArgs( pInterp, 1, aObj, "subcommand ?-option value?" );
return TCL_ERROR;
}
if ( Tcl_GetIndexFromObj( pInterp, aObj[1], (const char **)aszSubcommands, "subcommand", 0, &iSubcommand ) != TCL_OK )
{
return TCL_ERROR;
}
switch( iSubcommand )
{
case 0: // configure
if ( nObj % 2 != 0 )
{
Tcl_WrongNumArgs( pInterp, 2, aObj, "?-option value?" );
return TCL_ERROR;
}
return DimgConfigureCmd( pInterp, pMaster, nObj-2, aObj+2 );
break;
case 1: // cget
if ( nObj != 3 )
{
Tcl_WrongNumArgs( pInterp, 2, aObj, "-option" );
return TCL_ERROR;
}
return DimgCgetCmd( pInterp, pMaster, nObj-2, aObj+2 );
break;
case 2: case 3:// fill, oval
if ( nObj != 4 )
{
Tcl_WrongNumArgs( pInterp, 2, aObj, "[rgb|mask] value" );
return TCL_ERROR;
}
return DimgFillOrOvalCmd( iSubcommand - 2, pInterp, pMaster, nObj-2, aObj+2 );
}
return iResult;
}
/*
*----------------------------------------------------------------------
*
* DimgDeleteCommand --
*
* This procedure is invoked when the image command for an image
* is deleted. It deletes the image.
*
* Results:
* None.
*
* Side effects:
* The image is deleted.
*
*----------------------------------------------------------------------
*/
void DimgDeleteCommand( ClientData clientData )
{
DimgMaster *pMaster = (DimgMaster *) clientData;
/*
* the process of deleting an image can begin from two points,
* either by deleting the image directly or by deleting the image
* command. however each process must do both. therefore we
* must check if the image master is in the process of being deleted
* before trying to delete it again
*/
if ( pMaster->tkMaster )
{
Tk_DeleteImage( pMaster->pInterp, Tk_NameOfImage( pMaster->tkMaster) );
}
}
int DimgCreateMaster
(
Tcl_Interp *pInterp,
char *szName,
int nObj,
Tcl_Obj *CONST aObj[],
Tk_ImageType *typePtr,
Tk_ImageMaster pTkMaster,
ClientData *pClientData
)
{
DimgMaster *pMaster;
int iResult;
Tcl_Obj *pResult = Tcl_GetObjResult(pInterp);
/*
- Allocate and initialize the Dimg image master record.
*/
pMaster = (DimgMaster *) Tcl_Alloc(sizeof(DimgMaster));
if( pMaster == NULL )
{
Tcl_AppendStringsToObj( pResult, "create dimg ", szName, ": Out of memory\n", NULL );
return TCL_ERROR;
}
strcpy( pMaster->szName, szName );
pMaster->pInstance = NULL;
pMaster->tkMaster = pTkMaster;
pMaster->pInterp = pInterp;
pMaster->nInstance = 0;
pMaster->nWidth = 100;
pMaster->nHeight = 100;
pMaster->nMaskPitch = 100;
pMaster->dwMaskMode = 0;
pMaster->dwImageMode = SRCCOPY;
pMaster->bmImage = NULL;
pMaster->bmMask = NULL;
pMaster->pImageData = NULL;
pMaster->pMaskData = NULL;
memcpy( pMaster->aColorTable, g_aDefaultColorTable, sizeof(g_aDefaultColorTable) );
*pClientData = (ClientData) pMaster;
/*
- Process configuration options given in the image create command.
*/
if ( nObj > 1 )
{
iResult = DimgConfigureCmd( pInterp, pMaster, nObj, aObj );
if ( iResult != TCL_OK )
{
return iResult;
}
}
Tcl_CreateObjCommand( pInterp, szName, DimgDispatchCommand, (ClientData) pMaster, DimgDeleteCommand );
Tk_ImageChanged( pTkMaster, 0, 0, pMaster->nWidth, pMaster->nHeight, pMaster->nWidth, pMaster->nHeight );
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* DimgDisplay --
*
* This procedure is invoked to draw a Dimg image.
*
* Results:
* None.
*
* Side effects:
* A portion of the image gets rendered in a pixmap or window.
*
*----------------------------------------------------------------------
*/
void DimgDisplay
(
ClientData clientData,
Display *pDisplay,
Drawable stDrawable,
int iImageX,
int iImageY,
int nWidth,
int nHeight,
int iDrawableX,
int iDrawableY
)
{
HDC hdcDest;
HDC hdcSource;
TkWinDCState stWinDCState;
DimgInstance *pInstance = (DimgInstance*) clientData;
hdcDest = TkWinGetDrawableDC( pDisplay, stDrawable, &stWinDCState);
hdcSource = CreateCompatibleDC( hdcDest );
if ( pInstance->pMaster->dwMaskMode )
{
SelectObject( hdcSource, pInstance->pMaster->bmMask );
SetDIBColorTable( hdcSource, 0, 8, pInstance->pMaster->aColorTable );
BitBlt( hdcDest, iDrawableX, iDrawableY, nWidth, nHeight, hdcSource, iImageX, iImageY, pInstance->pMaster->dwMaskMode );
}
if ( pInstance->pMaster->dwImageMode )
{
SelectObject( hdcSource, pInstance->pMaster->bmImage );
BitBlt( hdcDest, iDrawableX, iDrawableY, nWidth, nHeight, hdcSource, iImageX, iImageY, pInstance->pMaster->dwImageMode );
}
DeleteDC( hdcSource );
TkWinReleaseDrawableDC( stDrawable, hdcDest, &stWinDCState );
}
/*
*----------------------------------------------------------------------
*
* DimgGetInstance --
*
* This procedure is called for each use of a Dimg image in a
* widget.
*
* Results:
* The return value is a token for the instance, which is passed
* back to us in calls to DimgDisplay and DimgDeleteInstance.
*
* Side effects:
* A data structure is set up for the instance (or, an existing
* instance is re-used for the new one).
*
*----------------------------------------------------------------------
*/
ClientData DimgGetInstance( Tk_Window tkwin, ClientData clientData )
{
DimgInstance *pInstance;
DimgMaster *pMaster = (DimgMaster *) clientData;
Tcl_Obj *pResult = Tcl_GetObjResult( pMaster->pInterp );
/* Make a new instance of the image. */
pInstance = (DimgInstance *) Tcl_Alloc( sizeof(DimgInstance) );
if( !pInstance )
{
Tcl_AppendStringsToObj( pResult, "DimgGetInstance: Out of memory\n", NULL) ;
return NULL;
}
pInstance->tkwin = tkwin;
pInstance->pMaster = pMaster;
pInstance->pNext = pMaster->pInstance;
pMaster->nInstance++;
pMaster->pInstance = pInstance;
return (ClientData) pInstance;
}
/*
*----------------------------------------------------------------------
*
* DimgFreeInstance --
*
* This procedure is called when a widget ceases to use a
* particular instance of an image.
*
* Results:
* None.
*
* Side effects:
* Internal data structures get cleaned up.
*
*----------------------------------------------------------------------
*/
void DimgFreeInstance( ClientData clientData, Display * display )
{
DimgInstance *pInstance = (DimgInstance*) clientData;
DimgInstance *pPrevInstance;
pInstance->pMaster->nInstance--;
if ( pInstance->pMaster->pInstance == pInstance )
{
pInstance->pMaster->pInstance = pInstance->pNext;
}
else
{
pPrevInstance = pInstance->pMaster->pInstance;
while ( ( pPrevInstance->pNext != pInstance ) && pPrevInstance->pNext )
{
pPrevInstance = pPrevInstance->pNext;
}
if ( pPrevInstance->pNext )
{
pPrevInstance->pNext = pInstance->pNext;
}
else
{
//oops instance not found
}
}
memset( pInstance, 0, sizeof(DimgInstance) );
Tcl_Free( (char*) pInstance );
return;
}
/*
*----------------------------------------------------------------------
*
* DimgDeleteMaster --
*
* This procedure is called by the image code to delete the
* master structure for an image.
*
* Results:
* None.
*
* Side effects:
* Resources associated with the image get freed.
*
*----------------------------------------------------------------------
*/
void DimgDeleteMaster( ClientData clientData )
{
const char *szName;
DimgMaster *pMaster = (DimgMaster *) clientData;
if ( pMaster->tkMaster )
{
szName = Tk_NameOfImage( pMaster->tkMaster );
pMaster->tkMaster = NULL; // flag image delete in progress
Tcl_DeleteCommand( pMaster->pInterp, szName );
}
if ( pMaster->bmImage )
{
DeleteObject( pMaster->bmImage );
pMaster->bmImage = NULL;
}
if ( pMaster->bmMask )
{
DeleteObject( pMaster->bmMask );
pMaster->bmMask = NULL;
}
Tcl_Free( (char *) pMaster );
return;
}
void vRegisterDimg()
{
g_stDimgDesc.name = "dimg";
g_stDimgDesc.createProc = DimgCreateMaster;
g_stDimgDesc.getProc = DimgGetInstance;
g_stDimgDesc.displayProc = DimgDisplay;
g_stDimgDesc.freeProc = DimgFreeInstance;
g_stDimgDesc.deleteProc = DimgDeleteMaster;
Tk_CreateImageType( &g_stDimgDesc );
}