Maksymalny rozmiar elementu <canvas>

112

Pracuję z elementem canvas o wysokości 600do 1000pikseli i szerokości kilkudziesięciu lub setek tysięcy pikseli. Jednak po określonej liczbie pikseli (oczywiście nieznanej) płótno nie wyświetla już kształtów, które narysowałem za pomocą JS.

Czy ktoś wie, czy istnieje limit?

Przetestowano zarówno w przeglądarce Chrome 12, jak i Firefox 4.

seriousdev
źródło
2
@ Šime Powiedział tens OR hundreds of thousands...
Tadeck
6
Ja też tego doświadczam. Mam płótno 8000x8000, które działa dobrze, ale kiedy je powiększę, zawartość znika i po prostu się nie rysuje. Rozmiar, w którym nie działa, jest znacznie niższy na moim iPadzie. Zastanawiam się, czy to jakieś ograniczenie pamięci.
Joshua
28
To dziwne, gdy znajdziesz swój własny komentarz sprzed 2 lat i wciąż masz do czynienia z tym samym problemem.
Joshua,
4
@Joshua i jeszcze rok później!
Eugene Yu
1
Pytanie brzmi, jak wyłapać te błędy lub lepsze ostrzeżenia, co to jest? Nie mogę wykryć sprzętu urządzenia, więc muszę zareagować na błąd.
br4nnigan

Odpowiedzi:

115

Zaktualizowano 10/13/2014

Wszystkie testowane przeglądarki mają ograniczenia dotyczące wysokości / szerokości elementów canvas, ale wiele przeglądarek ogranicza również łączną powierzchnię elementu canvas. Limity są następujące dla przeglądarek, które mogę przetestować:

Chrom:

Maksymalna wysokość / szerokość: 32767 pikseli
Maksymalny obszar: 268 435 456 pikseli (np. 16 384 x 16 384)

Firefox:

Maksymalna wysokość / szerokość: 32767 pikseli
Maksymalny obszar: 472 907 776 pikseli (np. 22 528 x 20 992)

TO ZNACZY:

Maksymalna wysokość / szerokość: 8192 piksele
Maksymalny obszar: nie dotyczy

IE Mobile:

Maksymalna wysokość / szerokość: 4096 pikseli
Maksymalny obszar: nie dotyczy

Inny:

W tej chwili nie mogę testować innych przeglądarek. Dodatkowe ograniczenia można znaleźć w innych odpowiedziach na tej stronie.


Przekroczenie maksymalnej długości / szerokości / obszaru w większości przeglądarek sprawia, że ​​płótno nie nadaje się do użytku. (Zignoruje wszelkie polecenia rysowania, nawet w obszarze użytkowym). IE i IE Mobile będą honorować wszystkie polecenia rysowania w przestrzeni użytkowej.

Brandon Gano
źródło
3
Więc powiązane pytanie ... jak obejść ten limit, jeśli potrzebujesz płótna większego niż dozwolone maksimum?
aroth
2
@aroth, skończyło się na napisaniu opakowania wokół API Canvas, które wykorzystywało wiele ułożonych w stos <canvas>elementów i automatycznie stosowało instrukcje rysowania do odpowiedniego kontekstu.
Brandon Gano,
1
Brzmi bardzo przydatne. Nie myśl, że jest na githubie?
aroth
5
Safari (wersja inna niż iOS) używa 32K, tak jak Chrome.Firefox. Ponadto, jeśli chciałbyś spróbować odtworzyć część tego kodu opakowania, uruchomiłem własną wersję open source dzięki marnemu
aroth
7
iOS 9.3.1 Safari na iPhonie 6 Plus jest ograniczony do obszaru 16777216 pikseli (na przykład 4096 x 4096). Podejrzewam, że jest to częsta liczba na nowszych urządzeniach z systemem iOS
matb33
16

Wystąpiły błędy braku pamięci w przeglądarce Firefox przy wysokości płótna większej niż 8000, Chrome wydaje się radzić sobie znacznie wyżej, przynajmniej do 32000.

