iPad 3 图片解压缩测试

之前翻译过Avoiding Image Decompression Sickness[iOS]如何避免图像解压缩的时间开销,现在iPad 3出了,原文也有了后续iPad 3 Image Decompression Benchmarked,所以我也就简单再翻译个后续,推荐先看一眼之前那篇,以下内容都是建立在之前的基础上的。

此次原文没有太多重要内容,挑重点简要翻译,括号内大部分是我的注释,少部分原文,很好辨别。

iPad !!!(iPad 3,这是跟k-on!!或者working!!学的么?),根据GeekBench测试,CPU几乎没有变化(因为A5X只是显卡4核心,CPU还是和iPad 2一样),根据GLBenchmark的结果,苹果对iPad 3做了很大优化,是的在Retina屏幕上的祯数几乎和iPad 2相同。

一位Australia的同学帮忙在他的iPad 3上帮我们运行了benchmark,并把测试结果发给了我们。(之前的结果参考之前的那篇文章,我就不贴图了)

我们预想iPad 3解压缩速度将是iPad 2的2-4倍,iPad 2默认的launch image需要100ms来解压缩并显示,如果在iPad 3上也有相同的速度就可以了。

测试结果如下

PNG Crushed, 1024*768 (init+decode+draw)

  • iPad 2: 5 ms + 89 ms + 18 ms = 113 ms
  • iPad 3: 1 ms + 50 ms + 18 ms = 69 ms

-47%

JPG 80%, 1024*768 (init+decode+draw)

  • iPad 2: 2 ms + 32 ms + 18 ms = 52 ms
  • iPad 3: 2 ms + 32 ms + 17 ms = 51 ms

-2%

PNG Crushed, 2048*1536 (init+decode+draw)

  • iPad 2: 5 ms + 266 ms + 96 ms = 368 ms
  • iPad 3: 2 ms + 171 ms + 66 ms = 238 ms

-33%

JPG 80%, 2048*1536 (init+decode+draw)

  • iPad 2: 1 ms + 121 ms + 69 ms = 192 ms
  • iPad 3: 1 ms + 122 ms + 66 ms = 189 ms

-2%

粗略看下我们发现iPad 3的速度并没有我们预想的那样有4倍的提升。退一步讲,iPad 3的效率的确像Apple描述的那样有2倍的提升(-50%)(A5X比A5显卡快了一倍),但这仅仅针对处理PNG Crushed的情形(-47%),在处理JPEG的时候速度仅仅提高2%,这点儿提升也许是来自SSD读取速度的提升,看起来苹果的开发人员似乎都将JPEG遗忘了。

至于"感觉上" iPad 3是不是比iPad 2快了呢?

恐怕不是,iPad 2显示launch image要113ms,iPad 3上却要消耗238ms,如果没有任何优化的话,一个应用启动要多消耗125ms(记住CPU性能是相同的)。

因此最大的问题就是,杂志类应用开发者更期望这种“感觉上”的速度提升,而实际上即使使用80%全屏大小的JPEG也只能得到5-19(128ms-52ms)的祯数,这就导致了如果在主线程绘制图片,从一个页Scroll到下一个页时就会产生一个明显的卡顿。

总结

现在我们知道为什么新的核心命名为A5X了,这仅仅是与A5相同的CPU配上了一个性能稍高点的显示核心。这个“X”我们计算出来大概为1.4,但仅仅针对PNG,对于JPEG,也许在下一个iOS更新中体现吧。

目前发生这种情况的原因很可能是这样:针对JPEG图片的硬件加速处理模块因为某种情况在iOS 5.1中被遗忘了,或者干脆没有准备好。如果事实果真如此,那么对于苹果来说这真的很尴尬。

最后一段原文打广告。大概就是建议用大图的人暂时只留下必须的小图片,使用他们做的DTCoreText来渲染富文本(蛋疼核心文本?LOL)。(要我说按苹果这么个折腾法矢量图才是王道啊!)

–以上–

[WWDC][AppFramework]Session 121 Understanding UIKit Rendering

