Вопрос

Я использую графику GDI+ для рисования изображения размером 4000*3000 на экране, но это очень медленно.Это занимает около 300 мс.Я бы хотел, чтобы это занимало менее 10 мс.

Bitmap *bitmap = Bitmap::FromFile("XXXX",...);

//-------------------------------------------- // эта часть занимает около 300 мс, ужасно!

int width = bitmap->GetWidth();
int height = bitmap->GetHeight();
DrawImage(bitmap,0,0,width,height);

//------------------------------------------

Я не могу использовать CachedBitmap, так как хочу отредактировать растровое изображение позже.

Как я могу его улучшить?Или что-то не так?

Эта встроенная функция GDI также выводит изображение на экран, и это занимает всего 1 мс:

SetStretchBltMode(hDC, COLORONCOLOR);   
StretchDIBits(hDC, rcDest.left, rcDest.top, 
        rcDest.right-rcDest.left, rcDest.bottom-rcDest.top, 
        0, 0, width, height,
        BYTE* dib, dibinfo, DIB_RGB_COLORS, SRCCOPY);

//--------------------------------------------------------------

Если я хочу использовать StretchDIBits, мне нужно передать BITMAPINFO. Но как я могу получить BITMAPINFO из растрового объекта Gdi+?Я провел эксперимент с помощью библиотеки FreeImage, я вызываю StretchDIBits, используя объект FreeImageplus, он рисуется очень быстро.Но теперь мне нужно нарисовать растровое изображение и написать некоторый алгоритм для битового массива растрового изображения. Как я могу получить BITMAPINFO, если у меня есть объект растрового изображения?Это очень раздражает -___________-|

Это было полезно?

Решение

У вас экран с разрешением 4000х3000?Ух ты!

Если нет, то следует рисовать только видимую часть изображения, это будет гораздо быстрее...

[РЕДАКТИРОВАНИЕ после первого комментария] Мое замечание действительно немного глупо, я полагаю, что DrawImage замаскирует/пропустит ненужные пиксели.

После вашего редактирования (показывающего StretchDIBits), я предполагаю, что возможный источник разницы в скорости может быть связан с тем, что РастянутьDIBits имеет аппаратное ускорение ("Если драйвер не поддерживает изображения файлов JPEG или PNG" - это подсказка...), в то время как DrawImage может быть (у меня нет доказательств!) закодирован на C, полагаясь на мощность процессора, а не на мощность графического процессора...

Если я правильно помню, изображения DIB работают быстро (несмотря на то, что они «независимы от устройства»).Видеть Высокоскоростная анимация Win32: "используйте CreateDIBSection для создания высокоскоростной анимации".Хорошо, это относится к DIB vs.GDI в старой версии Windows (1996 года!), но я думаю, что это все еще верно.

[РЕДАКТИРОВАТЬ] Может быть Растровое изображение::GetHBITMAP функция может помочь вам использовать StretchDIBits (не проверено...).

Другие советы

Если вы используете GDI+, класс TextureBrush — это то, что вам нужно для быстрого рендеринга изображений.Я написал на нем пару 2D-игр, получив около 30 FPS или около того.

Я никогда не писал код .NET на C++, поэтому вот пример на C#:

Bitmap bmp = new Bitmap(...)
TextureBrush myBrush = new TextureBrush(bmp)

private void Paint(object sender, PaintEventArgs e):
{
    //Don't draw the bitmap directly. 
    //Only draw TextureBrush inside the Paint event.
    e.Graphics.FillRectangle(myBrush, ...)
}

Просто мысль;вместо того, чтобы получать ширину и высоту изображения перед рисованием, почему бы не кэшировать эти значения при загрузке изображения?

Изучите влияние явной установки режима интерполяции NearestNeighbor (где в вашем примере кажется, что интерполяция на самом деле не нужна!Но 300 мс — это своего рода затраты на качественную интерполяцию, когда интерполяция не нужна, так что попробовать стоит)

Еще одна вещь, которую стоит изучить, — это изменение глубины цвета растрового изображения.

К сожалению, когда у меня возникла подобная проблема, я обнаружил, что GDI+, как известно, намного медленнее, чем GDI, и, как правило, не имеет аппаратного ускорения, но теперь Microsoft перешла на WPF, и они не вернутся, чтобы улучшить GDI+!

Все производители видеокарт перешли на производительность 3D и, похоже, не заинтересованы в 2D-ускорении, и нет четкого источника информации о том, какие функции поддерживаются или могут быть аппаратно ускорены или нет.Очень неприятно, потому что, написав приложение на .NET с использованием GDI+, я не рад перейти на совершенно другую технологию, чтобы ускорить его до разумного уровня.

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

