Skip to content

Commit dd3cadf

Browse files
authored
2.4.4 release (#21)
- update ImageSpeedComparison sample - clean up nullability all over - image transform support - image view fetch helper category for UIImageView - added by Brandon Carpenter - add support to change cached image's identifier - update WebP codec (0.6.0) - fixe WebP codec - added by protosphere - other misc bug fixes
1 parent efd6b25 commit dd3cadf

File tree

98 files changed

+2406
-622
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+2406
-622
lines changed

CHANGELOG.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,42 @@
22

33
## Info
44

5-
**Document version:** 2.3.0
5+
**Document version:** 2.4.4
66

7-
**Last updated:** 03/24/2017
7+
**Last updated:** 05/29/2017
88

99
**Author:** Nolan O'Brien
1010

1111
## History
1212

13+
### 2.4.4 - protosphere
14+
15+
- Fix WebP Encoder (channels could be mixed up)
16+
17+
### 2.4.3
18+
19+
- Update WebP codec to v0.6.0
20+
21+
### 2.4.2
22+
23+
- Add support for changing a cached image's identifier
24+
25+
### 2.4.1 - Brandon Carpenter
26+
27+
- Add category to `UIImageView` for setting a `TIPImageViewFetchHelper`
28+
- offers convenience of __TIP__ work encapsulated in `TIPImageViewFetchHelper` without needing to refactor onto `TIPImageView`
29+
30+
### 2.4.0
31+
32+
- Add image transform support to __TIP__
33+
- A `TIPImageFetchTransform` can be set globally or on a specific request
34+
- Clean up some nullability notation
35+
- Update ImageSpeedComparison sample app
36+
- add WebP
37+
- add more bitrates
38+
- add optional blur transform (for progress)
39+
- add smaller PJPEG
40+
1341
### 2.3.0
1442

1543
- Reduce memory pressure w/ `@autoreleasepool` blocks on GCD queues in _TIP_

ImageSpeedComparison/AppDelegate.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
//
88

99
#import <TwitterImagePipeline/TwitterImagePipeline.h>
10+
1011
#import "AppDelegate.h"
12+
#import "TIPXWebPCodec.h"
1113

1214
@interface AppDelegate ()
1315
@end
@@ -20,6 +22,7 @@ @implementation AppDelegate
2022
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2123
{
2224
[TIPGlobalConfiguration sharedInstance].logger = self;
25+
[TIPImageCodecCatalogue sharedInstance][TIPXImageTypeWebP] = [[TIPXWebPCodec alloc] init];
2326
return YES;
2427
}
2528

ImageSpeedComparison/Base.lproj/Main.storyboard

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11542" systemVersion="15G1108" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES" initialViewController="vXZ-lx-hvc">
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES" initialViewController="vXZ-lx-hvc">
33
<device id="retina4_7" orientation="portrait">
44
<adaptation id="fullscreen"/>
55
</device>
66
<dependencies>
77
<deployment identifier="iOS"/>
8-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
8+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
99
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
1010
</dependencies>
1111
<scenes>
@@ -18,7 +18,7 @@
1818
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1919
<subviews>
2020
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="Rfb-30-5ml">
21-
<rect key="frame" x="0.0" y="0.0" width="375" height="375.5"/>
21+
<rect key="frame" x="0.0" y="0.0" width="375" height="375"/>
2222
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
2323
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
2424
</imageView>
@@ -76,10 +76,22 @@ Final Size: N/A</string>
7676
<action selector="select:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="uIx-1Y-PTI"/>
7777
</connections>
7878
</button>
79+
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="Gg0-Us-8qK">
80+
<rect key="frame" x="321" y="394" width="51" height="31"/>
81+
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
82+
</switch>
83+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Blur Scans" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="rp1-wo-6ci">
84+
<rect key="frame" x="215" y="394" width="106" height="31"/>
85+
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
86+
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="17"/>
87+
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
88+
<nil key="highlightedColor"/>
89+
</label>
7990
</subviews>
8091
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
8192
</view>
8293
<connections>
94+
<outlet property="_blurSwitch" destination="Gg0-Us-8qK" id="6ZC-P1-fSO"/>
8395
<outlet property="_imageView" destination="Rfb-30-5ml" id="pYy-OF-30U"/>
8496
<outlet property="_pickerView" destination="kSh-Lk-LJa" id="viX-dR-bir"/>
8597
<outlet property="_progressView" destination="Oju-55-fXh" id="Cxg-1Q-NxH"/>

ImageSpeedComparison/ViewController.m

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// Copyright (c) 2015 Twitter. All rights reserved.
77
//
88

9+
#import <Accelerate/Accelerate.h>
910
#import <TwitterImagePipeline/TwitterImagePipeline.h>
1011

1112
#import "TIPTestImageFetchDownloadInternalWithStubbing.h"
@@ -27,8 +28,13 @@
2728
{ @"public.png", "PNG", "twitterfied.png", NO, NO },
2829
{ @"public.tiff", "TIFF", "twitterfied.tiff", NO, NO },
2930
{ @"com.compuserve.gif", "GIF", "fireworks_original.gif", NO, YES },
31+
{ @"com.google.webp", "WEBP", "twitterfied.webp", NO, NO },
32+
{ @"public.jpeg", "Small-PJPEG", "twitterfied.small.pjpg", YES, NO },
3033
};
3134

