agg/src/platform/mac/agg_mac_pmap.cpp

299 lines
8.5 KiB
C++

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
// Contact: mcseemagg@yahoo.com
// baer@karto.baug.ethz.ch
//----------------------------------------------------------------------------
//
// class pixel_map
//
//----------------------------------------------------------------------------
#include <cstring>
#include <Carbon.h>
#include <QuickTimeComponents.h>
#include <ImageCompression.h>
#include "platform/mac/agg_mac_pmap.h"
#include "agg_basics.h"
namespace agg
{
//------------------------------------------------------------------------
pixel_map::~pixel_map()
{
destroy();
}
//------------------------------------------------------------------------
pixel_map::pixel_map() :
m_pmap(0),
m_buf(0),
m_bpp(0),
m_img_size(0)
{
}
//------------------------------------------------------------------------
void pixel_map::destroy()
{
delete[] m_buf;
m_buf = NULL;
if (m_pmap != nil)
{
DisposeGWorld(m_pmap);
m_pmap = nil;
}
}
//------------------------------------------------------------------------
void pixel_map::create(unsigned width,
unsigned height,
org_e org,
unsigned clear_val)
{
destroy();
if(width == 0) width = 1;
if(height == 0) height = 1;
m_bpp = org;
Rect r;
int row_bytes = calc_row_len (width, m_bpp);
MacSetRect(&r, 0, 0, width, height);
m_buf = new unsigned char[m_img_size = row_bytes * height];
// The Quicktime version for creating GWorlds is more flexible than the classical function.
QTNewGWorldFromPtr (&m_pmap, m_bpp, &r, nil, nil, 0, m_buf, row_bytes);
// create_gray_scale_palette(m_pmap); I didn't care about gray scale palettes so far.
if(clear_val <= 255)
{
std::memset(m_buf, clear_val, m_img_size);
}
}
//------------------------------------------------------------------------
void pixel_map::clear(unsigned clear_val)
{
if(m_buf) std::memset(m_buf, clear_val, m_img_size);
}
//static
//This function is just copied from the Win32 plattform support.
//Is also seems to be appropriate for MacOS as well, but it is not
//thouroughly tested so far.
//------------------------------------------------------------------------
unsigned pixel_map::calc_row_len(unsigned width, unsigned bits_per_pixel)
{
unsigned n = width;
unsigned k;
switch(bits_per_pixel)
{
case 1: k = n;
n = n >> 3;
if(k & 7) n++;
break;
case 4: k = n;
n = n >> 1;
if(k & 3) n++;
break;
case 8:
break;
case 16: n = n << 1;
break;
case 24: n = (n << 1) + n;
break;
case 32: n = n << 2;
break;
default: n = 0;
break;
}
return ((n + 3) >> 2) << 2;
}
//------------------------------------------------------------------------
void pixel_map::draw(WindowRef window, const Rect *device_rect, const Rect *pmap_rect) const
{
if(m_pmap == nil || m_buf == NULL) return;
PixMapHandle pm = GetGWorldPixMap (m_pmap);
CGrafPtr port = GetWindowPort (window);
Rect dest_rect;
// Again, I used the Quicktime version.
// Good old 'CopyBits' does better interpolation when scaling
// but does not support all pixel depths.
MacSetRect (&dest_rect, 0, 0, this->width(), this->height());
ImageDescriptionHandle image_description;
MakeImageDescriptionForPixMap (pm, &image_description);
if (image_description != nil)
{
DecompressImage (GetPixBaseAddr (pm), image_description, GetPortPixMap (port), nil, &dest_rect, ditherCopy, nil);
DisposeHandle ((Handle) image_description);
}
}
//------------------------------------------------------------------------
void pixel_map::draw(WindowRef window, int x, int y, double scale) const
{
if(m_pmap == nil || m_buf == NULL) return;
unsigned width = (unsigned)(this->width() * scale);
unsigned height = (unsigned)(this->height() * scale);
Rect rect;
SetRect (&rect, x, y, x + width, y + height);
draw(window, &rect);
}
//------------------------------------------------------------------------
void pixel_map::blend(WindowRef window, const Rect *device_rect, const Rect *bmp_rect) const
{
draw (window, device_rect, bmp_rect); // currently just mapped to drawing method
}
//------------------------------------------------------------------------
void pixel_map::blend(WindowRef window, int x, int y, double scale) const
{
draw(window, x, y, scale); // currently just mapped to drawing method
}
// I let Quicktime handle image import since it supports most popular
// image formats such as:
// *.psd, *.bmp, *.tif, *.png, *.jpg, *.gif, *.pct, *.pcx
//------------------------------------------------------------------------
bool pixel_map::load_from_qt(const char *filename)
{
FSSpec fss;
OSErr err;
// get file specification to application directory
err = HGetVol(nil, &fss.vRefNum, &fss.parID);
if (err == noErr)
{
CopyCStringToPascal(filename, fss.name);
GraphicsImportComponent gi;
err = GetGraphicsImporterForFile (&fss, &gi);
if (err == noErr)
{
ImageDescriptionHandle desc;
GraphicsImportGetImageDescription(gi, &desc);
// For simplicity, all images are currently converted to 32 bit.
// create an empty pixelmap
short depth = 32;
create ((**desc).width, (**desc).height, (org_e)depth, 0xff);
DisposeHandle ((Handle)desc);
// let Quicktime draw to pixelmap
GraphicsImportSetGWorld(gi, m_pmap, nil);
GraphicsImportDraw(gi);
// Well, this is a hack. The graphics importer sets the alpha channel of the pixelmap to 0x00
// for imported images without alpha channel but this would cause agg to draw an invisible image.
// set alpha channel to 0xff
unsigned char * buf = m_buf;
for (unsigned int size = 0; size < m_img_size; size += 4)
{
*buf = 0xff;
buf += 4;
}
}
}
return err == noErr;
}
//------------------------------------------------------------------------
bool pixel_map::save_as_qt(const char *filename) const
{
FSSpec fss;
OSErr err;
// get file specification to application directory
err = HGetVol(nil, &fss.vRefNum, &fss.parID);
if (err == noErr)
{
GraphicsExportComponent ge;
CopyCStringToPascal(filename, fss.name);
// I decided to use PNG as output image file type.
// There are a number of other available formats.
// Should I check the file suffix to choose the image file format?
err = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG, &ge);
if (err == noErr)
{
err = GraphicsExportSetInputGWorld(ge, m_pmap);
if (err == noErr)
{
err = GraphicsExportSetOutputFile (ge, &fss);
if (err == noErr)
{
GraphicsExportDoExport(ge, nil);
}
}
CloseComponent(ge);
}
}
return err == noErr;
}
//------------------------------------------------------------------------
unsigned char* pixel_map::buf()
{
return m_buf;
}
//------------------------------------------------------------------------
unsigned pixel_map::width() const
{
if(m_pmap == nil) return 0;
PixMapHandle pm = GetGWorldPixMap (m_pmap);
Rect bounds;
GetPixBounds (pm, &bounds);
return bounds.right - bounds.left;
}
//------------------------------------------------------------------------
unsigned pixel_map::height() const
{
if(m_pmap == nil) return 0;
PixMapHandle pm = GetGWorldPixMap (m_pmap);
Rect bounds;
GetPixBounds (pm, &bounds);
return bounds.bottom - bounds.top;
}
//------------------------------------------------------------------------
int pixel_map::row_bytes() const
{
if(m_pmap == nil) return 0;
PixMapHandle pm = GetGWorldPixMap (m_pmap);
return calc_row_len(width(), GetPixDepth(pm));
}
}