| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Звук, графика и видео > Прорисовка массива пикселей на граф. компонент |
| Автор: RESIN 7.12.2010, 04:02 | ||
| Здравствуйте уважаимые! Очень нужна помощь. Имеется двумерный массив значений цветов пикселей Picture[y, x] типа array of array of longint. Массив достаточно большой, ~3000х3000 значений (=пикселей). Требуется сравнительно быстро ( > 30 кадров в секунду) выводить этот массив на любой графический компонент (по вашему усмотрению), который позволит создать события OnDblKlick, OnMouseMove, OnMouseDown и OnMouseUp. К тому же нужно, чтобы алгоритм вывода был не чувствителен к изменению размеров и позиции выбранного компонента, а при изменении позиции (Left и Top) изображение не "мерцало". Уже пробовал Scanline-ами, BitBlt-ом, Handle-ом, QCanvas-ом (на ассемблере). Тормозит до жути. С OpenGL не разобрался до конца, сам дошел только до вот такого извращения:
Но это дебилизм полнейший, хоть и работает (иногда появляются вертикальные черные полоски толщиной 1px). Пожалуйста, помогите решить задачу с учетом того, что выводить нужно ИМЕННО двумерный массив, описанный мной в пользовательских типах., не битмап, не спрайт, не PNGObject, а именно массив. Буду крайне рад готовому примеру (ну эт я уже обнаглел Готов рассмотреть любые варианты: GDI, OpenGL, DirectX (DirectDraw) и любой другой. |
| Автор: x128 7.12.2010, 10:43 | ||
1) Массив полностью обновляется >30 раз в секунду? 2) Что подразумевается под "не чувствителен к изменению размеров и позиции выбранного компонента", нужно выводить определенную часть массива? |
| Автор: Bitter 7.12.2010, 14:55 |
| Можешь использовать устаревший DIrectDraw, но хоть он и устарел, он именно для этих целей и очень простой. В общем он для быстрого копирования растра на экран. |
| Автор: RESIN 7.12.2010, 15:30 |
| to x128: Полностью обновляется >90 раз в секунду. Массив может менять свои размеры в зависимости от заданного масштаба. В програме так же доступен скроллинг. По-этому при выводе компонент должен менять свой размер, а при скроллинге - позицию. to Bitter: А как это сделать? Подскажите, пожалуйста. И где взять SDK DirectDraw для Delphi. |
| Автор: Bitter 7.12.2010, 16:35 |
| скачай книгу М Краснова "DitectX Графика в проектах Delphi". Вот ссылка. http://www.delphilab.ru/files/book/krasnov_dx.7z К ней прилагаются и примеры и модули для подключения DirectX. Там первая половина книги как раз про DirectDraw, но тебе не нужна вся половина, тебе достаточно будет несколько первых глав. |
| Автор: RESIN 7.12.2010, 17:42 | ||||
| Спасибо большое, Bitter! Скачал, разбираюсь, нравится. Правда уже при попытке запустить пример Ex10 из Chapter02 возникла проблема. Впринципе, не важно, какой пример, CodGear Rad 2009 ругается на строку #173 в модуле DirectDraw.pas:
Пишет: [DCC Error] DirectDraw.pas(173): E2154 Type 'IDirectDrawSurface' needs finalization - not allowed in variant record Не знаешь, как решить сию проблему?
Чаще и не нужно )) Просто сейчас я иногда не получаю и 15 кадров в сек., и это косяк именно вывода на экран (проверял). По поводу SetDIBitsToDevice - а она правда сможет принять в качестве параметра мой массив? |
| Автор: Bitter 7.12.2010, 18:30 | ||||
RESIN, попробуй написать вместо
вот так:
а если он тебе понадобится, то использовать приведение типов. но я думаю не понадобится Добавлено через 1 минуту и 47 секунд По поводу той ошибки, я так понял, что в делфи 2010 (как и в Делфи 5) нельзя включать COM объекты в вариантные записи, бла бла бла и так далее |
| Автор: Alexeis 7.12.2010, 19:17 | ||
Нет конечно, но операция копирования байтов в DIB Bitmap не такая уж трудоемкая по сравнению с операцией вывода на экран (по времени) средствами GDI. На самом деле это отнимет времени не превышающего 10% от времени отрисовки (зависит от производительности процессора), поэтому ею можно пренебречь. |
| Автор: AntonN 7.12.2010, 23:59 |
| А вы найдете такой монитор, чтобы вывести все 3000*3000 пикселей? Вывод на экран осуществляется путем копирования куска этого массива ил ресайзом массива до размеров контрола? |
| Автор: AntonN 8.12.2010, 00:41 |
| Если нужно выводить часть массива, то выводи в битмап только часть его, а сам битмап ограничь размерами контрола. Вывод на контрол через BitBlt() (это очень быстрая функция по сравнению с остальным GD). Однако собрав тестовый проект я не смог без тормозов изменять данные хотя бы 30 раз в секунду по всему массиву (рандомный нойс по всем каналам), уже один проц полностью напрягался (коре 2 дуо 6420). Вывод же сам по себе по диспетчеру занимает 0-1%. Прилепил тестовый проект (только ехе, проверить напряжность операции) - http://desksoft.ru/index.php?downloads=attachments&id=304 (380кб), исходный массив за кадр изменяется путем рисования пяти линий, ума не приложу как изменять его полностью, это гигабайт в секунду (при 30 кадрах) нужно проворачивать, писать и читать в память - нехило так. ЗЫ курсорными клавишами - "навигация" |
| Автор: Bitter 8.12.2010, 01:39 |
| Думаю всё же под "полностью обновляется" автор имел в виду "частично обновляется", потому, что действительно гиг в секунду переписать это анбиливбл какой-то. Хотя вот я только что попробовал в фотошопе картинку 3000х3000 прогнать через фильтр "шум", ну замерить время выполнения я не знаю как, но на глаз миллисекунд 50 заняло это, так что теоретически это можно делать 20 раз в секунду как минимум. да, и если уж нужно всё таки обновлять такой массив, то это делается частичным обновлением видимой области. |
| Автор: AntonN 8.12.2010, 01:58 |
| Это реально, но задача так нефигово ресурсоемкая, в плане пропускной способности (если тупо переписывать из какого то массива в этот). Фотошоп может использовать универсальные методы, потому может позволить себе тормозить. В принципе, я могу понять когда массив обновляется кем-то (класс какой либо, процесс) в какой либо области, и нам не известна эта область (как у меня в архиве выше - рисуется линия с рандомными координатами), но нужно быть готовым увидеть любую часть такого массива. Тогда рекомендую делать как я выше сказал - в битмап загонять лишь часть массива, а сам битмап через BitBlt() (битмап нужен для буферизации, в onpaint контрола его же и рисовать на контроле). Тогда вывод получается вполне быстрым и ненапряжным. |
| Автор: RESIN 8.12.2010, 03:17 |
| Заверяю всех, что макссив по крайней мере 2000х2000 пикселей у меня меняется с весьма высокой скоростью. Проверял так: создал таймер интервалом 5 сек., создал отдельный поток, на который в бесконечный repeat-until повесил преобразование массива (склейка из отдельных массивов того же типа с наложением некоторых поверх и некот. вычислениями). Там же сделал инкрементацию переменной, считающей колличество повторений цикла. В самом таймере обнулял значение кол-ва кадров, предварительно выведя его, деленное на 5, в капчу формы. Даже относительно старый Pentium 4 с тактовой 1,3 примерно пятьдесят раз в секунду мой массив обновлял. Потому что в преобразованиях я использовал указатели на строки массивов пикселей (индексы Y), а сами преобразования для каждого пикселя хорошо оптимизировал (битовые сдвиги вместо умножений и делений, вынесения общих множетелей и т.д.). После формирования массив масштабируется исходя из вещественного коэффициента: создается еще один массив, в который переписывается выборка строк и столбцов из исходного массива. Выборка тоже быстрая, т.к. вычисление индексов производится в целых числах: использовал алгоритм Брезентхамма для инкрементирования индексов, если интересно - могу показать. Эту часть тоже включал в зацикленный поток - утяжелила на ~0,3 кадра в секунду. А вот вывод отмасштабированного массива очень медленен. Если масштаб =0.2 (20%) то еще терпимо, а вот когда =1 (100%) - жестокие тормоза, даже на моем Core2 Duo E8600. Насчет обрезки массива по видимой области контролла уже думал, но будет очень неудобно переписывать часть программы: у меня много действий происходит по нажатию клавиши мыми по контроллу, и сам скроллинг контролла происходит по движению мыши по нему с зажатой ЛКМ. Придется подумать над преобразованием координат. Сделать впринципе можно, но размер контролла почти равен размеру самой формы (за минусом бордеров, гор. и верт. скроллов и маленькой панельки сверху, кот. скрывается либо отображается по двойному ЛКМ), а форма, в свою очередь, может и на весь экран быть растянута. Боюсь, что даже обрезка массива до 1200х980 не даст требуемой производительности (хоть правда, будет быстрее, чем сейчас). Добавлено через 2 минуты и 50 секунд Да, и при скроллинге боюсь появления мерцаний и "артефактов", если вывод не будет скроллы догонять... |
| Автор: Alexeis 8.12.2010, 09:00 | ||
Можно использовать метод ближайшего соседа + отказаться от floating poing для вычисления точки откуда брать цвет. |
| Автор: x128 8.12.2010, 10:05 | ||
Если массив обновляется >90 раз в секунду (более 3гб в сек.), это уже за приделами производительности для большинства компьютеров даже при обычном копировании не говоря о прочих операциях. Самый разумный подход, это обновлять только видимую часть изображения(массива) и выводить этот участок на экран, при таком подходе нагрузка будет минимальная. |
| Автор: RESIN 8.12.2010, 14:09 | ||
Не понял? Вещественные числа для вычисления координат пикселей, рисуемых на экран под масштабом? Итак использую целочисленный алгоритм (спер идею у Брезентхамма). |
| Автор: AntonN 8.12.2010, 14:11 | ||
А у меня видел мерцание? =) Затестил с ресайзом из fastlib, файсресайз и билинейный, оба позволяют отресайзить поле 1024*786 почти без тормозов (первый так вообще без них). Правда на линиях при таком ресайзе даже биленейного мало для качества. http://desksoft.ru/index.php?downloads=attachments&id=305 |
| Автор: RESIN 8.12.2010, 14:16 | ||
Так понимаю, делиться тем, как ты этого добился не собираешься? Кстати, если не менять размера, позиции Пеинтбокса, у меня тоже не мерцает. Но вот скорость не ахти... |
| Автор: AntonN 8.12.2010, 16:55 |
| В onpaint рисуй буферный битмап на контрол, и не будет мерцания. мне пока некогда выпилить из класса вещи, которые я не могу показывать |
| Автор: RESIN 9.12.2010, 01:36 | ||
Так и лежит А проблема то в том и состоит, что буферный битмап долго рисуется даже со сканлайном. Ищу сейчас методы быстрого формирования битмапа... |
| Автор: AntonN 9.12.2010, 01:50 |
| рисуется или формируется? |
| Автор: RESIN 9.12.2010, 05:13 |
| Не так выразился, справедливое замечание ). Формируется медленно. BitBlt куда быстрее на канву Пейнтбокса происходит, в сравнении с самим формированием битмапа. |
| Автор: Alexeis 9.12.2010, 09:50 | ||
Не может быть! Совсем наоборот ScanLine дает адрес обычной памяти. При помощи него я за секунду могу создать несколько тысяч кадров, но отрисовать лишь несколько десятков. |
| Автор: AntonN 9.12.2010, 19:42 | ||
может, сканлайн дает адрес, а мы кроме того что берем адрес еще им и манипулируем, вот тут и затык - как быстро заполнить данными битмап |
| Автор: Alexeis 9.12.2010, 20:14 |
Адресация по указателю никак не медленнее чем элемента массива, так что не вижу ни каких оснований для тормозов. Кстати построчная обработка быстрее чем послолбцовая. |
| Автор: RESIN 10.12.2010, 03:02 | ||
100% вы правы! Только вот сама функция Scanline, видимо не очень быстро адрес возвращает. |
| Автор: RESIN 10.12.2010, 04:32 | ||||
Вот так я сейчас отрисовываю, используя библиотеку QuickPixels с мастеров делфи: http://www.delphimaster.ru/articles/pixels/
Или вот так, со сканлайном, без QuickPixels:
Разници на своем E8600 не заметил. Если есть идеи, как это можно улучшить, делитесь, пожалуйста |
| Автор: AntonN 10.12.2010, 09:12 | ||
Преобразование типов при изменении байта по указателю, операции над longint при изменении байта по указателю В то время как у меня "пиксели" напрямую копируются через регистр MMX в две операции. Вот о чем я - мало адрес получить, важно какими операциями в него писать. RESIN, у меня там было два массива, один 3000*3000, второй по размеру битмапа вывода (он потом 1:1 выводился в битмап, который и рисовался). Быстрый ресайз (fast который, без качества) на меньший массив (так проще было сделать переключалку на полный показ массива и только на его часть), потом вывод. Постараюсь сегодня выпилить пример. Добавлено через 1 минуту и 16 секунд Ах да, самое главное - битмап создавать только один раз, а не при каждом выводе |
| Автор: Alexeis 10.12.2010, 09:42 | ||||||||
Так зачем же вызывать ее каждый раз? На самом деле ее достаточно вызывать всего 1 раз, после создания битмапа для получения адреса последней строки, что и будет адресом начала всего битмапа. Дальше строки идут одна за другой.
А я и не говорил что нужно адресовать байты.
А смысл? Запись в регистры mmx и вывод из них в память займет дополнительное время, в результате выигрыш от такого копирования будет исчезающе мал (по сравнению с той же CopyMemory).
Интересно, что такой же косяк я заметил в коде в механизме двойной буферизации VCL |
| Автор: AntonN 10.12.2010, 23:42 | ||||||||
потому что я не копирую весь массив в битмап, а лишь часть его. CopyMemory тоже не волшебством работает, пока в тестах победил вариант с копированием через регистры MMX (по 4 байта в регистре, но с movq по 8, я обычно парой запускаю, это 16). Еще одна фича - у битмапа в памяти строки наоборот.
что быстрее:
или
? вот о чем я, то что сканлайн дал адрес - это одно, то что нам в этот адрес надо записать (и главное как записать) - другое. |
| Автор: AntonN 11.12.2010, 00:10 |
| Проект, выпиленый. Кроме самого массива 3000*3000 создается дополнительный под размер вывода, на который ресайзится большой, потом этот меньший выводится в битмап, битмап на канву |
| Автор: RESIN 11.12.2010, 10:24 | ||||
| AntonN: Спасибо огромное за пример! Добавлено @ 10:30 Вопрос теперь, ко всем. Какой код будет работать быстрее? Этот:
или этот:
A, B, G, R : byte; pix : longint; |
| Автор: x128 14.12.2010, 13:43 | ||
Быстрей будет такой код:
|