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");
|
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
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user