Quote: "dbGetImageData gives you a pointer to a string? That's peculiar."
Not really, it's just a pointer to a byte array, which can hold any data. It would only cause problems if you tried to use it with any string-expecting functions
Anyway, it turns out that the problem lies in the dbGetImageData function allocating the memory on its own heap (since it is called from a dll (using DGDK 2)) and thus cannot be free'd from the "local" heap.
Since the DBProImageDebug dll doesn't provide any method for either giving it a pre-allocated memory pointer that it will just fill, or a function for freeing memory allocated by the dll, there seems to be no way to overcome this type of issue.
For this particular function a work-around can be created using another method from the dll though and basically re-implementing the dbGetImageData function so that it resides on your own heap:
/**
* This function returns a pointer to a LPDIRECT3DTEXTURE.
* These appear to exist (permanently) for all images (?) and can thus be used to read Image data without any
* memory leaks due to unfree-able pointers.
**/
typedef LPDIRECT3DTEXTURE9 (__cdecl *ProtodbGetImagePointer_1)(int32_t imageID);
LPDIRECT3DTEXTURE9 dbGetImagePointer(int32_t imageID) {
static ProtodbGetImagePointer_1 Ptr;
if (!Ptr && !GDKLoadPtr(&Ptr,"DBProImageDebug.dll", "?GetPointer@@YAPAUIDirect3DTexture9@@H@Z"))
return 0;
LPDIRECT3DTEXTURE9 pTexture(NULL);
if (GDKWaitFunction()) {
if (imageID <= 0)
return NULL; // Invalid image ID
pTexture = Ptr(imageID);
GDKCleanupFunctionCall();
}
return pTexture;
}
/**
* Apparently this allocates the pData array from within the DLL => it exists on a different heap than the "local" program
* code runs on. Therefore we can't free the memory from this heap. Since there is no built-in functionality (seemingly) to
* free such memory from the DLL (which supposedly is the only way to do it) we'll have to write our own work-around using other
* functions from the given DLL (that doesn't dynamically allocate memory => we won't have to free it).
**/
void dbGetImageData(int32_t id, DWORD *width, DWORD *height, DWORD *depth, LPSTR *pData, DWORD *size, bool lockPixels) {
LPDIRECT3DTEXTURE9 lpTexture = dbGetImagePointer(id);
if(!lpTexture) {
*pData = NULL;
*size = 0;
return; // Unsure whether this might ever happen
}
D3DSURFACE_DESC desc;
lpTexture->GetLevelDesc(0, &desc);
*width = desc.Width;
*height = desc.Height;
*depth = 32; // Images seem to «always» be 32-bit in DBPro. This *MIGHT* change to 16 if you run in a 16-bit display mode
// however, I'm not sure about that.
*size = (*width) * (*height) * 4; // Again, assuming 32-bit depth
// Lock Pixels
D3DLOCKED_RECT d3dLock;
RECT rc = {0, 0, *width, *height};
if(SUCCEEDED(lpTexture->LockRect(0, &d3dLock, &rc, 0))) {
// Allocate memory to copy the pixel data to
*pData = new char[*size];
// Copy pixel data
// TODO: This can probably be improved by just copying all pixels at once (since that is what this function is intended to do
// rather than just a specific rect of the source image as is handled by the below)
LPSTR pSrc = (LPSTR)d3dLock.pBits,
pPtr = *pData;
DWORD dwDataWidth = (*width) * 4;
for(DWORD y = 0; y < *height; y++) {
memcpy(pPtr, pSrc, dwDataWidth);
pPtr += dwDataWidth;
pSrc += d3dLock.Pitch;
}
// Unlock the pixel data
lpTexture->UnlockRect(0);
// Let the function finish normally; as the returned data is sent through reference parameters we don't need to return
// anything specific here where the function should have completed successfully.
} else {
*size = 1; // Apparently setting the size to 1 signals that the resource exists but cannot be locked.
// One can wonder why 0 wasn't used instead (there doesn't seem to be any other error code for this function).
}
}
Note that this workaround is only required with DarkGDK 2, in the original edition this function works out of the box.
"Why do programmers get Halloween and Christmas mixed up?" Because Oct(31) = Dec(25)