wxImage

From WxWiki
Jump to navigation Jump to search
Official Classes SmallBlocks.png Archive Containers Controls Data Structures Database Date & Time Debug Device Contexts Dialogs Document & Views Drag & Drop Events Filesystem Frames Graphics Grid Cell Help HTML Logging Miscellaneous Networking Printing Sizers Streams Threading Windows

This class encapsulates a platform-independent image.

An image can be created from data, or using wxBitmap::ConvertToImage. An image can be loaded from a file in a variety of formats, and is extensible to new formats via image format handlers. Functions are available to set and get image bits, so it can be used for basic image manipulation.

A wxImage cannot (currently) be drawn directly to a wxDC. Instead, a platform-specific wxBitmap object must be created from it using the wxBitmap::wxBitmap(wxImage,int depth) constructor. This bitmap can then be drawn in a device context, using wxDC::DrawBitmap.

More on the difference between wxImage and wxBitmap: wxImage is just a buffer of RGB bytes with an optional buffer for the alpha bytes. It is all generic, platform independent and image file format independent code. It includes generic code for scaling, resizing, clipping, and other manipulations of the image data. OTOH, wxBitmap is intended to be a wrapper of whatever is the native image format that is quickest/easiest to draw to a DC or to be the target of the drawing operations performed on a wxMemoryDC. By splitting the responsibilities between wxImage/wxBitmap like this then it's easier to use generic code shared by all platforms and image types for generic operations and platform specific code where performance or compatibility is needed.

One colour value of the image may be used as a mask colour which will lead to the automatic creation of a wxMask object associated to the bitmap object.

Gotchas

If you're opening a file and get: 'Warning: No image handler for type xx defined.', you probably forgot to add the image handler and/or initialise all handlers. See <wxref class="wxImage"></wxref> and <wxref class="wxImageHandler"></wxref>.

The default compression algorithm for the TIFF image handler's SaveFile method is LZW. This algorithm is patented by Unisys Corporation. Unisys is currently enforcing its patent, so software which uses LZW may be held liable. For more information see http://www.unisys.com/about__unisys/lzw. In particular, this patent is no longer enforceable in Canada, France, Germany, Italy, Japan, the United Kingdom and the United States of America - but it remains enforceable in other countries.

wxBasic implementation

This short example shows how to use wxImage to resize an image:

  
dim Path_img = "immagine.bmp" '     ******* CHANGE NAME HERE!!! ******

frame  = new wxFrame( Null, -1, "Using wxImage", wxPoint(20, 20), wxSize(320, 200 + 25))
panel  = new wxPanel( frame, -1, wxPoint(0, 200), wxSize(320, 200))
panel2 = new wxPanel( frame, -1, wxPoint(-1, -1), wxSize(0, 0))

bmp = new wxEmptyImage(0, 0)  ' Create an empty Image. Size does NOT matter...
img = new wxEmptyBitmap(0, 0) ' Create empty bitmap

bmp2DC = wxMemoryDC() ' Allocate memory: this space will be used to store the bitmap.

if(bmp.LoadFile(Path_img, wxBITMAP_TYPE_BMP)) then   'Load image
	wxMessageBox("image loaded correctly")
end if

'resize l'immagine
wxMessageBox("Original size(X,Y) = (" & bmp.GetWidth() & "," & bmp.GetHeight() & ")")    
bmp = bmp.Scale(60,60) ' Change image size
wxMessageBox("Size(X,Y) after resize = (" & bmp.GetWidth() & "," & bmp.GetHeight() & ")")

bmp2 = bmp.ConvertToBitmap()    'convert image into bmp
wxMessageBox("convert image into bitmap")

Sub onPaint( event )
	dc = wxPaintDC( frame )
	dc.BeginDrawing()
	dc.DrawBitmap(bmp2, 1, 1, FALSE) 'draw converted image
	dc.EndDrawing()
End Sub
  
Connect( frame, -1, wxEVT_PAINT, "onPaint" )
  
frame.Show(True)

You can find wxBasic at this address. The language is under development, so the above source may require minor adjustment on some wxBasic versions.

Creating Standalone Copy of wxImage

The wxImage copy constructor doesn't seem to create a new copy of the image object. I used this code to do so:

wxImage* m_pOldImg = new wxImage( "image.bmp", wxBITMAP_TYPE_BMP, -1 );

int dataSize = m_pOldImg->GetWidth() * m_pOldImg->GetHeight() * 3;
unsigned char* NewImgData = ( unsigned char* ) malloc( dataSize );
unsigned char* OldImgData = m_pOldImg->GetData();

memcpy( NewImgData, OldImgData, dataSize );

wxImage* m_pNewImg = new wxImage( m_pOldImg->GetWidth(), m_pOldImg->GetHeight(), NewImgData );

A neater way would be to use wxImage::Copy, but at the time of writing (jan 2006), you need to copy the Alpha channel manually (GetAlphaData(), memcpy, SetAlphaData()).