DrawImage(bitmap,0,0);

Как я уже сказал, я сомневаюсь, что это будет иметь какое-либо значение, потому что я конечно что Нарисовать изображение проверяет Ширина и Высота растрового изображения, и если изменение размера не требуется, просто вызывает эту перегрузку.(я надеюсь, что это не помешает пройти через все 12 миллионов пикселей, не выполняя никакой реальной работы).

Обновлять:Мои рассуждения ошибочны.С тех пор я узнал, но комментарий ребят напомнил мне мой старый ответ:ты хотеть указать размер назначения;даже если он соответствует размеру источника:

DrawImage(bitmap, 0, 0, bitmap.GetWidth, bitmap.GetHeight);

Причина в различиях в dpi между dpi bitmap и разрешение места назначения.GDI+ выполнит масштабирование, чтобы изображение получилось нужного «размера» (т. е.в дюймах)


С октября прошлого года я понял, что действительно хочется нарисовать "кэшированный" версию вашего растрового изображения.Eсть CachedBitmap класс в GDI+.Есть несколько хитростей по его использованию.Но в конце концов у меня есть функциональный фрагмент кода (Delphi), который это делает.

Предостережение заключается в том, что CachedBitmap может стать недействительным, то есть его нельзя использовать для рисования.Это происходит, если пользователь меняет разрешение или глубину цвета (например,Удаленного рабочего стола).В этом случае DrawImage потерпит неудачу, и вам придется заново создать CachedBitmap:

class procedure TGDIPlusHelper.DrawCachedBitmap(image: TGPImage;
      var cachedBitmap: TGPCachedBitmap;
      Graphics: TGPGraphics; x, y: Integer; width, height: Integer);
var
   b: TGPBitmap;
begin
   if (image = nil) then
   begin
      //i've chosen to not throw exceptions during paint code - it gets very nasty
      Exit;
   end;

   if (graphics = nil) then
   begin
      //i've chosen to not throw exceptions during paint code - it gets very nasty
      Exit;
   end;

   //Check if we have to invalidate the cached image because of size mismatch
   //i.e. if the user has "zoomed" the UI
   if (CachedBitmap <> nil) then
   begin
      if (CachedBitmap.BitmapWidth <> width) or (CachedBitmap.BitmapHeight <> height) then
         FreeAndNil(CachedBitmap); //nil'ing it will force it to be re-created down below
   end;

   //Check if we need to create the "cached" version of the bitmap
   if CachedBitmap = nil then
   begin
      b := TGDIPlusHelper.ResizeImage(image, width, height);
      try
         CachedBitmap := TGPCachedBitmap.Create(b, graphics);
      finally
         b.Free;
      end;
end;

    if (graphics.DrawCachedBitmap(cachedBitmap, x, y) <> Ok) then
begin
       //The calls to DrawCachedBitmap failed
       //The API is telling us we have to recreate the cached bitmap
       FreeAndNil(cachedBitmap);
       b := TGDIPlusHelper.ResizeImage(image, width, height);
       try
          CachedBitmap := TGPCachedBitmap.Create(b, graphics);
       finally
          b.Free;
       end;

       graphics.DrawCachedBitmap(cachedBitmap, x, y);
    end;
 end;

А cachedBitmap передается по ссылке.Первый звонок в DrawCachedBitmap это кэшированный версия будет создана.Затем вы передаете его в последующих вызовах, например:

Image imgPrintInvoice = new Image.FromFile("printer.png");
CachedBitmap imgPrintInvoiceCached = null;

...

int glyphSize = 16 * (GetCurrentDpi() / 96);

DrawCachedBitmap(imgPrintInvoice , ref imgPrintInvoiceCached , graphics, 
      0, 0, glyphSize, glyphSize);

я использую процедуру для рисования глифов на кнопках с учетом текущего DPI.То же самое могло бы быть использовано командой Internet Explorer для рисования изображений, когда пользователь работает с высоким разрешением (т. е. рисование увеличенных изображений очень медленно, поскольку они используют GDI+).

Попробуйте использовать копию растрового изображения из файла.Функция FromFile для некоторых файлов возвращает «медленное» изображение, но его копия будет рисоваться быстрее.

Bitmap *bitmap = Bitmap::FromFile("XXXX",...);

Bitmap *bitmap2 = new Bitmap(bitmap); // make copy

DrawImage(bitmap2,0,0,width,height);

