style cleanup
[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) {
157                         printf("quicktime_import: could not allocate memory for the image.\n");
158                 }
159                 return NULL;
160         }
161         
162         /*Get the bitmap of the image*/
163         enumerator = [[sourceImage representations] objectEnumerator];
164         while ((representation = [enumerator nextObject])) {
165         if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
166             bitmapImage = (NSBitmapImageRep *)representation;
167                         break;
168         }
169     }
170         if (bitmapImage == nil) return NULL;
171
172         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
173                 && ![bitmapImage isPlanar]) {
174                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
175                 toIBuf = (uchar *)ibuf->rect;
176                 rasterRGB = (uchar *)[bitmapImage bitmapData];
177                 for (y = 0; y < height; y++) {
178                         to_i = (height-y-1)*width;
179                         from_i = y*width;
180                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*width);
181                 }
182         }
183         else {
184
185                 bitmapSize.width = width;
186                 bitmapSize.height = height;
187                 
188                 /* Tell cocoa image resolution is same as current system one */
189                 [bitmapImage setSize:bitmapSize];
190                 
191                 /* Convert the image in a RGBA 32bit format */
192                 /* As Core Graphics does not support contextes with non premutliplied alpha,
193                  we need to get alpha key values in a separate batch */
194                 
195                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
196                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
197                                                                                                                                                  pixelsWide:width 
198                                                                                                                                                  pixelsHigh:height
199                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
200                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
201                                                                                                                                            bitmapFormat:0
202                                                                                                                                                 bytesPerRow:4*width
203                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
204                 
205                 [NSGraphicsContext saveGraphicsState];
206                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
207                 [bitmapImage draw];
208                 [NSGraphicsContext restoreGraphicsState];
209                 
210                 rasterRGB = (uchar *)[blBitmapFormatImageRGB bitmapData];
211                 if (rasterRGB == NULL) {
212                         [blBitmapFormatImageRGB release];
213                         return NULL;
214                 }
215                 
216                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
217                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
218                                                                                                                                                   pixelsWide:width
219                                                                                                                                                   pixelsHigh:height
220                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
221                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
222                                                                                                                                                 bitmapFormat:0
223                                                                                                                                                  bytesPerRow:4*width
224                                                                                                                                                 bitsPerPixel:32/* RGBA */];
225                 
226                 [NSGraphicsContext saveGraphicsState];
227                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
228                 [bitmapImage draw];
229                 [NSGraphicsContext restoreGraphicsState];
230                 
231                 rasterRGBA = (uchar *)[blBitmapFormatImageRGBA bitmapData];
232                 if (rasterRGBA == NULL) {
233                         [blBitmapFormatImageRGB release];
234                         [blBitmapFormatImageRGBA release];
235                         return NULL;
236                 }
237
238                 /*Copy the image to ibuf, flipping it vertically*/
239                 toIBuf = (uchar *)ibuf->rect;
240                 for (y = 0; y < height; y++) {
241                         for (x = 0; x < width; x++) {
242                                 to_i = (height-y-1)*width + x;
243                                 from_i = y*width + x;
244                                 
245                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
246                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
247                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
248                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
249                         }
250                 }
251
252                 [blBitmapFormatImageRGB release];
253                 [blBitmapFormatImageRGBA release];
254         }
255         
256         return ibuf;
257 }
258
259 ImBuf * qtime_fetchibuf (struct anim *anim, int position)
260 {
261         NSImage *frameImage;
262         QTTime time;
263         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
264         ImBuf *ibuf;
265         
266         if (anim == NULL) {
267                 return (NULL);
268         }
269
270         if (position == anim->qtime->previousPosition+1) { //Optimize sequential read
271                 [anim->qtime->movie stepForward];
272                 frameImage = [anim->qtime->movie currentFrameImage];
273                 anim->qtime->previousPosition++;
274         }
275         else {
276                 time.timeScale = anim->qtime->durationScale;
277                 time.timeValue = (anim->qtime->durationTime * position) / anim->qtime->framecount;
278         
279                 [anim->qtime->movie setCurrentTime:time];
280                 frameImage = [anim->qtime->movie currentFrameImage]; 
281                 
282                 anim->qtime->previousPosition = position;
283         }
284                 
285         if (frameImage == nil) {
286                 if(QTIME_DEBUG) printf ("Error reading frame from Quicktime");
287                 [pool drain];
288                 return NULL;
289         }
290
291         ibuf = nsImageToiBuf(frameImage,anim->x, anim->y);
292         [pool drain];
293         
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 image.\n");
459                 [bitmapImage release];
460                 [pool drain];
461                 return NULL;
462         }
463         
464         /* read in the image data */
465         if (!(flags & IB_test)) {
466                 
467                 /* allocate memory for the ibuf->rect */
468                 imb_addrectImBuf(ibuf);
469                 
470                 /* Convert the image in a RGBA 32bit format */
471                 /* As Core Graphics does not support contextes with non premutliplied alpha,
472                  we need to get alpha key values in a separate batch */
473                 
474                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
475                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
476                                                                                                                                                  pixelsWide:bitmapSize.width 
477                                                                                                                                                  pixelsHigh:bitmapSize.height
478                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
479                                                                                                                                          colorSpaceName:NSCalibratedRGBColorSpace 
480                                                                                                                                            bitmapFormat:0
481                                                                                                                                                 bytesPerRow:4*bitmapSize.width
482                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
483                 
484                 [NSGraphicsContext saveGraphicsState];
485                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
486                 [bitmapImage draw];
487                 [NSGraphicsContext restoreGraphicsState];
488                 
489                 rasterRGB = (uchar *)[blBitmapFormatImageRGB bitmapData];
490                 if (rasterRGB == NULL) {
491                         [bitmapImage release];
492                         [blBitmapFormatImageRGB release];
493                         [pool drain];
494                         return NULL;
495                 }
496                 
497                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
498                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
499                                                                                                                                                   pixelsWide:bitmapSize.width
500                                                                                                                                                   pixelsHigh:bitmapSize.height
501                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
502                                                                                                                                           colorSpaceName:NSCalibratedRGBColorSpace
503                                                                                                                                                 bitmapFormat:0
504                                                                                                                                                  bytesPerRow:4*bitmapSize.width
505                                                                                                                                                 bitsPerPixel:32/* RGBA */];
506                 
507                 [NSGraphicsContext saveGraphicsState];
508                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
509                 [bitmapImage draw];
510                 [NSGraphicsContext restoreGraphicsState];
511                 
512                 rasterRGBA = (uchar *)[blBitmapFormatImageRGBA bitmapData];
513                 if (rasterRGBA == NULL) {
514                         [bitmapImage release];
515                         [blBitmapFormatImageRGB release];
516                         [blBitmapFormatImageRGBA release];
517                         [pool drain];
518                         return NULL;
519                 }
520                 
521                 /*Copy the image to ibuf, flipping it vertically*/
522                 toIBuf = (uchar *)ibuf->rect;
523                 for (x = 0; x < bitmapSize.width; x++) {
524                         for (y = 0; y < bitmapSize.height; y++) {
525                                 to_i = (bitmapSize.height-y-1)*bitmapSize.width + x;
526                                 from_i = y*bitmapSize.width + x;
527                                 
528                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
529                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
530                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
531                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
532                         }
533                 }
534                 
535                 [blBitmapFormatImageRGB release];
536                 [blBitmapFormatImageRGBA release];
537         }
538         
539         /* release the cocoa objects */
540         [bitmapImage release];
541         [pool drain];
542         
543         if (ENDIAN_ORDER == B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf);
544         
545         /* return successfully */
546         return (ibuf);  
547 }
548
549
550 #endif /* WITH_QUICKTIME */
551