Maximale Größe eines <canvas> -Elements

112

Ich arbeite mit einem Canvas-Element mit einer Höhe von 600bis zu 1000Pixeln und einer Breite von mehreren Zehntausend oder Hunderttausenden von Pixeln. Nach einer bestimmten Anzahl von Pixeln (offensichtlich unbekannt) zeigt die Leinwand jedoch keine Formen mehr an, die ich mit JS zeichne.

Weiß jemand, ob es ein Limit gibt?

Getestet sowohl in Chrome 12 als auch in Firefox 4.

Ernst
quelle
2
@ Šime Er sagte tens OR hundreds of thousands...
Tadeck
6
Das erlebe ich auch. Ich habe eine 8000x8000 Leinwand, die gut funktioniert, aber wenn ich sie größer mache, verschwindet der Inhalt und es wird einfach nicht gezeichnet. Die Größe, bei der es nicht funktioniert, ist auf meinem iPad viel geringer. Ich frage mich, ob es eine Art Gedächtnisbeschränkung ist.
Joshua
28
Es ist seltsam, wenn Sie Ihren eigenen Kommentar von vor 2 Jahren finden und immer noch mit dem gleichen verdammten Problem zu tun haben.
Joshua
4
@ Joshua und noch ein Jahr später!
Eugene Yu
1
Die Frage ist, wie ich diese Fehler oder besser Warnungen abfange. Was sind sie? Ich kann die Hardware des Geräts nicht erkennen und muss daher auf einen Fehler reagieren.
Br4nnigan

Antworten:

115

Aktualisiert am 13.10.2014

Alle getesteten Browser haben Grenzen für die Höhe / Breite von Canvas-Elementen, aber viele Browser beschränken auch die Gesamtfläche des Canvas-Elements. Für die Browser, die ich testen kann, gelten folgende Grenzwerte:

Chrom:

Maximale Höhe / Breite: 32.767 Pixel
Maximale Fläche: 268.435.456 Pixel (z. B. 16.384 x 16.384)

Feuerfuchs:

Maximale Höhe / Breite: 32.767 Pixel
Maximale Fläche: 472.907.776 Pixel (z. B. 22.528 x 20.992)

IE:

Maximale Höhe / Breite: 8.192 Pixel
Maximale Fläche: N / A.

IE Mobile:

Maximale Höhe / Breite: 4.096 Pixel
Maximale Fläche: N / A.

Andere:

Ich kann derzeit keine anderen Browser testen. Weitere Grenzwerte finden Sie in den anderen Antworten auf dieser Seite.


Das Überschreiten der maximalen Länge / Breite / Fläche in den meisten Browsern macht die Leinwand unbrauchbar. (Zeichnungsbefehle werden auch im verwendbaren Bereich ignoriert.) IE und IE Mobile berücksichtigen alle Zeichenbefehle innerhalb des nutzbaren Bereichs.

Brandon Gano
quelle
3
Also, verwandte Frage ... wie kann man das Limit umgehen, wenn eine Leinwand benötigt wird, die größer als das zulässige Maximum ist?
aroth
2
@aroth, am Ende habe ich einen Wrapper um die Canvas-API geschrieben, der mehrere gestapelte <canvas>Elemente verwendet und Zeichenanweisungen automatisch auf den entsprechenden Kontext angewendet hat.
Brandon Gano
1
Klingt sehr nützlich. Nehmen Sie nicht an, es ist auf Github?
aroth
5
Safari (Nicht-iOS-Version) verwendet 32K wie Chrome.Firefox. Wenn Sie versuchen möchten, einen Teil dieses Wrapper-Codes neu zu erstellen, habe ich dank des geringen 8-
KB-
7
iOS 9.3.1 Safari auf einem iPhone 6 Plus ist auf einen Bereich von 16777216 Pixel (z. B. 4096 x 4096) beschränkt. Ich vermute, dies ist eine häufige Zahl auf neueren iOS-Geräten
matb33
16

Ich habe bei Firefox Speicherfehler mit Canvas-Höhen von mehr als 8000 festgestellt. Chrom scheint viel höher zu sein, zumindest bis 32000.

BEARBEITEN: Nachdem ich einige weitere Tests ausgeführt habe, habe ich einige sehr seltsame Fehler mit Firefox 16.0.2 gefunden.

Erstens scheine ich ein anderes Verhalten als im Speicher (in Javascript erstellt) zu haben, im Gegensatz zu HTML-deklarierter Leinwand.

Zweitens, wenn Sie nicht über das richtige HTML-Tag und den richtigen Meta-Zeichensatz verfügen, ist die Zeichenfläche möglicherweise auf 8196 beschränkt, andernfalls können Sie auf 32767 wechseln.

Drittens, wenn Sie den 2D-Kontext der Zeichenfläche abrufen und dann die Zeichenflächengröße ändern, sind Sie möglicherweise auch auf 8196 beschränkt. Durch einfaches Festlegen der Canvas-Größe vor dem Abrufen des 2D-Kontexts können Sie bis zu 32767 haben, ohne dass Speicherfehler auftreten.

Ich konnte die Speicherfehler nicht konsistent abrufen. Manchmal wird sie nur beim Laden der ersten Seite angezeigt, und nachfolgende Höhenänderungen funktionieren. Dies ist die HTML-Datei, die ich mit http://pastebin.com/zK8nZxdE getestet habe .

Ben Burnett
quelle
4
"Zweitens, wenn Sie nicht über das richtige HTML-Tag und den richtigen Meta-Zeichensatz verfügen, ist die Zeichenfläche möglicherweise auf 8196 beschränkt, andernfalls können Sie bis zu 32767" - was meinen Sie hier mit dem richtigen HTML-Tag und Meta-Zeichensatz?
Jack Aidley
Sicher meinst du 8192 (2 ^ 13)? 8196 ist eine seltsame Zahl.
Jamesdlin
14

Maximale iOS-Leinwandgröße (Breite x Höhe):

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

getestet im März 2014.

Dado
quelle
Diese Werte sind beliebig. Bitte
lesen
11

So erweitern Sie die Antwort von @FredericCharette ein wenig: Gemäß dem Inhaltshandbuch von Safari unter Abschnitt " Kenne die Ressourcenbeschränkungen für iOS":

Die maximale Größe für ein Canvas-Element beträgt 3 Megapixel für Geräte mit weniger als 256 MB RAM und 5 Megapixel für Geräte mit mehr oder mehr als 256 MB RAM

Daher funktioniert jede Größenänderung von 5242880 (5 x 1024 x 1024) Pixel auf großen Speichergeräten, andernfalls sind es 3145728 Pixel.

Beispiel für eine Leinwand mit 5 Megapixeln (Breite x Höhe):

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

und so weiter..

Die größten SQUARE-Leinwände sind ("MiB" = 1024x1024 Bytes):

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
quelle
1
Genauer gesagt beträgt das iPhone5-Limit 3 * 1024 * 1024 = 3145728 Pixel. (Nachdem ich mich gefragt hatte, warum meine Leinwand unter Android in Ordnung war, aber unter iOS leer, fand ich diese Antwort und auch stackoverflow.com/questions/12556198/… und brachte meine Phonegap-App zum Laufen.)
Magnus Smith
Zu Ihrer Information, Apple hat bestimmte Größeninformationen aus "Safari / Know iOS Resource Limits" entfernt. Jetzt handelt es sich lediglich um allgemeine Hinweise zur Minimierung der Bandbreite durch Verwendung kleiner Bilder.
ToolmakerSteve
@ matb33 berichtete in einem Kommentar zu der Frage, dass das iPhone 6 anscheinend ein Limit von 16 * 1024 * 1024 hat.
ToolmakerSteve
11

Update für 2018:

Im Laufe der Zeit haben sich die Einschränkungen für die Leinwand geändert. Was sich leider nicht geändert hat, ist die Tatsache, dass Browser über die Canvas-API immer noch keine Informationen zu Einschränkungen der Canvas-Größe bereitstellen.

Wenn Sie die maximale Leinwandgröße des Browsers programmgesteuert ermitteln oder die Unterstützung für benutzerdefinierte Leinwandabmessungen testen möchten, lesen Sie die Leinwandgröße .

Aus den Dokumenten:

Das HTML-Canvas-Element wird von modernen und älteren Browsern weitgehend unterstützt, aber jede Kombination aus Browser und Plattform führt zu eindeutigen Größenbeschränkungen, die eine Canvas unbrauchbar machen, wenn sie überschritten wird. Leider bieten Browser weder eine Möglichkeit, ihre Einschränkungen zu ermitteln, noch geben sie Feedback, nachdem eine unbrauchbare Zeichenfläche erstellt wurde. Dies macht die Arbeit mit großen Canvas-Elementen zu einer Herausforderung, insbesondere für Anwendungen, die eine Vielzahl von Browsern und Plattformen unterstützen.

