Jak skalować obrazy na płótnie html5 z lepszą interpolacją?

Po pierwsze: co ja próbuję zrobić?

Mam aplikację do przeglądania obrazów. Używa elementu canvas do renderowania obrazu. Możesz powiększyć, pomniejszyć i przeciągnąć. Ta część działa teraz doskonale.

Ale powiedzmy, że mam obraz z dużą ilością tekstu. Ma rozdzielczość 1200x1700, a moje płótno ma 1200x900. Początkowo, po powiększeniu, prowadzi to do renderowanej rozdzielczości ~560x800.

Mój rzeczywisty rysunek wygląda jak to:

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

Mały tekst na tym obrazku wygląda bardzo, bardzo źle, szczególnie w porównaniu do innych przeglądarek obrazów (np. IrfanView), a nawet elementu html .

Odkryłem, że algorytm interpolacji przeglądarek jest przyczyną tego problemu. Porównanie różnych przeglądarek wykazało, że Chrome renderuje skalowane obrazy najlepiej, ale nadal nie jest wystarczająco dobry.

Szukałam w każdym zakątku Interwebs przez 4-5 godzin i nie znalazłam tego, czego potrzebuję. I znalazłem opcję "imageSmoothingEnabled", style CSS" image-rendering", których nie można używać na płótnie, renderowanie w pozycjach float i wiele implementacji JavaScript algorytmów interpolacji(są to daleko , aby zwolnić dla mojego celu).

Możesz zapytać, dlaczego mówię ci to wszystko: aby zaoszczędzić Ci czasu, aby dać mi odpowiedzi, które już wiem:))

Więc: czy istnieje jakiś dobry i szybki sposób na lepszą interpolację? Moim obecnym pomysłem jest stworzenie obiektu obrazu, Zmień rozmiar (ponieważ img ma dobrą interpolację po przeskalowaniu!) i następnie renderować. Niestety, zastosowanie img.szerokość wydaje się wpływać tylko na wyświetlaną szerokość...

Z góry dziękuję za odpowiedzi!! :)

Update: Dzięki Simonowi mogłem rozwiązać mój problem. Oto algorytm skalowania dynamicznego, którego użyłem. Zauważ, że zachowuje współczynnik proporcji, parametr wysokości służy tylko do unikania większej liczby obliczeń pływakowych. Teraz tylko się obniża.

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);
    }
Author: Kai, 2013-09-12

1 answers

Musisz" ustąpić " kilka razy. Zamiast skalowania z bardzo dużego obrazu do bardzo małego, musisz przeskalować go do rozmiarów pośrednich.

Rozważ obraz, który chcesz narysować w skali 1/6. Możesz to zrobić:

var w = 1280;
var h = 853;

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

Lub możesz narysować go na płótnie w pamięci w skali 1/2, następnie ponownie w skali 1/2, a następnie ponownie w skali 1/2. Rezultatem jest obraz w skali 1/6, ale używamy trzech kroków:

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);

Następnie możesz narysować to z powrotem do oryginału kontekst:

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

Możesz zobaczyć różnicę na żywo, tutaj:

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>

Zobacz ten sam fragment na jsfiddle.

 45
Author: Simon Sarris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-23 18:49:12