Как записать в буфер изображения (растровое изображение?) для более быстрого отображения GDI +?
Вопрос
Используя C ++ и .net, у меня есть поток данных, который я хочу отобразить в виде прокручиваемого изображения.Каждый раз, когда я получаю какие-то новые данные, я хочу добавить их в виде новой строки (128x1 пикселей) и прокрутить предыдущее содержимое в одну сторону.
Мой первый шаг к решению проблемы заключался в рендеринге всего набора данных каждый раз, когда я получал новую строку.Это сработало, но было слишком медленным, поэтому я думаю, что было бы разумнее записать данные в какой-нибудь буфер (может быть, в растровое изображение?).Проблема в том, что я не вижу, как я могу это сделать; Graphic
объекты позволяют вам рисовать довольно удачно, но я не вижу очевидного способа сообщить моему элементу управления использовать Bitmap
объект как это буфер?Точно так же я не вижу способа нарисовать растровое изображение, которое я мог бы затем вывести на экран.
Это должно быть возможно, но мой google-foo до сих пор меня подводил...
[Правка1] Просто чтобы уточнить, данные представляют собой спектрограмму.На следующем изображении показано, чего я пытался достичь:
Данные, которые я строю, представлены в виде массивов с плавающей точкой.Ничто не ограничивает количество, которое я получу, поэтому я просто хочу забыть данные, когда они выпадают за пределы графика.
В настоящее время я наследую от System::Windows::Forms::UserControl
, но не могли бы вы переключиться на что-то другое, если есть вариант получше?
Решение
Взгляните на Окно прокрутки метод win32.Вы можете прокрутить существующие данные на экране, а затем нарисовать только новые данные.Это очень быстро.
Другие советы
Растровое изображение bmpImage = новое растровое изображение (512,512);
for (int iRow = 0;иРоу < 512;iRow++)
{
for (int iCol = 0; iCol <512; iCol++)
{
Color clr;
bmpImage.SetPixel(iCol, iRow, clr);
}
}
(Изображение)bmpImage.save()
Возможная стратегия:
Нарисуйте задний буфер слева направо, который оборачивается при достижении конца.Выполняйте логику прокрутки только при выводе изображения на экран (с некоторой заданной частотой кадров).Для достижения этой цели используйте drawImage с исходным прямоугольником и целевым прямоугольником.
Используйте bitmap.LockBits(...) и bitmap.Метод UnlockBits(...) для изменения необработанных растровых данных.Будьте осторожны, чтобы заблокировать только прямоугольник, который вы собираетесь изменить, поскольку эти функции фактически копируют растровые данные из неуправляемой в управляемую память.Пример того, как это сделать, описан здесь Bitmap..::.Метод LockBits (Rectangle, ImageLockMode, PixelFormat).
Альтернативой LockBits является использование SetPixel на растровом изображении.Но SetPixel, как известно, работает медленно.
- При выводе изображений на экран убедитесь, что режим компоновки в экземпляре Graphics установлен в bmpg.Режим компоновки = CompositingMode.Исходная копия, и этот пиксельный формат заднего буфера равен PixelFormat.Формат32bppPArgb.
Я не совсем понимаю, что именно вы пытаетесь нарисовать (какой-то элемент управления в диалоговом окне?) но, как я предполагаю, это должно работать примерно так:
class Foo {
...
Gdiplus::Bitmap* m_pBitmap;
};
void Foo::DrawItem(LPDRAWITEMSTRUCT lpDraw) {
// update bitmap if needed
if(some_condition_requiring_bitmap_redraw) {
// do expensive drawing into bitmap
Gdiplus::Graphics graphics(m_pBitmap);
}
// create a graphics object to draw the control from the bitmap
Gdiplus::Graphics graphics(lpDraw->hDC);
graphics.DrawImage(m_pBitmap, ...);
}
В любом случае, это очень приблизительное предположение.Вызов DrawItem может выглядеть совершенно иначе, если вы используете .NET (я с ним не знаком ...), но базовая логика должна быть примерно такой же.
В зависимости от того, что именно представляют собой ваши данные, одновременное рисование строк в 1 пиксель может оказаться неэффективным.Возможно, вам было бы лучше нарисовать большую область и показывать только ее фрагменты по мере необходимости - хотя это, очевидно, будет зависеть от того, как поступают ваши данные.
Вам также может потребоваться выполнить какое-либо обновление вашего растрового изображения, чтобы "прокрутить" его содержимое.Я оставляю это на ваше усмотрение :-)
Попробуйте следующее:
- Запустите новое приложение VC ++ WinForms.
- Добавьте пользовательский элемент управления с именем 'Spectrogram' в проект
- Добавьте элемент управления таймером к пользовательскому элементу управления 'Spectrogram' и установите для свойства 'Enabled' значение true
- Добавьте следующие личные переменные в пользовательский элемент управления 'Spectrogram'
private:
Graphics ^m_gfxBuffer;
Graphics ^m_gfxOriginal;
Bitmap ^m_bmpBuffer;
Bitmap ^m_bmpOriginal;
- Добавьте следующий код в конструктор 'Spectrogram':
m_bmpBuffer = gcnew Bitmap(this->ClientSize.Width, this->ClientSize.Height);
m_gfxBuffer = Graphics::FromImage(m_bmpBuffer);
m_bmpOriginal = gcnew Bitmap(this->ClientSize.Width, this->ClientSize.Height);
m_gfxOriginal = Graphics::FromImage(m_bmpOriginal);
this->SetStyle(::ControlStyles::AllPaintingInWmPaint | ::ControlStyles::DoubleBuffer | ::ControlStyles::UserPaint | ::ControlStyles::OptimizedDoubleBuffer, true);
this->UpdateStyles();
- Добавьте следующий код к событию рисования 'Spectrogram':
array<unsigned char, 1> ^bytes = gcnew array<unsigned char, 1>(m_bmpBuffer->Height * 3);
Random ^r = gcnew Random();
r->NextBytes(bytes);
m_gfxOriginal->DrawImage(m_bmpBuffer, -1, 0);
int y = 0;
for (int i = 0; i < m_bmpOriginal->Height * 3; i += 3)
{
m_bmpOriginal->SetPixel(m_bmpOriginal->Width - 1, y++, ::Drawing::Color::FromArgb(255, bytes[i], bytes[i + 1], bytes[i + 2]));
}
m_gfxBuffer->DrawImage(m_bmpOriginal, 0, 0);
e->Graphics->DrawImage(m_bmpOriginal, 0, 0);
- Добавьте следующий код, чтобы таймеры 'Spectrogram' отмечали событие
this->Invalidate(false);
- Сохраните свой проект
- Очистите и перестройте
- Запустите проект
- Закройте текущую форму
- Пользовательский элемент управления Spectrogram теперь должен находиться в "Панели инструментов"
- Перетащите его из "Панели инструментов" в форму, и вы должны увидеть своего рода прокручивающуюся спектрограмму случайного цвета.
Это должно дать вам общее представление о элементе управления с буферизацией растрового изображения.Ключевым моментом здесь является вызов "setStyle" в конструкторе и смещение растрового изображения на -1 в событии paint.
Вам нужно будет правильно утилизировать графические и растровые объекты, а также обрабатывать их уничтожение и восстановление в событии изменения размера.
Надеюсь, это поможет.Дай мне знать, как все пройдет.