added new platform-independent BMP writing
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_2_BRANCH@7580 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -31,12 +31,9 @@ public:
|
||||
m_mime = _T("image/bmp");
|
||||
};
|
||||
|
||||
// saving bitmaps is Windows-only
|
||||
#ifdef __WIN32__
|
||||
virtual bool SaveFile( wxImage *image, wxOutputStream& stream, bool verbose=TRUE );
|
||||
#endif // __WIN32__
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
virtual bool SaveFile( wxImage *image, wxOutputStream& stream, bool verbose=TRUE );
|
||||
|
||||
virtual bool LoadFile( wxImage *image, wxInputStream& stream, bool verbose=TRUE, int index=0 );
|
||||
virtual bool DoCanRead( wxInputStream& stream );
|
||||
#endif // wxUSE_STREAMS
|
||||
|
@@ -47,173 +47,121 @@
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler)
|
||||
|
||||
#ifdef __WIN32__
|
||||
|
||||
#include <wx/msw/private.h>
|
||||
#if wxUSE_STREAMS
|
||||
|
||||
static void OutOfMemory(size_t nBytesNeeded)
|
||||
{
|
||||
wxLogError(_("Failed to allocate %u bytes of memory."), nBytesNeeded);
|
||||
}
|
||||
|
||||
// VZ: I'm not an expert in bitmaps, this is a quick and dirty C function I
|
||||
// wrote some time ago and it probably doesn't deal properly with all
|
||||
// bitmaps - any corrections/improvements are more than welcome.
|
||||
bool wxBMPHandler::SaveFile(wxImage *image,
|
||||
wxOutputStream& stream,
|
||||
bool verbose)
|
||||
{
|
||||
// get HBITMAP first
|
||||
wxCHECK_MSG( image, FALSE, _T("invalid pointer in wxBMPHandler::SaveFile") );
|
||||
|
||||
wxBitmap bitmap = image->ConvertToBitmap();
|
||||
HBITMAP hBmp = GetHbitmapOf(bitmap);
|
||||
wxCHECK_MSG( hBmp, FALSE, _T("can't save invalid bitmap") );
|
||||
|
||||
// actual C code starts here
|
||||
BITMAPFILEHEADER hdr;
|
||||
BITMAPINFOHEADER *pbih;
|
||||
BITMAP bmp;
|
||||
WORD nClrBits;
|
||||
size_t sizeOfBmi, sizeColTable;
|
||||
BITMAPINFO *pbmi = NULL;
|
||||
void *bmpData = NULL;
|
||||
HDC hdc = NULL;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
create and initialize BITMAPINFO
|
||||
*/
|
||||
|
||||
/* get the number of bitmap colours and its size */
|
||||
if ( !GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp) ) {
|
||||
wxLogLastError(_T("GetObject"));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* calc the number of colour bits needed */
|
||||
nClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
|
||||
if ( nClrBits == 1 ) nClrBits = 1;
|
||||
else if ( nClrBits <= 4 ) nClrBits = 4;
|
||||
else if ( nClrBits <= 8 ) nClrBits = 8;
|
||||
else if ( nClrBits <= 16 ) nClrBits = 16;
|
||||
else if ( nClrBits <= 24 ) nClrBits = 24;
|
||||
else nClrBits = 32;
|
||||
|
||||
/* alloc memory knowing that all bitmaps except 24bpp need extra RGBQUAD
|
||||
* table */
|
||||
sizeOfBmi = sizeof(BITMAPINFOHEADER);
|
||||
if ( nClrBits != 24 )
|
||||
sizeOfBmi += sizeof(RGBQUAD) * ( 1 << nClrBits);
|
||||
|
||||
pbmi = (BITMAPINFO *)calloc(sizeOfBmi, 1);
|
||||
if ( !pbmi ) {
|
||||
OutOfMemory(sizeOfBmi);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* initialize the fields in the BITMAPINFO structure */
|
||||
pbih = (BITMAPINFOHEADER *)pbmi;
|
||||
pbih->biSize = sizeof(BITMAPINFOHEADER);
|
||||
pbih->biWidth = bmp.bmWidth;
|
||||
pbih->biHeight = bmp.bmHeight;
|
||||
pbih->biPlanes = bmp.bmPlanes;
|
||||
pbih->biBitCount = bmp.bmBitsPixel;
|
||||
if ( nClrBits < 24 )
|
||||
pbih->biClrUsed = 1 << nClrBits;
|
||||
pbih->biCompression = BI_RGB;
|
||||
pbih->biSizeImage = 0;
|
||||
pbih->biClrImportant = 0;
|
||||
|
||||
/* let GetDIBits fill the size field, calc it ourselves if it didn't */
|
||||
hdc = CreateCompatibleDC(NULL);
|
||||
if ( !hdc ) {
|
||||
wxLogLastError(_T("CreateCompatibleDC(NULL)"));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
(void)GetDIBits(hdc, hBmp, 0, pbih->biHeight, NULL, pbmi, DIB_RGB_COLORS);
|
||||
if ( !pbih->biSizeImage )
|
||||
pbih->biSizeImage = (pbih->biWidth + 15) / 16 * pbih->biHeight * nClrBits;
|
||||
|
||||
/*
|
||||
get the data from the bitmap
|
||||
*/
|
||||
bmpData = malloc(pbih->biSizeImage);
|
||||
if ( !bmpData ) {
|
||||
OutOfMemory(pbih->biSizeImage);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ( !GetDIBits(hdc, hBmp,
|
||||
0, pbih->biHeight,
|
||||
bmpData, pbmi, DIB_RGB_COLORS) ) {
|
||||
wxLogLastError(_T("GetDIBits"));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
write the data to the file
|
||||
*/
|
||||
|
||||
/* write the file header */
|
||||
sizeColTable = pbih->biClrUsed * sizeof(RGBQUAD);
|
||||
hdr.bfType = 0x4d42; /* "BM" string */
|
||||
hdr.bfSize = sizeof(BITMAPFILEHEADER) +
|
||||
pbih->biSize + sizeColTable + pbih->biSizeImage;
|
||||
hdr.bfReserved1 = 0;
|
||||
hdr.bfReserved2 = 0;
|
||||
hdr.bfOffBits = hdr.bfSize - pbih->biSizeImage;
|
||||
|
||||
if ( !stream.Write(&hdr, sizeof(hdr)) ) {
|
||||
if ( verbose )
|
||||
wxLogError(_("Couldn't write the BMP file header."));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* write the bitmap header to the file */
|
||||
if ( !stream.Write(pbih, sizeof(BITMAPINFOHEADER) + sizeColTable) ) {
|
||||
if ( verbose )
|
||||
wxLogError(_("Couldn't write the bitmap header."));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* write the data to the file */
|
||||
if ( !stream.Write(bmpData, pbih->biSizeImage) ) {
|
||||
if ( verbose )
|
||||
wxLogError(_("Couldn't write bitmap data."));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* success */
|
||||
rc = 1;
|
||||
|
||||
cleanup:
|
||||
free(bmpData);
|
||||
free(pbmi);
|
||||
if ( hdc )
|
||||
DeleteDC(hdc);
|
||||
|
||||
if ( !rc ) {
|
||||
wxLogError(_("Failed to save the bitmap."));
|
||||
|
||||
if (!image->Ok())
|
||||
{
|
||||
if (verbose) wxLogError(_("BMP: Couldn't save invalid image."));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
unsigned width = image->GetWidth();
|
||||
unsigned row_width = width * 3 +
|
||||
(((width % 4) == 0) ? 0 : (4 - (width * 3) % 4));
|
||||
// each row must be aligned to dwords
|
||||
struct
|
||||
{
|
||||
// BitmapHeader:
|
||||
wxUint16 magic; // format magic, always 'BM'
|
||||
wxUint32 filesize; // total file size, inc. headers
|
||||
wxUint32 reserved; // for future use
|
||||
wxUint32 data_offset; // image data offset in the file
|
||||
|
||||
// BitmapInfoHeader:
|
||||
wxUint32 bih_size; // 2nd part's size
|
||||
wxUint32 width, height; // bitmap's dimensions
|
||||
wxUint16 planes; // num of planes
|
||||
wxUint16 bpp; // bits per pixel
|
||||
wxUint32 compression; // compression method
|
||||
wxUint32 size_of_bmp; // size of the bitmap
|
||||
wxUint32 h_res, v_res; // image resolution in dpi
|
||||
wxUint32 num_clrs; // number of colors used
|
||||
wxUint32 num_signif_clrs;// number of significant colors
|
||||
} hdr;
|
||||
wxUint32 hdr_size = 14/*BitmapHeader*/ + 40/*BitmapInfoHeader*/;
|
||||
|
||||
hdr.magic = wxUINT16_SWAP_ON_BE(0x4D42/*'BM'*/);
|
||||
hdr.filesize = wxUINT32_SWAP_ON_BE(
|
||||
hdr_size +
|
||||
row_width * image->GetHeight()
|
||||
);
|
||||
hdr.reserved = 0;
|
||||
hdr.data_offset = wxUINT32_SWAP_ON_BE(hdr_size);
|
||||
|
||||
hdr.bih_size = wxUINT32_SWAP_ON_BE(hdr_size - 14);
|
||||
hdr.width = wxUINT32_SWAP_ON_BE(image->GetWidth());
|
||||
hdr.height = wxUINT32_SWAP_ON_BE(image->GetHeight());
|
||||
hdr.planes = wxUINT16_SWAP_ON_BE(1); // always 1 plane
|
||||
hdr.bpp = wxUINT16_SWAP_ON_BE(24); // always TrueColor
|
||||
hdr.compression = 0; // RGB uncompressed
|
||||
hdr.size_of_bmp = wxUINT32_SWAP_ON_BE(row_width * image->GetHeight());
|
||||
hdr.h_res = hdr.v_res = wxUINT32_SWAP_ON_BE(72); // 72dpi is standard
|
||||
hdr.num_clrs = 0; // maximal possible = 2^24
|
||||
hdr.num_signif_clrs = 0; // all colors are significant
|
||||
|
||||
if (// VS: looks ugly but compilers tend to do ugly things with structs,
|
||||
// like aligning hdr.filesize's ofset to dword :(
|
||||
!stream.Write(&hdr.magic, 2) ||
|
||||
!stream.Write(&hdr.filesize, 4) ||
|
||||
!stream.Write(&hdr.reserved, 4) ||
|
||||
!stream.Write(&hdr.data_offset, 4) ||
|
||||
!stream.Write(&hdr.bih_size, 4) ||
|
||||
!stream.Write(&hdr.width, 4) ||
|
||||
!stream.Write(&hdr.height, 4) ||
|
||||
!stream.Write(&hdr.planes, 2) ||
|
||||
!stream.Write(&hdr.bpp, 2) ||
|
||||
!stream.Write(&hdr.compression, 4) ||
|
||||
!stream.Write(&hdr.size_of_bmp, 4) ||
|
||||
!stream.Write(&hdr.h_res, 4) ||
|
||||
!stream.Write(&hdr.v_res, 4) ||
|
||||
!stream.Write(&hdr.num_clrs, 4) ||
|
||||
!stream.Write(&hdr.num_signif_clrs, 4)
|
||||
)
|
||||
{
|
||||
if (verbose)
|
||||
wxLogError(_("BMP: Couldn't write the file header."));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wxUint8 *data = (wxUint8*) image->GetData();
|
||||
wxUint8 *buffer = new wxUint8[row_width];
|
||||
wxUint8 tmpvar;
|
||||
memset(buffer, 0, row_width);
|
||||
int y; unsigned x;
|
||||
|
||||
for (y = image->GetHeight() -1 ; y >= 0; y--)
|
||||
{
|
||||
memcpy(buffer, data + y * 3 * width, 3 * width);
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
tmpvar = buffer[3 * x + 0];
|
||||
buffer[3 * x + 0] = buffer[3 * x + 2];
|
||||
buffer[3 * x + 2] = tmpvar;
|
||||
}
|
||||
|
||||
if (!stream.Write(buffer, row_width))
|
||||
{
|
||||
if (verbose)
|
||||
wxLogError(_("BMP: Couldn't write data."));
|
||||
delete[] buffer;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
delete[] buffer;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif // __WIN32__
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
|
||||
|
||||
#ifndef BI_RGB
|
||||
#define BI_RGB 0
|
||||
@@ -240,6 +188,7 @@ bool wxBMPHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose
|
||||
} *cmap = NULL;
|
||||
|
||||
off_t start_offset = stream.TellI();
|
||||
if (start_offset == wxInvalidOffset) start_offset = 0;
|
||||
|
||||
image->Destroy();
|
||||
|
||||
|
Reference in New Issue
Block a user