细字符的像素不是所要求的确切颜色

Pixels of Thin Chars are not of the Exact Requested Color

为了恢复字符边界框,我将完整的 HTML 用不同的颜色绘制到 HTML canvas 上,这样我就可以通过像素颜色值找到它们。并非所有像素颜色都是我分配给角色的颜色,但大多数是例如使用字体颜色 rgb(0,0,100) 绘制的 'a' 的大部分像素的颜色为 rgb(0,0,100).

但是,对于一些细字符的像素,例如指数的“-”或“2”,颜色是分配的 none。如果我将 (0,20,71) 指定为字体颜色,则大多数像素颜色值不是 (0,20,71),而是 (66,81,119)。由于这些替代颜色,当我寻找颜色为 (0,20,71) 的像素时,我发现 none,但最终没有“-”等的边界框

  1. 是否有颜色选择可以最大限度地减少使用替代颜色来显示字符?如果是,那么也许我可以使用这些颜色而不是随机颜色值。
  2. 是否有一个 HTML 渲染属性,我可以将其 on/off 以尽量减少对替代颜色的使用?
  3. 或者是否有更简单的方法来完成这一切?以前都是一个一个画的,效果好一些,但是太慢了,没用。

请注意,我不需要可以使用字体信息或 getBoundingClientRect 恢复的边界框,因为它们并不总是准确的。

注意:这个问题是从 graphicdesign 交叉发布的,因为它更适合这个论坛。

您的问题可能是由文本的抗锯齿引起的。为了更好地理解,让我们看一下 这个高度放大的 'a' 红色字符 (#ff000) 就像在 canvas 上呈现的一样。

如您所见,主要形状确实涂成红色,但周围是浅红色。要是我们 简单地寻找固定颜色(例如#ff0000),选择不会包含周围的像素,因为它是 在前景和背景颜色之间混合 - 例如#ffbfc1(粉红色)。因此,如果我们更进一步并尝试获得基于红色的边界框,它将如下所示:

一个可能的修复有点像这样:

  • 获取canvas像素数据
  • 检查每个像素的颜色 - 如果它不同于完全透明的颜色,请将其替换为完全不透明的纯黑色 #000000
  • 将像素数据写回canvas

对于上面的 'a' 这会给我们这样的东西:

现在我们可以简单地再次检查像素数据,寻找黑色像素并计算出 min/max x 和 y 值以获得周围区域。

这是一个完整的代码示例:

let canvas = document.getElementById('canvas');
let canvas2 = document.getElementById('canvas2');
let ctx = canvas.getContext('2d');
let ctx2 = canvas2.getContext('2d');
ctx.font = '12px Arial';
ctx.fillText('a²+b²=c²', 110, 90);
ctx2.font = '12px Arial';
ctx2.fillText('a²+b²=c²', 110, 90);

let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let color;
for (let a = 0; a < imageData.data.length; a += 4) {
  color = imageData.data[a] * imageData.data[a + 1] + imageData.data[a + 2] + imageData.data[a + 3];
  if (color > 0) {
    imageData.data[a] = 0;
    imageData.data[a + 1] = 0;
    imageData.data[a + 2] = 0;
    imageData.data[a + 3] = 255;
  }
}
ctx.putImageData(imageData, 0, 0);

let x = 0;
let y = 0;
let minY = canvas.height;
let maxY = 0;
let minX = canvas.width;
let maxX = 0;
for (let a = 0; a < imageData.data.length; a += 4) {
  if (imageData.data[a + 3] == 255) {
    y = Math.floor((a / 4) / canvas.width);

    x = (a / 4) - (y * canvas.width);
    if (y < minY + 1) {
      minY = y - 1;
    }
    if (y > maxY - 1) {
      maxY = y + 1;
    }
    if (x < minX + 1) {
      minX = x - 1;
    }
    if (x > maxX - 1) {
      maxX = x + 1;
    }
  }
}

ctx.strokeStyle = 'red';
ctx.strokeRect(minX, minY, maxX - minX, maxY - minY);
<div style="display:block;"><canvas id="canvas2" style="background-color:grey;"></canvas>
  <canvas id="canvas" style="background-color:grey;"></canvas></div>