35+
static const NSUInteger kBitrateDribble = 4 * 1000;
36+
static const NSUInteger kBitrate80sModem = 16 * 1000;
37+
static const NSUInteger kBitrateBad2G = 56 * 1000;
3238
static const NSUInteger kBitrate2G = 128 * 1000; // 2G
3339
static const NSUInteger kBitrate2GPlus = kBitrate2G * 2; // 2.5G
3440
static const NSUInteger kBitrate3G = kBitrate2GPlus * 2; // 3G
@@ -37,14 +43,15 @@
3743
static const NSUInteger kBitrate4GPlus = kBitrate4G * 2; // ~LTE
3844

3945
static const NSUInteger sBitrates[] = {
46+
kBitrateDribble, kBitrate80sModem, kBitrateBad2G,
4047
kBitrate2G, kBitrate2GPlus,
4148
kBitrate3G, kBitrate3GPlus,
4249
kBitrate4G, kBitrate4GPlus
4350
};
4451

45-
static const NSUInteger kDefaultBitrateIndex = 2;
52+
static const NSUInteger kDefaultBitrateIndex = 5;
4653

47-
@interface ViewController () <UIPickerViewDataSource, UIPickerViewDelegate, TIPImageFetchRequest, TIPImageFetchDelegate>
54+
@interface ViewController () <UIPickerViewDataSource, UIPickerViewDelegate, TIPImageFetchRequest, TIPImageFetchDelegate, TIPImageFetchTransformer>
4855
@end
4956

5057
@implementation ViewController
@@ -56,6 +63,7 @@ @implementation ViewController
5663
IBOutlet UIButton *_startButton;
5764
IBOutlet UIPickerView *_pickerView;
5865
IBOutlet UILabel *_resultsLabel;
66+
IBOutlet UISwitch *_blurSwitch;
5967

6068
BOOL _selectingSpeed;
6169
UITapGestureRecognizer *_tapper;
@@ -314,6 +322,148 @@ - (UIViewContentMode)targetContentMode
314322
return _imageView.contentMode;
315323
}
316324

