/* See 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 specifc 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 ); }