/* Сначала извините за MA English, и код отчасти в польском, но его просто понять.У меня была такая же проблема, и я нашел лучшее решение.Вот.

Не используйте:Графика графика(hdc);графики.DrawImage(gpBitmap, 0, 0);Это медленно.

Использовать:GetHBITMAP(Gdiplus::Color(), &g_hBitmap) для HBITMAP и нарисуйте с помощью моей функции ShowBitmapStretch().

Вы можете изменить его размер, и это намного быстрее!Артур Чекальски / Польша

*/

//--------Global-----------
Bitmap *g_pGDIBitmap; //for loading picture
int gRozXOkna, gRozYOkna; //size of working window
int gRozXObrazu, gRozYObrazu; //Size of picture X,Y
HBITMAP g_hBitmap = NULL; //for displaying on window
//------------------------------------------------------------------------------
int ShowBitmapStretch(HDC hdc, HBITMAP hBmp, int RozX, int RozY, int RozXSkal, int RozYSkal, int PozX, int PozY) 
{
    if (hBmp == NULL) return -1;
    HDC hdc_mem = CreateCompatibleDC(hdc); //utworzenie kontekstu pamięciowego
    if (NULL == hdc_mem) return -2;
    //Trzeba połączyć BMP z hdc_mem, tzn. umieścić bitmapę w naszym kontekście pamięciowym
    if (DeleteObject(SelectObject(hdc_mem, hBmp)) == NULL) return -3; 

    SetStretchBltMode(hdc, COLORONCOLOR); //important! for smoothness
    if (StretchBlt(hdc, PozX, PozY, RozXSkal, RozYSkal, hdc_mem, 0, 0, RozX, RozY, SRCCOPY) == 0) return -4;

    if (DeleteDC(hdc_mem) == 0) return -5;
    return 0; //OK
}
//---------------------------------------------------------------------------
void ClearBitmaps(void)
{
    if (g_hBitmap) { DeleteObject(g_hBitmap);  g_hBitmap = NULL; }
    if (g_pGDIBitmap) { delete g_pGDIBitmap;  g_pGDIBitmap = NULL; }
}
//---------------------------------------------------------------------------
void MyOpenFile(HWND hWnd, szFileName)
{
    ClearBitmaps(); //Important!
    g_pGDIBitmap = new Bitmap(szFileName); //load a picture from file

    if (g_pGDIBitmap == 0) return;
    //---Checking if picture was loaded
    gRozXObrazu = g_pGDIBitmap->GetWidth();
    gRozYObrazu = g_pGDIBitmap->GetHeight();
    if (gRozXObrazu == 0 || gRozYObrazu == 0) return;

    //---Uworzenie bitmapy do wyświatlaia; DO IT ONCE HERE!
    g_pGDIBitmap->GetHBITMAP(Gdiplus::Color(), &g_hBitmap); //creates a GDI bitmap from this Bitmap object
    if (g_hBitmap == 0) return;

    //---We need to force the window to redraw itself
    InvalidateRect(hWnd, NULL, TRUE);
    UpdateWindow(hWnd);
}
//---------------------------------------------------------------------------
void MyOnPaint(HDC hdc, HWND hWnd) //in case WM_PAINT; DO IT MANY TIMES
{
    if (g_hBitmap)
    {
        double SkalaX = 1.0, SkalaY = 1.0; //scale
        if (gRozXObrazu > gRozXOkna || gRozYObrazu > gRozYOkna || //too big picture, więc zmniejsz; 
           (gRozXObrazu < gRozXOkna && gRozYObrazu < gRozYOkna)) //too small picture, można powiększyć 
        {
            SkalaX = (double)gRozXOkna / (double)gRozXObrazu; //np. 0.7 dla zmniejszania; FOR DECREASE
            SkalaY = (double)gRozYOkna / (double)gRozYObrazu; //np. 1.7 dla powiększania; FOR INCREASE
            if (SkalaY < SkalaX) SkalaX = SkalaY; //ZAWSZE wybierz większe skalowanie, czyli mniejszą wartość i utaw w SkalaX
        }

    if (ShowBitmapStretch(hdc, g_hBitmap, gRozXObrazu, gRozYObrazu, (int)(gRozXObrazu*SkalaX), (int)(gRozYObrazu*SkalaX), 0, 0, msg) < 0) return;

Я провел небольшое исследование и не смог найти способ рендерить изображения с помощью GDI/GDI+ быстрее, чем

Graphics.DrawImage/DrawImageUnscaled

и в то же время просто нравится.

Пока я не обнаружил

ImageList.Draw(GFX,Point,Index)

и да, это действительно так быстро и просто.

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