给定具有任何 pointSize 的 UIFont,如何计算适当的笔画宽度?
How to calculate appropriate stroke width given a UIFont with any pointSize?
我想对文本应用单实线描边。这很容易使用 NSAttributedString
指定 .strokeWidth
获得。然而,我发现确定 strokeWidth
应该在任何给定的 pointSize
处呈现什么 UIFont
是很棘手的。我可以很容易地说,好吧,在 50 的磅值下,1 的笔画宽度看起来很棒。我凭直觉假设,如果将字体大小加倍,则应该将笔画宽度加倍,因此随着字体缩放,笔画将按比例缩放,并导致笔画粗细与原始 "base" 字体大小一致。然而,事实并非如此。随着字体大小和笔画宽度按比例增加,笔画宽度变得太粗了。
此处的屏幕截图显示第一行的字体大小为 50,笔画宽度为 1。下一行加倍,因此字体大小为 100,笔画宽度为 2,重复此操作直到最后一行为 350 vs 7。
我相信这是因为笔画既向内又向外呈现。它的中心在角色的边缘,然后向两个方向扩展。如果你比较这两张图片,你可以看到这一点,这张没有应用笔触。
因此随着字体大小的增加,笔划宽度不应按比例增加,需要以较慢的速度增加以确保粗细在所有大小上保持一致。我正在尝试确定计算此值的正确方法。
所以给定一个看起来很理想的基本配置(假设 50pt 字体大小和 1pt 笔画宽度)和一个新的 pointSize
(例如 350pt),你如何计算正确的 strokeWidth
?或者我应该使用不同的值而不是 pointSize
?
我当前按比例缩放的算法是:
let strokeWidth = font.pointSize / 50
(简单地求解 1/50 = x/pointSize
中的 x)
这是我用来绘制文本的代码:
let text = "hello"
let imageRect = CGRect(x: 0, y: 0, width: 343 * 3, height: 500 * 3)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let alphaInfo = CGImageAlphaInfo.premultipliedLast.rawValue
let bitmapContext = CGContext(data: nil, width: Int(imageRect.width), height: Int(imageRect.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: alphaInfo)!
bitmapContext.setAlpha(1)
bitmapContext.setTextDrawingMode(CGTextDrawingMode.fill)
//1
bitmapContext.textPosition = CGPoint(x: 40, y: 1080)
let displayLineText1 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 50), .strokeColor: UIColor.red, .strokeWidth: 1]))
CTLineDraw(displayLineText1, bitmapContext)
//2
bitmapContext.textPosition = CGPoint(x: 40, y: 1000)
let displayLineText2 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 100), .strokeColor: UIColor.red, .strokeWidth: 2]))
CTLineDraw(displayLineText2, bitmapContext)
//3
bitmapContext.textPosition = CGPoint(x: 40, y: 875)
let displayLineText3 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 150), .strokeColor: UIColor.red, .strokeWidth: 3]))
CTLineDraw(displayLineText3, bitmapContext)
//4
bitmapContext.textPosition = CGPoint(x: 40, y: 725)
let displayLineText4 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 200), .strokeColor: UIColor.red, .strokeWidth: 4]))
CTLineDraw(displayLineText4, bitmapContext)
//5
bitmapContext.textPosition = CGPoint(x: 40, y: 540)
let displayLineText5 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 250), .strokeColor: UIColor.red, .strokeWidth: 5]))
CTLineDraw(displayLineText5, bitmapContext)
//6
bitmapContext.textPosition = CGPoint(x: 40, y: 310)
let displayLineText6 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 300), .strokeColor: UIColor.red, .strokeWidth: 6]))
CTLineDraw(displayLineText6, bitmapContext)
//7
bitmapContext.textPosition = CGPoint(x: 40, y: 40)
let displayLineText7 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 350), .strokeColor: UIColor.red, .strokeWidth: 7]))
CTLineDraw(displayLineText7, bitmapContext)
let textCGImage = bitmapContext.makeImage()!
let textImage = CIImage(cgImage: textCGImage)
来自 the documentation for .strokeWidth
(已强调):
The value of this attribute is an NSNumber
object containing a
floating-point value. This value represents the amount to change the
stroke width and is specified as a percentage of the font point size.
Specify 0 (the default) for no additional changes. Specify positive
values to change the stroke width alone. Specify negative values to
stroke and fill the text. For example, a typical value for outlined
text would be 3.0.
因此,该值已经缩放到字体大小。你也不应该自己扩展它。选择一个值,在任何给定的字体大小下提供您喜欢的相对笔画宽度,并将其用于所有字体大小。
我想对文本应用单实线描边。这很容易使用 NSAttributedString
指定 .strokeWidth
获得。然而,我发现确定 strokeWidth
应该在任何给定的 pointSize
处呈现什么 UIFont
是很棘手的。我可以很容易地说,好吧,在 50 的磅值下,1 的笔画宽度看起来很棒。我凭直觉假设,如果将字体大小加倍,则应该将笔画宽度加倍,因此随着字体缩放,笔画将按比例缩放,并导致笔画粗细与原始 "base" 字体大小一致。然而,事实并非如此。随着字体大小和笔画宽度按比例增加,笔画宽度变得太粗了。
此处的屏幕截图显示第一行的字体大小为 50,笔画宽度为 1。下一行加倍,因此字体大小为 100,笔画宽度为 2,重复此操作直到最后一行为 350 vs 7。
我相信这是因为笔画既向内又向外呈现。它的中心在角色的边缘,然后向两个方向扩展。如果你比较这两张图片,你可以看到这一点,这张没有应用笔触。
因此随着字体大小的增加,笔划宽度不应按比例增加,需要以较慢的速度增加以确保粗细在所有大小上保持一致。我正在尝试确定计算此值的正确方法。
所以给定一个看起来很理想的基本配置(假设 50pt 字体大小和 1pt 笔画宽度)和一个新的 pointSize
(例如 350pt),你如何计算正确的 strokeWidth
?或者我应该使用不同的值而不是 pointSize
?
我当前按比例缩放的算法是:
let strokeWidth = font.pointSize / 50
(简单地求解 1/50 = x/pointSize
中的 x)
这是我用来绘制文本的代码:
let text = "hello"
let imageRect = CGRect(x: 0, y: 0, width: 343 * 3, height: 500 * 3)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let alphaInfo = CGImageAlphaInfo.premultipliedLast.rawValue
let bitmapContext = CGContext(data: nil, width: Int(imageRect.width), height: Int(imageRect.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: alphaInfo)!
bitmapContext.setAlpha(1)
bitmapContext.setTextDrawingMode(CGTextDrawingMode.fill)
//1
bitmapContext.textPosition = CGPoint(x: 40, y: 1080)
let displayLineText1 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 50), .strokeColor: UIColor.red, .strokeWidth: 1]))
CTLineDraw(displayLineText1, bitmapContext)
//2
bitmapContext.textPosition = CGPoint(x: 40, y: 1000)
let displayLineText2 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 100), .strokeColor: UIColor.red, .strokeWidth: 2]))
CTLineDraw(displayLineText2, bitmapContext)
//3
bitmapContext.textPosition = CGPoint(x: 40, y: 875)
let displayLineText3 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 150), .strokeColor: UIColor.red, .strokeWidth: 3]))
CTLineDraw(displayLineText3, bitmapContext)
//4
bitmapContext.textPosition = CGPoint(x: 40, y: 725)
let displayLineText4 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 200), .strokeColor: UIColor.red, .strokeWidth: 4]))
CTLineDraw(displayLineText4, bitmapContext)
//5
bitmapContext.textPosition = CGPoint(x: 40, y: 540)
let displayLineText5 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 250), .strokeColor: UIColor.red, .strokeWidth: 5]))
CTLineDraw(displayLineText5, bitmapContext)
//6
bitmapContext.textPosition = CGPoint(x: 40, y: 310)
let displayLineText6 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 300), .strokeColor: UIColor.red, .strokeWidth: 6]))
CTLineDraw(displayLineText6, bitmapContext)
//7
bitmapContext.textPosition = CGPoint(x: 40, y: 40)
let displayLineText7 = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 350), .strokeColor: UIColor.red, .strokeWidth: 7]))
CTLineDraw(displayLineText7, bitmapContext)
let textCGImage = bitmapContext.makeImage()!
let textImage = CIImage(cgImage: textCGImage)
来自 the documentation for .strokeWidth
(已强调):
The value of this attribute is an
NSNumber
object containing a floating-point value. This value represents the amount to change the stroke width and is specified as a percentage of the font point size. Specify 0 (the default) for no additional changes. Specify positive values to change the stroke width alone. Specify negative values to stroke and fill the text. For example, a typical value for outlined text would be 3.0.
因此,该值已经缩放到字体大小。你也不应该自己扩展它。选择一个值,在任何给定的字体大小下提供您喜欢的相对笔画宽度,并将其用于所有字体大小。