Exemple #1
0
    bool Renderable::BuildBitmap(Displayer* displayer, DanmakuConfig* config) {
        if (!HasTextLayout())
            return false;

        ComPtr<ID2D1Factory1> d2dFactory = displayer->GetD2DFactory();
        if (nullptr == d2dFactory)
            return false;

        ComPtr<ID2D1Bitmap1> bmp = displayer->CreateBitmap(
            static_cast<uint32_t>(mDanmaku->mTextWidth),
            static_cast<uint32_t>(mDanmaku->mTextHeight)
        );
        if (bmp == nullptr)
            return false;

        float strokeWidth = 1.5f * config->FontScaleFactor;
        strokeWidth *= displayer->GetDpiY() / 96.0f;

        ComPtr<ID2D1DeviceContext> deviceContext = displayer->AcquireDeviceContext(bmp);
        if (deviceContext == nullptr)
            return false;

        deviceContext->BeginDraw();

        deviceContext->Clear();
        deviceContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);

        ComPtr<ID2D1SolidColorBrush> brush;
        deviceContext->CreateSolidColorBrush(D2D1::ColorF(mDanmaku->mTextColor), &brush);

        ComPtr<ID2D1SolidColorBrush> outlineBrush;
        deviceContext->CreateSolidColorBrush(D2D1::ColorF(mDanmaku->mTextShadowColor, 1.0f), &outlineBrush);

        switch (config->DanmakuStyle) {
            case kOutline: {
                ComPtr<OutlineTextRenderer> textRenderer(new OutlineTextRenderer(d2dFactory, deviceContext, outlineBrush, strokeWidth, brush));
                mTextLayout->Draw(deviceContext.Get(), textRenderer.Get(), 0.0f, 0.0f);
                break;
            }
            case kProjection: {
                deviceContext->DrawTextLayout(D2D1::Point2F(1.0f, 1.0f), mTextLayout.Get(), outlineBrush.Get());
                deviceContext->DrawTextLayout(D2D1::Point2F(0.0f, 0.0f), mTextLayout.Get(), brush.Get());
                break;
            }
        }

        HRESULT hr = deviceContext->EndDraw();

        displayer->ReleaseDeviceContext(deviceContext);

        if (FAILED(hr))
            return false;

        mBitmap = bmp;
        mBitmapValidFlag = config->BitmapValidFlag;
        return true;
    }
