2012-03-05 32 views
4

上下文:多用户应用程序(的node.js) - 1个画家,正客户最小化画布 “位图” 的数据大小

帆布尺寸: 650x400像素(= 260000像素)

对于画布会经常更新(我每秒思考10次),所以我需要保持尽可能小的数据大小,特别是在考虑上传速率时。

返回base64字符串的toDataURL()方法很好,但它包含我甚至不需要的大量数据(每像素23位)。它的长度是8,088(没有前面的MIME信息),并假设JavaScript字符串具有8位编码,即8.1千字节数据,每秒10次。

我的下一个尝试使用JS对象用于不同环境的行动像moveTo(x, y)lineTo(x, y),将它们发送到服务器,并有客户收到增量更新数据(通过时间戳)。然而,这比base64字符串效率更低。

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

,因为有近300 lineTo命令已经当您联系您的刷卡刷它不流利,也没有精确的工作。有时候会有一部分运动缺失(使一条直线而不是四舍五入),有时事件甚至不会被脚本客户端识别,因为它似乎已经被已经触发的大量事件“淹没”了。

所以我必须最终使用8.1 KB的base64字符串。我不想担心这一点 - 但即使与增量更新异步完成,实际服务器上也会出现严重滞后现象,更不用说偶尔带宽超限了。

我使用的唯一颜色是#000和#FFF,所以我只考虑1位数据结构,只有增量更新。这基本上就足够了,我不介意任何“颜色”精度损失(毕竟它是黑色)。

由于大部分画布都是白色的,您可以考虑使用额外的Huffman游程编码来进一步缩小尺寸。像的尺寸50X2像素中,并在一个单一的黑色像素画布(26,2)将返回以下字符串:75W1B74W(50个+ 25的白色像素,然后1个黑色像素,则24个白色像素)

它即使倘使画布由1位串这样的:

00000000000000000000000000000000000000000000000000 
00000000000000000000000001000000000000000000000000 

这将有很大的帮助了。

我的第一个问题是:如何写一个算法来获得这个数据高效

二是:我怎么能(通过节点服务器)通过纯二进制数据帆布的客户呢?我怎样才能将1位数据结构发送到服务器?我需要将我的位转换为十六进制(或更多)的数字并重新解析吗?

是否可以使用this作为数据结构?

由于提前,

哈尔蒂

回答

1

我最后整理出来了。我使用了一种算法来获取指定区域(即当前绘制的区域)内的图像数据,然后将图像数据粘贴到相同的坐标上。

  1. 虽然画,我把我的应用程序了解改性区有多大,它开始的地方(存储在currentDrawingCoords)。

  2. pixels是通过调用context.getImageData(left, top, width, height)与存储的绘图坐标获得的ImageData数组。

  3. getDeltaUpdate是在onmouseup称为(是的,这是区域构思的缺点):(...|w,b,w,b,w,[...]

    getDeltaUpdate = function(pixels, currentDrawingCoords) { 
        var image = "" + 
         currentDrawingCoords.left + "," + // x 
         currentDrawingCoords.top + "," + // y 
         (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width 
         (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height 
        var blk = 0, wht = 0, d = "|"; 
    
        // http://stackoverflow.com/questions/667045/getpixel-from-html-canvas 
        for (var 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是具有报头部分(x,y,width,height|...)和数据本体部分的字符串)

  5. 结果是具有比所述的base64串少字符的字符串已经(相对于8K的字符串,增量更新具有1K-6K字符,依赖新生丁多少东西被抽入修改区)

  6. 该字符串被发送到服务器,被推到所有其他客户端和使用getImageData复归的ImageData:

    getImageData = function(imagestring) { 
        var data = imagestring.split("|"); 
        var header = data[0].split(","); 
        var body = data[1].split(","); 
    
        var where = {"x": header[0], "y": header[1]}; 
        var image = context.createImageData(header[2], header[3]); // create ImageData object (width, height) 
    
        var currentpixel = 0, 
        pos = 0, 
        until = 0, 
        alpha = 0, 
        white = true; 
    
        for(var i=0, n=body.length; i < n; i++) { 
         var pixelamount = parseInt(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. 呼叫context.putImageData(data.image, data.where.x, data.where.y);把这个区域放在所有东西上面!

正如前面提到的,这可能不是每一种单色画布绘制应用的完美套装,因为修改区域只提交onmouseup。但是,我可以忍受这种折衷,因为它比服务器上的所有其他方法的压力要小得多。

我希望我能帮助人们去关注这个问题。

2

我需要保持数据尺寸尽可能小

那就不要发送整个数据。只发送更改,接近你自己提出的建议。

制作框架,使每个用户只能执行“操作”,例如“从X1,Y1到X2,Y2绘制黑色笔画宽度2”。

我不会打扰一些纯二进制的东西。如果只有两种颜色,那么可以很容易地发送字符串“1,2,x,y,x2,y2”,其他人将以与本地客户端完全相同的方式进行解析,并且将以相同方式绘制。

我不会过时这一点。在担心任何聪明的编码之前,先用简单的字符串来处理它。值得首先尝试简单的事情。也许这个表现会很好,不会遇到很多麻烦!

+0

是的,这也是我第一次想到的(我也是这样做的)。 我使用的对象类似如下: '{ \t “时间戳”:0, \t “什么”:{ \t \t “名”:LINE_TO, \t \t “ARGS”:{ “X”: X, “Y” 为:y} \t \t} }' 然而,大约60X40像素的单刷线,你有超过300个这样的命令了。 但是,简单(较短)的字符串而不是对象的想法听起来也很不错。 对象对我来说并不是很流利(在localhost上)。 Base64字符串确实可以流畅地工作,但恐怕只有LAN节点服务器才是这种情况。 – Harti 2012-03-06 10:12:47