// This method takes an attributed string and outputs a GlyphLayout data // structure that contains the glyph number and location of each inidividual glyph void getGlyphLayout (const AttributedString& text, GlyphLayout& glyphLayout) { // For now we are creating the DirectWrite Factory, System Font Collection, // D2D Factory and GDI Render target every time we layout text. // This is inefficient and we may be loading and unloading libraries each layout. // These four things should be created once at application startup and be destroyed // when the application exits. I'm not sure where the best place to do this so // for now I will just use the inefficient method. IDWriteFactory* dwFactory = nullptr; HRESULT hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwFactory)); IDWriteFontCollection* dwFontCollection = nullptr; hr = dwFactory->GetSystemFontCollection (&dwFontCollection); // To add color to text, we need to create a D2D render target // Since we are not actually rendering to a D2D context we create a temporary GDI render target ID2D1Factory *d2dFactory = nullptr; hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory); D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_SOFTWARE, D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 0, 0, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, D2D1_FEATURE_LEVEL_DEFAULT ); ID2D1DCRenderTarget* d2dDCRT = nullptr; hr = d2dFactory->CreateDCRenderTarget (&d2dRTProp, &d2dDCRT); // Initially we set the paragraph up with a default font and then apply the attributed string ranges later Font defaultFont; const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (defaultFont, *dwFontCollection); // We should probably be detecting the locale instead of hard coding it to en-us String localeName("en-us"); // We multiply the font height by the size factor so we layout text at the correct size IDWriteTextFormat* dwTextFormat = nullptr; hr = dwFactory->CreateTextFormat ( defaultFont.getTypefaceName().toWideCharPointer(), dwFontCollection, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, localeName.toWideCharPointer(), &dwTextFormat ); // Paragraph Attributes // Set Paragraph Alignment if (text.getTextAlignment() == AttributedString::left) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING); if (text.getTextAlignment() == AttributedString::right) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_TRAILING); if (text.getTextAlignment() == AttributedString::center) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_CENTER); // DirectWrite cannot justify text, default to left alignment if (text.getTextAlignment() == AttributedString::justified) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING); // Set Word Wrap if (text.getWordWrap() == AttributedString::none) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_NO_WRAP); if (text.getWordWrap() == AttributedString::byWord) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP); // DirectWrite does not support wrapping by character, default to wrapping by word if (text.getWordWrap() == AttributedString::byChar) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP); // DirectWrite does not automatically set reading direction // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic) if (text.getReadingDirection() == AttributedString::rightToLeft) dwTextFormat->SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); IDWriteTextLayout* dwTextLayout = nullptr; hr = dwFactory->CreateTextLayout ( text.getText().toWideCharPointer(), text.getText().length(), dwTextFormat, glyphLayout.getWidth(), glyphLayout.getHeight(), &dwTextLayout ); // Character Attributes int numCharacterAttributes = text.getCharAttributesSize(); for (int i = 0; i < numCharacterAttributes; ++i) { Attr* attr = text.getCharAttribute (i); // Character Range Error Checking if (attr->range.getStart() > text.getText().length()) continue; if (attr->range.getEnd() > text.getText().length()) attr->range.setEnd (text.getText().length()); if (attr->attribute == Attr::font) { AttrFont* attrFont = static_cast<AttrFont*>(attr); DWRITE_TEXT_RANGE dwRange; dwRange.startPosition = attrFont->range.getStart(); dwRange.length = attrFont->range.getLength(); dwTextLayout->SetFontFamilyName (attrFont->font.getTypefaceName().toWideCharPointer(), dwRange); // We multiply the font height by the size factor so we layout text at the correct size const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (attrFont->font, *dwFontCollection); dwTextLayout->SetFontSize (attrFont->font.getHeight() * fontHeightToEmSizeFactor, dwRange); } if (attr->attribute == Attr::foregroundColour) { AttrColour* attrColour = static_cast<AttrColour*>(attr); DWRITE_TEXT_RANGE dwRange; dwRange.startPosition = attrColour->range.getStart(); dwRange.length = attrColour->range.getLength(); ID2D1SolidColorBrush* d2dBrush = nullptr; d2dDCRT->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF(attrColour->colour.getFloatRed(), attrColour->colour.getFloatGreen(), attrColour->colour.getFloatBlue(), attrColour->colour.getFloatAlpha())), &d2dBrush); // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours dwTextLayout->SetDrawingEffect (d2dBrush, dwRange); safeRelease (&d2dBrush); } } UINT32 actualLineCount = 0; hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); // Preallocate GlyphLayout Line Array glyphLayout.setNumLines (actualLineCount); HeapBlock <DWRITE_LINE_METRICS> dwLineMetrics (actualLineCount); hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount); int location = 0; // Create GlyphLine structures for each line in the layout for (UINT32 i = 0; i < actualLineCount; ++i) { // Get string range Range<int> lineStringRange (location, (int) location + dwLineMetrics[i].length); location = dwLineMetrics[i].length; GlyphLine* glyphLine = new GlyphLine(); glyphLine->setStringRange (lineStringRange); glyphLayout.addGlyphLine (glyphLine); } // To copy glyph data from DirectWrite into our own data structures we must create our // own CustomTextRenderer. Instead of passing the draw method an actual graphics context, // we pass it the GlyphLayout object that needs to be filled with glyphs. CustomDirectWriteTextRenderer* textRenderer = nullptr; textRenderer = new CustomDirectWriteTextRenderer(); hr = dwTextLayout->Draw ( &glyphLayout, textRenderer, glyphLayout.getX(), glyphLayout.getY() ); safeRelease (&textRenderer); safeRelease (&dwTextLayout); safeRelease (&dwTextFormat); safeRelease (&d2dDCRT); safeRelease (&d2dFactory); safeRelease (&dwFontCollection); safeRelease (&dwFactory); }
int CDirectWriteRenderer::GetFitCharCount(LPCWSTR pText, int Length, int Width, CDirectWriteFont &Font) { if (pText == nullptr || Length == 0) return 0; if (m_pRenderTarget == nullptr) return 0; int FitCharCount = 0; IDWriteFactory *pFactory = m_System.GetDWriteFactory(); if (pFactory != nullptr) { IDWriteTextFormat *pTextFormat = Font.GetTextFormat(); if (pTextFormat != nullptr) { IDWriteTextLayout *pTextLayout; pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); if (Length < 0) Length = ::lstrlenW(pText); HRESULT hr = pFactory->CreateTextLayout( pText, Length, pTextFormat, static_cast<float>(Width), m_pRenderTarget->GetSize().height, &pTextLayout); if (SUCCEEDED(hr)) { Util::CTempBuffer<DWRITE_CLUSTER_METRICS, 256> ClusterMetrics(Length); UINT32 ClusterCount; hr = pTextLayout->GetClusterMetrics(ClusterMetrics.GetBuffer(), Length, &ClusterCount); if (SUCCEEDED(hr)) { float Pos = 0.0f; for (UINT32 i = 0; i < ClusterCount; i++) { Pos += ClusterMetrics[i].width; if (static_cast<int>(std::ceil(Pos)) > Width) break; FitCharCount += ClusterMetrics[i].length; } } pTextLayout->Release(); } pTextFormat->Release(); } pFactory->Release(); } return FitCharCount; }
bool CDirectWriteRenderer::DrawText( LPCWSTR pText, int Length, const RECT &Rect, CDirectWriteFont &Font, CDirectWriteBrush &Brush, unsigned int Flags) { if (pText == nullptr) return false; if (m_pRenderTarget == nullptr) return false; bool fOK = false; IDWriteTextFormat *pTextFormat = Font.GetTextFormat(); if (pTextFormat != nullptr) { ID2D1Brush *pBrush = Brush.GetBrush(); if (pBrush != nullptr) { pTextFormat->SetTextAlignment( (Flags & DRAW_TEXT_ALIGN_HORZ_CENTER) != 0 ? DWRITE_TEXT_ALIGNMENT_CENTER : (Flags & DRAW_TEXT_ALIGN_RIGHT) != 0 ? DWRITE_TEXT_ALIGNMENT_TRAILING : ((Flags & DRAW_TEXT_ALIGN_JUSTIFIED) != 0 && Util::OS::IsWindows8OrLater()) ? DWRITE_TEXT_ALIGNMENT_JUSTIFIED : DWRITE_TEXT_ALIGNMENT_LEADING); pTextFormat->SetParagraphAlignment( (Flags & DRAW_TEXT_ALIGN_VERT_CENTER) != 0 ? DWRITE_PARAGRAPH_ALIGNMENT_CENTER : (Flags & DRAW_TEXT_ALIGN_BOTTOM) != 0 ? DWRITE_PARAGRAPH_ALIGNMENT_FAR : DWRITE_PARAGRAPH_ALIGNMENT_NEAR); pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); DWRITE_TRIMMING Trimming = {DWRITE_TRIMMING_GRANULARITY_NONE, 0, 0}; pTextFormat->SetTrimming(&Trimming, nullptr); m_pRenderTarget->DrawText( pText, Length >= 0 ? Length : ::lstrlenW(pText), pTextFormat, D2DRectF(Rect), pBrush); fOK = true; pBrush->Release(); } pTextFormat->Release(); } return fOK; }
bool CDirectWriteRenderer::GetTextMetrics( LPCWSTR pText, int Length, CDirectWriteFont &Font, TextMetrics *pMetrics) { if (pText == nullptr || pMetrics == nullptr) return false; if (m_pRenderTarget == nullptr) return false; HRESULT hr = E_UNEXPECTED; IDWriteFactory *pFactory = m_System.GetDWriteFactory(); if (pFactory != nullptr) { IDWriteTextFormat *pTextFormat = Font.GetTextFormat(); if (pTextFormat != nullptr) { D2D1_SIZE_F Size; IDWriteTextLayout *pTextLayout; pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); if (Length < 0) Length = ::lstrlenW(pText); Size = m_pRenderTarget->GetSize(); hr = pFactory->CreateTextLayout( pText, Length, pTextFormat, Size.width, Size.height, &pTextLayout); if (SUCCEEDED(hr)) { DWRITE_TEXT_METRICS Metrics; hr = pTextLayout->GetMetrics(&Metrics); if (SUCCEEDED(hr)) { pMetrics->Width = Metrics.width; pMetrics->WidthIncludingTrailingWhitespace = Metrics.widthIncludingTrailingWhitespace; pMetrics->Height = Metrics.height; } pTextLayout->Release(); } pTextFormat->Release(); } pFactory->Release(); } return SUCCEEDED(hr); }
void Graphics::GDrawText(int x, int y, wchar_t* text, wchar_t* fontName, int fontSize, DWRITE_FONT_STYLE style, D2D1_COLOR_F color) { ID2D1SolidColorBrush* brush; IDWriteTextFormat* textFormat; UINT32 textLength; textLength = (UINT32)wcslen(text); writeFactory->CreateTextFormat(fontName, NULL, DWRITE_FONT_WEIGHT_REGULAR, style, DWRITE_FONT_STRETCH_NORMAL, fontSize, L"en-us", &textFormat); textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brush); renderTarget->DrawTextW(text, textLength, textFormat, D2D1::RectF(x, y, fontSize * textLength, y + fontSize), brush); SafeRelease(&brush); SafeRelease(&textFormat); }
void DWriteContext::SetFont(const LOGFONTW &logFont) { SafeRelease(&mTextFormat); mLastHFont = NULL; HRESULT hr = SetLOGFONT(logFont, 0.f); if (SUCCEEDED(hr)) hr = mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); if (SUCCEEDED(hr)) hr = mTextFormat->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER); if (SUCCEEDED(hr)) hr = mTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); }
// 创建字体格式 IDWriteTextFormat* TextFormatCache::CreateTextFormat(const TextFormat& font){ #ifdef _DEBUG TextFormatCache::Map::iterator itr = s_mapTextCache.find(font); if (itr != s_mapTextCache.end()){ _cwprintf(L"目标字体已存在.\n"); return itr->second; } if (!s_ppDWriteFactory){ _cwprintf(L"s_ppDWriteFactory未初始化.\n"); return NULL; } if (!(*s_ppDWriteFactory)){ _cwprintf(L"DWriteFactory未初始化.\n"); return NULL; } #endif IDWriteTextFormat* pTF; (*s_ppDWriteFactory)->CreateTextFormat( font.font_name.c_str(), s_pCollection, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, font.font_size * (96.0f / 72.f), L"", //locale &pTF ); if (pTF){ pTF->SetTextAlignment(font.font_alignment); } #ifdef _DEBUG else _cwprintf(L"IDWriteTextFormat 创建失败.\n"); #endif s_mapTextCache.insert(Map::value_type(font, pTF)); return pTF; }
void setTextFormatProperties (const AttributedString& text, IDWriteTextFormat& format) { DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING; DWRITE_WORD_WRAPPING wrapType = DWRITE_WORD_WRAPPING_WRAP; switch (text.getJustification().getOnlyHorizontalFlags()) { case Justification::left: break; case Justification::right: alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break; case Justification::horizontallyCentred: alignment = DWRITE_TEXT_ALIGNMENT_CENTER; break; case Justification::horizontallyJustified: break; // DirectWrite cannot justify text, default to left alignment default: jassertfalse; break; // Illegal justification flags } switch (text.getWordWrap()) { case AttributedString::none: wrapType = DWRITE_WORD_WRAPPING_NO_WRAP; break; case AttributedString::byWord: break; case AttributedString::byChar: break; // DirectWrite doesn't support wrapping by character, default to word-wrap default: jassertfalse; break; // Illegal flags! } // DirectWrite does not automatically set reading direction // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic) if (text.getReadingDirection() == AttributedString::rightToLeft) { format.SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); switch (text.getJustification().getOnlyHorizontalFlags()) { case Justification::left: alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break; case Justification::right: alignment = DWRITE_TEXT_ALIGNMENT_LEADING; break; default: break; } } format.SetTextAlignment (alignment); format.SetWordWrapping (wrapType); }
IDWriteTextFormat* createTextFormat(IDWriteFactory1* writeFactory, const char* fontName, bool alignmentCenterX, bool alignmentCenterY) { IDWriteTextFormat* textFormat; // Create a DirectWrite text format object. DX::ThrowIfFailed( writeFactory->CreateTextFormat( Utils::convertStringToWString(fontName).c_str(),//Gabriola NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 64.f, L"en-US", // Locale &textFormat ) ); // Center the text horizontally and vertically. textFormat->SetTextAlignment(alignmentCenterX ? DWRITE_TEXT_ALIGNMENT_CENTER : DWRITE_TEXT_ALIGNMENT_LEADING); textFormat->SetParagraphAlignment(alignmentCenterY ? DWRITE_PARAGRAPH_ALIGNMENT_CENTER : DWRITE_PARAGRAPH_ALIGNMENT_NEAR); return textFormat; }
HRESULT SaveToImageFile() { HRESULT hr = S_OK; // // Create Factories // IWICImagingFactory *pWICFactory = NULL; ID2D1Factory *pD2DFactory = NULL; IDWriteFactory *pDWriteFactory = NULL; IWICBitmap *pWICBitmap = NULL; ID2D1RenderTarget *pRT = NULL; IDWriteTextFormat *pTextFormat = NULL; ID2D1PathGeometry *pPathGeometry = NULL; ID2D1GeometrySink *pSink = NULL; ID2D1GradientStopCollection *pGradientStops = NULL; ID2D1LinearGradientBrush *pLGBrush = NULL; ID2D1SolidColorBrush *pBlackBrush = NULL; IWICBitmapEncoder *pEncoder = NULL; IWICBitmapFrameEncode *pFrameEncode = NULL; IWICStream *pStream = NULL; hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<void **>(&pWICFactory) ); if (SUCCEEDED(hr)) { hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory); } if (SUCCEEDED(hr)) { hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(pDWriteFactory), reinterpret_cast<IUnknown **>(&pDWriteFactory) ); } // // Create IWICBitmap and RT // static const UINT sc_bitmapWidth = 640; static const UINT sc_bitmapHeight = 480; if (SUCCEEDED(hr)) { hr = pWICFactory->CreateBitmap( sc_bitmapWidth, sc_bitmapHeight, GUID_WICPixelFormat32bppBGR, WICBitmapCacheOnLoad, &pWICBitmap ); } if (SUCCEEDED(hr)) { hr = pD2DFactory->CreateWicBitmapRenderTarget( pWICBitmap, D2D1::RenderTargetProperties(), &pRT ); } if (SUCCEEDED(hr)) { // // Create text format // static const WCHAR sc_fontName[] = L"Calibri"; static const FLOAT sc_fontSize = 50; hr = pDWriteFactory->CreateTextFormat( sc_fontName, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, sc_fontSize, L"", //locale &pTextFormat ); } if (SUCCEEDED(hr)) { pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); // // Create a path geometry representing an hour glass // hr = pD2DFactory->CreatePathGeometry(&pPathGeometry); } if (SUCCEEDED(hr)) { hr = pPathGeometry->Open(&pSink); } if (SUCCEEDED(hr)) { pSink->SetFillMode(D2D1_FILL_MODE_ALTERNATE); pSink->BeginFigure( D2D1::Point2F(0, 0), D2D1_FIGURE_BEGIN_FILLED ); pSink->AddLine(D2D1::Point2F(200, 0)); pSink->AddBezier( D2D1::BezierSegment( D2D1::Point2F(150, 50), D2D1::Point2F(150, 150), D2D1::Point2F(200, 200)) ); pSink->AddLine(D2D1::Point2F(0, 200)); pSink->AddBezier( D2D1::BezierSegment( D2D1::Point2F(50, 150), D2D1::Point2F(50, 50), D2D1::Point2F(0, 0)) ); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); hr = pSink->Close(); } if (SUCCEEDED(hr)) { // // Create a linear-gradient brush // static const D2D1_GRADIENT_STOP stops[] = { { 0.f, { 0.f, 1.f, 1.f, 1.f } }, { 1.f, { 0.f, 0.f, 1.f, 1.f } }, }; hr = pRT->CreateGradientStopCollection( stops, ARRAYSIZE(stops), &pGradientStops ); } if (SUCCEEDED(hr)) { hr = pRT->CreateLinearGradientBrush( D2D1::LinearGradientBrushProperties( D2D1::Point2F(100, 0), D2D1::Point2F(100, 200)), D2D1::BrushProperties(), pGradientStops, &pLGBrush ); } if (SUCCEEDED(hr)) { hr = pRT->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Black), &pBlackBrush ); } if (SUCCEEDED(hr)) { // // Render into the bitmap // pRT->BeginDraw(); pRT->Clear(D2D1::ColorF(D2D1::ColorF::White)); D2D1_SIZE_F rtSize = pRT->GetSize(); // Set the world transform to a 45 degree rotation at the center of the render target // and write "Hello, World". pRT->SetTransform( D2D1::Matrix3x2F::Rotation( 45, D2D1::Point2F( rtSize.width / 2, rtSize.height / 2)) ); static const WCHAR sc_helloWorld[] = L"Hello, World!"; pRT->DrawText( sc_helloWorld, ARRAYSIZE(sc_helloWorld) - 1, pTextFormat, D2D1::RectF(0, 0, rtSize.width, rtSize.height), pBlackBrush); // // Reset back to the identity transform // pRT->SetTransform(D2D1::Matrix3x2F::Translation(0, rtSize.height - 200)); pRT->FillGeometry(pPathGeometry, pLGBrush); pRT->SetTransform(D2D1::Matrix3x2F::Translation(rtSize.width - 200, 0)); pRT->FillGeometry(pPathGeometry, pLGBrush); hr = pRT->EndDraw(); } if (SUCCEEDED(hr)) { // // Save image to file // hr = pWICFactory->CreateStream(&pStream); } WICPixelFormatGUID format = GUID_WICPixelFormatDontCare; if (SUCCEEDED(hr)) { static const WCHAR filename[] = L"output.png"; hr = pStream->InitializeFromFilename(filename, GENERIC_WRITE); } if (SUCCEEDED(hr)) { hr = 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(); } SafeRelease(&pWICFactory); SafeRelease(&pD2DFactory); SafeRelease(&pDWriteFactory); SafeRelease(&pWICBitmap); SafeRelease(&pRT); SafeRelease(&pTextFormat); SafeRelease(&pPathGeometry); SafeRelease(&pSink); SafeRelease(&pGradientStops); SafeRelease(&pLGBrush); SafeRelease(&pBlackBrush); SafeRelease(&pEncoder); SafeRelease(&pFrameEncode); SafeRelease(&pStream); return hr; }
HRESULT SaveToImageFile(PlotData **data , unsigned int imageWidth, unsigned int imageHeight , float rangeX, float rangeY , float leftMargin, float bottomMargin , float xTick, float yTick) { HRESULT hr = S_OK; // // Create Factories // IWICImagingFactory *pWICFactory = NULL; ID2D1Factory *pD2DFactory = NULL; IDWriteFactory *pDWriteFactory = NULL; IWICBitmap *pWICBitmap = NULL; ID2D1RenderTarget *pRT = NULL; IDWriteTextFormat *pTextFormat = NULL; ID2D1SolidColorBrush *pSolidBrush = NULL; IWICBitmapEncoder *pEncoder = NULL; IWICBitmapFrameEncode *pFrameEncode = NULL; IWICStream *pStream = NULL; hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<void **>(&pWICFactory) ); if (SUCCEEDED(hr)) { hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory); } if (SUCCEEDED(hr)) { hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(pDWriteFactory), reinterpret_cast<IUnknown **>(&pDWriteFactory) ); } // // Create IWICBitmap and RT // if (SUCCEEDED(hr)) { hr = pWICFactory->CreateBitmap( imageWidth, imageHeight, GUID_WICPixelFormat32bppBGR, WICBitmapCacheOnLoad, &pWICBitmap ); } if (SUCCEEDED(hr)) { hr = pD2DFactory->CreateWicBitmapRenderTarget( pWICBitmap, D2D1::RenderTargetProperties(), &pRT ); } if (SUCCEEDED(hr)) { // // Create text format // static const WCHAR sc_fontName[] = L"Arial"; static const FLOAT sc_fontSize = 20; hr = pDWriteFactory->CreateTextFormat( sc_fontName, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, sc_fontSize, L"", //locale &pTextFormat ); } if (SUCCEEDED(hr)) { pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); // // Create a path geometry representing an hour glass // } if (SUCCEEDED(hr)) { hr = pRT->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Black), &pSolidBrush ); } if (SUCCEEDED(hr)) { // // Render into the bitmap // pRT->BeginDraw(); pRT->Clear(D2D1::ColorF(D2D1::ColorF::White)); D2D1_SIZE_F rtSize = pRT->GetSize(); //static const WCHAR sc_helloWorld[] = L"Hello, World!"; //pRT->DrawText( // sc_helloWorld, // ARRAYSIZE(sc_helloWorld) - 1, // pTextFormat, // D2D1::RectF(0, 0, rtSize.width, rtSize.height), // pBlackBrush); float scaleX = (rtSize.width - leftMargin) / rangeX; float scaleY = (rtSize.height - bottomMargin) / rangeY; // // Reset back to the identity transform // pRT->DrawLine(D2D1::Point2F(leftMargin, 0.0f), D2D1::Point2F(leftMargin, rtSize.height - bottomMargin), pSolidBrush); pRT->DrawLine(D2D1::Point2F(leftMargin, rtSize.height - bottomMargin), D2D1::Point2F(rtSize.width, rtSize.height - bottomMargin), pSolidBrush); pSolidBrush->SetColor(D2D1::ColorF(D2D1::ColorF::LightGray, 0.4f)); size_t countTicks = rangeY / yTick; for(size_t tick = 1; tick <= countTicks; ++tick) { float y = rtSize.height - bottomMargin - static_cast<float>(tick) * yTick * scaleY; pRT->DrawLine(D2D1::Point2F(leftMargin - 3.0f, y), D2D1::Point2F(rtSize.width, y), pSolidBrush); } countTicks = rangeX / xTick; for(size_t tick = 1; tick <= countTicks; ++tick) { float x = leftMargin + static_cast<float>(tick) * xTick * scaleX; pRT->DrawLine(D2D1::Point2F(x, 0.0f), D2D1::Point2F(x, rtSize.height - bottomMargin + 3.0f), pSolidBrush); } //D2D1_MATRIX_3X2_F rotation = D2D1::Matrix3x2F::Rotation(-90.0f); D2D1_MATRIX_3X2_F translation = D2D1::Matrix3x2F::Translation(leftMargin, rtSize.height - bottomMargin); //D2D1_MATRIX_3X2_F scale = D2D1::Matrix3x2F::Scale(D2D1::SizeF(600.0f, 1.0f)); pRT->SetTransform(translation); GraphData(data, pD2DFactory, pRT, pSolidBrush, scaleX, scaleY ); hr = pRT->EndDraw(); } if (SUCCEEDED(hr)) { // // Save image to file // hr = pWICFactory->CreateStream(&pStream); } WICPixelFormatGUID format = GUID_WICPixelFormatDontCare; if (SUCCEEDED(hr)) { static const WCHAR filename[] = L"output.png"; hr = pStream->InitializeFromFilename(filename, GENERIC_WRITE); } if (SUCCEEDED(hr)) { hr = 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(imageWidth, imageHeight); } 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(); } SafeRelease(&pWICFactory); SafeRelease(&pD2DFactory); SafeRelease(&pDWriteFactory); SafeRelease(&pWICBitmap); SafeRelease(&pRT); SafeRelease(&pTextFormat); SafeRelease(&pSolidBrush); SafeRelease(&pEncoder); SafeRelease(&pFrameEncode); SafeRelease(&pStream); return hr; }
int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = S_OK; // Create a Direct2D factory. CHECKHR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &g_pD2DFactory)); // Create a DirectWrite factory. CHECKHR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&g_pDWriteFactory))); // Create a WIC factory IWICImagingFactory* pWICFactory = NULL; CHECKHR(CoInitialize(nullptr)); CHECKHR(CoCreateInstance(CLSID_WICImagingFactory1, nullptr, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pWICFactory)); // Create an IWICBitmap and RT IWICBitmap* pWICBitmap = NULL; static const UINT sc_bitmapWidth = 640; static const UINT sc_bitmapHeight = 480; CHECKHR(pWICFactory->CreateBitmap(sc_bitmapWidth, sc_bitmapHeight, GUID_WICPixelFormat32bppBGR, WICBitmapCacheOnLoad, &pWICBitmap)); // Set the render target type to D2D1_RENDER_TARGET_TYPE_DEFAULT to use software rendering. CHECKHR(g_pD2DFactory->CreateWicBitmapRenderTarget( pWICBitmap, D2D1::RenderTargetProperties(), &g_pRenderTarget)); // Create text format and a path geometry representing an hour glass. IDWriteTextFormat* pTextFormat = NULL; static const WCHAR sc_fontName[] = L"Calibri"; static const FLOAT sc_fontSize = 50; CHECKHR(g_pDWriteFactory->CreateTextFormat( sc_fontName, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, sc_fontSize, L"", //locale &pTextFormat )); CHECKHR(pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); CHECKHR(pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); // Create a path geometry ID2D1PathGeometry* pPathGeometry = NULL; ID2D1GeometrySink* pSink = NULL; CHECKHR(g_pD2DFactory->CreatePathGeometry(&pPathGeometry)); CHECKHR(pPathGeometry->Open(&pSink)); pSink->SetFillMode(D2D1_FILL_MODE_ALTERNATE); pSink->BeginFigure( D2D1::Point2F(0, 0), D2D1_FIGURE_BEGIN_FILLED ); pSink->AddLine(D2D1::Point2F(200, 0)); pSink->AddBezier( D2D1::BezierSegment( D2D1::Point2F(150, 50), D2D1::Point2F(150, 150), D2D1::Point2F(200, 200)) ); pSink->AddLine(D2D1::Point2F(0, 200)); pSink->AddBezier( D2D1::BezierSegment( D2D1::Point2F(50, 150), D2D1::Point2F(50, 50), D2D1::Point2F(0, 0)) ); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); CHECKHR(pSink->Close()); // Create GradientStopCollection ID2D1GradientStopCollection *pGradientStops = NULL; static const D2D1_GRADIENT_STOP stops[] = { { 0.f, { 0.f, 1.f, 1.f, 1.f } }, { 1.f, { 0.f, 0.f, 1.f, 1.f } }, }; CHECKHR(g_pRenderTarget->CreateGradientStopCollection( stops, ARRAYSIZE(stops), &pGradientStops )); ID2D1LinearGradientBrush* pLGBrush = NULL; ID2D1SolidColorBrush* pBlackBrush = NULL; CHECKHR(g_pRenderTarget->CreateLinearGradientBrush( D2D1::LinearGradientBrushProperties( D2D1::Point2F(100, 0), D2D1::Point2F(100, 200)), D2D1::BrushProperties(), pGradientStops, &pLGBrush )); CHECKHR(g_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Black), &pBlackBrush )); // Render into the bitmap. g_pRenderTarget->BeginDraw(); g_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White)); D2D1_SIZE_F rtSize = g_pRenderTarget->GetSize(); // Set the world transform to a 45 degree rotation at the center of the render target // and write "Hello, World". g_pRenderTarget->SetTransform( D2D1::Matrix3x2F::Rotation( 45, D2D1::Point2F( rtSize.width / 2, rtSize.height / 2)) ); static const WCHAR sc_helloWorld[] = L"Hello, World!"; g_pRenderTarget->DrawText( sc_helloWorld, ARRAYSIZE(sc_helloWorld) - 1, pTextFormat, D2D1::RectF(0, 0, rtSize.width, rtSize.height), pBlackBrush); // Reset back to the identity transform. g_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Translation(0, rtSize.height - 200)); g_pRenderTarget->FillGeometry(pPathGeometry, pLGBrush); g_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Translation(rtSize.width - 200, 0)); g_pRenderTarget->FillGeometry(pPathGeometry, pLGBrush); CHECKHR(g_pRenderTarget->EndDraw()); // Save the image to a file. IWICStream* pStream = NULL; CHECKHR(pWICFactory->CreateStream(&pStream)); // Use InitializeFromFilename to write to a file. If there is need to write inside the memory, use InitializeFromMemory. static const WCHAR filename[] = L"output.png"; CHECKHR(pStream->InitializeFromFilename(filename, GENERIC_WRITE)); IWICBitmapEncoder* pEncoder = NULL; CHECKHR(pWICFactory->CreateEncoder(GUID_ContainerFormatPng, NULL, &pEncoder)); CHECKHR(pEncoder->Initialize(pStream, WICBitmapEncoderNoCache)); IWICBitmapFrameEncode* pFrameEncode = NULL; CHECKHR(pEncoder->CreateNewFrame(&pFrameEncode, NULL)); // Use IWICBitmapFrameEncode to encode the bitmap into the picture format you want. CHECKHR(pFrameEncode->Initialize(NULL)); CHECKHR(pFrameEncode->SetSize(sc_bitmapWidth, sc_bitmapHeight)); WICPixelFormatGUID format = GUID_WICPixelFormatDontCare; CHECKHR(pFrameEncode->SetPixelFormat(&format)); CHECKHR(pFrameEncode->WriteSource(pWICBitmap, NULL)); CHECKHR(pFrameEncode->Commit()); CHECKHR(pEncoder->Commit()); return 0; }