EDYCJA: Po wykonaniu kilku kolejnych testów znalazłem bardzo dziwne błędy w przeglądarce Firefox 16.0.2.

Po pierwsze, wydaje mi się, że zachowuje się inaczej z płótna w pamięci (utworzonego w javascript), w przeciwieństwie do płótna zadeklarowanego w formacie HTML.

Po drugie, jeśli nie masz odpowiedniego tagu HTML i meta zestawu znaków, kanwa może być ograniczona do 8196, w przeciwnym razie możesz przejść do 32767.

Po trzecie, jeśli uzyskasz kontekst 2D płótna, a następnie zmienisz rozmiar płótna, możesz być również ograniczony do 8196. Proste ustawienie rozmiaru płótna przed przechwyceniem kontekstu 2d pozwala uzyskać do 32767 bez błędów pamięci.

Nie udało mi się konsekwentnie wyłapać błędów pamięci, czasami jest to tylko ładowanie pierwszej strony, a potem działają kolejne zmiany wysokości. To jest plik html, który testowałem za pomocą http://pastebin.com/zK8nZxdE .

Ben Burnett
źródło
4
„Po drugie, jeśli nie masz odpowiedniego tagu html i meta charset, płótno może być ograniczone do 8196, w przeciwnym razie możesz wzrosnąć do 32767” - co masz na myśli, mówiąc tutaj o odpowiednim tagu HTML i meta?
Jack Aidley,
Z pewnością masz na myśli 8192 (2 ^ 13)? 8196 to dziwna liczba.
jamesdlin
14

Maksymalny rozmiar obszaru roboczego w systemie iOS (szerokość x wysokość):

 iPod Touch 16GB = 1448x1448
 iPad Mini       = 2290x2289
 iPhone 3        = 1448x1448
 iPhone 5        = 2290x2289

testowany w marcu 2014 r.

Boazeria
źródło
Te wartości są arbitralne. Proszę zobaczyć mój post poniżej, który odwołuje się do przewodnika po safari
dcbarans
11

Aby trochę rozwinąć odpowiedź @FredericCharette: zgodnie z przewodnikiem po safari w sekcji „Poznaj limity zasobów iOS”:

Maksymalny rozmiar elementu płótna to 3 megapiksele dla urządzeń z mniej niż 256 MB pamięci RAM i 5 megapikseli dla urządzeń z większą lub równą 256 MB pamięci RAM

Dlatego każda zmiana rozmiaru wynosząca 5242880 (5 x 1024 x 1024) pikseli będzie działać na urządzeniach z dużą pamięcią, w przeciwnym razie będzie to 3145728 pikseli.

Przykład dla płótna o rozdzielczości 5 megapikseli (szerokość x wysokość):

Any total <= 5242880
--------------------
5 x 1048576 ~= 5MP   (1048576 = 1024 x 1024)
50 x 104857 ~= 5MP
500 x 10485 ~= 5MP

i tak dalej..

Największe płótna KWADRATOWE to („MiB” = 1024x1024 bajtów):

device < 256 MiB   device >= 256 MiB   iPhone 6 [not confirmed]
-----------------  -----------------   ---------------------
<= 3145728 pixels  <= 5242880 pixels   <= 16 x 1024 x 1024 p
1773 x 1773        2289 x 2289         4096 x 4096
dcbarans
źródło
1
Mówiąc dokładniej, limit iPhone5 wynosi 3 * 1024 * 1024 = 3145728 pikseli. (Po zastanowieniu się, dlaczego moje płótno działa dobrze na Androidzie, ale puste na iOS, znalazłem tę odpowiedź, a także stackoverflow.com/questions/12556198/ ... i uruchomiłem moją aplikację Phonegap.)
Magnus Smith
Do Twojej wiadomości, firma Apple usunęła określone informacje o rozmiarze z „Safari / Poznaj limity zasobów iOS”. Teraz jest to tylko ogólna rada dotycząca minimalizowania przepustowości przy użyciu małych obrazów.
ToolmakerSteve
@ matb33 zgłosił w komentarzu do pytania, że ​​iPhone 6 wydaje się mieć limit 16 * 1024 * 1024.
ToolmakerSteve
11

