Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

быстродействие TImage после SaveToFile

12K
01 февраля 2006 года
phoenix_87
20 / / 01.02.2006
Доброе время суток!
Никто не замечал уменьшение быстродействия TImage после выполнения метода SaveToFile()?
Проблема заключается в следующем:
есть ScrollBox, на нем TImage: imgMain. В Image отрисовую различные картинки\примитивы (все графические операции - с помощью GDI+). Все быстро, Image в ScrollBox'е прокручивается плавно. Стоит только сохранить содержимое Imag'a методом imgMain.Picture.SaveToFile(), сразу увеличивается размер программы в памяти, и после перерисовки Image он моргает при прокрутке ScrollBox'а . Неприятно :(.
Пробовал ...Picture.Bitmap.SaveToFile(), ...Picture.Grpahic.SaveToFile(), а также SaveToStream - разницы никакой (собственно SaveToFile и вызывает SaveToStream).
Стало интересно, для пробы, перед сохранением в поток\файл, создаю левый временный TPicture (также пробовал TBitmap, и даже TImage на форме), присваиваю свойству Picture созданного объекта imgMain.Picture. И сохраняю этот временный объект:
temp.SaveToFile().
Результат - тот же самый, т.е. после перерисовки imgMain снова моргает при прокрутке.
Вопрос: Почему? Неужели SaveToStream как-то влияет на исходную картинку?
7.8K
02 февраля 2006 года
DrCoder
106 / / 27.01.2006
Раз уж ты используешь GDI, наверное ты рисуешь на HDC (или языком VCL - Canvas.Handle)...
Тогда попробуй создать временный TImage и вызвать CopyRect, после чего сохранение можно выполнить с этого Image.
CopyRect выполняет API-шное копирование графики на уровне контекста графического устройства (HDC), так что может быть усе будя путем :)
12K
02 февраля 2006 года
phoenix_87
20 / / 01.02.2006
Спасыба, заработало!
Создаю TBitmap и делаю Canvas.CopyRect.
Единственный недостаток - скорость. Особенно если картинка размером 700х4000 или больше.
Вообще, задача такая:
Нужно в TGPImage (класс в GDI+, если кто не сталкивался с ним) загнать картинку из обычного TImage. Предоставленные конструкторы TGPImage позволяют это сделать двумя способами: либо из файла, либо из потока. Поэтому выход - сохранить TImage в поток, а потом создать TGPImage из этого потока (что я и делал). Возникшая проблема с мерцанием при скролле исчезла, благодаря DrCoder. Но появился один большой минус - создание временного Bitmap + сохранение в поток - очень медленно. Чтобы объяснить суть проблемы, привожу алгоритм:
1. Кликаем на TImage - создается TGPImage описанным выше методом.
2. Двигаем мышью - отрисовуется графика на TImage.
Так вот если мы кликаем (на достаточно большой картинке) и дергаем мышью,TGPImage создается медленно и графика затем рисуется с тормозами.
Если же мы кликаем, потом ждем, а потом дергаем мышью - графика рисуется плавно и красиво.
Т.е. видимо процесс создания Bitmap'а+сохранение в поток не успевает завершится до отрисовки графики. Когда же мы делаем паузу после клика - все нормально.

Тогда есть два выхода:
Либо как-то ускорить создание временного Bitmap'а, Либо узнавать когда процесс пункта 1) завершится, а потом отрисовывать графику. Вопрос - как? Или может причина в другом?

P.S. Целая Война и Мир получилась... Очень прошу вникнуть, а то без посторонней помощи мне не справиться
5
03 февраля 2006 года
hardcase
4.5K / / 09.08.2005
Используй вместо TImage такой контрол, как TPaintBox - все операции прорисовки ты должен будешь производить самомтоятельно, а скорость будет максимальна. Опять-же у TPaintBox есь свойстово Canvas которое гораздо быстрее аналогичного в TImage.
Дерзай!
7.8K
03 февраля 2006 года
DrCoder
106 / / 27.01.2006
Дык это... -а че в этом классе нету канвы или на худой конец HDC? -зачем создавать временные структуры данных, когда можно управляться напрямую (гораздо быстрее)

а насчет тормозов как-то странно, что обработка мыши выполняется до того, как выполнится полная прорисовка окна(обычно такие вещи бывают только в многопотоковом приложении) - попробуй перед вызовом конструктора TGPImage(где у тебя идет закачка картинки) выполнить у формы Enabled := False, а после - Enabled := True. Тогда объект скроллирования временно не будет принимать сообщения ввода с клавы и мыши.
5
03 февраля 2006 года
hardcase
4.5K / / 09.08.2005
Цитата:
Originally posted by DrCoder
Дык это... -а че в этом классе нету канвы или на худой конец HDC? -зачем создавать временные структуры данных, когда можно управляться напрямую (гораздо быстрее)


