// // ElisController.m // Elis Colors // // Created by 柳 on 09/09/12. // Copyright 2009 __MyCompanyName__. All rights reserved. // #import "ElisController.h" static float convertQTTimeToSecond(QTTime t) { return (float)t.timeValue/t.timeScale; } @implementation ElisController - (void)awakeFromNib { layers = [[NSMutableArray alloc] init]; _animationLayerFactory = [[ElisAnimationLayerFactory alloc] init]; playing = NO; recording = NO; ProjectMovieSize = CGRectMake(0, 0, 640, 480); // カスタムフィルタを初期化。 [ElisCustomFilter class]; NSLog(@"Building effects ..."); // エフェクトメニューを構築。 [self buildEffectMenu]; } - (CALayer*)createNewLayer:(NSString*)path { ElisLayer* layer; CALayer* alayer; ElisMedia* m; layer = [[ElisLayer alloc] init]; m = [[ElisMedia alloc] initWithMovieFile:path]; layer.media = m; alayer = [_animationLayerFactory createNewMovieLayer:convertQTTimeToSecond([m duration]) name:[path lastPathComponent]]; // [alayer setValue:layer forKey:@"ElisLayer"]; [layer setAlayer:alayer]; [layers addObject:layer]; return alayer; } // 絶対時間qttimeと関係があるレイヤーをまとめて返す。 - (void)getFrameForTime:(QTTime)qttime result:(NSMutableArray*)layerSet { int i, size = [layers count]; ElisLayer* l; // 再生時間オーバー。停止。 if(convertQTTimeToSecond(qttime) >= hipTime){ [_mainView stopDisplayLink]; [self stop:qttime]; return; } globalCurrentTime = qttime; [self moveSliderTo:qttime]; [_tableController reload]; // GCDなりOpenMPなりで並列化すること。 for(i = 0; i < size; i++){ l = [layers objectAtIndex:i]; if([l isInclude:qttime]){ if(playing) [l play]; [layerSet addObject:l]; }else{ if(playing) [l stop]; } } } - (void)play:(QTTime)time { NSMutableArray* interestLayers = [[NSMutableArray alloc] init]; int i, size = [layers count]; hipTime = [self getHipTime]; for(i = 0; i < size; i++) if([[layers objectAtIndex:i] isInclude:time]) [interestLayers addObject:[layers objectAtIndex:i]]; size = [interestLayers count]; for(i = 0; i < size; i++) [(ElisLayer*)[interestLayers objectAtIndex:i] play]; playing = YES; } - (void)stop:(QTTime)time { NSMutableArray* interestLayers = [[NSMutableArray alloc] init]; int i, size = [layers count]; // hipTime = [self getHipTime]; // // for(i = 0; i < size; i++) //// if([[layers objectAtIndex:i] isInclude:time]) // [interestLayers addObject:[layers objectAtIndex:i]]; // // size = [interestLayers count]; // // for(i = 0; i < size; i++) // [(ElisLayer*)[interestLayers objectAtIndex:i] stop]; for(i = 0; i < size; i++) [[layers objectAtIndex:i] stop]; playing = NO; globalCurrentTime = time; _currentTime = time; } - (IBAction)startPlay:(id)sender { hipTime = [self getHipTime]; QTTime currentTime = QTMakeTime([timeSlider floatValue] * hipTime * DEFAULT_FPS, DEFAULT_FPS); [self seek:currentTime]; [self play:currentTime]; [_mainView startDisplayLink]; } - (IBAction)stopPlay:(id)sender { hipTime = [self getHipTime]; QTTime currentTime = QTMakeTime([timeSlider floatValue] * hipTime * DEFAULT_FPS, DEFAULT_FPS); [self stop:currentTime]; [_mainView stopDisplayLink]; } - (float)getHipTime { int i, size = [layers count]; float hipTimeSecond = 0.0f, candidate; for(i = 0; i < size; i++){ candidate = convertQTTimeToSecond([[layers objectAtIndex:i] mapping].time) + convertQTTimeToSecond([[layers objectAtIndex:i] mapping].duration); if(candidate > hipTimeSecond) hipTimeSecond = candidate; } return hipTimeSecond; } - (void)moveSliderTo:(QTTime)time { float now = convertQTTimeToSecond(time); [timeSlider setFloatValue:now/hipTime]; [timeCodeField setStringValue:QTStringFromTime(QTMakeTime(now * DEFAULT_FPS, DEFAULT_FPS))]; [_timeLineController movePlaybackBar:now*timeLineScale]; } - (IBAction)timeSliderChanged:(id)sender { float seconds = [sender floatValue]; QTTime currentTime = QTMakeTime(seconds * hipTime * DEFAULT_FPS, DEFAULT_FPS); [timeCodeField setStringValue:QTStringFromTime(currentTime)]; _currentTime = currentTime; globalCurrentTime = currentTime; [self refresh]; } - (void)seek:(QTTime)time { int i, size = [layers count]; [_mainView seek:time]; for(i = 0; i < size; i++) [[layers objectAtIndex:i] seek:time]; } - (void)refresh { if(playing) return; hipTime = [self getHipTime]; [_mainView getFrameForQTTime:_currentTime]; } - (IBAction)deleteSelectLayer:(id)sender { CALayer* selected = [_timeLineController getSelectLayer]; ElisLayer* layer = [selected valueForKey:@"ElisLayer"]; [layers removeObject:layer]; [selected removeFromSuperlayer]; [_timeLineController removeSelectLayer]; [_tableController createPropertyTable:nil]; [_tableController reload]; [self refresh]; } - (IBAction)recordingStateChanged:(id)sender { recording = !recording; } - (IBAction)removeAllKeyFrame:(id)sender { [_tableController removeAllKeyframe]; [self refresh]; } - (IBAction)removeEffect:(id)sender { [_tableController removeEffect]; [self refresh]; } - (IBAction)writeToFile:(id)sender { ElisWriter* writer = [[ElisWriter alloc] init]; [writer setView:_mainView]; [writer setContoller:self]; [writer write:@"/Users/yanagi/Desktop/result.mov"]; } // エフェクトのメニュー項目を構築 - (void)buildEffectMenu { NSArray* filterNames; CIFilter* filter; NSDictionary* attrs; filterNames = [CIFilter filterNamesInCategories:[NSArray arrayWithObjects: kCICategoryDistortionEffect, kCICategoryGeometryAdjustment, kCICategoryCompositeOperation, kCICategoryHalftoneEffect, kCICategoryColorAdjustment, kCICategoryColorEffect, kCICategoryTransition, kCICategoryTileEffect, kCICategoryGenerator, kCICategoryGradient, kCICategoryStylize, kCICategorySharpen, kCICategoryBlur, nil]]; NSString* name; NSArray* inputKeys; id elm; NSMenuItem* item = [[NSMenuItem alloc] init], *child; NSMenu* menu = [[NSMenu alloc] init]; int c = 0; [item setTitle:@"Effect"]; [menu setTitle:@"Effect"]; filterNames = [CIFilter filterNamesInCategory:kCICategoryBuiltIn]; for(name in filterNames){ filter = [CIFilter filterWithName:name]; attrs = [filter attributes]; inputKeys = [filter inputKeys]; for(elm in inputKeys){ if([elm isEqualToString:@"inputImage"]) continue; if([[[attrs valueForKey:elm] valueForKey:kCIAttributeClass] isEqualToString:@"CIVector"]){ if([[[attrs valueForKey:elm] valueForKey:kCIAttributeDefault] count] == 2) goto ok; } if(!([[[attrs valueForKey:elm] valueForKey:kCIAttributeClass] isEqualToString:@"NSNumber"] || [[[attrs valueForKey:elm] valueForKey:kCIAttributeClass] isEqualToString:@"CIColor"])){ goto jimp; } } ok: child = [[[NSMenuItem alloc] init] autorelease]; [child setTitle:name]; [child setAction:@selector(effectMenuPushed:)]; [child setTarget:self]; c++; [menu addItem:child]; // [menu addItemWithTitle:name action:@selector(effectMenuPushed:) keyEquivalent:@""]; jimp: ; // [filter release]; } NSLog(@"%d effetcs usable.", c); [item setSubmenu:menu]; [menu setAutoenablesItems:NO]; [item setEnabled:YES]; [menu release]; [[NSApp mainMenu] insertItem:item atIndex:5]; [item setTarget:self]; } - (void)effectMenuPushed:(id)sender { CALayer* l; l = [_timeLineController getSelectLayer]; [[l valueForKey:@"ElisLayer"] addEffect:[sender title]]; [self refresh]; [_tableController createPropertyTable:[l valueForKey:@"ElisLayer"]]; } @end