1/*
2 *  grfmt_imageio.cpp
3 *
4 *
5 *  Created by Morgan Conbere on 5/17/07.
6 *
7 */
8
9#include "_highgui.h"
10
11#ifdef HAVE_IMAGEIO
12
13#include "grfmt_imageio.h"
14#include <iostream>
15using namespace std;
16
17// ImageIO filter factory
18
19GrFmtImageIO::GrFmtImageIO()
20{
21    m_sign_len = 0;
22    m_signature = NULL;
23    m_description = "Apple ImageIO (*.bmp;*.dib;*.exr;*.jpeg;*.jpg;*.jpe;*.jp2;*.pdf;*.png;*.tiff;*.tif)";
24}
25
26
27GrFmtImageIO::~GrFmtImageIO()
28{
29}
30
31
32bool  GrFmtImageIO::CheckFile( const char* filename )
33{
34    if( !filename ) return false;
35
36    // If a CFImageRef can be retrieved from an image file, it is
37    // readable by ImageIO.  Effectively this is using ImageIO
38    // to check the signatures and determine the file format for us.
39    CFURLRef imageURLRef = CFURLCreateFromFileSystemRepresentation( NULL,
40                                                                    (const UInt8*)filename,
41                                                                    strlen( filename ),
42                                                                    false );
43    if( !imageURLRef ) return false;
44
45    CGImageSourceRef sourceRef = CGImageSourceCreateWithURL( imageURLRef, NULL );
46    CFRelease( imageURLRef );
47    if( !sourceRef ) return false;
48
49    CGImageRef imageRef = CGImageSourceCreateImageAtIndex( sourceRef, 0, NULL );
50    CFRelease( sourceRef );
51    if( !imageRef ) return false;
52
53    return true;
54}
55
56
57GrFmtReader* GrFmtImageIO::NewReader( const char* filename )
58{
59    return new GrFmtImageIOReader( filename );
60}
61
62
63GrFmtWriter* GrFmtImageIO::NewWriter( const char* filename )
64{
65    return new GrFmtImageIOWriter( filename );
66}
67
68
69/////////////////////// GrFmtImageIOReader ///////////////////
70
71GrFmtImageIOReader::GrFmtImageIOReader( const char* filename ) : GrFmtReader( filename )
72{
73    // Nothing to do here
74}
75
76
77GrFmtImageIOReader::~GrFmtImageIOReader()
78{
79    Close();
80}
81
82
83void  GrFmtImageIOReader::Close()
84{
85    CGImageRelease( imageRef );
86
87    GrFmtReader::Close();
88}
89
90
91bool  GrFmtImageIOReader::ReadHeader()
92{
93    CFURLRef         imageURLRef;
94    CGImageSourceRef sourceRef;
95    imageRef = NULL;
96
97    imageURLRef = CFURLCreateFromFileSystemRepresentation( NULL,
98                                                           (const UInt8*)m_filename,
99                                                           strlen(m_filename),
100                                                           false );
101
102    sourceRef = CGImageSourceCreateWithURL( imageURLRef, NULL );
103    CFRelease( imageURLRef );
104    if ( !sourceRef )
105        return false;
106
107    imageRef = CGImageSourceCreateImageAtIndex( sourceRef, 0, NULL );
108    CFRelease( sourceRef );
109    if( !imageRef )
110        return false;
111
112    m_width = CGImageGetWidth( imageRef );
113    m_height = CGImageGetHeight( imageRef );
114
115    CGColorSpaceRef colorSpace = CGImageGetColorSpace( imageRef );
116    if( !colorSpace )
117        return false;
118
119    m_iscolor = ( CGColorSpaceGetNumberOfComponents( colorSpace ) > 1 );
120
121    return true;
122}
123
124
125bool  GrFmtImageIOReader::ReadData( uchar* data, int step, int color )
126{
127    int bpp; // Bytes per pixel
128
129    // Set color to either CV_IMAGE_LOAD_COLOR or CV_IMAGE_LOAD_GRAYSCALE if unchanged
130    color = color > 0 || ( m_iscolor && color < 0 );
131
132    // Get Height, Width, and color information
133    if( !ReadHeader() )
134        return false;
135
136    CGContextRef     context = NULL; // The bitmap context
137    CGColorSpaceRef  colorSpace = NULL;
138    uchar*           bitmap = NULL;
139    CGImageAlphaInfo alphaInfo;
140
141    // CoreGraphics will take care of converting to grayscale and back as long as the
142    // appropriate colorspace is set
143    if( color == CV_LOAD_IMAGE_GRAYSCALE )
144    {
145        colorSpace = CGColorSpaceCreateDeviceGray();
146        bpp = 1;
147        alphaInfo = kCGImageAlphaNone;
148    }
149    else if( color == CV_LOAD_IMAGE_COLOR )
150    {
151        colorSpace = CGColorSpaceCreateDeviceRGB();
152        bpp = 4; /* CG only has 8 and 32 bit color spaces, so we waste a byte */
153        alphaInfo = kCGImageAlphaNoneSkipLast;
154    }
155    if( !colorSpace )
156        return false;
157
158    bitmap = (uchar*)malloc( bpp * m_height * m_width );
159    if( !bitmap )
160    {
161        CGColorSpaceRelease( colorSpace );
162        return false;
163    }
164
165    context = CGBitmapContextCreate( (void *)bitmap,
166                                     m_width,        /* width */
167                                     m_height,       /* height */
168                                     m_bit_depth,    /* bit depth */
169                                     bpp * m_width,  /* bytes per row */
170                                     colorSpace,     /* color space */
171                                     alphaInfo);
172
173    CGColorSpaceRelease( colorSpace );
174    if( !context )
175    {
176        free( bitmap );
177        return false;
178    }
179
180    // Copy the image data into the bitmap region
181    CGRect rect = {{0,0},{m_width,m_height}};
182    CGContextDrawImage( context, rect, imageRef );
183
184    uchar* bitdata = (uchar*)CGBitmapContextGetData( context );
185    if( !bitdata )
186    {
187        free( bitmap);
188        CGContextRelease( context );
189        return false;
190    }
191
192    // Move the bitmap (in RGB) into data (in BGR)
193    int bitmapIndex = 0;
194
195    if( color == CV_LOAD_IMAGE_COLOR )
196	{
197		uchar * base = data;
198
199		for (int y = 0; y < m_height; y++)
200		{
201			uchar * line = base + y * step;
202
203		    for (int x = 0; x < m_width; x++)
204		    {
205				// Blue channel
206				line[0] = bitdata[bitmapIndex + 2];
207				// Green channel
208				line[1] = bitdata[bitmapIndex + 1];
209				// Red channel
210				line[2] = bitdata[bitmapIndex + 0];
211
212				line        += 3;
213				bitmapIndex += bpp;
214			}
215		}
216    }
217    else if( color == CV_LOAD_IMAGE_GRAYSCALE )
218    {
219		for (int y = 0; y < m_height; y++)
220			memcpy (data + y * step, bitmap + y * m_width, m_width);
221    }
222
223    free( bitmap );
224    CGContextRelease( context );
225    return true;
226}
227
228
229/////////////////////// GrFmtImageIOWriter ///////////////////
230
231GrFmtImageIOWriter::GrFmtImageIOWriter( const char* filename ) : GrFmtWriter( filename )
232{
233    // Nothing to do here
234}
235
236
237GrFmtImageIOWriter::~GrFmtImageIOWriter()
238{
239    // Nothing to do here
240}
241
242
243static
244CFStringRef  FilenameToUTI( const char* filename )
245{
246    const char* ext = filename;
247    for(;;)
248    {
249        const char* temp = strchr( ext + 1, '.' );
250        if( !temp ) break;
251        ext = temp;
252    }
253
254    CFStringRef imageUTI = NULL;
255
256    if( !strcmp(ext, ".bmp") || !strcmp(ext, ".dib") )
257        imageUTI = CFSTR( "com.microsoft.bmp" );
258    else if( !strcmp(ext, ".exr") )
259        imageUTI = CFSTR( "com.ilm.openexr-image" );
260    else if( !strcmp(ext, ".jpeg") || !strcmp(ext, ".jpg") || !strcmp(ext, ".jpe") )
261        imageUTI = CFSTR( "public.jpeg" );
262    else if( !strcmp(ext, ".jp2") )
263        imageUTI = CFSTR( "public.jpeg-2000" );
264    else if( !strcmp(ext, ".pdf") )
265        imageUTI = CFSTR( "com.adobe.pdf" );
266    else if( !strcmp(ext, ".png") )
267        imageUTI = CFSTR( "public.png" );
268    else if( !strcmp(ext, ".tiff") || !strcmp(ext, ".tif") )
269        imageUTI = CFSTR( "public.tiff" );
270
271    return imageUTI;
272}
273
274
275bool  GrFmtImageIOWriter::WriteImage( const uchar* data, int step,
276                                      int width, int height, int /*depth*/, int _channels )
277{
278    // Determine the appropriate UTI based on the filename extension
279    CFStringRef imageUTI = FilenameToUTI( m_filename );
280
281    // Determine the Bytes Per Pixel
282    int bpp = (_channels == 1) ? 1 : 4;
283
284    // Write the data into a bitmap context
285    CGContextRef context;
286    CGColorSpaceRef colorSpace;
287    uchar* bitmapData = NULL;
288
289    if( bpp == 1 )
290        colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericGray );
291    else if( bpp == 4 )
292        colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
293    if( !colorSpace )
294        return false;
295
296    bitmapData = (uchar*)malloc( bpp * height * width );
297    if( !bitmapData )
298    {
299        CGColorSpaceRelease( colorSpace );
300        return false;
301    }
302
303    context = CGBitmapContextCreate( bitmapData,
304                                     width,
305                                     height,
306                                     8,
307                                     bpp * width,
308                                     colorSpace,
309                                     (bpp == 1) ? kCGImageAlphaNone :
310                                     kCGImageAlphaNoneSkipLast );
311    CGColorSpaceRelease( colorSpace );
312    if( !context )
313    {
314        free( bitmapData );
315        return false;
316    }
317
318    // Copy pixel information from data into bitmapData
319    if (bpp == 4)
320    {
321        int           bitmapIndex = 0;
322		const uchar * base        = data;
323
324		for (int y = 0; y < height; y++)
325		{
326			const uchar * line = base + y * step;
327
328		    for (int x = 0; x < width; x++)
329		    {
330				// Blue channel
331                bitmapData[bitmapIndex + 2] = line[0];
332				// Green channel
333				bitmapData[bitmapIndex + 1] = line[1];
334				// Red channel
335				bitmapData[bitmapIndex + 0] = line[2];
336
337				line        += 3;
338				bitmapIndex += bpp;
339			}
340		}
341    }
342    else if (bpp == 1)
343    {
344		for (int y = 0; y < height; y++)
345			memcpy (bitmapData + y * width, data + y * step, width);
346    }
347
348    // Turn the bitmap context into an imageRef
349    CGImageRef imageRef = CGBitmapContextCreateImage( context );
350    CGContextRelease( context );
351    if( !imageRef )
352    {
353        free( bitmapData );
354        return false;
355    }
356
357    // Write the imageRef to a file based on the UTI
358    CFURLRef imageURLRef = CFURLCreateFromFileSystemRepresentation( NULL,
359                                                                    (const UInt8*)m_filename,
360                                                                    strlen(m_filename),
361                                                                    false );
362    if( !imageURLRef )
363    {
364        CGImageRelease( imageRef );
365        free( bitmapData );
366        return false;
367    }
368
369    CGImageDestinationRef destRef = CGImageDestinationCreateWithURL( imageURLRef,
370                                                                     imageUTI,
371                                                                     1,
372                                                                     NULL);
373    CFRelease( imageURLRef );
374    if( !destRef )
375    {
376        CGImageRelease( imageRef );
377        free( bitmapData );
378        std::cerr << "!destRef" << std::endl << std::flush;
379        return false;
380    }
381
382    CGImageDestinationAddImage(destRef, imageRef, NULL);
383    if( !CGImageDestinationFinalize(destRef) )
384    {
385        std::cerr << "Finalize failed" << std::endl << std::flush;
386        return false;
387    }
388
389    CFRelease( destRef );
390    CGImageRelease( imageRef );
391    free( bitmapData );
392
393    return true;
394}
395
396#endif /* HAVE_IMAGEIO */
397