Supported image formats

I have the following code snippet in an app to find out what image formats are available for use:

#include <wx/list.h>

WX_DECLARE_LIST(wxImageHandler, HandlerList);
#include <wx/listimpl.cpp>
WX_DEFINE_LIST(HandlerList);

wxChoice *formatchoice = new wxChoice( this, -1 );
wxImageHandler* handler;

wxInitAllImageHandlers();

HandlerList& handlers = (HandlerList&)wxImage::GetHandlers();
HandlerList::Node *node = handlers.GetFirst();

while(node) {
	wxString extn;
	handler = (wxImageHandler *)node->GetData();
	extn = handler->GetExtension();
	if(extn.Len() > 0) formatchoice->Append(handler->GetExtension(), (void *)handler);

	/* If you want to use this for saving, add these checks to the if statement. */

	/* && extn != "cur" // Format not good for general-purpose writing
	   && extn != "ico" // Format not good for general-purpose writing
	   && extn != "iff" // wx Doesn't support writing to this
	   && extn != "gif" // wx Doesn't support writing to this
	   && extn != "ani"  // wx Doesn't support writing to this
	*/

	node = node->GetNext();
}
 
/* Use the wxChoice however you want */

const wxString mimetype = ((wxImageHandler *)formatchoice->GetClientData(formatchoice->GetSelection()))->GetMimeType();
if(image.SaveFile(filename->GetValue(), mimetype ) == TRUE) {
	/* Image saved successfully */
} else {
	/* Image not saved successfully */
}

Basic function to automatically crop 'whitespace' surrounding an image

Very simplistic/slow - only for example purposes:

  wxImage AutoCropWhiteSpace(const wxImage& InputImage)
  {
      int nHeight=InputImage.GetHeight();
      int nWidth=InputImage.GetWidth();
      unsigned char nR=InputImage.GetRed(0,0);
      unsigned char nG=InputImage.GetGreen(0,0);
      unsigned char nB=InputImage.GetBlue(0,0);
      int nCloseness=15;
      
      wxRect ClipRect;
      //Calculate top clip
      for(int y=0;y<nHeight;y++)
      {
          for(int x=0;x<nWidth;x++)
          {
              if(std::abs(InputImage.GetRed(x,y)-nR)>nCloseness||std::abs(InputImage.GetGreen(x,y)-nG)>nCloseness||std::abs(InputImage.GetBlue(x,y)-nB)>nCloseness)
              {
                  ClipRect.y=std::max(y-1,0);
                  goto bottom;
              }
          }
      }
      bottom:
      //Calculate bottom clip
      for(int y=nHeight-1;y>=0; y--)
      {
          for(int x=0;x<nWidth;x++)
          {
              if(std::abs(InputImage.GetRed(x,y)-nR)>nCloseness||std::abs(InputImage.GetGreen(x,y)-nG)>nCloseness||std::abs(InputImage.GetBlue(x,y)-nB)>nCloseness)
              {
                  ClipRect.height=std::min(y+1,nHeight)-ClipRect.y;
                  goto left;
              }
          }
      }
      left:
      //Calculate left clip
      for(int x=0;x<nWidth; x++)
      {
          for(int y=ClipRect.y;y<ClipRect.y+ClipRect.height;y++)
          {
              if(std::abs(InputImage.GetRed(x,y)-nR)>nCloseness||std::abs(InputImage.GetGreen(x,y)-nG)>nCloseness||std::abs(InputImage.GetBlue(x,y)-nB)>nCloseness)
              {
                  ClipRect.x=std::max(x-1,0);
                  goto right;
              }
          }
      }
      right:
      //Calculate right clip
      for(int x=nWidth-1;x>=0; x--)
      {
          for(int y=ClipRect.y;y<ClipRect.y+ClipRect.height;y++)
          {
              if(std::abs(InputImage.GetRed(x,y)-nR)>nCloseness||std::abs(InputImage.GetGreen(x,y)-nG)>nCloseness||std::abs(InputImage.GetBlue(x,y)-nB)>nCloseness)
              {
                  ClipRect.width=std::min(x+1,nWidth)-ClipRect.x;
                  goto crop;
              }
          }
      }
      crop:
      //Create new cropped image
      return InputImage.GetSubImage(ClipRect);
  }

Correct SetData Documentation

The docs don't correctly document the SetData method. The actual methods for SetImage are:

   void SetData( unsigned char *data, bool static_data=false );
   void SetData( unsigned char *data, int new_width, int new_height, bool static_data=false );

The most important part is the "static_data" parameter. If you pass true, then wxImage will not try to free the memory. It's also important to note that if you pass "false", thereby indicating that you wish wxImage to delete the data in its own destructor that the memory used for the data was allocated with "malloc()" (i.e. NOT "new" or "new <type>[]) as wxImage's destructor will destroy the data with "free()"

See Also