|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Direct2D, TDirectCanvas и все-все-все
Дня доброго.
Я тут новенький. С Дельфи профессионально не работаю, моя стезя - 1С 8.2. В 1с есть проблема - картинки, при выводе которых на экран/принтер через графическую систему 1С - идет отжор памяти без ее освобождения. Немного об GDI от 1С: окна Windows ей нужны только для масштабирования окон (но не элементов) а также для отлова событий от ОС (WM_CLOSE, и.т.д.). Вся отрисовка - своя, основана на перехвате WM_PAINT Запилил addins через отдельную dll, в которой формочка (окно), отдельное от графической системы 1С (1С не перехватывает его WM_PAINT). На формочке - TImage. Все нормально, супергуд. 10 метровые фотки летают... Но хочется большего. По умолчанию - GDI не поддерживает TIFF (а это важно, часто сканы в 1С - это сканы сопроводиловки - Ч/Б печать). Впилил GDI+. Но даже по ощущениям увидел, что медленней стало. Замерил - примерно в 5 раз медленнее таже конвертация из bmp в jpeg идет. Бида. Узнал про поддержку Direct2D в Win7, начал экспериментировать. Увидел, что она действительно работает - например масштабирование рисунка через TDirect2DCanvas.RenderTarget выполняется раза в 2.5 быстрее, чем через GDI (реализацию приведу чуть позже) и при это загружен GPU видюхи на 17-20 процентов. Профит. Но вот как потом получить трансформированное изображение с TDirect2DCanvas - не могу осилить. |
#2
|
|||
|
|||
Вернемся к старым песням.
С конвертацией изображений разобрался, все быстро и четко. Особенно понравился новый формат HD-Photo (JPEG-XR) на основе вейвлет сжатия. На малом качестве он творит чудеса, вытягивая картинку. Но мы движемся дальше. Теперь моя цель - создать свой компонент на основе TImage который будет отрисовывать Direct2D графику. Вот первые наброски: Код:
unit Direct2DImages; interface uses Windows,SysUtils,Classes,messages,Controls,Forms,ExtCtrls,Direct2d,WinCodec,d2d1,Graphics,ActiveX; type TDirect2DImage= class(TImage) private fOldWidth:integer; fOldHeight:integer; fDirectBitmap:IWICFormatConverter; procedure SetDirectImage(ImageData:TMemoryStream); procedure DirectPaint(); protected procedure Paint;override; procedure WMResize(var MSG:TWMSize);message WM_SIZE; public constructor Create(AOwner:TComponent);override; Property DirectImage:TMemoryStream write SetDirectImage; published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples',[TDirect2DImage]); end; function _GetErrorMsg(ecode:DWORD):string; var Buf: array [0..1024] of char; begin SetString(result,Buf,FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,nil,ecode,0,@Buf,sizeOf(Buf),nil)); result:=StringReplace(result,#13#10,'',[rfReplaceAll]); end; { TDirect2DImage } constructor TDirect2DImage.Create(AOwner:TComponent); begin inherited Create(AOwner); fOldWidth:=0; fOldHeight:=0; end; procedure TDirect2DImage.DirectPaint; var Status:Hresult; ImagingFactory:IWICImagingFactory; DirectCanvas:TDirect2DCanvas; DirectBitmap:ID2D1Bitmap; RenderTarget:ID2D1RenderTarget; BitmapSize,RenderSize:TD2DSizeF; Scale:TD2D1Matrix3x2F; D2DSizeF:D2D1_SIZE_F; D2DPointF:D2D_POINT_2F; ImageRect,OwnerRect:TRect; ScaleX,ScaleY:real; begin if fDirectBitmap=nil then exit; try ImageRect:=(self as TImage).ClientRect; OwnerRect:=(self.Owner as TForm).ClientRect; ImageRect.Left:=ImageRect.Left+(self as TImage).Left; ImageRect.Right:=ImageRect.Right+(self as TImage).Left; ImageRect.Top:=ImageRect.Top+(self as TImage).Top; ImageRect.Bottom:=ImageRect.Bottom+(self as TImage).Top; DirectCanvas:=TDirect2DCanvas.Create((self.Owner as TForm).Canvas.Handle,ImageRect); // Связываем Direct 2D canvas с canvas формы RenderTarget:=DirectCanvas.RenderTarget as ID2D1RenderTarget; Status:=RenderTarget.CreateBitmapFromWicBitmap(fDirectBitmap,nil,DirectBitmap); // Создаем bitmap, годный для операций с Direct2D, типа ID2D1Bitmap. Это уже голый Direct2D, до этого был WIC. еще понятный. см. http://msdn.microsoft.com/en-us/library/windows/desktop/dd742779%28v=vs.85%29.aspx if not Succeeded(Status) then raise Exception.Create('Не удалось создать контекст изображения D2D про причине:"'+_GetErrorMsg(ResultCode(Status))+'"'); RenderTarget.BeginDraw(); // Начинаем рисовать RenderTarget.Clear(D2D1ColorF(clRed)); // Очищаем красным, чтобы было видно DirectBitmap.GetSize(BitmapSize); // Получаем размер изображения ZeroMemory(@Scale,16); RenderTarget.GetSize(RenderSize); // получаем размер холста ScaleX:=RenderSize.Width/BitmapSize.Width; ScaleY:=RenderSize.height/BitmapSize.height; D2DSizeF.Width:=ScaleX; D2DSizeF.height:=ScaleY; // Увеличиваем рисунок до холста, потом будет с сохранением пропорций D2DPointF.x:=0; D2DPointF.y:=0; Scale:=TD2DMatrix3x2F.Scale(D2DSizeF,D2DPointF); // Создаем матрицу пространственного преобразования RenderTarget.SetTransform(Scale); // Преобразовываем Direct2D холст, по аналогии с SetWorldTransform() для HDC // Мне дико непонятно, какой режим масштабирования - тупо "ближайший сосед", "Билинейная" или годная "Бикубическая". Есть мысли? if assigned(DirectBitmap) then RenderTarget.DrawBitmap(DirectBitmap); // Рисуем картинку. Все норм, на форме она есть. Ура. RenderTarget.EndDraw(); // Фиксируем finally RenderTarget:=nil; DirectCanvas.Free; ImagingFactory:=nil; end; end; procedure TDirect2DImage.Paint; // Что то изменилось, размеры, лиюо форма перекрыта другой. begin // if ((Self.Owner as TForm).Width<>fOldWidth) or ((Self.Owner as TForm).Height<>fOldHeight) then if ((self as TImage).Width<>fOldWidth)or((self as TImage).height<>fOldHeight) then begin fOldWidth:=(self as TImage).Width; fOldHeight:=(self as TImage).height; DirectPaint(); end; end; // Загружаем изобрадение из потока в Конвертер, выполняется 1 раз, при этом происходит масштабирование до размера экрана procedure TDirect2DImage.SetDirectImage(ImageData:TMemoryStream); function Max(const A,B:Extended):Extended; begin if A>B then result:=A else result:=B; end; var ImagingFactory:IWICImagingFactory; WicImage:TWicImage; BitmapScaler:IWICBitmapScaler; HDC:THandle; ScreenWidth:integer; ScreenHeight:integer; CoefWidth:real; CoefHeight:real; TotalCoef:real; Status:Hresult; begin if ImageData=nil then exit; // Нуавдруг HDC:=GetDC((self.Owner as TForm).Handle); ScreenWidth:=GetDeviceCaps(HDC,HORZRES); ScreenHeight:=GetDeviceCaps(HDC,VERTRES); WicImage:=TWicImage.Create(); // Обертка Дельфи над IWicBitmap. Воспользуемся. ImagingFactory:=WicImage.ImagingFactory; ImageData.Seek(0,0); // На начало, мало ли. WicImage.LoadFromStream(ImageData); // Загрузим из потока CoefWidth:=WicImage.Width/ScreenWidth; CoefHeight:=WicImage.height/ScreenHeight; TotalCoef:=Max(CoefWidth,CoefHeight); TotalCoef:=Max(1,TotalCoef); // Нам надо отмасштабировать под размер экрана, сохранив пропорции Status:=ImagingFactory.CreateBitmapScaler(BitmapScaler); // Создадим масштабировщик if not Succeeded(Status) then raise Exception.Create(''); // Инициируем масштабирование, бикубичесое. Status:=BitmapScaler.Initialize(WicImage.Handle,Round(WicImage.Width/TotalCoef),Round(WicImage.height/TotalCoef),WICBitmapInterpolationModeCubic); if not Succeeded(Status) then raise Exception.Create(''); Status:=ImagingFactory.CreateFormatConverter(fDirectBitmap); // Создаем конвертер из фабрики if not Succeeded(Status) then raise Exception.Create(''); // Render не принимает на отрисовку BitmapScaler, а BitmapConverter - Норм, дадим ему его. Status:=fDirectBitmap.Initialize(BitmapScaler,GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,nil,0,WICBitmapPaletteTypeCustom); // инициализируем конвертер потоком с изображением, при этом он и сделает то, что требуется. BitmapScaler:=nil; ImagingFactory:=nil; WicImage.Free; end; procedure TDirect2DImage.WMResize(var MSG:TWMSize); begin end; end. |
#3
|
|||
|
|||
Чего пока нет:
1) Годной обработки ошибок. 2) Поддержки старого GDI (2-х режимности, ну это нетрудно). Есть много вопросов: 1) Как перехватить ресайз TImage. Именно ресайс. При отлове Paint - этих сообщений дико много, по каждому чиху (движение формы, например) - и отрисовка кадрируется. Даже после предварительного масштабирования до размера экрана. Смотрел как сделано в типовой Дельфи - вроде все также, однако не тормозит. Ниасилил. 2) Изображение можно масштабировать 2 методами - через WIC (Интерфейс IWICBitmapScaler) и непосредственно при rendere через SetWorldTransform(). В первом случае можно задать режим масштабирования (бикубический, билинейный, ближайший), однако я не уверен, что там (в WIC) используется Direct2D (обработка через GPU). Во втором случае точно используется GPU (мерил через MSI Afterburner), однако не понятно, как указать режим масштабирования. Есть мысли? |
#4
|
|||
|
|||
Что то не густо.
|
#5
|
|||
|
|||
Цитата:
Не густо чего? Вопрос то где? Кстати, у меня один проект без всяких Direct2D, GDI, GDI+ делает отрисовку, в том числе с масштабированием, без всяких тормозов. Картинка JPEG 8mp, все летает на не самых мощных компах. Все дело в волшебных пузырьках. Т.е. в буфферировании. Картинки читается, конвертится в битмап а памяти (да, не очень экономично по памяти, но тут компромис ресурсы/время) и далее еще через один буфер выводится на канву (второй буфер идет под отрисовку по размерам). |