Session 121

Understanding UIKit Rendering


个人感觉是WWDC 2011里比较精品的Session,强烈建议各位同学反复看几遍视频,可以了解到许多iOS的实现机制

提要
  • UIView and CALayer
  • CATransaction and when views get rendered
  • Quality and Performance

     

    • Clipping and masking
    • Edge anti-aliasing
    • Group opacity
    • Shadows

UIView and CALayer

Geometry

iPhone 4
UIKit 左上为原点,320 x 480, 横屏原点不变 CoreAnimation 左上为原点,640 x 960

iPad 2
UIKit 左上原点 768 x 1024 CoreAnimation 左下为原点 1024 x 768

关于坐标问题直接参看官方文档就可以了

Drawing
  • view.layer.contents
  • UIImageView
  • drawRect

参考Practical Drawing for iOS Developers

UIImageView 与 drawRect 对比

放大一个图片,drawRect会占用更多内存,UIImageView只占原始图片所占内存,没有额外开销 用一个基础图片(tile)填充一个更大的区域,drawRect同样占用更多内存,UIImageView会在性能与最小内存占用之间选择一个平衡点(将多个tile拼成一个大一些的tile)

CATransaction

转换在runloop结束时生效

Performance
Avoid Offscreen Rendering

“正常"的渲染都直接在当前可视区域进行 屏幕外渲染,让显卡指向一个屏幕外的内存,alloc那段内存并开始绘图,渲染结束后,显卡指向主屏幕,使用之前在屏幕外渲染来在屏幕内绘图,这个过程需要消耗额外的内存,显卡从一个buffer切换到另一个buffer也需要消耗额外的时间,还需要刷新缓冲区,所以在屏幕内的和屏幕外的buffer之间切换代价是很昂贵的。 由于CoreAnimation是逐帧渲染的,所以屏幕外的渲染将会带来极大的开销, 每次屏幕刷新都会有屏幕外的渲染发生,比如一个scrollView,每次scroll的时候,都会在屏幕内进行一些渲染,屏幕外进行一些渲染,然后又将屏幕外的绘制到屏幕内。

Layer Rasterization

这个算是offscreen rendering的一种解决方法吧,屏幕外的渲染会被缓存起来,这样就不用每次都重新绘制一下屏幕外的buffer. ([CALayer setShouldRasterize:])
使用这个特性要保证你的内容是固定不变的,因为如果是个变化的内容,那么每一帧的内容都会与前一帧不同,缓存不能被重复利用,反而影响了性能。


比如要实现图中所示效果
CuriousFrog

Clipping and Masking

这里有两个技术点,一是圆角的实现,我们可以直接用CALayer的cornerRadius来达到这个效果,虽然很方,但是效率很低。二是倒影的实现,实现倒影的方法是首先取得上半部分原始View hierarchy的一个副本,y轴翻转绘制在下面,然后根据剃度来设置mask,但是这个mask开销是很大的,一方面要对整个View hierachy进行操作,另一方面需要进行Offscreen Render。

下面的方法效率都比较低,虽然很方便 * [CALayer cornerRadius] * [CALayer mask] * [UIView clipsToBounds] 或 [CALayer masksToBounds]

一些Trick * [CALayer contentsRect] 设置需要渲染的范围,本例中并不适用。 * [UIView drawRect:] 预先渲染好来防止每一帧都进行渲染 * Transparent overlay 比如圆角就可以通过覆盖黑色来让它看起来是圆的,但是本例中还要看到圆角后面的内容,所以不采用

Group Opacity

设置Group Opacity和没设置的区别

GroupOpacity

可以通过一下几个方法实现: * 在Info.plist里设置UIViewGroupOpacity键值 这样就又会产生offscreen rendering * 在drawRect:里预先渲染 * 设置shouldRasterize为YES

Shadowed text

使用CALayer的shadow属性很方便,但是开销也很昂贵

Demo

首先给出检测offscreen rendering的方法:使用Instruments的Core Animation模板,选中Color Offscreen-Rendered Yellow就可以让所有的Offscreen rendering都染成黄色