325+
- (id<TIPImageFetchTransformer>)transformer
326+
{
327+
return _blurSwitch.on ? self : nil;
328+
}
329+
330+
- (UIImage *)tip_transformImage:(UIImage *)image withProgress:(float)progress hintTargetDimensions:(CGSize)targetDimensions hintTargetContentMode:(UIViewContentMode)targetContentMode forImageFetchOperation:(TIPImageFetchOperation *)op
331+
{
332+
if (!image.CGImage) {
333+
return nil;
334+
}
335+
336+
BOOL shouldScaleFirst = NO;
337+
const CGSize imageDimension = [image tip_dimensions];
338+
CGFloat blurRadius = 0;
339+
if (progress < 0 || progress >= 1.f) {
340+
// placeholder?
341+
id<TIPImageFetchRequest> request = op.request;
342+
if (![request respondsToSelector:@selector(options)]) {
343+
return nil;
344+
}
345+
if ((request.options & TIPImageFetchTreatAsPlaceholder) == 0) {
346+
return nil;
347+
}
348+
if (targetDimensions.width <= imageDimension.width && targetDimensions.height <= imageDimension.height) {
349+
return nil;
350+
}
351+
blurRadius = log2(MAX(targetDimensions.height / imageDimension.height, targetDimensions.width / targetDimensions.width));
352+
shouldScaleFirst = YES;
353+
} else {
354+
// progressive
355+
if (progress > .65f) {
356+
return nil;
357+
}
358+
const CGFloat divisor = (1.f + progress) * 2.f;
359+
blurRadius = log2(MAX(imageDimension.width, imageDimension.height)) / divisor;
360+
blurRadius *= 1.f - progress;
361+
}
362+
363+
if (blurRadius < 0.5) {
364+
return nil;
365+
}
366+
367+
// TRANSFORM!
368+
if (shouldScaleFirst) {
369+
image = [image tip_scaledImageWithTargetDimensions:targetDimensions contentMode:targetContentMode];
370+
}
371+
UIImage *transformed = [self applyBlurToImage:image withRadius:blurRadius];
372+
NSAssert(CGSizeEqualToSize([image tip_dimensions], [transformed tip_dimensions]), @"sizing missmatch!");
373+
return transformed;
374+
}
375+
376+
// This is a modified version of Apple's 2013 WWDC sample code for UIImage(ImageEffects)
377+
// https://developer.apple.com/library/content/samplecode/UIImageEffects/Listings/UIImageEffects_UIImageEffects_m.html
378+
- (UIImage *)applyBlurToImage:(UIImage *)image withRadius:(CGFloat)blurRadius
379+
{
380+
// Check pre-conditions
381+
if (image.size.width < 1 || image.size.height < 1) {
382+
return nil;
383+
}
384+
if (!image.CGImage) {
385+
return nil;
386+
}
387+
388+
CGRect imageRect = { CGPointZero, image.size };
389+
UIImage *effectImage = image;
390+
391+
const BOOL hasBlur = blurRadius > __FLT_EPSILON__;
392+
if (hasBlur) {
393+
UIGraphicsBeginImageContextWithOptions(image.size, NO, [[UIScreen mainScreen] scale]);
394+
CGContextRef effectInContext = UIGraphicsGetCurrentContext();
395+
CGContextScaleCTM(effectInContext, 1.0, -1.0);
396+
CGContextTranslateCTM(effectInContext, 0, -image.size.height);
397+
CGContextDrawImage(effectInContext, imageRect, image.CGImage);
398+
399+
vImage_Buffer effectInBuffer;
400+
effectInBuffer.data = CGBitmapContextGetData(effectInContext);
401+
effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
402+
effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
403+
effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
404+
405+
UIGraphicsBeginImageContextWithOptions(image.size, NO, [[UIScreen mainScreen] scale]);
406+
CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
407+
vImage_Buffer effectOutBuffer;
408+
effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
409+
effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
410+
effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
411+
effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
412+
413+
// A description of how to compute the box kernel width from the Gaussian
414+
// radius (aka standard deviation) appears in the SVG spec:
415+
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
416+
//
417+
// For larger values of 's' (s >= 2.0), an approximation can be used: Three
418+
// successive box-blurs build a piece-wise quadratic convolution kernel, which
419+
// approximates the Gaussian kernel to within roughly 3%.
420+
//
421+
// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
422+
//
423+
// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
424+
//
425+
const CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
426+
uint32_t radius = (uint32_t)floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
427+
if (radius % 2 != 1) {
428+
radius += 1; // force radius to be odd so that the three box-blur methodology works.
429+
}
430+
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
431+
vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
432+
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
433+
434+
effectImage = UIGraphicsGetImageFromCurrentImageContext();
435+
UIGraphicsEndImageContext();
436+
UIGraphicsEndImageContext();
437+
}
438+
439+
// Set up output context.
440+
UIGraphicsBeginImageContextWithOptions(image.size, NO, [[UIScreen mainScreen] scale]);
441+
CGContextRef outputContext = UIGraphicsGetCurrentContext();
442+
CGContextScaleCTM(outputContext, 1.0, -1.0);
443+
CGContextTranslateCTM(outputContext, 0, -image.size.height);
444+
445+
// Draw base image.
446+
CGContextDrawImage(outputContext, imageRect, image.CGImage);
447+
448+
// Draw effect image.
449+
if (hasBlur) {
450+
CGContextSaveGState(outputContext);
451+
CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
452+
CGContextRestoreGState(outputContext);
453+
}
454+
455+
// Output image is ready.
456+
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
457+
UIGraphicsEndImageContext();
458+
459+
return outputImage;
460+
}
461+
462+
//- (NSDictionary *)progressiveLoadingPolicies
463+
//{
464+
// return @{ TIPImageTypeJPEG : [[TIPGreedyProgressiveLoadingPolicy alloc] init] };
465+
//}
466+
317467
//- (NSDictionary *)progressiveLoadingPolicies
318468
//{
319469
// if ([sImageTypes[_imageTypeIndex].type isEqualToString:TIPImageTypeJPEG2000] || [sImageTypes[_imageTypeIndex].type isEqualToString:TIPImageTypePNG]) {
50.8 KB
Binary file not shown.

ImageSpeedComparison/twitterfied.webp

161 KB
Binary file not shown.

TIP Sample App/TwitterAPI.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ - (void)loadAccount
6767
- (void)_api_loadAccount:(dispatch_block_t)complete
6868
{
6969
if (_account) {
70-
complete();
70+
if (complete) {
71+
complete();
72+
}
7173
return;
7274
}
7375

0 commit comments

Comments
 (0)