Canvas 上绘制的文本的抗锯齿效果较差

发布于 2024-10-21 03:46:17 字数 192 浏览 2 评论 0原文

我正在 Canvas 上绘制文本,对抗锯齿的质量感到失望。据我所知,浏览器不会对 Canvas 上的文本进行子像素抗锯齿处理。

这准确吗?

这在 iPhone 和 Android 上尤其明显,生成的文本不像其他 DOM 元素渲染的文本那么清晰。

对于在 Canvas 上输出高质量文本有什么建议吗?

茹伯特

I'm drawing text on Canvas, and am disappointed with the quality of antialiasing. As far as I've been able to determine, browsers don't do subpixel antialising of text on Canvas.

Is this accurate?

This is particularly noticeable on iPhone and Android, where the resulting text isn't as crisp as text rendered by other DOM elements.

Any suggestions for high quality text out put on Canvas?

Joubert

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夜空下最亮的亮点 2024-10-28 03:46:17

我的答案来自此链接,也许对其他人有帮助。
http://www.html5rocks.com/en/tutorials/canvas/hidpi/

重要代码如下。

// finally query the various pixel ratios
    devicePixelRatio = window.devicePixelRatio || 1,
    backingStoreRatio = context.webkitBackingStorePixelRatio ||
                        context.mozBackingStorePixelRatio ||
                        context.msBackingStorePixelRatio ||
                        context.oBackingStorePixelRatio ||
                        context.backingStorePixelRatio || 1,

    ratio = devicePixelRatio / backingStoreRatio;

// upscale the canvas if the two ratios don't match
if (devicePixelRatio !== backingStoreRatio) {

    var oldWidth = canvas.width;
    var oldHeight = canvas.height;

    canvas.width = oldWidth * ratio;
    canvas.height = oldHeight * ratio;

    canvas.style.width = oldWidth + 'px';
    canvas.style.height = oldHeight + 'px';

    // now scale the context to counter
    // the fact that we've manually scaled
    // our canvas element
    context.scale(ratio, ratio);

}

My answer came from this link, maybe it will help someone else.
http://www.html5rocks.com/en/tutorials/canvas/hidpi/

The important code is as follows.

// finally query the various pixel ratios
    devicePixelRatio = window.devicePixelRatio || 1,
    backingStoreRatio = context.webkitBackingStorePixelRatio ||
                        context.mozBackingStorePixelRatio ||
                        context.msBackingStorePixelRatio ||
                        context.oBackingStorePixelRatio ||
                        context.backingStorePixelRatio || 1,

    ratio = devicePixelRatio / backingStoreRatio;

// upscale the canvas if the two ratios don't match
if (devicePixelRatio !== backingStoreRatio) {

    var oldWidth = canvas.width;
    var oldHeight = canvas.height;

    canvas.width = oldWidth * ratio;
    canvas.height = oldHeight * ratio;

    canvas.style.width = oldWidth + 'px';
    canvas.style.height = oldHeight + 'px';

    // now scale the context to counter
    // the fact that we've manually scaled
    // our canvas element
    context.scale(ratio, ratio);

}
掩饰不了的爱 2024-10-28 03:46:17

尝试将以下 META 标记添加到您的页面。这似乎解决了我在 iPhone Safari 上遇到的抗锯齿问题:

<meta name="viewport" content="user-scalable=no, width=device-width,
      initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5" />

Try adding the following META tag to your page. This seems to fix anti-aliasing issues I've had on iPhone Safari:

<meta name="viewport" content="user-scalable=no, width=device-width,
      initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5" />
拧巴小姐 2024-10-28 03:46:17

我意识到这是一个老问题,但我今天解决了这个问题并得到了很好的解决。我使用了上面 Alix Axel 的答案,并将我在那里找到的代码(在 web.archive.org 链接上)精简为最基本的内容。

我稍微修改了解决方案,使用两个画布,一个隐藏画布用于原始文本,第二个画布用于实际显示抗锯齿文本。

这是我想出的... http://jsfiddle.net/X2cKa/

代码如下所示;

function alphaBlend(gamma, c1, c2, alpha) {
    c1 = c1/255.0;
    c2 = c2/255.0;
    var c3 = Math.pow(
    Math.pow(c1, gamma) * (1 - alpha)
        + Math.pow(c2, gamma) * alpha,
    1/gamma
    );
    return Math.round(c3 * 255);
 }

function process(textPixels, destPixels, fg, bg) {
    var gamma = 2.2;
    for (var y = 0; y < textPixels.height; y ++) {
        var history = [255, 255, 255];
        var pixel_number = y * textPixels.width;
        var component = 0;
        for (var x = 0; x < textPixels.width; x ++) {
            var alpha = textPixels.data[(y * textPixels.width + x) * 4 + 1] / 255.0;
            alpha = Math.pow(alpha, gamma);
            history[component] = alpha;
            alpha = (history[0] + history[1] + history[2]) / 3;
            out = alphaBlend(gamma, bg[component], fg[component], alpha);
            destPixels.data[pixel_number * 4 + component] = out;    
            /* advance to next component/pixel */
            component ++;
            if (component == 3) {
            pixel_number ++;
            component = 0;
            }
        }
    }
}

function toColor(colorString) {
    return [parseInt(colorString.substr(1, 2), 16),
    parseInt(colorString.substr(3, 2), 16),
    parseInt(colorString.substr(5, 2), 16)];
}

