| /* |
| * grfmt_imageio.cpp |
| * |
| * |
| * Created by Morgan Conbere on 5/17/07. |
| * |
| */ |
| |
| #include "_highgui.h" |
| |
| #ifdef HAVE_IMAGEIO |
| |
| #include "grfmt_imageio.h" |
| #include <iostream> |
| using namespace std; |
| |
| // ImageIO filter factory |
| |
| GrFmtImageIO::GrFmtImageIO() |
| { |
| m_sign_len = 0; |
| m_signature = NULL; |
| m_description = "Apple ImageIO (*.bmp;*.dib;*.exr;*.jpeg;*.jpg;*.jpe;*.jp2;*.pdf;*.png;*.tiff;*.tif)"; |
| } |
| |
| |
| GrFmtImageIO::~GrFmtImageIO() |
| { |
| } |
| |
| |
| bool GrFmtImageIO::CheckFile( const char* filename ) |
| { |
| if( !filename ) return false; |
| |
| // If a CFImageRef can be retrieved from an image file, it is |
| // readable by ImageIO. Effectively this is using ImageIO |
| // to check the signatures and determine the file format for us. |
| CFURLRef imageURLRef = CFURLCreateFromFileSystemRepresentation( NULL, |
| (const UInt8*)filename, |
| strlen( filename ), |
| false ); |
| if( !imageURLRef ) return false; |
| |
| CGImageSourceRef sourceRef = CGImageSourceCreateWithURL( imageURLRef, NULL ); |
| CFRelease( imageURLRef ); |
| if( !sourceRef ) return false; |
| |
| CGImageRef imageRef = CGImageSourceCreateImageAtIndex( sourceRef, 0, NULL ); |
| CFRelease( sourceRef ); |
| if( !imageRef ) return false; |
| |
| return true; |
| } |
| |
| |
| GrFmtReader* GrFmtImageIO::NewReader( const char* filename ) |
| { |
| return new GrFmtImageIOReader( filename ); |
| } |
| |
| |
| GrFmtWriter* GrFmtImageIO::NewWriter( const char* filename ) |
| { |
| return new GrFmtImageIOWriter( filename ); |
| } |
| |
| |
| /////////////////////// GrFmtImageIOReader /////////////////// |
| |
| GrFmtImageIOReader::GrFmtImageIOReader( const char* filename ) : GrFmtReader( filename ) |
| { |
| // Nothing to do here |
| } |
| |
| |
| GrFmtImageIOReader::~GrFmtImageIOReader() |
| { |
| Close(); |
| } |
| |
| |
| void GrFmtImageIOReader::Close() |
| { |
| CGImageRelease( imageRef ); |
| |
| GrFmtReader::Close(); |
| } |
| |
| |
| bool GrFmtImageIOReader::ReadHeader() |
| { |
| CFURLRef imageURLRef; |
| CGImageSourceRef sourceRef; |
| imageRef = NULL; |
| |
| imageURLRef = CFURLCreateFromFileSystemRepresentation( NULL, |
| (const UInt8*)m_filename, |
| strlen(m_filename), |
| false ); |
| |
| sourceRef = CGImageSourceCreateWithURL( imageURLRef, NULL ); |
| CFRelease( imageURLRef ); |
| if ( !sourceRef ) |
| return false; |
| |
| imageRef = CGImageSourceCreateImageAtIndex( sourceRef, 0, NULL ); |
| CFRelease( sourceRef ); |
| if( !imageRef ) |
| return false; |
| |
| m_width = CGImageGetWidth( imageRef ); |
| m_height = CGImageGetHeight( imageRef ); |
| |
| CGColorSpaceRef colorSpace = CGImageGetColorSpace( imageRef ); |
| if( !colorSpace ) |
| return false; |
| |
| m_iscolor = ( CGColorSpaceGetNumberOfComponents( colorSpace ) > 1 ); |
| |
| return true; |
| } |
| |
| |
| bool GrFmtImageIOReader::ReadData( uchar* data, int step, int color ) |
| { |
| int bpp; // Bytes per pixel |
| |
| // Set color to either CV_IMAGE_LOAD_COLOR or CV_IMAGE_LOAD_GRAYSCALE if unchanged |
| color = color > 0 || ( m_iscolor && color < 0 ); |
| |
| // Get Height, Width, and color information |
| if( !ReadHeader() ) |
| return false; |
| |
| CGContextRef context = NULL; // The bitmap context |
| CGColorSpaceRef colorSpace = NULL; |
| uchar* bitmap = NULL; |
| CGImageAlphaInfo alphaInfo; |
| |
| // CoreGraphics will take care of converting to grayscale and back as long as the |
| // appropriate colorspace is set |
| if( color == CV_LOAD_IMAGE_GRAYSCALE ) |
| { |
| colorSpace = CGColorSpaceCreateDeviceGray(); |
| bpp = 1; |
| alphaInfo = kCGImageAlphaNone; |
| } |
| else if( color == CV_LOAD_IMAGE_COLOR ) |
| { |
| colorSpace = CGColorSpaceCreateDeviceRGB(); |
| bpp = 4; /* CG only has 8 and 32 bit color spaces, so we waste a byte */ |
| alphaInfo = kCGImageAlphaNoneSkipLast; |
| } |
| if( !colorSpace ) |
| return false; |
| |
| bitmap = (uchar*)malloc( bpp * m_height * m_width ); |
| if( !bitmap ) |
| { |
| CGColorSpaceRelease( colorSpace ); |
| return false; |
| } |
| |
| context = CGBitmapContextCreate( (void *)bitmap, |
| m_width, /* width */ |
| m_height, /* height */ |
| m_bit_depth, /* bit depth */ |
| bpp * m_width, /* bytes per row */ |
| colorSpace, /* color space */ |
| alphaInfo); |
| |
| CGColorSpaceRelease( colorSpace ); |
| if( !context ) |
| { |
| free( bitmap ); |
| return false; |
| } |
| |
| // Copy the image data into the bitmap region |
| CGRect rect = {{0,0},{m_width,m_height}}; |
| CGContextDrawImage( context, rect, imageRef ); |
| |
| uchar* bitdata = (uchar*)CGBitmapContextGetData( context ); |
| if( !bitdata ) |
| { |
| free( bitmap); |
| CGContextRelease( context ); |
| return false; |
| } |
| |
| // Move the bitmap (in RGB) into data (in BGR) |
| int bitmapIndex = 0; |
| |
| if( color == CV_LOAD_IMAGE_COLOR ) |
| { |
| uchar * base = data; |
| |
| for (int y = 0; y < m_height; y++) |
| { |
| uchar * line = base + y * step; |
| |
| for (int x = 0; x < m_width; x++) |
| { |
| // Blue channel |
| line[0] = bitdata[bitmapIndex + 2]; |
| // Green channel |
| line[1] = bitdata[bitmapIndex + 1]; |
| // Red channel |
| line[2] = bitdata[bitmapIndex + 0]; |
| |
| line += 3; |
| bitmapIndex += bpp; |
| } |
| } |
| } |
| else if( color == CV_LOAD_IMAGE_GRAYSCALE ) |
| { |
| for (int y = 0; y < m_height; y++) |
| memcpy (data + y * step, bitmap + y * m_width, m_width); |
| } |
| |
| free( bitmap ); |
| CGContextRelease( context ); |
| return true; |
| } |
| |
| |
| /////////////////////// GrFmtImageIOWriter /////////////////// |
| |
| GrFmtImageIOWriter::GrFmtImageIOWriter( const char* filename ) : GrFmtWriter( filename ) |
| { |
| // Nothing to do here |
| } |
| |
| |
| GrFmtImageIOWriter::~GrFmtImageIOWriter() |
| { |
| // Nothing to do here |
| } |
| |
| |
| static |
| CFStringRef FilenameToUTI( const char* filename ) |
| { |
| const char* ext = filename; |
| for(;;) |
| { |
| const char* temp = strchr( ext + 1, '.' ); |
| if( !temp ) break; |
| ext = temp; |
| } |
| |
| CFStringRef imageUTI = NULL; |
| |
| if( !strcmp(ext, ".bmp") || !strcmp(ext, ".dib") ) |
| imageUTI = CFSTR( "com.microsoft.bmp" ); |
| else if( !strcmp(ext, ".exr") ) |
| imageUTI = CFSTR( "com.ilm.openexr-image" ); |
| else if( !strcmp(ext, ".jpeg") || !strcmp(ext, ".jpg") || !strcmp(ext, ".jpe") ) |
| imageUTI = CFSTR( "public.jpeg" ); |
| else if( !strcmp(ext, ".jp2") ) |
| imageUTI = CFSTR( "public.jpeg-2000" ); |
| else if( !strcmp(ext, ".pdf") ) |
| imageUTI = CFSTR( "com.adobe.pdf" ); |
| else if( !strcmp(ext, ".png") ) |
| imageUTI = CFSTR( "public.png" ); |
| else if( !strcmp(ext, ".tiff") || !strcmp(ext, ".tif") ) |
| imageUTI = CFSTR( "public.tiff" ); |
| |
| return imageUTI; |
| } |
| |
| |
| bool GrFmtImageIOWriter::WriteImage( const uchar* data, int step, |
| int width, int height, int /*depth*/, int _channels ) |
| { |
| // Determine the appropriate UTI based on the filename extension |
| CFStringRef imageUTI = FilenameToUTI( m_filename ); |
| |
| // Determine the Bytes Per Pixel |
| int bpp = (_channels == 1) ? 1 : 4; |
| |
| // Write the data into a bitmap context |
| CGContextRef context; |
| CGColorSpaceRef colorSpace; |
| uchar* bitmapData = NULL; |
| |
| if( bpp == 1 ) |
| colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericGray ); |
| else if( bpp == 4 ) |
| colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ); |
| if( !colorSpace ) |
| return false; |
| |
| bitmapData = (uchar*)malloc( bpp * height * width ); |
| if( !bitmapData ) |
| { |
| CGColorSpaceRelease( colorSpace ); |
| return false; |
| } |
| |
| context = CGBitmapContextCreate( bitmapData, |
| width, |
| height, |
| 8, |
| bpp * width, |
| colorSpace, |
| (bpp == 1) ? kCGImageAlphaNone : |
| kCGImageAlphaNoneSkipLast ); |
| CGColorSpaceRelease( colorSpace ); |
| if( !context ) |
| { |
| free( bitmapData ); |
| return false; |
| } |
| |
| // Copy pixel information from data into bitmapData |
| if (bpp == 4) |
| { |
| int bitmapIndex = 0; |
| const uchar * base = data; |
| |
| for (int y = 0; y < height; y++) |
| { |
| const uchar * line = base + y * step; |
| |
| for (int x = 0; x < width; x++) |
| { |
| // Blue channel |
| bitmapData[bitmapIndex + 2] = line[0]; |
| // Green channel |
| bitmapData[bitmapIndex + 1] = line[1]; |
| // Red channel |
| bitmapData[bitmapIndex + 0] = line[2]; |
| |
| line += 3; |
| bitmapIndex += bpp; |
| } |
| } |
| } |
| else if (bpp == 1) |
| { |
| for (int y = 0; y < height; y++) |
| memcpy (bitmapData + y * width, data + y * step, width); |
| } |
| |
| // Turn the bitmap context into an imageRef |
| CGImageRef imageRef = CGBitmapContextCreateImage( context ); |
| CGContextRelease( context ); |
| if( !imageRef ) |
| { |
| free( bitmapData ); |
| return false; |
| } |
| |
| // Write the imageRef to a file based on the UTI |
| CFURLRef imageURLRef = CFURLCreateFromFileSystemRepresentation( NULL, |
| (const UInt8*)m_filename, |
| strlen(m_filename), |
| false ); |
| if( !imageURLRef ) |
| { |
| CGImageRelease( imageRef ); |
| free( bitmapData ); |
| return false; |
| } |
| |
| CGImageDestinationRef destRef = CGImageDestinationCreateWithURL( imageURLRef, |
| imageUTI, |
| 1, |
| NULL); |
| CFRelease( imageURLRef ); |
| if( !destRef ) |
| { |
| CGImageRelease( imageRef ); |
| free( bitmapData ); |
| std::cerr << "!destRef" << std::endl << std::flush; |
| return false; |
| } |
| |
| CGImageDestinationAddImage(destRef, imageRef, NULL); |
| if( !CGImageDestinationFinalize(destRef) ) |
| { |
| std::cerr << "Finalize failed" << std::endl << std::flush; |
| return false; |
| } |
| |
| CFRelease( destRef ); |
| CGImageRelease( imageRef ); |
| free( bitmapData ); |
| |
| return true; |
| } |
| |
| #endif /* HAVE_IMAGEIO */ |