Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Bug in Image::Save() -- doesn't truncate destination file if it already exists

1 view
Skip to first unread message

Mike Morearty

unread,
Dec 6, 2001, 2:55:27 PM12/6/01
to
Under Windows XP Professional, Image::Save(), when saving to a file,
does not truncate the destination file if it already exists. For
example, if you write to FOO.JPG, and FOO.JPG already exists as a 200K
file, then the new FOO.JPG will be at least 200K, even if the image
you're writing out is really tiny.

To see this, compile the following program. Then run it from the
command line as "makepng anyfile", where "anyfile" is any image type
(bmp, jpeg, gif). A new "anyfile.png" will be created. Take a look
at its size.

Now, try this again, but first make sure that "anyfile.png" already
exists, as a BIGGER file (find some big file on your hard disk and
copy it to anyfile.png).

Now you'll see that "anyfile.png" is the same size as whatever larger
file you had copied to that filename! Eeek.

The workaround, of course, is to delete or truncate the destination
file before calling Image::Save().

Here's the sample, makepng.cpp (patched together mostly from samples
in the GDI+ documentation):


// cl -YX -MDd -ZI -GX makepng.cpp gdiplus.lib

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#include <string>
using namespace Gdiplus;
using namespace std;

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes

ImageCodecInfo* pImageCodecInfo = NULL;

GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure

pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure

GetImageEncoders(num, size, pImageCodecInfo);

for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}

free(pImageCodecInfo);
return -1; // Failure
}

wstring change_extension(const wchar_t* filename, const wchar_t* ext)
{
wstring newname(filename);

wstring::size_type slash = newname.find_last_of(L"/\\");
wstring::size_type dot = newname.find_last_of(L'.');
if (dot == wstring::npos || (slash != wstring::npos && dot <
slash))
newname += L".png";
else
newname.replace(dot, newname.length() - dot, L".png");
return newname;
}

INT wmain(int argc, wchar_t* argv[])
{
if (argc != 2)
{
printf("Usage: makepng filename.bmp\n");
return 1;
}

// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

CLSID encoderClsid;
Status stat;
Image* image = new Image(argv[1]);

// Get the CLSID of the PNG encoder.
GetEncoderClsid(L"image/png", &encoderClsid);

// Make a new filename which ends with .png
wstring newname = change_extension(argv[1], L".png");

// Save the file as a PNG
stat = image->Save(newname.c_str(), &encoderClsid, NULL);

if(stat == Ok)
printf("%S was saved successfully\n", newname.c_str());
else
printf("Failure: stat = %d\n", stat);

delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}

James DeBroeck

unread,
Dec 6, 2001, 5:09:59 PM12/6/01
to
Hi Mike,

Yes, this is an issue that we have recently discovered.

Your suggested work-around of deleting the file before calling the Save
method is also the best way that we have concluded to resolve this.

This happens because the code behind Save() that sets up a file to be
written to by the filters does not include a flag to truncate the file. The
filters of course do not make any presumptions about truncating the
destination that they are writting into. The end result is an overwrite of
the file contents up to the size of the new "file" but not a truncation of
any existing file bits.

It is really weird to open a postage stamp sized JPG image that has a file
size of multi-megabytes. It makes one question one's perspective on reality.

Thank you for keeping us aware of the things that you find in GdiPlus.

Sincerely,
James
Microsoft Windows Developer Support


The fine but small print:

This posting is provided "AS IS" with no warranties, and confers no rights.

0 new messages