Канвы там нету - да и не должно быть (HDC ессно имеется). Это ведь не VCL - а портированная с С++ библиотека GDI+ (поищи по форуму - я хедеры выкладывал).
Кроме того, эти классы несколько неудобы для использоания в Делфи (ориентировано на модель ООП в С++), хотя гоРРРаздо лучше использования API - чтобы сильно изменить содежримое класса (источник данных, размер картинки), надо заново создавать объект - там у всех классов куча конструкторов с тьмой параметров.

12K
03 февраля 2006 года
phoenix_87
20 / / 01.02.2006
Цитата:
Originally posted by DrCoder
Дык это... -а че в этом классе нету канвы или на худой конец HDC? -зачем создавать временные структуры данных, когда можно управляться напрямую (гораздо быстрее)



В том то и дело, что нету! Конечно напрямую быстрее - но моргания при скролле. Кстати,видимо нашел причину почему после SaveToStream Image работает медленней. Судя по исходникам SaveToStream преобразовывает сохраняемую картинку в аппаратно-независимый Bitmap (DIB), который порядком медленней, чем DDB.

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


Вот именно! Как будто "закачка картинки" идет отдельным процессом. На счет Enabled:=true/false - уже пробовал, собственно у меня было почти то же самое, только с флагом.
Привожу примерный код:

Код:
[FONT=courier new]
...
var
  SourceStream: TMemoryStream;
  DestStream: IStream;
  FImageCreated: Boolean;
...
procedure TfrmMain.imgObjectsMouseDown();
var
  Temp: TBitmap;
begin
  FImageCreated := False;
  //создаем поток, куда будем записывать TImage
  SourceStream := TMemoryStream.Create;

  {если использовать напрямую imgObjects.Picture.SaveToStream,
то он нагадит в исходный имаж imgObjects. Дабы исходный имаж
оставался нетронутым и скроллился плавно,создаем временный}

  Temp := TBitmap.Create;
  with Temp do
  begin
    Width := imgObjects.Width;
    Height := imgObjects.Height;
    Canvas.CopyRect(Rect, imgObjects.Canvas, Rect);
    //и сохраняем его в поток
    SaveToStream(SourceStream);
  end;
  Temp.Free;
  //создаем поток типа IStream для совместимости
  //с конструктором TGPImage
  DestStream := TStreamAdapter.Create(SourceStream);
  GPImage := TGPImage.Create(DestStream);
  FImageCreated := True;
end;

procedure TfrmMain.imgObjectsMouseMove();
begin
  if (левый кнопарь нажат) then  
    if FImageCreated then  
    begin
      //если создание GPImage завершено
      ...
      //тут отрисовуется графика на imgObjects (TImage),
 а также вносятся изменения в GPImage (TGPImage)
      ...
    end;
end;

procedure TfrmMain.imgObjectsMouseUp();
begin
  //освобождаю ресурсы
  ...
  GPImage.Free;
  SourceStream.Free;
end;
[/FONT]


Т.е. никаких отрисовок не выполняется, пока FImageCreated = False, и тем не менее, если кликаем и дергаем - чаще всего тормоза(на средней картинке - через раз, на большой - каждый раз), а если кликаем, ждем и дергаем - то все ОК, хоть на какой картинке. Бред?! Что мне делать :???:

Цитата:
Originally posted by DrCoder
Канвы там нету - да и не должно быть (HDC ессно имеется). Это ведь не VCL - а портированная с С++ библиотека GDI+ (поищи по форуму - я хедеры выкладывал).


GDI+ - это библиотека с WinXP; HDC, Handle или че-то наподобие в TGPImage нету, искал где можно. Если не трудно, напиши как к нему достучаться, я бы как-нибудь с ним выкрутил. (може у меня Делфевые заголовки кривые, хотя в MSDN вроде все так же).
В связи с этим вопрос: а може в TGPImage можно другим способом загнать картинку из TImage, без потоков?

5
04 февраля 2006 года
hardcase
4.5K / / 09.08.2005
Цитата:
Originally posted by phoenix_87
GDI+ - это библиотека с WinXP; HDC, Handle или че-то наподобие в TGPImage нету, искал где можно. Если не трудно, напиши как к нему достучаться, я бы как-нибудь с ним выкрутил. (може у меня Делфевые заголовки кривые, хотя в MSDN вроде все так же).


Вот так можно построить контекст воспроизведения на текущем кадра имейджа (я не тестировал, но помоему это так)

 
Код:
var Image: TGPImage;
    GP: TGPGraphics;
    DC: HDC;
GP:=TGPGraphics.Create(Image);
DC:=GP.GetHDC;
...
GP.ReleaseHDC(DC);

