The MSDN function from
https://learn.microsoft.com/en-us/windows/win32/direct2d/save-direct2d-content-to-an-image-file
works fine
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Bitmap and text were output to DIALOG using Direct2D.
I tried to make this rendered screen into a bitmap and save it as a file.
But, I get D2DERR_WRONG_RESOURCE_DOMAIN.
Is there a way to transform ID2D1Bitmap to IWICBitmap?
My goal is to use GDI+
Bitmap->LockBits(&rect,ImageLockModeRead,PixelFormat32bppARGB,bitmapData);
BYTE *rawData = (BYTE *)bitmapData->Scan0;
The purpose is to obtain memory information of ID2D1Bitmap as shown in the code.
Here my code
// NS_DisplayDlg.h : 헤더 파일
//
#pragma once
#include <d2d1.h>
#include <d2d1_1.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <wrl/client.h>
#include <wincodec.h>
#include <dwrite.h>
#include <thread>
#include <atomic>
#include <vector>
#include <string>
#include <atlbase.h>
#include <gdiplus.h>
using namespace Gdiplus;
using Microsoft::WRL::ComPtr;
// CNS_DisplayDlg 대화 상자
class CNS_DisplayDlg : public CDialogEx
{
// 생성입니다.
public:
CNS_DisplayDlg(CWnd* pParent = NULL); // 표준 생성자입니다.
~CNS_DisplayDlg();
// 대화 상자 데이터입니다.
enum { IDD = IDD_NS_DISPLAY_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 지원입니다.
public:
bool m_bBoardUse;
ULONG_PTR m_gdiplusToken;
public:
std::thread m_animationThread;
std::atomic<bool> m_running;
int m_maxY;
int m_textPosY;
int m_textSpeed;
int m_imagePosY;
int m_videoFormat;
BYTE* m_rawData;
public:
ID2D1Factory* m_pDirect2dFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1Bitmap* m_pBitmap;
IWICImagingFactory* m_pWICFactory;
IDWriteFactory* m_pDWriteFactory;
IDWriteTextFormat* m_pTextFormat;
ID2D1SolidColorBrush* m_pTextBrush;
ID2D1RenderTarget* m_pRenderTarget2;
public:
HRESULT LoadBitmapFromFile(LPCWSTR uri, ID2D1Bitmap** ppBitmap);
void AnimationLoop(); // 애니메이션 루프 메서드
BOOL InitDirect();
void DrawingRender();
HRESULT SaveBitmapToFile(PCWSTR uri,ID2D1Bitmap* pBitmap);
IWICBitmap* m_pWicBitmap;
// 구현입니다.
protected:
HICON m_hIcon;
// 생성된 메시지 맵 함수
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnDestroy();
};
//CPP
// NS_DisplayDlg.cpp : 구현 파일
//
#include "stdafx.h"
#include "NS_Display.h"
#include "NS_DisplayDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
int cnt = 0;
// 응용 프로그램 정보에 사용되는 CAboutDlg 대화 상자입니다.
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 대화 상자 데이터입니다.
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 지원입니다.
// 구현입니다.
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CNS_DisplayDlg 대화 상자
CNS_DisplayDlg::CNS_DisplayDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CNS_DisplayDlg::IDD, pParent),
m_pDirect2dFactory(nullptr), m_pRenderTarget(nullptr), m_pBitmap(nullptr), m_pWICFactory(nullptr), m_pDWriteFactory(nullptr), m_pTextFormat(nullptr), m_pTextBrush(nullptr), m_rawData(nullptr),
m_textPosY(0), m_textSpeed(100), m_imagePosY(10) , m_videoFormat(5),
m_running(false),m_bBoardUse(false)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
}
CNS_DisplayDlg::~CNS_DisplayDlg()
{
Gdiplus::GdiplusShutdown(m_gdiplusToken);
m_running = false;
if (m_animationThread.joinable())
{
m_animationThread.join();
}
if(m_rawData)
{
delete m_rawData;
m_rawData = nullptr;
}
if(m_pDirect2dFactory) m_pDirect2dFactory->Release();
if(m_pRenderTarget) m_pRenderTarget->Release();
if(m_pBitmap) m_pBitmap->Release();
if(m_pWICFactory) m_pWICFactory->Release();
if(m_pDWriteFactory) m_pDWriteFactory->Release();
if(m_pTextFormat) m_pTextFormat-> Release();
if(m_pTextBrush) m_pTextBrush->Release();
}
void CNS_DisplayDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CNS_DisplayDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_DESTROY()
END_MESSAGE_MAP()
// CNS_DisplayDlg 메시지 처리기
BOOL CNS_DisplayDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 시스템 메뉴에 "정보..." 메뉴 항목을 추가합니다.
// IDM_ABOUTBOX는 시스템 명령 범위에 있어야 합니다.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 이 대화 상자의 아이콘을 설정합니다. 응용 프로그램의 주 창이 대화 상자가 아닐 경우에는
// 프레임워크가 이 작업을 자동으로 수행합니다.
SetIcon(m_hIcon, TRUE); // 큰 아이콘을 설정합니다.
SetIcon(m_hIcon, FALSE); // 작은 아이콘을 설정합니다.
// TODO: 여기에 추가 초기화 작업을 추가합니다.
SetWindowPos(nullptr, 0, 0, 1920, 1080, SWP_NOZORDER | SWP_NOMOVE);
InitDirect();
return TRUE; // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}
void CNS_DisplayDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 대화 상자에 최소화 단추를 추가할 경우 아이콘을 그리려면
// 아래 코드가 필요합니다. 문서/뷰 모델을 사용하는 MFC 응용 프로그램의 경우에는
// 프레임워크에서 이 작업을 자동으로 수행합니다.
void CNS_DisplayDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 클라이언트 사각형에서 아이콘을 가운데에 맞춥니다.
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 아이콘을 그립니다.
dc.DrawIcon(x, y, m_hIcon);
}
else
{
//CDialogEx::OnPaint();
DrawingRender();
//ProcessRawData();
//DrawingRenderAJABoard();
}
}
// 사용자가 최소화된 창을 끄는 동안에 커서가 표시되도록 시스템에서
// 이 함수를 호출합니다.
HCURSOR CNS_DisplayDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CNS_DisplayDlg::OnDestroy()
{
CDialogEx::OnDestroy();
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
}
BOOL CNS_DisplayDlg::InitDirect()
{
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
if (FAILED(hr))
{
MessageBox(L"Direct2D Factory 생성 실패", L"오류", MB_OK);
return FALSE;
}
hr = CoInitialize(nullptr);
if (FAILED(hr))
{
MessageBox(L"COM 라이브러리 초기화 실패", L"오류", MB_OK);
return FALSE;
}
hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pWICFactory));
if (FAILED(hr))
{
MessageBox(L"WIC Factory 생성 실패", L"오류", MB_OK);
return FALSE;
}
// 렌더 타겟 생성
RECT rc;
GetClientRect(&rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hWnd, size),
&m_pRenderTarget);
if (FAILED(hr))
{
MessageBox(L"Direct2D 렌더 타겟 생성 실패", L"오류", MB_OK);
return FALSE;
}
if (FAILED(hr))
{
MessageBox(L"Direct2D 렌더 타겟 생성 실패", L"오류", MB_OK);
return FALSE;
}
hr = LoadBitmapFromFile(L"..\\Debug\\test.png", &m_pBitmap);
if (FAILED(hr))
{
MessageBox(L"이미지 로드 실패", L"오류", MB_OK);
return FALSE;
}
// DirectWrite 팩토리 생성
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&m_pDWriteFactory));
if (FAILED(hr))
{
MessageBox(L"DirectWrite Factory 생성 실패", L"오류", MB_OK);
return FALSE;
}
// 텍스트 형식 생성
hr = m_pDWriteFactory->CreateTextFormat(
L"Arial", // 폰트 이름
nullptr,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
24.0f,
L"en-us",
&m_pTextFormat);
if (FAILED(hr))
{
MessageBox(L"텍스트 형식 생성 실패", L"오류", MB_OK);
return FALSE;
}
// 텍스트 브러시 생성
hr = m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &m_pTextBrush);
if (FAILED(hr))
{
MessageBox(L"텍스트 브러시 생성 실패", L"오류", MB_OK);
return FALSE;
}
// 화면의 가장 아래의 Y 값(m_maxY) 구하기
m_imagePosY = m_textPosY = m_maxY = rc.bottom - 10; // 여유 공간을 둬서 화면의 가장 아래에 위치하도록 설정
m_running = true;
m_animationThread = std::thread(&CNS_DisplayDlg::AnimationLoop, this);
return TRUE;
}
HRESULT CNS_DisplayDlg::LoadBitmapFromFile(LPCWSTR uri, ID2D1Bitmap** ppBitmap)
{
IWICBitmapDecoder* pDecoder = nullptr;
IWICBitmapFrameDecode* pFrame = nullptr;
IWICFormatConverter* pConverter = nullptr;
HRESULT hr = m_pWICFactory->CreateDecoderFromFilename(uri, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &pDecoder);
if (FAILED(hr))
{
MessageBox(L"WIC 디코더 생성 실패", L"오류", MB_OK);
return hr;
}
hr = pDecoder->GetFrame(0, &pFrame);
if (FAILED(hr))
{
MessageBox(L"WIC 프레임 디코딩 실패", L"오류", MB_OK);
pDecoder->Release();
return hr;
}
hr = m_pWICFactory->CreateFormatConverter(&pConverter);
if (FAILED(hr))
{
MessageBox(L"WIC 포맷 변환기 생성 실패", L"오류", MB_OK);
pFrame->Release();
pDecoder->Release();
return hr;
}
hr = pConverter->Initialize(
pFrame,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.f,
WICBitmapPaletteTypeMedianCut);
if (FAILED(hr))
{
MessageBox(L"WIC 포맷 변환기 초기화 실패", L"오류", MB_OK);
pConverter->Release();
pFrame->Release();
pDecoder->Release();
return hr;
}
// 이 부분에서 Direct2D 렌더 타겟이 유효한지 확인
if (!m_pRenderTarget)
{
MessageBox(L"Direct2D 렌더 타겟이 유효하지 않습니다.", L"오류", MB_OK);
pConverter->Release();
pFrame->Release();
pDecoder->Release();
return E_FAIL;
}
hr = m_pRenderTarget->CreateBitmapFromWicBitmap(pConverter, nullptr, ppBitmap);
if (FAILED(hr))
{
MessageBox(L"Direct2D 비트맵 생성 실패", L"오류", MB_OK);
}
pConverter->Release();
pFrame->Release();
pDecoder->Release();
return hr;
}
void CNS_DisplayDlg::AnimationLoop()
{
while (m_running)
{
// 텍스트 위치 업데이트
m_textPosY -= m_textSpeed / 100; // 100 픽셀을 1초에 이동 (100ms 마다 10씩 이동)
if (m_textPosY < -50) {
m_textPosY = m_maxY; // 화면 아래에서 위로 이동할 때 화면 아래로 이동
}
// 이미지 위치 업데이트 (텍스트와 같은 속도로 이동)
m_imagePosY -= m_textSpeed / 100;
if (m_imagePosY < -50) {
m_imagePosY = m_maxY; // 화면 아래에서 위로 이동할 때 화면 아래로 이동
}
// 화면 다시 그리기 요청
Invalidate(FALSE);
// 100ms 대기
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void CNS_DisplayDlg::DrawingRender()
{
HRESULT hr = S_OK;
if (m_pRenderTarget == nullptr)
{
RECT rc;
GetClientRect(&rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
// Create render target
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hWnd, size),
&m_pRenderTarget);
if (FAILED(hr))
{
MessageBox(L"Failed to create render target", L"Error", MB_OK);
return ;
}
}
m_pRenderTarget->BeginDraw();
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
if (m_pBitmap)
{
D2D1_SIZE_F imageSize = m_pBitmap->GetSize();
m_pRenderTarget->DrawBitmap(m_pBitmap, D2D1::RectF(10, m_imagePosY, 10 + imageSize.width, m_imagePosY + imageSize.height));
}
// Draw text
static const WCHAR sc_helloWorld[] = L"Hello, World!";
D2D1_RECT_F layoutRect = D2D1::RectF(20, static_cast<float>(m_textPosY), 300, static_cast<float>(m_textPosY + 50));
m_pRenderTarget->DrawText(sc_helloWorld, ARRAYSIZE(sc_helloWorld) - 1, m_pTextFormat, &layoutRect, m_pTextBrush);
hr = m_pRenderTarget->EndDraw();
if (FAILED(hr))
{
MessageBox(L"Rendering failed", L"Error", MB_OK);
}
//ID2D1Factory::CreateWicBitmapRenderTarget(pWicBitmap,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Create bitmap render target
ComPtr<ID2D1BitmapRenderTarget> pBitmapRenderTarget;
hr = m_pRenderTarget->CreateCompatibleRenderTarget(&pBitmapRenderTarget);
if (FAILED(hr))
{
MessageBox(L"Failed to create bitmap render target", L"Error", MB_OK);
return;
}
pBitmapRenderTarget->BeginDraw();
pBitmapRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
if (m_pBitmap)
{
D2D1_SIZE_F imageSize = m_pBitmap->GetSize();
pBitmapRenderTarget->DrawBitmap(m_pBitmap, D2D1::RectF(10, m_imagePosY, 10 + imageSize.width, m_imagePosY + imageSize.height));
}
// Draw text
static const WCHAR sc_helloWorld2[] = L"Hello, World!";
D2D1_RECT_F layoutRect2 = D2D1::RectF(20, static_cast<float>(m_textPosY), 300, static_cast<float>(m_textPosY + 50));
pBitmapRenderTarget->DrawText(sc_helloWorld2, ARRAYSIZE(sc_helloWorld2) - 1, m_pTextFormat, &layoutRect2, m_pTextBrush);
hr = pBitmapRenderTarget->EndDraw();
if (FAILED(hr))
{
MessageBox(L"Rendering failed", L"Error", MB_OK);
}
//ComPtr<ID2D1Bitmap> pBitmap;
ID2D1Bitmap* pBitmap;
hr = pBitmapRenderTarget->GetBitmap(&pBitmap);
D2D1_SIZE_U sizet = pBitmap->GetPixelSize();
if (FAILED(hr))
{
MessageBox(L"Failed to get bitmap", L"Error", MB_OK);
return;
}
CString str = L"";
//str.Format(L"D:\\1. workspace\\1. source\\12. vs2012\\NS_Display\\Debug\\TEST%d.png",cnt);
str.Format(L"..\\Debug\\TEST%d.png",cnt);
IWICBitmap* ppWICBitmap;
//SaveD2DBitmapToFile(pBitmap, &ppWICBitmap,str);
SaveBitmapToFile(str,pBitmap);
cnt++;
pBitmap->Release();
}
HRESULT CNS_DisplayDlg::SaveBitmapToFile(PCWSTR uri,ID2D1Bitmap* pBitmap)
{
HRESULT hr = S_OK;
//ID2D1Factory *pD2DFactory = NULL;
IWICBitmap *pWICBitmap = NULL;
ID2D1RenderTarget *pRT = NULL;
IWICBitmapEncoder *pEncoder = NULL;
IWICBitmapFrameEncode *pFrameEncode = NULL;
IWICStream *pStream = NULL;
//
// Create IWICBitmap and RT
//
UINT sc_bitmapWidth = pBitmap->GetSize().width;
UINT sc_bitmapHeight = pBitmap->GetSize().height;
if (SUCCEEDED(hr))
{
hr = m_pWICFactory->CreateBitmap(
sc_bitmapWidth,
sc_bitmapHeight,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&pWICBitmap
);
}
if (SUCCEEDED(hr))
{
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
//auto rtProps = D2D1::RenderTargetProperties();
//rtProps.type = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
//rtProps.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget(
pWICBitmap,
rtProps,
&pRT
);
}
if (SUCCEEDED(hr))
{
//
// Render into the bitmap
//
pRT->BeginDraw();
pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black));
if (m_pBitmap)
{
D2D1_SIZE_F imageSize = m_pBitmap->GetSize();
pRT->DrawBitmap(m_pBitmap, D2D1::RectF(10, m_imagePosY, 10 + imageSize.width, m_imagePosY + imageSize.height));
}
// Draw text
static const WCHAR sc_helloWorld[] = L"Hello, World!";
D2D1_RECT_F layoutRect = D2D1::RectF(20, static_cast<float>(m_textPosY), 300, static_cast<float>(m_textPosY + 50));
pRT->DrawText(sc_helloWorld, ARRAYSIZE(sc_helloWorld) - 1, m_pTextFormat, &layoutRect, m_pTextBrush);
//pRT->DrawBitmap(pBitmap);
hr = pRT->EndDraw();
}
if (SUCCEEDED(hr))
{
//
// Save image to file
//
hr = m_pWICFactory->CreateStream(&pStream);
}
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
if (SUCCEEDED(hr))
{
hr = pStream->InitializeFromFilename(uri, GENERIC_WRITE);
}
if (SUCCEEDED(hr))
{
hr = m_pWICFactory->CreateEncoder(GUID_ContainerFormatPng, NULL, &pEncoder);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->CreateNewFrame(&pFrameEncode, NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Initialize(NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetSize(sc_bitmapWidth, sc_bitmapHeight);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetPixelFormat(&format);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Commit();
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Commit();
}
if(pWICBitmap) pWICBitmap->Release();
if(pRT) pRT->Release();
if(pEncoder) pEncoder->Release();
if(pStream)pStream->Release();
return hr;
}
The MSDN function from
https://learn.microsoft.com/en-us/windows/win32/direct2d/save-direct2d-content-to-an-image-file
works fine