Aktualizacja na rok 2018:

W miarę upływu czasu ograniczenia płótna uległy zmianie. Niestety, to, co się nie zmieniło, to fakt, że przeglądarki nadal nie dostarczają informacji o ograniczeniach rozmiaru kanwy za pośrednictwem interfejsu API Canvas.

Dla tych, którzy chcą programowo określić maksymalny rozmiar obszaru roboczego przeglądarki lub przetestować obsługę niestandardowych wymiarów kanwy, wypróbuj rozmiar płótna .

Z dokumentów:

Element HTML canvas jest szeroko obsługiwany przez nowoczesne i starsze przeglądarki, ale każda kombinacja przeglądarki i platformy narzuca unikalne ograniczenia rozmiaru, które spowodują, że płótno stanie się bezużyteczne po przekroczeniu. Niestety przeglądarki nie zapewniają sposobu na określenie swoich ograniczeń ani nie dostarczają żadnych informacji zwrotnych po utworzeniu bezużytecznego płótna. To sprawia, że ​​praca z dużymi elementami kanwy jest wyzwaniem, szczególnie w przypadku aplikacji obsługujących różne przeglądarki i platformy.

Ta mikro-biblioteka zapewnia maksymalny obszar, wysokość i szerokość elementu płótna HTML obsługiwanego przez przeglądarkę, a także możliwość testowania niestandardowych wymiarów płótna. Zbierając te informacje przed utworzeniem nowego elementu kanwy, aplikacje są w stanie niezawodnie ustawić wymiary kanwy w ramach ograniczeń rozmiaru każdej przeglądarki / platformy.

Łącze do wersji demonstracyjnej i wyniki testów są dostępne w pliku README , a także w sekcji znanych problemów, która dotyczy wydajności i maszyn wirtualnych.

Pełna jawność, jestem autorem biblioteki. Stworzyłem go w 2014 roku i niedawno ponownie wróciłem do kodu nowego projektu związanego z płótnem. Byłem zaskoczony, gdy zauważyłem ten sam brak dostępnych narzędzi do wykrywania ograniczeń rozmiaru płótna w 2018 roku, więc zaktualizowałem kod, opublikowałem go i mam nadzieję, że pomoże to innym napotkać podobne problemy.

jhildenbiddle
źródło
8

Zgodnie ze specyfikacjami w3 , interfejs szerokość / wysokość jest bez znaku długości - więc od 0 do 4 294 967 295 (jeśli dobrze pamiętam tę liczbę - może być kilka).

EDYCJA: Dziwne, jest napisane unsigned long, ale test pokazuje tylko normalną wartość long, jako max: 2147483647. Jsfiddle - 47 działa, ale do 48 i wraca do domyślnych.

WSkid
źródło
Hmm. Ciekawe, ale nie dochodzę nawet do 1,5 miliarda.
seriousdev
Edytowane, przepraszam (to jest to, co dostaję za to, że nie testowałem najpierw) - dodano jsfiddle do pokazania.
WSkid
7

Mimo że płótno pozwoli Ci ustawić wysokość = 2147483647, po rozpoczęciu rysowania nic się nie stanie

Rysowanie dzieje się tylko wtedy, gdy przywracam wysokość do 32767

avikaza123
źródło
7

iOS ma różne ograniczenia.

Korzystając z symulatora iOS 7 udało mi się zademonstrować, że limit to 5MB:

var canvas = document.createElement('canvas');
canvas.width = 1024 * 5;
canvas.height = 1024;
alert(canvas.toDataURL('image/jpeg').length);
// prints "110087" - the expected length of the dataURL

ale jeśli zwiększę rozmiar obszaru roboczego o jeden rząd pikseli:

var canvas = document.createElement('canvas');
canvas.width = 1024 * 5;
canvas.height = 1025;
alert(canvas.toDataURL('image/jpeg'));
// prints "data:," - a broken dataURL
matowe oparzenia
źródło
2
Nie należy opierać takich danych na symulatorze iOS.
Zoltán
5

Na PC
- nie sądzę, że jest jakieś ograniczenie, ale tak, możesz wydostać się z wyjątku pamięci.

Na urządzeniach mobilnych -
Oto ograniczenia kanwy dla urządzeń mobilnych: -

Maksymalny rozmiar elementu Canvas to 3 megapiksele dla urządzeń z mniej niż 256 MB RAM i 5 megapikseli dla urządzeń z co najmniej 256 MB RAM.

Na przykład - jeśli chcesz obsługiwać starszy sprzęt Apple, rozmiar twojego płótna nie może przekraczać 2048 × 1464.

Mam nadzieję, że te zasoby pomogą ci się wyciągnąć.

Manish Gupta
źródło
3

Ograniczenia przeglądarki Safari (wszystkie platformy) są znacznie niższe.

Znane ograniczenia iOS / Safari

Na przykład miałem bufor płótna o wymiarach 6400 x 6400 pikseli z narysowanymi na nim danymi. Śledząc / eksportując zawartość i testując na innych przeglądarkach, mogłem zobaczyć, że wszystko jest w porządku. Ale w Safari pomijałoby rysowanie tego konkretnego bufora w moim głównym kontekście.

NodeNodeNode
źródło
Tak! Safari pomija rysowanie twojego płótna, ponieważ używasz większego płótna jako „dozwolonego”. Zobacz maksymalne wymiary, które zamieściłem powyżej! To jest max. płótno na tym safari / urządzeniach.
Dado,
Twoje rozmiary są dość arbitralne, prawda? Czy masz jakąś dokumentację? Jaki był twój proces testowania?
NodeNodeNode
3

Próbowałem programowo określić limit: ustawienie rozmiaru płótna zaczynając od 35000, zmniejszanie o 100, aż zostanie znaleziony prawidłowy rozmiar. Na każdym kroku zapisuj prawy dolny piksel, a następnie go odczytuj. Działa - z ostrożnością.

Prędkość jest do zaakceptowania, jeśli zarówno szerokość lub wysokość jest ustawiany na pewną wartość w niskich (na przykład 10-200). Ten sposób: get_max_canvas_size('height', 20).

Ale jeśli zostanie wywołany bez szerokości lub wysokości get_max_canvas_size(), utworzone płótno jest tak duże, że odczytanie koloru POJEDYNCZEGO piksela jest bardzo powolne, aw IE powoduje poważne zawieszenie.

Gdyby ten podobny test można było przeprowadzić bez odczytywania wartości piksela, prędkość byłaby akceptowalna.

Oczywiście najłatwiejszym sposobem wykrycia maksymalnego rozmiaru byłby natywny sposób zapytania o maksymalną szerokość i wysokość. Ale płótno to „standard życia”, więc może kiedyś nadejdzie.

http://jsfiddle.net/timo2012/tcg6363r/2/ ( Uwaga ! Twoja przeglądarka może się zawiesić!)

if (!Date.now)
{
  Date.now = function now()
  {
    return new Date().getTime();
  };
}

var t0 = Date.now();
//var size = get_max_canvas_size('width', 200);
var size = get_max_canvas_size('height', 20);
//var size = get_max_canvas_size();
var t1 = Date.now();
var c = size.canvas;
delete size.canvas;
$('body').append('time: ' + (t1 - t0) + '<br>max size:' + JSON.stringify(size) + '<br>');
//$('body').append(c);