// Renders one frame.
void DirectXLatencyRenderer::Render()
{
    ComPtr<ID2D1DeviceContext1> deviceContext = m_deviceResources->GetD2DDeviceContext();

    deviceContext->BeginDraw();

    // Rotate the rendered scene based on the current orientation of the device.
    deviceContext->SetTransform(m_deviceResources->GetOrientationTransform2D());

    // Draw the circle at its current position.
    deviceContext->FillEllipse(
        m_ellipse,
        m_brush.Get()
        );

    // We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
    // is lost. It will be handled during the next call to Present.
    HRESULT hr = deviceContext->EndDraw();
    if (hr != D2DERR_RECREATE_TARGET)
    {
        DX::ThrowIfFailed(hr);
    }
}
Exemple #3
0
	virtual bool get_bitmap(unicode_char chnum, bitmap_argb32 &bitmap, std::int32_t &width, std::int32_t &xoffs, std::int32_t &yoffs) override
	{
		const int MEM_ALIGN_CONST = 31;
		const int BITMAP_PAD = 50;

		HRESULT result;
		UINT cbData;
		BYTE* pixels = nullptr;

		ComPtr<ID2D1BitmapRenderTarget>     target;
		ComPtr<ID2D1SolidColorBrush>        pWhiteBrush;
		ComPtr<IWICBitmap>                  wicBitmap;
		ComPtr<IWICBitmapLock>              lock;

		ComPtr<IDWriteFontFace> face;
		HR_RET0(m_font->CreateFontFace(face.GetAddressOf()));

		// get the GDI metrics
		DWRITE_FONT_METRICS gdi_metrics;
		HR_RET0(face->GetGdiCompatibleMetrics(
			m_fontEmHeightInDips,
			1.0f,
			nullptr,
			&gdi_metrics));

		FontDimensionFactory fdf(gdi_metrics.designUnitsPerEm, m_fontEmHeightInDips);

		UINT32 tempChar = chnum;
		UINT16 glyphIndex;
		HR_RET0(face->GetGlyphIndicesW(&tempChar, 1, &glyphIndex));

		// get the width of this character
		DWRITE_GLYPH_METRICS glyph_metrics = { 0 };
		HR_RET0(face->GetGdiCompatibleGlyphMetrics(
			m_fontEmHeightInDips,
			1.0f,
			nullptr,
			FALSE,
			&glyphIndex,
			1,
			&glyph_metrics));

		// The height is the ascent added to the descent
		// By definition, the Em is equal to Cell Height minus Internal Leading (topSide bearing).
		//auto cellheight = fdf.FromDesignUnit(gdi_metrics.ascent + gdi_metrics.descent + gdi_metrics.);
		auto ascent = fdf.FromDesignUnit(gdi_metrics.ascent);
		auto descent = fdf.FromDesignUnit(gdi_metrics.descent);
		auto charHeight = ascent + descent;

		auto abc = fdf.CreateAbcWidths(
			glyph_metrics.advanceWidth,
			glyph_metrics.leftSideBearing,
			glyph_metrics.rightSideBearing);

		width = abc.abcA().Dips() + abc.abcB().Dips() + abc.abcC().Dips();

		// determine desired bitmap size
		int bmwidth = (BITMAP_PAD + abc.abcA().Dips() + abc.abcB().Dips() + abc.abcC().Dips() + BITMAP_PAD + MEM_ALIGN_CONST) & ~MEM_ALIGN_CONST;
		int bmheight = BITMAP_PAD + charHeight.Dips() + BITMAP_PAD;

		// GUID_WICPixelFormat8bppAlpha is 8 bits per pixel
		const REFWICPixelFormatGUID     source_bitmap_wic_format = GUID_WICPixelFormat8bppAlpha;
		const DXGI_FORMAT               source_bitmap_dxgi_format = DXGI_FORMAT_A8_UNORM;
		const D2D1_ALPHA_MODE           source_bitmap_d2d_alpha_mode = D2D1_ALPHA_MODE_STRAIGHT;

		// describe the bitmap we want
		HR_RET0(m_wicFactory->CreateBitmap(
			bmwidth,
			bmheight,
			source_bitmap_wic_format,
			WICBitmapCacheOnLoad,
			wicBitmap.GetAddressOf()));

		D2D1_RENDER_TARGET_PROPERTIES targetProps;
		targetProps = D2D1::RenderTargetProperties();
		targetProps.pixelFormat = D2D1::PixelFormat(source_bitmap_dxgi_format, source_bitmap_d2d_alpha_mode);

		// create a DIB to render to
		HR_RET0(this->m_d2dfactory->CreateWicBitmapRenderTarget(
			wicBitmap.Get(),
			&targetProps,
			reinterpret_cast<ID2D1RenderTarget**>(target.GetAddressOf())));

		target->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);

		// Create our brush
		HR_RET0(target->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), pWhiteBrush.GetAddressOf()));

		// Signal the start of the frame
		target->BeginDraw();

		// clear the bitmap
		// In the alpha mask, it will look like 0x00 per pixel
		target->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));

		// now draw the character
		DWRITE_GLYPH_RUN run = { nullptr };
		DWRITE_GLYPH_OFFSET offsets;
		offsets.advanceOffset = 0;
		offsets.ascenderOffset = 0;
		float advanceWidth = abc.advanceWidth().Dips();

		run.fontEmSize = m_fontEmHeightInDips;
		run.fontFace = face.Get();
		run.glyphCount = 1;
		run.glyphIndices = &glyphIndex;
		run.glyphAdvances = &advanceWidth;
		run.glyphOffsets = &offsets;

		auto baseline_origin = D2D1::Point2F(BITMAP_PAD + abc.abcA().Dips() + 1, BITMAP_PAD + ascent.Dips());
		target->DrawGlyphRun(
			baseline_origin,
			&run,
			pWhiteBrush.Get(),
			DWRITE_MEASURING_MODE_GDI_CLASSIC);

		HR_RET0(target->EndDraw());

