Resize a DXGI or Texture2D resource in SharpDX

I want to resize a screen shot using the SharpDX desktop duplication API. I am using the Capture Screen Sample Code from the Samples SharpDX repository , the relevant part is as follows :.

SharpDX.DXGI.Resource screenResource; OutputDuplicateFrameInformation duplicateFrameInformation; // Try to get duplicated frame within given time duplicatedOutput.AcquireNextFrame(10000, out duplicateFrameInformation, out screenResource); if (i > 0) { // copy resource into memory that can be accessed by the CPU using (var screenTexture2D = screenResource.QueryInterface<Texture2D>()) device.ImmediateContext.CopyResource(screenTexture2D, screenTexture); // Get the desktop capture texture var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None); System.Diagnostics.Debug.WriteLine(watch.Elapsed); // Create Drawing.Bitmap var bitmap = new System.Drawing.Bitmap(width, height, PixelFormat.Format32bppArgb); var boundsRect = new System.Drawing.Rectangle(0, 0, width, height); // Copy pixels from screen capture Texture to GDI bitmap var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); var sourcePtr = mapSource.DataPointer; var destPtr = mapDest.Scan0; for (int y = 0; y < height; y++) { // Iterate and write to bitmap... 

I would like to resize the image much smaller than the actual screen size before treating it as an array of bytes. I do not need to save the image, just go into bytes. I would like to do this relatively quickly and efficiently (e.g. using a GPU, if possible).

I cannot scale during CopyResource , since the output sizes must match the input sizes. Is it possible to execute another copy from my screenTexture2D to scale? How exactly do I scale the resource - am I using a variable chain, matrix transformation, or something else?

+7
directx-11 sharpdx
source share
3 answers

If you perfectly resize to two from the screen, you can do this:

  • Create a smaller texture using RenderTarget/ShaderResource and the GenerateMipMaps options, with the same screen size, mipcount> 1 (2 for size / 2, 3 for presence /4...etc.).
  • Copy the first mipmap of the screen texture to a smaller texture
  • DeviceContext.GenerateMipMaps on a smaller texture
  • Copy the selected copy of the smaller texture (1: / 2, 2: / 4 ... etc.) Into the intermediate texture (which should also be declared smaller, i.e. the same size as the mipmap to be used)

