Direct 2D #9 О текстурах

Страницы:  1

Ответить
 

Professor Seleznov


Здравствуйте! После разбора WIC начинается работа с данными, для которой он как раз и пригодится.
Сразу скажу, что спрайты будут в следующей статье, так как понадобится немного залезть в Direct3D, рассказать про DXGI-цепочки и всё что будет важным для понимания этих вещей. Поэтому эта статья не очень длинная, но касается текстур - а текстуры, к слову, будут использоваться спрайтами.
Кстати, о разнице между спрайтами и текстурой:
Текстура - после того как вы декодировали файл с изображением и конвертировали его в нужный пиксельный формат, вы можете передать данные в видеопамять (такие данные в видеопамяти - это текстура).
Спрайт - ссылается на текстуру и содержит описание того, как её отрисовать, и т.п. То есть если нужно отрисовать текстуру по-разному, вы прибегаете к спрайтам.
Просто напоминаю, что пиксельные форматы описывают хранение одного пикселя. Вот весь их список в Direct2D.
Ну а теперь - процесс получения текстуры. Как я говорил ранее, необходимо декодировать файл с изображением и конвертировать его в один из пиксельных форматов. Так как это было в статье #8, здесь будет не полный код:
1. Получаем доступ к фабрике WIC
IWICImagingFactory* pWicFactory;
CoCreateInstance(CLSID_WICImagingFactory, ...);
2. Декодируем файл
IWICBitmapDecoder* pDecoder;
pWicFactory->CreateDecoderFromFilename(L"image.png", ..., &pDecoder);
3. Извлекаем кадр
IWICBitmapFrameDecode* pFrame;
pDecoder->GetFrame(0, &pFrame);
4. Конвертируем в понятный Direct2D формат
IWICFormatConverter* pConverter;
pWicFactory->CreateFormatConverter(&pConverter);
pConverter->Initialize(pFrame, GUID_WICPixelFormat32bppPBGRA, ...);
Теперь создание текстуры:
ID2D1Bitmap* pBitmap;
pRenderTarget->CreateBitmapFromWicBitmap(pConverter, nullptr, &pBitmap);
После этого текстура находится в видеопамяти. Теперь о том, что можно сделать с битмапом:
  • D2D1_SIZE_F GetSize() - возвращает размеры битмапа с учётом DPI.
  • D2D1_SIZE_U GetPixelSize() - возвращает размеры без учёта DPI
  • D2D1_PIXEL_FORMAT GetPixelFormat() - возвращает формат пикселей.
  • void GetDpi(FLOAT* dpiX, FLOAT* dpiY) - возвращает значения DPI, с которыми был создан битмап.
  • HRESULT CopyFromMemory(const D2D1_RECT_U* dstRect, const void* srcData, UINT32 pitch) - копирует пиксели из памяти в битмап. dstRect - область в битмапе, куда копируем (если nullptr, то во весь битмап). srcData - указатель на данные в системной памяти, pitch - ширина строки данных в байтах.
  • HRESULT CopyFromBitmap(const D2D1_POINT_2U* destPoint, ID2D1Bitmap* bitmap, const D2D1_RECT_U* srcRect) - копирует пиксели из другого битмапа в текущий. Аргументы аналогичны CopyFromMemory.
  • HRESULT CopyFromRenderTarget(const D2D1_POINT_2U* destPoint, ID2D1RenderTarget* renderTarget, const D2D1_RECT_U* srcRect) - копирует пиксели с рендер-таргета. Аргументы аналогичны CopyFromBitmap.
  • HRESULT CreateBitmapBrush(ID2D1Bitmap* bitmap, const D2D1_BITMAP_BRUSH_PROPERTIES* bitmapBrushProperties, const D2D1_BRUSH_PROPERTIES* brushProperties, ID2D1BitmapBrush** bitmapBrush) - создаёт кисть из текстуры. bitmapBrushProperties - свойства кисти (можно передать nullptr для значений по умолчанию), brushProperties - общие свойства (непрозрачность, матрица трансформации и т.п.).
