Tom*_* C. 13 performance processing-efficiency ios swift
问题
我正在构建一系列照片拼贴画,这些照片是我放在桌面视图上的.当图像数量达到tableview单元格宽度的边界时,我想让图像换行(这样我就可以在拼贴中显示图像行).目前我得到一排.如果需要其他信息,请随时告知.我很可能不会以最有效的方式接近这一点,因为随着阵列中使用的图像数量开始增加而存在延迟.(对此的任何反馈将非常感激).
Nota Bene
我正在创建拼贴图像.它实际上是一个图像.我想通过在内存中创建一个有效的列和行矩阵来安排拼贴 .然后我用图像填充这些图像.最后,我对生成的图像进行快照,并在需要时使用它.算法写入效率不高,只产生一行图像.我需要一个轻量级替代下面使用的算法.在这种情况下,我不相信UICollectionView会是一个有用的替代品.
伪代码
使用:Swift 2.0
class func collageImage (rect:CGRect, images:[UIImage]) -> UIImage {
let maxSide = max(rect.width / CGFloat(images.count), rect.height / CGFloat(images.count))
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.mainScreen().scale)
var xtransform:CGFloat = 0.0
for img in images {
let smallRect:CGRect = CGRectMake(xtransform, 0.0,maxSide, maxSide)
let rnd = arc4random_uniform(270) + 15
//draw in rect
img.drawInRect(smallRect)
//rotate img using random angle.
UIImage.rotateImage(img, radian: CGFloat(rnd))
xtransform += CGFloat(maxSide * 0.8)
}
let outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage
}
class func rotateImage(src: UIImage, radian:CGFloat) -> UIImage
{
// Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox = UIView(frame: CGRectMake(0,0, src.size.width, src.size.height))
let t: CGAffineTransform = CGAffineTransformMakeRotation(radian)
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap:CGContextRef = UIGraphicsGetCurrentContext()
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
// Rotate the image context
CGContextRotateCTM(bitmap, radian);
// Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-src.size.width / 2, -src.size.height / 2, src.size.width, src.size.height), src.CGImage)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Run Code Online (Sandbox Code Playgroud)
备选方案1
我稍微改进了我的解决方案.然而,如上所述,这个会将图像堆叠成列和行; 我的兴趣在于尽可能提高效率.所呈现的是我尝试制作最简单的可行方法.
警告
使用此图像生成的图像是倾斜的,而不是均匀分布在整个tableview单元格中.在tableview单元格上的高效,均匀分布将是最佳的.
class func collageImage (rect:CGRect, images:[UIImage]) -> UIImage {
let maxSide = max(rect.width / CGFloat(images.count), rect.height / CGFloat(images.count)) //* 0.80
//let rowHeight = rect.height / CGFloat(images.count) * 0.8
let maxImagesPerRow = 9
var index = 0
var currentRow = 1
var xtransform:CGFloat = 0.0
var ytransform:CGFloat = 0.0
var smallRect:CGRect = CGRectZero
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.mainScreen().scale)
for img in images {
let x = ++index % maxImagesPerRow //row should change when modulus is 0
//row changes when modulus of counter returns zero @ maxImagesPerRow
if x == 0 {
//last column of current row
//xtransform += CGFloat(maxSide)
smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
//reset for new row
++currentRow
xtransform = 0.0
ytransform = (maxSide * CGFloat(currentRow - 1))
} else {
//not a new row
if xtransform == 0 {
//this is first column
//draw rect at 0,ytransform
smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
xtransform += CGFloat(maxSide)
} else {
//not the first column so translate x, ytransform to be reset for new rows only
smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
xtransform += CGFloat(maxSide)
}
}
//draw in rect
img.drawInRect(smallRect)
}
let outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage
}
Run Code Online (Sandbox Code Playgroud)
备选方案2
下面给出的替代方案缩放图像,使它们总是填充矩形(在我的情况下是tableview单元格).随着添加更多图像,它们被缩放以适合矩形的宽度.当图像满足每行的最大图像数时,它们会换行.这是期望的行为,发生在内存中,相对较快,并且包含在我在UIImage类上扩展的简单类函数中.我仍然对任何能够更快地提供相同功能的算法感兴趣.
Nota Bene:我不相信添加更多UI对于实现上述效果很有用.因此,我正在寻求一种更有效的编码算法.
class func collageImage (rect:CGRect, images:[UIImage]) -> UIImage {
let maxImagesPerRow = 9
var maxSide : CGFloat = 0.0
if images.count >= maxImagesPerRow {
maxSide = max(rect.width / CGFloat(maxImagesPerRow), rect.height / CGFloat(maxImagesPerRow))
} else {
maxSide = max(rect.width / CGFloat(images.count), rect.height / CGFloat(images.count))
}
var index = 0
var currentRow = 1
var xtransform:CGFloat = 0.0
var ytransform:CGFloat = 0.0
var smallRect:CGRect = CGRectZero
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.mainScreen().scale)
for img in images {
let x = ++index % maxImagesPerRow //row should change when modulus is 0
//row changes when modulus of counter returns zero @ maxImagesPerRow
if x == 0 {
//last column of current row
smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
//reset for new row
++currentRow
xtransform = 0.0
ytransform = (maxSide * CGFloat(currentRow - 1))
} else {
//not a new row
smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
xtransform += CGFloat(maxSide)
}
//draw in rect
img.drawInRect(smallRect)
}
let outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage
}
Run Code Online (Sandbox Code Playgroud)
效率测试
Reda Lemeden在这篇博客文章中提供了一些有关如何在Instruments内测试这些CG调用的程序性见解.他还指出了Andy Matuschak(UIKit团队)关于屏幕外渲染的一些特点的一些有趣的笔记.我可能仍然没有正确利用CIImage解决方案,因为初始结果显示解决方案在尝试强制GPU利用率时变慢.
Tra*_*vis 14
为了在内存中构建拼贴,并尽可能高效,我建议调查核心图像.您可以组合多个CIF过滤器来创建输出图像.
您可以将CIAffineTransform过滤器应用于每个图像以对其进行排列(如果需要,使用CICrop将其裁剪为大小),然后使用CISourceOverCompositing过滤器将它们组合在一起.在您要求输出之前,Core Image不会处理任何内容; 因为这一切都发生在GPU上,所以它快速而有效.
这是一些代码.为了理解,我尽量让它尽可能接近你的例子.如果我从头开始使用核心图像,那么我的结构代码并不一定如此.
class func collageImage (rect: CGRect, images: [UIImage]) -> UIImage {
let maxImagesPerRow = 3
var maxSide : CGFloat = 0.0
if images.count >= maxImagesPerRow {
maxSide = max(rect.width / CGFloat(maxImagesPerRow), rect.height / CGFloat(maxImagesPerRow))
} else {
maxSide = max(rect.width / CGFloat(images.count), rect.height / CGFloat(images.count))
}
var index = 0
var currentRow = 1
var xtransform:CGFloat = 0.0
var ytransform:CGFloat = 0.0
var smallRect:CGRect = CGRectZero
var composite: CIImage? // used to hold the composite of the images
for img in images {
let x = ++index % maxImagesPerRow //row should change when modulus is 0
//row changes when modulus of counter returns zero @ maxImagesPerRow
if x == 0 {
//last column of current row
smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
//reset for new row
++currentRow
xtransform = 0.0
ytransform = (maxSide * CGFloat(currentRow - 1))
} else {
//not a new row
smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
xtransform += CGFloat(maxSide)
}
// Note, this section could be done with a single transform and perhaps increase the
// efficiency a bit, but I wanted it to be explicit.
//
// It will also use the CI coordinate system which is bottom up, so you can translate
// if the order of your collage matters.
//
// Also, note that this happens on the GPU, and these translation steps don't happen
// as they are called... they happen at once when the image is rendered. CIImage can
// be thought of as a recipe for the final image.
//
// Finally, you an use core image filters for this and perhaps make it more efficient.
// This version relies on the convenience methods for applying transforms, etc, but
// under the hood they use CIFilters
var ci = CIImage(image: img)!
ci = ci.imageByApplyingTransform(CGAffineTransformMakeScale(maxSide / img.size.width, maxSide / img.size.height))
ci = ci.imageByApplyingTransform(CGAffineTransformMakeTranslation(smallRect.origin.x, smallRect.origin.y))!
if composite == nil {
composite = ci
} else {
composite = ci.imageByCompositingOverImage(composite!)
}
}
let cgIntermediate = CIContext(options: nil).createCGImage(composite!, fromRect: composite!.extent())
let finalRenderedComposite = UIImage(CGImage: cgIntermediate)!
return finalRenderedComposite
}
Run Code Online (Sandbox Code Playgroud)
您可能会发现您的CIImage旋转不正确.您可以使用以下代码更正它:
var transform = CGAffineTransformIdentity
switch ci.imageOrientation {
case UIImageOrientation.Up:
fallthrough
case UIImageOrientation.UpMirrored:
println("no translation necessary. I am ignoring the mirrored cases because in my code that is ok.")
case UIImageOrientation.Down:
fallthrough
case UIImageOrientation.DownMirrored:
transform = CGAffineTransformTranslate(transform, ci.size.width, ci.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
case UIImageOrientation.Left:
fallthrough
case UIImageOrientation.LeftMirrored:
transform = CGAffineTransformTranslate(transform, ci.size.width, 0)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
case UIImageOrientation.Right:
fallthrough
case UIImageOrientation.RightMirrored:
transform = CGAffineTransformTranslate(transform, 0, ci.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
}
ci = ci.imageByApplyingTransform(transform)
Run Code Online (Sandbox Code Playgroud)
请注意,此代码忽略修复多个镜像案例.我会把它作为练习留给你,但它的要点就在这里.
如果你已经优化了你的核心图像处理,那么在这一点上你看到的任何减速可能是由于你的CIImage变成了UIImage; 那是因为你的图像必须从GPU转换到CPU.如果要跳过此步骤以向用户显示结果,则可以.只需将结果直接渲染到GLKView即可.您可以在用户想要保存拼贴的位置始终转换为UIImage或CGImage.
// this would happen during setup
let eaglContext = EAGLContext(API: .OpenGLES2)
glView.context = eaglContext
let ciContext = CIContext(EAGLContext: glView.context)
// this would happen whenever you want to put your CIImage on screen
if glView.context != EAGLContext.currentContext() {
EAGLContext.setCurrentContext(glView.context)
}
let result = ViewController.collageImage(glView.bounds, images: images)
glView.bindDrawable()
ciContext.drawImage(result, inRect: glView.bounds, fromRect: result.extent())
glView.display()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3968 次 |
| 最近记录: |