Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Delphi: Звук, графика и видео > GDI+, Direct2D, firemonkey и все все все |
Автор: gonzales 29.5.2013, 16:56 | ||
Доброго времени суток, уважаемые форумчане! Вопрос выбора технологии для отрисовки 2D графики. Необходимые функции 1. отрисовка растров с прозрачностью 2. возможность работы в альфаканалом: использовать для изображения альфаканал из стороннего файла 3. антиалайсинг 4. слои 5. поддержка windows xp Сейчас проект реализован на GDI+, но все работает достаточно медлено. Например пересчет изображения с учетом полупрозрачной маски уменьшает производительность с 94 до 43 fps, но это полбеды. Эта отрисовка происходит во внутренний буфер, а когда я делаю repain, то есть вывожу содержимое буфера на экран, fps падает до 7-10, а это уже очень критично. Вопросов собственно несколько, может кто-то сталкивался. 1. можно ли выводить битмап на канву быстрее чем
2. Можно ли каким-то образом прикрутить к GDI+ аппаратную акселерацию. 3. Можно ли использовать D2D для вывода, а GDI+ для обработки изображения одновременно В общем, задача несколько размыта, надеюсь на Ваши мысли Заранее спасибо |
Автор: Beltar 29.5.2013, 17:54 |
GLScene, Asphyre Sphinx или иной движок. Сфинкс поддерживает даже DX11, а я на нем недавно вариацию Lines сделал, на полностью заполненном поле из 240 клеток с 4-хцетными градиентными шарами у меня 200+ fps тянуло, но на днях автор объявил о закрытии проекта, ссылаясь на занятость, так что неясно что с ним будет. :( D2D под XP не работает. Вообще. Слои, я так понимаю, та же прозрачность? В Corel Photo Paint, например, такого понятия как слой вообще нет, есть просто объекты, которыые от фотошоповых слоев неотличимы. |
Автор: gonzales 29.5.2013, 21:33 |
спасибо, покопаюсь. а есть ли какая инфа по d2d? я так понял, что firemonkey приложения по умолчанию d2d используют. Добавлено @ 21:43 еще вопросик, по сфинксу можете что-нибудь посоветовать, литературу, примеры заранее спасибо кстати, написанные приложения работают на xp? |
Автор: Beltar 29.5.2013, 22:45 |
D2D это DirectX. Fire Monkey принципиально OpenGL иначе никакой кроссплатформенности не будет. Литература есть по DirectX, гугл решает. По сфинксу литературы я не видел. Однако там сами исходники очень грамотно написаны и откомментированы. В простейшем случае просто берешь какой-нибудь из примеров, идущих с ним, выкидываешь, оттуда рисовку, загружаешь свои ресурсы (помимо графики он поддерживает создание упакованных ресурных и сеть) и просто вызываешь в ProcessEvent свою обработку логики, а в RenderEvent свою отрисовку. Таймер с синхронизацией там уже готовый. Если игровой подход, когда игра просто шпарит кадры насколько может, периодически вызывая обсчет логики не устраивает, то можно предельный fps ограничить и в принципе я так понимаю, просто отключить таймер и вызывать рисовку, как в GDI Refresh. Само 2D-рисование в принципе такое же, как в GDI, даже в чем-то проще, т. к. не надо 100500 свойств у канваса задавать и всякие буферные битмепы юзать, базовые примитивы есть, только есть тонкости, связанные со сбросом кеша рендеринга и просадкой fps. По крайней мере, это то, что я в простейшем 2D проекте заметил. По GLScene ничего не скажу, но у нее есть русскоязычное сообщество. |
Автор: gonzales 30.5.2013, 11:15 | ||
Beltar, спасибо!!! буду пробывать Alexeis, я писал в топике про просадку фпс при вызове repaint. Какая оптимизация еще может быть? вот что пишут официально про d2d
то есть получается, что на вин7 автоматически уже используют новую систему рендеринга Direct2D, или же это надо дополнительно подключать в самом приложении и выводить уже явно через Direct2D. К сожалению такой инфы я не нашел |
Автор: Beltar 30.5.2013, 12:39 | ||||
Мне года 3 назад советовали GDI+. Я попробовал, оказалось, что даже медленнее, чем GDI, по крайней мере на моей задаче. DirectX и, насколько я понимаю, OpenGL напрямую для 2D-графики использовать попахивает мазохизмом, т. к. они работают с вершинами и треугольниками, а базовых примитивов для 2D-графики там нет. Т. е. или сам их делай, или юзай готовый движок. Я так понимаю, что D2D и есть такой движок, работал бы на XP, думаю, никто бы уже на GDI не писал.
В случае Repaint ИМХО некорректно говорить об fps. Оно ведь рисуется 1 раз и потом может не перерисовываться сколь-угодно долго. Например, ты перетащишь форму, она не будет перерисована. Касательно тормозов и оптимизаций, то можно было код привести, что оптимизировать наверняка найдется.
Если приложение написано в эпоху Win9X и ничего кроме GDI не знает, то и юзать оно будет только GDI. От семерки тут ничего не зависит, она рисует каждое приложение так, как приложение запрашивает. |
Автор: Alexeis 30.5.2013, 13:27 | ||
Драйвера видео могут оптимизировать операции с растрами даже при использовании GDI . Насколько я помню самая быстрая GDI функция для обновления SetDIBitsToDevice . |
Автор: gonzales 30.5.2013, 14:31 | ||
Beltar, пробую юзать сфикса. с подгрузкой растров из файлов я разобрался, а вот со шрифтами какая-то лажа. Сфинкс позволяет юзать системные шрифты или только с подгрузкой имиджей из своего архива?
перерисовка произходит по mousemove с изменением параметров, поэтому приходится перерисовывать многократно. код слишком громоздкий, поэтому не привожу Alexeis, спасибо за наводку, буду пробывать. |
Автор: Beltar 30.5.2013, 15:14 | ||
Я так понял, что использует пререндеренные, утилита для их создания прилагается. Меня для игры вполне устроил шрифт из примера Shapes. Обычные я не пробовал.
В таких случаях часто можно разделить код на 2 части, рисование ресурсоемкой подложки (например сложное изображение, график и т. п., в общем основная часть) на битмепе, потом этот битмеп копируется на другой, а нем рисуются разного рода надписи, вспомогательные линии, и уже готовый рисунок копируется на канвас (ну можно и сразу на канвас копировать и по нему дорисовать, по обстановке). И соответствено в событиях мыши вызывается только быстрая дорисовка. |
Автор: gonzales 30.5.2013, 17:03 | ||||
так тоже пробывал, создаю битмеп, на нем отрисовываю, потом копирую на canvas. Но все равно приходится вызывать repaint, иначе не перерисовывается. Может это из-за doublebuffer:=true? Или из-за того, что отрисовка происходит не в onPaint?
|
Автор: Beltar 30.5.2013, 17:15 |
Я уже не помню, когда рисовал сразу по форме или чему-то еще, обычно делал свой контрол и у него просто перекрывал Paint. Repaint руками по идее вызывать не нужно, она сама вызывается при, например, Refresh. |
Автор: Alexeis 30.5.2013, 18:15 | ||
Вполне вероятно. Лучше, если указать винде, что кусок области стал невалидным при помощи функции InvalidateRect , при этом желательно отключить очистку фона у окна. После вызова InvalidateRect винда сама вызовет OnPaint . Далее при помощи BeginPaint в обработчике OnPaint мы можем узнать какой же прямоугольник требует обновления и обновить только его. |
Автор: gonzales 30.5.2013, 21:37 | ||||
конечно я тоже сделал свой)) наследник TImage. Но я не перекрывал paint а сделал свою процедуру DrawImage которую вызываю по MouseMove. Repaint или Refrash в данном случае значения не имеет. Подскажите, принципиально перекрывать Paint, есть какое-то кардинальное отличие от любой другой процедуры вызываемой руками? Как в таком случае должен выглядеть алгоритм перерисовки OnMouseMove -> InvalidateRect -> OnPaint -> {отрисовка битмапа} -> SetDIBitsToDevice
я правильно понимаю, что я должен вызвать InvalidateRect для всей области моего контрола и тем самым вызвать OnPaint? Буду пробывать! Кстати, SetDIBitsToDevice предполагает конвертацию из TBitmap в DIB, не даст ли это обратный эффект (ухудшения оптимизации)? ЗЫ. Комрады, в любом случае спасибо, очень приятно обсуждать вопросы с нормальными людьми, ВинГрад один из немногих форумов, где можно получить реальную помощь ![]() |
Автор: Beltar 30.5.2013, 23:43 |
Ну я примерно вот так делал. Проект на XE3, но там бэкапы файла проекта для 2007 и XE лежат, а сам компонент будет работать даже в D7, если все inline убрать. Производительность довольно высокая. У меня на Core Duo 2.66 даже в Debug версии строит график по 10 сериям с 1 728 010 точками примерно за 0.3 сек. Это полная отрисовка с примерно 148000 вызовами LineTo для 1920х1080. А так можно возить по нему мышкой, подлагивает, но не сильно, если зона рисования меньше, то лага может и не быть. Первый вариант Lines на GDI с 240 клетками у меня без лага полностью перерисовывался по MouseMove. Ты что-то не так, похоже, делаешь. |
Автор: Alexeis 31.5.2013, 10:38 | ||||
Неа, только для области под курсором. Если можно перерисовывать не все то желательно так и делать.
Эмм.. насколько я понимаю TBitmap это и есть DIB . По крайней мере TBitmap может быть DIB если ему установить PixelFormat . По идее, получается прямое рисование DIB на контекст безо всякого копирования. |
Автор: gonzales 31.5.2013, 16:30 | ||||
что то я совсем запутался, окончательно и бесповоротно(((((((( сделал я SetDIBitsToDevice все одно, пока repaint или refrash не вызову, изображение на экране не перерисовывается. перекрыл событие Paint для моего наследника TImage, написал банальное
получаю 4 мессаджбокса, хотя процедура перерисовки DrawImage вызывалась 1 раз. Это может быть из-за того, что во время перерисовки на экране появляется вторая форма, частично закрывающая мой контрол? Если я пишу
то все навеки зависает. Может я свойство какое у TImage не заполняю, что он так себя ведет? Практически все реализовано как в примере AvgChart. Только там используется TCustomControl, а у меня TImage. убираю из функции DrawImage repaint и fps на уровне 50, но отрисовка идет не плавно а большими рывками, ставлю repaint, fps падает до 7 но отрисовка гораздо плавнее.((( |
Автор: Beltar 31.5.2013, 20:04 |
Между TImage и TCustomControl есть одно отличие, TImage не имеет дескриптора. Я, правда, не помню сейчас, почему я использовал именно TCustomControl, а не TGraphicControl, но что-то меня там не устроило. В принципе есть еще и компонент TPaintBox, Но это тоже GraphicControl. Попробуй тоже TCustomControl заюзать. Paint должен вызываться автоматически при любой перерисовке, разворачивание окна, закрытие его части и т. п. Я не вижу смысла вызывать методы перерисовки кроме Refresh вручную. Как ты fps мерял? В играх средне fps измеряется из-за непрерывности цикла отрисовки, тут-то цикла нету. |
Автор: gonzales 1.6.2013, 12:12 | ||
а у tcustomcontrol у меня возникли проблемы с прозрачным canvas. Изначально я и хотел сделать из TCustomControl контейнер, в который положить несколько TImage, которые будут как бы слоями с изображениями, но не смог добиться прозрачного фона. TImage уже предполагает работу с прозрачностью, поэтому я его и выбрал. А fps меряю просто. Замеряю кол-во тактов в начале процедуры и в конце, делю на частоту процессора, получаю время работы процедуры. Ну а fps - это будет 1/время. Это конечно не средне-статистическое значение, но приблизительное представление оно дает, по крайней мере порядок величины. Добавлено через 5 минут и 48 секунд
как раз приходится вызывать вручную, чтобы избежать ненужных прорисовок объектов, когда из например частично перекрывают другие объекты. если я запихну процедуру отрисовки объекта в Paint то при любом перекрытии объекта он будет перерисован, хотя с ним никаких изменений не происводилось. |
Автор: Beltar 1.6.2013, 13:23 |
Как-то слишком сложно. Если у тебя там нет непрерывной анимации, то для замера времени будет достаточно GetTickCount. Можно Stopwatch, т. к. GetTickCount дает значения с шагом 16. Но если у тебя рисуется за 100 мс, то тебе и этой точности хватит для оценки лага. Если есть анимация, то лучше заюзать Сфинкса и не думать о таймерах, 2D-графику он тебе даже на нетбуке, скорее всего, вытащит. Избыточные вычисления, если на сотни fps шпарить будет, забирая ядро целиком можно подавить ограничением fps. Да и фотошопы вроде уже давно за счет аппаратной отрисовки живут. Заблокировать отрисовку можно просто добавив какое-то условие в ф-ию отрисовки, или в Paint перед ее вызовом. Сам вызов Paint ты не можешь контролировать, если приложение свернули, а затем развернули, то Paint будет вызван. Но вот само условие блокировки рисовки будет весьма нетривиальным. У меня в AvgChart ручной вызов перерисовки делается только в том случае, если были изменены данные и то можно с помощью BeginUpdate перекрыть это дело. Опиши лучше, что рисуешь и в каком порядке. Не исключено, что и саму Paint можно свести к тупому вызову BitBlt, или как она там на канвасе, Draw вроде, а сложную часть запоминать на битмепе и уже ее перерисовывать вручную, если, например, добавился объект, или был сделан ресайз контрола. |
Автор: Alexeis 1.6.2013, 14:01 | ||
TImage лучше совсем не трогать по вопросам сообщений. Надежнее обращаться к окну которое лежит ниже. Я бы вообще не использовал TImage. Этот класс предназначен для статических изображений. Эффективнее выводить графику прям на окно. |
Автор: gonzales 3.6.2013, 13:22 | ||
заменил repaint на paint, отрисовка заработала на несколько порядков быстрей, НО Paint не стирает содержимое а рисует поверх. Как в перекрытом методе Paint заставить его стереть изображение? Рисую графический фрейм для изображения. см рисунок. создаю его на основе TImage. прописал функции mousedown, mousemove, mouseup и drawimage. По событиям мыши производятся все координатные вычисления, затем вызывается drawimage.
|
Автор: gonzales 3.6.2013, 13:44 | ||
пробую внедрить флаг перерисовки, все равно канва не очищается
|
Автор: Beltar 3.6.2013, 14:37 | ||||
А она и не будет очищаться, просто залей ее цветом фона перед рисованием.
Говорю же, Paint вызывается автоматически. Должно быть
Ты сейчас все запутываешь только. В DrawImage должно быть описано как рисовать, а не когда. Для этого программа сама когда нужно Paint вызовет. Вот у тебя на картинке рамка, ты за нее явно тянешь. У тебя только рамка тянется или само изображение тут же перерисовывается? Если только рамка, то кто мешает хранить изображение на битмепе, перерисовывая только разметку, как у меня с графиком и делается? |
Автор: gonzales 3.6.2013, 15:39 | ||||||||
изображение тоже пееррисовывается.
Так у меня и описано. А когда перерисовывать решают события мыши. Я убрал явный вызов drawImage из событий мыши и написал
и получил висяк, у меня бесконечно вызывается Paint, что вообще логично, получается рекурсия.
так я заливаю Canvas.FillRect(Canvas.ClipRect); и все равно не очищается. |
Автор: gonzales 3.6.2013, 17:14 | ||
я не понимаю как у тебя в примере все работает
у тебя по paint вызывается draw, в котором сидит fastrepaint, в котором изменяется канва Canvas.Draw(0,0,Bmp1); соответственно опять должно автоматически вызваться Paint и опять же рекурсия. Но я так понимаю, этого не происходит. Можешь объяснить, почему? |
Автор: Beltar 3.6.2013, 19:32 |
Зачем? Paint вызывается или автоматом, когда контролу надо перерисоваться (типичный случай, разворот приложения, или изменение размеров контрола), или если контролу был сделан принудительный Refresh. Но к низкоуровневым функциям, которые переводят байты канваса в пиксели монитора она никакого отношения не имеет. У TCustomControl Paint ЕМНИП вообще пустая. Обработка мыши у меня прописана в самом контроле и вызывает только быструю упрощенную прорисовку. Попросту Paint это метод, который должен осуществлять полную прорисовку по умолчанию. В твоем случае рисование тоже разделяется на 2 части: сами картинки и пользовательская разметка. http://biblioteka.cc/index.php?newsid=30955 стр 75. ЕМНИП Repaint как раз и вызывает Paint. |
Автор: gonzales 5.6.2013, 13:19 | ||||
если не имеет, то я не понимаю, почему у меня все время вызывается paint, если я использую
получается у меня где-то в drawimage вызывается paint? |
Автор: gonzales 5.6.2013, 13:48 |
НАШЕЛ!!! методом научного тыка выяснил функция Canvas.FillRect(Canvas.ClipRect); в DrawImage вызывает Paint осталось понять, как очистить канву не используя FillRect |
Автор: Beltar 5.6.2013, 15:12 |
А ты там брейкпойнт не ставишь, так что приложение переключается нна IDE, а потом обратно и снова рисуется? |
Автор: gonzales 5.6.2013, 16:05 | ||
у меня два экрана, я вижу, что оно Paint постоянно вызывается
вот такой код приводит к цикличности. |
Автор: Beltar 5.6.2013, 18:00 |
Это проблема не заливки, а базового класса, у меня все работает именно потому что TCustomControl. При наследовании от TImage тоже загрузка проца в 100% вечным циклом. Попробуй унаследоваться от TGraphicControl, который обычно и юзают для своих компонентов, которым не надо оконного дескриптора, с ним у меня все правильно работает и ничего не виснет, а что там у TImage уже сделано смотреть надо. |
Автор: gonzales 6.6.2013, 10:46 |
Beltar, спасибо большое! В общем и целом понятно. Буду пробывать TGraphicControl |
Автор: gonzales 5.7.2013, 15:59 | ||||
Beltar, прошу помощи. Я переделал компонент на TGraphicControl, соответственно
но заметил одну особенность, если у меня на форме несколько компонентов TFrameImage, то при изменении одного, перерисовываются все, то есть Paint срабатывает для всех, даже если они не перекрываются. Соответственно при сложных перерисовках и большом кол-ве компонентов опять имею тормоза. Как заставить перерисовываться только компонент, с которым сейчас производятся манипуляции? пробывал внедрять флаг needrepaint
но получаю следующее: компонент с которым работаю перерисовывается, а все остальные при этом пропадают, становятся прозрачными. |
Автор: Gwire 29.7.2013, 09:15 |
gonzales, я на GDI+ делал стрелочный индикатор. И заметил интересную особенность если перерисовывать на холсте TWinControl (скажем TPanel) c размером, подогнанным под то, что собираешься рисовать, то производительность заметно увеличивается (в сравнении, если рисовать прямо на холсте TForm) По поводу оптимизации подумай, что у тебя статичное и рисуй его в на холсте буферного TBitmap. B момент перерисовки просто "печатай" с помощью BitBlt(), а после дорисуй динамические элементы. Например: В Индикаторе шкала ёмкая но статичная. Стрелка динамическая но она одна =) Если я правильно понял у тебя графические объекты которые нужно перемещать по форме. Здесь есть "захваченный" и "остальные". Так вот "остальные" - это статика. В TBitmap рисуешь все кроме "захваченного". А захваченный рисуешь уже на TWinControl. По другому еще можно для каждого графического объекта сделать "обертку" на основе TGraphicControl, тогда - что и когда перерисовывать будет "думать" система. - - - - - - PS: Если конечно графические объекты у тебя сами не "шевелятся". Хотя я думаю и там можно найти статику. ![]() - - - - - - PPS: Не увидел вторые станицы, на которой рассмотрены некоторые моменты ![]() |
Автор: Beltar 12.10.2013, 22:32 |
Хм-м. Похоже, что из-за TGraphicControl и перерисовка всех, он ведь своей канвы не имеет и рисуется поверх винконтрола на котором лежит. Т. е. нужен все-таки кто-то с дескриптором вроде TCustomControl. |