UI: Refactor timecode functions into BLI_timecode
[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 "BLI_sys_types.h"
33 #include "BLI_utildefines.h"
34 #include "BKE_global.h"
35
36 #include "BLI_dynstr.h"
37 #include "BLI_path_util.h"
38
39 #import <Cocoa/Cocoa.h>
40 #import <QTKit/QTKit.h>
41
42 #include "quicktime_import.h"
43 #include "quicktime_export.h"
44
45 // quicktime structure definition
46 // this structure is part of the anim struct
47
48 typedef struct _QuicktimeMovie {
49         QTMovie *movie;
50         QTMedia *media;
51         
52         long durationTime;
53         long durationScale;
54         long framecount;
55         
56
57         ImBuf *ibuf;
58         
59         long previousPosition;
60         
61 } QuicktimeMovie;
62
63
64 #define QTIME_DEBUG 0
65
66
67 void quicktime_init(void)
68 {
69         G.have_quicktime = TRUE;
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, ".tif") ||
97             BLI_testextensie(name, ".exr") ||
98             BLI_testextensie(name, ".wav") ||
99             BLI_testextensie(name, ".zip") ||
100             BLI_testextensie(name, ".mp3"))
101         {
102                 return 0;
103         }
104
105         if (QTIME_DEBUG) printf("qt: checking as movie: %s\n", name);
106
107         pool = [[NSAutoreleasePool alloc] init];
108         
109         if ([QTMovie canInitWithFile:[NSString stringWithCString:name
110                                       encoding:[NSString defaultCStringEncoding]]])
111         {
112                 [pool drain];
113                 return true;
114         }
115         else
116         {
117                 [pool drain];
118                 return false;
119         }
120 }
121
122
123 void free_anim_quicktime(struct anim *anim)
124 {
125         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
126         
127         if (anim == NULL) return;
128         if (anim->qtime == NULL) return;
129
130         if (anim->qtime->ibuf)
131                 IMB_freeImBuf(anim->qtime->ibuf);
132
133         [anim->qtime->media release];
134         [anim->qtime->movie release];
135
136         [QTMovie exitQTKitOnThread];
137
138         if (anim->qtime) MEM_freeN (anim->qtime);
139
140         anim->qtime = NULL;
141
142         anim->duration = 0;
143
144         [pool drain];
145 }
146
147 static ImBuf *nsImageToiBuf(NSImage *sourceImage, int width, int height)
148 {
149         ImBuf *ibuf = NULL;
150         uchar *rasterRGB = NULL;
151         uchar *rasterRGBA = NULL;
152         uchar *toIBuf = NULL;
153         int x, y, to_i, from_i;
154         NSSize bitmapSize;
155         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA, *bitmapImage = nil;
156         NSEnumerator *enumerator;
157         NSImageRep *representation;
158         
159         ibuf = IMB_allocImBuf(width, height, 32, IB_rect);
160         if (!ibuf) {
161                 if (QTIME_DEBUG) {
162                         printf("quicktime_import: could not allocate memory for the image.\n");
163                 }
164                 return NULL;
165         }
166         
167         /*Get the bitmap of the image*/
168         enumerator = [[sourceImage representations] objectEnumerator];
169         while ((representation = [enumerator nextObject])) {
170                 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
171                         bitmapImage = (NSBitmapImageRep *)representation;
172                         break;
173                 }
174         }
175         if (bitmapImage == nil) return NULL;
176
177         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
178                 && ![bitmapImage isPlanar]) {
179                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
180                 toIBuf = (uchar *)ibuf->rect;
181                 rasterRGB = (uchar *)[bitmapImage bitmapData];
182                 for (y = 0; y < height; y++) {
183                         to_i = (height-y-1)*width;
184                         from_i = y*width;
185                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*width);
186                 }
187         }
188         else {
189
190                 bitmapSize.width = width;
191                 bitmapSize.height = height;
192                 
193                 /* Tell cocoa image resolution is same as current system one */
194                 [bitmapImage setSize:bitmapSize];
195                 
196                 /* Convert the image in a RGBA 32bit format */
197                 /* As Core Graphics does not support contextes with non premutliplied alpha,
198                  we need to get alpha key values in a separate batch */
199                 
200                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
201                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
202                                                                                                                                                  pixelsWide:width 
203                                                                                                                                                  pixelsHigh:height
204                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
205                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
206                                                                                                                                            bitmapFormat:0
207                                                                                                                                                 bytesPerRow:4*width
208                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
209                 
210                 [NSGraphicsContext saveGraphicsState];
211                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
212                 [bitmapImage draw];
213                 [NSGraphicsContext restoreGraphicsState];
214                 
215                 rasterRGB = (uchar *)[blBitmapFormatImageRGB bitmapData];
216                 if (rasterRGB == NULL) {
217                         [blBitmapFormatImageRGB release];
218                         return NULL;
219                 }
220                 
221                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
222                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
223                                                                                                                                                   pixelsWide:width
224                                                                                                                                                   pixelsHigh:height
225                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
226                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
227                                                                                                                                                 bitmapFormat:0
228                                                                                                                                                  bytesPerRow:4*width
229                                                                                                                                                 bitsPerPixel:32/* RGBA */];
230                 
231                 [NSGraphicsContext saveGraphicsState];
232                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
233                 [bitmapImage draw];
234                 [NSGraphicsContext restoreGraphicsState];
235                 
236                 rasterRGBA = (uchar *)[blBitmapFormatImageRGBA bitmapData];
237                 if (rasterRGBA == NULL) {
238                         [blBitmapFormatImageRGB release];
239                         [blBitmapFormatImageRGBA release];
240                         return NULL;
241                 }
242
243                 /*Copy the image to ibuf, flipping it vertically*/
244                 toIBuf = (uchar *)ibuf->rect;
245                 for (y = 0; y < height; y++) {
246                         for (x = 0; x < width; x++) {
247                                 to_i = (height-y-1)*width + x;
248                                 from_i = y*width + x;
249                                 
250                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
251                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
252                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
253                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
254                         }
255                 }
256
257                 [blBitmapFormatImageRGB release];
258                 [blBitmapFormatImageRGBA release];
259         }
260         
261         return ibuf;
262 }
263
264 ImBuf *qtime_fetchibuf (struct anim *anim, int position)
265 {
266         NSImage *frameImage;
267         QTTime time;
268         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
269         ImBuf *ibuf;
270         
271         if (anim == NULL) {
272                 return (NULL);
273         }
274
275         if (position == anim->qtime->previousPosition+1) { //Optimize sequential read
276                 [anim->qtime->movie stepForward];
277                 frameImage = [anim->qtime->movie currentFrameImage];
278                 anim->qtime->previousPosition++;
279         }
280         else {
281                 time.timeScale = anim->qtime->durationScale;
282                 time.timeValue = (anim->qtime->durationTime * position) / anim->qtime->framecount;
283         
284                 [anim->qtime->movie setCurrentTime:time];
285                 frameImage = [anim->qtime->movie currentFrameImage]; 
286                 
287                 anim->qtime->previousPosition = position;
288         }
289                 
290         if (frameImage == nil) {
291                 if (QTIME_DEBUG) printf ("Error reading frame from Quicktime");
292                 [pool drain];
293                 return NULL;
294         }
295
296         ibuf = nsImageToiBuf(frameImage,anim->x, anim->y);
297         [pool drain];
298         
299         return ibuf;
300 }
301
302
303 int startquicktime(struct anim *anim)
304 {
305         NSAutoreleasePool *pool;
306         NSArray* videoTracks;
307         NSSize frameSize;
308         QTTime qtTimeDuration;
309         NSDictionary *attributes;
310         
311         anim->qtime = MEM_callocN(sizeof(QuicktimeMovie),"animqt");
312
313         if (anim->qtime == NULL) {
314                 if (QTIME_DEBUG) printf("Can't alloc qtime: %s\n", anim->name);
315                 return -1;
316         }
317
318         pool = [[NSAutoreleasePool alloc] init];
319         
320         [QTMovie enterQTKitOnThread];
321
322         attributes = [NSDictionary dictionaryWithObjectsAndKeys:
323                 [NSString stringWithCString:anim->name
324                 encoding:[NSString defaultCStringEncoding]], QTMovieFileNameAttribute,
325                 [NSNumber numberWithBool:NO], QTMovieEditableAttribute,
326             nil];
327
328         anim->qtime->movie = [QTMovie movieWithAttributes:attributes error:NULL];
329         
330         if (!anim->qtime->movie) {
331                 if (QTIME_DEBUG) printf("qt: bad movie %s\n", anim->name);
332                 MEM_freeN(anim->qtime);
333                 if (QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
334                 [QTMovie exitQTKitOnThread];
335                 [pool drain];
336                 return -1;
337         }
338         [anim->qtime->movie retain];
339
340         // sets Media and Track!
341         
342         videoTracks = [anim->qtime->movie tracksOfMediaType:QTMediaTypeVideo];
343         
344         if ([videoTracks count] == 0) {
345                 if (QTIME_DEBUG) printf("qt: no video tracks for movie %s\n", anim->name);
346                 [anim->qtime->movie release];
347                 MEM_freeN(anim->qtime);
348                 if (QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
349                 [QTMovie exitQTKitOnThread];
350                 [pool drain];
351                 return -1;
352         }
353         
354         anim->qtime->media = [[videoTracks objectAtIndex:0] media];
355         [anim->qtime->media retain];
356         
357         
358         frameSize = [[anim->qtime->movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
359         anim->x = frameSize.width;
360         anim->y = frameSize.height;
361
362         if (anim->x == 0 && anim->y == 0) {
363                 if (QTIME_DEBUG) printf("qt: error, no dimensions\n");
364                 free_anim_quicktime(anim);
365                 [pool drain];
366                 return -1;
367         }
368
369         anim->qtime->ibuf = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
370         
371         qtTimeDuration = [[anim->qtime->media attributeForKey:QTMediaDurationAttribute] QTTimeValue];
372         anim->qtime->durationTime = qtTimeDuration.timeValue;
373         anim->qtime->durationScale = qtTimeDuration.timeScale;
374         
375         anim->qtime->framecount = [[anim->qtime->media attributeForKey:QTMediaSampleCountAttribute] longValue];
376         anim->qtime->previousPosition = -2; //Force seeking for first read
377         
378         //fill blender's anim struct
379
380         anim->duration = anim->qtime->framecount;
381         anim->params = 0;
382
383         anim->interlacing = 0;
384         anim->orientation = 0;
385         anim->framesize = anim->x * anim->y * 4;
386
387         anim->curposition = 0;
388
389         [pool drain];
390
391         return 0;
392 }
393
394 #endif /* WITH_QUICKTIME */
395