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