Transparent glitches while drawing GIF - c++

I wrote some solution to draw gifs, using Direct2D GIF sample:
// somewhere in render loop
IWICBitmapFrameDecode* frame = nullptr;
IWICFormatConverter* converter = nullptr;
gifDecoder->GetFrame(current_frame, &frame);
if (frame)
{
d2dWICFactory->CreateFormatConverter(&converter);
if (converter)
{
ID2D1Bitmap* temp = nullptr;
converter->Initialize(frame, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteType::WICBitmapPaletteTypeCustom);
tar->CreateBitmapFromWicBitmap(converter, NULL, &temp);
scale->SetValue(D2D1_SCALE_PROP_SCALE, D2D1::Vector2F(1, 1));
scale->SetInput(0, temp);
target->DrawImage(scale);
SafeRelease(&temp);
}
}
SafeRelease(&frame);
SafeRelease(&converter);
Where gifDecoder is obtained this way:
d2dWICFactory->CreateDecoderFromFilename(pathToGif, NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &gifDecoder);
But in my program almost with all gifs, all frames, except the first one, for some reason have horizontal transparent lines. Obviously, when I view the same gif in browser or in another program there are no that holes.
I tried to change pixel format, pallet type, but that glitches are still there.
What do I do wrong?

Basic steps:
On image load, in case of gif, for caching purposes store not all frames as ready to draw ID2D1Bitmaps but only the image's decoder. Every time decode the frame, get bitmap and draw it. At least, at first animation loop frames bitmaps and metadata (again, for each frame) can be cached.
For each gif create compatible target with the size of gif screen (obtained from metadata) for composing.
Compose animation on render: get metadata for current frame -> if current frame has disposal = true, clear compose target -> draw current frame to compose target (even if disposal true) -> draw compose target to render target,
Note, that frames may have different sizes and even offsets.
Approximate code:
ID2D1DeviceContext *renderTarget;
class GIF
{
ID2D1BitmapRenderTarget *compose;
IWICBitmapDecoder *decoder; // it seems like precache all frames costs too much time, it will be faster to obtain each frame each time (maybe cache during first loop)
float naturalWidth; // gif screen width
float naturalHeight; // gif screen height
int framesCount;
int currentFrame;
unsigned int lastFrameEpoch;
int x;
int y;
int width; // image width needed to be drawn
int height; // image height
float fw; // scale factor width;
float fh; // scale factor height
GIF(const wchar_t *path, int x, int y, int width, int height)
{
// Init, using WIC obtain here image's decoder, naturalWidth, naturalHeight
// framesCount and other optional stuff like loop information
// using IWICMetadataQueryReader obtained from the whole image,
// not from zero frame
compose = nullptr;
currentFrame = 0;
lastFrameEpoch = epoch(); // implement Your millisecond function
Resize(width, height);
}
void Resize(int width, int height)
{
this->width = width;
this->height = height;
// calculate scale factors
fw = width / naturalWidth;
fh = height / naturalHeight;
if(compose == nullptr)
{
renderTarget->CreateCompatibleRenderTarget(D2D1::SizeF(naturalWidth, naturalHeight), &compose);
compose->Clear(D2D1::ColorF(0,0,0,0));
}
}
void Render()
{
IWICBitmapFrameDecode* frame = nullptr;
decoder->GetFrame(currentFrame, &frame);
IWICFormatConverter* converter = nullptr;
d2dWICFactory->CreateFormatConverter(&converter);
converter->Initialize(frame, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteType::WICBitmapPaletteTypeCustom);
IWICMetadataQueryReader* reader = nullptr;
frame->GetMetadataQueryReader(&reader);
PROPVARIANT propValue;
PropVariantInit(&propValue);
char disposal = 0;
float l = 0, t = 0; // frame offsets (may have)
HRESULT hr = S_OK;
reader->GetMetadataByName(L"/grctlext/Delay", &propValue);
if (SUCCEEDED((propValue.vt == VT_UI2 ? S_OK : E_FAIL)))
{
UINT frameDelay = 0;
UIntMult(propValue.uiVal, 10, &fr);
frameDelay = fr;
if (frameDelay < 16)
{
frameDelay = 16;
}
if(epoch() - lastFrameEpoch < frameDelay)
{
SafeRelease(&reader);
SafeRelease(&frame);
SafeRelease(&converter);
return;
}
}
if (SUCCEEDED(reader->GetMetadataByName(
L"/grctlext/Disposal",
&propValue)))
{
hr = (propValue.vt == VT_UI1) ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
disposal = propValue.bVal;
}
}
PropVariantClear(&propValue);
{
hr = reader->GetMetadataByName(L"/imgdesc/Left", &propValue);
if (SUCCEEDED(hr))
{
hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL);
if (SUCCEEDED(hr))
{
l = static_cast<FLOAT>(propValue.uiVal);
}
PropVariantClear(&propValue);
}
}
{
hr = reader->GetMetadataByName(L"/imgdesc/Top", &propValue);
if (SUCCEEDED(hr))
{
hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL);
if (SUCCEEDED(hr))
{
t = static_cast<FLOAT>(propValue.uiVal);
}
PropVariantClear(&propValue);
}
}
renderTarget->CreateBitmapFromWicBitmap(converter, NULL, &temp);
compose->BeginDraw();
if (disposal == 2)
compose->Clear(D2D1::ColorF(0, 0, 0, 0));
auto ss = temp->GetSize();
compose->DrawBitmap(temp, D2D1::RectF(l, t, l + ss.width, t + ss.height));
compose->EndDraw();
ID2D1Bitmap* composition = nullptr;
compose->GetBitmap(&composition);
// You need to create scale effect to have antialised resizing
scale->SetValue(D2D1_SCALE_PROP_SCALE, D2D1::Vector2F(fw, fh));
scale->SetInput(0, composition);
auto p = D2D1::Point2F(x, y);
renderTarget->DrawImage(iscale, p);
SafeRelease(&temp);
SafeRelease(&composition);
SafeRelease(&reader);
SafeRelease(&frame);
SafeRelease(&converter);
}
}*gif[100] = {nullptr};
inline void Render()
{
renderTarget->BeginDraw();
for(int i = 0; i < 100 && gif[i] != nullptr; i++)
gif[i]->Render();
renderTarget->EndDraw();
}

Related

GetPixel via DirectX 11 efficient implementation

