Mac / COCOA : Imbuf
[blender.git] / source / blender / imbuf / intern / imbuf_cocoa.m
1 /*
2  * imbuf_coca.m
3  *
4  * $Id: imbuf_cocoa.m 17958 2008-12-19 19:11:02Z blendix $
5  * 
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * Contributor(s): Damien Plisson 10/2009
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /**
28  * Provides image file loading and saving for Blender, via Cocoa.
29  *
30  */
31
32 #include <string.h>
33 #import <Cocoa/Cocoa.h>
34
35 #include "imbuf.h"
36 #include "imbuf_patch.h"
37
38 #include "IMB_cocoa.h"
39
40 #include "BKE_global.h"
41 #include "BKE_colortools.h"
42
43 #include "IMB_imbuf_types.h"
44 #include "IMB_imbuf.h"
45
46 #include "IMB_allocimbuf.h"
47
48
49
50 #pragma mark load/save functions
51
52 /**
53  * Loads an image from the supplied buffer
54  *
55  * Loads any Core Graphics supported type
56  * Currently is : TIFF, BMP, JPEG, GIF, PNG, DIB, ICO, and various RAW formats
57  *
58  * @param mem:   Memory containing the bitmap image
59  * @param size:  Size of the mem buffer.
60  * @param flags: If flags has IB_test set then the file is not actually loaded,
61  *                but all other operations take place.
62  *
63  * @return: A newly allocated ImBuf structure if successful, otherwise NULL.
64  */
65 struct ImBuf *imb_cocoaLoadImage(unsigned char *mem, int size, int flags)
66 {
67         struct ImBuf *ibuf = NULL;
68         uint32 width, height;
69         uchar *rasterRGB = NULL;
70         uchar *rasterRGBA = NULL;
71         uchar *toIBuf = NULL;
72         int x, y, to_i, from_i;
73         NSData *data;
74         NSBitmapImageRep *bitmapImage;
75         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA;
76         NSAutoreleasePool *pool;
77         
78         pool = [[NSAutoreleasePool alloc] init];
79         
80         data = [NSData dataWithBytes:mem length:size];
81         bitmapImage = [[NSBitmapImageRep alloc] initWithData:data];
82
83         if (!bitmapImage) {
84                 fprintf(stderr, "imb_cocoaLoadImage: error loading image\n");
85                 [pool drain];
86                 return NULL;
87         }
88         
89         width = [bitmapImage pixelsWide];
90         height = [bitmapImage pixelsHigh];
91         
92         /* allocate the image buffer */
93         ibuf = IMB_allocImBuf(width, height, 32/*RGBA*/, 0, 0);
94         if (!ibuf) {
95                 fprintf(stderr, 
96                         "imb_cocoaLoadImage: could not allocate memory for the " \
97                         "image.\n");
98                 [bitmapImage release];
99                 [pool drain];
100                 return NULL;
101         }
102
103         /* read in the image data */
104         if (!(flags & IB_test)) {
105
106                 /* allocate memory for the ibuf->rect */
107                 imb_addrectImBuf(ibuf);
108
109                 /* Convert the image in a RGBA 32bit format */
110                 /* As Core Graphics does not support contextes with non premutliplied alpha,
111                  we need to get alpha key values in a separate batch */
112                 
113                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
114                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
115                                                                                                                                           pixelsWide:width 
116                                                                                                                                           pixelsHigh:height
117                                                                                                                                    bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
118                                                                                                                                   colorSpaceName:NSCalibratedRGBColorSpace 
119                                                                                                                                         bitmapFormat:0
120                                                                                                                                          bytesPerRow:4*width
121                                                                                                                                         bitsPerPixel:32/*RGB format padded to 32bits*/];
122                                 
123                 [NSGraphicsContext saveGraphicsState];
124                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
125                 [bitmapImage draw];
126                 [NSGraphicsContext restoreGraphicsState];
127                 
128                 rasterRGB = (uchar*)[blBitmapFormatImageRGB bitmapData];
129                 if (rasterRGB == NULL) {
130                         [bitmapImage release];
131                         [blBitmapFormatImageRGB release];
132                         [pool drain];
133                         return NULL;
134                 }
135
136                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
137                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
138                                                                                                                                                   pixelsWide:width
139                                                                                                                                                   pixelsHigh:height
140                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
141                                                                                                                                           colorSpaceName:NSCalibratedRGBColorSpace
142                                                                                                                                                 bitmapFormat:0
143                                                                                                                                                  bytesPerRow:4*width
144                                                                                                                                                 bitsPerPixel:32/* RGBA */];
145                 
146                 [NSGraphicsContext saveGraphicsState];
147                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
148                 [bitmapImage draw];
149                 [NSGraphicsContext restoreGraphicsState];
150                 
151                 rasterRGBA = (uchar*)[blBitmapFormatImageRGBA bitmapData];
152                 if (rasterRGBA == NULL) {
153                         [bitmapImage release];
154                         [blBitmapFormatImageRGB release];
155                         [blBitmapFormatImageRGBA release];
156                         [pool drain];
157                         return NULL;
158                 }
159                 
160                 /*Copy the image to ibuf, flipping it vertically*/
161                 toIBuf = (uchar*)ibuf->rect;
162                 for (x = 0; x < width; x++) {
163                         for (y = 0; y < height; y++) {
164                                 to_i = (height-y-1)*width + x;
165                                 from_i = y*width + x;
166
167                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
168                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
169                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
170                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
171                         }
172                 }
173
174                 [blBitmapFormatImageRGB release];
175                 [blBitmapFormatImageRGBA release];
176         }
177
178         /* release the cocoa objects */
179         [bitmapImage release];
180         [pool drain];
181
182         if (ENDIAN_ORDER == B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf);
183
184         /* return successfully */
185         return (ibuf);
186 }
187
188 /**
189  * Saves an image to a file.
190  *
191  * ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA 
192  * respectively) are accepted, and interpreted correctly. 
193  *
194  * Accepted formats: TIFF, GIF, BMP, PNG, JPEG, JPEG2000
195  *
196  * @param ibuf:  Image buffer.
197  * @param name:  Name of the image file to create.
198  * @param flags: Currently largely ignored.
199  *
200  * @return: 1 if the function is successful, 0 on failure.
201  */
202
203 #define FTOUSHORT(val) ((val >= 1.0f-0.5f/65535)? 65535: (val <= 0.0f)? 0: (unsigned short)(val*65535.0f + 0.5f))
204
205 short imb_cocoaSaveImage(struct ImBuf *ibuf, char *name, int flags)
206 {
207         uint16 samplesperpixel, bitspersample;
208         unsigned char *from = NULL, *to = NULL;
209         unsigned short *to16 = NULL;
210         float *fromf = NULL;
211         int x, y, from_i, to_i, i;
212         int success;
213         BOOL hasAlpha;
214         NSString* colorSpace;
215         NSBitmapImageRep *blBitmapFormatImage;
216         NSData *dataToWrite;
217         NSDictionary *imageProperties;
218         
219         NSAutoreleasePool *pool;
220         
221         if (!ibuf) return FALSE;
222         if (!name) return FALSE;
223         
224         /* check for a valid number of bytes per pixel.  Like the PNG writer,
225          * the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding
226          * to gray, RGB, RGBA respectively. */
227         samplesperpixel = (uint16)((ibuf->depth + 7) >> 3);
228         switch (samplesperpixel) {
229                 case 4: /*RGBA type*/
230                         hasAlpha = YES;
231                         colorSpace = NSCalibratedRGBColorSpace;
232                         break;
233                 case 3: /*RGB type*/
234                         hasAlpha = NO;
235                         colorSpace = NSCalibratedRGBColorSpace;
236                         break;
237                 case 1:
238                         hasAlpha = NO;
239                         colorSpace = NSCalibratedWhiteColorSpace;
240                         break;
241                 default:
242                         fprintf(stderr,
243                                         "imb_cocoaSaveImage: unsupported number of bytes per " 
244                                         "pixel: %d\n", samplesperpixel);
245                         return (0);
246         }
247
248         if((ibuf->ftype & TIF_16BIT) && ibuf->rect_float)
249                 bitspersample = 16;
250         else
251                 bitspersample = 8;
252
253         pool = [[NSAutoreleasePool alloc] init];
254         
255         /* Create bitmap image rep in blender format */
256         blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
257                                                                                                                                   pixelsWide:ibuf->x 
258                                                                                                                                   pixelsHigh:ibuf->y
259                                                                                                                            bitsPerSample:bitspersample samplesPerPixel:samplesperpixel hasAlpha:hasAlpha isPlanar:NO
260                                                                                                                           colorSpaceName:colorSpace 
261                                                                                                                                 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
262                                                                                                                                  bytesPerRow:(ibuf->x*bitspersample*samplesperpixel/8)
263                                                                                                                                 bitsPerPixel:(bitspersample*samplesperpixel)];
264         if (!blBitmapFormatImage) {
265                 [pool drain];
266                 return FALSE;
267         }
268         
269         /* setup pointers */
270         if(bitspersample == 16) {
271                 fromf = ibuf->rect_float;
272                 to16   = (unsigned short*)[blBitmapFormatImage bitmapData];
273         }
274         else {
275                 from = (unsigned char*)ibuf->rect;
276                 to   = (unsigned char*)[blBitmapFormatImage bitmapData];
277         }
278
279         /* copy pixel data.  While copying, we flip the image vertically. */
280         for (x = 0; x < ibuf->x; x++) {
281                 for (y = 0; y < ibuf->y; y++) {
282                         from_i = 4*(y*ibuf->x+x);
283                         to_i   = samplesperpixel*((ibuf->y-y-1)*ibuf->x+x);
284                         
285                         if(bitspersample == 16) {
286                                 if (ibuf->profile == IB_PROFILE_SRGB) {
287                                         switch (samplesperpixel) {
288                                                 case 4 /*RGBA*/:
289                                                         to16[to_i] = FTOUSHORT(linearrgb_to_srgb(fromf[from_i]));
290                                                         to16[to_i+1] = FTOUSHORT(linearrgb_to_srgb(fromf[from_i+1]));
291                                                         to16[to_i+2] = FTOUSHORT(linearrgb_to_srgb(fromf[from_i+2]));
292                                                         to16[to_i+3] = FTOUSHORT(fromf[from_i+3]);
293                                                         break;
294                                                 case 3 /*RGB*/:
295                                                         to16[to_i] = FTOUSHORT(linearrgb_to_srgb(fromf[from_i]));
296                                                         to16[to_i+1] = FTOUSHORT(linearrgb_to_srgb(fromf[from_i+1]));
297                                                         to16[to_i+2] = FTOUSHORT(linearrgb_to_srgb(fromf[from_i+2]));
298                                                         break;
299                                                 case 1 /*BW*/:
300                                                         to16[to_i] = FTOUSHORT(linearrgb_to_srgb(fromf[from_i]));
301                                                         break;
302                                         }
303                                 } 
304                                 else {
305                                         for (i = 0; i < samplesperpixel; i++, to_i++, from_i++)
306                                                         to16[to_i] = FTOUSHORT(fromf[from_i]);
307                                 }
308                         }
309                         else {
310                                 /* 8bits per sample*/
311                                 for (i = 0; i < samplesperpixel; i++, to_i++, from_i++)
312                                         to[to_i] = from[from_i];
313                         }
314                 }
315         }
316         
317         /* generate file data */
318         if (IS_tiff(ibuf)) {
319                 dataToWrite = [blBitmapFormatImage TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0];
320         }
321         else if (IS_png(ibuf)) {
322                 imageProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false], NSImageInterlaced,
323                                                    nil];
324                 dataToWrite = [blBitmapFormatImage representationUsingType:NSPNGFileType properties:imageProperties];
325         }
326         else if (IS_bmp(ibuf)) {
327                 dataToWrite = [blBitmapFormatImage representationUsingType:NSBMPFileType properties:nil];
328         }
329         else {/* JPEG by default */
330                 int quality;
331                 
332                 quality = ibuf->ftype & 0xff;
333                 if (quality <= 0) quality = 90; /* Standard quality if wrong supplied*/
334                 if (quality > 100) quality = 100;
335                 
336                 imageProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:quality], NSImageCompressionFactor,
337                                                    [NSNumber numberWithBool:true], NSImageProgressive, 
338                                                    nil];
339                 dataToWrite = [blBitmapFormatImage representationUsingType:NSJPEGFileType properties:imageProperties];
340         }
341
342         /* Write the file */
343         success = [dataToWrite writeToFile:[NSString stringWithCString:name encoding:NSISOLatin1StringEncoding]
344                                   atomically:YES];
345
346         [blBitmapFormatImage release];
347         [pool drain];
348         
349         return success;
350 }
351
352 #pragma mark format checking functions
353
354 /* Currently, only tiff format is handled, so need to include here function that was previously in tiff.c */
355
356 /**
357  * Checks whether a given memory buffer contains a TIFF file.
358  *
359  * FIXME: Possible memory leak if mem is less than IMB_TIFF_NCB bytes long.
360  *        However, changing this will require up-stream modifications.
361  *
362  * This method uses the format identifiers from:
363  *     http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-9.html
364  * The first four bytes of big-endian and little-endian TIFF files
365  * respectively are (hex):
366  *      4d 4d 00 2a
367  *      49 49 2a 00
368  * Note that TIFF files on *any* platform can be either big- or little-endian;
369  * it's not platform-specific.
370  *
371  * AFAICT, libtiff doesn't provide a method to do this automatically, and
372  * hence my manual comparison. - Jonathan Merritt (lancelet) 4th Sept 2005.
373  */
374 #define IMB_TIFF_NCB 4          /* number of comparison bytes used */
375 int imb_is_a_tiff(void *mem)
376 {
377         char big_endian[IMB_TIFF_NCB] = { 0x4d, 0x4d, 0x00, 0x2a };
378         char lil_endian[IMB_TIFF_NCB] = { 0x49, 0x49, 0x2a, 0x00 };
379         
380         return ( (memcmp(big_endian, mem, IMB_TIFF_NCB) == 0) ||
381                         (memcmp(lil_endian, mem, IMB_TIFF_NCB) == 0) );
382 }