быстродействие TImage после SaveToFile
Никто не замечал уменьшение быстродействия 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 как-то влияет на исходную картинку?
Тогда попробуй создать временный TImage и вызвать CopyRect, после чего сохранение можно выполнить с этого Image.
CopyRect выполняет API-шное копирование графики на уровне контекста графического устройства (HDC), так что может быть усе будя путем :)
Создаю TBitmap и делаю Canvas.CopyRect.
Единственный недостаток - скорость. Особенно если картинка размером 700х4000 или больше.
Вообще, задача такая:
Нужно в TGPImage (класс в GDI+, если кто не сталкивался с ним) загнать картинку из обычного TImage. Предоставленные конструкторы TGPImage позволяют это сделать двумя способами: либо из файла, либо из потока. Поэтому выход - сохранить TImage в поток, а потом создать TGPImage из этого потока (что я и делал). Возникшая проблема с мерцанием при скролле исчезла, благодаря DrCoder. Но появился один большой минус - создание временного Bitmap + сохранение в поток - очень медленно. Чтобы объяснить суть проблемы, привожу алгоритм:
1. Кликаем на TImage - создается TGPImage описанным выше методом.
2. Двигаем мышью - отрисовуется графика на TImage.
Так вот если мы кликаем (на достаточно большой картинке) и дергаем мышью,TGPImage создается медленно и графика затем рисуется с тормозами.
Если же мы кликаем, потом ждем, а потом дергаем мышью - графика рисуется плавно и красиво.
Т.е. видимо процесс создания Bitmap'а+сохранение в поток не успевает завершится до отрисовки графики. Когда же мы делаем паузу после клика - все нормально.
Тогда есть два выхода:
Либо как-то ускорить создание временного Bitmap'а, Либо узнавать когда процесс пункта 1) завершится, а потом отрисовывать графику. Вопрос - как? Или может причина в другом?
P.S. Целая Война и Мир получилась... Очень прошу вникнуть, а то без посторонней помощи мне не справиться
Дерзай!
а насчет тормозов как-то странно, что обработка мыши выполняется до того, как выполнится полная прорисовка окна(обычно такие вещи бывают только в многопотоковом приложении) - попробуй перед вызовом конструктора TGPImage(где у тебя идет закачка картинки) выполнить у формы Enabled := False, а после - Enabled := True. Тогда объект скроллирования временно не будет принимать сообщения ввода с клавы и мыши.
Дык это... -а че в этом классе нету канвы или на худой конец HDC? -зачем создавать временные структуры данных, когда можно управляться напрямую (гораздо быстрее)
Канвы там нету - да и не должно быть (HDC ессно имеется). Это ведь не VCL - а портированная с С++ библиотека GDI+ (поищи по форуму - я хедеры выкладывал).
Кроме того, эти классы несколько неудобы для использоания в Делфи (ориентировано на модель ООП в С++), хотя гоРРРаздо лучше использования API - чтобы сильно изменить содежримое класса (источник данных, размер картинки), надо заново создавать объект - там у всех классов куча конструкторов с тьмой параметров.
Дык это... -а че в этом классе нету канвы или на худой конец HDC? -зачем создавать временные структуры данных, когда можно управляться напрямую (гораздо быстрее)
В том то и дело, что нету! Конечно напрямую быстрее - но моргания при скролле. Кстати,видимо нашел причину почему после SaveToStream Image работает медленней. Судя по исходникам SaveToStream преобразовывает сохраняемую картинку в аппаратно-независимый Bitmap (DIB), который порядком медленней, чем DDB.
Вот именно! Как будто "закачка картинки" идет отдельным процессом. На счет Enabled:=true/false - уже пробовал, собственно у меня было почти то же самое, только с флагом.
Привожу примерный код:
...
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, и тем не менее, если кликаем и дергаем - чаще всего тормоза(на средней картинке - через раз, на большой - каждый раз), а если кликаем, ждем и дергаем - то все ОК, хоть на какой картинке. Бред?! Что мне делать :???:
Канвы там нету - да и не должно быть (HDC ессно имеется). Это ведь не VCL - а портированная с С++ библиотека GDI+ (поищи по форуму - я хедеры выкладывал).
GDI+ - это библиотека с WinXP; HDC, Handle или че-то наподобие в TGPImage нету, искал где можно. Если не трудно, напиши как к нему достучаться, я бы как-нибудь с ним выкрутил. (може у меня Делфевые заголовки кривые, хотя в MSDN вроде все так же).
В связи с этим вопрос: а може в TGPImage можно другим способом загнать картинку из TImage, без потоков?
GDI+ - это библиотека с WinXP; HDC, Handle или че-то наподобие в TGPImage нету, искал где можно. Если не трудно, напиши как к нему достучаться, я бы как-нибудь с ним выкрутил. (може у меня Делфевые заголовки кривые, хотя в MSDN вроде все так же).
Вот так можно построить контекст воспроизведения на текущем кадра имейджа (я не тестировал, но помоему это так)
GP: TGPGraphics;
DC: HDC;
GP:=TGPGraphics.Create(Image);
DC:=GP.GetHDC;
...
GP.ReleaseHDC(DC);
В связи с этим вопрос: а може в TGPImage можно другим способом загнать картинку из TImage, без потоков?
Нет по-моему.
Я говорил - используй для отрисовки TPaintBox - он быстрее. А для сохранения/открытия файлов - обычные для GDI+ методы.
Насчёт мнимой многопоточности - в VCL естьвстроенный механизм для обработки системных сообщений при длительных операциях с битмапами. Он, похоже, автоматически вызывает Application.ProcessMessages. У битмапов есть такое событие - OnProgress. Специально для этого.
Теперь TGPImage создается напрямую:
frmMain.imgObjects.Picture.Bitmap.SaveToStream(SourceStream);
DestStream := TStreamAdapter.Create(SourceStream);
gpBackImage := TGPImage.Create(DestStream);
Гораздо быстрей, чем было, но все равно занимает какое-то время, т.е. остается проблема с "мнимой многопоточностью" на больших картинках.
Да я думал об этом, его неудобство - надо в OnPaint'е все прорисовывать. Может это и хорошо, но у меня алгоритм построен так, что гораздо удобней TImage, потому что там этого делать не надо. Менять щас его на TPaintBox - значит менять весь проект. Хотя... я сравню их скорости, и если PaintBox в моем приложении будет на порядок быстрее - видимо придется менять.
Насчёт мнимой многопоточности - в VCL естьвстроенный механизм для обработки системных сообщений при длительных операциях с битмапами. Он, похоже, автоматически вызывает Application.ProcessMessages. У битмапов есть такое событие - OnProgress. Специально для этого.
Теперь-то я уже Bitmap не создаю. Одни стримы. С imgMain.Bitmap - я толкько считываю (на всякий пожарный попробовал imgMainOnProgress - не заходит туда вообще и при включенном IncrementalDisplay). Не ужели нет никакого механизма, чтоб узнать когда вышеописанные процедуры завершатся? В потоках ли причина?
Теперь-то я уже Bitmap не создаю. Одни стримы. С imgMain.Bitmap - я толкько считываю (на всякий пожарный попробовал imgMainOnProgress - не заходит туда вообще и при включенном IncrementalDisplay). Не ужели нет никакого механизма, чтоб узнать когда вышеописанные процедуры завершатся? В потоках ли причина?
Ты используешь GDI+, а эта библиотека создаёт в твоём приложении дополнительный поток, в котором и проиходят все процедуры отрисовки т.п. Правда я не знаю способов организовать с ним синхронизацию.
Если у тебя большой битмапик, а судя по всему это так, то операции со стримами могут занять большой промежуток времени, а если они ещё и частые....
Мне так, кажется, лучше сразу рисовать изображение в TGPImage - это будет быстрее, чем потом конвертить их тудым-сюдым.
P.S. Кстати про стрим адаптер. Что ж ты сразу не сказал, что твой битмапик генерится в программе а потом пересылается в GDI+ классы? Я б тебе стразу рассказал про механизм стриминга в GDI+...
Если у тебя большой битмапик, а судя по всему это так, то операции со стримами могут занять большой промежуток времени
Так и есть, плюс еще большой размер памяти :((
Попробовал так (все отрисовки в глобальном TGPImage и вывод именно его в TImage - тогда не надо париться ни с потоками, ни с чем-то еще, GPImage всегда под рукой, готовый). Но - программа занимает много памяти на протяжении всей работы.
Короче говоря, если так - одно плохо, если эдак - другое. Видимо надо менять алгоритм. Ладно, чтоб не морочить людям мозги, создам-ка я новую ветвь в форуме, а то это уже немного другая тема, и изложу там свою задачу... И наверное выложу кусок проекта, чтоб было понятней.
Всем спасибо за предложенные идеи.
Название новой ветви: "Реализация выделения полупрозрачным прямоугольником"
Буду благодарен за помощь