// 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);
    }
Exemple #2
0
    virtual HRESULT STDMETHODCALLTYPE CreateTextFormat(
        WCHAR const* fontFamilyName,
        IDWriteFontCollection* fontCollection,
        DWRITE_FONT_WEIGHT fontWeight,
        DWRITE_FONT_STYLE fontStyle,
        DWRITE_FONT_STRETCH fontStretch,
        FLOAT fontSize,
        WCHAR const* localeName,
        IDWriteTextFormat** textFormat
        )
    {
        UINT32 index;
        BOOL exists;
        HRESULT result;
        OutputDebugString("delegate_dwritefactory::CreateTextFormat");
        OutputDebugStringW(fontFamilyName);
        EnterCriticalSection(&collcs);
        result = this->mycoll->FindFamilyName(fontFamilyName, &index, &exists);
        if (SUCCEEDED(result)) {
            result = E_FAIL;
            if (exists != FALSE) {
                OutputDebugString("delegate_dwritefactory::CreateTextFormat -> mycoll");
                result = orig_this->CreateTextFormat(fontFamilyName, this->mycoll, fontWeight, fontStyle, fontStretch, fontSize, localeName, textFormat);
            }
        }
        LeaveCriticalSection(&collcs);
	if (FAILED(result)) {
            OutputDebugString("delegate_dwritefactory::CreateTextFormat -> fallback");
            result = orig_this->CreateTextFormat(fontFamilyName, fontCollection, fontWeight, fontStyle, fontStretch, fontSize, localeName, textFormat);
        }
        return result;
    }
D3D11CanvasWindowGraphicsPtr D3D11CanvasWindowGraphics::Create(
	HWND hWnd, D3D11DriverPtr driver, CameraPtr camera)
{
	D3D11CanvasWindowGraphicsPtr p(new D3D11CanvasWindowGraphics);

	p->m_hWnd = hWnd;
	p->m_driver = driver;
	p->m_camera = camera;

	ID3D11Device* pDevice = p->m_driver->GetD3D11Device();
	IDXGIFactory1* pDXGIFactory = p->m_driver->GetDXGIFactory();
	IDWriteFactory* pDWriteFactory = p->m_driver->GetDWriteFactory();

	// Create text format

	CHECK_HR(pDWriteFactory->CreateTextFormat(
		L"Calibri", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
		DWRITE_FONT_STRETCH_NORMAL, 24.0f, L"en-US", p->m_textFormat.Receive()));

	// Create swap chain

	DXGI_SWAP_CHAIN_DESC scd = { 0 };
	scd.BufferDesc.Format = BACKBUFFER_FORMAT;
	scd.SampleDesc.Count = 1;
	scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	scd.BufferCount = 1;
	scd.OutputWindow = hWnd;
	scd.Windowed = TRUE;
	CHECK_HR(pDXGIFactory->CreateSwapChain(pDevice, &scd, p->m_swapChain.Receive()));

	p->CreateSwapChainResources();

	return p;
}
    bool setupLayout (const AttributedString& text, const float maxWidth, const float maxHeight,
                      ID2D1RenderTarget& renderTarget, IDWriteFactory& directWriteFactory,
                      IDWriteFontCollection& fontCollection, ComSmartPtr<IDWriteTextLayout>& textLayout)
    {
        // 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

        Font defaultFont;
        BOOL fontFound = false;
        uint32 fontIndex;
        fontCollection.FindFamilyName (defaultFont.getTypeface()->getName().toWideCharPointer(), &fontIndex, &fontFound);

        if (! fontFound)
            fontIndex = 0;

        ComSmartPtr<IDWriteFontFamily> dwFontFamily;
        HRESULT hr = fontCollection.GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress());

        ComSmartPtr<IDWriteFont> dwFont;
        hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL,
                                                 dwFont.resetAndGetPointerAddress());
        jassert (dwFont != nullptr);

        const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont);

        ComSmartPtr<IDWriteTextFormat> dwTextFormat;
        hr = directWriteFactory.CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), &fontCollection,
                                                  DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
                                                  defaultFont.getHeight() * defaultFontHeightToEmSizeFactor,
                                                  L"en-us", dwTextFormat.resetAndGetPointerAddress());

        setTextFormatProperties (text, *dwTextFormat);

        {
            DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 };
            ComSmartPtr<IDWriteInlineObject> trimmingSign;
            hr = directWriteFactory.CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress());
            hr = dwTextFormat->SetTrimming (&trimming, trimmingSign);
        }

        const int textLen = text.getText().length();

        hr = directWriteFactory.CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat,
                                                  maxWidth, maxHeight, textLayout.resetAndGetPointerAddress());

        if (FAILED (hr) || textLayout == nullptr)
            return false;

        const int numAttributes = text.getNumAttributes();

        for (int i = 0; i < numAttributes; ++i)
            addAttributedRange (*text.getAttribute (i), *textLayout, textLen, renderTarget, fontCollection);

        return true;
    }