I have this function that get a Pixel color from the screen with GDI+ API:
RGBTRIPLE GetPixelColorGDI(int x, int y)
{
RGBTRIPLE rgb;
HDC dc = GetDC(NULL);
COLORREF color = GetPixel(dc, x, y);
ReleaseDC(NULL, dc);
rgb.rgbtRed = GetRValue(color);
rgb.rgbtGreen = GetGValue(color);
rgb.rgbtBlue = GetBValue(color);
return rgb;
}
the function works perfectly but it is too slow.
For this reason I like to try to implement the equivalent in DirectX 11 (I don't have a good experience in DirectX in general) and please excuse me if it is incorrect, this in my first attempt:
/// <summary>
/// Get pixel color from screen (24 bit) via DirextX 11
/// </summary>
/// <param name="X"></param>
/// <param name="Y"></param>
/// <param name="swapchainDescription"></param>
RGBTRIPLE GetPixelColorDX11(int X, int Y, IDXGISwapChain* pSwapChain)
{
RGBTRIPLE rgb;
HRESULT hr = 0;
using Texture2D = ID3D11Texture2D*;
IDXGIResource* backbufferptr = nullptr;
ID3D11Resource* SourceResource = nullptr;
Texture2D DestResource = nullptr;
ID3D11Device* device = nullptr;
ID3D11DeviceContext* context = nullptr;
D3D11_MAPPED_SUBRESOURCE MappedSubresource;
hr = pSwapChain->GetBuffer(0, __uuidof(IDXGIResource), (void**)&backbufferptr);
if (hr < 0) {
return;
}
hr = backbufferptr->QueryInterface(__uuidof(ID3D11Resource), (void**)&SourceResource);
if (hr < 0) {
return;
}
hr = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
if (hr < 0) {
return;
}
DXGI_SWAP_CHAIN_DESC desc;
hr = pSwapChain->GetDesc(&desc);
if (hr < 0) {
return;
}
D3D11_TEXTURE2D_DESC TextureDesciption = { };
TextureDesciption.Format = desc.BufferDesc.Format;
TextureDesciption.Width = desc.BufferDesc.Width;
TextureDesciption.Height = desc.BufferDesc.Height;
TextureDesciption.MipLevels = 1;
TextureDesciption.ArraySize = 1;
TextureDesciption.SampleDesc.Count = 1;
TextureDesciption.Usage = D3D11_USAGE_STAGING;
TextureDesciption.BindFlags = 0;
TextureDesciption.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
TextureDesciption.MiscFlags = 0;
hr = device->CreateTexture2D(&TextureDesciption, nullptr, &DestResource);
if (hr < 0) {
return;
}
device->GetImmediateContext(&context);
if (!context) {
return;
}
context->CopyResource(DestResource, SourceResource);
context->Map(DestResource, 0, D3D11_MAP_READ, 0, &MappedSubresource);
COLORREF* pPixels = (COLORREF*)MappedSubresource.pData;
rgb.rgbtRed = (pPixels[0] >> 16) & 0xff;
rgb.rgbtGreen = (pPixels[0] >> 8) & 0xff;
rgb.rgbtBlue = pPixels[0] & 0xff;
return rgb;
}
In this code is missing where I can set in X and Y coordinate in pixel because I don't known how to do it.
Apart this I don't sure if this way is enough efficient to make this more faster then the GDI++
GetPixel version.
The idea is (I don't sure if is possible) is select only one pixel (x, y) on source texture and read the color on "pPixels[0]"
Update
I explain better why I need and why.
My hooked DLL hook the DX11 "present" and draw a rectangle using shader that work as "placeholder" on a game menu.
Drawing phase is very fast becouse use DX11 and there no any FPS drop.
To do it I use a library called "imgui".
Before draw the rectangle I need to known in what position I need to place the rectangle and this depend by the result of the actual GetPixelColorGDI that is slow.
The user press a arrow key to move the rectangle on another position.
I call "GetPixelColorGDI" from 1 to 11 times for single screen.
If the next slot is filled I call GetPixelColorGDI only one time, but if the next 10 slots are empty I need to call GetPixelColorGDI 10 times.
So best solution can be create GetPixelColorDX11 like this:
RGBTRIPLE GetPixelColorDX11(int X, int Y, IDXGISwapChain* pSwapChain)
and this overloald:
struct COORDINATE
{
int x;
int y;
}
RGBTRIPLE[] GetPixelColorDX11(COORDINATE[] Pixel , IDXGISwapChain* pSwapChain)
so this can cover all cases.
here my function that I use to initialize DirectX to draw the rectangles:
bool InitDirectX(IDXGISwapChain* pChain)
{
// Get swapchain
pSwapchain = pChain;
// Get device and context
HRESULT hr = pSwapchain->GetDevice(__uuidof(ID3D11Device), (PVOID*)&pDevice);
if (FAILED(hr))
{
std::cerr << "Failed to get device from swapchain" << std::endl;
return false;
}
pDevice->GetImmediateContext(&pContext);
// Get window from swapchain description
DXGI_SWAP_CHAIN_DESC swapchainDescription;
pSwapchain->GetDesc(&swapchainDescription);
hWindow = swapchainDescription.OutputWindow;
// Use SetWindowLongPtr to modify window behaviour and get input
wndProcHandlerOriginal = (WNDPROC)SetWindowLongPtr(hWindow, GWLP_WNDPROC, (LONG_PTR)hWndProc);
std::cout << "Successfully initialised DirectX - resolution " << swapchainDescription.BufferDesc.Width << "x" << swapchainDescription.BufferDesc.Height << std::endl;
// Update the screen resolution multiplier
UpdateResolutionMultiplier(swapchainDescription.BufferDesc.Width, swapchainDescription.BufferDesc.Height);
return true;
}

Capture Image from Secondary Monitor, cropping, and saving

I'm trying to write an application that takes a screen shot of an area on one of the monitors and saves it to an image. The Screen shot should be taken at a x,y coordinate with a given width and height. When the image saves it should be saved at 0,0 and the same width height. I have this working on my main monitor but when I attempt to take the screen shot of my 2nd or 3rd monitor it doesnt work. I either get a black image or a file that when opened says its an invalid format.
My code is a modified version of the following code that takes a screen shot of the entire desktop.
https://github.com/GERD0GDU/dxgi_desktop_capture
Main Monitor width/height
2560 X 1440
Main monitor top corner x/y coordinates are 0,0 retrieved by calling
POINT cursorPos;
GetCursorPos(&cursorPos);
Secondary Monitor width/height
1080 x 1920
Secondary monitor top corner x/y coordinates are 2560,-238
For reference for my image coordinates that work on the main monitor i'm passing in x:801 y:227, width:1756, height:1115
For my 2nd monitor which sits to the right of my main monitor and is flipped sideways making it taller than wide I'm passing in x:2907 y:-11 width:737 height:1595
HRESULT CDXGICapture::CaptureToFile(_In_ LPCWSTR lpcwOutputFileName, int x, int y, int width, int height)
{
AUTOLOCK();
if (!m_bInitialized) {
return D2DERR_NOT_INITIALIZED;
}
CHECK_POINTER_EX(m_ipDxgiOutputDuplication, E_INVALIDARG);
CHECK_POINTER_EX(lpcwOutputFileName, E_INVALIDARG);
HRESULT hr = S_OK;
hr = DXGICaptureHelper::IsRendererInfoValid(&m_rendererInfo);
if (FAILED(hr)) {
return hr;
}
// is valid?
hr = DXGICaptureHelper::GetContainerFormatByFileName(lpcwOutputFileName);
if (FAILED(hr)) {
return hr;
}
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
CComPtr<IDXGIResource> ipDesktopResource;
CComPtr<ID3D11Texture2D> ipAcquiredDesktopImage;
CComPtr<ID2D1Bitmap> ipD2D1SourceBitmap;
// Get new frame
int lTryCount = 4;
do
{
Sleep(1);
hr = m_ipDxgiOutputDuplication->AcquireNextFrame(250, &FrameInfo, &ipDesktopResource);
if (SUCCEEDED(hr))
break;
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
continue;
}
else if (FAILED(hr))
break;
} while (--lTryCount > 0);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
return S_FALSE;
}
else if (FAILED(hr))
{
return hr;
}
// QI for ID3D11Texture2D
hr = ipDesktopResource->QueryInterface(IID_PPV_ARGS(&ipAcquiredDesktopImage));
ipDesktopResource = nullptr;
CHECK_HR_RETURN(hr);
if (nullptr == ipAcquiredDesktopImage)
{
// release frame
m_ipDxgiOutputDuplication->ReleaseFrame();
return E_OUTOFMEMORY;
}
// Copy needed full part of desktop image
m_ipD3D11DeviceContext->CopyResource(m_ipCopyTexture2D, ipAcquiredDesktopImage);
// release frame
hr = m_ipDxgiOutputDuplication->ReleaseFrame();
CHECK_HR_RETURN(hr);
// create D2D1 source bitmap
hr = DXGICaptureHelper::CreateBitmap(m_ipD2D1RenderTarget, m_ipCopyTexture2D, &ipD2D1SourceBitmap);
CHECK_HR_RETURN(hr);
//try this
D2D1_RECT_F rcSource = D2D1::RectF(
(FLOAT)x,
(FLOAT)y,
(FLOAT)x + width,
(FLOAT)y+ height);
D2D1_RECT_F rcTarget = D2D1::RectF(
(FLOAT)0,
(FLOAT)0,
(FLOAT)width,
(FLOAT)height);
D2D1_POINT_2F ptTransformCenter = D2D1::Point2F(width / 2.0f, height / 2.0f);
// Apply the rotation transform to the render target.
D2D1::Matrix3x2F rotate = D2D1::Matrix3x2F::Rotation(
m_rendererInfo.RotationDegrees,
ptTransformCenter
);
D2D1::Matrix3x2F scale = D2D1::Matrix3x2F::Scale(
D2D1::SizeF(1, 1),
ptTransformCenter
);
// Priority: first rotate, after scale...
m_ipD2D1RenderTarget->SetTransform(rotate * scale);
m_ipD2D1RenderTarget->BeginDraw();
// clear background color
m_ipD2D1RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White, 1.0f));
m_ipD2D1RenderTarget->DrawBitmap(ipD2D1SourceBitmap, rcTarget, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, rcSource);
//m_ipD2D1RenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
hr = m_ipD2D1RenderTarget->EndDraw();
if (FAILED(hr)) {
return hr;
}
hr = DXGICaptureHelper::SaveImageToFile(m_ipWICImageFactory, m_ipWICOutputBitmap, lpcwOutputFileName, width, height);
if (FAILED(hr)) {
return hr;
}
return S_OK;
} // CaptureToFile
My save to file method
static
COM_DECLSPEC_NOTHROW
inline
HRESULT
SaveImageToFile(
_In_ IWICImagingFactory* pWICImagingFactory,
_In_ IWICBitmapSource* pWICBitmapSource,
_In_ LPCWSTR lpcwFileName,
_In_ unsigned int uiWidth,
_In_ unsigned int uiHeight
)
{
CHECK_POINTER_EX(pWICImagingFactory, E_INVALIDARG);
CHECK_POINTER_EX(pWICBitmapSource, E_INVALIDARG);
HRESULT hr = S_OK;
GUID guidContainerFormat;
hr = GetContainerFormatByFileName(lpcwFileName, &guidContainerFormat);
if (FAILED(hr)) {
return hr;
}
WICPixelFormatGUID format = GUID_WICPixelFormatDontCare;
CComPtr<IWICImagingFactory> ipWICImagingFactory(pWICImagingFactory);
CComPtr<IWICBitmapSource> ipWICBitmapSource(pWICBitmapSource);
CComPtr<IWICStream> ipStream;
CComPtr<IWICBitmapEncoder> ipEncoder;
CComPtr<IWICBitmapFrameEncode> ipFrameEncode;
// unsigned int uiWidth = 1756;
// unsigned int uiHeight = 1115;
hr = ipWICImagingFactory->CreateStream(&ipStream);
if (SUCCEEDED(hr)) {
hr = ipStream->InitializeFromFilename(lpcwFileName, GENERIC_WRITE);
}
if (SUCCEEDED(hr)) {
hr = ipWICImagingFactory->CreateEncoder(guidContainerFormat, NULL, &ipEncoder);
}
if (SUCCEEDED(hr))
{
hr = ipEncoder->Initialize(ipStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr))
{
hr = ipEncoder->CreateNewFrame(&ipFrameEncode, NULL);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->Initialize(NULL);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->SetSize(uiWidth, uiHeight);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->SetPixelFormat(&format);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->WriteSource(ipWICBitmapSource, NULL);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->Commit();
}
if (SUCCEEDED(hr))
{
hr = ipEncoder->Commit();
}
return hr;
} // SaveImageToFile
The coordinates being passed in needed to be normalized to the monitor by itself without considering the other monitors. IE all x and y values needed to be positive from a 0,0 being the top left of the monitor.
To get the coordinates for a monitor I used the following where the Control is the view I was trying to capture within my application.
Monitor[] monitors = Display.getDefault().getMonitors();
Control control = getScreenCaptureControlArea();
Point p = control.toDisplay(0, 0);
for (Monitor monitor : monitors) {
Rectangle bounds = monitor.getBounds();
if (monitor.getBounds().contains(p.x, p.y)) {
setxCord(Math.abs(Math.abs(bounds.x) - Math.abs(p.x)));
setyCord(Math.abs(Math.abs(bounds.y) - Math.abs(p.y)));
setWidth(control.getSize().x);
setHeight(control.getSize().y);
break;
}
}
Will note that this still doesnt work when the monitor is in Portrait mode as the image saved is incorrect. But seeing as my application isnt meant to be used in a Portrait mode it didnt matter much so i didnt put any more effort into figuring out that problem.

