DirectWriteをいじる(1)

公開:2010-10-30 21:19
更新:2020-02-15 04:36
カテゴリ:directwrite

ちょっとSTed2から外れて、DirectWriteをいじることに。 昨日書いた通り、DirectWriteとGDIを比較するためのベース・コードを作った。作ったというよりも過去コードを引っ張り出してきてすこしいじっただけだが。なのでいらないコードも含まれている。 実行結果は下記のとおり。

以下ウィンドウラッパクラス+描画部分のコード。このソースコードだけでは動作しないのでご注意を。いろいろインクルードする必要がある。そのあたりはまた後日。

ヘッダ


#pragma once
// Windows Header Files:
#include "exception.h"
#include "xbyak.h"
//#include "input.h"
_COM_SMARTPTR_TYPEDEF(ID2D1Factory,__uuidof(ID2D1Factory));
_COM_SMARTPTR_TYPEDEF(IWICImagingFactory, __uuidof(IWICImagingFactory));
_COM_SMARTPTR_TYPEDEF(IDWriteFactory , __uuidof(IDWriteFactory));
_COM_SMARTPTR_TYPEDEF(ID2D1HwndRenderTarget , __uuidof(ID2D1HwndRenderTarget));
_COM_SMARTPTR_TYPEDEF(ID2D1GdiInteropRenderTarget , __uuidof(ID2D1GdiInteropRenderTarget));
_COM_SMARTPTR_TYPEDEF(IDWriteTextFormat, __uuidof(IDWriteTextFormat));
_COM_SMARTPTR_TYPEDEF(ID2D1PathGeometry , __uuidof(ID2D1PathGeometry));
_COM_SMARTPTR_TYPEDEF(ID2D1LinearGradientBrush , __uuidof(ID2D1LinearGradientBrush));
_COM_SMARTPTR_TYPEDEF(ID2D1SolidColorBrush , __uuidof(ID2D1SolidColorBrush));
_COM_SMARTPTR_TYPEDEF(ID2D1BitmapBrush , __uuidof(ID2D1BitmapBrush));
_COM_SMARTPTR_TYPEDEF(ID2D1Bitmap , __uuidof(ID2D1Bitmap));
_COM_SMARTPTR_TYPEDEF(IWICBitmapDecoder,__uuidof(IWICBitmapDecoder));
_COM_SMARTPTR_TYPEDEF(IWICBitmapFrameDecode,__uuidof(IWICBitmapFrameDecode));
_COM_SMARTPTR_TYPEDEF(IWICStream,__uuidof(IWICStream));
_COM_SMARTPTR_TYPEDEF(IWICFormatConverter,__uuidof(IWICFormatConverter));
_COM_SMARTPTR_TYPEDEF(IWICBitmapScaler,__uuidof(IWICBitmapScaler));
namespace sf{
ID2D1BitmapPtr load_bitmap_from_file(
ID2D1HwndRenderTargetPtr render_target,
IWICImagingFactoryPtr wic_factory,
std::wstring uri,
boost::uint32_t destination_width = 0,
boost::uint32_t destination_height = 0
);
/** WNDCLASSEXラッパクラス */
struct window_class_ex
{
window_class_ex(
const wchar_t*  menu_name ,
const std::wstring&  class_name ,
HINSTANCE   hInstance = NULL,
WNDPROC     lpfnWndProc = ::DefWindowProcW,
boost::uint32_t        style = CS_HREDRAW | CS_VREDRAW,
boost::int32_t     cbClsExtra  = 0,
HICON       hIcon = ::LoadIcon(NULL,IDI_APPLICATION),
HCURSOR     hCursor = ::LoadCursor(NULL, IDC_ARROW),
HBRUSH      hbrBackground = NULL,
HICON       hIconSm = NULL
) : is_register_(false)
{
if(::GetClassInfoExW(hInstance,class_name.c_str(),&wndclass_) == 0)
{
if(::GetLastError() == ERROR_CLASS_DOES_NOT_EXIST)
{
::ZeroMemory(&wndclass_,sizeof(wndclass_));
wndclass_.lpszMenuName = menu_name;
wndclass_.lpszClassName = class_name.c_str();
wndclass_.cbSize = sizeof(::WNDCLASSEXW);
wndclass_.cbWndExtra = sizeof(LONG_PTR);
wndclass_.hInstance = hInstance;
wndclass_.lpfnWndProc = lpfnWndProc;
wndclass_.style = style;
wndclass_.cbClsExtra = cbClsExtra;
wndclass_.hIcon = hIcon;
wndclass_.hCursor = hCursor;
wndclass_.hbrBackground = hbrBackground;
wndclass_.hIconSm = hIconSm;
atom_ = ::RegisterClassExW(&wndclass_) ;
BOOST_ASSERT(atom_ != 0);
is_register_ = true;
} else {
throw win32_error_exception();
}
} else {
is_register_ = false;
}
};
~window_class_ex()
{
if(is_register_){
::UnregisterClassW(wndclass_.lpszClassName,wndclass_.hInstance);
}
}
private:
bool is_register_;
ATOM atom_;
::WNDCLASSEXW wndclass_;
};
struct get_dc {
get_dc(HWND hwnd) : hwnd_(hwnd),hdc_(GetDC(hwnd)) {}
HDC get(){return hdc_;}
~get_dc(){::ReleaseDC(hwnd_,hdc_);}
private:
HDC hdc_;
HWND hwnd_;
};
struct compatible_dc {
compatible_dc(HDC hdc) : hdc_(::CreateCompatibleDC(hdc)){};
~compatible_dc(){::DeleteDC(hdc_);};
HDC get() { return hdc_;};
private:
HDC hdc_;
};
struct ref_dc {
ref_dc(HDC& hdc) : hdc_(hdc) {};
~ref_dc(){};
HDC get() { return hdc_;};
private:
HDC& hdc_;
};
template <typename Holder>
struct device_context
{
explicit device_context(Holder* holder) : holder_(holder){};
~device_context() {}
operator HDC(){return holder_->get();}
private:
boost::scoped_ptr<Holder> holder_;
};
struct paint_struct
{
paint_struct(HWND hwnd) : hwnd_(hwnd)
{
::BeginPaint(hwnd,&paintstruct_);
}
~paint_struct() {::EndPaint(hwnd_,&paintstruct_);}
PAINTSTRUCT* operator->(){return &paintstruct_;}
private:
HWND hwnd_;
PAINTSTRUCT paintstruct_;
};
template <typename T>
struct begin_draw
{
begin_draw(T& render_target) : render_target_(render_target) ,is_end_(false) {render_target->BeginDraw();}
HRESULT end_draw()
{
HRESULT hr = S_OK;
if(!is_end_) {
hr = render_target->EndDraw();
is_end_ = true;
}
return hr;
};
~begin_draw(){ if(!is_end_) { render_target_->EndDraw();}}
private:
T& render_target_;
bool is_end_;
};
struct mouse
{
mouse() : x_(0.0f),y_(0.0f),left_button_(false),middle_button_(false),right_button_(false){}
private:
float x_,y_;
bool left_button_,middle_button_,right_button_;
};
/** window ベースクラス */
struct base_window
{
typedef boost::signals2::signal<LRESULT (HWND,boost::uint32_t,WPARAM, LPARAM) > on_message_type;
on_message_type on_message;
typedef boost::signals2::signal<void ()> on_render_type;
on_render_type on_render;
operator HWND();
protected:
base_window(const std::wstring& title,const std::wstring& name,bool fit_to_display,float width,float height);
~base_window();
void register_class (
wchar_t* menu_name,
boost::uint32_t      style  ,
boost::int32_t     cbClsExtra  = 0,
HICON       hIcon = ::LoadIcon(NULL,IDI_APPLICATION),
HCURSOR     hCursor = ::LoadCursor(NULL, IDC_ARROW),
HBRUSH      hbrBackground = NULL,
HICON       hIconSm = NULL
);
/** デフォルト設定 */
void register_class();
void create_window();
void update();
void show(boost::uint32_t show_flag);
void discard_device();
void create_device();
void create_device_independent_resources();
//ID2D1FactoryPtr factory();
//ID2D1HwndRenderTargetPtr render_target();
//IDWriteFactoryPtr write_factory();
virtual LRESULT window_proc(HWND hwnd,boost::uint32_t message, WPARAM wParam, LPARAM lParam);
protected:
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
base_window* ptr = reinterpret_cast<base_window*>(hwnd);
hwnd = ptr->hwnd_;
return ptr->window_proc(hwnd,message,wParam,lParam);
};
struct hwnd_this_thunk : public Xbyak::CodeGenerator {
hwnd_this_thunk(base_window* impl,WNDPROC proc)
{
push(eax);
mov(eax,ptr[esp + 8]);
mov(ptr[&(impl->hwnd_)],eax);
mov(eax,(DWORD)impl);
mov(ptr[esp + 8],eax);
pop(eax);
jmp(proc);
}
};
HWND hwnd_;
ID2D1FactoryPtr factory_;
ID2D1HwndRenderTargetPtr render_target_;
ID2D1GdiInteropRenderTargetPtr gdi_render_target_;
IDWriteFactoryPtr write_factory_;
IWICImagingFactoryPtr wic_imaging_factory_;
ID2D1BitmapPtr bitmap_;
hwnd_this_thunk thunk_;
std::wstring title_;
std::wstring name_;
float width_,height_;
bool fit_to_display_;
boost::shared_ptr<sf::window_class_ex> wnd_class_;
WNDPROC thunk_proc_;
private:
};
struct toplevel_window;
typedef boost::shared_ptr<toplevel_window> toplevel_window_ptr;
/** toplevel_window を生成する関数 */
toplevel_window_ptr create_toplevel_window (
const std::wstring& title,
const std::wstring& name,
const boost::uint32_t show_flag = SW_SHOWNORMAL,
bool fit_to_display = false,
float width = 640,
float height = 480
);
/** toplevel ウィンドウクラス */
/* このクラスは、create_toplevel_window 関数からのみ生成可能 */
struct toplevel_window : public base_window
{
friend   toplevel_window_ptr create_toplevel_window
(
const std::wstring& title,
const std::wstring& name,
const boost::uint32_t show_flag,
bool fit_to_display ,
float width ,
float height
);
void main_loop();
protected:
void render();
toplevel_window(const std::wstring& title,const std::wstring& name,bool fit_to_display,
float width = 640,float height = 480) : base_window(title,name,fit_to_display,width,height)
{
on_render.connect(boost::bind(&toplevel_window::render,this));
};
LRESULT toplevel_window::window_proc(HWND hwnd,boost::uint32_t message, WPARAM wParam, LPARAM lParam);
};
struct av_mm_thread_characteristics
{
av_mm_thread_characteristics(std::wstring& str) : task_name_(str)
{
handle_ = ::AvSetMmThreadCharacteristicsW(str.c_str(),(LPDWORD)&task_index_);
}
bool set_priority(AVRT_PRIORITY p){return (::AvSetMmThreadPriority(handle_,p) == TRUE);}
~av_mm_thread_characteristics()
{
::AvRevertMmThreadCharacteristics(handle_);
}
private:
std::wstring task_name_;
boost::uint32_t task_index_;
HANDLE handle_;
};
struct widget
{
void draw();
float x_,y_;
};
}

