Как записать в (Bitmap?) Буфер изображения для более быстрого отображения GDI +? - PullRequest
1 голос
/ 29 июня 2009

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

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

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

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

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

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

В настоящее время я наследую от System::Windows::Forms::UserControl, но мог бы переключиться на что-то еще, если есть лучшая альтернатива?

Ответы [ 5 ]

5 голосов
/ 29 июня 2009

Взгляните на ScrollWindow win32 метод. Вы можете прокрутить существующие данные на экране, а затем нарисовать только новые данные. Это очень быстро.

1 голос
/ 07 августа 2009

Возможная стратегия:

  • Нарисуйте защитный буфер слева направо, который оборачивается при достижении конца. Выполняйте логику прокрутки только при рисовании на экране (с определенной частотой кадров). Для этого используйте DrawImage с исходным и конечным прямоугольниками.

  • Используйте методы bitmap.LockBits (...) и bitmap.UnlockBits (...) для изменения необработанных растровых данных. Соблюдайте осторожность, чтобы заблокировать только прямоугольник, который вы собираетесь изменить, поскольку эти функции фактически делают копии растровых данных из неуправляемой в управляемую память. Пример того, как это сделать, описан здесь Метод Bitmap .. ::. LockBits (Rectangle, ImageLockMode, PixelFormat) .

  • Альтернативой LockBits является использование SetPixel на растровом изображении. Но SetPixel, как известно, работает медленно.

  • При перетаскивании изображений на экран убедитесь, что для параметра CompositingMode on Graphics установлено значение bmpg.CompositingMode = CompositingMode.SourceCopy, а формат пикселя заднего буфера равен PixelFormat.Format32bppPArgb.
1 голос
/ 28 июля 2009

Растровое изображение bmpImage = новое растровое изображение (512,512);

для (int iRow = 0; iRow <512; iRow ++) </p>

{

  for (int iCol = 0; iCol <512; iCol++)
                        {
                            Color clr;
                            bmpImage.SetPixel(iCol, iRow, clr);
                        }

}

(изображение) bmpImage.save ()

0 голосов
/ 29 июня 2009

Попробуйте следующее:

  • Запустите новое приложение VC ++ WinForms.
  • Добавить пользовательский элемент управления «Спектрограмма» в проект
  • Добавить элемент управления таймером в пользовательский элемент управления «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();
  • Добавьте следующий код в событие рисования «Спектрограмма»:

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);    
  • Добавьте следующий код для события отметки времени таймеров «Спектрограммы»

this->Invalidate(false);
  • Сохранить ваш проект
  • Очистить и восстановить
  • Запустить проект
  • Закрыть работающую форму
  • Пользовательский элемент управления Spectrogram теперь должен быть в 'Toolbox'
  • Перетащите его из «Панели инструментов» в форму, и вы увидите прокручивающуюся спектрограмму случайного цвета.

Это должно дать вам общее представление о буферизованном элементе управления растровым изображением. Ключевым моментом здесь является вызов "SetStyle" в конструкторе и смещение растрового изображения на -1 в событии рисования.

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

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

0 голосов
/ 29 июня 2009

Мне не совсем понятно, что именно вы пытаетесь нарисовать (какой-то элемент управления в диалоге?), Но, по-моему, это должно работать примерно так:

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 пикселю за раз. Возможно, вам лучше нарисовать большую область и показывать ее только по мере необходимости - хотя это, очевидно, будет зависеть от того, как поступят ваши данные.

Вам также может понадобиться обновить свое растровое изображение, чтобы «прокрутить» его содержимое. Я оставлю это на ваше усмотрение: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...