This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/quicktime/apple/qtkit_import.m
2012-10-30 12:18:45 +00:00

555 lines
15 KiB
Objective-C

/*
* Code to use Quicktime to load images/movies as texture.
*
* ***** BEGIN GPL LICENSE BLOCK *****
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* The Original Code is written by Rob Haarsma (phase)
*
* Contributor(s): Stefan Gartner (sgefant)
* Damien Plisson 11/2009
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifdef WITH_QUICKTIME
#include "MEM_guardedalloc.h"
#include "IMB_anim.h"
#include "BLO_sys_types.h"
#include "BKE_global.h"
#include "BLI_dynstr.h"
#include "BLI_path_util.h"
#import <Cocoa/Cocoa.h>
#import <QTKit/QTKit.h>
#include "quicktime_import.h"
#include "quicktime_export.h"
// quicktime structure definition
// this structure is part of the anim struct
typedef struct _QuicktimeMovie {
QTMovie *movie;
QTMedia *media;
long durationTime;
long durationScale;
long framecount;
ImBuf *ibuf;
long previousPosition;
} QuicktimeMovie;
#define QTIME_DEBUG 0
void quicktime_init(void)
{
G.have_quicktime = TRUE;
}
void quicktime_exit(void)
{
if (G.have_quicktime) {
free_qtcomponentdata();
}
}
int anim_is_quicktime (const char *name)
{
NSAutoreleasePool *pool;
// don't let quicktime movie import handle these
if (BLI_testextensie(name, ".swf") ||
BLI_testextensie(name, ".txt") ||
BLI_testextensie(name, ".mpg") ||
BLI_testextensie(name, ".avi") || // wouldn't be appropriate ;)
BLI_testextensie(name, ".mov") || // disabled, suboptimal decoding speed
BLI_testextensie(name, ".mp4") || // disabled, suboptimal decoding speed
BLI_testextensie(name, ".m4v") || // disabled, suboptimal decoding speed
BLI_testextensie(name, ".tga") ||
BLI_testextensie(name, ".png") ||
BLI_testextensie(name, ".bmp") ||
BLI_testextensie(name, ".jpg") ||
BLI_testextensie(name, ".wav") ||
BLI_testextensie(name, ".zip") ||
BLI_testextensie(name, ".mp3"))
{
return 0;
}
if (QTIME_DEBUG) printf("qt: checking as movie: %s\n", name);
pool = [[NSAutoreleasePool alloc] init];
if ([QTMovie canInitWithFile:[NSString stringWithCString:name
encoding:[NSString defaultCStringEncoding]]])
{
[pool drain];
return true;
}
else
{
[pool drain];
return false;
}
}
void free_anim_quicktime (struct anim *anim)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (anim == NULL) return;
if (anim->qtime == NULL) return;
if(anim->qtime->ibuf)
IMB_freeImBuf(anim->qtime->ibuf);
[anim->qtime->media release];
[anim->qtime->movie release];
[QTMovie exitQTKitOnThread];
if(anim->qtime) MEM_freeN (anim->qtime);
anim->qtime = NULL;
anim->duration = 0;
[pool drain];
}
static ImBuf * nsImageToiBuf(NSImage *sourceImage, int width, int height)
{
ImBuf *ibuf = NULL;
uchar *rasterRGB = NULL;
uchar *rasterRGBA = NULL;
uchar *toIBuf = NULL;
int x, y, to_i, from_i;
NSSize bitmapSize;
NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA, *bitmapImage = nil;
NSEnumerator *enumerator;
NSImageRep *representation;
ibuf = IMB_allocImBuf (width, height, 32, IB_rect);
if (!ibuf) {
if (QTIME_DEBUG) {
printf("quicktime_import: could not allocate memory for the image.\n");
}
return NULL;
}
/*Get the bitmap of the image*/
enumerator = [[sourceImage representations] objectEnumerator];
while ((representation = [enumerator nextObject])) {
if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
bitmapImage = (NSBitmapImageRep *)representation;
break;
}
}
if (bitmapImage == nil) return NULL;
if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
&& ![bitmapImage isPlanar]) {
/* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
toIBuf = (uchar *)ibuf->rect;
rasterRGB = (uchar *)[bitmapImage bitmapData];
for (y = 0; y < height; y++) {
to_i = (height-y-1)*width;
from_i = y*width;
memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*width);
}
}
else {
bitmapSize.width = width;
bitmapSize.height = height;
/* Tell cocoa image resolution is same as current system one */
[bitmapImage setSize:bitmapSize];
/* Convert the image in a RGBA 32bit format */
/* As Core Graphics does not support contextes with non premutliplied alpha,
we need to get alpha key values in a separate batch */
/* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:width
pixelsHigh:height
bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bitmapFormat:0
bytesPerRow:4*width
bitsPerPixel:32/*RGB format padded to 32bits*/];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
[bitmapImage draw];
[NSGraphicsContext restoreGraphicsState];
rasterRGB = (uchar *)[blBitmapFormatImageRGB bitmapData];
if (rasterRGB == NULL) {
[blBitmapFormatImageRGB release];
return NULL;
}
/* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:width
pixelsHigh:height
bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bitmapFormat:0
bytesPerRow:4*width
bitsPerPixel:32/* RGBA */];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
[bitmapImage draw];
[NSGraphicsContext restoreGraphicsState];
rasterRGBA = (uchar *)[blBitmapFormatImageRGBA bitmapData];
if (rasterRGBA == NULL) {
[blBitmapFormatImageRGB release];
[blBitmapFormatImageRGBA release];
return NULL;
}
/*Copy the image to ibuf, flipping it vertically*/
toIBuf = (uchar *)ibuf->rect;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
to_i = (height-y-1)*width + x;
from_i = y*width + x;
toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
}
}
[blBitmapFormatImageRGB release];
[blBitmapFormatImageRGBA release];
}
return ibuf;
}
ImBuf * qtime_fetchibuf (struct anim *anim, int position)
{
NSImage *frameImage;
QTTime time;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
ImBuf *ibuf;
if (anim == NULL) {
return (NULL);
}
if (position == anim->qtime->previousPosition+1) { //Optimize sequential read
[anim->qtime->movie stepForward];
frameImage = [anim->qtime->movie currentFrameImage];
anim->qtime->previousPosition++;
}
else {
time.timeScale = anim->qtime->durationScale;
time.timeValue = (anim->qtime->durationTime * position) / anim->qtime->framecount;
[anim->qtime->movie setCurrentTime:time];
frameImage = [anim->qtime->movie currentFrameImage];
anim->qtime->previousPosition = position;
}
if (frameImage == nil) {
if(QTIME_DEBUG) printf ("Error reading frame from Quicktime");
[pool drain];
return NULL;
}
ibuf = nsImageToiBuf(frameImage,anim->x, anim->y);
[pool drain];
return ibuf;
}
int startquicktime (struct anim *anim)
{
NSAutoreleasePool *pool;
NSArray* videoTracks;
NSSize frameSize;
QTTime qtTimeDuration;
NSDictionary *attributes;
anim->qtime = MEM_callocN (sizeof(QuicktimeMovie),"animqt");
if (anim->qtime == NULL) {
if(QTIME_DEBUG) printf("Can't alloc qtime: %s\n", anim->name);
return -1;
}
pool = [[NSAutoreleasePool alloc] init];
[QTMovie enterQTKitOnThread];
attributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithCString:anim->name
encoding:[NSString defaultCStringEncoding]], QTMovieFileNameAttribute,
[NSNumber numberWithBool:NO], QTMovieEditableAttribute,
nil];
anim->qtime->movie = [QTMovie movieWithAttributes:attributes error:NULL];
if (!anim->qtime->movie) {
if(QTIME_DEBUG) printf("qt: bad movie %s\n", anim->name);
MEM_freeN(anim->qtime);
if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
[QTMovie exitQTKitOnThread];
[pool drain];
return -1;
}
[anim->qtime->movie retain];
// sets Media and Track!
videoTracks = [anim->qtime->movie tracksOfMediaType:QTMediaTypeVideo];
if([videoTracks count] == 0) {
if(QTIME_DEBUG) printf("qt: no video tracks for movie %s\n", anim->name);
[anim->qtime->movie release];
MEM_freeN(anim->qtime);
if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
[QTMovie exitQTKitOnThread];
[pool drain];
return -1;
}
anim->qtime->media = [[videoTracks objectAtIndex:0] media];
[anim->qtime->media retain];
frameSize = [[anim->qtime->movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
anim->x = frameSize.width;
anim->y = frameSize.height;
if(anim->x == 0 && anim->y == 0) {
if(QTIME_DEBUG) printf("qt: error, no dimensions\n");
free_anim_quicktime(anim);
[pool drain];
return -1;
}
anim->qtime->ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect);
qtTimeDuration = [[anim->qtime->media attributeForKey:QTMediaDurationAttribute] QTTimeValue];
anim->qtime->durationTime = qtTimeDuration.timeValue;
anim->qtime->durationScale = qtTimeDuration.timeScale;
anim->qtime->framecount = [[anim->qtime->media attributeForKey:QTMediaSampleCountAttribute] longValue];
anim->qtime->previousPosition = -2; //Force seeking for first read
//fill blender's anim struct
anim->duration = anim->qtime->framecount;
anim->params = 0;
anim->interlacing = 0;
anim->orientation = 0;
anim->framesize = anim->x * anim->y * 4;
anim->curposition = 0;
[pool drain];
return 0;
}
int imb_is_a_quicktime (char *name)
{
NSImage *image;
int result;
NSAutoreleasePool *pool;
if(!G.have_quicktime) return 0;
pool = [[NSAutoreleasePool alloc] init];
// don't let quicktime image import handle these
if( BLI_testextensie(name, ".swf") ||
BLI_testextensie(name, ".txt") ||
BLI_testextensie(name, ".mpg") ||
BLI_testextensie(name, ".wav") ||
BLI_testextensie(name, ".mov") || // not as image, doesn't work
BLI_testextensie(name, ".avi") ||
BLI_testextensie(name, ".mp3")) return 0;
image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:name]];
if (image) {
[image release];
result = true;
}
else
result = false;
[pool drain];
return result;
}
ImBuf *imb_quicktime_decode(unsigned char *mem, int size, int flags)
{
struct ImBuf *ibuf = NULL;
NSSize bitmapSize;
uchar *rasterRGB = NULL;
uchar *rasterRGBA = NULL;
uchar *toIBuf = NULL;
int x, y, to_i, from_i;
NSData *data;
NSBitmapImageRep *bitmapImage;
NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA;
NSAutoreleasePool *pool;
if(!G.have_quicktime)
return NULL;
pool = [[NSAutoreleasePool alloc] init];
data = [NSData dataWithBytes:mem length:size];
bitmapImage = [[NSBitmapImageRep alloc] initWithData:data];
if (!bitmapImage) {
fprintf(stderr, "imb_cocoaLoadImage: error loading image\n");
[pool drain];
return NULL;
}
bitmapSize.width = [bitmapImage pixelsWide];
bitmapSize.height = [bitmapImage pixelsHigh];
/* Tell cocoa image resolution is same as current system one */
[bitmapImage setSize:bitmapSize];
/* allocate the image buffer */
ibuf = IMB_allocImBuf(bitmapSize.width, bitmapSize.height, 32/*RGBA*/, 0);
if (!ibuf) {
fprintf(stderr,
"imb_cocoaLoadImage: could not allocate memory for the image.\n");
[bitmapImage release];
[pool drain];
return NULL;
}
/* read in the image data */
if (!(flags & IB_test)) {
/* allocate memory for the ibuf->rect */
imb_addrectImBuf(ibuf);
/* Convert the image in a RGBA 32bit format */
/* As Core Graphics does not support contextes with non premutliplied alpha,
we need to get alpha key values in a separate batch */
/* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:bitmapSize.width
pixelsHigh:bitmapSize.height
bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:0
bytesPerRow:4*bitmapSize.width
bitsPerPixel:32/*RGB format padded to 32bits*/];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
[bitmapImage draw];
[NSGraphicsContext restoreGraphicsState];
rasterRGB = (uchar *)[blBitmapFormatImageRGB bitmapData];
if (rasterRGB == NULL) {
[bitmapImage release];
[blBitmapFormatImageRGB release];
[pool drain];
return NULL;
}
/* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:bitmapSize.width
pixelsHigh:bitmapSize.height
bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:0
bytesPerRow:4*bitmapSize.width
bitsPerPixel:32/* RGBA */];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
[bitmapImage draw];
[NSGraphicsContext restoreGraphicsState];
rasterRGBA = (uchar *)[blBitmapFormatImageRGBA bitmapData];
if (rasterRGBA == NULL) {
[bitmapImage release];
[blBitmapFormatImageRGB release];
[blBitmapFormatImageRGBA release];
[pool drain];
return NULL;
}
/*Copy the image to ibuf, flipping it vertically*/
toIBuf = (uchar *)ibuf->rect;
for (x = 0; x < bitmapSize.width; x++) {
for (y = 0; y < bitmapSize.height; y++) {
to_i = (bitmapSize.height-y-1)*bitmapSize.width + x;
from_i = y*bitmapSize.width + x;
toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
}
}
[blBitmapFormatImageRGB release];
[blBitmapFormatImageRGBA release];
}
/* release the cocoa objects */
[bitmapImage release];
[pool drain];
if (ENDIAN_ORDER == B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf);
/* return successfully */
return (ibuf);
}
#endif /* WITH_QUICKTIME */