|
Blender
V2.59
|
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