Diese Mikrobibliothek bietet die maximale Fläche, Höhe und Breite eines vom Browser unterstützten HTML-Canvas-Elements sowie die Möglichkeit, benutzerdefinierte Canvas-Abmessungen zu testen. Durch das Sammeln dieser Informationen vor dem Erstellen eines neuen Canvas-Elements können Anwendungen die Canvas-Abmessungen innerhalb der Größenbeschränkungen jedes Browsers / jeder Plattform zuverlässig festlegen.

In der README-Datei finden Sie einen Demo-Link und Testergebnisse sowie einen Abschnitt mit bekannten Problemen, in dem die Leistung und Überlegungen zur virtuellen Maschine behandelt werden.

Vollständige Offenlegung, ich bin der Autor der Bibliothek. Ich habe es bereits 2014 erstellt und kürzlich den Code für ein neues Canvas-Projekt überarbeitet. Ich war überrascht, dass 2018 derselbe Mangel an verfügbaren Tools zum Erkennen von Einschränkungen der Leinwandgröße festgestellt wurde. Deshalb habe ich den Code aktualisiert, veröffentlicht und hoffe, dass er anderen hilft, auf ähnliche Probleme zu stoßen.

jhildenbiddle
quelle
8

Gemäß den w3-Spezifikationen ist die Breite / Höhe-Schnittstelle eine vorzeichenlose Länge - also 0 bis 4.294.967.295 (wenn ich mich richtig an diese Zahl erinnere - könnten einige davon abweichen).

BEARBEITEN: Seltsamerweise heißt es unsigned long, aber beim Testen wird nur ein normaler langer Wert als max: 2147483647 angezeigt. Jsfiddle - 47 funktioniert, aber bis zu 48, und es wird auf die Standardeinstellungen zurückgesetzt.

WSkid
quelle
Hmm. Interessant, aber ich komme nicht mal auf 1,5 Milliarden.
Seriousdev
Bearbeitet, sorry (das ist es, was ich bekomme, wenn ich nicht zuerst teste) - habe eine jsfiddle hinzugefügt, um sie zu zeigen.
WSkid
7

Obwohl Sie auf der Leinwand die Höhe = 2147483647 festlegen können, geschieht beim Zeichnen nichts

Das Zeichnen geschieht nur, wenn ich die Höhe auf 32767 zurückbringe

avikaza123
quelle
7

iOS hat unterschiedliche Grenzen.

Mit dem iOS 7-Simulator konnte ich zeigen, dass das Limit 5 MB beträgt:

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

aber wenn ich die Leinwandgröße um eine einzelne Pixelreihe vergrößere:

var canvas = document.createElement('canvas');
canvas.width = 1024 * 5;
canvas.height = 1025;
alert(canvas.toDataURL('image/jpeg'));
// prints "data:," - a broken dataURL
matte Verbrennungen
quelle
2
Sie sollten solche Daten nicht auf dem iOS-Simulator basieren.
Zoltán
5

Auf dem PC -
ich glaube nicht, dass es eine Einschränkung gibt, aber ja, Sie können eine Speicherausnahme bekommen.

Auf Mobilgeräten -
Hier sind die Einschränkungen für die Zeichenfläche für Mobilgeräte: -

Die maximale Größe für ein Canvas-Element beträgt 3 Megapixel für Geräte mit weniger als 256 MB RAM und 5 Megapixel für Geräte mit mehr oder mehr als 256 MB RAM.

Wenn Sie beispielsweise die ältere Hardware von Apple unterstützen möchten, darf die Größe Ihrer Leinwand 2048 × 1464 nicht überschreiten.

Hoffe, diese Ressourcen werden Ihnen helfen, Sie herauszuholen.

Manish Gupta
quelle
3

Die Einschränkungen für Safari (alle Plattformen) sind viel geringer.

Bekannte Einschränkungen für iOS / Safari

Zum Beispiel hatte ich einen 6400x6400px-Canvas-Puffer mit darauf gezeichneten Daten. Durch Verfolgen / Exportieren des Inhalts und Testen in anderen Browsern konnte ich feststellen, dass alles in Ordnung war. Aber auf Safari würde es das Zeichnen dieses spezifischen Puffers in meinem Hauptkontext überspringen.

NodeNodeNode
quelle
Ja! Safari überspringt das Zeichnen Ihrer Leinwand, da Sie eine größere Leinwand als "zulässig" verwenden. Siehe maximale Abmessungen, die ich oben gepostet habe! Das ist max. Leinwand auf dieser Safari / Geräte.
Dado
Ihre Größen sind ziemlich willkürlich, nicht wahr? Haben Sie Unterlagen? Was war Ihr Testprozess?
NodeNodeNode
3