首先使用

UIBezierPath *capsulePath = [UIBezierPath bezierPathWithRoundedRect:myBounds cornerRadius:myBounds.size.height / 2.0];
[[UIColor grayColor] set];
[capsulePath fill];

来绘制圆角的背景,免去setCornerRadius的开销。

接着使用

CGContextSetShadow(context, CGSizeMake(3.0f, 3.0f), 10.0f);
CGContextSetShadowWithColor(context, CGSizeMake(3.0f, 3.0f), 10.0f, [UIColor colorWithWhite:0 alpha:.4].CGColor);
 
[[UIColor blackColor] set];
[_labelTitle drawInRect:titleFrame withFont:[UIFont boldSystemFontOfSize:28]];

来代替setShadowOffset: 和 setShadowColor:,这里注意一点,CoreGraphics在你执行绘制的代码后立即就会进行绘制,所以要在绘制之前先把属性设置好;而Core Animation在Transaction commit之后才开始渲染,所以属性可以在之后设置。

倒影的绘制,预先绘制成一个图片来防止逐帧渲染

if (_isReflected) {
    // create mask image
 
    UIGraphicsBeginImageContext([self bounds].size);
    CGColorSpaceRef deviceGray = CGColorSpaceCreateDeviceGray();
    CGFloat locations[2] = {1.0, 0.0);
    NSArray *colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:0.0 alpha:0.0] CGColor], (id)[[UIColor colorWithWhite:0..0 alpha:0.5] CGColor], nil];
    CGGradientRef gradient = CGGradientCreateWithColors(deviceGray, (CFArrayRef)colors, locations);
 
    CGContextDrawLinearGradient(UIGraphicsGetCurrentContext(), gradient, CGPointMake(0, 0), CGPointMake(0, [self bounds].size.height), 0);
    CGGradientRelease(gradient);
    CGColorSpaceRelease(deviceGray);
    CGImageRef maskImage = [UIGraphicsGetImageFromCurrentImageContext() CGImage];
    UIGraphicsEndImageContext();
 
    CGContextClipToMask(context, myBounds, maskImage);
}
Edge Antialiasing

抗锯齿的实现有下面两个方法

  • Info.plist里设置UIViewEdgeAntialiasing键值 开销很大
  • 用一个像素的边来模拟

代码如下:

- (UIImageView *)createImageViewForImage:(UIImage *)image
{
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    [imageView setContentMode:UIViewContentModeScaleAspectFit];
 
    //Add a shadow around the image.
    [[imageView layer] setShadowOffset:CGSizeMake(10, 10)];
    [[imageView layer] setShadowColor:[[UIColor blackColor] CGColor]];
    [[imageView layer] setShadowOpacity:.5];
 
    //Rotate the image by some small random angle.
    [imageView setTransform:CGAffineTransformMakeRotation((((float)random() / RAND_MAX) * MAX_ROTATION) - MAX_ROTATION / 2)];
 
    //Rather than using Core Animation's edge antialiasing, which requires an off-screen rendering pass, we'll draw our image int own image with an empty 1px border on each side. That way, when it's rotated, the edges will appear smooth because the outermost pixels of the original image will be sampled with the clear pixels in the outer border we add. This sampling is much faster but not as high-quality.
 
    CGSize imageSizeWithBorder = CGSizeMake([image size].width + 2, [image size].height + 2);
    UIGraphicsBeginImageContext(imageSizeWithBorder);
    // The image starts off filled with clear pixels, so we don't need to explicitly fill them here.
    [image drawInRect:(CGRect){{1,1}, [image size]}];
    [imageView setImage:UIGraphicsGetImageFromCurrentImageContext()];
    UIGraphicsEndImageContext();
 
    // We no longer need CA's edge antialiasing on this layer
    [[imageView layer] setEdgeAntialiasingMask:0];
 
    return imageView;
}

阴影绘制:

- (void)layoutImageView:(UIImageView *)imageView inFrame:(CGRect)celFrame withAnimationDuration:(NSTimeInterval)duration
{
    // The image view may be rotated, so set its frame in terms of the identity transform and then reapply the transform.
    CGAffineTransform imageViewTransform = [image View transform];
    [imageView setTransform:CGAffineTransformIdentity];
    [imageView setFrame:CenteredRectInRect(CGRectMake(0, 0, [imageView image].size.width, [imageView image].size.height), celFrame)];
    [imageView setTransform:imageViewTransform];
 
    // Save Core Animation a pass to figure out where the transparent pixels are by informing it explicitly of the contents's shape.
    CGPathRef oldPath = CGPathRetain([[imageView layer] shadowPath]);
    [[imageView layer] setShadowPath:[[UIBezierPath bezierPathWithRect:[imageView bounds]] CGPath]];
 
    // Since the layer's delegate (its UIView) will not create an action for this change (via the CALayerDelegate method actionForLayer:forKey:), we must explicitly create the animation between these values.
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"shadowPath"];
    [pathAnimation setFromValue:(id)oldPath];
    [pathAnimation setToValue:(id)[[imageView layer] shadowPath]];
    [pathAnimation setDuration:duration];
    [pathAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    [pathAnimation setRemovedOnCompletion:YES];
 
    [[imageView layer] addAnimation:pathAnimation forKey:@"shadowPath"];
    CGPathRelease(oldPath);
}

以上就是这个session中个人觉得比较精髓的部分了,一些地方用中文表达的也不是太好,代码都是手打的,难免有错误,如有问题还请留言指正 对这篇文章涉及的内容感兴趣的同学也看看这篇文章(在墙外,如有需要我可以帮忙做个pdf传上来)

—以上—

10.7.2+iCloud+iOS5简评

冒着各种各样的危险升级了(我是黑苹果),简评一下,或者说是吐槽

10.7.2:

  • LaunchPad图标变大了,终于变大了,之前的小图标真是点3次也点不中啊,不过总感觉比以前丑了.
  • 支持拖动文件到全屏程序了,在此之前我的mail是全屏模式,添加附件特别但疼,开始的时候先恢复成窗口模式然后再托文件进去,后来用了usb overdrive把鼠标滚轮左右设置成切换桌面快捷键,接着按住鼠标拖动文件,同时拨动滚轮,成功,费死劲了.
  • 黑苹果N卡DSDT驱动的话,需要在DSDT里面小改一下,不然开机黑屏(显示器没信号):
    "device_type", 
    Buffer (0x0D)
    {
        "NVDA,Geforce"
    },
    改成

     

    "device_type", 
    Buffer (0x0C)
    {
        "NVDA,Parent"
    },
    以上方法来自 pcbeta.com,最好在升级10.7.2前改,除非你喜欢在Win下搞DSDT

     

  • 更新中提到safari提升性能,这个估计不跑分也看不出啥来.另外还有说温度低了,开机快了的,我是黑苹果,没什么参考价值,就不说了.
  • 10.16更新10.7.2的Application文件夹(我放在dock上了)图标加载不卡了.

iCloud,这个测试版的时候就一直在用,跟测试版没有太大区别:

  • 同步联系人,日历很happy,不用依赖与Google了,要么两个一起还重复,毕竟Mac用不了Google的Exchange,只有iOS可以
  • iCloud网页端的Mail挺好用的,编辑文字选项比较多,可以单独设置字体,颜色等等

