Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_5782.html
 

Загрузка изображений в Direct2D

Опубликовано 26.12.2012 г. 15:27

Статья-заметка, о том, как корректно загрузить изображение для использования с Direct2D.

В общем-то сложного ничего в этом нет. Исходные данные - информация об изображении в виде потока (всех потокамх TStream), т.е. файловые потоки, потоки из памяти, поток загруженный из поля БД и т.д. На выходе мы хотим получить экземпляр ID2D1Bitmap для его последующей отрисовке на поверхности ID2D1RenderTarget. Собственно для тестового примера загрузим от отрисуем прозрачное PNG изображение с центрированием по середине формы.

На старте программы создаим экземляр канвы TDirect2DCanvas, а также загрузим изображенеи из файла в поток данных:

procedure TMainForm.FormCreate(Sender: TObject);
var fs : TFileStream;
begin
    FCanvas := TDirect2DCanvas.Create(handle);
    FRt := FCanvas.RenderTarget as ID2D1HwndRenderTarget;
    FRt.SetDpi(96,96);

    fs := TFileStream.Create('D:\folder-open.png', fmOpenRead);
    try
        FBitmap := LoadD2DBitmap(fs);
    finally
        fs.Free();
    end;
end;

 

 Как видно из кода, в блоке try-finally происходит вызов метода LoadD2DBitmap(), который создает нужный d2d-растр. Создание изображение здесь происходит с помощью WIC. Первым делом мы создаем экзземпляр TWicImage и загружем данные из потока. Затем нам необходимо преобразовать формат изображения для его использования в d2d. Для этих целей, используя WIC-фабрику получаем экземляр IWicFormatConverter, после чего производим конвертацию. Теперь,  используя полученное изображение и RenderTarget создаем D2D-растр:

function TMainForm.LoadD2DBitmap(data : TStream):ID2D1Bitmap;
var wi : TWicImage;
    wicFactory : IWICImagingFactory;
    fc : IWICFormatConverter;
begin
    result := nil;
    wi := TWicImage.Create();
    try
        wi.LoadFromStream(data);

        wicfactory := wi.ImagingFactory;
        wicFactory.CreateFormatConverter(fc);

        fc.Initialize(wi.Handle, GUID_WICPixelFormat32bppPBGRA,
                      WICBitmapDitherTypeNone,
                      nil,
                      0,
                      WICBitmapPaletteTypeCustom);

        FRt.CreateBitmapFromWicBitmap(fc, nil, result);

        fc := nil;
        wicFactory := nil;
    finally
       wi.Free();
    end;
end;

Из подводных камней следует отметить, что здесь специально используется промежуточная переменная wicFactory, хотя, казалось бы, можно просто сделать вызов wi.ImageFactory.CreateFormatConverter(). Но в таком случае к моменту разрушения экземляра wi, остается одна дополнительная неявная ссылка на  фабрику, что ведет к тому, что фабрика остается жить и после завершения метода, в результате чего будет получена ошибка при закрытии программы.

Имея готовое изображение, отрисовать его на форме труда не составит. Добавим пару строк для центрирования изображения на форме. Используя идеология матриц преобразований, нам потребуется две матрицы переноса: первая - в центр формы, а вторая - сдвиг изображения относительно центра:

procedure TMainForm.FormPaint(Sender: TObject);
var size : TD2DSizeF;
    formCenter, imageCenter : TD2D1Matrix3x2F;
begin
    FRt.BeginDraw();
    try
        FRt.Clear(D2D1ColorF(clRed));

        FBitmap.GetSize(size);

        formCenter  := TD2DMatrix3x2F.Translation(clientWidth div 2, ClientHeight div 2);
        imageCenter := TD2DMatrix3x2F.Translation( -size.width / 2,  -size.height / 2);

        FRt.SetTransform(formCenter * imageCenter);

        if assigned(FBitmap) then
            FRt.DrawBitmap(FBitmap);
    finally
        FRt.EndDraw();
    end;
end;

Искомая позиция - произведение данных матриц

Метки:  Direct2D  |  WIC 

Комментарии

Vad
26.12.2012 в 20:54
Андрей, большое спасибо за статью - будем пробовать :)
teran
26.12.2012 в 22:44
да не за что (:
Сергей
23.01.2013 в 13:55
При изменении размеров формы, центр формы и картинки почему то начинают смещаться относительно друг друга.
teran
23.01.2013 в 14:17

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


var size : TD2DSizeU; 
begin 
    if assigned(FRt) then begin 
        size := D2D1SizeU(clientWidth, clientHeight); 
        FRt.Resize(size); 
        invalidate();
    end;
end; 

а так картинка начинает не только смещаться, но и растягиваться. Эти действия можно также выполнить с помощью перекрытия метода Resize формы (который только и вызывает OnResize), либо путем обработки сообщения WM_SIZE


 

Сергей
23.01.2013 в 15:54

Ага, спасибо. Я тоже уже разобрался :) + чтобы масштабировало изображение, в FRt.DrawBitmap(FImages[ FXCurSlide ], pClientRect ) добавил pClientRect с размерами клиентской области.

teran
23.01.2013 в 16:53

масштабирование тоже можно делать через матрицы преобразований. Для картинок может не особо актуально, конечно:

const DEFAULT_WIDTH = 600;
      DEFAULT_HEIGHT = 350; 
var scaleMatrix : TD2D1Matrix3x2F; 
    scaleX, scaleY : single; 
    scalePoint : TD2DPoint2f; 
begin 
    scaleX := ClientWidth / DEFAULT_WIDTH; 
    scaleY := ClientHeight / DEFAULT_HEIGHT; 
    scalePoint := D2D1PointF(ClientWidth div 2, clientHeight div 2); 
    scaleMatrix := TD2DMatrix3x2F.Scale(scaleX, scaleY, scalePoint); 

    FRt.SetTransform(formCenter * imageCenter * scaleMatrix);

в данном случае DEFAULT_ это изначальные размеры формы. Пропорции изображения, конечно же, не сохраняются.

suskuk
28.09.2013 в 16:32
Андрей, общий вопрос: а что с большинством скринов и файлов примеров по данной теме? Если читать всю ветку Direct2D много ссылок в никуда. Вроде как сайт не заброшен, новые статьи появляются....
teran
30.09.2013 в 19:07
хороший вопрос (:
вобще косяк связан с переездом на адреса блога, и собственно движок сменился. постараюсь исправить
When someone writes an piece of writing he/she retains the idea of a user in his/her brain that how a user
can understand it. So that's why this article is perfect. Thanks!
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно