123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- #import "SVGKFastImageView.h"
- @interface SVGKFastImageView ()
- @property(nonatomic,readwrite) NSTimeInterval timeIntervalForLastReRenderOfSVGFromMemory;
- @property (nonatomic, strong) NSDate* startRenderTime, * endRenderTime;
- @property (nonatomic) BOOL didRegisterObservers, didRegisterInternalRedrawObservers;
- @end
- @implementation SVGKFastImageView
- {
- NSString* internalContextPointerBecauseApplesDemandsIt;
- }
- @synthesize image = _image;
- @synthesize tileRatio = _tileRatio;
- @synthesize disableAutoRedrawAtHighestResolution = _disableAutoRedrawAtHighestResolution;
- @synthesize timeIntervalForLastReRenderOfSVGFromMemory = _timeIntervalForLastReRenderOfSVGFromMemory;
- - (id)init
- {
- NSAssert(false, @"init not supported, use initWithSVGKImage:");
-
- return nil;
- }
- - (id)initWithCoder:(NSCoder *)aDecoder
- {
- self = [super initWithCoder:aDecoder];
- if( self )
- {
- [self populateFromImage:nil];
- }
- return self;
- }
- -(id)initWithFrame:(CGRect)frame
- {
- self = [super initWithFrame:frame];
- if( self )
- {
- [self populateFromImage:nil];
- }
- return self;
- }
- - (id)initWithSVGKImage:(SVGKImage*) im
- {
- self = [super init];
- if (self)
- {
- [self populateFromImage:im];
- }
- return self;
- }
- - (void)populateFromImage:(SVGKImage*) im
- {
- #if SVGKIT_MAC && USE_SUBLAYERS_INSTEAD_OF_BLIT
-
- self.wantsLayer = YES;
- #endif
- if( im == nil )
- {
- SVGKitLogWarn(@"[%@] WARNING: you have initialized an SVGKImageView with a blank image (nil). Possibly because you're using Storyboards or NIBs which Apple won't allow us to decorate. Make sure you assign an SVGKImage to the .image property!", [self class]);
- }
-
- self.image = im;
- self.frame = CGRectMake( 0,0, im.size.width, im.size.height );
- self.tileRatio = CGSizeZero;
- #if SVGKIT_UIKIT
- self.backgroundColor = [UIColor clearColor];
- #else
- self.layer.backgroundColor = [NSColor clearColor].CGColor;
- #endif
- }
- - (void)setImage:(SVGKImage *)image {
-
- if( !internalContextPointerBecauseApplesDemandsIt ) {
- internalContextPointerBecauseApplesDemandsIt = @"Apple wrote the addObserver / KVO notification API wrong in the first place and now requires developers to pass around pointers to fake objects to make up for the API deficicineces. You have to have one of these pointers per object, and they have to be internal and private. They serve no real value.";
- }
-
- [_image removeObserver:self forKeyPath:@"size" context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- _image = image;
-
-
- if( self.disableAutoRedrawAtHighestResolution )
- ;
- else {
- [self addInternalRedrawOnResizeObservers];
- [_image addObserver:self forKeyPath:@"size" options:NSKeyValueObservingOptionNew context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- }
-
-
- if (!self.didRegisterObservers) {
- self.didRegisterObservers = true;
- [self addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- [self addObserver:self forKeyPath:@"tileRatio" options:NSKeyValueObservingOptionNew context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- [self addObserver:self forKeyPath:@"showBorder" options:NSKeyValueObservingOptionNew context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- }
- }
- -(void) addInternalRedrawOnResizeObservers
- {
- if (self.didRegisterInternalRedrawObservers) return;
- self.didRegisterInternalRedrawObservers = true;
- [self addObserver:self forKeyPath:@"layer" options:NSKeyValueObservingOptionNew context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- [self.layer addObserver:self forKeyPath:@"transform" options:NSKeyValueObservingOptionNew context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- }
- -(void) removeInternalRedrawOnResizeObservers
- {
- if (!self.didRegisterInternalRedrawObservers) return;
- [self removeObserver:self forKeyPath:@"layer" context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- [self.layer removeObserver:self forKeyPath:@"transform" context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- self.didRegisterInternalRedrawObservers = false;
- }
- -(void)setDisableAutoRedrawAtHighestResolution:(BOOL)newValue
- {
- if( newValue == _disableAutoRedrawAtHighestResolution )
- return;
-
- _disableAutoRedrawAtHighestResolution = newValue;
-
- if( self.disableAutoRedrawAtHighestResolution )
- {
- [self removeInternalRedrawOnResizeObservers];
- }
- else
- {
- [self addInternalRedrawOnResizeObservers];
- }
- }
- - (void)dealloc
- {
- if( self.disableAutoRedrawAtHighestResolution )
- ;
- else
- [self removeInternalRedrawOnResizeObservers];
-
- if (self.didRegisterObservers) {
- [self removeObserver:self forKeyPath:@"image" context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- [self removeObserver:self forKeyPath:@"tileRatio" context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- [self removeObserver:self forKeyPath:@"showBorder" context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- }
-
- [_image removeObserver:self forKeyPath:@"size" context:(__bridge void * _Nullable)(internalContextPointerBecauseApplesDemandsIt)];
- _image = nil;
-
- }
- -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
- {
- if( [keyPath isEqualToString:@"transform"] && CGSizeEqualToSize( CGSizeZero, self.tileRatio ) )
- {
-
- [self.image.CALayerTree removeFromSuperlayer];
- #if SVGKIT_UIKIT
- [self setNeedsDisplay];
- #else
- [self setNeedsDisplay:YES];
- #endif
- }
- else
- {
-
- if( self.disableAutoRedrawAtHighestResolution )
- ;
- else
- {
- #if SVGKIT_UIKIT
- [self setNeedsDisplay];
- #else
- [self setNeedsDisplay:YES];
- #endif
- }
- }
- }
- -(void)drawRect:(CGRect)rect
- {
- self.startRenderTime = self.endRenderTime = [NSDate date];
-
-
- CGRect imageBounds = CGRectMake( 0,0, self.image.size.width, self.image.size.height );
-
-
-
- int cols = ceil(self.tileRatio.width);
- int rows = ceil(self.tileRatio.height);
-
- if( cols < 1 )
- cols = 1;
- if( rows < 1 )
- rows = 1;
-
-
- CGSize scaleConvertImageToView;
- CGSize tileSize;
- if( cols == 1 && rows == 1 )
- {
- #ifdef USE_SUBLAYERS_INSTEAD_OF_BLIT
- if( self.image.CALayerTree.superlayer == self.layer )
- {
- [super drawRect:rect];
- return;
- }
- else
- {
- [self.layer addSublayer:self.image.CALayerTree];
- return;
- }
- #else
- scaleConvertImageToView = CGSizeMake( self.bounds.size.width / imageBounds.size.width, self.bounds.size.height / imageBounds.size.height );
- tileSize = self.bounds.size;
- #endif
- }
- else
- {
- scaleConvertImageToView = CGSizeMake( self.bounds.size.width / (self.tileRatio.width * imageBounds.size.width), self.bounds.size.height / ( self.tileRatio.height * imageBounds.size.height) );
- tileSize = CGSizeMake( self.bounds.size.width / self.tileRatio.width, self.bounds.size.height / self.tileRatio.height );
- }
-
-
-
- #if SVGKIT_UIKIT
- CGContextRef context = UIGraphicsGetCurrentContext();
- #else
- CGContextRef context = SVGKGraphicsGetCurrentContext();
- #endif
- for( int k=0; k<rows; k++ )
- for( int i=0; i<cols; i++ )
- {
- CGContextSaveGState(context);
-
- CGContextTranslateCTM(context, i * tileSize.width, k * tileSize.height );
- CGContextScaleCTM( context, scaleConvertImageToView.width, scaleConvertImageToView.height );
-
- [self.image renderInContext:context];
-
- CGContextRestoreGState(context);
- }
-
-
- if( self.showBorder )
- {
- [[UIColor blackColor] set];
- CGContextStrokeRect(context, rect);
- }
-
- self.endRenderTime = [NSDate date];
- self.timeIntervalForLastReRenderOfSVGFromMemory = [self.endRenderTime timeIntervalSinceDate:self.startRenderTime];
- }
- #if SVGKIT_MAC
- static CGContextRef SVGKGraphicsGetCurrentContext(void) {
- NSGraphicsContext *context = NSGraphicsContext.currentContext;
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wunguarded-availability"
- if ([context respondsToSelector:@selector(CGContext)]) {
- return context.CGContext;
- } else {
- return context.graphicsPort;
- }
- #pragma clang diagnostic pop
- }
- #endif
- @end
|