Ich habe versucht, das Limit programmgesteuert herauszufinden: Festlegen der Leinwandgröße ab 35000, Verringern um 100, bis eine gültige Größe gefunden wurde. Schreiben Sie in jedem Schritt das Pixel unten rechts und lesen Sie es dann. Es funktioniert - mit Vorsicht.

Die Geschwindigkeit ist akzeptabel, wenn entweder Breite oder Höhe auf einen niedrigen Wert (z. B. 10-200) eingestellt wird: get_max_canvas_size('height', 20) .

Wenn get_max_canvas_size()die erstellte Leinwand jedoch ohne Breite oder Höhe aufgerufen wird , ist die erstellte Leinwand so groß, dass das Lesen einer einzelnen Pixelfarbe sehr langsam ist und im IE zu ernsthaften Hängen führt.

Wenn dieser ähnliche Test auf irgendeine Weise durchgeführt werden könnte, ohne den Pixelwert zu lesen, wäre die Geschwindigkeit akzeptabel.

Natürlich ist der einfachste Weg, die maximale Größe zu ermitteln, eine native Methode, um die maximale Breite und Höhe abzufragen. Aber Leinwand ist 'ein Lebensstandard', also kann es sein, dass es eines Tages kommt.

http://jsfiddle.net/timo2012/tcg6363r/2/ (Achtung! Ihr Browser kann hängen bleiben!)

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
quelle
2
Dies würde durch die Verwendung von en.wikipedia.org/wiki/Exponential_search
Brendan Annable
1
Es ist wirklich verrückt, dass das Versagen, eine so große Leinwand zu erstellen, still ist, so dass es keine Möglichkeit gibt, sie zu erkennen ... keineswegs schlecht von Ihrer Seite, es ist nur eine verrückte Sache für Browser, sich auf uns
Colin D
@ColinD Ich stimme zu. Es wäre nur eine kleine Information irgendwo. Es würde. Und sollte.
Timo Kähkönen
3

Wenn Sie WebGL-Leinwände verwenden, legen die Browser (einschließlich der Desktop-Leinwände) zusätzliche Beschränkungen für die Größe des zugrunde liegenden Puffers fest. Selbst wenn Ihre Leinwand groß ist, z. B. 16.000 x 16.000, rendern die meisten Browser ein kleineres Bild (z. B. 4096 x 4096) und vergrößern es. Das kann zu hässlichen Pixeln usw. führen.

Ich habe Code geschrieben, um diese maximale Größe mithilfe der Exponentialsuche zu bestimmen, falls jemand sie jemals benötigt. determineMaxCanvasSize()ist die Funktion, die Sie interessiert.

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];
}

Auch in einer jsfiddle https://jsfiddle.net/1sh47wfk/1/

Michał
quelle
Gibt es eine offizielle Dokumentation zum Skalierungsverhalten?
Magnus Lind Oxlund
1
@MagnusLindOxlund Die Informationen aus der Antwort wurden experimentell ermittelt. Das Nächste, was ich nach einer schnellen Suche gefunden habe, ist Folgendes : khronos.org/registry/webgl/specs/latest/1.0/#2.2 : "Die obige Einschränkung ändert nicht den Speicherplatz, den das Canvas-Element auf der Webseite belegt "wenn es darum geht, die Größe des Zeichenpuffers zu begrenzen. Es scheint, dass der WebGL-Standard nicht ins Detail geht, wie das Canvas-Element den Zeichenpuffer anzeigen soll.
Michał
1

Sie können es zerlegen und in Javascript automatisch so viele kleinere Leinwände wie nötig hinzufügen und die Elemente auf die entsprechende Leinwand zeichnen. Möglicherweise geht Ihnen irgendwann immer noch der Speicher aus, aber Sie würden das Limit für eine einzelne Zeichenfläche erreichen.

JJ_Coder4Hire
quelle
0

Ich weiß nicht, wie ich die maximal mögliche Größe ohne Iteration erkennen kann, aber Sie können feststellen, ob eine bestimmte Leinwandgröße funktioniert, indem Sie ein Pixel füllen und dann die Farbe wieder auslesen. Wenn die Leinwand nicht gerendert wurde, stimmt die Farbe, die Sie zurückerhalten, nicht überein. W.

Teilcode:

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
quelle