Минимизация размера данных «растрового изображения» холста - PullRequest
4 голосов
/ 05 марта 2012

Контекст: многопользовательское приложение (node.js) - 1 художник, n клиентов

Размер холста: 650x400 px (= 260 000 px)

Чтобы холст часто обновлялся (я думаю, примерно 10 раз в секунду), мне нужно сохранять как можно меньший размер данных, особенно когда речь идет о скорости загрузки.

Метод toDataURL(), возвращающий строку base64, хорош, но он содержит массу данных, которые мне даже не нужны (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 команд, когда вы быстро проводите кистью.Иногда часть движения отсутствует (делает прямую, а не закругленную линию), иногда события даже не распознаются клиентской стороной сценария, потому что кажется, что они «перегружены» уже запущенной массой событий.

Итак, мне нужно использовать строку base64 с ее 8,1 КБ.Я не хочу беспокоиться об этом, но даже если асинхронно выполняется с дельта-обновлениями, на реальном сервере будут существенные задержки, не говоря уже о случайном превышении пропускной способности.

Я использую только цвета # 000 и #FFF, поэтому я думал о 1-битной структуре данных только с дельта-обновлениями.Этого было бы достаточно, и я не возражал бы против каких-либо «цветных» потерь точности (в конце концов, это black ).

Поскольку большая часть холста белая, вы можете подумать о дополнительном кодировании длин серий Хаффмана, чтобы еще больше уменьшить размер.Как холст размером 50x2 px и один черный пиксель в (26, 2) вернет следующую строку: 75W1B74W (50 + 25 белых пикселей, затем 1 черный пиксель, затем еще 24 белых пикселя)

Было бы даже полезно, если бы холст состоял из 1-битной строки, подобной этой:

00000000000000000000000000000000000000000000000000 
00000000000000000000000001000000000000000000000000

Это уже очень помогло бы.

Мой первый вопрос: как написать алгоритм для получения этих данных эффективно ?

Второй вопрос: как я мог передать чистыйдвоичные данные холста для клиентов (через сервер узла )?Как я могу даже отправить 1-битную структуру данных на сервер?Должен ли я преобразовать свои биты в шестнадцатеричное (или более) число и выполнить повторный анализ?

Можно ли использовать this в качестве структуры данных?

Заранее спасибо,

Harti

Ответы [ 2 ]

2 голосов
/ 05 марта 2012

Мне нужно, чтобы размер данных был как можно меньше

Тогда не отправляйте данные целиком.Отправляйте только изменения, близкие к тому, что вы предлагаете сами.

Создайте структуру таким образом, чтобы каждый пользователь мог выполнять только «действия», такие как «рисовать черный штрих-ширину 2 от X1, Y1 до X2, Y2».

Я бы не стал беспокоиться о какой-то бинарной вещи.Если есть только два цвета, то это легко отправить в виде строки «1,2, x, y, x2, y2», которую другие люди будут анализировать точно так же, как и локальный клиент, и она будет отображена таким же образом..

Я бы не стал обдумывать это.Работайте с простыми строками, прежде чем беспокоиться о любой умной кодировке.Сначала стоит попробовать простую вещь.Может быть, производительность будет довольно хорошей, без особых проблем!

1 голос
/ 23 марта 2012

Я разобрался, наконец. Я использовал алгоритм, чтобы получить данные изображения в пределах указанной области (то есть области, в которой в данный момент рисуется), а затем вставить данные изображения в те же координаты.

  1. Во время рисования я информирую свое приложение о том, насколько велика измененная область и где она начинается (хранится в currentDrawingCoords).

  2. pixels - это массив ImageData, полученный путем вызова context.getImageData(left, top, width, height) с сохраненными координатами чертежа.

  3. getDeltaUpdate призван onmouseup (да, это недостаток идеи области) :

    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 = "|";
    
        // /541449/getpixel-iz-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|...) и частью тела данных (...|w,b,w,b,w,[...])

  5. В результате получается строка с меньшим количеством символов, чем строка base64 (в отличие от строки 8k символов, дельта-обновления имеют 1k-6k символов, в зависимости от того, сколько элементов было нарисовано в области модификации)

  6. Эта строка отправляется на сервер, отправляется всем остальным клиентам и возвращается к ImageData с помощью getImageData:

    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. Тем не менее, я могу жить с этим компромиссом, потому что он гораздо менее стрессовый для сервера, чем все другие методы, представленные в вопросе.

Я надеюсь, что смог помочь людям следовать этому вопросу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...