Hello,
the Minolta "DiMAGE Image Viewer" (Minolta Dimage digicams use a wider gamut colorspace and to transform that to sRGB you need this tool) makes strange ASSumptions about Global{Alloc,ReAlloc,Size} in it's jpeg exporter (JPEG.GEX, a dll de facto) thus corrupting the output jpg if it's bigger then 1 MB. Deducing from the +relay,+global,+heap debug output it does basicaly this (in pseudo C code):
LPVOID ptr; hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 0); still_needed_space = size_of_output_jpeg; while (still_needed_space > 0) { old_size = GlobalSize(hGlobal); want = (still_needed_space / 0x100000) ? 0x100000 : still_needed_space; still_needed_space -= want; GlobalReAlloc(hGlobal, old_size + want, GMEM_ZEROINIT); ptr = GlobalLock(hGlobal) + old_size; GlobalUnlock(hGlobal) Do_the_work using ptr } WriteFile(output_jpg_h, GlobalLock(hGlobal), size_of_output_jpeg, ...)
The problem is that msdn states for GlobalAlloc (i implicitly assume that it applies for GlobalReAlloc too) that the memory is alligned on an 8byte boundary and that it allocates "at least" the requested number of bytes. That means more memory can be allocate and even msdn recommends to check the actual allocated size with GlobalSize. With the above code wine allocates 0x100004 bytes for the first call to GlobalReAlloc thus inserting four 0 bytes at position 0x100000 and cutting 4 bytes from the end of the jpeg. And this leads to corrupted jpeg's. Here are some snippets from the debug log to demonstrate it: 082c5d08:Call kernel32.GlobalAlloc(00000042,00000000) ret=46bd004a trace:global:GlobalAlloc () flags=0042 trace:heap:RtlAllocateHeap (0x40240000,00000002,00000080): returning 402baec0 08076498:Ret ntdll.RtlAllocateHeap() retval=402baec0 ret=40731b96 08076498:Call ntdll.RtlFreeHeap(40240000,00000000,402baec0) ret=40731b69 trace:heap:RtlAllocateHeap (0x40240000,00000002,00000018): returning 402b64f8 082c5d08:Ret kernel32.GlobalAlloc() retval=402b64fa ret=46bd004a [snip] 082c5d08:Call kernel32.GlobalSize(402b64fa) ret=46bce386 082c5d08:Ret kernel32.GlobalSize() retval=00000000 ret=46bce386 082c5d08:Call kernel32.GlobalReAlloc(402b64fa,00100000,00000040) ret=46bce39b trace:heap:HEAP_FindFreeBlock created new sub-heap 475c0000 of 00100040 bytes for heap 40240000 trace:heap:RtlAllocateHeap (0x40240000,0000000a,00100008): returning 475c0020 082c5d08:Ret kernel32.GlobalReAlloc() retval=402b64fa ret=46bce39b 082c5d08:Call kernel32.GlobalLock(402b64fa) ret=46bce3bd 082c5d08:Ret kernel32.GlobalLock() retval=475c0024 ret=46bce3bd 082c5d08:Call kernel32.GlobalUnlock(402b64fa) ret=46bce3d3 082c5d08:Ret kernel32.GlobalUnlock() retval=00000000 ret=46bce3d3 [snip] 082c5d08:Call kernel32.GlobalSize(402b64fa) ret=46bce41d trace:heap:RtlSizeHeap (0x40240000,00000002,475c0020): returning 00100008 082c5d08:Ret kernel32.GlobalSize() retval=00100004 ret=46bce41d
The first GlobalReAlloc adds 4 (sizeof(HGLOBAL)) to the size (0x100000) and calls RtlAllocateHeap where ROUND_SIZE() increases the size to 0x100008.
What makes me wonder is the different behaviour of Global[Re]Alloc regarding the allocated size when it's called without GMEM_MOVEABLE: with whithout GMEM_MOVEABLE GlobalAlloc(size=0) 0 0x18 GlobalAlloc(size=1MB) 0x100004 0x100000 GlobalReAlloc(size=1MB) 0x100004 0x100000 To demonstrate this i did a small patch to dlls/kernel/tests/alloc.c (Still need to install mingw to test it on Windows).
Do we want to mimic the GlobalAlloc/GlobalReAlloc behaviour of Windows (the DiMage Viewer dosn't corrupt the images at least on Win98 and it's the same binary for Win2000 too)? Could be that some other broken, real life programs are dependent of that behaviour). At a quick glance i don't know how to fix GlobalAlloc/GlobalReAlloc without breaking it. I'll do a bugzilla report but want to save me the work if it will get anyway a WONTFIX resolution.
The version of DiMage Viewer tested is 1.1 and it's not free. Minolta offers only a free upgrade from 2.0 -> 2.1 so I can't test it on a newer version (but i suspect it will still have the bug because the jpeg lib is bought from somebody else and has copyright 1999 whereas the rest is copyright 2001).
bye michael
P.S.: Not fixing this "bug" wont keep the linux users from enjoying their Minolta DiMage cams without windows because there is a Gimp plugin that can do the color space transformations (and Murphy sei Dank i found this AFTER debugging and finding the problem).
On Sat, Jan 04, 2003 at 02:54:12AM +0100, Michael Stefaniuc wrote:
Do we want to mimic the GlobalAlloc/GlobalReAlloc behaviour of Windows (the DiMage Viewer dosn't corrupt the images at least on Win98 and it's the same binary for Win2000 too)? Could be that some other broken, real life programs are dependent of that behaviour). At a quick glance i don't know how to fix GlobalAlloc/GlobalReAlloc without breaking it. I'll do a bugzilla report but want to save me the work if it will get anyway a WONTFIX resolution.
Well, it wasn't so hard to fix as I first thought. And the patch isn't too intrusive, it aligns only the storage needed for the HGLOBAL on an 8byte boundary. The only disatvantage is we waste 1 byte per memory region allocated with GMEM_MOVEABLE but this should be realy tolerable. Alexandre, i could replace the definition of HGLOBAL_STORAGE with something like: #define HGLOBAL_STORAGE ROUND_SIZE(sizeof(HGOBAL)) and use the ROUND_SIZE macro from dlls/ntdll/heap.c . The two magics "-2" in the patch could be also replaced by HGLOBAL_STORAGE/sizeof(HGOBAL)
License: LGPL, X11 Changelog: Michael Stefaniuc mstefani@redhat.com - the Minolta DiMAGE Image Viewer relies on Global{,Re}Alloc called with the GMEM_MOVEABLE flag set, to allocate the exact specified size and no byte more when size = 8*k, where k=1,2,3,4.... . To achieve this allign the storage needed for the HGLOBAL in the heap to 8byte boundary.
bye michael
P.S.: I'll redo the dlls/kernel/tests/alloc.c to have a test case