iOS 5

  • 一些UI真的感觉变丑了,iPod变成音乐+视频了,感觉那个音乐图标没有iPod图标好看,等完美越狱出了看看怎么改回去.
  • 一些HDU提示感觉长的都跟Cydia里面的提示似的,大大的汉字提示显得有些恶心
  • iCloud备份目前不好使,提示备份失败,原因不明
  • FaceTime激活似乎不需要发短信了,AppleID也可以,注意提示你激活FaceTime时选取消
  • 同步时不会锁屏,正常使用,很方便
  • iMessage发送方法:进入特定联系人,选择发送信息,选择邮箱发送.直接在发信息时添加联系人似乎不行.蓝色背景的信息是通过iMessage发送的,绿色是短信,偏好设置里面有对iMessage的一些设置建议看看.
  • 设置里面多了好多细节设置,比如"定位"里面的"系统服务"
  • Newsstand就是报刊杂志App放在一起,支持图标改变,没啥神奇之处,iTunes里面还是当App处理,内容还是InApp Purchase
  • Wifi-Sync不错,以后可以少占一个USB口了,就是需要先在iTunes里面设置好同步内容,如要更改就只能插到电脑上.因为Wifi同步之前iTunes看不到设备,现在也能在不连接USB的情况下在iTunes里看到设备了
    具体使用方法:
    1.iDevice通过USB连接到电脑
    2.选中"通过Wi-Fi与此设备同步(必须有这步,要不然下一步做不了)
    3.在你的iDevice>>设置>>通用>>iTunesWiFi同步 里面选择同步
  • 自带软件功能细节增强,还有一些都介绍烂了的就不在这里罗列了.

总结:Lion渐渐成熟一些了,其他感觉都是初期,出的有些仓促,各种细节问题不少,不过也可以理解,毕竟现在竞争越来越激烈了.

–以上–

iMac使用感受及Lion下TrackPad及Magic Mouse试用对比

前几天给别人的iMac装Win 7(国内的破网银,大家都知道怎么回事,话说iMac也是我忽悠人家买的,所以这活就落我身上了).终于同时玩了下TrackPad 和Magic Mouse,不过还是先扯点iMac,TrackPad和MM在第二部分:

  • 屏幕真心不错,就算调的很亮也没有刺眼的感觉,不知道跟那个黑边有没有关系
  • 屏幕上那个摄像头摄像的角度极好,清晰度也不错.不会像我们买的山寨摄像头那样摄出来的效果看着跟记者暗访似的.居家旅行,视频聊天必备
  • 屏幕倾角调节手感极好,有条件的找个iMac试试去,估计也就苹果能考虑这么细节
  • 吸入式光驱比较容易放进去一些小东西,比如SD卡

接下来就是TrackPad和Magic Mouse了:

  • TrackPad感觉没有像一些人说的那么神,比MM强多少,MM在Lion下手势虽然不多,但是装MagicPrefs以后,设置几个手势启动个LaunchPad,MissionControl啥的还是绰绰有余的,有MM完全没必要去追TrackPad
  • 如果你手指比较容易出汗用MM和TrackPad就会比较杯具,手感很涩,尤其是Swipe Up的时候,所以Lion下Swipe"自然方向"的设定就更显杯具了,毕竟我们还是向下滚动页面(也就是Lion下的Swipe Up)多一些.至于怎么判断你的手指是否容易出汗,建议你找个MM,TrackPad或者质感差不多的塑料,玻璃,或者手机贴膜试试,我反正是有些杯具,如果用时间长了真没准能磨起泡.
  • Magic Mouse超容易划花,尤其是边上放着个铝制的TrackPad或者Wireless Keyboard的时候,一碰就划一道(似乎好想有卖贴膜的). TrackPad不怎么移动也就没有这方面问题.
  • Magic Mouse没感觉手感有什么问题,可能是我没用过那些竞技鼠标吧.
  • Win7下装了Boot Camp驱动,Magic Mouse和TrackPad也没像一些人说的那样悲剧了,我用起来完全正常,跟mac下手感一样,不知道那些说Win下杯具的人怎么用的.

如果你认为花几个月研究各种硬件价格性能,研究如何在电脑城防骗,研究杀毒软件,研究win7激活Offic激活.颠颠的去跑去电脑城花一下午装个机器回家最先做的居然是跑个分,再上网晒晒是一件很杯具的事的话还是多花点钱,图个省心,买个Mac吧.虽然性能比不上你花同等价钱买的台式机,但是你真的需要那么高的机能么.至少也比你装的那台漂亮多了.

说着说着就成软文了

–以上–