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