Цитата:
Originally posted by phoenix_87
В связи с этим вопрос: а може в TGPImage можно другим способом загнать картинку из TImage, без потоков?

Нет по-моему.

Я говорил - используй для отрисовки TPaintBox - он быстрее. А для сохранения/открытия файлов - обычные для GDI+ методы.

Насчёт мнимой многопоточности - в VCL естьвстроенный механизм для обработки системных сообщений при длительных операциях с битмапами. Он, похоже, автоматически вызывает Application.ProcessMessages. У битмапов есть такое событие - OnProgress. Специально для этого.

12K
04 февраля 2006 года
phoenix_87
20 / / 01.02.2006
От мерцаний при скролее после прямого сохранения избавился! - с Божьей помощью и помощью на delphikingdom.com (нужно после SaveToStream и изменения размеров Bitmap'а присваивать Bitmap.HandleType := bmDDB, дабы вернуть ему зависимость:). В общем длинная история, кому интересно - вопрос №39648 круглого стола).
Теперь TGPImage создается напрямую:
 
Код:
SourceStream := TMemoryStream.Create;
  frmMain.imgObjects.Picture.Bitmap.SaveToStream(SourceStream);
  DestStream := TStreamAdapter.Create(SourceStream);
  gpBackImage := TGPImage.Create(DestStream);

Гораздо быстрей, чем было, но все равно занимает какое-то время, т.е. остается проблема с "мнимой многопоточностью" на больших картинках.
Цитата:
Originally posted by hardcase Я говорил - используй для отрисовки TPaintBox - он быстрее. А для сохранения/открытия файлов - обычные для GDI+ методы.


Да я думал об этом, его неудобство - надо в OnPaint'е все прорисовывать. Может это и хорошо, но у меня алгоритм построен так, что гораздо удобней TImage, потому что там этого делать не надо. Менять щас его на TPaintBox - значит менять весь проект. Хотя... я сравню их скорости, и если PaintBox в моем приложении будет на порядок быстрее - видимо придется менять.

Цитата:
Originally posted by hardcase
Насчёт мнимой многопоточности - в VCL естьвстроенный механизм для обработки системных сообщений при длительных операциях с битмапами. Он, похоже, автоматически вызывает Application.ProcessMessages. У битмапов есть такое событие - OnProgress. Специально для этого.


Теперь-то я уже Bitmap не создаю. Одни стримы. С imgMain.Bitmap - я толкько считываю (на всякий пожарный попробовал imgMainOnProgress - не заходит туда вообще и при включенном IncrementalDisplay). Не ужели нет никакого механизма, чтоб узнать когда вышеописанные процедуры завершатся? В потоках ли причина?

5
07 февраля 2006 года
hardcase
4.5K / / 09.08.2005
Цитата:
Originally posted by phoenix_87
Теперь-то я уже Bitmap не создаю. Одни стримы. С imgMain.Bitmap - я толкько считываю (на всякий пожарный попробовал imgMainOnProgress - не заходит туда вообще и при включенном IncrementalDisplay). Не ужели нет никакого механизма, чтоб узнать когда вышеописанные процедуры завершатся? В потоках ли причина?


Ты используешь GDI+, а эта библиотека создаёт в твоём приложении дополнительный поток, в котором и проиходят все процедуры отрисовки т.п. Правда я не знаю способов организовать с ним синхронизацию.

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

Мне так, кажется, лучше сразу рисовать изображение в TGPImage - это будет быстрее, чем потом конвертить их тудым-сюдым.

P.S. Кстати про стрим адаптер. Что ж ты сразу не сказал, что твой битмапик генерится в программе а потом пересылается в GDI+ классы? Я б тебе стразу рассказал про механизм стриминга в GDI+...

12K
08 февраля 2006 года
phoenix_87
20 / / 01.02.2006
Цитата:
Originally posted by hardcase
Если у тебя большой битмапик, а судя по всему это так, то операции со стримами могут занять большой промежуток времени


Так и есть, плюс еще большой размер памяти :((

Цитата:
Мне так, кажется, лучше сразу рисовать изображение в TGPImage - это будет быстрее, чем потом конвертить их тудым-сюдым.


Попробовал так (все отрисовки в глобальном TGPImage и вывод именно его в TImage - тогда не надо париться ни с потоками, ни с чем-то еще, GPImage всегда под рукой, готовый). Но - программа занимает много памяти на протяжении всей работы.
Короче говоря, если так - одно плохо, если эдак - другое. Видимо надо менять алгоритм. Ладно, чтоб не морочить людям мозги, создам-ка я новую ветвь в форуме, а то это уже немного другая тема, и изложу там свою задачу... И наверное выложу кусок проекта, чтоб было понятней.
Всем спасибо за предложенные идеи.

Название новой ветви: "Реализация выделения полупрозрачным прямоугольником"
Буду благодарен за помощь

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог