SKTransitionController.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. //
  2. // SKTransitionController.m
  3. // Skim
  4. //
  5. // Created by Christiaan Hofman on 7/15/07.
  6. /*
  7. This software is Copyright (c) 2007-2018
  8. Christiaan Hofman. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions
  11. are met:
  12. - Redistributions of source code must retain the above copyright
  13. notice, this list of conditions and the following disclaimer.
  14. - Redistributions in binary form must reproduce the above copyright
  15. notice, this list of conditions and the following disclaimer in
  16. the documentation and/or other materials provided with the
  17. distribution.
  18. - Neither the name of Christiaan Hofman nor the names of any
  19. contributors may be used to endorse or promote products derived
  20. from this software without specific prior written permission.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. /*
  34. This code is based partly on Apple's AnimatingTabView example code
  35. and Ankur Kothari's AnimatingTabsDemo application <http://dev.lipidity.com>
  36. */
  37. #import "SKTransitionController.h"
  38. #import "NSBitmapImageRep_SKExtensions.h"
  39. //#import "NSView_SKExtensions.h"
  40. #import <CoreFoundation/CoreFoundation.h>
  41. #import <Quartz/Quartz.h>
  42. #include <unistd.h>
  43. #import <OpenGL/OpenGL.h>
  44. #import <OpenGL/gl.h>
  45. #import <objc/runtime.h>
  46. NSString *SKStyleNameKey = @"styleName";
  47. NSString *SKDurationKey = @"duration";
  48. NSString *SKShouldRestrictKey = @"shouldRestrict";
  49. NSString *KMTransitionStyleName = @"transitionStyle";
  50. NSString *KMDurationName = @"duration";
  51. NSString *KMShouldRestrictName = @"shouldRestrict";
  52. NSString *KMPageTransitionsName = @"pageTransitions";
  53. NSString *KMOldValueName = @"oldValue";
  54. NSString *KMNewValueName = @"newValue";
  55. #define kCIInputBacksideImageKey @"inputBacksideImage"
  56. #define kCIInputRectangleKey @"inputRectangle"
  57. #define WEAK_NULL NULL
  58. #pragma mark Private Core Graphics types and functions
  59. typedef int CGSConnection;
  60. typedef int CGSWindow;
  61. typedef enum _CGSTransitionType {
  62. CGSNone,
  63. CGSFade,
  64. CGSZoom,
  65. CGSReveal,
  66. CGSSlide,
  67. CGSWarpFade,
  68. CGSSwap,
  69. CGSCube,
  70. CGSWarpSwitch,
  71. CGSFlip
  72. } CGSTransitionType;
  73. typedef enum _CGSTransitionOption {
  74. CGSDown,
  75. CGSLeft,
  76. CGSRight,
  77. CGSInRight,
  78. CGSBottomLeft = 5,
  79. CGSBottomRight,
  80. CGSDownTopRight,
  81. CGSUp,
  82. CGSTopLeft,
  83. CGSTopRight,
  84. CGSUpBottomRight,
  85. CGSInBottom,
  86. CGSLeftBottomRight,
  87. CGSRightBottomLeft,
  88. CGSInBottomRight,
  89. CGSInOut
  90. } CGSTransitionOption;
  91. typedef struct _CGSTransitionSpec {
  92. uint32_t unknown1;
  93. CGSTransitionType type;
  94. CGSTransitionOption option;
  95. CGSWindow wid; // Can be 0 for full-screen
  96. float *backColour; // Null for black otherwise pointer to 3 CGFloat array with RGB value
  97. } CGSTransitionSpec;
  98. extern CGSConnection _CGSDefaultConnection(void) __attribute__((weak_import));
  99. extern OSStatus CGSNewTransition(const CGSConnection cid, const CGSTransitionSpec* spec, int *pTransitionHandle) __attribute__((weak_import));
  100. extern OSStatus CGSInvokeTransition(const CGSConnection cid, int transitionHandle, float duration) __attribute__((weak_import));
  101. extern OSStatus CGSReleaseTransition(const CGSConnection cid, int transitionHandle) __attribute__((weak_import));
  102. #pragma mark Check whether the above functions are actually defined at run time
  103. static BOOL CoreGraphicsServicesTransitionsDefined() {
  104. return _CGSDefaultConnection != WEAK_NULL &&
  105. CGSNewTransition != WEAK_NULL &&
  106. CGSInvokeTransition != WEAK_NULL &&
  107. CGSReleaseTransition != WEAK_NULL;
  108. }
  109. #define SDK_BEFORE(_version) (!defined(MAC_OS_X_VERSION_ ## _version) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_ ## _version)
  110. #pragma mark -
  111. typedef void(^SKTransitionAnimationProgressHandler)(CGFloat);
  112. @interface SKTransitionAnimation : NSAnimation {
  113. SKTransitionAnimationProgressHandler progressHandler;
  114. }
  115. @property (nonatomic, copy) SKTransitionAnimationProgressHandler progressHandler;
  116. @end
  117. #pragma mark -
  118. #if SDK_BEFORE(10_7)
  119. @interface NSOpenGLView (SKLionDeclarations)
  120. - (BOOL)wantsBestResolutionOpenGLSurface;
  121. - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
  122. @end
  123. #endif
  124. @interface SKTransitionView : NSOpenGLView {
  125. CIImage *image;
  126. CGFloat imageScale;
  127. CIContext *context;
  128. BOOL needsReshape;
  129. }
  130. @property (nonatomic, retain) CIImage *image;
  131. @property (nonatomic) CGFloat imageScale;
  132. @end
  133. #pragma mark -
  134. @implementation SKTransitionController
  135. // transitionStyle duration shouldRestrict pageTransitions
  136. @synthesize view;
  137. @dynamic hasTransition;
  138. + (NSArray *)transitionNames {
  139. static NSArray *transitionNames = nil;
  140. if (transitionNames == nil) {
  141. transitionNames = [NSArray arrayWithObjects:
  142. @"",
  143. @"CoreGraphics SKTransitionFade",
  144. @"CoreGraphics SKTransitionZoom",
  145. @"CoreGraphics SKTransitionReveal",
  146. @"CoreGraphics SKTransitionSlide",
  147. @"CoreGraphics SKTransitionWarpFade",
  148. @"CoreGraphics SKTransitionSwap",
  149. @"CoreGraphics SKTransitionCube",
  150. @"CoreGraphics SKTransitionWarpSwitch",
  151. @"CoreGraphics SKTransitionWarpFlip", nil];
  152. // get all the transition filters
  153. [CIPlugIn loadAllPlugIns];
  154. transitionNames = [[transitionNames arrayByAddingObjectsFromArray:[CIFilter filterNamesInCategory:kCICategoryTransition]] copy];
  155. }
  156. return transitionNames;
  157. }
  158. + (NSString *)nameForStyle:(SKAnimationTransitionStyle)style {
  159. if (style > SKNoTransition && style < [[self transitionNames] count])
  160. return [[self transitionNames] objectAtIndex:style];
  161. else
  162. return nil;
  163. }
  164. + (SKAnimationTransitionStyle)styleForName:(NSString *)name {
  165. NSUInteger idx = [[self transitionNames] indexOfObject:name];
  166. return idx == NSNotFound ? SKNoTransition : idx;
  167. }
  168. + (NSString *)localizedNameForStyle:(SKAnimationTransitionStyle)style {
  169. switch (style) {
  170. case SKNoTransition: return NSLocalizedString(@"No Transition", @"Transition name");
  171. case SKTransitionFade: return NSLocalizedString(@"Fade", @"Transition name");
  172. case SKTransitionZoom: return NSLocalizedString(@"Zoom", @"Transition name");
  173. case SKTransitionReveal: return NSLocalizedString(@"Reveal", @"Transition name");
  174. case SKTransitionSlide: return NSLocalizedString(@"Slide", @"Transition name");
  175. case SKTransitionWarpFade: return NSLocalizedString(@"Warp Fade", @"Transition name");
  176. case SKTransitionSwap: return NSLocalizedString(@"Swap", @"Transition name");
  177. case SKTransitionCube: return NSLocalizedString(@"Cube", @"Transition name");
  178. case SKTransitionWarpSwitch: return NSLocalizedString(@"Warp Switch", @"Transition name");
  179. case SKTransitionWarpFlip: return NSLocalizedString(@"Flip", @"Transition name");
  180. default: return [CIFilter localizedNameForFilterName:[self nameForStyle:style]];
  181. };
  182. }
  183. - (id)initForView:(NSView *)aView {
  184. self = [super init];
  185. if (self) {
  186. self.view = aView; // don't retain as it may retain us
  187. imageRect = NSZeroRect;
  188. _transitionStyle = SKNoTransition;
  189. _duration = 1.0;
  190. _shouldRestrict = YES;
  191. currentTransitionStyle = SKNoTransition;
  192. currentDuration = 1.0;
  193. currentShouldRestrict = YES;
  194. currentForward = YES;
  195. }
  196. return self;
  197. }
  198. - (void)dealloc {
  199. self.view = nil;
  200. }
  201. - (NSRect)convertRectToScreen:(NSRect)rect view:(NSView *)view{
  202. rect = [view convertRect:rect toView:nil];
  203. rect.origin = [[view window] convertBaseToScreen:rect.origin];
  204. return rect;
  205. }
  206. - (BOOL)hasTransition {
  207. return self.transitionStyle != SKNoTransition || self.pageTransitions != nil;
  208. }
  209. static inline CGRect scaleRect(NSRect rect, CGFloat scale) {
  210. return CGRectMake(scale * NSMinX(rect), scale * NSMinY(rect), scale * NSWidth(rect), scale * NSHeight(rect));
  211. }
  212. // rect and bounds are in pixels
  213. - (CIFilter *)transitionFilterForRect:(CGRect)rect bounds:(CGRect)bounds forward:(BOOL)forward initialCIImage:(CIImage *)initialCIImage finalCIImage:(CIImage *)finalCIImage {
  214. NSString *filterName = [[self class] nameForStyle:currentTransitionStyle];
  215. CIFilter *transitionFilter = [CIFilter filterWithName:filterName];
  216. [transitionFilter setDefaults];
  217. for (NSString *key in [transitionFilter inputKeys]) {
  218. id value = nil;
  219. if ([key isEqualToString:kCIInputExtentKey]) {
  220. CGRect extent = currentShouldRestrict ? rect : bounds;
  221. value = [CIVector vectorWithX:CGRectGetMinX(extent) Y:CGRectGetMinY(extent) Z:CGRectGetWidth(extent) W:CGRectGetHeight(extent)];
  222. } else if ([key isEqualToString:kCIInputAngleKey]) {
  223. CGFloat angle = forward ? 0.0 : M_PI;
  224. if ([filterName isEqualToString:@"CIPageCurlTransition"] || [filterName isEqualToString:@"CIPageCurlWithShadowTransition"])
  225. angle = forward ? -M_PI_4 : -3.0 * M_PI_4;
  226. value = [NSNumber numberWithDouble:angle];
  227. } else if ([key isEqualToString:kCIInputCenterKey]) {
  228. value = [CIVector vectorWithX:CGRectGetMidX(rect) Y:CGRectGetMidY(rect)];
  229. } else if ([key isEqualToString:kCIInputImageKey]) {
  230. value = initialCIImage;
  231. if (CGRectEqualToRect(rect, bounds) == NO)
  232. value = [value imageByCroppingToRect:rect];
  233. } else if ([key isEqualToString:kCIInputTargetImageKey]) {
  234. value = finalCIImage;
  235. if (CGRectEqualToRect(rect, bounds) == NO)
  236. value = [value imageByCroppingToRect:rect];
  237. } else if ([key isEqualToString:kCIInputShadingImageKey]) {
  238. static CIImage *inputShadingImage = nil;
  239. if (inputShadingImage == nil)
  240. inputShadingImage = [[CIImage alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"TransitionShading" withExtension:@"tiff"]];
  241. value = inputShadingImage;
  242. } else if ([key isEqualToString:kCIInputBacksideImageKey]) {
  243. value = initialCIImage;
  244. if (CGRectEqualToRect(rect, bounds) == NO)
  245. value = [value imageByCroppingToRect:rect];
  246. } else if ([[[[transitionFilter attributes] objectForKey:key] objectForKey:kCIAttributeClass] isEqualToString:@"CIImage"]) {
  247. // Scale and translate our mask image to match the transition area size.
  248. static CIImage *inputMaskImage = nil;
  249. if (inputMaskImage == nil)
  250. inputMaskImage = [[CIImage alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"TransitionMask" withExtension:@"jpg"]];
  251. CGRect extent = [inputMaskImage extent];
  252. CGAffineTransform transform;
  253. if ((CGRectGetWidth(extent) < CGRectGetHeight(extent)) != (CGRectGetWidth(rect) < CGRectGetHeight(rect))) {
  254. transform = CGAffineTransformMake(0.0, 1.0, 1.0, 0.0, 0.0, 0.0);
  255. transform = CGAffineTransformTranslate(transform, CGRectGetMinY(rect) - CGRectGetMinY(bounds), CGRectGetMinX(rect) - CGRectGetMinX(bounds));
  256. transform = CGAffineTransformScale(transform, CGRectGetHeight(rect) / CGRectGetWidth(extent), CGRectGetWidth(rect) / CGRectGetHeight(extent));
  257. } else {
  258. transform = CGAffineTransformMakeTranslation(CGRectGetMinX(rect) - CGRectGetMinX(bounds), CGRectGetMinY(rect) - CGRectGetMinY(bounds));
  259. transform = CGAffineTransformScale(transform, CGRectGetWidth(rect) / CGRectGetWidth(extent), CGRectGetHeight(rect) / CGRectGetHeight(extent));
  260. }
  261. value = [inputMaskImage imageByApplyingTransform:transform];
  262. } else continue;
  263. [transitionFilter setValue:value forKey:key];
  264. }
  265. return transitionFilter;
  266. }
  267. - (CIImage *)newCurrentImage {
  268. NSRect bounds = [view bounds];
  269. NSBitmapImageRep *contentBitmap = [view bitmapImageRepForCachingDisplayInRect:bounds];
  270. [contentBitmap clear];
  271. [view cacheDisplayInRect:bounds toBitmapImageRep:contentBitmap];
  272. return [[CIImage alloc] initWithBitmapImageRep:contentBitmap];
  273. }
  274. - (SKTransitionView *)transitionViewForRect:(NSRect)rect image:(CIImage *)image scale:(CGFloat)imageScale {
  275. SKTransitionView *transitionView = nil;
  276. if (window == nil) {
  277. transitionView = [[SKTransitionView alloc] init];
  278. if ([transitionView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)])
  279. [transitionView setWantsBestResolutionOpenGLSurface:YES];
  280. window = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
  281. [window setReleasedWhenClosed:NO];
  282. [window setIgnoresMouseEvents:YES];
  283. [window setContentView:transitionView];
  284. } else {
  285. transitionView = (SKTransitionView *)[window contentView];
  286. }
  287. [transitionView setImageScale:imageScale];
  288. [transitionView setImage:image];
  289. [transitionView setNeedsDisplay:YES];
  290. [window setFrame:[self convertRectToScreen:rect view:view] display:NO];
  291. [window orderBack:nil];
  292. [[view window] addChildWindow:window ordered:NSWindowAbove];
  293. return transitionView;
  294. }
  295. - (void)prepareAnimationForRect:(NSRect)rect from:(NSUInteger)fromIndex to:(NSUInteger)toIndex {
  296. currentTransitionStyle = self.transitionStyle;
  297. currentDuration = self.duration;
  298. currentShouldRestrict = self.shouldRestrict;
  299. currentForward = (toIndex >= fromIndex);
  300. NSUInteger idx = MIN(fromIndex, toIndex);
  301. if (fromIndex != NSNotFound && toIndex != NSNotFound && idx < [self.pageTransitions count]) {
  302. NSDictionary *info = [self.pageTransitions objectAtIndex:idx];
  303. id value;
  304. if ((value = [info objectForKey:SKStyleNameKey]))
  305. currentTransitionStyle = [[self class] styleForName:value];
  306. if ((value = [info objectForKey:SKDurationKey]) && [value respondsToSelector:@selector(doubleValue)])
  307. currentDuration = [value doubleValue];
  308. if ((value = [info objectForKey:SKShouldRestrictKey]) && [value respondsToSelector:@selector(boolValue)])
  309. currentShouldRestrict = [value boolValue];
  310. }
  311. if (currentTransitionStyle >= SKCoreImageTransition) {
  312. initialImage = [self newCurrentImage];
  313. // We don't want the window to draw the next state before the animation is run
  314. [[view window] disableFlushWindow];
  315. } else if (currentTransitionStyle > SKNoTransition && CoreGraphicsServicesTransitionsDefined()) {
  316. if (currentShouldRestrict) {
  317. initialImage = [self newCurrentImage];
  318. }
  319. // We don't want the window to draw the next state before the animation is run
  320. [[view window] disableFlushWindow];
  321. }
  322. imageRect = rect;
  323. }
  324. - (void)animateUsingCoreGraphics {
  325. CIImage *finalImage = nil;
  326. NSWindow *viewWindow = [view window];
  327. SKTransitionView *transitionView = nil;
  328. if (currentShouldRestrict) {
  329. NSRect bounds = [view bounds];
  330. CGFloat imageScale = CGRectGetWidth([initialImage extent]) / NSWidth(bounds);
  331. finalImage = [self newCurrentImage];
  332. CGAffineTransform transform = CGAffineTransformMakeTranslation(-imageScale * NSMinX(imageRect), -imageScale * NSMinY(imageRect));
  333. initialImage = [initialImage imageByApplyingTransform:transform];
  334. finalImage = [finalImage imageByApplyingTransform:transform];
  335. transitionView = [self transitionViewForRect:imageRect image:initialImage scale:imageScale];
  336. initialImage = nil;
  337. }
  338. // declare our variables
  339. int handle = -1;
  340. CGSTransitionSpec spec;
  341. // specify our specifications
  342. spec.unknown1 = 0;
  343. spec.type = currentTransitionStyle;
  344. spec.option = currentForward ? CGSLeft : CGSRight;
  345. spec.backColour = NULL;
  346. spec.wid = [(currentShouldRestrict ? window : viewWindow) windowNumber];
  347. // Let's get a connection
  348. CGSConnection cgs = _CGSDefaultConnection();
  349. // Create a transition
  350. CGSNewTransition(cgs, &spec, &handle);
  351. if (currentShouldRestrict) {
  352. [transitionView setImage:finalImage];
  353. [transitionView display];
  354. }
  355. // Redraw the window
  356. [viewWindow display];
  357. // Remember we disabled flushing in the previous method, we need to balance that.
  358. [viewWindow enableFlushWindow];
  359. [viewWindow flushWindow];
  360. CGSInvokeTransition(cgs, handle, currentDuration);
  361. // We need to wait for the transition to finish before we get rid of it, otherwise we'll get all sorts of nasty errors... or maybe not.
  362. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:currentDuration]];
  363. CGSReleaseTransition(cgs, handle);
  364. handle = 0;
  365. if (currentShouldRestrict) {
  366. [viewWindow removeChildWindow:window];
  367. [window orderOut:nil];
  368. [transitionView setImage:nil];
  369. }
  370. }
  371. - (void)animateUsingCoreImage {
  372. NSRect bounds = [view bounds];
  373. CGFloat imageScale = CGRectGetWidth([initialImage extent]) / NSWidth(bounds);
  374. CIImage *finalImage = [self newCurrentImage];
  375. CIFilter *transitionFilter = [self transitionFilterForRect:scaleRect(imageRect, imageScale) bounds:scaleRect(bounds, imageScale) forward:currentForward initialCIImage:initialImage finalCIImage:finalImage];
  376. SKTransitionView *transitionView = [self transitionViewForRect:bounds image:initialImage scale:imageScale];
  377. initialImage = nil;
  378. SKTransitionAnimation *animation = [[SKTransitionAnimation alloc] initWithDuration:currentDuration animationCurve:NSAnimationEaseInOut];
  379. [animation setProgressHandler:^(CGFloat value){
  380. [transitionFilter setValue:[NSNumber numberWithDouble:value] forKey:kCIInputTimeKey];
  381. [transitionView setImage:[transitionFilter valueForKey:kCIOutputImageKey]];
  382. [transitionView display];
  383. }];
  384. [animation startAnimation];
  385. // Update the view and its window, so it shows the correct state when it is shown.
  386. [view display];
  387. // Remember we disabled flushing in the previous method, we need to balance that.
  388. NSWindow *viewWindow = [view window];
  389. [viewWindow enableFlushWindow];
  390. [viewWindow flushWindow];
  391. [viewWindow removeChildWindow:window];
  392. [window orderOut:nil];
  393. [transitionView setImage:nil];
  394. }
  395. - (void)animateForRect:(NSRect)rect {
  396. if (NSEqualRects(imageRect, NSZeroRect))
  397. [self prepareAnimationForRect:rect from:NSNotFound to:NSNotFound];
  398. imageRect = NSIntegralRect(NSIntersectionRect(NSUnionRect(imageRect, rect), [view bounds]));
  399. if (currentTransitionStyle >= SKCoreImageTransition)
  400. [self animateUsingCoreImage];
  401. else if (currentTransitionStyle > SKNoTransition && CoreGraphicsServicesTransitionsDefined())
  402. [self animateUsingCoreGraphics];
  403. currentTransitionStyle = self.transitionStyle;
  404. currentDuration = self.duration;
  405. currentShouldRestrict = self.shouldRestrict;
  406. currentForward = YES;
  407. imageRect = NSZeroRect;
  408. }
  409. - (void)setTransitionStyle:(SKAnimationTransitionStyle)transitionStyle {
  410. SKAnimationTransitionStyle oldValue = _transitionStyle;
  411. _transitionStyle = transitionStyle;
  412. [self.delegate transitionController:self valueDidChanged:@{
  413. KMTransitionStyleName : @{
  414. KMOldValueName : @(oldValue),
  415. KMNewValueName : @(transitionStyle)
  416. }
  417. }];
  418. }
  419. - (void)setDuration:(CGFloat)duration {
  420. CGFloat oldValue = self.duration;
  421. _duration = duration;
  422. [self.delegate transitionController:self valueDidChanged:@{
  423. KMDurationName : @{
  424. KMOldValueName : @(oldValue),
  425. KMNewValueName : @(duration)
  426. }
  427. }];
  428. }
  429. - (void)setShouldRestrict:(BOOL)shouldRestrict {
  430. BOOL oldValue = self.shouldRestrict;
  431. _shouldRestrict = shouldRestrict;
  432. [self.delegate transitionController:self valueDidChanged:@{
  433. KMShouldRestrictName : @{
  434. KMOldValueName : @(oldValue),
  435. KMNewValueName : @(shouldRestrict)
  436. }
  437. }];
  438. }
  439. - (void)setPageTransitions:(NSArray *)pageTransitions {
  440. NSArray *oldValue = self.pageTransitions;
  441. _pageTransitions = pageTransitions;
  442. [self.delegate transitionController:self valueDidChanged:@{
  443. KMPageTransitionsName : @{
  444. KMOldValueName : oldValue ? oldValue : @[],
  445. KMNewValueName : pageTransitions ? pageTransitions : @[]
  446. }
  447. }];
  448. }
  449. @end
  450. #pragma mark -
  451. @implementation SKTransitionAnimation
  452. @synthesize progressHandler;
  453. - (void)dealloc {
  454. progressHandler = nil;
  455. }
  456. - (void)setCurrentProgress:(NSAnimationProgress)progress {
  457. [super setCurrentProgress:progress];
  458. if (progressHandler) progressHandler([self currentValue]);
  459. }
  460. @end
  461. #pragma mark -
  462. @implementation SKTransitionView
  463. @synthesize image, imageScale;
  464. + (NSOpenGLPixelFormat *)defaultPixelFormat {
  465. static NSOpenGLPixelFormat *pf;
  466. if (pf == nil) {
  467. NSOpenGLPixelFormatAttribute attr[] = {
  468. NSOpenGLPFAAccelerated,
  469. NSOpenGLPFANoRecovery,
  470. NSOpenGLPFAColorSize,
  471. 32,
  472. 0
  473. };
  474. pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
  475. }
  476. return pf;
  477. }
  478. - (void)dealloc {
  479. }
  480. - (void)reshape {
  481. needsReshape = YES;
  482. }
  483. - (void)prepareOpenGL {
  484. // Enable beam-synced updates.
  485. GLint parm = 1;
  486. [[self openGLContext] setValues:&parm forParameter:NSOpenGLCPSwapInterval];
  487. // Make sure that everything we don't need is disabled.
  488. // Some of these are enabled by default and can slow down rendering.
  489. glDisable(GL_ALPHA_TEST);
  490. glDisable(GL_DEPTH_TEST);
  491. glDisable(GL_SCISSOR_TEST);
  492. glDisable(GL_BLEND);
  493. glDisable(GL_DITHER);
  494. glDisable(GL_CULL_FACE);
  495. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  496. glDepthMask(GL_FALSE);
  497. glStencilMask(0);
  498. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  499. glHint(GL_TRANSFORM_HINT_APPLE, GL_FASTEST);
  500. needsReshape = YES;
  501. }
  502. - (CGFloat)backingScaleNew {
  503. if ([self respondsToSelector:@selector(convertSizeToBacking:)])
  504. return [self convertSizeToBacking:NSMakeSize(1.0, 1.0)].width;
  505. return 1.0;
  506. }
  507. - (void)updateMatrices {
  508. NSRect bounds = [self bounds];
  509. CGFloat scale = ([self respondsToSelector:@selector(wantsBestResolutionOpenGLSurface)] && [self wantsBestResolutionOpenGLSurface]) ? [self backingScaleNew] : 1.0;
  510. [[self openGLContext] update];
  511. glViewport(0, 0, scale * NSWidth(bounds), scale * NSHeight(bounds));
  512. glMatrixMode(GL_PROJECTION);
  513. glLoadIdentity();
  514. glOrtho(scale * NSMinX(bounds), scale * NSMaxX(bounds), scale * NSMinY(bounds), scale * NSMaxY(bounds), -1, 1);
  515. glMatrixMode(GL_MODELVIEW);
  516. glLoadIdentity();
  517. needsReshape = NO;
  518. }
  519. - (void)drawRect:(NSRect)rect {
  520. [[self openGLContext] makeCurrentContext];
  521. if (needsReshape)
  522. [self updateMatrices];
  523. glClearColor(0.0, 0.0, 0.0, 1.0);
  524. glClear(GL_COLOR_BUFFER_BIT);
  525. if (image) {
  526. CGFloat scale = ([self respondsToSelector:@selector(wantsBestResolutionOpenGLSurface)] && [self wantsBestResolutionOpenGLSurface]) ? [self backingScaleNew] : 1.0;
  527. NSRect bounds = [self bounds];
  528. if (context == nil) {
  529. NSOpenGLPixelFormat *pf = [self pixelFormat] ?: [[self class] defaultPixelFormat];
  530. context = [CIContext contextWithCGLContext:CGLGetCurrentContext() pixelFormat:[pf CGLPixelFormatObj] colorSpace:nil options:nil];
  531. }
  532. [context drawImage:image inRect:scaleRect(bounds, scale) fromRect:scaleRect(bounds, imageScale)];
  533. }
  534. glFlush();
  535. }
  536. @end
  537. @implementation SKTransitionController (KMExtension)
  538. //- (void)setDelegate:(id<SKTransitionControllerDelegate>)delegate {
  539. // objc_setAssociatedObject(self, @selector(delegate), delegate, OBJC_ASSOCIATION_ASSIGN);
  540. //}
  541. //
  542. //- (id<SKTransitionControllerDelegate>)delegate {
  543. // return objc_getAssociatedObject(self, @selector(delegate));
  544. //}
  545. @end