static void convertScanline(void* data, size_t tileNumber, bool premultiply) { ScanlineData* scanlineData = static_cast<ScanlineData*>(data); vImage_Buffer src; src.data = scanlineData->srcData + tileNumber * scanlineData->srcRowBytes; src.height = 1; src.width = scanlineData->scanlineWidth; src.rowBytes = scanlineData->srcRowBytes; vImage_Buffer dest; dest.data = scanlineData->destData + tileNumber * scanlineData->destRowBytes; dest.height = 1; dest.width = scanlineData->scanlineWidth; dest.rowBytes = scanlineData->destRowBytes; if (premultiply) { if (kvImageNoError != vImagePremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile)) return; } else { if (kvImageNoError != vImageUnpremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile)) return; } // Swap channels 1 and 3, to convert BGRA<->RGBA. IOSurfaces is BGRA, ImageData expects RGBA. const uint8_t map[4] = { 2, 1, 0, 3 }; vImagePermuteChannels_ARGB8888(&dest, &dest, map, kvImageDoNotTile); }
/* 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; }
PassRefPtr<Uint8ClampedArray> ImageBufferData::getData(const IntRect& rect, const IntSize& size, bool accelerateRendering, bool unmultiplied, float resolutionScale) const { float area = 4.0f * rect.width() * rect.height(); if (area > static_cast<float>(std::numeric_limits<int>::max())) return 0; RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); unsigned char* data = result->data(); int endx = ceilf(rect.maxX() * resolutionScale); int endy = ceilf(rect.maxY() * resolutionScale); if (rect.x() < 0 || rect.y() < 0 || endx > size.width() || endy > size.height()) result->zeroFill(); int originx = rect.x(); int destx = 0; int destw = rect.width(); if (originx < 0) { destw += originx; destx = -originx; originx = 0; } destw = min<int>(destw, ceilf(size.width() / resolutionScale) - originx); originx *= resolutionScale; if (endx > size.width()) endx = size.width(); int width = endx - originx; int originy = rect.y(); int desty = 0; int desth = rect.height(); if (originy < 0) { desth += originy; desty = -originy; originy = 0; } desth = min<int>(desth, ceilf(size.height() / resolutionScale) - originy); originy *= resolutionScale; if (endy > size.height()) endy = size.height(); int height = endy - originy; if (width <= 0 || height <= 0) return result.release(); unsigned destBytesPerRow = 4 * rect.width(); unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; unsigned srcBytesPerRow; unsigned char* srcRows; if (!accelerateRendering) { srcBytesPerRow = 4 * size.width(); srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4; #if USE(ACCELERATE) if (unmultiplied && haveVImageRoundingErrorFix()) { vImage_Buffer src; src.height = height; src.width = width; src.rowBytes = srcBytesPerRow; src.data = srcRows; vImage_Buffer dst; dst.height = desth; dst.width = destw; dst.rowBytes = destBytesPerRow; dst.data = destRows; if (resolutionScale != 1) { vImage_AffineTransform scaleTransform = { 1 / resolutionScale, 0, 0, 1 / resolutionScale, 0, 0 }; // FIXME: Add subpixel translation. Pixel_8888 backgroundColor; vImageAffineWarp_ARGB8888(&src, &dst, 0, &scaleTransform, backgroundColor, kvImageEdgeExtend); // The unpremultiplying will be done in-place. src = dst; } vImageUnpremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags); return result.release(); } #endif if (resolutionScale != 1) { RetainPtr<CGContextRef> sourceContext(AdoptCF, CGBitmapContextCreate(srcRows, width, height, 8, srcBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast)); RetainPtr<CGImageRef> sourceImage(AdoptCF, CGBitmapContextCreateImage(sourceContext.get())); RetainPtr<CGContextRef> destinationContext(AdoptCF, CGBitmapContextCreate(destRows, destw, desth, 8, destBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast)); CGContextSetBlendMode(destinationContext.get(), kCGBlendModeCopy); CGContextDrawImage(destinationContext.get(), CGRectMake(0, 0, width / resolutionScale, height / resolutionScale), sourceImage.get()); // FIXME: Add subpixel translation. if (!unmultiplied) return result.release(); srcRows = destRows; srcBytesPerRow = destBytesPerRow; width = destw; height = desth; } if (unmultiplied) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; x++) { int basex = x * 4; unsigned char alpha = srcRows[basex + 3]; if (alpha) { destRows[basex] = (srcRows[basex] * 255) / alpha; destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha; destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha; destRows[basex + 3] = alpha; } else reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; } srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } else { for (int y = 0; y < height; ++y) { for (int x = 0; x < width * 4; x += 4) reinterpret_cast<uint32_t*>(destRows + x)[0] = reinterpret_cast<uint32_t*>(srcRows + x)[0]; srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } } else { #if USE(IOSURFACE_CANVAS_BACKING_STORE) IOSurfaceRef surface = m_surface.get(); IOSurfaceLock(surface, kIOSurfaceLockReadOnly, 0); srcBytesPerRow = IOSurfaceGetBytesPerRow(surface); srcRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + originy * srcBytesPerRow + originx * 4; #if USE(ACCELERATE) vImage_Buffer src; src.height = height; src.width = width; src.rowBytes = srcBytesPerRow; src.data = srcRows; vImage_Buffer dest; dest.height = desth; dest.width = destw; dest.rowBytes = destBytesPerRow; dest.data = destRows; if (resolutionScale != 1) { vImage_AffineTransform scaleTransform = { 1 / resolutionScale, 0, 0, 1 / resolutionScale, 0, 0 }; // FIXME: Add subpixel translation. Pixel_8888 backgroundColor; vImageAffineWarp_ARGB8888(&src, &dest, 0, &scaleTransform, backgroundColor, kvImageEdgeExtend); // The unpremultiplying and channel-swapping will be done in-place. if (unmultiplied) { srcRows = destRows; width = destw; height = desth; srcBytesPerRow = destBytesPerRow; } else src = dest; } if (unmultiplied) { ScanlineData scanlineData; scanlineData.scanlineWidth = width; scanlineData.srcData = srcRows; scanlineData.srcRowBytes = srcBytesPerRow; scanlineData.destData = destRows; scanlineData.destRowBytes = destBytesPerRow; dispatch_apply_f(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, unpremultitplyScanline); } else { // Swap pixel channels from BGRA to RGBA. const uint8_t map[4] = { 2, 1, 0, 3 }; vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags); } #else if (resolutionScale != 1) { RetainPtr<CGContextRef> sourceContext(AdoptCF, CGBitmapContextCreate(srcRows, width, height, 8, srcBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast)); RetainPtr<CGImageRef> sourceImage(AdoptCF, CGBitmapContextCreateImage(sourceContext.get())); RetainPtr<CGContextRef> destinationContext(AdoptCF, CGBitmapContextCreate(destRows, destw, desth, 8, destBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast)); CGContextSetBlendMode(destinationContext.get(), kCGBlendModeCopy); CGContextDrawImage(destinationContext.get(), CGRectMake(0, 0, width / resolutionScale, height / resolutionScale), sourceImage.get()); // FIXME: Add subpixel translation. srcRows = destRows; srcBytesPerRow = destBytesPerRow; width = destw; height = desth; } if (unmultiplied) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; x++) { int basex = x * 4; unsigned char b = srcRows[basex]; unsigned char alpha = srcRows[basex + 3]; if (alpha) { destRows[basex] = (srcRows[basex + 2] * 255) / alpha; destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha; destRows[basex + 2] = (b * 255) / alpha; destRows[basex + 3] = alpha; } else { destRows[basex] = srcRows[basex + 2]; destRows[basex + 1] = srcRows[basex + 1]; destRows[basex + 2] = b; destRows[basex + 3] = srcRows[basex + 3]; } } srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } else { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; x++) { int basex = x * 4; unsigned char b = srcRows[basex]; destRows[basex] = srcRows[basex + 2]; destRows[basex + 1] = srcRows[basex + 1]; destRows[basex + 2] = b; destRows[basex + 3] = srcRows[basex + 3]; } srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } #endif // USE(ACCELERATE) IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, 0); #else ASSERT_NOT_REACHED(); #endif // USE(IOSURFACE_CANVAS_BACKING_STORE) } return result.release(); }
PassRefPtr<ByteArray> ImageBufferData::getData(const IntRect& rect, const IntSize& size, bool accelerateRendering, bool unmultiplied) const { float area = 4.0f * rect.width() * rect.height(); if (area > static_cast<float>(std::numeric_limits<int>::max())) return 0; RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); unsigned char* data = result->data(); if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height()) memset(data, 0, result->length()); int originx = rect.x(); int destx = 0; if (originx < 0) { destx = -originx; originx = 0; } int endx = rect.maxX(); if (endx > size.width()) endx = size.width(); int width = endx - originx; int originy = rect.y(); int desty = 0; if (originy < 0) { desty = -originy; originy = 0; } int endy = rect.maxY(); if (endy > size.height()) endy = size.height(); int height = endy - originy; if (width <= 0 || height <= 0) return result.release(); unsigned destBytesPerRow = 4 * rect.width(); unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; unsigned srcBytesPerRow; unsigned char* srcRows; if (!accelerateRendering) { srcBytesPerRow = 4 * size.width(); srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4; #if USE(ACCELERATE) if (unmultiplied && haveVImageRoundingErrorFix()) { vImage_Buffer src; src.height = height; src.width = width; src.rowBytes = srcBytesPerRow; src.data = srcRows; vImage_Buffer dst; dst.height = height; dst.width = width; dst.rowBytes = destBytesPerRow; dst.data = destRows; vImageUnpremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags); return result.release(); } #endif if (unmultiplied) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; x++) { int basex = x * 4; unsigned char alpha = srcRows[basex + 3]; if (alpha) { destRows[basex] = (srcRows[basex] * 255) / alpha; destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha; destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha; destRows[basex + 3] = alpha; } else reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; } srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } else { for (int y = 0; y < height; ++y) { for (int x = 0; x < width * 4; x += 4) reinterpret_cast<uint32_t*>(destRows + x)[0] = reinterpret_cast<uint32_t*>(srcRows + x)[0]; srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } } else { #if USE(IOSURFACE_CANVAS_BACKING_STORE) IOSurfaceRef surface = m_surface.get(); IOSurfaceLock(surface, kIOSurfaceLockReadOnly, 0); srcBytesPerRow = IOSurfaceGetBytesPerRow(surface); srcRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + originy * srcBytesPerRow + originx * 4; #if USE(ACCELERATE) if (unmultiplied) { ScanlineData scanlineData; scanlineData.scanlineWidth = width; scanlineData.srcData = srcRows; scanlineData.srcRowBytes = srcBytesPerRow; scanlineData.destData = destRows; scanlineData.destRowBytes = destBytesPerRow; dispatch_apply_f(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, unpremultitplyScanline); } else { vImage_Buffer src; src.height = height; src.width = width; src.rowBytes = srcBytesPerRow; src.data = srcRows; vImage_Buffer dest; dest.height = height; dest.width = width; dest.rowBytes = destBytesPerRow; dest.data = destRows; // Swap pixel channels from BGRA to RGBA. const uint8_t map[4] = { 2, 1, 0, 3 }; vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags); } #else if (unmultiplied) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; x++) { int basex = x * 4; unsigned char alpha = srcRows[basex + 3]; if (alpha) { destRows[basex] = (srcRows[basex + 2] * 255) / alpha; destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha; destRows[basex + 2] = (srcRows[basex] * 255) / alpha; destRows[basex + 3] = alpha; } else { destRows[basex] = srcRows[basex + 2]; destRows[basex + 1] = srcRows[basex + 1]; destRows[basex + 2] = srcRows[basex]; destRows[basex + 3] = srcRows[basex + 3]; } } srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } else { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; x++) { int basex = x * 4; destRows[basex] = srcRows[basex + 2]; destRows[basex + 1] = srcRows[basex + 1]; destRows[basex + 2] = srcRows[basex]; destRows[basex + 3] = srcRows[basex + 3]; } srcRows += srcBytesPerRow; destRows += destBytesPerRow; } } #endif // USE(ACCELERATE) IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, 0); #else ASSERT_NOT_REACHED(); #endif // USE(IOSURFACE_CANVAS_BACKING_STORE) } return result.release(); }