A quick hack of source code to generate texture / 2 would look like this:

  [STAThread] private static void Main() { // # of graphics card adapter const int numAdapter = 0; // # of output device (ie monitor) const int numOutput = 0; const string outputFileName = "ScreenCapture.bmp"; // Create DXGI Factory1 var factory = new Factory1(); var adapter = factory.GetAdapter1(numAdapter); // Create device from Adapter var device = new Device(adapter); // Get DXGI.Output var output = adapter.GetOutput(numOutput); var output1 = output.QueryInterface<Output1>(); // Width/Height of desktop to capture int width = output.Description.DesktopBounds.Width; int height = output.Description.DesktopBounds.Height; // Create Staging texture CPU-accessible var textureDesc = new Texture2DDescription { CpuAccessFlags = CpuAccessFlags.Read, BindFlags = BindFlags.None, Format = Format.B8G8R8A8_UNorm, Width = width/2, Height = height/2, OptionFlags = ResourceOptionFlags.None, MipLevels = 1, ArraySize = 1, SampleDescription = { Count = 1, Quality = 0 }, Usage = ResourceUsage.Staging }; var stagingTexture = new Texture2D(device, textureDesc); // Create Staging texture CPU-accessible var smallerTextureDesc = new Texture2DDescription { CpuAccessFlags = CpuAccessFlags.None, BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource, Format = Format.B8G8R8A8_UNorm, Width = width, Height = height, OptionFlags = ResourceOptionFlags.GenerateMipMaps, MipLevels = 4, ArraySize = 1, SampleDescription = { Count = 1, Quality = 0 }, Usage = ResourceUsage.Default }; var smallerTexture = new Texture2D(device, smallerTextureDesc); var smallerTextureView = new ShaderResourceView(device, smallerTexture); // Duplicate the output var duplicatedOutput = output1.DuplicateOutput(device); bool captureDone = false; for (int i = 0; !captureDone; i++) { try { SharpDX.DXGI.Resource screenResource; OutputDuplicateFrameInformation duplicateFrameInformation; // Try to get duplicated frame within given time duplicatedOutput.AcquireNextFrame(10000, out duplicateFrameInformation, out screenResource); if (i > 0) { // copy resource into memory that can be accessed by the CPU using (var screenTexture2D = screenResource.QueryInterface<Texture2D>()) device.ImmediateContext.CopySubresourceRegion(screenTexture2D, 0, null, smallerTexture, 0); // Generates the mipmap of the screen device.ImmediateContext.GenerateMips(smallerTextureView); // Copy the mipmap 1 of smallerTexture (size/2) to the staging texture device.ImmediateContext.CopySubresourceRegion(smallerTexture, 1, null, stagingTexture, 0); // Get the desktop capture texture var mapSource = device.ImmediateContext.MapSubresource(stagingTexture, 0, MapMode.Read, MapFlags.None); // Create Drawing.Bitmap var bitmap = new System.Drawing.Bitmap(width/2, height/2, PixelFormat.Format32bppArgb); var boundsRect = new System.Drawing.Rectangle(0, 0, width/2, height/2); // Copy pixels from screen capture Texture to GDI bitmap var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); var sourcePtr = mapSource.DataPointer; var destPtr = mapDest.Scan0; for (int y = 0; y < height/2; y++) { // Copy a single line Utilities.CopyMemory(destPtr, sourcePtr, width/2 * 4); // Advance pointers sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); destPtr = IntPtr.Add(destPtr, mapDest.Stride); } // Release source and dest locks bitmap.UnlockBits(mapDest); device.ImmediateContext.UnmapSubresource(stagingTexture, 0); // Save the output bitmap.Save(outputFileName); // Capture done captureDone = true; } screenResource.Dispose(); duplicatedOutput.ReleaseFrame(); } catch (SharpDXException e) { if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { throw e; } } } // Display the texture using system associated viewer System.Diagnostics.Process.Start(Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, outputFileName))); // TODO: We should cleanp up all allocated COM objects here } 
+5
source share

You need to take the original source surface in the GPU memory and draw () on a smaller surface. This is due to simple vector / pixel shaders that some people with simple needs would rather avoid.

I would see if anyone had done a sprite lib for sharpdx. It should be a normal thing ... or using Direct2D (which is a lot more fun). Since D2D is the only user-mode library over D3D, it interacts very easily with D3D.

I never used SharpDx, but fFrom memory you would do something like this:

1.) Create an ID2D1Device by wrapping an existing DXGI device (make sure the dxgi device creation flag has D3D11_CREATE_DEVICE_BGRA_SUPPORT)

2.) Get ID2D1DeviceContext from your ID2D1Device

3.) Wrap your source and destination DXGI surfaces in D2D bitmaps using ID2D1DeviceContext :: CreateBitmapFromDxgiSurface

4.) ID2D1DeviceContext :: SetTarget of your target surface

5.) BeginDraw, ID2D1DeviceContext :: DrawBitmap, passing the original D2D bitmap. Endraw

6.) Save destination

+6
source share

Here is an example of a pixel ...

 d2d_device_context_h()->BeginDraw(); d2d_device_context_h()->SetTarget(mp_ppBitmap1.Get()); D2D1_SIZE_F rtSize = mp_ppBitmap1->GetSize(); rtSize.height *= (1.0f / cbpx.iPixelsize.y); rtSize.width *= (1.0f / cbpx.iPixelsize.x); D2D1_RECT_F rtRect = { 0.0f, 0.0f, rtSize.width, rtSize.height }; D2D1_SIZE_F rsSize = mp_ppBitmap0->GetSize(); D2D1_RECT_F rsRect = { 0.0f, 0.0f, rsSize.width, rsSize.height }; d2d_device_context_h()->DrawBitmap(mp_ppBitmap0.Get(), &rtRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, &rsRect); d2d_device_context_h()->SetTarget(mp_ppBitmap0.Get()); d2d_device_context_h()->DrawBitmap(mp_ppBitmap1.Get(), &rsRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, &rtRect); d2d_device_context_h()->EndDraw(); 

Where iPixelsize.xy is the size of the pixel, note that I just use linear interpolation when compressing BMP and NOT when I enlarge again. This will create a pixelation effect.

0
source share

All Articles