IDWriteTextFormat* MWinDeviceImpl::GetTextFormat()
{
	if (mTextFormat == nil)
	{
		THROW_IF_HRESULT_ERROR(
			sDWFactory->CreateTextFormat(
				mFont.c_str(),                // 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,
				mFontSize,
				L"en-us",
				&mTextFormat
			));
	}
	
	return mTextFormat;
}
Exemple #6
0
bool CDirectWriteFont::Create(CDirectWriteRenderer &Renderer, const LOGFONT &lf)
{
	Destroy();

	IDWriteFactory *pFactory = Renderer.GetSystem().GetDWriteFactory();

	if (pFactory == nullptr)
		return false;

	float FontSize;
	if (lf.lfHeight < 0 || Renderer.GetDC() == nullptr) {
		FontSize = static_cast<float>(std::abs(lf.lfHeight));
	} else {
		HDC hdc = Renderer.GetDC();
		HFONT hfont = ::CreateFontIndirect(&lf);
		HGDIOBJ hOldFont = ::SelectObject(hdc, hfont);
		TEXTMETRIC tm;
		::GetTextMetrics(hdc, &tm);
		FontSize = static_cast<float>(tm.tmHeight - tm.tmInternalLeading);
		::SelectObject(hdc, hOldFont);
		::DeleteObject(hfont);
	}

	IDWriteTextFormat *pTextFormat;
	HRESULT hr = pFactory->CreateTextFormat(
		lf.lfFaceName,
		nullptr,
		static_cast<DWRITE_FONT_WEIGHT>(lf.lfWeight),
		lf.lfItalic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
		DWRITE_FONT_STRETCH_NORMAL,
		FontSize,
		L"",
		&pTextFormat);
	if (SUCCEEDED(hr)) {
		m_pTextFormat = pTextFormat;
		m_LogFont = lf;
	}

	pFactory->Release();

	return true;
}
Exemple #7
0
    HRESULT
DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize)
{
    // Most of this function is copy from: http://msdn.microsoft.com/en-us/library/windows/desktop/dd941783(v=vs.85).aspx
    HRESULT hr = S_OK;

    IDWriteFont *font = NULL;
    IDWriteFontFamily *fontFamily = NULL;
    IDWriteLocalizedStrings *localizedFamilyNames = NULL;

    if (SUCCEEDED(hr))
    {
	hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);
    }

    // Get the font family to which this font belongs.
    if (SUCCEEDED(hr))
    {
	hr = font->GetFontFamily(&fontFamily);
    }

    // Get the family names. This returns an object that encapsulates one or
    // more names with the same meaning but in different languages.
    if (SUCCEEDED(hr))
    {
	hr = fontFamily->GetFamilyNames(&localizedFamilyNames);
    }

    // Get the family name at index zero. If we were going to display the name
    // we'd want to try to find one that matched the use locale, but for
    // purposes of creating a text format object any language will do.

    wchar_t familyName[100];
    if (SUCCEEDED(hr))
    {
	hr = localizedFamilyNames->GetString(0, familyName,
		ARRAYSIZE(familyName));
    }

    if (SUCCEEDED(hr))
    {
	// If no font size was passed in use the lfHeight of the LOGFONT.
	if (fontSize == 0)
	{
	    // Convert from pixels to DIPs.
	    fontSize = PixelsToDipsY(logFont.lfHeight);
	    if (fontSize < 0)
	    {
		// Negative lfHeight represents the size of the em unit.
		fontSize = -fontSize;
	    }
	    else
	    {
		// Positive lfHeight represents the cell height (ascent +
		// descent).
		DWRITE_FONT_METRICS fontMetrics;
		font->GetMetrics(&fontMetrics);

		// Convert the cell height (ascent + descent) from design units
		// to ems.
		float cellHeight = static_cast<float>(
			fontMetrics.ascent + fontMetrics.descent)
					       / fontMetrics.designUnitsPerEm;

		// Divide the font size by the cell height to get the font em
		// size.
		fontSize /= cellHeight;
	    }
	}
    }

    // The text format includes a locale name. Ideally, this would be the
    // language of the text, which may or may not be the same as the primary
    // language of the user. However, for our purposes the user locale will do.
    wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
    if (SUCCEEDED(hr))
    {
	if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
	    hr = HRESULT_FROM_WIN32(GetLastError());
    }

    if (SUCCEEDED(hr))
    {
	// Create the text format object.
	hr = mDWriteFactory->CreateTextFormat(
		familyName,
		NULL, // no custom font collection
		font->GetWeight(),
		font->GetStyle(),
		font->GetStretch(),
		fontSize,
		localeName,
		&mTextFormat);
    }

    if (SUCCEEDED(hr))
    {
	mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
	mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
	    : DWRITE_FONT_STYLE_NORMAL;
    }

    SafeRelease(&localizedFamilyNames);
    SafeRelease(&fontFamily);
    SafeRelease(&font);

    return hr;
}
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;
}