Отрисовку разберём, когда дойдём до этого пункта. К слову, начиная с Windows 8 существует также интерфейс ID2D1Bitmap1, в котором есть пара дополнительных функций:
  • HRESULT Map(D2D1_MAP_OPTIONS options, D2D1_MAPPED_RECT* mappedRect) - предоставляет прямой доступ к данным битмапа в видеопамяти. Пока битмап замаплен, его нельзя использовать для рисования. options - опции: D2D1_MAP_OPTIONS_READ (только чтение), D2D1_MAP_OPTIONS_WRITE (только запись), а также их комбинация.
  • HRESULT Unmap() - завершает доступ, начатый Map.
Теперь о отрисовке:
  • void DrawBitmap( ID2D1Bitmap* bitmap, const D2D1_RECT_F& destinationRectangle, FLOAT opacity, D2D1_BITMAP_INTERPOLATION_MODE interpolationMode, const D2D1_RECT_F* sourceRectangle = nullptr ) - упрощённая версия на ID2D1RenderTarget:
    • bitmap - рисуемый битмап
    • destinationRectangle - координаты где рисовать
    • opacity - непрозрачность от 0.0 (полностью прозрачно) до 1.0 (непрозрачно)
    • interpolationMode - режим интерполяции: LINEAR (сглаживание) или NEAREST_NEIGHBOR (без сглаживания)
    • sourceRectangle - область исходного битмапа (в пикселях) для рисования. nullptr - рисуется целиком .
  • void DrawBitmap( ID2D1Bitmap* bitmap, const D2D1_RECT_F* destinationRectangle, FLOAT opacity, D2D1_INTERPOLATION_MODE interpolationMode, const D2D1_RECT_F* sourceRectangle, const D2D1_MATRIX_4X4_F* perspectiveTransform ) - расширенная версия на ID2D1DeviceContext:
    • bitmap - битмап
    • destinationRectangle - координаты
    • opacity - непрозрачность
    • interpolationMode - D2D1_INTERPOLATION_MODE (аналог предыдущего, но включает дополнительные опции, например, ANISOTROPIC)
    • sourceRectangle - как раньше
    • perspectiveTransform - матрица 4×4 для перспективного преобразования. Позволяет рисовать битмап с эффектом наклона или трёхмерного поворота
Собственно, код, который использует все эти методы (с комментариями):
Информация о изображении: image.png (RGBA, 200×200). Содержимое:
pic
При запуске кода появится сообщение с информацией о файле:
pic
После нажатия "OK" будет такое содержимое окна:
pic

Код