function get_max_canvas_size(h_or_w, _size)
{
  var c = document.createElement('canvas');
  if (h_or_w == 'height') h = _size;
  else if (h_or_w == 'width') w = _size;
  else if (h_or_w && h_or_w !== 'width' && h_or_w !== 'height' || !window.CanvasRenderingContext2D)
    return {
      width: null,
      height: null
    };
  var w, h;
  var size = 35000;
  var cnt = 0;
  if (h_or_w == 'height') w = size;
  else if (h_or_w == 'width') h = size;
  else
  {
    w = size;
    h = size;
  }

  if (!valid(w, h))
    for (; size > 10; size -= 100)
    {
      cnt++;
      if (h_or_w == 'height') w = size;
      else if (h_or_w == 'width') h = size;
      else
      {
        w = size;
        h = size;
      }
      if (valid(w, h)) break;
    }
  return {
    width: w,
    height: h,
    iterations: cnt,
    canvas: c
  };

  function valid(w, h)
  {
    var t0 = Date.now();
    var color, p, ctx;
    c.width = w;
    c.height = h;
    if (c && c.getContext)
      ctx = c.getContext("2d");
    if (ctx)
    {
      ctx.fillStyle = "#ff0000";
      try
      {
        ctx.fillRect(w - 1, h - 1, 1, 1);
        p = ctx.getImageData(w - 1, h - 1, 1, 1).data;
      }
      catch (err)
      {
        console.log('err');
      }

      if (p)
        color = p[0] + '' + p[1] + '' + p[2];
    }
    var t1 = Date.now();

    if (color == '25500')
    {
      console.log(w, h, true, t1 - t0);
      return true;
    }
    console.log(w, h, false, t1 - t0);
    return false;
  }
}
Timo Kähkönen
źródło
2
Byłoby to znacznie zoptymalizowane przy użyciu en.wikipedia.org/wiki/Exponential_search
Brendan Annable
1
To naprawdę szalone, że niepowodzenie w stworzeniu tak dużego płótna jest ciche, więc nie ma sposobu, aby to wykryć ... nieźle z twojej strony, to po prostu szalone, że przeglądarki na nas
rzucają się
@ColinD Zgadzam się. Gdzieś byłaby to tylko jedna mała informacja. Byłoby. I powinien.
Timo Kähkönen
3

Kiedy używasz kanw WebGL, przeglądarki (w tym te desktopowe) nakładają dodatkowe ograniczenia na rozmiar bazowego bufora. Nawet jeśli płótno jest duże, np. 16 000 x 16 000, większość przeglądarek wyrenderuje mniejszy (powiedzmy 4096 x 4096) obraz i przeskaluje go w górę. Może to powodować brzydkie piksele itp.

Napisałem kod, aby określić ten maksymalny rozmiar za pomocą wyszukiwania wykładniczego, jeśli ktoś kiedykolwiek tego potrzebuje. determineMaxCanvasSize()to funkcja, która Cię interesuje.

function makeGLCanvas()
{
    // Get A WebGL context
    var canvas = document.createElement('canvas');
    var contextNames = ["webgl", "experimental-webgl"];
    var gl = null;
    for (var i = 0; i < contextNames.length; ++i)
    {
        try
        {
            gl = canvas.getContext(contextNames[i], {
                // Used so that the buffer contains valid information, and bytes can
                // be retrieved from it. Otherwise, WebGL will switch to the back buffer
                preserveDrawingBuffer: true
            });
        }
        catch(e) {}
        if (gl != null)
        {
            break;
        }
    }
    if (gl == null)
    {
        alert("WebGL not supported.\nGlobus won't work\nTry using browsers such as Mozilla " +
            "Firefox, Google Chrome or Opera");
        // TODO: Expecting that the canvas will be collected. If that is not the case, it will
        // need to be destroyed somehow.
        return;
    }

    return [canvas, gl];
}

// From Wikipedia
function gcd(a,b) {
    a = Math.abs(a);
    b = Math.abs(b);
    if (b > a) {var temp = a; a = b; b = temp;}
    while (true) {
        if (b == 0) return a;
        a %= b;
        if (a == 0) return b;
        b %= a;
    }
}

function isGlContextFillingTheCanvas(gl) {
    return gl.canvas.width == gl.drawingBufferWidth && gl.canvas.height == gl.drawingBufferHeight;
}

