Image::Image(CGImageRef image) : _width(CGImageGetWidth(image)), _height(CGImageGetHeight(image)), _bitsPerComponent(CGImageGetBitsPerComponent(image)), _bytesPerRow(CGImageGetBytesPerRow(image)), _colorSpaceRef(CGImageGetColorSpace(image)), _bitmapInfo(CGImageGetBitmapInfo(image)), _pixels(new pixel3f[_width * _height]), _copy(new pixel3f[_width * _height]) { // Obtain mutable image data. CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(image)); _data = CFDataCreateMutableCopy(NULL, CFDataGetLength(data), data); // Release data from image. CFRelease(data); }
Screenshot* ScreenShooter::take_screenshot(const CFStringRef format, float compression) { CGImageRef* images = new CGImageRef[_dsp_count]; /* Grab the images */ for (unsigned int i = 0; i < _dsp_count; i++) { images[i] = CGDisplayCreateImage(_displays[i]); } /* Calculate size of image to produce */ CGSize finalSize = CGSizeMake(_top_right.x - _bottom_left.x, _bottom_left.y - _top_right.y); /* Round out the bitmap size information */ size_t bytesPerRow = finalSize.width * CGImageGetBitsPerPixel(images[0]) / 8; /* Create context around bitmap */ CGContextRef context = CGBitmapContextCreate( NULL, finalSize.width, finalSize.height, CGImageGetBitsPerComponent(images[0]), bytesPerRow, CGImageGetColorSpace(images[0]), CGImageGetBitmapInfo(images[0]) ); /* Draw images into the bitmap */ for (unsigned int i = 0; i < _dsp_count; i++) { /* Adjust the positions to account for coordinate system shifts (displays origin is at top left; image origin is at bottom left) */ CGRect adjustedPoint = CGRectMake(_display_bounds[i].origin.x - _bottom_left.x, _bottom_left.y - _display_bounds[i].size.height - _display_bounds[i].origin.y, _display_bounds[i].size.width, _display_bounds[i].size.height); CGContextDrawImage(context, adjustedPoint, images[i]); } delete [] images; return new Screenshot(context, format, compression); }
/* Once we have our image (CGImageRef), we need to get it into an osg::Image */ osg::Image* CreateOSGImageFromCGImage(CGImageRef image_ref) { /* This code is adapted from Apple's Documentation found here: * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html * Listing 9-4††Using a Quartz image as a texture source. * Unfortunately, this guide doesn't show what to do about * non-RGBA image formats so I'm making the rest up * (and it's probably all wrong). */ size_t the_width = CGImageGetWidth(image_ref); size_t the_height = CGImageGetHeight(image_ref); CGRect the_rect = {{0, 0}, {the_width, the_height}}; size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref); size_t bytes_per_row = CGImageGetBytesPerRow(image_ref); // size_t bits_per_component = CGImageGetBitsPerComponent(image_ref); size_t bits_per_component = 8; CGImageAlphaInfo alpha_info = CGImageGetAlphaInfo(image_ref); GLint internal_format; GLenum pixel_format; GLenum data_type; void* image_data = calloc(the_width * 4, the_height); CGColorSpaceRef color_space; CGBitmapInfo bitmap_info = CGImageGetBitmapInfo(image_ref); switch(bits_per_pixel) { // Drat, if 8-bit, how do you distinguish // between a 256 color GIF, a LUMINANCE map // or an ALPHA map? case 8: { // I probably did the formats all wrong for this case, // especially the ALPHA case. if(kCGImageAlphaNone == alpha_info) { /* internal_format = GL_LUMINANCE; pixel_format = GL_LUMINANCE; */ internal_format = GL_RGBA8; pixel_format = GL_BGRA_EXT; data_type = GL_UNSIGNED_INT_8_8_8_8_REV; bytes_per_row = the_width*4; // color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); color_space = CGColorSpaceCreateDeviceRGB(); // bitmap_info = kCGImageAlphaPremultipliedFirst; #if __BIG_ENDIAN__ bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */ #else bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */ #endif } else { internal_format = GL_ALPHA; pixel_format = GL_ALPHA; data_type = GL_UNSIGNED_BYTE; // bytes_per_row = the_width; // color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); color_space = CGColorSpaceCreateDeviceGray(); } break; } case 24: { internal_format = GL_RGBA8; pixel_format = GL_BGRA_EXT; data_type = GL_UNSIGNED_INT_8_8_8_8_REV; bytes_per_row = the_width*4; // color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); color_space = CGColorSpaceCreateDeviceRGB(); // bitmap_info = kCGImageAlphaNone; #if __BIG_ENDIAN__ bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */ #else bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */ #endif break; } // // Tatsuhiro Nishioka // 16 bpp grayscale (8 bit white and 8 bit alpha) causes invalid argument combination // in CGBitmapContextCreate. // I guess it is safer to handle 16 bit grayscale image as 32-bit RGBA image. // It works at least on FlightGear // case 16: case 32: case 48: case 64: { internal_format = GL_RGBA8; pixel_format = GL_BGRA_EXT; data_type = GL_UNSIGNED_INT_8_8_8_8_REV; bytes_per_row = the_width*4; // color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); color_space = CGColorSpaceCreateDeviceRGB(); // bitmap_info = kCGImageAlphaPremultipliedFirst; #if __BIG_ENDIAN__ bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */ #else bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */ #endif break; } default: { // OSG_WARN << "Unknown file type in " << fileName.c_str() << " with " << origDepth << std::endl; return NULL; break; } } // Sets up a context to be drawn to with image_data as the area to be drawn to CGContextRef bitmap_context = CGBitmapContextCreate( image_data, the_width, the_height, bits_per_component, bytes_per_row, color_space, bitmap_info ); CGContextTranslateCTM(bitmap_context, 0, the_height); CGContextScaleCTM(bitmap_context, 1.0, -1.0); // Draws the image into the context's image_data CGContextDrawImage(bitmap_context, the_rect, image_ref); CGContextRelease(bitmap_context); if (!image_data) return NULL; // alpha is premultiplied with rgba, undo it vImage_Buffer vb; vb.data = image_data; vb.height = the_height; vb.width = the_width; vb.rowBytes = the_width * 4; vImageUnpremultiplyData_RGBA8888(&vb, &vb, 0); // changing it to GL_UNSIGNED_BYTE seems working, but I'm not sure if this is a right way. // data_type = GL_UNSIGNED_BYTE; osg::Image* osg_image = new osg::Image; osg_image->setImage( the_width, the_height, 1, internal_format, pixel_format, data_type, (unsigned char*)image_data, osg::Image::USE_MALLOC_FREE // Assumption: osg_image takes ownership of image_data and will free ); return osg_image; }
/* the error does not contain the filename as the caller already has it */ gboolean _cogl_bitmap_from_file (CoglBitmap *bmp, const gchar *filename, GError **error) { g_assert (bmp != NULL); g_assert (filename != NULL); g_assert (error == NULL || *error == NULL); CFURLRef url = CFURLCreateFromFileSystemRepresentation (NULL, (guchar*)filename, strlen(filename), false); CGImageSourceRef image_source = CGImageSourceCreateWithURL (url, NULL); int save_errno = errno; CFRelease (url); if (image_source == NULL) { /* doesn't exist, not readable, etc. */ g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_FAILED, "%s", g_strerror (save_errno)); return FALSE; } /* Unknown images would be cleanly caught as zero width/height below, but try * to provide better error message */ CFStringRef type = CGImageSourceGetType (image_source); if (type == NULL) { CFRelease (image_source); g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_UNKNOWN_TYPE, "Unknown image type"); return FALSE; } CFRelease (type); CGImageRef image = CGImageSourceCreateImageAtIndex (image_source, 0, NULL); CFRelease (image_source); size_t width = CGImageGetWidth (image); size_t height = CGImageGetHeight (image); if (width == 0 || height == 0) { /* incomplete or corrupt */ CFRelease (image); g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_CORRUPT_IMAGE, "Image has zero width or height"); return FALSE; } /* allocate buffer big enough to hold pixel data */ size_t rowstride; CGBitmapInfo bitmap_info = CGImageGetBitmapInfo (image); if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaNone) { bitmap_info = kCGImageAlphaNone; rowstride = 3 * width; } else { bitmap_info = kCGImageAlphaPremultipliedFirst; rowstride = 4 * width; } guint8 *out_data = g_malloc0 (height * rowstride); /* render to buffer */ CGColorSpaceRef color_space = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); CGContextRef bitmap_context = CGBitmapContextCreate (out_data, width, height, 8, rowstride, color_space, bitmap_info); CGColorSpaceRelease (color_space); const CGRect rect = {{0, 0}, {width, height}}; CGContextDrawImage (bitmap_context, rect, image); CGImageRelease (image); CGContextRelease (bitmap_context); /* store bitmap info */ bmp->data = out_data; bmp->format = bitmap_info == kCGImageAlphaPremultipliedFirst ? COGL_PIXEL_FORMAT_ARGB_8888 : COGL_PIXEL_FORMAT_RGB_888; bmp->width = width; bmp->height = height; bmp->rowstride = rowstride; return TRUE; }
/* read image file */ int BIReadFile( const char *fileName, BIFileType fileType, BIPadMode padMode, unsigned padSize, BIImageInfo *imageInfo, /* RETURNED */ unsigned char **bitmap) /* mallocd and RETURNED; caller must free */ { CFURLRef fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (unsigned char*)fileName, strlen(fileName), FALSE); if(fileURL == NULL) { fprintf(stderr, "***BIReadFile: Error on CFURLCreateFromFileSystemRepresentation\n"); return -1; } CFStringRef keys[1] = {kCGImageSourceTypeIdentifierHint}; CFStringRef values[1] = {BIGetUTI(fileURL, fileType)}; if(values[0] == NULL) { return -1; } CFDictionaryRef optionsDict = NULL; CGImageSourceRef imageSourceRef = NULL; CGImageRef imageRef = NULL; CGColorSpaceRef rgbColorSpaceRef = NULL; CGContextRef bitmapContextRef = NULL; CGBitmapInfo bitmapInfo = 0; CGImageAlphaInfo alpha; unsigned bytesPerPixel = 4; optionsDict = CFDictionaryCreate( kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); /* subsequent errors to errOut: */ int ourRtn = 0; /* source file --> CGImageRef */ imageSourceRef = CGImageSourceCreateWithURL(fileURL, optionsDict); if(imageSourceRef == NULL) { fprintf(stderr, "***BIReadFile: Error on CGImageSourceCreateWithURL\n"); ourRtn = 1; goto errOut; } CFRELEASE(fileURL); imageRef = CGImageSourceCreateImageAtIndex(imageSourceRef, 0, optionsDict ); if(imageRef == NULL) { fprintf(stderr, "***BIReadFile: Error on CGImageSourceCreateImageAtIndex\n"); ourRtn = 1; goto errOut; } imageInfo->imageWidth = CGImageGetWidth(imageRef); imageInfo->imageHeight = CGImageGetHeight(imageRef); imageInfo->bitsPerComponent = CGImageGetBitsPerComponent(imageRef); imageInfo->bitsPerPixel = CGImageGetBitsPerPixel(imageRef); if(imageInfo->bitsPerPixel == 8) { /* the image is gray */ rgbColorSpaceRef = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); imageInfo->bytesPerRow = imageInfo->imageWidth; alpha = kCGImageAlphaNone; bitmapInfo = CGImageGetBitmapInfo(imageRef); bytesPerPixel = 1; } else { rgbColorSpaceRef = CGColorSpaceCreateDeviceRGB(); imageInfo->bytesPerRow = imageInfo->imageWidth * 4; alpha = kCGImageAlphaPremultipliedLast; bitmapInfo = kCGBitmapByteOrder32Big | alpha; } if(rgbColorSpaceRef == NULL) { fprintf(stderr, "***BIReadFile: Error on CGColorSpaceCreateWithName\n"); ourRtn = 1; goto errOut; } /* optionally pad */ imageInfo->effectHeight = imageInfo->imageHeight; if(padMode != PM_None) { if(padSize == 0) { fprintf(stderr, "***Pad size of 0 invalid\n"); ourRtn = 1; goto errOut; } unsigned padSizeBytes = padSize; if(padMode == PM_Pixels) { padSizeBytes *= bytesPerPixel; } imageInfo->bytesPerRow = ROUND_TO(imageInfo->bytesPerRow, padSizeBytes); /* also round up row count */ imageInfo->effectHeight = ROUND_TO(imageInfo->imageHeight, padSize); } *bitmap = (unsigned char *)malloc(imageInfo->bytesPerRow * imageInfo->effectHeight); bitmapContextRef = CGBitmapContextCreate(*bitmap, imageInfo->imageWidth, imageInfo->imageHeight, imageInfo->bitsPerComponent, imageInfo->bytesPerRow, rgbColorSpaceRef, bitmapInfo); if(bitmapContextRef == NULL) { fprintf(stderr, "***BIReadFile: Error creating CGBitmapContext\n"); ourRtn = 1; goto errOut; } /* enable high quality interpolation */ CGContextSetInterpolationQuality(bitmapContextRef, kCGInterpolationHigh); /* Draw into the context */ CGContextDrawImage(bitmapContextRef, CGRectMake(0, 0, imageInfo->imageWidth, imageInfo->imageHeight), imageRef); errOut: CFRELEASE(optionsDict); CFRELEASE(imageSourceRef); CFRELEASE(imageRef); CFRELEASE(rgbColorSpaceRef); CFRELEASE(bitmapContextRef); if(ourRtn) { if(*bitmap) { free(*bitmap); *bitmap = NULL; } } return ourRtn; }
result_t MMMaker_make(const char **sourceMaskDirs, int nSourceMaskDirs, const char *filename) { #define MAX_FILENAMES 500 result_t err; char **sourceMaskFilenames; int nSourceMaskFilenames = 0; char *sourceMaskFilenamesBuffer = NULL; motionmaskmaker_t *maker = NULL; int i; CGImageRef makerSource[MAX_FILENAMES]; CGBitmapInfo bitmapInfo; pixelfmt_t pixelfmt; bitmap_set_t makerBitmaps; CFDataRef pixels[MAX_FILENAMES]; void *makerBitmapBases[MAX_FILENAMES]; err = findfilesbyregexp(sourceMaskDirs, nSourceMaskDirs, ".*\\.png", &sourceMaskFilenames, &nSourceMaskFilenames, &sourceMaskFilenamesBuffer); if (err) goto failure; if (nSourceMaskFilenames <= 0 || nSourceMaskFilenames > MAX_FILENAMES) { err = result_TOO_BIG; goto failure; } for (i = 0; i < nSourceMaskFilenames; i++) pixels[i] = NULL; err = motionmaskmaker_create(&maker); if (err) goto failure; for (i = 0; i < nSourceMaskFilenames; i++) { logf_info("sourcing mask %d from %s", i, sourceMaskFilenames[i]); makerSource[i] = createCGImageFromPNGFile(sourceMaskFilenames[i]); bitmapInfo = CGImageGetBitmapInfo(makerSource[i]); pixelfmt = bitmapInfoToPixelfmt(bitmapInfo); if (pixelfmt == pixelfmt_unknown) { logf_error("MMMaker_make: Unknown pixelfmt."); return result_BAD_ARG; } // bodge pixelfmt to be something we can currently cope with if (pixelfmt == pixelfmt_rgba8888) pixelfmt = pixelfmt_rgbx8888; if (pixelfmt == pixelfmt_abgr8888) pixelfmt = pixelfmt_xbgr8888; // turn the image into greyscale if it's anything else if (pixelfmt != pixelfmt_y8) { CGImageRef greyCopy; greyCopy = BitmapTransform_createGreyscaleCopy(makerSource[i]); CGImageRelease(makerSource[i]); makerSource[i] = greyCopy; bitmapInfo = CGImageGetBitmapInfo(makerSource[i]); pixelfmt = bitmapInfoToPixelfmt(bitmapInfo); if (pixelfmt == pixelfmt_unknown) return result_BAD_ARG; } pixels[i] = copyImagePixels(makerSource[i]); if (pixels[i] == NULL) goto failure; makerBitmapBases[i] = (void *) CFDataGetBytePtr(pixels[i]); } makerBitmaps.width = (int) CGImageGetWidth(makerSource[0]); makerBitmaps.height = (int) CGImageGetHeight(makerSource[0]); makerBitmaps.format = pixelfmt; makerBitmaps.rowbytes = (int) CGImageGetBytesPerRow(makerSource[0]); makerBitmaps.nbases = nSourceMaskFilenames; makerBitmaps.bases = makerBitmapBases; err = motionmaskmaker_pack(maker, &makerBitmaps); if (err) goto failure; err = motionmaskmaker_save(maker, filename); if (err) goto failure; /* cleanup */ failure: for (i = 0; i < nSourceMaskFilenames; i++) if (pixels[i]) CFRelease(pixels[i]); motionmaskmaker_destroy(maker); maker = NULL; free(sourceMaskFilenamesBuffer); free(sourceMaskFilenames); return err; }