Adapt OSX code for IMB_allocImBuf param changes.
[blender-staging.git] / source / blender / quicktime / apple / qtkit_import.m
1 /**
2  * $Id$
3  *
4  * qtkit_import.m
5  *
6  * Code to use Quicktime to load images/movies as texture.
7  *
8  * ***** BEGIN GPL LICENSE BLOCK *****
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  *
24  * The Original Code is written by Rob Haarsma (phase)
25  *
26  * Contributor(s): Stefan Gartner (sgefant)
27  *                                 Damien Plisson 11/2009
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31 #ifdef WITH_QUICKTIME
32
33 #include "MEM_guardedalloc.h"
34
35 #include "IMB_anim.h"
36 #include "BLO_sys_types.h"
37 #include "BKE_global.h"
38 #include "BLI_dynstr.h"
39
40 #import <Cocoa/Cocoa.h>
41 #import <QTKit/QTKit.h>
42
43 #include "quicktime_import.h"
44 #include "quicktime_export.h"
45
46 // quicktime structure definition
47 // this structure is part of the anim struct
48
49 typedef struct _QuicktimeMovie {
50         QTMovie *movie;
51         QTMedia *media;
52         
53         long durationTime;
54         long durationScale;
55         long framecount;
56         
57
58         ImBuf *ibuf;
59         
60         long previousPosition;
61         
62 } QuicktimeMovie;
63
64
65 #define QTIME_DEBUG 0
66
67
68 void quicktime_init(void)
69 {
70                 G.have_quicktime = TRUE;
71 }
72
73
74 void quicktime_exit(void)
75 {
76         if(G.have_quicktime) {
77                 free_qtcomponentdata();
78         }
79 }
80
81
82 int anim_is_quicktime (char *name)
83 {
84         NSAutoreleasePool *pool;
85         
86         // dont let quicktime movie import handle these
87         if( BLI_testextensie(name, ".swf") ||
88                 BLI_testextensie(name, ".txt") ||
89                 BLI_testextensie(name, ".mpg") ||
90                 BLI_testextensie(name, ".avi") ||       // wouldnt be appropriate ;)
91                 BLI_testextensie(name, ".tga") ||
92                 BLI_testextensie(name, ".png") ||
93                 BLI_testextensie(name, ".bmp") ||
94                 BLI_testextensie(name, ".jpg") ||
95                 BLI_testextensie(name, ".wav") ||
96                 BLI_testextensie(name, ".zip") ||
97                 BLI_testextensie(name, ".mp3")) return 0;
98
99         if(QTIME_DEBUG) printf("qt: checking as movie: %s\n", name);
100
101         pool = [[NSAutoreleasePool alloc] init];
102         
103         if([QTMovie canInitWithFile:[NSString stringWithCString:name 
104                                                                  encoding:[NSString defaultCStringEncoding]]])
105         {
106                 [pool drain];
107                 return true;
108         }
109         else
110         {
111                 [pool drain];
112                 return false;
113         }
114 }
115
116
117 void free_anim_quicktime (struct anim *anim) {
118         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
119         
120         if (anim == NULL) return;
121         if (anim->qtime == NULL) return;
122
123         if(anim->qtime->ibuf)
124                 IMB_freeImBuf(anim->qtime->ibuf);
125
126         [anim->qtime->media release];
127         [anim->qtime->movie release];
128
129         [QTMovie exitQTKitOnThread];
130
131         if(anim->qtime) MEM_freeN (anim->qtime);
132
133         anim->qtime = NULL;
134
135         anim->duration = 0;
136
137         [pool drain];
138 }
139
140 static ImBuf * nsImageToiBuf(NSImage *sourceImage, int width, int height)
141 {
142         ImBuf *ibuf = NULL;
143         uchar *rasterRGB = NULL;
144         uchar *rasterRGBA = NULL;
145         uchar *toIBuf = NULL;
146         int x, y, to_i, from_i;
147         NSSize bitmapSize;
148         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
149         NSEnumerator *enumerator;
150         NSImageRep *representation;
151         
152         ibuf = IMB_allocImBuf (width, height, 32, IB_rect);
153         if (!ibuf) {
154                 if(QTIME_DEBUG) printf("quicktime_import: could not allocate memory for the " \
155                                 "image.\n");
156                 return NULL;
157         }
158         
159         /*Get the bitmap of the image*/
160         enumerator = [[sourceImage representations] objectEnumerator];
161         while ((representation = [enumerator nextObject])) {
162         if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
163             bitmapImage = (NSBitmapImageRep *)representation;
164                         break;
165         }
166     }
167         if (bitmapImage == nil) return NULL;
168
169         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
170                 && ![bitmapImage isPlanar]) {
171                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
172                 toIBuf = (uchar*)ibuf->rect;
173                 rasterRGB = (uchar*)[bitmapImage bitmapData];
174                 for (y = 0; y < height; y++) {
175                         to_i = (height-y-1)*width;
176                         from_i = y*width;
177                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*width);
178                 }
179         }
180         else {
181
182                 bitmapSize.width = width;
183                 bitmapSize.height = height;
184                 
185                 /* Tell cocoa image resolution is same as current system one */
186                 [bitmapImage setSize:bitmapSize];
187                 
188                 /* Convert the image in a RGBA 32bit format */
189                 /* As Core Graphics does not support contextes with non premutliplied alpha,
190                  we need to get alpha key values in a separate batch */
191                 
192                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
193                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
194                                                                                                                                                  pixelsWide:width 
195                                                                                                                                                  pixelsHigh:height
196                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
197                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
198                                                                                                                                            bitmapFormat:0
199                                                                                                                                                 bytesPerRow:4*width
200                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
201                 
202                 [NSGraphicsContext saveGraphicsState];
203                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
204                 [bitmapImage draw];
205                 [NSGraphicsContext restoreGraphicsState];
206                 
207                 rasterRGB = (uchar*)[blBitmapFormatImageRGB bitmapData];
208                 if (rasterRGB == NULL) {
209                         [blBitmapFormatImageRGB release];
210                         return NULL;
211                 }
212                 
213                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
214                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
215                                                                                                                                                   pixelsWide:width
216                                                                                                                                                   pixelsHigh:height
217                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
218                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
219                                                                                                                                                 bitmapFormat:0
220                                                                                                                                                  bytesPerRow:4*width
221                                                                                                                                                 bitsPerPixel:32/* RGBA */];
222                 
223                 [NSGraphicsContext saveGraphicsState];
224                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
225                 [bitmapImage draw];
226                 [NSGraphicsContext restoreGraphicsState];
227                 
228                 rasterRGBA = (uchar*)[blBitmapFormatImageRGBA bitmapData];
229                 if (rasterRGBA == NULL) {
230                         [blBitmapFormatImageRGB release];
231                         [blBitmapFormatImageRGBA release];
232                         return NULL;
233                 }
234
235                 /*Copy the image to ibuf, flipping it vertically*/
236                 toIBuf = (uchar*)ibuf->rect;
237                 for (y = 0; y < height; y++) {
238                         for (x = 0; x < width; x++) {
239                                 to_i = (height-y-1)*width + x;
240                                 from_i = y*width + x;
241                                 
242                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
243                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
244                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
245                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
246                         }
247                 }
248
249                 [blBitmapFormatImageRGB release];
250                 [blBitmapFormatImageRGBA release];
251         }
252         
253         return ibuf;
254 }
255
256 ImBuf * qtime_fetchibuf (struct anim *anim, int position)
257 {
258         NSImage *frameImage;
259         QTTime time;
260         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
261         ImBuf *ibuf;
262         
263         if (anim == NULL) {
264                 return (NULL);
265         }
266
267         if (position == anim->qtime->previousPosition+1) { //Optimize sequential read
268                 [anim->qtime->movie stepForward];
269                 frameImage = [anim->qtime->movie currentFrameImage];
270                 anim->qtime->previousPosition++;
271         }
272         else {
273                 time.timeScale = anim->qtime->durationScale;
274                 time.timeValue = (anim->qtime->durationTime * position) / anim->qtime->framecount;
275         
276                 [anim->qtime->movie setCurrentTime:time];
277                 frameImage = [anim->qtime->movie currentFrameImage]; 
278                 
279                 anim->qtime->previousPosition = position;
280         }
281                 
282         if (frameImage == nil) {
283                 if(QTIME_DEBUG) printf ("Error reading frame from Quicktime");
284                 [pool drain];
285                 return NULL;
286         }
287
288         ibuf = nsImageToiBuf(frameImage,anim->x, anim->y);
289         [pool drain];
290         
291         ibuf->profile = IB_PROFILE_SRGB;
292         return ibuf;
293 }
294
295
296 int startquicktime (struct anim *anim)
297 {
298         NSAutoreleasePool *pool;
299         NSArray* videoTracks;
300         NSSize frameSize;
301         QTTime qtTimeDuration;
302         NSDictionary *attributes;
303         
304         anim->qtime = MEM_callocN (sizeof(QuicktimeMovie),"animqt");
305
306         if (anim->qtime == NULL) {
307                 if(QTIME_DEBUG) printf("Can't alloc qtime: %s\n", anim->name);
308                 return -1;
309         }
310
311         pool = [[NSAutoreleasePool alloc] init];
312         
313         [QTMovie enterQTKitOnThread];           
314
315         attributes = [NSDictionary dictionaryWithObjectsAndKeys:
316                                   [NSString stringWithCString:anim->name 
317                                                                          encoding:[NSString defaultCStringEncoding]], QTMovieFileNameAttribute,
318                                  [NSNumber numberWithBool:NO], QTMovieEditableAttribute,
319                                  nil];
320         
321         anim->qtime->movie = [QTMovie movieWithAttributes:attributes error:NULL];
322         
323         if (!anim->qtime->movie) {
324                 if(QTIME_DEBUG) printf("qt: bad movie %s\n", anim->name);
325                 MEM_freeN(anim->qtime);
326                 if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
327                 [QTMovie exitQTKitOnThread];
328                 [pool drain];
329                 return -1;
330         }
331         [anim->qtime->movie retain];
332
333         // sets Media and Track!
334         
335         videoTracks = [anim->qtime->movie tracksOfMediaType:QTMediaTypeVideo];
336         
337         if([videoTracks count] == 0) {
338                 if(QTIME_DEBUG) printf("qt: no video tracks for movie %s\n", anim->name);
339                 [anim->qtime->movie release];
340                 MEM_freeN(anim->qtime);
341                 if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
342                 [QTMovie exitQTKitOnThread];
343                 [pool drain];
344                 return -1;
345         }
346         
347         anim->qtime->media = [[videoTracks objectAtIndex:0] media];
348         [anim->qtime->media retain];
349         
350         
351         frameSize = [[anim->qtime->movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
352         anim->x = frameSize.width;
353         anim->y = frameSize.height;
354
355         if(anim->x == 0 && anim->y == 0) {
356                 if(QTIME_DEBUG) printf("qt: error, no dimensions\n");
357                 free_anim_quicktime(anim);
358                 [pool drain];
359                 return -1;
360         }
361
362         anim->qtime->ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect);
363         
364         qtTimeDuration = [[anim->qtime->media attributeForKey:QTMediaDurationAttribute] QTTimeValue];
365         anim->qtime->durationTime = qtTimeDuration.timeValue;
366         anim->qtime->durationScale = qtTimeDuration.timeScale;
367         
368         anim->qtime->framecount = [[anim->qtime->media attributeForKey:QTMediaSampleCountAttribute] longValue];
369         anim->qtime->previousPosition = -2; //Force seeking for first read
370         
371         //fill blender's anim struct
372
373         anim->duration = anim->qtime->framecount;
374         anim->params = 0;
375
376         anim->interlacing = 0;
377         anim->orientation = 0;
378         anim->framesize = anim->x * anim->y * 4;
379
380         anim->curposition = 0;
381
382         [pool drain];
383                                                                                                  
384         return 0;
385 }
386
387 int imb_is_a_quicktime (char *name)
388 {
389         NSImage *image;
390         int result;
391         NSAutoreleasePool *pool;
392         
393         if(!G.have_quicktime) return 0;
394
395         pool = [[NSAutoreleasePool alloc] init];
396         
397         // dont let quicktime image import handle these
398         if( BLI_testextensie(name, ".swf") ||
399                 BLI_testextensie(name, ".txt") ||
400                 BLI_testextensie(name, ".mpg") ||
401                 BLI_testextensie(name, ".wav") ||
402                 BLI_testextensie(name, ".mov") ||       // not as image, doesn't work
403                 BLI_testextensie(name, ".avi") ||
404                 BLI_testextensie(name, ".mp3")) return 0;
405
406         
407         image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:name]];
408         if (image) {
409                 [image release];
410                 result = true;
411         }
412         else 
413                 result = false;
414
415         [pool drain];
416         return result;
417 }
418
419 ImBuf  *imb_quicktime_decode(unsigned char *mem, int size, int flags)
420 {
421         struct ImBuf *ibuf = NULL;
422         NSSize bitmapSize;
423         uchar *rasterRGB = NULL;
424         uchar *rasterRGBA = NULL;
425         uchar *toIBuf = NULL;
426         int x, y, to_i, from_i;
427         NSData *data;
428         NSBitmapImageRep *bitmapImage;
429         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA;
430         NSAutoreleasePool *pool;
431
432         if(!G.have_quicktime)
433                 return NULL;
434         
435         pool = [[NSAutoreleasePool alloc] init];
436         
437         data = [NSData dataWithBytes:mem length:size];
438         bitmapImage = [[NSBitmapImageRep alloc] initWithData:data];
439         
440         if (!bitmapImage) {
441                 fprintf(stderr, "imb_cocoaLoadImage: error loading image\n");
442                 [pool drain];
443                 return NULL;
444         }
445         
446         bitmapSize.width = [bitmapImage pixelsWide];
447         bitmapSize.height = [bitmapImage pixelsHigh];
448         
449         /* Tell cocoa image resolution is same as current system one */
450         [bitmapImage setSize:bitmapSize];
451         
452         /* allocate the image buffer */
453         ibuf = IMB_allocImBuf(bitmapSize.width, bitmapSize.height, 32/*RGBA*/, 0);
454         if (!ibuf) {
455                 fprintf(stderr, 
456                                 "imb_cocoaLoadImage: could not allocate memory for the " \
457                                 "image.\n");
458                 [bitmapImage release];
459                 [pool drain];
460                 return NULL;
461         }
462         
463         /* read in the image data */
464         if (!(flags & IB_test)) {
465                 
466                 /* allocate memory for the ibuf->rect */
467                 imb_addrectImBuf(ibuf);
468                 
469                 /* Convert the image in a RGBA 32bit format */
470                 /* As Core Graphics does not support contextes with non premutliplied alpha,
471                  we need to get alpha key values in a separate batch */
472                 
473                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
474                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
475                                                                                                                                                  pixelsWide:bitmapSize.width 
476                                                                                                                                                  pixelsHigh:bitmapSize.height
477                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
478                                                                                                                                          colorSpaceName:NSCalibratedRGBColorSpace 
479                                                                                                                                            bitmapFormat:0
480                                                                                                                                                 bytesPerRow:4*bitmapSize.width
481                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
482                 
483                 [NSGraphicsContext saveGraphicsState];
484                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
485                 [bitmapImage draw];
486                 [NSGraphicsContext restoreGraphicsState];
487                 
488                 rasterRGB = (uchar*)[blBitmapFormatImageRGB bitmapData];
489                 if (rasterRGB == NULL) {
490                         [bitmapImage release];
491                         [blBitmapFormatImageRGB release];
492                         [pool drain];
493                         return NULL;
494                 }
495                 
496                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
497                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
498                                                                                                                                                   pixelsWide:bitmapSize.width
499                                                                                                                                                   pixelsHigh:bitmapSize.height
500                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
501                                                                                                                                           colorSpaceName:NSCalibratedRGBColorSpace
502                                                                                                                                                 bitmapFormat:0
503                                                                                                                                                  bytesPerRow:4*bitmapSize.width
504                                                                                                                                                 bitsPerPixel:32/* RGBA */];
505                 
506                 [NSGraphicsContext saveGraphicsState];
507                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
508                 [bitmapImage draw];
509                 [NSGraphicsContext restoreGraphicsState];
510                 
511                 rasterRGBA = (uchar*)[blBitmapFormatImageRGBA bitmapData];
512                 if (rasterRGBA == NULL) {
513                         [bitmapImage release];
514                         [blBitmapFormatImageRGB release];
515                         [blBitmapFormatImageRGBA release];
516                         [pool drain];
517                         return NULL;
518                 }
519                 
520                 /*Copy the image to ibuf, flipping it vertically*/
521                 toIBuf = (uchar*)ibuf->rect;
522                 for (x = 0; x < bitmapSize.width; x++) {
523                         for (y = 0; y < bitmapSize.height; y++) {
524                                 to_i = (bitmapSize.height-y-1)*bitmapSize.width + x;
525                                 from_i = y*bitmapSize.width + x;
526                                 
527                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
528                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
529                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
530                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
531                         }
532                 }
533                 
534                 [blBitmapFormatImageRGB release];
535                 [blBitmapFormatImageRGBA release];
536         }
537         
538         /* release the cocoa objects */
539         [bitmapImage release];
540         [pool drain];
541         
542         if (ENDIAN_ORDER == B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf);
543         
544         /* return successfully */
545         return (ibuf);  
546 }
547
548
549 #endif /* WITH_QUICKTIME */
550