DirectX Partial Screen Capture

I am trying to create a program that will capture a full screen directx application, look for a specific set of pixels on the screen and if it finds it then draw an image on the screen.
I have been able to set up the application to capture the screen the directx libraries using the code the answer for this question Capture screen using DirectX
In this example the code saves to the harddrive using the IWIC libraries. I would rather manipulate the pixels instead of saving it.
After I have captured the screen and have a LPBYTE of the entire screen pixels I am unsure how to crop it to the region I want and then being able to manipulate the pixel array. Is it just a multi dimensional byte array?
The way I think I should do it is
Capture screen to IWIC bitmap (done).
Convert IWIC bitmap to ID2D1 bitmap using ID2D1RenderTarget::CreateBitmapFromWicBitmap
Create new ID2D1::Bitmap to store partial image.
Copy region of the ID2D1 bitmap to a new bitmap using ID2D1::CopyFromBitmap.
Render back onto screen using ID2D1 .
Any help on any of this would be so much appreciated.
Here is a modified version of the original code that only captures a portion of the screen into a buffer, and also gives back the stride. Then it browses all the pixels, dumps their colors as a sample usage of the returned buffer.
In this sample, the buffer is allocated by the function, so you must free it once you've used it:
// sample usage
int main()
{
LONG left = 10;
LONG top = 10;
LONG width = 100;
LONG height = 100;
LPBYTE buffer;
UINT stride;
RECT rc = { left, top, left + width, top + height };
Direct3D9TakeScreenshot(D3DADAPTER_DEFAULT, &buffer, &stride, &rc);
// In 32bppPBGRA format, each pixel is represented by 4 bytes
// with one byte each for blue, green, red, and the alpha channel, in that order.
// But don't forget this is all modulo endianness ...
// So, on Intel architecture, if we read a pixel from memory
// as a DWORD, it's reversed (ARGB). The macros below handle that.
// browse every pixel by line
for (int h = 0; h < height; h++)
{
LPDWORD pixels = (LPDWORD)(buffer + h * stride);
for (int w = 0; w < width; w++)
{
DWORD pixel = pixels[w];
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
}
}
// get pixel at 50, 50 in the buffer, as #ARGB
DWORD pixel = GetBGRAPixel(buffer, stride, 50, 50);
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
SavePixelsToFile32bppPBGRA(width, height, stride, buffer, L"test.png", GUID_ContainerFormatPng);
LocalFree(buffer);
return 0;;
}
#define GetBGRAPixelBlue(p) (LOBYTE(p))
#define GetBGRAPixelGreen(p) (HIBYTE(p))
#define GetBGRAPixelRed(p) (LOBYTE(HIWORD(p)))
#define GetBGRAPixelAlpha(p) (HIBYTE(HIWORD(p)))
#define GetBGRAPixel(b,s,x,y) (((LPDWORD)(((LPBYTE)b) + y * s))[x])
int main()
HRESULT Direct3D9TakeScreenshot(UINT adapter, LPBYTE *pBuffer, UINT *pStride, const RECT *pInputRc = nullptr)
{
if (!pBuffer || !pStride) return E_INVALIDARG;
HRESULT hr = S_OK;
IDirect3D9 *d3d = nullptr;
IDirect3DDevice9 *device = nullptr;
IDirect3DSurface9 *surface = nullptr;
D3DPRESENT_PARAMETERS parameters = { 0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
*pBuffer = NULL;
*pStride = 0;
// init D3D and get screen size
d3d = Direct3DCreate9(D3D_SDK_VERSION);
HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode));
LONG width = pInputRc ? (pInputRc->right - pInputRc->left) : mode.Width;
LONG height = pInputRc ? (pInputRc->bottom - pInputRc->top) : mode.Height;
parameters.Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = height;
parameters.BackBufferWidth = width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
parameters.hDeviceWindow = NULL;
// create device & capture surface (note it needs desktop size, not our capture size)
HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &parameters, &device));
HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr));
// get pitch/stride to compute the required buffer size
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
*pStride = rc.Pitch;
HRCHECK(surface->UnlockRect());
// allocate buffer
*pBuffer = (LPBYTE)LocalAlloc(0, *pStride * height);
if (!*pBuffer)
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
// get the data
HRCHECK(device->GetFrontBufferData(0, surface));
// copy it into our buffer
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
CopyMemory(*pBuffer, rc.pBits, rc.Pitch * height);
HRCHECK(surface->UnlockRect());
cleanup:
if (FAILED(hr))
{
if (*pBuffer)
{
LocalFree(*pBuffer);
*pBuffer = NULL;
}
*pStride = 0;
}
RELEASE(surface);
RELEASE(device);
RELEASE(d3d);
return hr;
}