#define WIN32_LEAN_AND_MEAN
#include
#include
#include // ID2D1Bitmap1
#include
#include
#include
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "windowscodecs.lib")
#pragma comment(lib, "ole32.lib")
ID2D1Factory* g_pD2DFactory = nullptr;
ID2D1HwndRenderTarget* g_pRT = nullptr;
ID2D1Bitmap* g_pBitmap = nullptr;
ID2D1Bitmap* g_pCopyBitmap = nullptr;
ID2D1BitmapBrush* g_pBrush = nullptr;
IWICImagingFactory* g_pWICFactory = nullptr;
HWND g_hwnd;
// Загрузка битмапа через WIC
HRESULT LoadBitmapFromFile(ID2D1RenderTarget* pRT, const wchar_t* filename, ID2D1Bitmap** ppBitmap)
{
IWICBitmapDecoder* pDecoder = nullptr;
HRESULT hr = g_pWICFactory->CreateDecoderFromFilename(
filename, nullptr, GENERIC_READ,
WICDecodeMetadataCacheOnLoad, &pDecoder);
if (FAILED(hr)) return hr;
IWICBitmapFrameDecode* pFrame = nullptr;
hr = pDecoder->GetFrame(0, &pFrame);
pDecoder->Release();
if (FAILED(hr)) return hr;
IWICFormatConverter* pConverter = nullptr;
hr = g_pWICFactory->CreateFormatConverter(&pConverter);
if (SUCCEEDED(hr))
{
hr = pConverter->Initialize(
pFrame,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr, 0.f,
WICBitmapPaletteTypeMedianCut);
}
pFrame->Release();
if (FAILED(hr)) return hr;
hr = pRT->CreateBitmapFromWicBitmap(pConverter, nullptr, ppBitmap);
pConverter->Release();
return hr;
}
// Инициализация Direct2D и демонстрация методов
HRESULT InitD2D(HWND hWnd)
{
HRESULT hr;
// 1. Фабрика Direct2D
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &g_pD2DFactory);
if (FAILED(hr)) {
MessageBox(hWnd, L"Failed to create D2D factory", L"Error", MB_ICONERROR);
return hr;
}
// 2. WIC-фабрика
hr = CoCreateInstance(
CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&g_pWICFactory));
if (FAILED(hr)) {
MessageBox(hWnd, L"Failed to create WIC factory", L"Error", MB_ICONERROR);
return hr;
}
// 3. Создание HWND render target
RECT rc;
GetClientRect(hWnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
hr = g_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hWnd, size),
&g_pRT);
if (FAILED(hr)) {
MessageBox(hWnd, L"Failed to create render target", L"Error", MB_ICONERROR);
return hr;
}
// 4. Загрузка текстуры
hr = LoadBitmapFromFile(g_pRT, L"image.png", &g_pBitmap);
if (FAILED(hr)) {
wchar_t buf[256];
wsprintf(buf, L"Failed to load image.png\nHRESULT: 0x%08X", hr);
MessageBox(hWnd, buf, L"Error", MB_ICONERROR);
return hr;
}
// ------------------- GetSize / GetPixelSize / GetPixelFormat / GetDpi ------------------
D2D1_SIZE_F dipSize = g_pBitmap->GetSize();
D2D1_SIZE_U pxSize = g_pBitmap->GetPixelSize();
D2D1_PIXEL_FORMAT fmt = g_pBitmap->GetPixelFormat();
FLOAT dpiX, dpiY;
g_pBitmap->GetDpi(&dpiX, &dpiY);
char msg[256];
sprintf_s(msg, "Size (DIP): %.1f x %.1f\nPixel size: %u x %u\nFormat: %u, Alpha: %u\nDPI: %.0f x %.0f",
dipSize.width, dipSize.height, pxSize.width, pxSize.height, fmt.format, fmt.alphaMode, dpiX, dpiY);
MessageBoxA(hWnd, msg, "Bitmap Info", MB_OK);
// ------------------- CopyFromMemory ------------------
UINT w = pxSize.width, h = pxSize.height;
// Полупрозрачный красный квадрат 100x100
BYTE* redData = new BYTE[w * h * 4];
for (UINT y = 0; y < h; ++y)
for (UINT x = 0; x < w; ++x)
{
UINT idx = (y * w + x) * 4;
redData[idx + 0] = 0; // B
redData[idx + 1] = 0; // G
redData[idx + 2] = 255; // R
redData[idx + 3] = 128; // A
}
D2D1_RECT_U dstRect = D2D1::RectU(0, 0, 100, 100);
g_pBitmap->CopyFromMemory(&dstRect, redData, w * 4);
delete[] redData;
// ------------------- CopyFromBitmap ------------------
D2D1_BITMAP_PROPERTIES bmpProps = D2D1::BitmapProperties(g_pBitmap->GetPixelFormat());
g_pRT->CreateBitmap(pxSize, bmpProps, &g_pCopyBitmap);
g_pCopyBitmap->CopyFromBitmap(nullptr, g_pBitmap, nullptr);
// ------------------- CopyFromRenderTarget ------------------
ID2D1BitmapRenderTarget* pBmpRT = nullptr;
g_pRT->CreateCompatibleRenderTarget(D2D1::SizeF((FLOAT)w, (FLOAT)h), &pBmpRT);
pBmpRT->BeginDraw();
pBmpRT->Clear(D2D1::ColorF(D2D1::ColorF::LimeGreen));
pBmpRT->EndDraw();
D2D1_POINT_2U destPoint = { 0, 100 };
D2D1_RECT_U srcRect = D2D1::RectU(0, 0, 100, 100);
g_pBitmap->CopyFromRenderTarget(&destPoint, pBmpRT, &srcRect);
pBmpRT->Release();
// ------------------- Map / Unmap (ID2D1Bitmap1) ------------------
ID2D1Bitmap1* pBitmap1 = nullptr;
hr = g_pBitmap->QueryInterface(IID_PPV_ARGS(&pBitmap1));
if (SUCCEEDED(hr))
{
D2D1_MAPPED_RECT mapped;
hr = pBitmap1->Map(D2D1_MAP_OPTIONS_WRITE, &mapped);
if (SUCCEEDED(hr))
{
// Заливаем верхний правый квадрат (100,0)-(200,100)
for (UINT y = 0; y < 100; ++y)
{
BYTE* pixel = mapped.bits + y * mapped.pitch + 100 * 4;
for (UINT x = 100; x < 200; ++x)
{
pixel[0] = 255; // B
pixel[1] = 0; // G
pixel[2] = 0; // R
pixel[3] = 255; // A
pixel += 4;
}
}
pBitmap1->Unmap();
}
pBitmap1->Release();
}
// ------------------- CreateBitmapBrush ------------------
D2D1_BITMAP_BRUSH_PROPERTIES brushProps = D2D1::BitmapBrushProperties(
D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP,
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
g_pRT->CreateBitmapBrush(g_pCopyBitmap, brushProps, &g_pBrush);
D2D1_MATRIX_3X2_F translate = D2D1::Matrix3x2F::Translation(50.0f, 50.0f);
g_pBrush->SetTransform(translate);
return S_OK;
}
void Render()
{
g_pRT->BeginDraw();
g_pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
// Модифицированный битмап (с тремя цветными квадратами)
g_pRT->DrawBitmap(g_pBitmap, D2D1::RectF(10, 10, 210, 210));
// Копия
g_pRT->DrawBitmap(g_pCopyBitmap, D2D1::RectF(220, 10, 420, 210));
// Эллипс, заполненный текстурной кистью
D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(350.0f, 350.0f), 150.0f, 100.0f);
g_pRT->FillEllipse(ellipse, g_pBrush);
g_pRT->EndDraw();
}
void Cleanup()
{
if (g_pBrush) g_pBrush->Release();
if (g_pCopyBitmap) g_pCopyBitmap->Release();
if (g_pBitmap) g_pBitmap->Release();
if (g_pRT) g_pRT->Release();
if (g_pWICFactory) g_pWICFactory->Release();
if (g_pD2DFactory) g_pD2DFactory->Release();
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_PAINT:
Render();
ValidateRect(hWnd, nullptr);
return 0;
case WM_DESTROY:
Cleanup();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
{
// Инициализация COM для WIC
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"BitmapDemo";
RegisterClass(&wc);
g_hwnd = CreateWindow(
wc.lpszClassName, L"ID2D1Bitmap Methods Demo",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 600, 500,
nullptr, nullptr, hInstance, nullptr);
HRESULT hr = InitD2D(g_hwnd);
if (FAILED(hr))
{
wchar_t buf[128];
wsprintf(buf, L"Initialization failed! HRESULT: 0x%08X", hr);
MessageBox(g_hwnd, buf, L"Fatal Error", MB_ICONERROR);
CoUninitialize();
return 1;
}
ShowWindow(g_hwnd, nCmdShow);
UpdateWindow(g_hwnd);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
CoUninitialize();
return (int)msg.wParam;
}
На этом всё Да, статья получилась небольшой, но дальше будут спрайты — там будет о чём рассказать. К слову, когда требуется обработать много объектов и наложить на них текстуры, спрайты оказываются в разы производительнее.
На этом всё Да, статья получилась небольшой, но дальше будут спрайты - там будет о чём рассказать. К слову, когда требуется обработать много объектов и наложить на них текстуры, спрайты оказываются в разы производительнее.
При желании материально подержать перевод и структурирование информации — средства можете отправить через сбор в ЮМани .-Источник
 
Loading...
Error