Blender V2.61 - r43446
|
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