Blender V2.61 - r43446

qtkit_import.m

Go to the documentation of this file.
00001 /*
00002  * Code to use Quicktime to load images/movies as texture.
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  * This program is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * as published by the Free Software Foundation; either version 2
00008  * of the License, or (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software Foundation,
00017  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  *
00020  * The Original Code is written by Rob Haarsma (phase)
00021  *
00022  * Contributor(s): Stefan Gartner (sgefant)
00023  *                 Damien Plisson 11/2009
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 #ifdef WITH_QUICKTIME
00028 
00029 #include "MEM_guardedalloc.h"
00030 
00031 #include "IMB_anim.h"
00032 #include "BLO_sys_types.h"
00033 #include "BKE_global.h"
00034 
00035 #include "BLI_dynstr.h"
00036 #include "BLI_path_util.h"
00037 
00038 #import <Cocoa/Cocoa.h>
00039 #import <QTKit/QTKit.h>
00040 
00041 #include "quicktime_import.h"
00042 #include "quicktime_export.h"
00043 
00044 // quicktime structure definition
00045 // this structure is part of the anim struct
00046 
00047 typedef struct _QuicktimeMovie {
00048     QTMovie *movie;
00049     QTMedia *media;
00050     
00051     long durationTime;
00052     long durationScale;
00053     long framecount;
00054     
00055 
00056     ImBuf *ibuf;
00057     
00058     long previousPosition;
00059     
00060 } QuicktimeMovie;
00061 
00062 
00063 #define QTIME_DEBUG 0
00064 
00065 
00066 void quicktime_init(void)
00067 {
00068         G.have_quicktime = TRUE;
00069 }
00070 
00071 
00072 void quicktime_exit(void)
00073 {
00074     if(G.have_quicktime) {
00075         free_qtcomponentdata();
00076     }
00077 }
00078 
00079 
00080 int anim_is_quicktime (const char *name)
00081 {
00082     NSAutoreleasePool *pool;
00083     
00084     // dont let quicktime movie import handle these
00085     if( BLI_testextensie(name, ".swf") ||
00086         BLI_testextensie(name, ".txt") ||
00087         BLI_testextensie(name, ".mpg") ||
00088         BLI_testextensie(name, ".avi") ||   // wouldnt be appropriate ;)
00089         BLI_testextensie(name, ".tga") ||
00090         BLI_testextensie(name, ".png") ||
00091         BLI_testextensie(name, ".bmp") ||
00092         BLI_testextensie(name, ".jpg") ||
00093         BLI_testextensie(name, ".wav") ||
00094         BLI_testextensie(name, ".zip") ||
00095         BLI_testextensie(name, ".mp3")) return 0;
00096 
00097     if(QTIME_DEBUG) printf("qt: checking as movie: %s\n", name);
00098 
00099     pool = [[NSAutoreleasePool alloc] init];
00100     
00101     if([QTMovie canInitWithFile:[NSString stringWithCString:name 
00102                                  encoding:[NSString defaultCStringEncoding]]])
00103     {
00104         [pool drain];
00105         return true;
00106     }
00107     else
00108     {
00109         [pool drain];
00110         return false;
00111     }
00112 }
00113 
00114 
00115 void free_anim_quicktime (struct anim *anim)
00116 {
00117     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00118     
00119     if (anim == NULL) return;
00120     if (anim->qtime == NULL) return;
00121 
00122     if(anim->qtime->ibuf)
00123         IMB_freeImBuf(anim->qtime->ibuf);
00124 
00125     [anim->qtime->media release];
00126     [anim->qtime->movie release];
00127 
00128     [QTMovie exitQTKitOnThread];
00129 
00130     if(anim->qtime) MEM_freeN (anim->qtime);
00131 
00132     anim->qtime = NULL;
00133 
00134     anim->duration = 0;
00135 
00136     [pool drain];
00137 }
00138 
00139 static ImBuf * nsImageToiBuf(NSImage *sourceImage, int width, int height)
00140 {
00141     ImBuf *ibuf = NULL;
00142     uchar *rasterRGB = NULL;
00143     uchar *rasterRGBA = NULL;
00144     uchar *toIBuf = NULL;
00145     int x, y, to_i, from_i;
00146     NSSize bitmapSize;
00147     NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
00148     NSEnumerator *enumerator;
00149     NSImageRep *representation;
00150     
00151     ibuf = IMB_allocImBuf (width, height, 32, IB_rect);
00152     if (!ibuf) {
00153         if(QTIME_DEBUG) printf("quicktime_import: could not allocate memory for the " \
00154                 "image.\n");
00155         return NULL;
00156     }
00157     
00158     /*Get the bitmap of the image*/
00159     enumerator = [[sourceImage representations] objectEnumerator];
00160     while ((representation = [enumerator nextObject])) {
00161         if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
00162             bitmapImage = (NSBitmapImageRep *)representation;
00163             break;
00164         }
00165     }
00166     if (bitmapImage == nil) return NULL;
00167 
00168     if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
00169         && ![bitmapImage isPlanar]) {
00170         /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
00171         toIBuf = (uchar*)ibuf->rect;
00172         rasterRGB = (uchar*)[bitmapImage bitmapData];
00173         for (y = 0; y < height; y++) {
00174             to_i = (height-y-1)*width;
00175             from_i = y*width;
00176             memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*width);
00177         }
00178     }
00179     else {
00180 
00181         bitmapSize.width = width;
00182         bitmapSize.height = height;
00183         
00184         /* Tell cocoa image resolution is same as current system one */
00185         [bitmapImage setSize:bitmapSize];
00186         
00187         /* Convert the image in a RGBA 32bit format */
00188         /* As Core Graphics does not support contextes with non premutliplied alpha,
00189          we need to get alpha key values in a separate batch */
00190         
00191         /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
00192         blBitmapFormatImageRGB = /*RGB format padded to 32bits*/[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
00193                                                                          pixelsWide:width 
00194                                                                          pixelsHigh:height
00195                                                                       bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
00196                                                                      colorSpaceName:NSDeviceRGBColorSpace 
00197                                                                        bitmapFormat:0
00198                                                                         bytesPerRow:4*width
00199                                                                        bitsPerPixel:32];
00200         
00201         [NSGraphicsContext saveGraphicsState];
00202         [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
00203         [bitmapImage draw];
00204         [NSGraphicsContext restoreGraphicsState];
00205         
00206         rasterRGB = (uchar*)[blBitmapFormatImageRGB bitmapData];
00207         if (rasterRGB == NULL) {
00208             [blBitmapFormatImageRGB release];
00209             return NULL;
00210         }
00211         
00212         /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
00213         blBitmapFormatImageRGBA = /* RGBA */[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
00214                                                                           pixelsWide:width
00215                                                                           pixelsHigh:height
00216                                                                        bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
00217                                                                       colorSpaceName:NSDeviceRGBColorSpace
00218                                                                         bitmapFormat:0
00219                                                                          bytesPerRow:4*width
00220                                                                         bitsPerPixel:32];
00221         
00222         [NSGraphicsContext saveGraphicsState];
00223         [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
00224         [bitmapImage draw];
00225         [NSGraphicsContext restoreGraphicsState];
00226         
00227         rasterRGBA = (uchar*)[blBitmapFormatImageRGBA bitmapData];
00228         if (rasterRGBA == NULL) {
00229             [blBitmapFormatImageRGB release];
00230             [blBitmapFormatImageRGBA release];
00231             return NULL;
00232         }
00233 
00234         /*Copy the image to ibuf, flipping it vertically*/
00235         toIBuf = (uchar*)ibuf->rect;
00236         for (y = 0; y < height; y++) {
00237             for (x = 0; x < width; x++) {
00238                 to_i = (height-y-1)*width + x;
00239                 from_i = y*width + x;
00240                 
00241                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
00242                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
00243                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
00244                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
00245             }
00246         }
00247 
00248         [blBitmapFormatImageRGB release];
00249         [blBitmapFormatImageRGBA release];
00250     }
00251     
00252     return ibuf;
00253 }
00254 
00255 ImBuf * qtime_fetchibuf (struct anim *anim, int position)
00256 {
00257     NSImage *frameImage;
00258     QTTime time;
00259     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00260     ImBuf *ibuf;
00261     
00262     if (anim == NULL) {
00263         return (NULL);
00264     }
00265 
00266     if (position == anim->qtime->previousPosition+1) { //Optimize sequential read
00267         [anim->qtime->movie stepForward];
00268         frameImage = [anim->qtime->movie currentFrameImage];
00269         anim->qtime->previousPosition++;
00270     }
00271     else {
00272         time.timeScale = anim->qtime->durationScale;
00273         time.timeValue = (anim->qtime->durationTime * position) / anim->qtime->framecount;
00274     
00275         [anim->qtime->movie setCurrentTime:time];
00276         frameImage = [anim->qtime->movie currentFrameImage]; 
00277         
00278         anim->qtime->previousPosition = position;
00279     }
00280         
00281     if (frameImage == nil) {
00282         if(QTIME_DEBUG) printf ("Error reading frame from Quicktime");
00283         [pool drain];
00284         return NULL;
00285     }
00286 
00287     ibuf = nsImageToiBuf(frameImage,anim->x, anim->y);
00288     [pool drain];
00289     
00290     ibuf->profile = IB_PROFILE_SRGB;
00291     return ibuf;
00292 }
00293 
00294 
00295 int startquicktime (struct anim *anim)
00296 {
00297     NSAutoreleasePool *pool;
00298     NSArray* videoTracks;
00299     NSSize frameSize;
00300     QTTime qtTimeDuration;
00301     NSDictionary *attributes;
00302     
00303     anim->qtime = MEM_callocN (sizeof(QuicktimeMovie),"animqt");
00304 
00305     if (anim->qtime == NULL) {
00306         if(QTIME_DEBUG) printf("Can't alloc qtime: %s\n", anim->name);
00307         return -1;
00308     }
00309 
00310     pool = [[NSAutoreleasePool alloc] init];
00311     
00312     [QTMovie enterQTKitOnThread];       
00313 
00314     attributes = [NSDictionary dictionaryWithObjectsAndKeys:
00315             [NSString stringWithCString:anim->name
00316             encoding:[NSString defaultCStringEncoding]], QTMovieFileNameAttribute,
00317             [NSNumber numberWithBool:NO], QTMovieEditableAttribute,
00318         nil];
00319 
00320     anim->qtime->movie = [QTMovie movieWithAttributes:attributes error:NULL];
00321     
00322     if (!anim->qtime->movie) {
00323         if(QTIME_DEBUG) printf("qt: bad movie %s\n", anim->name);
00324         MEM_freeN(anim->qtime);
00325         if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
00326         [QTMovie exitQTKitOnThread];
00327         [pool drain];
00328         return -1;
00329     }
00330     [anim->qtime->movie retain];
00331 
00332     // sets Media and Track!
00333     
00334     videoTracks = [anim->qtime->movie tracksOfMediaType:QTMediaTypeVideo];
00335     
00336     if([videoTracks count] == 0) {
00337         if(QTIME_DEBUG) printf("qt: no video tracks for movie %s\n", anim->name);
00338         [anim->qtime->movie release];
00339         MEM_freeN(anim->qtime);
00340         if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
00341         [QTMovie exitQTKitOnThread];
00342         [pool drain];
00343         return -1;
00344     }
00345     
00346     anim->qtime->media = [[videoTracks objectAtIndex:0] media];
00347     [anim->qtime->media retain];
00348     
00349     
00350     frameSize = [[anim->qtime->movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
00351     anim->x = frameSize.width;
00352     anim->y = frameSize.height;
00353 
00354     if(anim->x == 0 && anim->y == 0) {
00355         if(QTIME_DEBUG) printf("qt: error, no dimensions\n");
00356         free_anim_quicktime(anim);
00357         [pool drain];
00358         return -1;
00359     }
00360 
00361     anim->qtime->ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect);
00362     
00363     qtTimeDuration = [[anim->qtime->media attributeForKey:QTMediaDurationAttribute] QTTimeValue];
00364     anim->qtime->durationTime = qtTimeDuration.timeValue;
00365     anim->qtime->durationScale = qtTimeDuration.timeScale;
00366     
00367     anim->qtime->framecount = [[anim->qtime->media attributeForKey:QTMediaSampleCountAttribute] longValue];
00368     anim->qtime->previousPosition = -2; //Force seeking for first read
00369     
00370     //fill blender's anim struct
00371 
00372     anim->duration = anim->qtime->framecount;
00373     anim->params = 0;
00374 
00375     anim->interlacing = 0;
00376     anim->orientation = 0;
00377     anim->framesize = anim->x * anim->y * 4;
00378 
00379     anim->curposition = 0;
00380 
00381     [pool drain];
00382                                                  
00383     return 0;
00384 }
00385 
00386 int imb_is_a_quicktime (char *name)
00387 {
00388     NSImage *image;
00389     int result;
00390     NSAutoreleasePool *pool;
00391     
00392     if(!G.have_quicktime) return 0;
00393 
00394     pool = [[NSAutoreleasePool alloc] init];
00395     
00396     // dont let quicktime image import handle these
00397     if( BLI_testextensie(name, ".swf") ||
00398         BLI_testextensie(name, ".txt") ||
00399         BLI_testextensie(name, ".mpg") ||
00400         BLI_testextensie(name, ".wav") ||
00401         BLI_testextensie(name, ".mov") ||   // not as image, doesn't work
00402         BLI_testextensie(name, ".avi") ||
00403         BLI_testextensie(name, ".mp3")) return 0;
00404 
00405     
00406     image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:name]];
00407     if (image) {
00408         [image release];
00409         result = true;
00410     }
00411     else 
00412         result = false;
00413 
00414     [pool drain];
00415     return result;
00416 }
00417 
00418 ImBuf  *imb_quicktime_decode(unsigned char *mem, int size, int flags)
00419 {
00420     struct ImBuf *ibuf = NULL;
00421     NSSize bitmapSize;
00422     uchar *rasterRGB = NULL;
00423     uchar *rasterRGBA = NULL;
00424     uchar *toIBuf = NULL;
00425     int x, y, to_i, from_i;
00426     NSData *data;
00427     NSBitmapImageRep *bitmapImage;
00428     NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA;
00429     NSAutoreleasePool *pool;
00430 
00431     if(!G.have_quicktime)
00432         return NULL;
00433     
00434     pool = [[NSAutoreleasePool alloc] init];
00435     
00436     data = [NSData dataWithBytes:mem length:size];
00437     bitmapImage = [[NSBitmapImageRep alloc] initWithData:data];
00438     
00439     if (!bitmapImage) {
00440         fprintf(stderr, "imb_cocoaLoadImage: error loading image\n");
00441         [pool drain];
00442         return NULL;
00443     }
00444     
00445     bitmapSize.width = [bitmapImage pixelsWide];
00446     bitmapSize.height = [bitmapImage pixelsHigh];
00447     
00448     /* Tell cocoa image resolution is same as current system one */
00449     [bitmapImage setSize:bitmapSize];
00450     
00451     /* allocate the image buffer */
00452     ibuf = IMB_allocImBuf(bitmapSize.width, bitmapSize.height, 32/*RGBA*/, 0);
00453     if (!ibuf) {
00454         fprintf(stderr, 
00455                 "imb_cocoaLoadImage: could not allocate memory for the " \
00456                 "image.\n");
00457         [bitmapImage release];
00458         [pool drain];
00459         return NULL;
00460     }
00461     
00462     /* read in the image data */
00463     if (!(flags & IB_test)) {
00464         
00465         /* allocate memory for the ibuf->rect */
00466         imb_addrectImBuf(ibuf);
00467         
00468         /* Convert the image in a RGBA 32bit format */
00469         /* As Core Graphics does not support contextes with non premutliplied alpha,
00470          we need to get alpha key values in a separate batch */
00471         
00472         /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
00473         blBitmapFormatImageRGB = /*RGB format padded to 32bits*/[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
00474                                                                          pixelsWide:bitmapSize.width 
00475                                                                          pixelsHigh:bitmapSize.height
00476                                                                       bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
00477                                                                      colorSpaceName:NSCalibratedRGBColorSpace 
00478                                                                        bitmapFormat:0
00479                                                                         bytesPerRow:4*bitmapSize.width
00480                                                                        bitsPerPixel:32];
00481         
00482         [NSGraphicsContext saveGraphicsState];
00483         [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
00484         [bitmapImage draw];
00485         [NSGraphicsContext restoreGraphicsState];
00486         
00487         rasterRGB = (uchar*)[blBitmapFormatImageRGB bitmapData];
00488         if (rasterRGB == NULL) {
00489             [bitmapImage release];
00490             [blBitmapFormatImageRGB release];
00491             [pool drain];
00492             return NULL;
00493         }
00494         
00495         /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
00496         blBitmapFormatImageRGBA = /* RGBA */[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
00497                                                                           pixelsWide:bitmapSize.width
00498                                                                           pixelsHigh:bitmapSize.height
00499                                                                        bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
00500                                                                       colorSpaceName:NSCalibratedRGBColorSpace
00501                                                                         bitmapFormat:0
00502                                                                          bytesPerRow:4*bitmapSize.width
00503                                                                         bitsPerPixel:32];
00504         
00505         [NSGraphicsContext saveGraphicsState];
00506         [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
00507         [bitmapImage draw];
00508         [NSGraphicsContext restoreGraphicsState];
00509         
00510         rasterRGBA = (uchar*)[blBitmapFormatImageRGBA bitmapData];
00511         if (rasterRGBA == NULL) {
00512             [bitmapImage release];
00513             [blBitmapFormatImageRGB release];
00514             [blBitmapFormatImageRGBA release];
00515             [pool drain];
00516             return NULL;
00517         }
00518         
00519         /*Copy the image to ibuf, flipping it vertically*/
00520         toIBuf = (uchar*)ibuf->rect;
00521         for (x = 0; x < bitmapSize.width; x++) {
00522             for (y = 0; y < bitmapSize.height; y++) {
00523                 to_i = (bitmapSize.height-y-1)*bitmapSize.width + x;
00524                 from_i = y*bitmapSize.width + x;
00525                 
00526                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
00527                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
00528                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
00529                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
00530             }
00531         }
00532         
00533         [blBitmapFormatImageRGB release];
00534         [blBitmapFormatImageRGBA release];
00535     }
00536     
00537     /* release the cocoa objects */
00538     [bitmapImage release];
00539     [pool drain];
00540     
00541     if (ENDIAN_ORDER == B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf);
00542     
00543     /* return successfully */
00544     return (ibuf);  
00545 }
00546 
00547 
00548 #endif /* WITH_QUICKTIME */
00549