Разработка собственных компонентов - Оптимизация отрисовки графики
Разработка собственных компонентов - Оптимизация отрисовки графики
У
каждого из нас бывают ситуации, когда кажется что того набора компонент,
которые доступны в стандартной поставке, недостаточно для комфортной работы.
Иногда написание своих компонентов является фатальной необходимостью. Так,
например, в моей практике часто возникали ситуации, когда необходимо было
разрабатывать новые элементы управления, которые по своей функциональности
заменяли бы несколько стандартных элементов. Зачастую к созданию новых
элементов управления нас подталкивает мода на всякого рода красивости,
которые так любят обычные пользователи.
Но так ли часто мы задумываемся о том, правильно ли выбрана реализация того
или иного компонента, на сколько быстро и эффективно работает наш компонент
и не будет ли его использование замедлять работу нашей программы? Конечно,
сегодня аппаратное обеспечение позволяет всё реже и реже задумываться о
таких вещах, но в случаях, когда недоработок слишком много, быстродействие
может снижаться очень заметно.
Сегодня я хочу рассказать о том, как же все-таки избавить себя от
головной боли при разработке элементов управления и обеспечить максимальное
быстродействие при отрисовке графики.
Представим себе ситуацию, что мы решили создать свой компонент и уже
определились с его функционалом (у нас это будут аналоговые часы). Прежде
чем начинать работу над компонентом, неплохо было бы создать небольшое
тестовое приложение, которое позволило бы проверить его работоспособность.
Ничего сложного мы делать не будем, и создадим обычное GUI-приложение с
одной формой:
PaintingTestMainFrame.h
#ifndef _PAINTING_TEST_MAINFRAME_H
#define _PAINTING_TEST_MAINFRAME_H
#include
#include "wxPaintingTestCtrl.h"
class PaintingTestMainFrame : public wxFrame
{
void CreateControls();
public:
PaintingTestMainFrame(wxWindow * parent, wxWindowID id = wxID_ANY,
const wxString & title = _("Painting Optimization Test"));
bool Create(wxWindow * parent, wxWindowID id = wxID_ANY,
const wxString & title = wxEmptyString,
const wxPoint & pos = wxDefaultPosition,
const wxSize & size = wxDefaultSize,
long style = wxCAPTION|wxSYSTEM_MENU|
wxMINIMIZE_BOX|wxMAXIMIZE_BOX|wxCLOSE_BOX|
wxRAISED_BORDER|wxRESIZE_BORDER);
};
#endif
Класс компонента содержит два объекта класса wxBitmap – первый хранит
себе фоновое изображение (m_BackgroundBitmap), а второй – изображение
циферблата (m_CenterBitmap).
Метод DoGetBestSize() позволяет переопределить размер компонента по
умолчанию (у нас это будет wxSize(100,100), см. ниже)
Get/SetBackgroundBitmap и Get/SetCenterBitmap – это, соответственно,
aceessor-методы для фонового изображения и для изображения циферблата
OnPaint – Обработчик события wxEVT_PAINT (отвечает за отрисовку графики)
Отлично. Собираем наше приложение, запускаем. И что мы видим: сильное
мерцание при изменении размеров. Но с этой проблемой мы тоже можем довольно
легко справиться, сделав небольшие изменения в исходном коде
wxPaintingTestCtrl.h
...
class wxPaintingTestCtrl : public wxControl
{
...
DECLARE_EVENT_TABLE()
...
void OnEraseBackground(wxEraseEvent & event);
};
...
Класс wxBufferedPaintDC обеспечивает doublebuffering при отрисовке, что
позволяет избавиться от мерцания.
Пустой обработчик события wxEVT_ERASE_BACKGROUND также позволяет немного
ускорить отрисовку.
Собираем приложение, запускаем…. отлично, мерцание исчезло. Но стрелки
наших аналоговых часов изменяют положение только при перерисовке содержимого
компонента. Это значит, что нам не обходимо добавить таймер, который будет
инициировать отрисовку через определенный промежуток времени
wxPaintingTestCtrl.h
...
class wxPaintingTestCtrl : public wxControl
{
protected:
...
wxTimer * m_Timer;
...
DECLARE_EVENT_TABLE()
void OnRefreshTimer(wxTimerEvent & event);
};
...
Собираем, запускаем.
Отлично. Видно что часы идут. Но на этом работа не заканчивается.
Разворачиваем окно программы на весь экран, запускаем Task Manager и
начинаем перемещать окно Task Manager над компонентом
В результате получаем загрузку процессора на 100%. Это происходит потому
что при каждой перерисовке объект класса wxBufferedPaintDC создает
изображение размером с наш компонент, отрисовка производится на это
изображение в памяти, а потом это изображение отрисовывается на компонент.
Попробуем сделать так, чтобы наш компонент работал быстрее и не потреблял
такое огромное количество ресурсов. Для этого мы попробуем реализовать
doublebuffering вручную.
Добавим новуые переменные в класс компонента
wxPaintingTestCtrl.h
...
class wxPaintingTestCtrl : public wxControl
{
protected:
wxBitmap m_DoubleBuffer;
wxMemoryDC m_DoubleBufferDC;
...
};
...
Как видно, при изменении размеров мы пересоздаем изображение,
используемое для doublebuffering’а, ассоциируем с ним контекст устройства
m_DoubleBuffer и производим отрисовку на изображение в памяти. А в
обработчике OnPaint производим копирование из контекста устройства
изображения на контекст устройства компонента. В результате загрузка
процессора значительно снизилась
Ну и результатом всей нашей работы будет вот такой компонент – аналоговые
часы
Хотелось бы заметить, что реализация doublebuffering'а вручную не всегда
является самым удачным выбором. Для компонентов, которые будут иметь
небольшой размер на форме, можно использовать wxBufferedPaintDC (или даже
wxPaintDC если не требуется отрисовка большого количества графических
объектов)
Проект для VisualStudio 2005 и wxDev-CPP можно скачать
на странице
автора.
При разработке CMS S.Builder наша команда
активно использовала AJAX. Теперь вот решили поделиться накопленным
опытом. Начнем с этого хабратопика. Не буду здесь затрагивать различные
фреймворки и библиотеки. Свой код всегда роднее. Для работы с AJAX-ом в
S.Builder написана библиотека
sbAJAX. Можете качать и пользоваться :). В этом файле есть функция
sbEvalJS. Для тех, кто не знает, объясню. При подгрузке через AJAX и вставке
на страницу HTML-кода, содержащего JavaScript, JavaScript выполняться не будет
или полезут баги. Эта функция как раз решает поставленную задачу.
Хотя наш обзор немного запоздал, оригинальный Dojo 1.2 вышел в релизной
версии ещё 6-го октября, но сейчас мы наверстаем упущенное. И так, Dojo Toolkit — это самая мощная и
гибкая ajax-библиотека из всех, что есть на рынке, она активно развивается и
имеет большое комьюнити. Кстати, это самое комьюнити, совместно с компанией
Sitepen, имеет ещё несколько проектов, среди которых и Cometd и некоторые
другие, не менее интересные, о которых мы скоро вам расскажем. Сегодня же все
внимание на флагманский продукт —
Dojo
1.2.
Если вы профессиональный веб-разработчик и постоянно имеете дело с
разработкой и отладкой сложных AJAX приложений, то наверняка знаете и
используете Firebug — плагин для браузера
Firefox, предназначенный для отладки и исследования веб-приложений. Текущая его
версия, 1.2х достаточно стабильная и функциональна, чтобы помочь в 99% проблем,
которые могут возникнуть при разработке. Но и этот инструмент не лишён если не
недостатков, то некоторых фич, которые могли бы облегчить работу. И даже
идеальный инструмент можно сделать ещё более идеальным, как бы это не звучало.