#ifdef DWRITE_DEBUGGING
		// Save to file for debugging
		SaveBitmap(wicBitmap.Get(), GUID_WICPixelFormatBlackWhite, L"C:\\temp\\ddraw_step1.bmp");
#endif

		// characters are expected to be full-height
		rectangle actbounds;
		actbounds.min_y = BITMAP_PAD;
		actbounds.max_y = BITMAP_PAD + charHeight.Dips() - 1;

		// Lock the bitmap and get the data pointer
		WICRect rect = { 0, 0, bmwidth, bmheight };
		HR_RET0(wicBitmap->Lock(&rect, WICBitmapLockRead, lock.GetAddressOf()));
		HR_RET0(lock->GetDataPointer(&cbData, static_cast<BYTE**>(&pixels)));

		// determine the actual left of the character
		for (actbounds.min_x = 0; actbounds.min_x < bmwidth; actbounds.min_x++)
		{
			BYTE *offs = pixels + actbounds.min_x;
			UINT8 summary = 0;
			for (int y = 0; y < bmheight; y++)
				summary |= offs[y * bmwidth];
			if (summary != 0)
			{
				break;
			}
		}

		// determine the actual right of the character
		// Start from the right edge, and move in until we find a pixel
		for (actbounds.max_x = bmwidth - 1; actbounds.max_x >= 0; actbounds.max_x--)
		{
			BYTE *offs = pixels + actbounds.max_x;
			UINT8 summary = 0;

			// Go through the entire column and build a summary
			for (int y = 0; y < bmheight; y++)
				summary |= offs[y * bmwidth];
			if (summary != 0)
			{
				break;
			}
		}

		// allocate a new bitmap
		if (actbounds.max_x >= actbounds.min_x && actbounds.max_y >= actbounds.min_y)
		{
			bitmap.allocate(actbounds.max_x + 1 - actbounds.min_x, actbounds.max_y + 1 - actbounds.min_y);

			// copy the bits into it
			for (int y = 0; y < bitmap.height(); y++)
			{
				UINT32 *dstrow = &bitmap.pix32(y);
				UINT8 *srcrow = &pixels[(y + actbounds.min_y) * bmwidth];
				for (int x = 0; x < bitmap.width(); x++)
				{
					int effx = x + actbounds.min_x;
					dstrow[x] = rgb_t(srcrow[effx], 0xff, 0xff, 0xff);
				}
			}

			// set the final offset values
			xoffs = actbounds.min_x - (BITMAP_PAD + abc.abcA().Dips());
			yoffs = actbounds.max_y - (BITMAP_PAD + ascent.Dips());

#ifdef DWRITE_DEBUGGING
			SaveBitmap2(bitmap, L"C:\\temp\\dwrite_final.bmp");
#endif
		}

		BOOL success = bitmap.valid();

#ifdef DWRITE_DEBUGGING
		osd_printf_debug(
			"dwr: %s, c'%S' w%i x%i y%i asc%i dsc%i a%ib%ic%i\n",
			success ? "Success" : "Error",
			(WCHAR*)&chnum,
			width,
			xoffs,
			yoffs,
			ascent.Dips(),
			descent.Dips(),
			abc.abcA().Dips(),
			abc.abcB().Dips(),
			abc.abcC().Dips());
#endif
		return success;
	}
