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:
Václav Slavík
2000-06-17 16:31:10 +00:00
parent 12675983a2
commit 5208c1aa1e
2 changed files with 102 additions and 156 deletions

View File

@@ -31,12 +31,9 @@ public:
m_mime = _T("image/bmp"); 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 #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 LoadFile( wxImage *image, wxInputStream& stream, bool verbose=TRUE, int index=0 );
virtual bool DoCanRead( wxInputStream& stream ); virtual bool DoCanRead( wxInputStream& stream );
#endif // wxUSE_STREAMS #endif // wxUSE_STREAMS

View File

@@ -47,173 +47,121 @@
IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler) 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, bool wxBMPHandler::SaveFile(wxImage *image,
wxOutputStream& stream, wxOutputStream& stream,
bool verbose) bool verbose)
{ {
// get HBITMAP first
wxCHECK_MSG( image, FALSE, _T("invalid pointer in wxBMPHandler::SaveFile") ); wxCHECK_MSG( image, FALSE, _T("invalid pointer in wxBMPHandler::SaveFile") );
wxBitmap bitmap = image->ConvertToBitmap(); if (!image->Ok())
HBITMAP hBmp = GetHbitmapOf(bitmap); {
wxCHECK_MSG( hBmp, FALSE, _T("can't save invalid bitmap") ); if (verbose) wxLogError(_("BMP: Couldn't save invalid image."));
// 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."));
return FALSE; 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; return TRUE;
} }
#endif // __WIN32__
#if wxUSE_STREAMS
#ifndef BI_RGB #ifndef BI_RGB
#define BI_RGB 0 #define BI_RGB 0
@@ -240,6 +188,7 @@ bool wxBMPHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose
} *cmap = NULL; } *cmap = NULL;
off_t start_offset = stream.TellI(); off_t start_offset = stream.TellI();
if (start_offset == wxInvalidOffset) start_offset = 0;
image->Destroy(); image->Destroy();