Hopefully I've now convinced Alexandre to commit this, so here is it before he changes his mind <g> Huw Davies <huw@xxxxxxxxxxxxxxx> Lets enhmetafiles reuse gdi handles. This works by a dc 'registering' with an object that it wants to know when the object is deleted. Until the dc gets delete notification it's free to assume that it doesn't need to write the create record for that object again. Upon receiving the delete notification it writes the DeleteObject record. Windows seems to do it this way too. Index: include/gdi.h =================================================================== RCS file: /home/wine/wine/include/gdi.h,v retrieving revision 1.86 diff -u -r1.86 gdi.h --- include/gdi.h 5 Nov 2003 01:43:57 -0000 1.86 +++ include/gdi.h 24 Nov 2003 23:35:52 -0000 @@ -64,12 +64,19 @@ BOOL (*pDeleteObject)( HGDIOBJ handle, void *obj ); }; +struct hdc_list +{ + HDC hdc; + struct hdc_list *next; +}; + typedef struct tagGDIOBJHDR { HANDLE16 hNext; WORD wMagic; DWORD dwCount; const struct gdi_obj_funcs *funcs; + struct hdc_list *hdcs; } GDIOBJHDR; @@ -189,6 +196,7 @@ HBITMAP (*pCreateDIBSection)(PHYSDEV,BITMAPINFO *,UINT,LPVOID *,HANDLE,DWORD,DWORD); BOOL (*pDeleteBitmap)(HBITMAP); BOOL (*pDeleteDC)(PHYSDEV); + BOOL (*pDeleteObject)(PHYSDEV,HGDIOBJ); INT (*pDescribePixelFormat)(PHYSDEV,INT,UINT,PIXELFORMATDESCRIPTOR *); DWORD (*pDeviceCapabilities)(LPSTR,LPCSTR,LPCSTR,WORD,LPSTR,LPDEVMODEA); BOOL (*pEllipse)(PHYSDEV,INT,INT,INT,INT); @@ -470,6 +478,9 @@ extern DC * DC_GetDCUpdate( HDC hdc ); extern void DC_InitDC( DC * dc ); extern void DC_UpdateXforms( DC * dc ); + +BOOL GDI_hdc_using_object(HGDIOBJ obj, HDC hdc); +BOOL GDI_hdc_not_using_object(HGDIOBJ obj, HDC hdc); /* bidi.c */ Index: objects/gdiobj.c =================================================================== RCS file: /home/wine/wine/objects/gdiobj.c,v retrieving revision 1.91 diff -u -r1.91 gdiobj.c --- objects/gdiobj.c 5 Nov 2003 01:43:57 -0000 1.91 +++ objects/gdiobj.c 24 Nov 2003 23:35:53 -0000 @@ -717,6 +717,7 @@ obj->wMagic = magic|OBJECT_NOSYSTEM; obj->dwCount = 0; obj->funcs = funcs; + obj->hdcs = NULL; TRACE_SEC( *handle, "enter" ); return obj; @@ -890,6 +891,24 @@ return TRUE; } + + while (header->hdcs) + { + DC *dc = DC_GetDCPtr(header->hdcs->hdc); + struct hdc_list *tmp; + + TRACE("hdc %p has interest in %p\n", header->hdcs->hdc, obj); + if(dc) + { + if(dc->funcs->pDeleteObject) + dc->funcs->pDeleteObject( dc->physDev, obj ); + GDI_ReleaseObj( header->hdcs->hdc ); + } + tmp = header->hdcs; + header->hdcs = header->hdcs->next; + HeapFree(GetProcessHeap(), 0, tmp); + } + if (header->dwCount) { TRACE("delayed for %p because object in use, count %ld\n", obj, header->dwCount ); @@ -909,6 +928,78 @@ return FALSE; } +/*********************************************************************** + * GDI_hdc_using_object + * + * Call this if the dc requires DeleteObject notification + */ +BOOL GDI_hdc_using_object(HGDIOBJ obj, HDC hdc) +{ + GDIOBJHDR * header; + struct hdc_list **pphdc; + + TRACE("obj %p hdc %p\n", obj, hdc); + + if (!(header = GDI_GetObjPtr( obj, MAGIC_DONTCARE ))) return FALSE; + + if (!(header->wMagic & OBJECT_NOSYSTEM) && + (header->wMagic >= FIRST_MAGIC) && (header->wMagic <= LAST_MAGIC)) + { + GDI_ReleaseObj(obj); + return FALSE; + } + + for(pphdc = &header->hdcs; *pphdc; pphdc = &(*pphdc)->next) + if((*pphdc)->hdc == hdc) + break; + + if(!*pphdc) { + *pphdc = HeapAlloc(GetProcessHeap(), 0, sizeof(**pphdc)); + (*pphdc)->hdc = hdc; + (*pphdc)->next = NULL; + } + + GDI_ReleaseObj(obj); + return TRUE; +} + +/*********************************************************************** + * GDI_hdc_not_using_object + * + */ +BOOL GDI_hdc_not_using_object(HGDIOBJ obj, HDC hdc) +{ + GDIOBJHDR * header; + struct hdc_list *phdc, **prev; + + TRACE("obj %p hdc %p\n", obj, hdc); + + if (!(header = GDI_GetObjPtr( obj, MAGIC_DONTCARE ))) return FALSE; + + if (!(header->wMagic & OBJECT_NOSYSTEM) && + (header->wMagic >= FIRST_MAGIC) && (header->wMagic <= LAST_MAGIC)) + { + GDI_ReleaseObj(obj); + return FALSE; + } + + phdc = header->hdcs; + prev = &header->hdcs; + + while(phdc) { + if(phdc->hdc == hdc) { + *prev = phdc->next; + HeapFree(GetProcessHeap(), 0, phdc); + phdc = *prev; + } else { + prev = &phdc->next; + phdc = phdc->next; + } + } + + GDI_ReleaseObj(obj); + return TRUE; +} /*********************************************************************** * GetStockObject (GDI32.@) Index: dlls/gdi/enhmfdrv/enhmetafiledrv.h =================================================================== RCS file: /home/wine/wine/dlls/gdi/enhmfdrv/enhmetafiledrv.h,v retrieving revision 1.14 diff -u -r1.14 enhmetafiledrv.h --- dlls/gdi/enhmfdrv/enhmetafiledrv.h 10 Oct 2003 00:06:59 -0000 1.14 +++ dlls/gdi/enhmfdrv/enhmetafiledrv.h 24 Nov 2003 23:35:53 -0000 @@ -35,7 +35,8 @@ HDC hdc; DC *dc; ENHMETAHEADER *emh; /* Pointer to enhanced metafile header */ - UINT nextHandle; /* Next handle number */ + UINT handles_size, cur_handles; + HGDIOBJ *handles; HANDLE hFile; /* Handle for disk based MetaFile */ INT horzres, vertres; INT horzsize, vertsize; @@ -49,10 +50,11 @@ extern BOOL EMFDRV_WriteRecord( PHYSDEV dev, EMR *emr ); -extern int EMFDRV_AddHandleDC( PHYSDEV dev ); extern void EMFDRV_UpdateBBox( PHYSDEV dev, RECTL *rect ); extern DWORD EMFDRV_CreateBrushIndirect( PHYSDEV dev, HBRUSH hBrush ); +#define HANDLE_LIST_INC 20 + /* Metafile driver functions */ extern BOOL EMFDRV_AbortPath( PHYSDEV dev ); extern BOOL EMFDRV_Arc( PHYSDEV dev, INT left, INT top, INT right, @@ -66,6 +68,7 @@ INT bottom, INT xstart, INT ystart, INT xend, INT yend ); extern BOOL EMFDRV_CloseFigure( PHYSDEV dev ); +extern BOOL EMFDRV_DeleteObject( PHYSDEV dev, HGDIOBJ obj ); extern BOOL EMFDRV_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom ); extern BOOL EMFDRV_EndPath( PHYSDEV dev ); Index: dlls/gdi/enhmfdrv/init.c =================================================================== RCS file: /home/wine/wine/dlls/gdi/enhmfdrv/init.c,v retrieving revision 1.27 diff -u -r1.27 init.c --- dlls/gdi/enhmfdrv/init.c 5 Nov 2003 01:43:57 -0000 1.27 +++ dlls/gdi/enhmfdrv/init.c 24 Nov 2003 23:35:53 -0000 @@ -47,6 +47,7 @@ NULL, /* pCreateDIBSection */ NULL, /* pDeleteBitmap */ NULL, /* pDeleteDC */ + EMFDRV_DeleteObject, /* pDeleteObject */ NULL, /* pDescribePixelFormat */ NULL, /* pDeviceCapabilities */ EMFDRV_Ellipse, /* pEllipse */ @@ -159,8 +160,13 @@ { EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev; DC *dc = physDev->dc; + UINT index; if (physDev->emh) HeapFree( GetProcessHeap(), 0, physDev->emh ); + for(index = 0; index < physDev->handles_size; index++) + if(physDev->handles[index]) + GDI_hdc_not_using_object(physDev->handles[index], physDev->hdc); + HeapFree( GetProcessHeap(), 0, physDev->handles ); HeapFree( GetProcessHeap(), 0, physDev ); dc->physDev = NULL; GDI_FreeObject( dc->hSelf, dc ); @@ -224,21 +230,6 @@ return; } -/****************************************************************** - * EMFDRV_AddHandleDC - * - * Note: this function assumes that we never delete objects. - * If we do someday, we'll need to maintain a table to re-use deleted - * handles. - */ -int EMFDRV_AddHandleDC( PHYSDEV dev ) -{ - EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev; - physDev->emh->nHandles++; - return physDev->nextHandle++; -} - - /********************************************************************** * CreateEnhMetaFileA (GDI32.@) */ @@ -324,7 +315,9 @@ return 0; } - physDev->nextHandle = 1; + physDev->handles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, HANDLE_LIST_INC * sizeof(physDev->handles[0])); + physDev->handles_size = HANDLE_LIST_INC; + physDev->cur_handles = 1; physDev->hFile = 0; physDev->horzres = GetDeviceCaps(hRefDC, HORZRES); Index: dlls/gdi/enhmfdrv/objects.c =================================================================== RCS file: /home/wine/wine/dlls/gdi/enhmfdrv/objects.c,v retrieving revision 1.7 diff -u -r1.7 objects.c --- dlls/gdi/enhmfdrv/objects.c 21 May 2003 18:28:49 -0000 1.7 +++ dlls/gdi/enhmfdrv/objects.c 24 Nov 2003 23:35:53 -0000 @@ -28,6 +28,74 @@ WINE_DEFAULT_DEBUG_CHANNEL(enhmetafile); + +/****************************************************************** + * EMFDRV_AddHandle + */ +static UINT EMFDRV_AddHandle( PHYSDEV dev, HGDIOBJ obj ) +{ + EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev; + UINT index; + + for(index = 0; index < physDev->handles_size; index++) + if(physDev->handles[index] == 0) break; + if(index == physDev->handles_size) { + physDev->handles_size += HANDLE_LIST_INC; + physDev->handles = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + physDev->handles, + physDev->handles_size * sizeof(physDev->handles[0])); + } + physDev->handles[index] = obj; + + physDev->cur_handles++; + if(physDev->cur_handles > physDev->emh->nHandles) + physDev->emh->nHandles++; + + return index + 1; /* index 0 is reserved for the hmf, so we increment everything by 1 */ +} + +/****************************************************************** + * EMFDRV_FindObject + */ +static UINT EMFDRV_FindObject( PHYSDEV dev, HGDIOBJ obj ) +{ + EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE*) dev; + UINT index; + + for(index = 0; index < physDev->handles_size; index++) + if(physDev->handles[index] == obj) break; + + if(index == physDev->handles_size) return 0; + + return index + 1; +} + + +/****************************************************************** + * EMFDRV_DeleteObject + */ +BOOL EMFDRV_DeleteObject( PHYSDEV dev, HGDIOBJ obj ) +{ + EMRDELETEOBJECT emr; + EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE*) dev; + UINT index; + BOOL ret = TRUE; + + if(!(index = EMFDRV_FindObject(dev, obj))) return 0; + + emr.emr.iType = EMR_DELETEOBJECT; + emr.emr.nSize = sizeof(emr); + emr.ihObject = index; + + if(!EMFDRV_WriteRecord( dev, &emr.emr )) + ret = FALSE; + + physDev->handles[index - 1] = 0; + physDev->cur_handles--; + return ret; +} + + /*********************************************************************** * EMFDRV_SelectBitmap */ @@ -55,7 +123,7 @@ EMRCREATEBRUSHINDIRECT emr; emr.emr.iType = EMR_CREATEBRUSHINDIRECT; emr.emr.nSize = sizeof(emr); - emr.ihBrush = index = EMFDRV_AddHandleDC( dev ); + emr.ihBrush = index = EMFDRV_AddHandle( dev, hBrush ); emr.lb = logbrush; if(!EMFDRV_WriteRecord( dev, &emr.emr )) @@ -80,7 +148,7 @@ if(!emr) break; emr->emr.iType = EMR_CREATEDIBPATTERNBRUSHPT; emr->emr.nSize = size; - emr->ihBrush = index = EMFDRV_AddHandleDC( dev ); + emr->ihBrush = index = EMFDRV_AddHandle( dev, hBrush ); emr->iUsage = LOWORD(logbrush.lbColor); emr->offBmi = sizeof(EMRCREATEDIBPATTERNBRUSHPT); emr->cbBmi = biSize; @@ -113,6 +181,7 @@ */ HBRUSH EMFDRV_SelectBrush(PHYSDEV dev, HBRUSH hBrush ) { + EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE*)dev; EMRSELECTOBJECT emr; DWORD index; int i; @@ -130,7 +199,11 @@ goto found; } } + if((index = EMFDRV_FindObject(dev, hBrush)) != 0) + goto found; + if (!(index = EMFDRV_CreateBrushIndirect(dev, hBrush ))) return 0; + GDI_hdc_using_object(hBrush, physDev->hdc); found: emr.emr.iType = EMR_SELECTOBJECT; @@ -153,7 +226,7 @@ emr.emr.iType = EMR_EXTCREATEFONTINDIRECTW; emr.emr.nSize = (sizeof(emr) + 3) / 4 * 4; - emr.ihFont = index = EMFDRV_AddHandleDC( dev ); + emr.ihFont = index = EMFDRV_AddHandle( dev, hFont ); emr.elfw.elfFullName[0] = '\0'; emr.elfw.elfStyle[0] = '\0'; emr.elfw.elfVersion = 0; @@ -185,6 +258,7 @@ */ HFONT EMFDRV_SelectFont( PHYSDEV dev, HFONT hFont ) { + EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE*)dev; EMRSELECTOBJECT emr; DWORD index; int i; @@ -203,7 +277,13 @@ goto found; } } + + if((index = EMFDRV_FindObject(dev, hFont)) != 0) + goto found; + if (!(index = EMFDRV_CreateFontIndirect(dev, hFont ))) return HGDI_ERROR; + GDI_hdc_using_object(hFont, physDev->hdc); + found: emr.emr.iType = EMR_SELECTOBJECT; emr.emr.nSize = sizeof(emr); @@ -227,7 +307,7 @@ emr.emr.iType = EMR_CREATEPEN; emr.emr.nSize = sizeof(emr); - emr.ihPen = index = EMFDRV_AddHandleDC( dev ); + emr.ihPen = index = EMFDRV_AddHandle( dev, hPen ); if(!EMFDRV_WriteRecord( dev, &emr.emr )) index = 0; @@ -239,6 +319,7 @@ */ HPEN EMFDRV_SelectPen(PHYSDEV dev, HPEN hPen ) { + EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE*)dev; EMRSELECTOBJECT emr; DWORD index; int i; @@ -257,7 +338,12 @@ goto found; } } + if((index = EMFDRV_FindObject(dev, hPen)) != 0) + goto found; + if (!(index = (DWORD)EMFDRV_CreatePenIndirect(dev, hPen ))) return 0; + GDI_hdc_using_object(hPen, physDev->hdc); + found: emr.emr.iType = EMR_SELECTOBJECT; emr.emr.nSize = sizeof(emr); Index: dlls/gdi/mfdrv/init.c =================================================================== RCS file: /home/wine/wine/dlls/gdi/mfdrv/init.c,v retrieving revision 1.26 diff -u -r1.26 init.c --- dlls/gdi/mfdrv/init.c 5 Nov 2003 01:43:57 -0000 1.26 +++ dlls/gdi/mfdrv/init.c 24 Nov 2003 23:35:53 -0000 @@ -48,6 +48,7 @@ NULL, /* pCreateDIBSection */ NULL, /* pDeleteBitmap */ NULL, /* pDeleteDC */ + NULL, /* pDeleteObject */ NULL, /* pDescribePixelFormat */ NULL, /* pDeviceCapabilities */ MFDRV_Ellipse, /* pEllipse */