Как записать в буфер изображения (растровое изображение?) для более быстрого отображения GDI +?

StackOverflow https://stackoverflow.com/questions/1055920

Вопрос

Используя C ++ и .net, у меня есть поток данных, который я хочу отобразить в виде прокручиваемого изображения.Каждый раз, когда я получаю какие-то новые данные, я хочу добавить их в виде новой строки (128x1 пикселей) и прокрутить предыдущее содержимое в одну сторону.

Мой первый шаг к решению проблемы заключался в рендеринге всего набора данных каждый раз, когда я получал новую строку.Это сработало, но было слишком медленным, поэтому я думаю, что было бы разумнее записать данные в какой-нибудь буфер (может быть, в растровое изображение?).Проблема в том, что я не вижу, как я могу это сделать; Graphic объекты позволяют вам рисовать довольно удачно, но я не вижу очевидного способа сообщить моему элементу управления использовать Bitmap объект как это буфер?Точно так же я не вижу способа нарисовать растровое изображение, которое я мог бы затем вывести на экран.

Это должно быть возможно, но мой google-foo до сих пор меня подводил...

[Правка1] Просто чтобы уточнить, данные представляют собой спектрограмму.На следующем изображении показано, чего я пытался достичь:

альтернативный текст http://www.geekops.co.uk/photos/0000-00-02%20(Forum%20images)/ScrollingGraphicsAlgorithmExplanation.png

Данные, которые я строю, представлены в виде массивов с плавающей точкой.Ничто не ограничивает количество, которое я получу, поэтому я просто хочу забыть данные, когда они выпадают за пределы графика.

В настоящее время я наследую от 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.

Вам нужно будет правильно утилизировать графические и растровые объекты, а также обрабатывать их уничтожение и восстановление в событии изменения размера.

Надеюсь, это поможет.Дай мне знать, как все пройдет.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top