Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   invalid array length (Копирование массиывов класса ArrayBuffer ) (https://javascript.ru/forum/misc/44837-invalid-array-length-kopirovanie-massiyvov-klassa-arraybuffer.html)

Merkury 04.02.2014 01:43

invalid array length (Копирование массиывов класса ArrayBuffer )
 
Ребята, извиняюсь если не туда.
Подскажите, что делаю не правильно (заренее скажу, что кое-как проблему победил, но это не торт).
Пытаюсь разобраться с воркерами и с безболезненной передачей ему данных (с точки зрения производиительности).
Ситуация следующая:
1. Имеется массив ImageData.data (тип Uint8ClampedArray)
2. передаю его буфер в воркер. Соответственно воркер получает сам буфер, т.е. тип массива ArraBufferView. В воркере пока ничего не делаю (позже воркер будет делать преобразования изображения и накладвать фильтры), возвращаю этот буфер обратно в основной поток (как есть, т.е. тестирую передачу данных воркеру и обратно).
3. Взвращенный буфер привожу к типу Uint8ClampedArray.
4. Далее нужно поместить полученные значения из воркера в ImageData.data и отобразить.
Как раз в этом пункте начинаются прблемы.

Примерный код п.п. 1-3:
// Создаем канвас, отображаем картинку, получаем ImageData
var cnv, ctx, imData;
cnv = document.createElement('canvas');
cnv.height = h;  // картинку img, h и w получем по коду выше (не суть)
cnv.width = w;
ctx = cnv.getContext('2d');
panorama.context.drawImage(img,0,0);
imData = ctx.getImagedata(0,0,w,h);
...
// создан воркер, определен обработчик события onmessage
// передаем данные воркеру (передаю буфер)
worker.postMessage(imData.data.buffer, [imData.data.buffer]);
console.info(imData.data.byteLength);


Поясню, передача работает корректно, т.к. при успешной передаче, imData.data.byteLength будет 0 (ноль), что и подтверждает информация в косоле.
Отступление. Кто не знает, скажу, если передвать данные просто как массив, т.е. так: worker.postMessage(imData.data), то массив два раза будет конвертироваться в json-объект, 1-й раз при передаче туда, 2-й раз - оттуда, что накладно при передаче нескольких МБайт данных. При передаче буффера содержимое не копируется, а предполагаю, старый массив перестает ссылать на участок памяти, на него ссылается event.data внутри воркера.
Кто знает как происходит, подскажите плз, ибо русской инфы в инете не нашел, а английского не знаю, так, только знакомые слова.
Конец отступления.

Теперь начинаются танцы с бубном.
Данные от воркера я получаю и делаю различные проверки, которые подтверждают, что воркер отдает в event.data тот же буффер, но сложности возникают при его отображнии на канвасе.

Попытка №1. Первое что приходит в голову, это взять и присвоить буффер из event.data обратно в imData:
imData.data.buffer = event.data

Далее пытаюсь отобразить полученное:
ctx.putImageData(imData,0,0);

Но в этой строке получаю ошибку: SyntaxError: An invalid or illegal string was specified
*Чешу затылок* Ошибок быть не должно (исключительно мое мнение, которое оcновано на скудных знаниях JS)
Вопр. №1: Почему не сработало присвоение буфера? Ведь это былоб эффективно.

Попытка №2. Тогда создаю объект типа Uint8ClampedArray из полученного буфера и пытаюсь тупо присвоить созданный массив массиву ImData.data (по сути ImData.data становится ссылкой на resImData, это меня вполне устраивает, т.к. я пытаюсь уйти от лишних переборов большого массива).
var resImData = new Uint8ClampedArray(event.data);
imData.data = resImData;
ctx.putImageData(imData,0,0);

*Печально* В результате та же ошибка, и тот же вопрос что и при первой попытке (в обоих примерах массив ImData.data получается исходного размера, данные читаются, например alert(imData.data[3] показывает значение прозрачности пикселя).

Попытка №3. Делаю то, от чего хотел уйти, а именно перебор данных большого массива. Т.е. вместо присвоения ссылки как в примере №2 использую метод set() обекта Uint8ClampedArray.
var resImData = new Uint8ClampedArray(event.data);
imData.data.set(resImData);
ctx.putImageData(imData,0,0);

*убица веником* Новая ошибка "RangeError: invalid array length".
Вопр. №2 Почему не putImageData не захотел работать с этим массивом даже после перебора?

Попытка №4 (На самом деле попыток, как и мыслей, интересных слов, было ооочень много).
После некольких кружек кофе подумалось "а что если в воркер в парметрах передать не сам imData.data.buffer, а буффер из ссылки", т.е. так (переделал п.2 описанные в самом начале, хотя мысль пипец как бредовая):
...
// создам ссылку на массив imData.data
var imDataTest =  imData.data;
// передаем данные воркеру (передаю буфер)
worker.postMessage(imDataTest.buffer, [imDataTest.buffer]);
console.info(imDataTest.byteLength);

А дальше осталось то, что и было в попытке №2:
var resImData = new Uint8ClampedArray(event.data);
imData.data.set(resImData);
ctx.putImageData(imData,0,0);


*в шоке* Сработало, потом сделал несколько преобразований изображения в воркере, чтоб убедиться, что отображаются именно данные полученные из воркера.
Вопр. №3 Что произошло, ведь массивы во всех случаях получились одинаковые?
Вопр. №4 Как можно обойтись без метода set(), т.е. без переборов массива при передачи и приемке данных в воркер/из воркера?

Merkury 04.02.2014 01:54

PS. Браузер ФФ 27.0 Beta

Merkury 05.02.2014 23:21

Да, сделав замеры времени выполнения
imData.data.set(resImData);

и конструкции
for (var i=0; i < resImData.byteLength; i++){
  imData[i] = resImData[i];
}

понял что это далеко не одно и тоже.
Метод set() отрабатывает меньше одной мс, примерно за 0.45-0.55мс, а цикл, при тех же условиях, за 1000-1200мс.
Могу только догадываться как работает set(), но работает быстро, что мне и нужно, вопрос №4, наверное теряет актуальность, но если есть еще способы, приму в дар мнения школьников, советы бывалых, наставления гуру.
Если кто может дать ответы на вопросы 1-3, милости прошу, не стесняемся.


Часовой пояс GMT +3, время: 13:40.