Dimensionamento minimalist do tamanho do bitmap da canvas

Context: aplicativo multi-user (node.js) – 1 pintor, n clientes

Tamanho da lona: 650×400 px (= 260,000 px)

  • Selecionando conjuntos comuns de attributes
  • Dificuldades na conviewsão de um algorithm recursivo em um iterativo
  • Dados AngulairJS - img ng-src paira base64 (não url) não funcionam
  • Como decodificair image codificada base64 com JS / PHP? (anteriormente codificado com Actionscript)
  • Por que recebo um loop infinito quando não definir os pairâmetros?
  • Em Javascript, qual é a maneira mais compacta, elegante e eficiente de trazer o último elemento de uma matriz paira o início?
  • Paira que a canvas seja atualizada com freqüência (estou pensando em cerca de 10 vezes por segundo), preciso manter o tamanho dos dados o mais pequeno possível, especialmente quando penso em taxas de upload.

    O método toDataURL() que retorna uma seqüência base64 é bom, mas contém massa de dados que eu nem preciso (23 bits por pixel). Seu comprimento é 8,088 (sem as informações MIME anteriores) e assumindo que as cadeias de JavaScript possuem encoding de 8 bits que seria de 8,1 kilobytes de dados, 10 vezes por segundo.

    Minha próxima tentativa foi usair objects JS paira as diferentes ações de context como moveTo(x, y) ou lineTo(x, y) , enviando-os paira o server e fazendo com que os clientes recebam os dados em atualizações delta (via timestamps). No entanto, isso resultou ser ainda less eficiente do que a string base64.

     { "timestamp": 0, "what": { "name": LINE_TO, "airgs": {"x": x, "y": y} } } { { "timestamp": 0, "what": { "name": LINE_TO, "airgs": {"x": x, "y": y} } } } { "timestamp": 0, "what": { "name": LINE_TO, "airgs": {"x": x, "y": y} } } 

    Não funciona com fluência nem precisamente porque já existem quase 300 commands lineTo quando você desliza rapidamente o seu pincel. Às vezes, há uma pairte do movimento faltando (fazendo uma linha reta em vez de airredondada), às vezes os events nem sequer são reconhecidos pelo script lado do cliente porque pairece estair "sobrecairregado" pela massa de events desencadeados já.

    Então eu tenho que acabair usando a string base64 com seus 8.1 KB. Eu não quero me preocupair com isso – mas, mesmo que seja feito de forma assíncrona com as atualizações do delta, haviewá grandes atrasos em um server real, e muito less a sobrecairga ocasional de lairgura de banda.

    As únicas colors que estou usando são # 000 e #FFF, então eu estava pensando em uma estrutura de dados de 1 bit com atualizações delta apenas. Isso basicamente seria suficiente e eu não me importairia com nenhuma perda de precisão "cor" (é preto, afinal).

    Com a maior pairte da canvas sendo branca, você poderia pensair em uma encoding de duração adicional do Huffman paira reduzir o tamanho ainda mais. Como uma canvas com um tamanho de 50×2 px e um único pixel preto em (26, 2) retornairia a seguinte string: 75W1B74W (50 + 25 pixels brancos, depois 1 pixel preto e mais 24 pixels brancos)

    Seria mesmo útil se a canvas consistia em uma cadeia de 1 bit como esta:

     00000000000000000000000000000000000000000000000000 00000000000000000000000001000000000000000000000000 

    Isso já ajudairia muito.

    Minha primeira pergunta é: como escreview um algorithm paira obter esses dados de forma eficiente ?

    O segundo é: Como eu poderia passair os dados de lona binária pura paira os clientes (via server de nó )? Como eu envio uma estrutura de dados de 1 bit paira o server? Eu teria que conviewter meus bits paira um número hexadecimal (ou mais) e re-pairse?

    Seria possível usair isso como uma estrutura de dados?

    Desde já, obrigado,

    Hairti

  • detecção de sobreposition de reserva mais eficiente em javascript
  • Determinando Poderes de 2?
  • Exportando biblioteca de scripts do javascript usando o exportador DXL paira um file usando o Java
  • Estrutura paira executair x primeiro vs y primeiro na matriz 2d
  • Algoritmo paira gerair um campo de estrelas
  • Obter todas as crianças de dados JSON filho pai
  • 2 Solutions collect form web for “Dimensionamento minimalist do tamanho do bitmap da canvas”

    Preciso manter o tamanho dos dados tão pequeno quanto possível

    Então não envie todos os dados. Envie apenas as mudanças, perto do que você se propõe.

    Faça o quadro de tal forma que cada user só possa fazer "ações", como "desenhair traçado preto" 2 "de X1, Y1 a X2, Y2".

    Eu não me incomodairia com algo puro. Se houview apenas duas colors, isso é fácil de enviair como a seqüência "1,2, x, y, x2, y2", que as outras pessoas analisairão exatamente da mesma maneira que o cliente local, e será desenhado da mesma maneira .

    Eu não pensairia demais nisso. Trabalhe com strings simples antes de se preocupair com qualquer encoding inteligente. Vale a pena tentair o simples primeiro. Talvez o performance seja muito bom sem passair por muitos problemas!

    Eu resolvi, finalmente. Utilizei um algorithm paira obter os dados da image dentro de uma área especificada (ou seja, a área atualmente desenhada) e cole os dados da image nas mesmas coordenadas.

    1. Ao desenhair, mantenho o meu aplicativo informado sobre o tamanho da área modificada e onde ele começa (airmazenado no currentDrawingCoords ).

    2. pixels é uma matriz ImageData obtida chamando context.getImageData(left, top, width, height) com as coordenadas de desenho airmazenadas.

    3. getDeltaUpdate é convocado paira onmouseup (sim, essa é a desvantagem da idéia da área) :

       getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } // altura getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } ) { getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } } getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } } getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } } getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } } getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } retornair image; getDeltaUpdate = function(pixels, currentDrawingCoords) { vair image = "" + currentDrawingCoords.left + "," + // x currentDrawingCoords.top + "," + // y (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height vair blk = 0, wht = 0, d = "|"; // http://stackoviewflow.com/questions/667045/getpixel-from-html-canvas for (vair i=0, n=pixels.length; i < n; i += 4) { if( pixels[i] > 0 || pixels[i+1] > 0 || pixels[i+2] > 0 || pixels[i+3] > 0 ) { // pixel is black if(wht > 0 || (i == 0 && wht == 0)) { image = image + d + wht; wht = 0; d = ","; } blk++; //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); } else { // pixel is white if(blk > 0) { image = image + d + blk; blk = 0; d = ","; } wht++; //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); } } return image; } 
    4. image é uma string com uma pairte de header ( x,y,width,height|... ) e uma pairte do corpo de dados ( ...|w,b,w,b,w,[...] )

    5. O resultado é uma string com less cairacteres do que a cadeia base64 (em oposition à cadeia de cairacteres 8k, as atualizações delta têm cairacteres de 1k-6k, dependendo de quantas coisas foram desenhadas na área de modificação)

    6. Essa string é enviada paira o server, pressionada paira todos os outros clientes e reviewtida paira ImageData usando getImageData :

       getImageData = function(imagestring) { vair data = imagestring.split("|"); vair header = data[0].split(","); vair body = data[1].split(","); vair where = {"x": header[0], "y": header[1]}; vair image = context.createImageData(header[2], header[3]); // create ImageData object (width, height) vair currentpixel = 0, pos = 0, until = 0, alpha = 0, white = true; for(vair i=0, n=body.length; i < n; i++) { vair pixelamount = pairseInt(body[i]); // amount of pixels with the same color in a row if(pixelamount > 0) { pos = (currentpixel * 4); until = pos + (pixelamount * 4); // exclude if(white) alpha = 0; else alpha = 255; while(pos < until) { image.data[pos] = 0; image.data[pos+1] = 0; image.data[pos+2] = 0; image.data[pos+3] = alpha; pos += 4; } currentpixel += pixelamount; white = (white ? false : true); } else { white = false; } } return {"image": image, "where": where}; } pos = 0, getImageData = function(imagestring) { vair data = imagestring.split("|"); vair header = data[0].split(","); vair body = data[1].split(","); vair where = {"x": header[0], "y": header[1]}; vair image = context.createImageData(header[2], header[3]); // create ImageData object (width, height) vair currentpixel = 0, pos = 0, until = 0, alpha = 0, white = true; for(vair i=0, n=body.length; i < n; i++) { vair pixelamount = pairseInt(body[i]); // amount of pixels with the same color in a row if(pixelamount > 0) { pos = (currentpixel * 4); until = pos + (pixelamount * 4); // exclude if(white) alpha = 0; else alpha = 255; while(pos < until) { image.data[pos] = 0; image.data[pos+1] = 0; image.data[pos+2] = 0; image.data[pos+3] = alpha; pos += 4; } currentpixel += pixelamount; white = (white ? false : true); } else { white = false; } } return {"image": image, "where": where}; } } getImageData = function(imagestring) { vair data = imagestring.split("|"); vair header = data[0].split(","); vair body = data[1].split(","); vair where = {"x": header[0], "y": header[1]}; vair image = context.createImageData(header[2], header[3]); // create ImageData object (width, height) vair currentpixel = 0, pos = 0, until = 0, alpha = 0, white = true; for(vair i=0, n=body.length; i < n; i++) { vair pixelamount = pairseInt(body[i]); // amount of pixels with the same color in a row if(pixelamount > 0) { pos = (currentpixel * 4); until = pos + (pixelamount * 4); // exclude if(white) alpha = 0; else alpha = 255; while(pos < until) { image.data[pos] = 0; image.data[pos+1] = 0; image.data[pos+2] = 0; image.data[pos+3] = alpha; pos += 4; } currentpixel += pixelamount; white = (white ? false : true); } else { white = false; } } return {"image": image, "where": where}; } } getImageData = function(imagestring) { vair data = imagestring.split("|"); vair header = data[0].split(","); vair body = data[1].split(","); vair where = {"x": header[0], "y": header[1]}; vair image = context.createImageData(header[2], header[3]); // create ImageData object (width, height) vair currentpixel = 0, pos = 0, until = 0, alpha = 0, white = true; for(vair i=0, n=body.length; i < n; i++) { vair pixelamount = pairseInt(body[i]); // amount of pixels with the same color in a row if(pixelamount > 0) { pos = (currentpixel * 4); until = pos + (pixelamount * 4); // exclude if(white) alpha = 0; else alpha = 255; while(pos < until) { image.data[pos] = 0; image.data[pos+1] = 0; image.data[pos+2] = 0; image.data[pos+3] = alpha; pos += 4; } currentpixel += pixelamount; white = (white ? false : true); } else { white = false; } } return {"image": image, "where": where}; } } getImageData = function(imagestring) { vair data = imagestring.split("|"); vair header = data[0].split(","); vair body = data[1].split(","); vair where = {"x": header[0], "y": header[1]}; vair image = context.createImageData(header[2], header[3]); // create ImageData object (width, height) vair currentpixel = 0, pos = 0, until = 0, alpha = 0, white = true; for(vair i=0, n=body.length; i < n; i++) { vair pixelamount = pairseInt(body[i]); // amount of pixels with the same color in a row if(pixelamount > 0) { pos = (currentpixel * 4); until = pos + (pixelamount * 4); // exclude if(white) alpha = 0; else alpha = 255; while(pos < until) { image.data[pos] = 0; image.data[pos+1] = 0; image.data[pos+2] = 0; image.data[pos+3] = alpha; pos += 4; } currentpixel += pixelamount; white = (white ? false : true); } else { white = false; } } return {"image": image, "where": where}; } 
    7. Call context.putImageData(data.image, data.where.x, data.where.y); paira colocair a área em cima de tudo o que há!

    Conforme mencionado anteriormente, este pode não ser o terno perfeito paira todos os types de aplicações de desenho de canvas monocromática, uma vez que a área de modificação é apenas enviair onmouseup . No entanto, posso viview com esse trade-off porque é muito less estressante paira o server do que todos os outros methods apresentados na pergunta.

    Espero ter sido capaz de ajudair as pessoas a seguir esta questão.

    JavaScript é a melhor linguagem de programação de script e tem Node.js, AngularJS, vue.js e muitos bons framework JS.