SDL2 resize a surface

We want to create an SDL surface by loading an image with SDL_Image and if the dimensions exceed a limit resize the surface.
The reason we need to do this is on Raspbian SDL throws an error creating a texture from the surface ('Texture dimensions are limited to 2048x2048'). Whilst that's a very large image we don't want users to be concerned about image size, we want to resize it for them. Although we haven't encountered this limit on Windows, we're trying to develop the solution on windows and having issues resizing the texture.
Looking for a solution there have been similar questions...:
2008 not SDL2 custom blitting
2010 use SDL_gfx
2008 can't be done use SDL_gfx, 2015 use SDL_BlitScaled, 2015 use SDL_RenderCopyEx
Is a custom blitter or SDL_gfx necessary with current SDL2 (those answers pre-date SDL2's 2013 release)? SDLRenderCopyEx doesn't help as you need to generate the texture which is where our problem occurs.
So we tried some of the available blitting functions like SDL_BlitScaled, below is a simple program to render a 2500x2500 PNG with no scaling:
#include <SDL.h>
#include <SDL_image.h>
#include <sstream>
#include <string>
SDL_Texture * get_texture(
SDL_Renderer * pRenderer,
std::string image_filename) {
SDL_Texture * result = NULL;
SDL_Surface * pSurface = IMG_Load(image_filename.c_str());
if (pSurface == NULL) {
printf("Error image load: %s\n", IMG_GetError());
}
else {
SDL_Texture * pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);
if (pTexture == NULL) {
printf("Error image load: %s\n", SDL_GetError());
}
else {
SDL_SetTextureBlendMode(
pTexture,
SDL_BLENDMODE_BLEND);
result = pTexture;
}
SDL_FreeSurface(pSurface);
pSurface = NULL;
}
return result;
}
int main(int argc, char* args[]) {
SDL_Window * pWindow = NULL;
SDL_Renderer * pRenderer = NULL;
// set up
SDL_Init(SDL_INIT_VIDEO);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
SDL_Rect screenDimensions;
screenDimensions.x = 0;
screenDimensions.y = 0;
screenDimensions.w = 640;
screenDimensions.h = 480;
pWindow = SDL_CreateWindow("Resize Test",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
screenDimensions.w,
screenDimensions.h,
SDL_WINDOW_SHOWN);
pRenderer = SDL_CreateRenderer(pWindow,
-1,
SDL_RENDERER_ACCELERATED);
IMG_Init(IMG_INIT_PNG);
// render
SDL_SetRenderDrawColor(
pRenderer,
0,
0,
0,
0);
SDL_RenderClear(pRenderer);
SDL_Texture * pTexture = get_texture(
pRenderer,
"2500x2500.png");
if (pTexture != NULL) {
SDL_RenderCopy(
pRenderer,
pTexture,
NULL,
&screenDimensions);
SDL_DestroyTexture(pTexture);
pTexture = NULL;
}
SDL_RenderPresent(pRenderer);
// wait for quit
bool quit = false;
while (!quit)
{
// poll input for quit
SDL_Event inputEvent;
while (SDL_PollEvent(&inputEvent) != 0) {
if ((inputEvent.type == SDL_KEYDOWN) &&
(inputEvent.key.keysym.sym == 113)) {
quit = true;
}
}
}
IMG_Quit();
SDL_DestroyRenderer(pRenderer);
pRenderer = NULL;
SDL_DestroyWindow(pWindow);
pWindow = NULL;
return 0;
}
Changing the get_texture function so it identifies a limit and tries to create a new surface:
SDL_Texture * get_texture(
SDL_Renderer * pRenderer,
std::string image_filename) {
SDL_Texture * result = NULL;
SDL_Surface * pSurface = IMG_Load(image_filename.c_str());
if (pSurface == NULL) {
printf("Error image load: %s\n", IMG_GetError());
}
else {
const int limit = 1024;
int width = pSurface->w;
int height = pSurface->h;
if ((width > limit) ||
(height > limit)) {
SDL_Rect sourceDimensions;
sourceDimensions.x = 0;
sourceDimensions.y = 0;
sourceDimensions.w = width;
sourceDimensions.h = height;
float scale = (float)limit / (float)width;
float scaleH = (float)limit / (float)height;
if (scaleH < scale) {
scale = scaleH;
}
SDL_Rect targetDimensions;
targetDimensions.x = 0;
targetDimensions.y = 0;
targetDimensions.w = (int)(width * scale);
targetDimensions.h = (int)(height * scale);
SDL_Surface *pScaleSurface = SDL_CreateRGBSurface(
pSurface->flags,
targetDimensions.w,
targetDimensions.h,
pSurface->format->BitsPerPixel,
pSurface->format->Rmask,
pSurface->format->Gmask,
pSurface->format->Bmask,
pSurface->format->Amask);
if (SDL_BlitScaled(pSurface, NULL, pScaleSurface, &targetDimensions) < 0) {
printf("Error did not scale surface: %s\n", SDL_GetError());
SDL_FreeSurface(pScaleSurface);
pScaleSurface = NULL;
}
else {
SDL_FreeSurface(pSurface);
pSurface = pScaleSurface;
width = pSurface->w;
height = pSurface->h;
}
}
SDL_Texture * pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);
if (pTexture == NULL) {
printf("Error image load: %s\n", SDL_GetError());
}
else {
SDL_SetTextureBlendMode(
pTexture,
SDL_BLENDMODE_BLEND);
result = pTexture;
}
SDL_FreeSurface(pSurface);
pSurface = NULL;
}
return result;
}
SDL_BlitScaled fails with an error 'Blit combination not supported' other variations have a similar error:
SDL_BlitScaled(pSurface, NULL, pScaleSurface, NULL)
SDL_BlitScaled(pSurface, &sourceDimensions, pScaleSurface, &targetDimensions)
SDL_LowerBlitScaled(pSurface, &sourceDimensions, pScaleSurface, &targetDimensions) // from the wiki this is the call SDL_BlitScaled makes internally
Then we tried a non-scaled blit... which didn't throw an error but just shows white (not the clear colour or a colour in the image).
SDL_BlitSurface(pSurface, &targetDimensions, pScaleSurface, &targetDimensions)
With that blitting function not working we then tried it with the same image as a bitmap (just exporting the .png as .bmp), still loading the file with SDL_Image and both those functions work with SDL_BlitScaled scaling as expected 😐
Not sure what's going wrong here (we expect and need support for major image file formats like .png) or if this is the recommended approach, any help appreciated!
TL;DR The comment from #kelter pointed me in the right direction and another stack overflow question gave me a solution: it works if you first Blit to a 32bpp surface and then BlitScaled to another 32bpp surface. That worked for 8 and 24 bit depth pngs, 32 bit were invisible again another stack overflow question suggested first filling the surface before blitting.
An updated get_texture function:
SDL_Texture * get_texture(
SDL_Renderer * pRenderer,
std::string image_filename) {
SDL_Texture * result = NULL;
SDL_Surface * pSurface = IMG_Load(image_filename.c_str());
if (pSurface == NULL) {
printf("Error image load: %s\n", IMG_GetError());
}
else {
const int limit = 1024;
int width = pSurface->w;
int height = pSurface->h;
if ((width > limit) ||
(height > limit)) {
SDL_Rect sourceDimensions;
sourceDimensions.x = 0;
sourceDimensions.y = 0;
sourceDimensions.w = width;
sourceDimensions.h = height;
float scale = (float)limit / (float)width;
float scaleH = (float)limit / (float)height;
if (scaleH < scale) {
scale = scaleH;
}
SDL_Rect targetDimensions;
targetDimensions.x = 0;
targetDimensions.y = 0;
targetDimensions.w = (int)(width * scale);
targetDimensions.h = (int)(height * scale);
// create a 32 bits per pixel surface to Blit the image to first, before BlitScaled
// https://stackoverflow.com/questions/33850453/sdl2-blit-scaled-from-a-palettized-8bpp-surface-gives-error-blit-combination/33944312
SDL_Surface *p32BPPSurface = SDL_CreateRGBSurface(
pSurface->flags,
sourceDimensions.w,
sourceDimensions.h,
32,
pSurface->format->Rmask,
pSurface->format->Gmask,
pSurface->format->Bmask,
pSurface->format->Amask);
if (SDL_BlitSurface(pSurface, NULL, p32BPPSurface, NULL) < 0) {
printf("Error did not blit surface: %s\n", SDL_GetError());
}
else {
// create another 32 bits per pixel surface are the desired scale
SDL_Surface *pScaleSurface = SDL_CreateRGBSurface(
p32BPPSurface->flags,
targetDimensions.w,
targetDimensions.h,
p32BPPSurface->format->BitsPerPixel,
p32BPPSurface->format->Rmask,
p32BPPSurface->format->Gmask,
p32BPPSurface->format->Bmask,
p32BPPSurface->format->Amask);
// 32 bit per pixel surfaces (loaded from the original file) won't scale down with BlitScaled, suggestion to first fill the surface
// 8 and 24 bit depth pngs did not require this
// https://stackoverflow.com/questions/20587999/sdl-blitscaled-doesnt-work
SDL_FillRect(pScaleSurface, &targetDimensions, SDL_MapRGBA(pScaleSurface->format, 255, 0, 0, 255));
if (SDL_BlitScaled(p32BPPSurface, NULL, pScaleSurface, NULL) < 0) {
printf("Error did not scale surface: %s\n", SDL_GetError());
SDL_FreeSurface(pScaleSurface);
pScaleSurface = NULL;
}
else {
SDL_FreeSurface(pSurface);
pSurface = pScaleSurface;
width = pSurface->w;
height = pSurface->h;
}
}
SDL_FreeSurface(p32BPPSurface);
p32BPPSurface = NULL;
}
SDL_Texture * pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);
if (pTexture == NULL) {
printf("Error image load: %s\n", SDL_GetError());
}
else {
SDL_SetTextureBlendMode(
pTexture,
SDL_BLENDMODE_BLEND);
result = pTexture;
}
SDL_FreeSurface(pSurface);
pSurface = NULL;
}
return result;
}
The comment from #kelter had me look more closely at the surface pixel formats, bitmaps were working at 24bpp, pngs were being loaded at 8bpp and not working. Tried changing the target surface to 24 or 32 bpp but that didn't help. We had generated the png with auto-detected bit depth, setting it to 8 or 24 and performing the BlitScaled on a surface with the same bits-per-pixel worked although it didn't work for 32. Googling the blit conversion error lead to the question and answer from #Petruza.
Update Was a bit quick writing up this answer, the original solution handled bmp and 8 and 24 bit pngs but 32 bit pngs weren't rendering. #Retired Ninja answer to another question about Blit_Scaled suggested filling the surface before calling the function and that sorts it, there's another question related to setting alpha on new surfaces that may be relevant to this (particularily if you needed transparency) but filling with a solid colour is enough for me... for now.

