// -*- objc -*- #import #include #import #include "helpers.h" namespace Rays { static CGBitmapInfo make_bitmapinfo (const ColorSpace& cs) { // Q: What color spaces does CGBitmapContextCreate support? // http://developer.apple.com/library/mac/#qa/qa1037/_index.html CGBitmapInfo info = 0; if (cs.is_alpha_first()) { info |= cs.is_premult() ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaFirst; } else if (cs.is_alpha_last()) { info |= cs.is_premult() ? kCGImageAlphaPremultipliedLast : kCGImageAlphaLast; } else if (cs.is_skip_first()) info |= kCGImageAlphaNoneSkipFirst; else if (cs.is_skip_last()) info |= kCGImageAlphaNoneSkipLast; else info |= kCGImageAlphaNone; if (cs.is_rgb()) info |= kCGBitmapByteOrder32Big; else if (cs.is_bgr()) info |= kCGBitmapByteOrder32Little; else return false; if (cs.is_float()) info |= kCGBitmapFloatComponents; return info; } struct Bitmap::Data { int width, height; ColorSpace colorspace; void* pixels; CGContextRef context; bool dirty; Data () : width(0), height(0), pixels(NULL), context(NULL), dirty(true) { } ~Data () { clear(); } bool setup (int w, int h, const ColorSpace& cs) { if (w <= 0 || h <= 0 || !cs) return false; clear(); width = w; height = h; colorspace = cs; size_t size = w * h * cs.Bpp(); pixels = new uchar[size]; memset(pixels, 0, size); return true; } CGContextRef get_context () { if (context) return context; int bpc = colorspace.bpc(); int pitch = width * colorspace.Bpp(); if (bpc <= 0 || pitch <= 0) return NULL; CGColorSpaceRef cgcs = NULL; if (colorspace.is_gray()) cgcs = CGColorSpaceCreateDeviceGray(); else if (colorspace.is_rgb() || colorspace.is_bgr()) cgcs = CGColorSpaceCreateDeviceRGB(); else return NULL; context = CGBitmapContextCreate( pixels, width, height, bpc, pitch, cgcs, make_bitmapinfo(colorspace)); CGColorSpaceRelease(cgcs); return context; } CGImageRef get_image () { CGContextRef c = get_context(); if (!c) return NULL; return CGBitmapContextCreateImage(c); } void clear () { if (context) CGContextRelease(context); if (pixels) delete [] (uchar*) pixels; width = height = 0; colorspace = COLORSPACE_UNKNOWN; pixels = NULL; context = NULL; dirty = true; } };// Bitmap::Data Bitmap::Bitmap () { } Bitmap::Bitmap (int width, int height, const ColorSpace& colorspace) { self->setup(width, height, colorspace); } Bitmap::~Bitmap () { } int Bitmap::width () const { if (!*this) return 0; return self->width; } int Bitmap::height () const { if (!*this) return 0; return self->height; } const ColorSpace& Bitmap::color_space () const { if (!*this) { static const ColorSpace UNKNOWN = COLORSPACE_UNKNOWN; return UNKNOWN; } return self->colorspace; } int Bitmap::pitch () const { return width() * self->colorspace.Bpp(); } size_t Bitmap::size () const { return pitch() * height(); } bool Bitmap::dirty () const { return self->dirty; } void Bitmap::set_dirty (bool b) { self->dirty = b; } void* Bitmap::data () { if (!*this) return NULL; return self->pixels; } const void* Bitmap::data () const { return const_cast(this)->data(); } Bitmap::operator bool () const { return self->width > 0 && self->height > 0 && self->colorspace && self->pixels; } bool Bitmap::operator ! () const { return !operator bool(); } bool load_bitmap (Bitmap* bmp, const char* path_) { if (!bmp || !path_) return false; NSString* path = [NSString stringWithUTF8String: path_]; NSBitmapImageRep* imagerep = [NSBitmapImageRep imageRepWithContentsOfFile: path]; if (!imagerep) return false; CGImageRef image = [imagerep CGImage]; if (!image) return false; int width = CGImageGetWidth(image); int height = CGImageGetHeight(image); *bmp = Bitmap(width, height, ColorSpace(RGBA, true)); if (!*bmp) return false; CGContextRef context = bmp->self->get_context(); if (!context) return false; CGContextDrawImage(context, CGRectMake(0, 0, width, height), image); return true; } bool save_bitmap (const Bitmap& bmp, const char* path_) { boost::shared_ptr img( bmp.self->get_image(), CGImageRelease); if (!img) return false; NSString* path = [NSString stringWithUTF8String: path_]; NSURL* url = [NSURL fileURLWithPath: path]; if (!url) return false; boost::shared_ptr dest( CGImageDestinationCreateWithURL((CFURLRef) url, kUTTypePNG, 1, NULL), safe_cfrelease); if (!dest) return false; CGImageDestinationAddImage(dest.get(), img.get(), NULL); return CGImageDestinationFinalize(dest.get()); } bool draw_string ( CGContextRef, coord, const char*, coord, coord, const Font&); bool draw_string ( Bitmap* bmp, const char* str, coord x, coord y, const Font& font) { if (!bmp || !*bmp || !str || !font) return false; if (*str == '\0') return true; if (!draw_string(bmp->self->get_context(), bmp->height(), str, x, y, font)) return false; bmp->set_dirty(); return true; } }// Rays