function renderOnce() {
    var phrase = "Corporate GOVERNANCE"
    var c1 = document.getElementById("c1"); //the hidden canvas
    var c2 = document.getElementById("c2"); //the canvas
    var textSize=40;

    var font = textSize+"px Arial"
    var fg = "#ff0000";
    var bg = "#fff9e1";

    var ctx1 = c1.getContext("2d");
    var ctx2 = c2.getContext("2d");
    ctx1.fillStyle = "rgb(255, 255, 255)";
    ctx1.fillRect(0, 0, c1.width, c1.height);

    ctx1.save();
    ctx1.scale(3, 1);
    ctx1.font = font;
    ctx1.fillStyle = "rgb(255, 0, 0)";
    ctx1.fillText(phrase, 0, textSize);
    ctx1.restore();

    var textPixels = ctx1.getImageData(0, 0, c1.width, c1.height);

    var colorFg = toColor(fg);
    var colorBg = toColor(bg);
    var destPixels3 = ctx1.getImageData(0, 0, c1.width, c1.height);
    process(textPixels, destPixels3, colorBg, colorFg);
    ctx2.putImageData(destPixels3, 0, 0);


    //for comparison, show Comparison Text without anti aliaising
    ctx2.font = font;
    ctx2.fillStyle = "rgb(255, 0, 0)";
    ctx2.fillText(phrase, 0, textSize*2);

};

renderOnce();

我还添加了一个比较文本对象,以便您可以看到抗锯齿的工作情况。

希望这对某人有帮助!

I realise this is an old question, but I worked on this problem today and got it working nicely. I used Alix Axel's answer above and stripped down the code I found there (on the web.archive.org link) to the bare essentials.

I modified the solution a bit, using two canvases, one hidden canvas for the original text and a second canvas to actually show the anti-aliaised text.

Here's what I came up with... http://jsfiddle.net/X2cKa/

The code looks like this;

function alphaBlend(gamma, c1, c2, alpha) {
    c1 = c1/255.0;
    c2 = c2/255.0;
    var c3 = Math.pow(
    Math.pow(c1, gamma) * (1 - alpha)
        + Math.pow(c2, gamma) * alpha,
    1/gamma
    );
    return Math.round(c3 * 255);
 }

function process(textPixels, destPixels, fg, bg) {
    var gamma = 2.2;
    for (var y = 0; y < textPixels.height; y ++) {
        var history = [255, 255, 255];
        var pixel_number = y * textPixels.width;
        var component = 0;
        for (var x = 0; x < textPixels.width; x ++) {
            var alpha = textPixels.data[(y * textPixels.width + x) * 4 + 1] / 255.0;
            alpha = Math.pow(alpha, gamma);
            history[component] = alpha;
            alpha = (history[0] + history[1] + history[2]) / 3;
            out = alphaBlend(gamma, bg[component], fg[component], alpha);
            destPixels.data[pixel_number * 4 + component] = out;    
            /* advance to next component/pixel */
            component ++;
            if (component == 3) {
            pixel_number ++;
            component = 0;
            }
        }
    }
}

function toColor(colorString) {
    return [parseInt(colorString.substr(1, 2), 16),
    parseInt(colorString.substr(3, 2), 16),
    parseInt(colorString.substr(5, 2), 16)];
}

function renderOnce() {
    var phrase = "Corporate GOVERNANCE"
    var c1 = document.getElementById("c1"); //the hidden canvas
    var c2 = document.getElementById("c2"); //the canvas
    var textSize=40;

    var font = textSize+"px Arial"
    var fg = "#ff0000";
    var bg = "#fff9e1";

    var ctx1 = c1.getContext("2d");
    var ctx2 = c2.getContext("2d");
    ctx1.fillStyle = "rgb(255, 255, 255)";
    ctx1.fillRect(0, 0, c1.width, c1.height);

    ctx1.save();
    ctx1.scale(3, 1);
    ctx1.font = font;
    ctx1.fillStyle = "rgb(255, 0, 0)";
    ctx1.fillText(phrase, 0, textSize);
    ctx1.restore();

    var textPixels = ctx1.getImageData(0, 0, c1.width, c1.height);

    var colorFg = toColor(fg);
    var colorBg = toColor(bg);
    var destPixels3 = ctx1.getImageData(0, 0, c1.width, c1.height);
    process(textPixels, destPixels3, colorBg, colorFg);
    ctx2.putImageData(destPixels3, 0, 0);


    //for comparison, show Comparison Text without anti aliaising
    ctx2.font = font;
    ctx2.fillStyle = "rgb(255, 0, 0)";
    ctx2.fillText(phrase, 0, textSize*2);

};

renderOnce();

I also added a comparison text object so that you can see the anti-aliasing working.

Hope this helps someone!

独闯女儿国 2024-10-28 03:46:17

已完成一些子像素抗锯齿处理,但这取决于浏览器/操作系统。

之前对此进行了一些讨论,可能会对你有帮助。

我没有 Android 或 iOS 设备,但只是为了好玩,在绘制之前尝试将上下文平移 (.5, 0) 像素,看看这是否会对文本的渲染方式产生影响。

There is some subpixel antialiasing done, but it is up to the browser/OS.

There was a bit of an earlier discussion on this that may be of help to you.

I don't have an android or iOS device but just for kicks, try translating the context by (.5, 0) pixels before you draw and see if that makes a difference in how your text is rendered.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文