2014-06-05 32 views
7

Chcę zmienić rozmiar ekranu przechwyconego za pomocą interfejsu Desktop Duplication API w SharpDX. Używam Screen Capture sample code from the SharpDX Samples repository, odpowiednia część następuje :.Zmiana rozmiaru zasobu DXGI lub tekstury Texture2D w SharpDX

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... 

Chciałbym zmienić rozmiar obrazu o wiele mniejszy niż rzeczywisty rozmiar ekranu przed przetwarzaniem go jako tablicy bajtów. Nie muszę zapisywać obrazu, tylko pobrać bajty. Chciałbym to zrobić stosunkowo szybko i skutecznie (na przykład, jeśli to możliwe, wykorzystując GPU).

Nie mogę przeskalować podczas CopyResource, ponieważ wymiary wyjściowe muszą być takie same, jak wymiary wejściowe. Czy mogę wykonać kolejną kopię z mojej skali screenTexture2D? Jak dokładnie skalować zasób - czy używam transformacji Swap, przekształcenia macierzy lub czegoś innego?

Odpowiedz

5

Jeśli są w porządku skalowanie do potęgi dwójki z ekranu, można to zrobić poprzez:

  • Tworzenie mniejszą tekstury z RenderTarget/ShaderResource użytkowania oraz opcje GenerateMipMaps, ten sam rozmiar ekranu, liczba mipcount> 1 (2 dla rozmiaru/2, 3 dla posiadania /4...etc.).
  • kopii pierwszego mipmapa tekstury ekranowych do mniejszych tekstury
  • DeviceContext.GenerateMipMaps o mniejszej tekstury
  • skopiować wybrane mimap mniejszego tekstury (1:/2, 2:. /4...etc) na fakturze pomostowym (które powinny być również uznane za mniejsze, czyli taki sam rozmiar jak mipmapa który ma być używany)

szybkie siekać na oryginalny kod, aby wygenerować/2 tekstury byłoby tak:

[STAThread] 
    private static void Main() 
    { 
     // # of graphics card adapter 
     const int numAdapter = 0; 

     // # of output device (i.e. 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 
    } 
6

Musisz wziąć oryginalną powierzchnię źródłową w pamięci GPU i przeciągnąć() na mniejszą powierzchnię. Obejmuje to proste shadery wektorowe/pikselowe, które niektórzy z prostymi potrzebami wolą ominąć.

Chciałbym sprawdzić, czy ktoś zrobił sprite lib dla sharpdx. Powinny być wspólne "rzeczy" ... lub używając Direct2D (co jest o wiele zabawniejsze). Ponieważ D2D jest tylko biblioteką trybu użytkownika nad D3D, bardzo łatwo wchodzi w interakcje z D3D.

nigdy nie używałem SharpDx, ale fFrom pamięć byś zrobił coś takiego:

1.) Utwórz ID2D1Device, owijając swoje istniejące urządzenie DXGI (upewnij się, że urządzenie tworzenie flag dxgi ma D3D11_CREATE_DEVICE_BGRA_SUPPORT)

2.) Pobierz ID2D1DeviceContext z ID2D1Device

3.) owinąć powierzchnie źródłowy i docelowy DXGI język bitmap D2D z ID2D1DeviceContext :: CreateBitmapFromDxgiSurface

4.) ID2D1DeviceContext :: SetTarget docelowej powierzchni

5.) BeginDraw, ID2D1DeviceContext :: DrawBitmap, przekazując źródłową bitmapę D2D. EndDraw

6.) Zapisz cel

0

Oto przykład piksetu ...

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(); 

Gdzie iPixelsize.xy jest wielkość piksela „piksele”, trzeba pamiętać, że po prostu użyć interpolacji liniowej, gdy kurczy BMP a nie kiedy ja reenlarge. Spowoduje to efekt pikselu.