How to unlock a locked bitmap

I want to unlock a locked ID2D1Bitmap I have tried m_pBitmap1->Release(); but it doesn't seem to work
hr=m_pBitmap1->CopyFromRenderTarget(nullptr, m_pRenderTarget2, nullptr); gives an access violation error:
"Unhandled exception at 0x00fb2a46 in dent_detection_sys.exe: 0xC0000005: Access violation reading location 0x00000024."
WICRect rcLock = { 0, 0, sc_bitmapWidth , sc_bitmapHeight };
IWICBitmapLock *pILock=NULL;
hr =pWICBitmap->Lock(&rcLock, WICBitmapLockWrite, &pILock);
hr=pRT->CreateSharedBitmap(
IID_IWICBitmapLock,
static_cast<void *>(pILock),
&bp2,
&m_pBitmap1
);
hr=m_pBitmap1->Release();
hr=m_pBitmap1->CopyFromRenderTarget(nullptr, m_pRenderTarget2, nullptr);
To unlock the WIC bitmap, release the IWICBitmapLock:
pILock->Release();
You should only release m_pBitmap1 when you don't want to use it anymore.
hr=m_pBitmap1->CopyFromRenderTarget(nullptr, m_pRenderTarget2, nullptr);
hr=m_pBitmap1->Release();
According to the MSDN to use a shared WIC bitmap the render target type must be D2D1_RENDER_TARGET_TYPE_SOFTWARE when the render target is created.
cdemo is a structure object with basic d2d, wic, dwrite interface pointers.
example: cdemo->d2d.factory->CreateSomething(), cdemo->wic.factory->CreateSomething(), cdemo->dwrite.factory->CreateSomething, cdemo->xaudio.factory->CreateSomething, etc.
cbmp is a pointer to a structure that has interfaces and properties related to a WIC bitmap
The following example does not work as is without some tweaks and typo fixes, but it can be used to demonstrate how to use wic for editing a bitmap and directly accessing pixels
This code assumes that the cdemo->d2d.factory and cdemo->wic.factory are already created.
#define DEBUG_FAILED_GOTO(a,b) MessageBox(m_hwnd, a, L"FAILED", MB_OK); goto b
#define DEBUG_DISPLAY(a) MessageBox(m_hwnd, a, L"DEBUG", MB_OK)
#define USING_SHARED_WIC_BITMAP
#define USING_WIC_RENDER_TARGET
#define USING_WICBMP_COPY_TO_D2DBMP
struct COMMON_WIC_BGRA { BYTE b, g, r, a };
struct COMMON_WIC_BMP
{
//// Miscelaneous variables
bool ready;
bool using_d2d_bmp;
bool using_shared_bmp;
bool using_render_tgt;
bool ready_d2d_bmp;
bool ready_shared_bmp;
bool ready_render_tgt;
UINT BPPPP; // Bit-Per-Pixel-Per-Plane
UINT stride; // cbStride = row size;
UINT buff_size; // (org_size.y * stride);
POINT org_size, clip_TpLt, padding;
POINT cur_size, clip_BtRt;
D2D1_BITMAP_PROPERTIES props_bmp; // = D2D1::BitmapProperties();
D2D1_RENDER_TARGET_PROPERTIES props_tgt; // = D2D1::RenderTargetProperties();
WICPixelFormatGUID formatGUID; // = GUID_WICPixelFormat32bppPBGRA;
WICRect rc_lock; // the lock region, usually the entire
//// Interfaces
IWICBitmap* ifc_bmp; // WIC bitmap: lock and unlock bmp data;
IWICBitmapLock* ifc_lock; // Used to access the pixels to read/write
ID2D1RenderTarget* ifc_render_tgt; // Creates a d2d render target
ID2D1Bitmap* ifc_d2d_bmp; // creates an d2d bitmap for display
ID2D1Bitmap* ifc_shared_bmp; // creates a shared bitmap for display
ID2D1SolidColorBrush* ifc_render_brush; // This is needed for the render target
//// Data pointers
BYTE* byte; // Points to a pixel's byte; 8 bits
COMMON_WIC_BGRA* wpixel; // Points to a pixel; 32 bits
};
BOOL Lock_Release (COMON_WIC_BMP *cbmp);
BOOL Lock_Start (COMMON_INTERFACE_STUFF *cdemo, COMMON_WIC_BMP *cbmp, DWORD flags);
void Create_BMP_n_Stuff (
COMMON_INTERFACE_STUFF *cdemo,
COMMON_WIC_BMP *cbmp,
int org_xsize,
int org_ysize)
{
DEBUG_DISPLAY(L"Gate 0-1 Open: started xxx process");
if (cbmp == NULL) { return E_FAIL; }
if (cdemo == NULL) { return E_FAIL; }
DEBUG_DISPLAY(L"Gate 0-2 Open: passed the sanity test");
HRESULT hr = S_OK;
ZeroMemory(cbmp, sizeof(COMMON_WIC_BMP));
// Create a Direct2D render target.
if (cdemo->d2d.hwnd_tgt == NULL)
{
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU((rc.right - rc.left), (rc.bottom - rc.top));
cdemo->d2d.props_tgt = D2D1::HwndRenderTargetProperties(m_hwnd, size);
cdemo->d2d.props_tgt_type = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
cdemo->d2d.props_bmp = D2D1::BitmapProperties();
hr = cdemo->d2d.factory->CreateHwndRenderTarget(
cdemo->d2d.props_tgt_type,
cdemo->d2d.props_tgt,
&cdemo->d2d.hwnd_tgt);
if (FAILED(hr)) { goto CleanUp; }
}
DEBUG_DISPLAY(L"Gate 1 Open: hwnd_tgt created");
cbmp->ready = false; // type is: bool
// this option is compatible to D2D bitmap without conversion
cbmp->formatGUID = GUID_WICPixelFormat32bppPBGRA; // type is: WICPixelFormatGUID
cbmp->buff_size = 0; // type is: UINT32
cbmp->stride = 0; // type is: UINT32
cbmp->clip_TpLt.x = 0; // type is: POINT or POINTS
cbmp->clip_TpLt.y = 0;
cbmp->clip_BtRt.x = cbmp->org_size.x = org_xsize; // type is: POINT or POINTS
cbmp->clip_BtRt.y = cbmp->org_size.y = org_ysize;
cbmp->byte = NULL; // type is: pointer to BYTE, BYTE*
cbmp->pixel = NULL; // type is: pointer to COMMON_WIC_BGRA, COMMON_WIC_BGRA*
cbmp->ifc_bmp = NULL; // type is: IWICBitmap*
cbmp->ifc_d2d_bmp = NULL; // type is: ID2D1Bitmap*
cbmp->ifc_lock = NULL; // type is: IWICBitmapLock*
cbmp->ifc_shared_bmp = NULL; // type is: ID2D1Bitmap*
cbmp->ifc_render_tgt = NULL; // type is: ID2D1RenderTarget*
cbmp->ifc_render_brush = NULL; // type is: ID2D1SolidColorBrush*
//D2D1_BITMAP_PROPERTIES props_bmp; = D2D1::BitmapProperties();
//D2D1_RENDER_TARGET_PROPERTIES props_tgt; = D2D1::RenderTargetProperties();
//bool ready;
//bool using_d2d_bmp;
//bool using_shared_bmp;
//bool using_render_tgt;
//bool ready_d2d_bmp;
//bool ready_shared_bmp;
//bool ready_render_tgt;
//UINT BPPPP; // Bit-Per-Pixel-Per-Plane
if (cdemo->wic.factory == NULL)
{ // (re)create the WIC factory
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
reinterpret_cast<void **>(&cdemo->wic.factory));
if (FAILED(hr)) { goto CleanUp; }
}
hr = cdemo->wic.factory->CreateBitmap(
cbmp->org_size.x,
cbmp->org_size.y,
cbmp->formatGUID,
WICBitmapCacheOnDemand,
&cbmp->ifc_bmp);
// Experimental debug
//if (FAILED(hr)) { DEBUG_FAILED_GOTO(L"FAILED creating wic bitmap", CleanUp); }
if (FAILED(hr)) { goto CleanUp; }
DEBUG_DISPLAY(L"Gate 2 Open: created the WIC bitmap");
// type is: WICRect;
cbmp->rc_lock = { 0, 0, (UINT)cbmp->org_size.x, (UINT)cbmp->org_size.y };
hr = cbmp->ifc_bmp->Lock(&cbmp->rc_lock, WICBitmapLockWrite, &cbmp->ifc_lock);
hr = cbmp->ifc_lock->GetStride(&cbmp->stride); //row size = (xsize*BPPP) + xpadding
hr = cbmp->ifc_lock->GetDataPointer(&cbmp->buff_size, &cbmp->byte);
cbmp->wpixel = (COMMON_WIC_BRGA *)cbmp->byte;
// clear the bitmap
ZeroMemory(cbmp->byte, cbmp->buff_size);
#ifdef USING_SHARED_WIC_BITMAP
cbmp->props_bmp = D2D1:BitmapProperties();
hr = demo->d2d.hwnd_tgt->CreateSharedBitmap(
IID_IWICBitmapLock,
(void*)cbmp->ifc_lock,
&cbmp->props_bmp,
&cbmp->ifc_shared_bmp);
if (SUCCEDED(hr))
{
cbmp->using_shared_bmp = true;
DEBUG_DISPLAY(L"Gate 4-1 Open: created shared wic bitmap");
}
#endif
#ifdef USING_WICBMP_COPY_TO_D2DBMP
hr = cdemo->d2d.factory->CreateBitmapFromWicBitmap(
cbmp->ifc_bmp,
&cbmp->props_bmp,
&cbmp->ifc_d2d_bmp);
if (SUCCEDED(hr))
{
cbmp->using_d2d_bmp = true;
DEBUG_DISPLAY(L"Gate 4-2 Open: created d2d bitmap ");
}
#endif
#ifdef USING_WIC_RENDER_TARGET
cbmp->props_tgt = D2D1::RenderTargetProperties();
cbmp->props_tgt.type = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
hr = cdemo->d2d.factory->CreateWicBitmapRenderTarget(
cbmp->ifc_bmp,
&cbmp->props_tgt,
&cbmp->ifc_render_tgt);
if (SUCCEDED(hr))
{
hr = cbmp->ifc_render_tgt->CreateSolidColorBrush(
{ 1.0f, 1.0f, 1.0f, 1.0f }, // Solid white
&cbmp->ifc_render_brush);
cbmp->using_shared_bmp = true;
DEBUG_DISPLAY(L"Gate 4-3 Open: created wic render target and brush");
}
#endif
if (FAILED(hr)) { goto CleanUp; }
cbmp->ready = true;
// Rules of engagement if using all possible combinations above
// 1) After SafeRelease(&cbmp->ifc_lock) you cannot use cbmp->byte or cbmp->wpixel
// 2) To use cbmp->ifc_render_tgt you must first SafeRelease(cbmp->ifc_lock) and
// SafeRelease(&cbmp->ifc_shared_bmp). Later reinitialize them as needed.
// 3) To display the wic bitmap (cbmp->ifc_bmp) onto cdemo->d2d.hwnd_tgt:
// you cannot copy the wic bitmap (cbmp->ifc_bmp) directly to an hwnd render target
// option 1: This is whole point of creating the shared bmp
// cdemo->d2d.hwnd_tgt->DrawBMP( [ &dst_rc, ] cbmp->ifc_shared_bmp);
// option 2: Copy the pixels to the d2d bitmap, copy the d2d bitmap to the target
// cbmp->ifc_d2d_bmp->CopyFromMemory(cbmp->byte, &dst_rc, cbmp->stride);
// cdemo->d2d.hwnd_tgt->DrawBMP( [ &dst_rc, ] cbmp->ifc_d2d_bmp);
// option 3: Copy from the render target to the d2d bitmap
// cbmp->ifc_d2d_bmp->CopyFromRenderTarget(&pt_dst, cbmp->ifc_render_tgt, &src_rc);
// cdemo->d2d.hwnd_tgt->DrawBMP( [ &dst_rc, ] cbmp->ifc_d2d_bmp);
// 4) When drawing directly to the wic bitmap either use cbmp->ifc_render_tgt or
// cbmp->ifc_lock + cbmp->byte + your own algorithms to draw shapes
//
// 5) for simplicty: it can get confusing when trying to use all methods
option 1: use the ifc_lock with the ifc_shared_bmp + your own algorithms
option 2: use the ifc_render_tgt with the ifc_d2d_bmp
// Example: Draw a filled 12x15 rectangle example:
int x = 20, byte_col = 0, wpixel_col = 0, sizey = 12;
int y = 35, byte_row = 0, wpixel_row = 0, sizex = 15;
D2D1_COLOR_F d2d_colr = { 0.50f, 0.10f, 0.80f, 1.00f }; //some random color
COMMON_WIC_BGRA wic_colr = { 0, 0, 0, 0 };
D2D1_POINT_2U d2d_pt_dst_f = { 0, 0 };
D2D_RECT_F d2d_outputrect = { 0.0f, 0.0f, 0.0f, 0.0f };
D2D1_RECT_F d2d_dst_rcf =
{ 0.0f, 0.0f, (FLOAT)cbmp->org_size.x, (FLOAT)cbmp->org_size.y };
D2D1_RECT_U d2d_src_rcu =
{ 0, 0, (UINT32)cbmp->org_size.x, (UINT32)cbmp->org_size.y };
WIC_RECT_U wic_dst_rcu =
{ 0, 0, (UINT32)cbmp->org_size.x, (UINT32)cbmp->org_size.y };
WIC_RECT_U wic_src_rcu = wic_dst_rcu
// must release the lock and shared bitmap before using the render target
Lock_End(cbmp);
// This should look familiar
d2d_outputrect = { (FLOAT)x, (FLOAT)y, (FLOAT)(x+sizex-1), (FLOAT)(y+sizey-1) };
cbmp->ifc_render_tgt->BeginDraw();
cbmp->ifc_render_brush->SetColor(d2d_colr);
cbmp->ifc_render_tgt->SetTransform(D2D1::IdentityMatrix());
cbmp->ifc_render_tgt->FillRectangle(&d2d_outputrect, cbmp->ifc_render_brush);
hr = cbmp->ifc_render_tgt->EndDraw();
// display the wic bitmap on the hwnd render target
hr = cbmp->ifc_d2d_bmp->CopyFromRenderTarget(
&d2d_pt_dst,
cbmp->ifc_render_tgt,
&d2d_src_rc);
hr = cdemo->d2d.hwnd_tgt->DrawBMP(&d2d_dst_rc, cbmp->ifc_d2d_bmp);
// Alternative: using the ifc_lock with the ifc_shared_bmp + home grown algorithms
if (!Lock_Start(cdemo, cbmp, WICBitmapLockWrite)) { goto CleanUp; }
// convert D2D1_COLOR_F { b, g, r, a} to BYTE { b, g, r, a }
wic_colr.b = (BYTE)ceil(d2d_colr.b * 255);
wic_colr.g = (BYTE)ceil(d2d_colr.g * 255);
wic_colr.r = (BYTE)ceil(d2d_colr.r * 255);
wic_colr.a = (BYTE)ceil(d2d_colr.a * 255);
for (int run_y = 0; run_y < sizey; ++run_y)
{
// clipping for the y values
if (((run_y + y) < cbmp->clip_TpLt.y) || ((run_y + y) >= clip_BtRt.y))
{ continue; }
// convert the y to a byte position
byte_row = ((run_y + y) * cbmp->stride);
wpixel_row = ((run_y + y) * cbmp->org_size.x) + cbmp->padding.x; //optional
for (int run_x = 0; run_x < sizex; ++run_x)
{
// clipping for the x values
if (((run_x + x) < cbmp->clip_TpLt.x) || ((run_x + x) >= clip_BtRt.x))
{ continue; }
// convert the x to an offset position
byte_col = ((run_x + x) * 4); // must multiply by 4 bytes (b, g, r, a)
wpixel_col = (run_x + x); // cbmp->wpixel points to every 4 bytes
// access the pixels by means of pointer[(y_position + x_offset)]
cbmp->byte[(byte_row + byte_col + 0)] = wic_colr.b;
cbmp->byte[(byte_row + byte_col + 1)] = wic_colr.g;
cbmp->byte[(byte_row + byte_col + 2)] = wic_colr.r;
cbmp->byte[(byte_row + byte_col + 3)] = wic_colr.a;
cbmp->wpixel[(wpixel_row + wpixel_col)] = wic_colr; // Alternative
// Another method
cbmp->wpixel[(wpixel_row + wpixel_col)].b = wic_colr.b; // Alternatives
cbmp->wpixel[(wpixel_row + wpixel_col)].g = wic_colr.g; // Alternatives
cbmp->wpixel[(wpixel_row + wpixel_col)].r = wic_colr.r; // Alternatives
cbmp->wpixel[(wpixel_row + wpixel_col)].a = wic_colr.a; // Alternatives
}
}
// display the wic bitmap on the hwnd render target
cdemo->d2d.hwnd_tgt->DrawBMP(&dst_rc, cbmp->ifc_shared_bmp);
// Optionally release the lock after every use
// Lock_Release(cbmp);
return; // commnent out if cleanup is required
CleanUp:
// SafeRelease everything that needs to be released
}
BOOL Lock_Start (
COMMON_INTERFACE_STUFF *cdemo,
COMMON_WIC_BMP *cbmp,
DWORD flags)
{
if (cdemo == NULL) { return FALSE; }
//if (!cdemo->ready) { CreateResouces(cdemo); }
if (cbmp == NULL) { return FALSE; }
if (cbmp->ifc_lock != NULL) { return TRUE; }
SafeRelease(&cbmp->ifc_lock);
hr = cbmp->ifc_bmp->Lock(&cbmp->rc_lock, flags, &cbmp->ifc_lock);
if (FAILED(hr)) { return FALSE; }
hr = cbmp->ifc_lock->GetStride(&cbmp->stride);
hr = cbmp->ifc_lock->GetDataPointer(&cbmp->buff_size, &cbmp->byte);
cbmp->wpixel = (COMMON_WIC_BGRA *)cbmp->byte;
// recreate the shared bitmap
SafeRelease(&cbmp->ifc_shared_bmp);
hr = cdemo->d2d.factory->CreateSharedBitmap(
IID_IWICBitmapLock,
(void*)cbmp->ifc_lock,
&cbmp->props_bmp,
&cbmp->ifc_shared_bmp);
return TRUE;
}
BOOL Lock_Release (COMON_WIC_BMP *cbmp)
{
if (cbmp == NULL) { return FALSE; }
if (cbmp->ifc_lock == NULL) { return TRUE; }
SafeRelease(&cbmp->ifc_lock);
SafeRelease(&cbmp->ifc_shared_bmp);
cbmp->byte = NULL;
cbmp->wpixel = NULL;
if (cbmp->using_render_tgt) { cbmp->ready_render_tgt = true; }
return TRUE;
}