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