Exemple #4
0
//
// Saves the current image
//
HRESULT SimpleImage::Save(__in IShellItem *saveAsItem)
{
    ComPtr<IWICImagingFactory> wicFactory;
    ComPtr<ID2D1Factory> d2dFactory;
    ComPtr<IWICBitmap> wicBitmap;
    ComPtr<ID2D1RenderTarget> wicRenderTarget;

    // Clear backup information from previous save
    m_imageInfo.backupFileName.clear();

    // Don't save if there are no image operations applied to this image unless the user specifed 'Save As'
    if (m_imageOperations.empty() && nullptr == saveAsItem)
    {
        return S_OK;
    }

    HRESULT hr = Direct2DUtility::GetWICFactory(&wicFactory);
    if (SUCCEEDED(hr))
    {
        hr = Direct2DUtility::GetD2DFactory(&d2dFactory);
    }

    // Get the original bitmap rectangle in terms of the current crop
    D2D1_RECT_F originalBitmapRect =
        D2D1::RectF(0, 0, Direct2DUtility::GetRectWidth(m_clipRect), Direct2DUtility::GetRectHeight(m_clipRect));

    // Adjust height and width based on current orientation and clipping rectangle
    float width = m_isHorizontal ? Direct2DUtility::GetRectWidth(m_clipRect) : Direct2DUtility::GetRectHeight(m_clipRect);
    float height = m_isHorizontal ? Direct2DUtility::GetRectHeight(m_clipRect) : Direct2DUtility::GetRectWidth(m_clipRect);

    if (SUCCEEDED(hr))
    {
        // Create WIC bitmap for rendering
        hr = wicFactory->CreateBitmap(
            static_cast<unsigned int>(width),
            static_cast<unsigned int>(height),
            GUID_WICPixelFormat32bppBGR,
            WICBitmapCacheOnLoad,
            &wicBitmap);
    }

    if (SUCCEEDED(hr))
    {
        hr = d2dFactory->CreateWicBitmapRenderTarget(wicBitmap, D2D1::RenderTargetProperties(), &wicRenderTarget);
    }

    if (SUCCEEDED(hr))
    {
        // Replace current bitmap with one that's compatible with the WIC render target
        if (m_bitmap)
        {
            m_bitmap = nullptr;
        }
        
        hr = Direct2DUtility::LoadBitmapFromFile(wicRenderTarget, m_imageInfo.fileName.c_str(), 0, 0, &m_bitmap);
    }

    if (SUCCEEDED(hr))
    {
        // When rotating images make sure that the point around which rotation occurs lines
        // up with the center of the rotated render target
        if (false == m_isHorizontal)
        {
            float offsetX;
            float offsetY;

            if (width > height)
            {
                offsetX = (width - height) / 2;
                offsetY = -offsetX;
            }
            else
            {
                offsetY = (height - width) / 2;
                offsetX = - offsetY;
            }

            D2D1_MATRIX_3X2_F translation = D2D1::Matrix3x2F::Translation(offsetX, offsetY);
            wicRenderTarget->SetTransform(translation);
        }

        // Update current render target to point to WIC render target
        m_currentRenderTarget = wicRenderTarget;

        // Draw updated image to WIC render target
        wicRenderTarget->BeginDraw();
        DrawImage(originalBitmapRect, m_clipRect, true);
        wicRenderTarget->EndDraw();
    }

    if (SUCCEEDED(hr) && !m_imageOperations.empty())
    {
        // Create copy of original image unless the user is simply using 'Save As'
        std::wstring backupPath(m_imageInfo.fileName);
        backupPath.insert(backupPath.find_last_of('\\'), L"\\AnnotatorBackup");

        std::wstring backupDirectory(backupPath.substr(0, backupPath.find_last_of('\\')));

        // Create backup directory if needed
        if (false == ::CreateDirectoryW(backupDirectory.c_str(), nullptr))
        {
            hr = (GetLastError() == ERROR_ALREADY_EXISTS) ? S_OK : E_FAIL;
        }
    
        if (SUCCEEDED(hr))
        {
            // Do not copy if the backup file already exists
            if (false == ::CopyFile(m_imageInfo.fileName.c_str(), backupPath.c_str(), true))
            {
                hr = (GetLastError() == ERROR_FILE_EXISTS) ? S_OK : E_FAIL;
            }
            else
            {
                // Capture name of backup file
                m_imageInfo.backupFileName.assign(backupPath);
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        if (nullptr == saveAsItem)
        {
            // Save updated file
            hr = Direct2DUtility::SaveBitmapToFile(wicBitmap, m_imageInfo.fileName.c_str());
        }
        else
        {
            // Save updated file as the specifed shell item
            wchar_t * saveAsFileName;
            hr = saveAsItem->GetDisplayName(SIGDN_FILESYSPATH, &saveAsFileName);

            if (SUCCEEDED(hr))
            {
                hr = Direct2DUtility::SaveBitmapToFile(wicBitmap, m_imageInfo.fileName.c_str(), saveAsFileName);

                ::CoTaskMemFree(saveAsFileName);
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        // Force image to reload
        hr = DiscardResources();
        m_isHorizontal = true;

        if (nullptr == saveAsItem)
        {
            // Clear all undo operations
            m_imageOperations.clear();

            // Empty redo stack
            while (!m_redoStack.empty())
            {
                m_redoStack.pop();
            }
        }
    }

    return hr;
}
// Renders one frame. This method draws a small ruler at the correct physical size.
// The distance ticks on the ruler should be accurate, regardless of any user or 
// OS DPI settings.
void PhysicalDpiRenderer::Render()
{
    ComPtr<ID2D1DeviceContext1> deviceContext = m_deviceResources->GetD2DDeviceContext();

    // Retrieve the physical DPI (raw DPI) of the current display. This is the actual pixel density
    // reported by the display. These values do not change based on any user or OS settings.
    DisplayInformation^ displayInfo = DisplayInformation::GetForCurrentView();
    float rawDpiX = displayInfo->RawDpiX;
    float rawDpiY = displayInfo->RawDpiY;

    // Save the device context's current DPI so that it can be restored later.
    float savedDpiX;
    float savedDpiY;
    deviceContext->GetDpi(&savedDpiX, &savedDpiY);

    // Set the device context's DPI to the physical DPI. Note that the DisplayInformation object
    // returns 0 for RawDpiX and RawDpiY if the monitor does not report a DPI or if the monitor
    // is being run in Duplicate mode. Passing 0 values to SetDpi specifies the factory-read system
    // DPI.
    deviceContext->SetDpi(rawDpiX, rawDpiY);

    // Compute the physical size (in DIPs) of the current display from its pixel size. The physical
    // size is useful when laying out elements based on physical DPI.
    Size pixelSize = m_deviceResources->GetOutputSize();
    Size physicalSize(
        (pixelSize.Width / displayInfo->RawDpiX) * 96.0f,
        (pixelSize.Height / displayInfo->RawDpiY) * 96.0f
        );

    deviceContext->BeginDraw();

    // Set the transform to the physical (raw) orientation transform, so that content rendered
    // according to the physical DPI is rotated and translated appropriately.
    deviceContext->SetTransform(m_deviceResources->GetRawOrientationTransform2D());

    // Draw a horizontal ruler: a rectangle five inches wide and half an inch tall. By
    // using the physical DPI, this ruler will have this size regardless of any user or
    // OS DPI settings.
    float xPos = ConvertInchesToDips(1.0f);
    float yPos = ConvertInchesToDips(1.0f);
    float width = ConvertInchesToDips(5.0f);
    float height = ConvertInchesToDips(0.5f);
    deviceContext->FillRectangle(
        D2D1::RectF(xPos, yPos, xPos + width, yPos + height),
        m_goldBrush.Get()
        );

    // Draw ticks to denote the one-inch intervals on the ruler.
    for (float tick = 1.0f; tick <= 4.0f; tick += 1.0f)
    {
        deviceContext->DrawLine(
            D2D1::Point2F(xPos + ConvertInchesToDips(tick), yPos),
            D2D1::Point2F(xPos + ConvertInchesToDips(tick), yPos + ConvertInchesToDips(0.2f)),
            m_blackBrush.Get()
            );
    }

    // Draw ticks to denote the half-inch intervals on the ruler.
    for (float tick = 0.5f; tick <= 4.5f; tick += 1.0f)
    {
        deviceContext->DrawLine(
            D2D1::Point2F(xPos + ConvertInchesToDips(tick), yPos),
            D2D1::Point2F(xPos + ConvertInchesToDips(tick), yPos + ConvertInchesToDips(0.1f)),
            m_blackBrush.Get()
            );
    }

    // Draw ticks to denote the quarter-inch intervals on the ruler.
    for (float tick = 0.25f; tick <= 4.75f; tick += 0.5f)
    {
        deviceContext->DrawLine(
            D2D1::Point2F(xPos + ConvertInchesToDips(tick), yPos),
            D2D1::Point2F(xPos + ConvertInchesToDips(tick), yPos + ConvertInchesToDips(0.05f)),
            m_blackBrush.Get()
            );
    }

    // Draw a vertical ruler: a rectangle 3 inches tall and half an inch wide. By
    // using the physical DPI, this ruler will have this size regardless of any user or
    // OS DPI settings.
    xPos = ConvertInchesToDips(1.0f);
    yPos = ConvertInchesToDips(2.0f);
    width = ConvertInchesToDips(0.5f);
    height = ConvertInchesToDips(3.0f);
    deviceContext->FillRectangle(
        D2D1::RectF(xPos, yPos, xPos + width, yPos + height),
        m_goldBrush.Get()
        );

    // Draw ticks to denote the one-inch intervals on the ruler.
    for (float tick = 1.0f; tick <= 2.0f; tick += 1.0f)
    {
        deviceContext->DrawLine(
            D2D1::Point2F(xPos, yPos + ConvertInchesToDips(tick)),
            D2D1::Point2F(xPos + ConvertInchesToDips(0.2f), yPos + ConvertInchesToDips(tick)),
            m_blackBrush.Get()
            );
    }

    // Draw ticks to denote the half-inch intervals on the ruler.
    for (float tick = 0.5f; tick <= 2.5f; tick += 1.0f)
    {
        deviceContext->DrawLine(
            D2D1::Point2F(xPos, yPos + ConvertInchesToDips(tick)),
            D2D1::Point2F(xPos + ConvertInchesToDips(0.1f), yPos + ConvertInchesToDips(tick)),
            m_blackBrush.Get()
            );
    }

    // Draw ticks to denote the quarter-inch intervals on the ruler.
    for (float tick = 0.25f; tick <= 2.75f; tick += 0.5f)
    {
        deviceContext->DrawLine(
            D2D1::Point2F(xPos, yPos + ConvertInchesToDips(tick)),
            D2D1::Point2F(xPos + ConvertInchesToDips(0.05f), yPos + ConvertInchesToDips(tick)),
            m_blackBrush.Get()
            );
    }

    // Draw a message explaining the sizes of the rulers.
    String^ message = "The ruler above is 5 inches long. The ruler to the left is 3 "
        "inches long. Their sizes are independent of user and OS DPI settings.";

    xPos = ConvertInchesToDips(2.0f);
    yPos = ConvertInchesToDips(2.0f);
    deviceContext->DrawText(
        message->Data(),
        message->Length(),
        m_textFormat.Get(),
        D2D1::RectF(xPos, yPos, physicalSize.Width - ConvertInchesToDips(1.0f), physicalSize.Height - ConvertInchesToDips(1.0f)),
        m_whiteBrush.Get()
        );

    // We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
    // is lost. It will be handled during the next call to Present.
    HRESULT hr = deviceContext->EndDraw();
    if (hr != D2DERR_RECREATE_TARGET)
    {
        DX::ThrowIfFailed(hr);
    }

    // Restore the DPI Direct2D was using. The rest of the sample will be drawn using the
    // logical DPI, rather than the physical DPI, so it will change size in accordance
    // with user DPI settings.
    deviceContext->SetDpi(savedDpiX, savedDpiY);
}