42

まず第一に:私は何をしようとしていますか?

画像を表示するアプリケーションがあります。canvas 要素を使用して画像をレンダリングします。ズームイン、ズームアウト、ドラッグすることができます。この部分は現在完全に機能しています。

しかし、多くのテキストを含む画像があるとしましょう。解像度は 1200x1700 で、キャンバスの解像度は 1200x900 です。最初にズームアウトすると、レンダリング解像度は ~560x800 になります。

私の実際の図面は次のようになります。

drawImage(src, srcOffsetX, srcOffsetY, sourceViewWidth, sourceViewHeight,
destOffsetX, destOffsetY, destWidth, destHeight);

この画像の小さなテキストは、特に他の画像ビューア (IrfanView など) や html の < img > 要素と比較すると、非常に見栄えが悪くなります。

ブラウザの補間アルゴリズムがこの問題の原因であることがわかりました。さまざまなブラウザーを比較すると、Chrome は拡大縮小された画像を最適にレンダリングしますが、それでも十分ではないことがわかりました。

さて、インターウェブの隅々まで 4 ~ 5 時間続けて検索しましたが、必要なものが見つかりませんでした。「imageSmoothingEnabled」オプション、キャンバスでは使用できない「image-rendering」CSS スタイル、フロート位置でのレンダリング、および補間アルゴリズムの多くの JavaScript 実装 (これらは私の目的には遅すぎます) を見つけました。

あなたは、なぜ私がこのすべてをあなたに話しているのかと尋ねるかもしれません: 私がすでに知っている答えを私に与える時間を節約するためです

だから:補間を改善する良い方法はありますか?私の現在のアイデアは、画像オブジェクトを作成し、これをサイズ変更して (img はスケーリング時に適切に補間されるため)、レンダリングすることです。残念ながら、 img.width を適用しても表示される幅にしか影響しないようです...

更新: Simon のおかげで、問題を解決できました。これが私が使用した動的スケーリングアルゴリズムです。アスペクト比を維持することに注意してください。高さパラメータは、より多くのフロート コンピューティングを回避するためだけのものです。現在は縮小のみです。

scale(destWidth, destHeight){
        var start = new Date().getTime();
        var scalingSteps = 0;
        var ctx = this._sourceImageCanvasContext;
        var curWidth = this._sourceImageWidth;
        var curHeight = this._sourceImageHeight;

        var lastWidth = this._sourceImageWidth;
        var lastHeight = this._sourceImageHeight;

        var end = false;
        var scale=0.75;
        while(end==false){
            scalingSteps +=1;
            curWidth *= scale;
            curHeight *= scale;
            if(curWidth < destWidth){
                curWidth = destWidth;
                curHeight = destHeight;
                end=true;
            }
            ctx.drawImage(this._sourceImageCanvas, 0, 0, Math.round(lastWidth), Math.round(lastHeight), 0, 0, Math.round(curWidth), Math.round(curHeight));
            lastWidth = curWidth;
            lastHeight = curHeight;
        }
        var endTime =new Date().getTime();
        console.log("execution time: "+ ( endTime - start) + "ms. scale per frame: "+scale+ " scaling step count: "+scalingSteps);
    }
4

1 に答える 1

59

何度か「降りる」必要があります。非常に大きな画像から非常に小さな画像にスケーリングする代わりに、中間サイズに再スケーリングする必要があります。

1/6 スケールで描きたい画像を考えてみましょう。あなたはこれを行うことができます:

var w = 1280;
var h = 853;

ctx.drawImage(img, 0, 0, w/6, h/6);   

または、メモリ内のキャンバスに 1/2 の縮尺で描画し、次に 1/2 の縮尺で、次に 1/2 の縮尺で描画することもできます。結果は 1/6 スケールの画像ですが、次の 3 つの手順を使用します。

var can2 = document.createElement('canvas');
can2.width = w/2;
can2.height = w/2;
var ctx2 = can2.getContext('2d');

ctx2.drawImage(img, 0, 0, w/2, h/2);
ctx2.drawImage(can2, 0, 0, w/2, h/2, 0, 0, w/4, h/4);
ctx2.drawImage(can2, 0, 0, w/4, h/4, 0, 0, w/6, h/6);

次に、それを元のコンテキストに戻すことができます。

ctx.drawImage(can2, 0, 0, w/6, h/6, 0, 200, w/6, h/6);

ここで違いをライブで見ることができます:

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');

var img = new Image();
var w = 1280;
var h = 853;
img.onload = function() {
    // step it down only once to 1/6 size:
    ctx.drawImage(img, 0, 0, w/6, h/6);   
    
    // Step it down several times
    var can2 = document.createElement('canvas');
    can2.width = w/2;
    can2.height = w/2;
    var ctx2 = can2.getContext('2d');
    
    // Draw it at 1/2 size 3 times (step down three times)
    
    ctx2.drawImage(img, 0, 0, w/2, h/2);
    ctx2.drawImage(can2, 0, 0, w/2, h/2, 0, 0, w/4, h/4);
    ctx2.drawImage(can2, 0, 0, w/4, h/4, 0, 0, w/6, h/6);
    ctx.drawImage(can2, 0, 0, w/6, h/6, 0, 200, w/6, h/6);
}



img.src = 'http://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Equus_quagga_%28Namutoni%2C_2012%29.jpg/1280px-Equus_quagga_%28Namutoni%2C_2012%29.jpg'
canvas {
    border: 1px solid gray;
}
<canvas id="canvas1" width="400" height="400"></canvas>

jsfiddle で同じスニペットを表示します。

于 2013-09-12T13:41:26.583 に答える