如何在没有抗锯齿的情况下调整 UIImage 的大小?
How to resize a UIImage without antialiasing?
我正在开发 iOS 棋盘游戏。我想给董事会一种 "texture".
我做的是我创建了这个非常小的图像(真的很小,一定要仔细看):
然后我将此图像传递给 UIColor.init(patternImage:)
初始化程序以创建一个 UIColor
即此图像。我用这个 UIColor
填充了一些正方形 UIBezierPath
s,结果如下所示:
该图像的所有副本都完美对齐,并且它们形成了许多对角线。到目前为止一切顺利。
现在在iPad上,我画的方块会变大,而且这些方块的边框也会变大。我已经成功计算出正方形的笔划宽度和大小应该是多少,所以这不是问题。
但是,由于 iPad 上的方块较大,每个方块上的对角线会更多。我不要那个。我需要将非常小的图像调整为更大的图像,并且大小取决于正方形的笔划宽度。具体来说,调整后的图片宽度应该是笔画宽度的两倍。
我写了这个扩展来调整图像大小,改编自这个 :
extension UIImage {
func resized(toWidth newWidth: CGFloat) -> UIImage {
let scale = newWidth / size.width
let newHeight = size.height * scale
UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0)
self.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
并这样称呼它:
// this is the code I used to draw a single square
let path = UIBezierPath(rect: CGRect(origin: point(for: Position(x, y)), size: CGSize(width: squareLength, height: squareLength)))
UIColor.black.setStroke()
path.lineWidth = strokeWidth
// this is the line that's important!
UIColor(patternImage: #imageLiteral(resourceName:
"texture").resized(toWidth: strokeWidth * 2)).setFill()
path.fill()
path.stroke()
现在游戏板在iPhone上看起来像这样:
您可能需要稍微放大网页才能明白我的意思。董事会现在看起来非常丑陋。您可以看到每个图像副本的 "borders"。我不想要这个。不过,在 iPad 上,棋盘看起来不错。我怀疑只有当我缩小图像尺寸时才会发生这种情况。
我想这可能是因为我在使用扩展程序时发生了抗锯齿。我发现 this post and this post 关于去除抗锯齿,但前者似乎在图像视图中这样做,而我在我的自定义 GameBoardView
的 draw(_:)
方法中这样做。后者的方案好像和我用的一模一样
如何在不使用抗锯齿的情况下调整大小?或者在更高的抽象层次上,如何使我的板看起来漂亮?
class Ruled: UIView {
override func draw(_ rect: CGRect) {
let T: CGFloat = 15 // desired thickness of lines
let G: CGFloat = 30 // desired gap between lines
let W = rect.size.width
let H = rect.size.height
guard let c = UIGraphicsGetCurrentContext() else { return }
c.setStrokeColor(UIColor.orange.cgColor)
c.setLineWidth(T)
var p = -(W > H ? W : H) - T
while p <= W {
c.move( to: CGPoint(x: p-T, y: -T) )
c.addLine( to: CGPoint(x: p+T+H, y: T+H) )
c.strokePath()
p += G + T + T
}
}
}
尽情享受吧。
请注意,您显然会剪裁该视图。
如果你想在屏幕上或以某种模式显示其中的一些,就这样做吧。
要裁剪到给定的矩形:
上面的 class 只是将其绘制为“UIView 的大小”。
但是,您通常希望在视图中的不同坐标处实际绘制多个“框”。 (一个很好的例子是日历)。
此外,此示例明确绘制“两条条纹”而不是在背景颜色上绘制一条条纹:
func simpleStripes(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) {
let stripeWidth: CGFloat = 20.0 // whatever you want
let m = stripeWidth / 2.0
guard let c = UIGraphicsGetCurrentContext() else { return }
c.setLineWidth(stripeWidth)
let r = CGRect(x: x, y: y, width: width, height: height)
let longerSide = width > height ? width : height
c.saveGState()
c.clip(to: r)
var p = x - longerSide
while p <= x + width {
c.setStrokeColor(pale blue)
c.move( to: CGPoint(x: p-m, y: y-m) )
c.addLine( to: CGPoint(x: p+m+height, y: y+m+height) )
c.strokePath()
p += stripeWidth
c.setStrokeColor(pale gray)
c.move( to: CGPoint(x: p-m, y: y-m) )
c.addLine( to: CGPoint(x: p+m+height, y: y+m+height) )
c.strokePath()
p += stripeWidth
}
c.restoreGState()
}
extension UIImage {
func ResizeImage(targetSize: CGSize) -> UIImage
{
let size = self.size
let widthRatio = targetSize.width / self.size.width
let heightRatio = targetSize.height / self.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width,height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
我正在开发 iOS 棋盘游戏。我想给董事会一种 "texture".
我做的是我创建了这个非常小的图像(真的很小,一定要仔细看):
然后我将此图像传递给 UIColor.init(patternImage:)
初始化程序以创建一个 UIColor
即此图像。我用这个 UIColor
填充了一些正方形 UIBezierPath
s,结果如下所示:
该图像的所有副本都完美对齐,并且它们形成了许多对角线。到目前为止一切顺利。
现在在iPad上,我画的方块会变大,而且这些方块的边框也会变大。我已经成功计算出正方形的笔划宽度和大小应该是多少,所以这不是问题。
但是,由于 iPad 上的方块较大,每个方块上的对角线会更多。我不要那个。我需要将非常小的图像调整为更大的图像,并且大小取决于正方形的笔划宽度。具体来说,调整后的图片宽度应该是笔画宽度的两倍。
我写了这个扩展来调整图像大小,改编自这个
extension UIImage {
func resized(toWidth newWidth: CGFloat) -> UIImage {
let scale = newWidth / size.width
let newHeight = size.height * scale
UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0)
self.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
并这样称呼它:
// this is the code I used to draw a single square
let path = UIBezierPath(rect: CGRect(origin: point(for: Position(x, y)), size: CGSize(width: squareLength, height: squareLength)))
UIColor.black.setStroke()
path.lineWidth = strokeWidth
// this is the line that's important!
UIColor(patternImage: #imageLiteral(resourceName:
"texture").resized(toWidth: strokeWidth * 2)).setFill() path.fill() path.stroke()
现在游戏板在iPhone上看起来像这样:
您可能需要稍微放大网页才能明白我的意思。董事会现在看起来非常丑陋。您可以看到每个图像副本的 "borders"。我不想要这个。不过,在 iPad 上,棋盘看起来不错。我怀疑只有当我缩小图像尺寸时才会发生这种情况。
我想这可能是因为我在使用扩展程序时发生了抗锯齿。我发现 this post and this post 关于去除抗锯齿,但前者似乎在图像视图中这样做,而我在我的自定义 GameBoardView
的 draw(_:)
方法中这样做。后者的方案好像和我用的一模一样
如何在不使用抗锯齿的情况下调整大小?或者在更高的抽象层次上,如何使我的板看起来漂亮?
class Ruled: UIView {
override func draw(_ rect: CGRect) {
let T: CGFloat = 15 // desired thickness of lines
let G: CGFloat = 30 // desired gap between lines
let W = rect.size.width
let H = rect.size.height
guard let c = UIGraphicsGetCurrentContext() else { return }
c.setStrokeColor(UIColor.orange.cgColor)
c.setLineWidth(T)
var p = -(W > H ? W : H) - T
while p <= W {
c.move( to: CGPoint(x: p-T, y: -T) )
c.addLine( to: CGPoint(x: p+T+H, y: T+H) )
c.strokePath()
p += G + T + T
}
}
}
尽情享受吧。
请注意,您显然会剪裁该视图。
如果你想在屏幕上或以某种模式显示其中的一些,就这样做吧。
要裁剪到给定的矩形:
上面的 class 只是将其绘制为“UIView 的大小”。
但是,您通常希望在视图中的不同坐标处实际绘制多个“框”。 (一个很好的例子是日历)。
此外,此示例明确绘制“两条条纹”而不是在背景颜色上绘制一条条纹:
func simpleStripes(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) {
let stripeWidth: CGFloat = 20.0 // whatever you want
let m = stripeWidth / 2.0
guard let c = UIGraphicsGetCurrentContext() else { return }
c.setLineWidth(stripeWidth)
let r = CGRect(x: x, y: y, width: width, height: height)
let longerSide = width > height ? width : height
c.saveGState()
c.clip(to: r)
var p = x - longerSide
while p <= x + width {
c.setStrokeColor(pale blue)
c.move( to: CGPoint(x: p-m, y: y-m) )
c.addLine( to: CGPoint(x: p+m+height, y: y+m+height) )
c.strokePath()
p += stripeWidth
c.setStrokeColor(pale gray)
c.move( to: CGPoint(x: p-m, y: y-m) )
c.addLine( to: CGPoint(x: p+m+height, y: y+m+height) )
c.strokePath()
p += stripeWidth
}
c.restoreGState()
}
extension UIImage {
func ResizeImage(targetSize: CGSize) -> UIImage
{
let size = self.size
let widthRatio = targetSize.width / self.size.width
let heightRatio = targetSize.height / self.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width,height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}