// (See issue #2) All browsers reduce the size of the WebGL draw buffer for large canvases 
// (usually over 4096px in width or height). This function uses a varian of binary search to
// find the maximum size for a canvas given the provided x to y size ratio.
//
// To produce exact results, this function expects an integer ratio. The ratio will be equal to:
// xRatio/yRatio.
function determineMaxCanvasSize(xRatio, yRatio) {
    // This function works experimentally, by creating an actual canvas and finding the maximum
    // value, the browser allows.
    [canvas, gl] = makeGLCanvas();

    // Reduce the ratio to minimum
    gcdOfRatios = gcd(xRatio, yRatio);
    [xRatio, yRatio] = [xRatio/gcdOfRatios, yRatio/gcdOfRatios];

    // if the browser cannot handle the minimum ratio, there is not much we can do
    canvas.width = xRatio;
    canvas.height = yRatio;

    if (!isGlContextFillingTheCanvas(gl)) {
        throw "The browser is unable to use WebGL canvases with the specified ratio: " + 
            xRatio + ":" + yRatio;
    }

    // First find an upper bound
    var ratioMultiple = 1;  // to maintain the exact ratio, we will keep the multiplyer that
                            // resulted in the upper bound for the canvas size
    while (isGlContextFillingTheCanvas(gl)) {
        canvas.width *= 2;
        canvas.height *= 2;
        ratioMultiple *= 2;
    }

    // Search with minVal inclusive, maxVal exclusive
    function binarySearch(minVal, maxVal) {
        if (minVal == maxVal) {
            return minVal;
        }

        middle = Math.floor((maxVal - minVal)/2) + minVal;

        canvas.width = middle * xRatio;
        canvas.height = middle * yRatio;

        if (isGlContextFillingTheCanvas(gl)) {
            return binarySearch(middle + 1, maxVal);
        } else {
            return binarySearch(minVal, middle);
        }
    }

    ratioMultiple = binarySearch(1, ratioMultiple);
    return [xRatio * ratioMultiple, yRatio * ratioMultiple];
}

Również w jsfiddle https://jsfiddle.net/1sh47wfk/1/

Michał
źródło
Czy istnieje oficjalna dokumentacja dotycząca zachowania podczas skalowania?
Magnus Lind Oxlund
1
@MagnusLindOxlund Informacje zawarte w odpowiedzi zostały określone eksperymentalnie. Najbliższa rzecz, jaką znalazłem po szybkim wyszukiwaniu, to: khronos.org/registry/webgl/specs/latest/1.0/#2.2 , mówiąc: „Powyższe ograniczenie nie zmienia ilości miejsca zajmowanego przez element canvas na stronie internetowej „kiedy mówi o ograniczaniu rozmiaru bufora rysunkowego. Wygląda na to, że standard WebGL nie zawiera szalonych szczegółów na temat tego, jak element canvas ma wyświetlać bufor rysunku.
Michał
1

Możesz go podzielić na kawałki iw javascript automatycznie dodać tyle mniejszych płócien, ile potrzeba, i narysować elementy na odpowiednim płótnie. Możesz w końcu zabraknąć pamięci, ale przekroczysz limit pojedynczego płótna.

JJ_Coder4Hire
źródło
0

Nie wiem, jak wykryć maksymalny możliwy rozmiar bez itteracji, ale możesz wykryć, czy dany rozmiar płótna działa, wypełniając piksel, a następnie odczytując kolor. Jeśli płótno nie zostało wyrenderowane, kolor, który otrzymasz, nie będzie pasował. W

kod częściowy:

function rgbToHex(r, g, b) {
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
}
var test_colour = '8ed6ff';
working_context.fillStyle = '#' + test_colour;
working_context.fillRect(0,0,1,1);
var colour_data = working_context.getImageData(0, 0, 1, 1).data;
var colour_hex = ("000000" + rgbToHex(colour_data[0], colour_data[1], colour_data[2])).slice(-6);
SystemicPlural
źródło