実装部分。


#include "includes.h"
#define BOOST_ASSIGN_MAX_PARAMS 7
#include <boost/assign.hpp>
#include <boost/assign/ptr_list_of.hpp>
#include <boost/assign/ptr_list_inserter.hpp>
#include <boost/foreach.hpp>
#include "sf_windows.h"
#include "exception.h"
#define EXCEPTION_IF_ERROR(hres) \
if (FAILED(hres)) { throw sf::win32_error_exception(hres); }
#ifndef HINST_THISCOMPONENT
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
#endif
#define SAFE_RELEASE(x) if(x) x.Release();
namespace sf
{
struct SimpleVertex
{
D3DXVECTOR3 Pos;
D3DXVECTOR2 Tex;
};
LRESULT base_window::window_proc(HWND hwnd,boost::uint32_t message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch (message)
{
case WM_CREATE:
{
// TODO:
create_device();
break;
}
case WM_SIZE:
{
if (render_target_)
{
D2D1_SIZE_U size;
size.width = lParam & 0xFFFF;
size.height = (lParam >> 16) & 0xFFFF; ;
render_target_->Resize(size);
}
}
case WM_PAINT:
{
paint_struct begin_paint(hwnd);
return FALSE;
}
case WM_DISPLAYCHANGE:
{
::InvalidateRect(hwnd, NULL, FALSE);
}
case WM_ERASEBKGND:
{
return FALSE;
}
case WM_MOUSEMOVE:
{
//                  on_mouse_move(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),wParam);
}
case WM_LBUTTONDOWN:
{
}
}
return ::DefWindowProcW(hwnd,message,wParam,lParam);
};
void base_window::create_device_independent_resources()
{
//wic_imaging_factory_.CreateInstance(CLSID_WICImagingFactory);
thunk_proc_ = (WNDPROC)thunk_.getCode();
};
void base_window::register_class (
wchar_t* menu_name,
boost::uint32_t        style ,
boost::int32_t     cbClsExtra,
HICON       hIcon ,
HCURSOR     hCursor,
HBRUSH      hbrBackground ,
HICON       hIconSm
)
{
wnd_class_.reset(new sf::window_class_ex(menu_name,name_,HINST_THISCOMPONENT,thunk_proc_,style,cbClsExtra,hIcon,hCursor,hbrBackground,hIconSm));
}
/** デフォルト設定 */
void base_window::register_class()
{
wnd_class_.reset(new sf::window_class_ex(NULL,name_,HINST_THISCOMPONENT,thunk_proc_));
}
void base_window::create_window()
{
// Create the application window.
//
// Because the CreateWindow function takes its size in pixels, we
// obtain the system DPI and use it to scale the window size.
FLOAT dpiX, dpiY;
//factory_->GetDesktopDpi(&dpiX, &dpiY);
// Windowを作成する
CreateWindow(
name_.c_str(),
title_.c_str(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
static_cast<boost::uint32_t>(ceil(width_ /** dpiX / 96.f*/)),
static_cast<boost::uint32_t>(ceil(height_ /** dpiY / 96.f*/)),
NULL,
NULL,
HINST_THISCOMPONENT,
this
);
}
void base_window::create_device()
{
//      input_.reset(new input(HINST_THISCOMPONENT,hwnd_));
HRESULT hr = S_OK;
// Direct2DFactory の生成
EXCEPTION_IF_ERROR(D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &factory_));
EXCEPTION_IF_ERROR(::DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&write_factory_)
));
//ウィンドウの現在の幅、高さを求める
RECT rc;
GetClientRect( hwnd_, &rc );
boost::uint32_t width = rc.right - rc.left;
boost::uint32_t height = rc.bottom - rc.top;
{
wic_imaging_factory_.CreateInstance(CLSID_WICImagingFactory);
//          bitmap_ = load_bitmap_from_file(render_target_,wic_imaging_factory_,L"myship.png");
}
if(!render_target_)
{
RECT rc;
GetClientRect(hwnd_, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top
);
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties();
props.usage =  D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
EXCEPTION_IF_ERROR(factory_->CreateHwndRenderTarget(
props,
D2D1::HwndRenderTargetProperties(hwnd_, size),
&render_target_
));
render_target_->QueryInterface<ID2D1GdiInteropRenderTarget>(&gdi_render_target_);
}
}
void base_window::discard_device ()
{
/*      if(render_target_)
{
render_target_.Release();
}*/
}
void base_window::show(boost::uint32_t show_flag) {::ShowWindow(hwnd_,show_flag);}
void base_window::update() {::UpdateWindow(hwnd_);}
base_window::~base_window()
{
}
base_window::base_window(const std::wstring& menu_name,const std::wstring& name,bool fit_to_display,float width,float height)
: title_(menu_name),name_(name),fit_to_display_(fit_to_display),
width_(width),height_(height),thunk_(this,base_window::WndProc),hwnd_(0)
{
create_device_independent_resources();
}
base_window::operator HWND()
{
return hwnd_;
};
//ID2D1FactoryPtr base_window::factory() { return impl_->factory();};
//ID2D1HwndRenderTargetPtr base_window::render_target() { return impl_->render_target();};
//IDWriteFactoryPtr base_window::write_factory() {return impl_->write_factory();};
toplevel_window_ptr create_toplevel_window
(
const std::wstring& menu_name,
const std::wstring& name,
const boost::uint32_t show_flag,
bool fit_to_display,
float width,
float height
)
{
toplevel_window* p = new toplevel_window(menu_name,name,fit_to_display,width,height);
p->register_class();
p->create_window();
p->show(show_flag);
p->update();
return toplevel_window_ptr(p);
}
LRESULT toplevel_window::window_proc(HWND hwnd,boost::uint32_t message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
// TODO:
create_device();
break;
}
case WM_SIZE:
{
if (render_target_)
{
D2D1_SIZE_U size;
size.width = lParam & 0xFFFF;
size.height = (lParam >> 16) & 0xFFFF; ;
render_target_->Resize(size);
}
}
case WM_PAINT:
{
paint_struct begin_paint(hwnd);
render();
return FALSE;
}
case WM_DISPLAYCHANGE:
{
::InvalidateRect(hwnd, NULL, FALSE);
}
case WM_ERASEBKGND:
{
return FALSE;
}
case WM_MOUSEMOVE:
{
//                  on_mouse_move(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),wParam);
}
case WM_LBUTTONDOWN:
{
}
}
if(message == WM_CLOSE)
{
BOOL ret(::DestroyWindow(hwnd));
BOOST_ASSERT(ret != 0);
}
if(message == WM_DESTROY)
{
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProcW(hwnd,message,wParam,lParam);
}
void toplevel_window::main_loop()
{
render();
}
void toplevel_window::render()
{
static float t = 0.0f;
if (render_target_)
{
// Retrieve the size of the render target.
D2D1_SIZE_F renderTargetSize = render_target_->GetSize();
try {
render_target_->BeginDraw();
render_target_->Clear(D2D1::ColorF(D2D1::ColorF::White));
render_target_->SetTransform(D2D1::Matrix3x2F::Identity());
//render_target_->Clear(D2D1::ColorF(D2D1::ColorF::White));
ID2D1SolidColorBrushPtr brush;
render_target_->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &brush);
ID2D1SolidColorBrushPtr brushr;
render_target_->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &brushr);
D2D1_RECT_F layoutRect = D2D1::RectF(50.f, 50.f, 600.f, 200.f);
IDWriteTextFormatPtr write_text_format;
// Text Formatの作成
EXCEPTION_IF_ERROR(write_factory_->CreateTextFormat(
L"メイリオ",                // Font family name.
NULL,                       // Font collection (NULL sets it to use the system font collection).
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
48.0f,
L"ja-jp",
&write_text_format
));
// Actually draw the text at the origin.
std::wstring t(L"こんにちは、世界!By S.F.");
render_target_->DrawTextW(
t.c_str(),
t.size(),
write_text_format,
layoutRect,
brush);
{
HDC dc;
gdi_render_target_->GetDC(D2D1_DC_INITIALIZE_MODE_COPY,&dc);
HGDIOBJ original = NULL;
original = SelectObject(
dc,
GetStockObject(DC_PEN)
);
HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
::SetBkMode(dc,TRANSPARENT);
SelectObject(dc, blackPen);
::TextOutW(dc,100,300,t.data(),t.size());
DeleteObject(blackPen);
// Restore the original object.
SelectObject(dc, original);
gdi_render_target_->ReleaseDC(NULL);
}
EXCEPTION_IF_ERROR(render_target_->EndDraw());
}  catch(...) {
throw;
}
}
};
//
// Creates a Direct2D bitmap from the specified
// file name.
//
ID2D1BitmapPtr load_bitmap_from_file(
ID2D1HwndRenderTargetPtr render_target,
IWICImagingFactoryPtr wic_factory,
std::wstring uri,
boost::uint32_t destination_width,
boost::uint32_t destination_height
)
{
HRESULT hr = S_OK;
IWICBitmapDecoderPtr decoder;
IWICBitmapFrameDecodePtr decoder_source;
IWICStreamPtr stream;
IWICFormatConverterPtr converter;
IWICBitmapScalerPtr scaler;
ID2D1BitmapPtr bitmap;
EXCEPTION_IF_ERROR(wic_factory->CreateDecoderFromFilename(
uri.c_str(),
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&decoder
));
// Create the initial frame.
EXCEPTION_IF_ERROR(decoder->GetFrame(0, &decoder_source));
// Convert the image format to 32bppPBGRA
// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
EXCEPTION_IF_ERROR(hr = wic_factory->CreateFormatConverter(&converter));
// If a new width or height was specified, create an
// IWICBitmapScaler and use it to resize the image.
if (destination_width != 0 || destination_height != 0)
{
boost::uint32_t originalWidth, originalHeight;
EXCEPTION_IF_ERROR(decoder_source->GetSize((UINT*)&originalWidth, (UINT*)&originalHeight));
if (destination_width == 0)
{
FLOAT scalar = static_cast<FLOAT>(destination_height) / static_cast<FLOAT>(originalHeight);
destination_width = static_cast<boost::uint32_t>(scalar * static_cast<FLOAT>(originalWidth));
}
else if (destination_height == 0)
{
FLOAT scalar = static_cast<FLOAT>(destination_width) / static_cast<FLOAT>(originalWidth);
destination_height = static_cast<boost::uint32_t>(scalar * static_cast<FLOAT>(originalHeight));
}
EXCEPTION_IF_ERROR(wic_factory->CreateBitmapScaler(&scaler));
EXCEPTION_IF_ERROR(scaler->Initialize(
decoder_source,
destination_width,
destination_height,
WICBitmapInterpolationModeCubic
));
EXCEPTION_IF_ERROR(converter->Initialize(
scaler.GetInterfacePtr(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
));
}
else // Don't scale the image.
{
EXCEPTION_IF_ERROR(converter->Initialize(
decoder_source.GetInterfacePtr(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
));
}
// Create a Direct2D bitmap from the WIC bitmap.
EXCEPTION_IF_ERROR(render_target->CreateBitmapFromWicBitmap(
converter.GetInterfacePtr(),
NULL,
&bitmap
));
return bitmap;
}
}