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(), т.е. без переборов массива при передачи и приемке данных в воркер/из воркера? |
PS. Браузер ФФ 27.0 Beta
|
Да, сделав замеры времени выполнения
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. |