I'm trying to use the TWICImage class in C++builder to save an in-memory bitmap to TIFF file. However, any attempt to use the Assign() method on TWICImage gives an access error.
For example:
TBitmap* bmp = new TBitmap();
TWICImage* wic = new TWICImage();
wic->Assign( bmp );
gives the access violation on the third line.
After a bit more digging, the following gives an error on the second and subsequent assignments:
TBitmap* bmp = new TBitmap();
for (int i=0; i<10; ++i) {
std::unique_ptr<TWICImage> wic( new TWICImage() );
wic->Assign( bmp );
}
However, if I make the TWICImage pointer static, it all works as expected. So it looks like there is something odd going on in the TWICImage destructor.
Related
How to clear a bitmap that was declared in the .cpp as follows:
Graphics::TBitmap * Bitmap1 = new Graphics::TBitmap;
All the examples I find are for Firemonkey and there it seems quite simple
MyBitmap = new TBitmap(0,0);
...
MyBitmap->Clear(claWhite);
or
MyBitmap->ClearRect(MyRect);
But Clear() and ClearRect() are not members of TBitmap in VCL
I expect I should delete Bitmap1; in order to clear it, but then how to re-declare it, so that it is still global to all methods in the form?
Thanks in advance.
You don't need to delete and recreate the TBitmap. Simply draw a new image over top of it, for instance by using its Canvas->FillRect() method (that is essentially what the FMX examples are doing), eg:
Bitmap1->Brush->Color = clWhite;
Bitmap1->Canvas->FillRect(Rect(0, 0, Bitmap1->Width, Bitmap1->Height));
I am welcoming Embarcaderos efforts to make TListView more dynamically, and was exited to see Sarina Duponts post here where you could just link the imageindex to the TListView properties in LiveBindings Designer, and even the image property to a datafield (integer) when using DynamicAppearance and TImageObjectAppearance.
But... I tried, and did almost succeed.
In my challenge I have an application where I use TFDMemTable with TREST* function to populate the TFDMemTable. All works well if I don't use the DynamicAppearance and use i.e. ImageListItem and links the datafield I want to use to the imageindex property in TListView using LiveBindings Designer.
With DynamicApperance though, there are no imageindex property to link to, but Sarina Dupont says in here post that you could link the integer field directly to the image property (and IDE/compiler will figure it out).
Well... I figured following out: My data fields (semicreated from TREST* and TFDMemTable) are not neccesserely what they seems to be. Since I am using REST/JSON, the fieldtypes are "anonymized" to WideString, actually the FieldDefs->'dataitem'->DataType is set to "ftWideString". I tried to change this value to ftInteger in hope that this would help, but I did just get this errormessage: "FDMemtTable1: Type mismatch in field for 'datafield', exepecting: WideString actual: Integer".
So... I was nearly there, and I really want to use DynamicAppearance and view several images and textfields for each TListViewItem...
...or is it easier to make a ListViewItem 'Template' dynamically and populate it with data instead, and what is the best way to do that ?
I usually try to avoid LiveBindings. The fastest, most stable and easiest way is to add items using code.
ListView1->BeginUpdate();
try {
for (int i = 0; i < arrayOfThings->item.Length; i++) {
TListViewItem* item = ListView1->Items->Add();
item->Text = arrayOfThings->item[i]->item_name;
item->Data["itemName"] = TValue::From<UnicodeString>( arrayOfThings->item[i]->item_name);
item->Data["itemId"] = TValue::From<UnicodeString>( IntToStr( arrayOfThings->item[i]->id ));
item->Data["itemDate"] = TValue::From<UnicodeString>(arrayOfThings->item[i]->item_date);
// adding the image - imgClock is name of the image field [TImageObjectAppearance ] added to items in ItemAppearance in the TListView object that uses DynamicAppereance
const UnicodeString imgClock = L"imgClock";
dynamic_cast<TListItemImage*>(item->Objects->FindDrawable(imgClock))->Bitmap = //bitmap source;
}
} catch (...) {
}
ListView1->EndUpdate();
The question is very similar to this, but that one didn't get answered yet. My question is, I have a D2D DXGI RenderTarget from d2dfactory->CreateDxgiSurfaceRenderTarget(), and I want to save its content to an image file using WIC. I was just reading this and this, so it looks to me that I can not just create a ID2D1Bitmap on a WIC render target and use ID2D1Bitmap::CopyFromRenderTarget() to copy from the input render target I want to save, because they are using different resources. So here is what I came up with using ID2D1RenderTarget::CreateSharedBitmap():
HRESULT SaveRenderTargetToFile(
ID2D1RenderTarget* pRTSrc,
LPCWSTR uri
)
{
HRESULT hr = S_OK;
ComPtr<IWICBitmap> spWICBitmap;
ComPtr<ID2D1RenderTarget> spRT;
ComPtr<IWICBitmapEncoder> spEncoder;
ComPtr<IWICBitmapFrameEncode> spFrameEncode;
ComPtr<IWICStream> spStream;
//
// Create WIC bitmap to save and associated render target
//
UINT bitmapWidth = static_cast<UINT>(pRTSrc->GetSize().width + .5f);
UINT bitmapHeight = static_cast<UINT>(pRTSrc->GetSize().height + .5f);
HR(m_spWICFactory->CreateBitmap(
bitmapWidth,
bitmapHeight,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&spWICBitmap
));
D2D1_RENDER_TARGET_PROPERTIES prop = D2D1::RenderTargetProperties();
prop.pixelFormat = D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_PREMULTIPLIED
);
prop.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
prop.usage = D2D1_RENDER_TARGET_USAGE_NONE;
HR(m_spD2D1Factory->CreateWicBitmapRenderTarget(
spWICBitmap,
prop,
&spRT
));
//
// Create a shared bitmap from this RenderTarget
//
ComPtr<ID2D1Bitmap> spBitmap;
D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties();
bp.pixelFormat = prop.pixelFormat;
HR(spRT->CreateSharedBitmap(
__uuidof(IWICBitmap),
static_cast<void*>(spWICBitmap.GetRawPointer()),
&bp,
&spBitmap
)); // <------------------------- This fails with E_INVALIDARG
//
// Copy the source RenderTarget to this bitmap
//
HR(spBitmap->CopyFromRenderTarget(nullptr, pRTSrc, nullptr));
//
// Draw this bitmap to the output render target
//
spRT->BeginDraw();
spRT->Clear(D2D1::ColorF(D2D1::ColorF::GreenYellow));
spRT->DrawBitmap(spBitmap);
HR(spRT->EndDraw());
//
// Save image to file
//
HR(m_spWICFactory->CreateStream(&spStream));
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
HR(spStream->InitializeFromFilename(uri, GENERIC_WRITE));
HR(m_spWICFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &spEncoder));
HR(spEncoder->Initialize(spStream, WICBitmapEncoderNoCache));
HR(spEncoder->CreateNewFrame(&spFrameEncode, nullptr));
HR(spFrameEncode->Initialize(nullptr));
HR(spFrameEncode->SetSize(bitmapWidth, bitmapHeight));
HR(spFrameEncode->SetPixelFormat(&format));
HR(spFrameEncode->WriteSource(spWICBitmap, nullptr));
HR(spFrameEncode->Commit());
HR(spEncoder->Commit());
HR(spStream->Commit(STGC_DEFAULT));
done:
return hr;
}
Anything wrong with this code? (I'm sure there's a lot :)) Somewhere on MSDN it says that WIC render target only supports software mode, while DXGI render target only supports hardware mode. Is this the reason why the above call to CreateSharedBitmap() fails? How should I save a DXGI surface content to an image file with D2D then?
With some limitations, you can use D3DX11SaveTextureToFile. Use QI on your surface to get the ID3D11Resource.
On the same page they are recommending DirectXTex library as a replacement, CaptureTexture then SaveToXXXFile (where XXX is WIC, DDS, or TGA). So that's another option.
Also, if your surface has been created as GDI compatible, you can use IDXGISurface1::GetDC. (Use QI on your IDXGISurface to get the IDXGISurface1). Saving DC to a file is left as an exercise to the reader.
Remember to use the Debug Layer for help with cryptic return codes like E_INVALIDARG.
You could try this (I haven't):
Make your old DXGISurface.
Make an auxiliary ID2D1DeviceContext render target.
Use ID2D1DeviceContext::CreateBitmapFromDxgiSurface to create an ID2D1Bitmap1 associated to the DXGI surface.
Draw on your DXGISurface. You should get the same on the ID2D1Bitmap1.
Use ID2D1Bitmap1::Map to get a memory pointer to the pixeldata.
Copy the pixeldata to file, or to a wicbitmap for encoding (jpeg, tiff, etc.)
Perhaps this:(succeed running)
D2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperies(D2D1_RENDER_TARGET_TYPE_SOFTWARE,D2D1::Pixel Format(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_PREMULTIPLIED)), ……)
your RenderTarget should be set the static of SOFTWARE the same as WICRenderTarget.
I'm having a weird problem while developing my DirectShow application. I am using Delphi 6 with the DSPACK DirectShow component library. One of the IBaseFilter instances doesn't seem to recognize a pin that it owns when I try to find the pin in the filter using it's TPinInfo.achName property (_PinInfo). (Note, in this case it is the IBaseFilter created by the TSampleGrabber component that is exhibiting this weird behavior).
The sequence of events, encapsulated in the code sample below is this:
Find the first available input pin in the IBaseFilter instance. In the code below this is the pin passed to testPinInfo().
Execute QueryPinInfo() on the returned pin to get that information. The returned information shows the pin's achName as 'Input'.
Try to find a pin named 'Input' in the very same IBaseFilter instance using IBaseFilter.findPin().
Get NIL back indicating a pin could not be found with that name. This in my opinion is a really strange condition (error).
Does anyone know what kind of conditions could cause this scenario? I don't think it's a memory corruption problem because the data structures involved look fine when I inspect them in the debugger. Is it possible that some IBaseFilter implementations neglect to implement the FindPin() method properly?
Here's the code below:
procedure testPinInfo(intfInputPin: IPin);
var
intfTestPin: IPin;
pinInfo_input: TPinInfo;
begin
intfTestPin := nil;
// Get the pin information.
ZeroMemory(#pinInfo_input, SizeOf(pinInfo_input));
intfInputPin.QueryPinInfo(pinInfo_input);
// Now immediately turn around and try to find the pin in the filter that
// owns it, using the name found in pinInfo_input
pinInfo_input.pFilter.FindPin(pinInfo_input.achName, intfTestPin);
// >>> intfTestPin is NIL (unassigned). This is an error.
end;
Don't use FindPin, you always have better ways to do it. Look for unconnected pin of desired direction with the media type of interest. If you look for preview/capture pins specifically, you always have an option to use IKsPropertySet interface to unambiguously identify the pins you need.
I had a similar issue to this so I made my own version of FindPin :-
HRESULT GraphControl::FindPinByName(IBaseFilter* pFilter,LPCWSTR pName,IPin** ppPin)
{
HRESULT hr = E_FAIL;
IEnumPins* pEnum = NULL;
IPin* pPin = NULL;
DWORD pFetched = 0;
PIN_INFO pinInfo = {0};
// Create a pin enumerator
if(FAILED(pFilter->EnumPins(&pEnum)))
return E_FAIL;
// Get the first instance
hr = pEnum->Next(1,&pPin,&pFetched);
while( hr == S_OK )
{
pPin->QueryPinInfo(&pinInfo);
// Compare the names
if (wcscmp(pName,pinInfo.achName) == 0 )
{
// pin names match so use this one and exit
*ppPin = pPin;
break;
}
SAFE_RELEASE(pinInfo.pFilter);
SAFE_RELEASE(pPin);
hr = pEnum->Next(1,&pPin,&pFetched);
}
SAFE_RELEASE(pinInfo.pFilter);
SAFE_RELEASE(pEnum);
// if the pPin address is null we didnt find a pin with the wanted name
if(&*pPin == NULL)
hr = VFW_E_NOT_FOUND;
return hr;
}
For FindPin you need the corresponding Id, check QueryId(). For Input it's usually "In".
I need to figure out how to get the data from D3D textures and surfaces back to system memory. What's the fastest way to do such things and how?
Also if I only need one subrect, how can one read back only that portion without having to read back the entire thing to system memory?
In short I'm looking for concise descriptions of how to copy the following to system memory:
a texture
a subset of a texture
a surface
a subset of a surface
a D3DUSAGE_RENDERTARGET texture
a subset of a D3DUSAGE_RENDERTARGET texture
This is Direct3D 9, but answers about newer versions of D3D would be appreciated too.
The most involved part is reading from some surface that is in video memory ("default pool"). This is most often render targets.
Let's get the easy parts first:
reading from a texture is the same as reading from 0-level surface of that texture. See below.
the same for subset of a texture.
reading from a surface that is in non-default memory pool ("system" or "managed") is just locking it and reading bytes.
the same for subset of surface. Just lock relevant portion and read it.
So now we have left surfaces that are in video memory ("default pool"). This would be any surface/texture marked as render target, or any regular surface/texture that you have created in default pool, or the backbuffer itself. The complex part here is that you can't lock it.
Short answer is: GetRenderTargetData method on D3D device.
Longer answer (a rough outline of the code that will be below):
rt = get render target surface (this can be surface of the texture, or backbuffer, etc.)
if rt is multisampled (GetDesc, check D3DSURFACE_DESC.MultiSampleType), then: a) create another render target surface of same size, same format but without multisampling; b) StretchRect from rt into this new surface; c) rt = this new surface (i.e. proceed on this new surface).
off = create offscreen plain surface (CreateOffscreenPlainSurface, D3DPOOL_SYSTEMMEM pool)
device->GetRenderTargetData( rt, off )
now off contains render target data. LockRect(), read data, UnlockRect() on it.
cleanup
Even longer answer (paste from the codebase I'm working on) follows. This will not compile out of the box, because it uses some classes, functions, macros and utilities from the rest of codebase; but it should get you started. I also ommitted most of error checking (e.g. whether given width/height is out of bounds). I also omitted the part that reads actual pixels and possibly converts them into suitable destination format (that is quite easy, but can get long, depending on number of format conversions you want to support).
bool GfxDeviceD3D9::ReadbackImage( /* params */ )
{
HRESULT hr;
IDirect3DDevice9* dev = GetD3DDevice();
SurfacePointer renderTarget;
hr = dev->GetRenderTarget( 0, &renderTarget );
if( !renderTarget || FAILED(hr) )
return false;
D3DSURFACE_DESC rtDesc;
renderTarget->GetDesc( &rtDesc );
SurfacePointer resolvedSurface;
if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
{
hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
if( FAILED(hr) )
return false;
hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
if( FAILED(hr) )
return false;
renderTarget = resolvedSurface;
}
SurfacePointer offscreenSurface;
hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
if( FAILED(hr) )
return false;
hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
bool ok = SUCCEEDED(hr);
if( ok )
{
// Here we have data in offscreenSurface.
D3DLOCKED_RECT lr;
RECT rect;
rect.left = 0;
rect.right = rtDesc.Width;
rect.top = 0;
rect.bottom = rtDesc.Height;
// Lock the surface to read pixels
hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
if( SUCCEEDED(hr) )
{
// Pointer to data is lt.pBits, each row is
// lr.Pitch bytes apart (often it is the same as width*bpp, but
// can be larger if driver uses padding)
// Read the data here!
offscreenSurface->UnlockRect();
}
else
{
ok = false;
}
}
return ok;
}
SurfacePointer in the code above is a smart pointer to a COM object (it releases object on assignment or destructor). Simplifies error handling a lot. This is very similar to _comptr_t things in Visual C++.
The code above reads back whole surface. If you want to read just a part of it efficiently, then I believe fastest way is roughly:
create a default pool surface that is of the needed size.
StretchRect from part of original surface to that smaller one.
proceed as normal with the smaller one.
In fact this is quite similar to what code above does to handle multi-sampled surfaces. If you want to get just a part of a multi-sampled surface, you can do a multisample resolve and get part of it in one StretchRect, I think.
Edit: removed piece of code that does actual read of pixels and format conversions. Was not directly related to question, and the code was long.
Edit: updated to match edited question.