This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
853 lines
26 KiB
C++
853 lines
26 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/dib.cpp
|
|
// Purpose: implements wxDIB class
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 03.03.03 (replaces the old file with the same name)
|
|
// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
TODO: support for palettes is very incomplete, several functions simply
|
|
ignore them (we should select and realize the palette, if any, before
|
|
caling GetDIBits() in the DC we use with it.
|
|
*/
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_WXDIB
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/string.h"
|
|
#include "wx/log.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/bitmap.h"
|
|
#include "wx/image.h"
|
|
#endif //WX_PRECOMP
|
|
|
|
#include "wx/file.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <memory.h>
|
|
|
|
#include "wx/msw/dib.h"
|
|
|
|
#ifdef __WXWINCE__
|
|
#include <shellapi.h> // for SHLoadDIBitmap()
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// private functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// calculate the number of palette entries needed for the bitmap with this
|
|
// number of bits per pixel
|
|
static inline WORD GetNumberOfColours(WORD bitsPerPixel)
|
|
{
|
|
// only 1, 4 and 8bpp bitmaps use palettes (well, they could be used with
|
|
// 24bpp ones too but we don't support this as I think it's quite uncommon)
|
|
return (WORD)(bitsPerPixel <= 8 ? 1 << bitsPerPixel : 0);
|
|
}
|
|
|
|
// wrapper around ::GetObject() for DIB sections
|
|
static inline bool GetDIBSection(HBITMAP hbmp, DIBSECTION *ds)
|
|
{
|
|
// note that at least under Win9x (this doesn't seem to happen under Win2K
|
|
// but this doesn't mean anything, of course), GetObject() may return
|
|
// sizeof(DIBSECTION) for a bitmap which is *not* a DIB section and the way
|
|
// to check for it is by looking at the bits pointer
|
|
return ::GetObject(hbmp, sizeof(DIBSECTION), ds) == sizeof(DIBSECTION) &&
|
|
ds->dsBm.bmBits;
|
|
}
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDIB creation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxDIB::Create(int width, int height, int depth)
|
|
{
|
|
// we don't support formats using palettes right now so we only create
|
|
// either 24bpp (RGB) or 32bpp (RGBA) bitmaps
|
|
wxASSERT_MSG( depth, wxT("invalid image depth in wxDIB::Create()") );
|
|
if ( depth < 24 )
|
|
depth = 24;
|
|
|
|
// allocate memory for bitmap structures
|
|
BITMAPINFO info;
|
|
wxZeroMemory(info);
|
|
|
|
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
info.bmiHeader.biWidth = width;
|
|
|
|
// we use positive height here which corresponds to a DIB with normal, i.e.
|
|
// bottom to top, order -- normally using negative height (which means
|
|
// reversed for MS and hence natural for all the normal people top to
|
|
// bottom line scan order) could be used to avoid the need for the image
|
|
// reversal in Create(image) but this doesn't work under NT, only Win9x!
|
|
info.bmiHeader.biHeight = height;
|
|
|
|
info.bmiHeader.biPlanes = 1;
|
|
info.bmiHeader.biBitCount = (WORD)depth;
|
|
info.bmiHeader.biSizeImage = GetLineSize(width, depth)*height;
|
|
|
|
m_handle = ::CreateDIBSection
|
|
(
|
|
0, // hdc (unused with DIB_RGB_COLORS)
|
|
&info, // bitmap description
|
|
DIB_RGB_COLORS, // use RGB, not palette
|
|
&m_data, // [out] DIB bits
|
|
NULL, // don't use file mapping
|
|
0 // file mapping offset (not used here)
|
|
);
|
|
|
|
if ( !m_handle )
|
|
{
|
|
wxLogLastError(wxT("CreateDIBSection"));
|
|
|
|
return false;
|
|
}
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
m_depth = depth;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDIB::Create(HBITMAP hbmp)
|
|
{
|
|
wxCHECK_MSG( hbmp, false, wxT("wxDIB::Create(): invalid bitmap") );
|
|
|
|
// this bitmap could already be a DIB section in which case we don't need
|
|
// to convert it to DIB
|
|
DIBSECTION ds;
|
|
if ( GetDIBSection(hbmp, &ds) )
|
|
{
|
|
m_handle = hbmp;
|
|
|
|
// wxBitmap will free it, not we
|
|
m_ownsHandle = false;
|
|
|
|
// copy all the bitmap parameters too as we have them now anyhow
|
|
m_width = ds.dsBm.bmWidth;
|
|
m_height = ds.dsBm.bmHeight;
|
|
m_depth = ds.dsBm.bmBitsPixel;
|
|
|
|
m_data = ds.dsBm.bmBits;
|
|
}
|
|
else // no, it's a DDB -- convert it to DIB
|
|
{
|
|
// prepare all the info we need
|
|
BITMAP bm;
|
|
if ( !::GetObject(hbmp, sizeof(bm), &bm) )
|
|
{
|
|
wxLogLastError(wxT("GetObject(bitmap)"));
|
|
|
|
return false;
|
|
}
|
|
|
|
int d = bm.bmBitsPixel;
|
|
if ( d <= 0 )
|
|
d = wxDisplayDepth();
|
|
|
|
if ( !Create(bm.bmWidth, bm.bmHeight, d) || !CopyFromDDB(hbmp) )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Windows CE doesn't have GetDIBits() so use an alternative implementation
|
|
// for it
|
|
//
|
|
// in fact I'm not sure if GetDIBits() is really much better than using
|
|
// BitBlt() like this -- it should be faster but I didn't do any tests, if
|
|
// anybody has time to do them and by chance finds that GetDIBits() is not
|
|
// much faster than BitBlt(), we could always use the Win CE version here
|
|
#ifdef __WXWINCE__
|
|
|
|
bool wxDIB::CopyFromDDB(HBITMAP hbmp)
|
|
{
|
|
MemoryHDC hdcSrc;
|
|
if ( !hdcSrc )
|
|
return false;
|
|
|
|
SelectInHDC selectSrc(hdcSrc, hbmp);
|
|
if ( !selectSrc )
|
|
return false;
|
|
|
|
MemoryHDC hdcDst;
|
|
if ( !hdcDst )
|
|
return false;
|
|
|
|
SelectInHDC selectDst(hdcDst, m_handle);
|
|
if ( !selectDst )
|
|
return false;
|
|
|
|
|
|
if ( !::BitBlt(
|
|
hdcDst,
|
|
0, 0, m_width, m_height,
|
|
hdcSrc,
|
|
0, 0,
|
|
SRCCOPY
|
|
) )
|
|
{
|
|
wxLogLastError(wxT("BitBlt(DDB -> DIB)"));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#else // !__WXWINCE__
|
|
|
|
bool wxDIB::CopyFromDDB(HBITMAP hbmp)
|
|
{
|
|
DIBSECTION ds;
|
|
if ( !GetDIBSection(m_handle, &ds) )
|
|
{
|
|
// we're sure that our handle is a DIB section, so this should work
|
|
wxFAIL_MSG( wxT("GetObject(DIBSECTION) unexpectedly failed") );
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( !::GetDIBits
|
|
(
|
|
ScreenHDC(), // the DC to use
|
|
hbmp, // the source DDB
|
|
0, // first scan line
|
|
m_height, // number of lines to copy
|
|
ds.dsBm.bmBits, // pointer to the buffer
|
|
(BITMAPINFO *)&ds.dsBmih, // bitmap header
|
|
DIB_RGB_COLORS // and not DIB_PAL_COLORS
|
|
) )
|
|
{
|
|
wxLogLastError(wxT("GetDIBits()"));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // __WXWINCE__/!__WXWINCE__
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Loading/saving the DIBs
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxDIB::Load(const wxString& filename)
|
|
{
|
|
#ifdef __WXWINCE__
|
|
m_handle = SHLoadDIBitmap(filename);
|
|
#else // !__WXWINCE__
|
|
m_handle = (HBITMAP)::LoadImage
|
|
(
|
|
wxGetInstance(),
|
|
filename.t_str(),
|
|
IMAGE_BITMAP,
|
|
0, 0, // don't specify the size
|
|
LR_CREATEDIBSECTION | LR_LOADFROMFILE
|
|
);
|
|
#endif // __WXWINCE__
|
|
|
|
if ( !m_handle )
|
|
{
|
|
wxLogLastError(wxT("Loading DIB from file"));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDIB::Save(const wxString& filename)
|
|
{
|
|
wxCHECK_MSG( m_handle, false, wxT("wxDIB::Save(): invalid object") );
|
|
|
|
#if wxUSE_FILE
|
|
wxFile file(filename, wxFile::write);
|
|
bool ok = file.IsOpened();
|
|
if ( ok )
|
|
{
|
|
DIBSECTION ds;
|
|
if ( !GetDIBSection(m_handle, &ds) )
|
|
{
|
|
wxLogLastError(wxT("GetObject(hDIB)"));
|
|
}
|
|
else
|
|
{
|
|
BITMAPFILEHEADER bmpHdr;
|
|
wxZeroMemory(bmpHdr);
|
|
|
|
const size_t sizeHdr = ds.dsBmih.biSize;
|
|
const size_t sizeImage = ds.dsBmih.biSizeImage;
|
|
|
|
bmpHdr.bfType = 0x4d42; // 'BM' in little endian
|
|
bmpHdr.bfOffBits = sizeof(BITMAPFILEHEADER) + ds.dsBmih.biSize;
|
|
bmpHdr.bfSize = bmpHdr.bfOffBits + sizeImage;
|
|
|
|
// first write the file header, then the bitmap header and finally the
|
|
// bitmap data itself
|
|
ok = file.Write(&bmpHdr, sizeof(bmpHdr)) == sizeof(bmpHdr) &&
|
|
file.Write(&ds.dsBmih, sizeHdr) == sizeHdr &&
|
|
file.Write(ds.dsBm.bmBits, sizeImage) == sizeImage;
|
|
}
|
|
}
|
|
#else // !wxUSE_FILE
|
|
bool ok = false;
|
|
#endif // wxUSE_FILE/!wxUSE_FILE
|
|
|
|
if ( !ok )
|
|
{
|
|
wxLogError(_("Failed to save the bitmap image to file \"%s\"."),
|
|
filename.c_str());
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDIB accessors
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxDIB::DoGetObject() const
|
|
{
|
|
// only do something if we have a valid DIB but we don't [yet] have valid
|
|
// data
|
|
if ( m_handle && !m_data )
|
|
{
|
|
// although all the info we need is in BITMAP and so we don't really
|
|
// need DIBSECTION we still ask for it as modifying the bit values only
|
|
// works for the real DIBs and not for the bitmaps and it's better to
|
|
// check for this now rather than trying to find out why it doesn't
|
|
// work later
|
|
DIBSECTION ds;
|
|
if ( !GetDIBSection(m_handle, &ds) )
|
|
{
|
|
wxLogLastError(wxT("GetObject(hDIB)"));
|
|
return;
|
|
}
|
|
|
|
wxDIB *self = wxConstCast(this, wxDIB);
|
|
|
|
self->m_width = ds.dsBm.bmWidth;
|
|
self->m_height = ds.dsBm.bmHeight;
|
|
self->m_depth = ds.dsBm.bmBitsPixel;
|
|
self->m_data = ds.dsBm.bmBits;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// DDB <-> DIB conversions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#ifndef __WXWINCE__
|
|
|
|
HBITMAP wxDIB::CreateDDB(HDC hdc) const
|
|
{
|
|
wxCHECK_MSG( m_handle, 0, wxT("wxDIB::CreateDDB(): invalid object") );
|
|
|
|
DIBSECTION ds;
|
|
if ( !GetDIBSection(m_handle, &ds) )
|
|
{
|
|
wxLogLastError(wxT("GetObject(hDIB)"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
// how many colours are we going to have in the palette?
|
|
DWORD biClrUsed = ds.dsBmih.biClrUsed;
|
|
if ( !biClrUsed )
|
|
{
|
|
// biClrUsed field might not be set
|
|
biClrUsed = GetNumberOfColours(ds.dsBmih.biBitCount);
|
|
}
|
|
|
|
if ( !biClrUsed )
|
|
{
|
|
return ConvertToBitmap((BITMAPINFO *)&ds.dsBmih, hdc, ds.dsBm.bmBits);
|
|
}
|
|
else
|
|
{
|
|
// fake a BITMAPINFO w/o bits, just the palette info
|
|
wxCharBuffer bmi(sizeof(BITMAPINFO) + (biClrUsed - 1)*sizeof(RGBQUAD));
|
|
BITMAPINFO *pBmi = (BITMAPINFO *)bmi.data();
|
|
MemoryHDC hDC;
|
|
// get the colour table
|
|
SelectInHDC sDC(hDC, m_handle);
|
|
::GetDIBColorTable(hDC, 0, biClrUsed, pBmi->bmiColors);
|
|
memcpy(&pBmi->bmiHeader, &ds.dsBmih, ds.dsBmih.biSize);
|
|
|
|
return ConvertToBitmap(pBmi, hdc, ds.dsBm.bmBits);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
HBITMAP wxDIB::ConvertToBitmap(const BITMAPINFO *pbmi, HDC hdc, void *bits)
|
|
{
|
|
wxCHECK_MSG( pbmi, 0, wxT("invalid DIB in ConvertToBitmap") );
|
|
|
|
// here we get BITMAPINFO struct followed by the actual bitmap bits and
|
|
// BITMAPINFO starts with BITMAPINFOHEADER followed by colour info
|
|
const BITMAPINFOHEADER *pbmih = &pbmi->bmiHeader;
|
|
|
|
// get the pointer to the start of the real image data if we have a plain
|
|
// DIB and not a DIB section (in the latter case the pointer must be passed
|
|
// to us by the caller)
|
|
if ( !bits )
|
|
{
|
|
// we must skip over the colour table to get to the image data
|
|
//
|
|
// colour table either has the real colour data in which case its
|
|
// number of entries is given by biClrUsed or is used for masks to be
|
|
// used for extracting colour information from true colour bitmaps in
|
|
// which case it always have exactly 3 DWORDs
|
|
int numColors;
|
|
switch ( pbmih->biCompression )
|
|
{
|
|
case BI_BITFIELDS:
|
|
numColors = 3;
|
|
break;
|
|
|
|
case BI_RGB:
|
|
// biClrUsed has the number of colors but it may be not initialized at
|
|
// all
|
|
numColors = pbmih->biClrUsed;
|
|
if ( !numColors )
|
|
{
|
|
numColors = GetNumberOfColours(pbmih->biBitCount);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// no idea how it should be calculated for the other cases
|
|
numColors = 0;
|
|
}
|
|
|
|
bits = (char *)pbmih + sizeof(*pbmih) + numColors*sizeof(RGBQUAD);
|
|
}
|
|
|
|
HBITMAP hbmp = ::CreateDIBitmap
|
|
(
|
|
hdc ? hdc // create bitmap compatible
|
|
: (HDC) ScreenHDC(), // with this DC
|
|
pbmih, // used to get size &c
|
|
CBM_INIT, // initialize bitmap bits too
|
|
bits, // ... using this data
|
|
pbmi, // this is used for palette only
|
|
DIB_RGB_COLORS // direct or indexed palette?
|
|
);
|
|
|
|
if ( !hbmp )
|
|
{
|
|
wxLogLastError(wxT("CreateDIBitmap"));
|
|
}
|
|
|
|
return hbmp;
|
|
}
|
|
|
|
/* static */
|
|
size_t wxDIB::ConvertFromBitmap(BITMAPINFO *pbi, HBITMAP hbmp)
|
|
{
|
|
wxASSERT_MSG( hbmp, wxT("invalid bmp can't be converted to DIB") );
|
|
|
|
// prepare all the info we need
|
|
BITMAP bm;
|
|
if ( !::GetObject(hbmp, sizeof(bm), &bm) )
|
|
{
|
|
wxLogLastError(wxT("GetObject(bitmap)"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
// we need a BITMAPINFO anyhow and if we're not given a pointer to it we
|
|
// use this one
|
|
BITMAPINFO bi2;
|
|
|
|
const bool wantSizeOnly = pbi == NULL;
|
|
if ( wantSizeOnly )
|
|
pbi = &bi2;
|
|
|
|
// just for convenience
|
|
const int h = bm.bmHeight;
|
|
|
|
// init the header
|
|
BITMAPINFOHEADER& bi = pbi->bmiHeader;
|
|
wxZeroMemory(bi);
|
|
bi.biSize = sizeof(BITMAPINFOHEADER);
|
|
bi.biWidth = bm.bmWidth;
|
|
bi.biHeight = h;
|
|
bi.biPlanes = 1;
|
|
bi.biBitCount = bm.bmBitsPixel;
|
|
|
|
// memory we need for BITMAPINFO only
|
|
DWORD dwLen = bi.biSize + GetNumberOfColours(bm.bmBitsPixel) * sizeof(RGBQUAD);
|
|
|
|
// get either just the image size or the image bits
|
|
if ( !::GetDIBits
|
|
(
|
|
ScreenHDC(), // the DC to use
|
|
hbmp, // the source DDB
|
|
0, // first scan line
|
|
h, // number of lines to copy
|
|
wantSizeOnly ? NULL // pointer to the buffer or
|
|
: (char *)pbi + dwLen, // NULL if we don't have it
|
|
pbi, // bitmap header
|
|
DIB_RGB_COLORS // or DIB_PAL_COLORS
|
|
) )
|
|
{
|
|
wxLogLastError(wxT("GetDIBits()"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
// return the total size
|
|
return dwLen + bi.biSizeImage;
|
|
}
|
|
|
|
/* static */
|
|
HGLOBAL wxDIB::ConvertFromBitmap(HBITMAP hbmp)
|
|
{
|
|
// first calculate the size needed
|
|
const size_t size = ConvertFromBitmap(NULL, hbmp);
|
|
if ( !size )
|
|
{
|
|
// conversion to DDB failed?
|
|
return NULL;
|
|
}
|
|
|
|
HGLOBAL hDIB = ::GlobalAlloc(GMEM_MOVEABLE, size);
|
|
if ( !hDIB )
|
|
{
|
|
// this is an error which does risk to happen especially under Win9x
|
|
// and which the user may understand so let him know about it
|
|
wxLogError(_("Failed to allocate %luKb of memory for bitmap data."),
|
|
(unsigned long)(size / 1024));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if ( !ConvertFromBitmap((BITMAPINFO *)(void *)GlobalPtrLock(hDIB), hbmp) )
|
|
{
|
|
// this really shouldn't happen... it worked the first time, why not
|
|
// now?
|
|
wxFAIL_MSG( wxT("wxDIB::ConvertFromBitmap() unexpectedly failed") );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return hDIB;
|
|
}
|
|
|
|
#endif // __WXWINCE__
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// palette support
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_PALETTE
|
|
|
|
wxPalette *wxDIB::CreatePalette() const
|
|
{
|
|
// GetDIBColorTable not available in eVC3
|
|
#if !defined(__WXMSW__) || defined(_WIN32_WCE) && _WIN32_WCE < 400
|
|
return NULL;
|
|
#else
|
|
wxCHECK_MSG( m_handle, NULL, wxT("wxDIB::CreatePalette(): invalid object") );
|
|
|
|
DIBSECTION ds;
|
|
if ( !GetDIBSection(m_handle, &ds) )
|
|
{
|
|
wxLogLastError(wxT("GetObject(hDIB)"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
// how many colours are we going to have in the palette?
|
|
DWORD biClrUsed = ds.dsBmih.biClrUsed;
|
|
if ( !biClrUsed )
|
|
{
|
|
// biClrUsed field might not be set
|
|
biClrUsed = GetNumberOfColours(ds.dsBmih.biBitCount);
|
|
}
|
|
|
|
if ( !biClrUsed )
|
|
{
|
|
// bitmaps of this depth don't have palettes at all
|
|
//
|
|
// NB: another possibility would be to return
|
|
// GetStockObject(DEFAULT_PALETTE) or even CreateHalftonePalette()?
|
|
return NULL;
|
|
}
|
|
|
|
MemoryHDC hDC;
|
|
|
|
// LOGPALETTE struct has only 1 element in palPalEntry array, we're
|
|
// going to have biClrUsed of them so add necessary space
|
|
LOGPALETTE *pPalette = (LOGPALETTE *)
|
|
malloc(sizeof(LOGPALETTE) + (biClrUsed - 1)*sizeof(PALETTEENTRY));
|
|
wxCHECK_MSG( pPalette, NULL, wxT("out of memory") );
|
|
|
|
// initialize the palette header
|
|
pPalette->palVersion = 0x300; // magic number, not in docs but works
|
|
pPalette->palNumEntries = (WORD)biClrUsed;
|
|
|
|
// and the colour table
|
|
wxCharBuffer rgb(sizeof(RGBQUAD) * biClrUsed);
|
|
RGBQUAD *pRGB = (RGBQUAD*)rgb.data();
|
|
SelectInHDC selectHandle(hDC, m_handle);
|
|
::GetDIBColorTable(hDC, 0, biClrUsed, pRGB);
|
|
for ( DWORD i = 0; i < biClrUsed; i++, pRGB++ )
|
|
{
|
|
pPalette->palPalEntry[i].peRed = pRGB->rgbRed;
|
|
pPalette->palPalEntry[i].peGreen = pRGB->rgbGreen;
|
|
pPalette->palPalEntry[i].peBlue = pRGB->rgbBlue;
|
|
pPalette->palPalEntry[i].peFlags = 0;
|
|
}
|
|
|
|
HPALETTE hPalette = ::CreatePalette(pPalette);
|
|
|
|
free(pPalette);
|
|
|
|
if ( !hPalette )
|
|
{
|
|
wxLogLastError(wxT("CreatePalette"));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
wxPalette *palette = new wxPalette;
|
|
palette->SetHPALETTE((WXHPALETTE)hPalette);
|
|
|
|
return palette;
|
|
#endif
|
|
}
|
|
|
|
#endif // wxUSE_PALETTE
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxImage support
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_IMAGE
|
|
|
|
bool wxDIB::Create(const wxImage& image, PixelFormat pf)
|
|
{
|
|
wxCHECK_MSG( image.IsOk(), false, wxT("invalid wxImage in wxDIB ctor") );
|
|
|
|
const int h = image.GetHeight();
|
|
const int w = image.GetWidth();
|
|
|
|
// if we have alpha channel, we need to create a 32bpp RGBA DIB, otherwise
|
|
// a 24bpp RGB is sufficient
|
|
const bool hasAlpha = image.HasAlpha();
|
|
const int bpp = hasAlpha ? 32 : 24;
|
|
|
|
if ( !Create(w, h, bpp) )
|
|
return false;
|
|
|
|
// DIBs are stored in bottom to top order (see also the comment above in
|
|
// Create()) so we need to copy bits line by line and starting from the end
|
|
const int srcBytesPerLine = w * 3;
|
|
const int dstBytesPerLine = GetLineSize(w, bpp);
|
|
const unsigned char *src = image.GetData() + ((h - 1) * srcBytesPerLine);
|
|
const unsigned char *alpha = hasAlpha ? image.GetAlpha() + (h - 1)*w
|
|
: NULL;
|
|
unsigned char *dstLineStart = (unsigned char *)m_data;
|
|
for ( int y = 0; y < h; y++ )
|
|
{
|
|
// Copy one DIB line. Note that RGB components order is reversed in
|
|
// Windows bitmaps compared to wxImage and is actually BGR.
|
|
unsigned char *dst = dstLineStart;
|
|
if ( alpha )
|
|
{
|
|
int x;
|
|
|
|
switch ( pf )
|
|
{
|
|
case PixelFormat_PreMultiplied:
|
|
// Pre-multiply pixel values so that the DIB could be used
|
|
// with ::AlphaBlend().
|
|
for ( x = 0; x < w; x++ )
|
|
{
|
|
const unsigned char a = *alpha++;
|
|
*dst++ = (unsigned char)((src[2] * a + 127) / 255);
|
|
*dst++ = (unsigned char)((src[1] * a + 127) / 255);
|
|
*dst++ = (unsigned char)((src[0] * a + 127) / 255);
|
|
*dst++ = a;
|
|
src += 3;
|
|
}
|
|
break;
|
|
|
|
case PixelFormat_NotPreMultiplied:
|
|
// Just copy pixel data without changing it.
|
|
for ( x = 0; x < w; x++ )
|
|
{
|
|
*dst++ = src[2];
|
|
*dst++ = src[1];
|
|
*dst++ = src[0];
|
|
|
|
*dst++ = *alpha++;
|
|
src += 3;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
else // no alpha channel
|
|
{
|
|
for ( int x = 0; x < w; x++ )
|
|
{
|
|
*dst++ = src[2];
|
|
*dst++ = src[1];
|
|
*dst++ = src[0];
|
|
src += 3;
|
|
}
|
|
}
|
|
|
|
// pass to the previous line in the image
|
|
src -= 2*srcBytesPerLine;
|
|
if ( alpha )
|
|
alpha -= 2*w;
|
|
|
|
// and to the next one in the DIB
|
|
dstLineStart += dstBytesPerLine;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
wxImage wxDIB::ConvertToImage() const
|
|
{
|
|
wxCHECK_MSG( IsOk(), wxNullImage,
|
|
wxT("can't convert invalid DIB to wxImage") );
|
|
|
|
// create the wxImage object
|
|
const int w = GetWidth();
|
|
const int h = GetHeight();
|
|
wxImage image(w, h, false /* don't bother clearing memory */);
|
|
if ( !image.IsOk() )
|
|
{
|
|
wxFAIL_MSG( wxT("could not allocate data for image") );
|
|
return wxNullImage;
|
|
}
|
|
|
|
const int bpp = GetDepth();
|
|
|
|
// Remember if we have any "real" transparency, i.e. either any partially
|
|
// transparent pixels or not all pixels are fully opaque or fully
|
|
// transparent.
|
|
bool hasAlpha = false;
|
|
bool hasOpaque = false;
|
|
bool hasTransparent = false;
|
|
|
|
if ( bpp == 32 )
|
|
{
|
|
// 32 bit bitmaps may be either 0RGB or ARGB and we don't know in
|
|
// advance which one do we have so suppose we have alpha of them and
|
|
// get rid of it later if it turns out we didn't.
|
|
image.SetAlpha();
|
|
}
|
|
|
|
// this is the same loop as in Create() just above but with copy direction
|
|
// reversed
|
|
const int dstBytesPerLine = w * 3;
|
|
const int srcBytesPerLine = GetLineSize(w, bpp);
|
|
unsigned char *dst = image.GetData() + ((h - 1) * dstBytesPerLine);
|
|
unsigned char *alpha = image.HasAlpha() ? image.GetAlpha() + (h - 1)*w
|
|
: NULL;
|
|
const unsigned char *srcLineStart = (unsigned char *)GetData();
|
|
for ( int y = 0; y < h; y++ )
|
|
{
|
|
// copy one DIB line
|
|
const unsigned char *src = srcLineStart;
|
|
for ( int x = 0; x < w; x++ )
|
|
{
|
|
dst[2] = *src++;
|
|
dst[1] = *src++;
|
|
dst[0] = *src++;
|
|
|
|
if ( bpp == 32 )
|
|
{
|
|
// wxImage uses non premultiplied alpha so undo
|
|
// premultiplication done in Create() above
|
|
const unsigned char a = *src;
|
|
*alpha++ = a;
|
|
|
|
// Check what kind of alpha do we have.
|
|
switch ( a )
|
|
{
|
|
case 0:
|
|
hasTransparent = true;
|
|
break;
|
|
|
|
default:
|
|
// Anything in between means we have real transparency
|
|
// and must use alpha channel.
|
|
hasAlpha = true;
|
|
break;
|
|
|
|
case 255:
|
|
hasOpaque = true;
|
|
break;
|
|
}
|
|
|
|
if ( a > 0 )
|
|
{
|
|
dst[0] = (dst[0] * 255) / a;
|
|
dst[1] = (dst[1] * 255) / a;
|
|
dst[2] = (dst[2] * 255) / a;
|
|
}
|
|
|
|
src++;
|
|
}
|
|
|
|
dst += 3;
|
|
}
|
|
|
|
// pass to the previous line in the image
|
|
dst -= 2*dstBytesPerLine;
|
|
if ( alpha )
|
|
alpha -= 2*w;
|
|
|
|
// and to the next one in the DIB
|
|
srcLineStart += srcBytesPerLine;
|
|
}
|
|
|
|
if ( hasOpaque && hasTransparent )
|
|
hasAlpha = true;
|
|
|
|
if ( !hasAlpha && image.HasAlpha() )
|
|
image.ClearAlpha();
|
|
|
|
return image;
|
|
}
|
|
|
|
#endif // wxUSE_IMAGE
|
|
|
|
#endif // wxUSE_WXDIB
|