簡體   English   中英

使用 SharpDX 調整 Texture2D(打印屏幕)的大小

[英]Resize Texture2D (printscreen) with SharpDX

以下問題回答了如何通過 SharpDX 中的 DXGI Resource 或 Texture2D的 2 次方來調整使用 SharpDX 拍攝的打印屏幕的大小。 我正在嘗試通過可變數量調整打印屏幕的大小(例如原始大小的 80% - 不一定是 2 的冪)。 現在,我通過調整打印屏幕生成的 bitmap 的大小找到了“一種實現目標的方法”。 我通過首先轉換為WicImage來實現這一點:

    private void button1_Click(object sender, EventArgs e)
    {
        Stopwatch stopWatchInstance = Stopwatch.StartNew();
        //or Bitmap.save(new filestream)
        var stream = File.OpenRead("c:\\test\\pc.png");
        
        var test = DrawResizedImage(stream);
        stopWatchInstance.Stop();

        File.WriteAllBytes("c:\\test\\result.png", test.ToArray());

        int previousCalculationTimeServer = (int)(stopWatchInstance.ElapsedMilliseconds % Int32.MaxValue);
    }


    MemoryStream DrawResizedImage(Stream fileName)
    {
        ImagingFactory wic = new WIC.ImagingFactory();
        D2D.Factory d2d = new D2D.Factory();
        FormatConverter image = CreateWicImage(wic, fileName);
        var wicBitmap = new WIC.Bitmap(wic, image.Size.Width, image.Size.Height, WIC.PixelFormat.Format32bppPBGRA, WIC.BitmapCreateCacheOption.CacheOnDemand);
        var target = new D2D.WicRenderTarget(d2d, wicBitmap, new D2D.RenderTargetProperties());
        var bmpPicture = D2D.Bitmap.FromWicBitmap(target, image);

        target.BeginDraw();
        {
            target.DrawBitmap(bmpPicture, new SharpDX.RectangleF(0, 0, target.Size.Width, target.Size.Height), 1.0f, D2D.BitmapInterpolationMode.Linear);
        }
        target.EndDraw();

        var ms = new MemoryStream();
        SaveD2DBitmap(wic, wicBitmap, ms);
        return ms;
    }

    void SaveD2DBitmap(WIC.ImagingFactory wicFactory, WIC.Bitmap wicBitmap, Stream outputStream)
    {
        var encoder = new WIC.BitmapEncoder(wicFactory, WIC.ContainerFormatGuids.Png);
        encoder.Initialize(outputStream);
        var frame = new WIC.BitmapFrameEncode(encoder);

        frame.Initialize();
        frame.SetSize(wicBitmap.Size.Width, wicBitmap.Size.Height);

        var pixelFormat = wicBitmap.PixelFormat;
        frame.SetPixelFormat(ref pixelFormat);
        frame.WriteSource(wicBitmap);

        frame.Commit();
        encoder.Commit();
    }

    WIC.FormatConverter CreateWicImage(WIC.ImagingFactory wicFactory, Stream stream)
    {
        var decoder = new WIC.PngBitmapDecoder(wicFactory);

        var decodeStream = new WIC.WICStream(wicFactory, stream);
        decoder.Initialize(decodeStream, WIC.DecodeOptions.CacheOnLoad);
        var decodeFrame = decoder.GetFrame(0);

        var scaler = new BitmapScaler(wicFactory);
        scaler.Initialize(decodeFrame, 2000, 2000, SharpDX.WIC.BitmapInterpolationMode.Fant);
        var test = (BitmapSource)scaler;

        var converter = new WIC.FormatConverter(wicFactory);
        converter.Initialize(test, WIC.PixelFormat.Format32bppPBGRA);
        return converter;
    }

單擊按鈕后,上述代碼將 bitmap(包含打印屏幕)的大小調整為 2000x2000。 但是,上面的代碼很慢,大約需要200ms(不考慮fileread和filewrite的時間)。 我使用 BitmapScaler 來調整大小。

有誰知道如何可變地調整 SharpDX 中Resizing a DXGI Resource 或 Texture2D問題產生的 output 的大小,因此調整大小變得更快? 我試圖尋找文檔以將位圖縮放器直接應用於已回答代碼中的任何對象,但沒有成功。

我上傳了上面的代碼,可以找到一個小的 Visual Studio 項目,它可以編譯

這是您的程序的重寫和注釋版本,它使用 DXGI 的 Output 復制捕獲桌面,使用 Direct2D 使用任意比例調整大小,並使用 WIC 將其保存為 a.jpeg 文件。

它僅適用於 GPU,直到使用 WIC 將圖像保存到文件(流)中。 在我的 PC 上,捕獲和調整大小需要 10-15 毫秒,WIC 保存到文件需要 30-40 毫秒。

我沒有使用我在評論中談到的 D2D 縮放效果,因為ID2D1DeviceContext::DrawBitmap方法可以使用各種插值因子調整其大小,而無需使用任何效果。 但是您可以使用相同的代碼來應用硬件加速效果

請注意,我在button1_Click中創建和處置的一些對象可以在構造函數(如工廠等)中創建並重用。

using System;
using System.Windows.Forms;
using System.IO;
using DXGI = SharpDX.DXGI;
using D3D11 = SharpDX.Direct3D11;
using D2D = SharpDX.Direct2D1;
using WIC = SharpDX.WIC;
using Interop = SharpDX.Mathematics.Interop;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private readonly D3D11.Device _device;
        private readonly DXGI.OutputDuplication _outputDuplication;

        public Form1()
        {
            InitializeComponent();

            var adapterIndex = 0; // adapter index
            var outputIndex = 0; // output index

            using (var dxgiFactory = new DXGI.Factory1())
            using (var dxgiAdapter = dxgiFactory.GetAdapter1(adapterIndex))
            using (var output = dxgiAdapter.GetOutput(outputIndex))
            using (var dxgiOutput = output.QueryInterface<DXGI.Output1>())
            {
                _device = new D3D11.Device(dxgiAdapter,
#if DEBUG
                    D3D11.DeviceCreationFlags.Debug |
#endif
                    D3D11.DeviceCreationFlags.BgraSupport); // for D2D support

                _outputDuplication = dxgiOutput.DuplicateOutput(_device);
            }
        }

        protected override void Dispose(bool disposing) // remove from Designer.cs
        {
            if (disposing && components != null)
            {
                components.Dispose();
                _outputDuplication?.Dispose();
                _device?.Dispose();
            }
            base.Dispose(disposing);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var ratio = 0.8; // resize ratio

            using (var dxgiDevice = _device.QueryInterface<DXGI.Device>())
            using (var d2dFactory = new D2D.Factory1())
            using (var d2dDevice = new D2D.Device(d2dFactory, dxgiDevice))
            {
                // acquire frame
                _outputDuplication.AcquireNextFrame(10000, out var _, out var frame);
                using (frame)
                {
                    // get DXGI surface/bitmap from resource
                    using (var frameDc = new D2D.DeviceContext(d2dDevice, D2D.DeviceContextOptions.None))
                    using (var frameSurface = frame.QueryInterface<DXGI.Surface>())
                    using (var frameBitmap = new D2D.Bitmap1(frameDc, frameSurface))
                    {
                        // create a GPU resized texture/surface/bitmap
                        var desc = new D3D11.Texture2DDescription
                        {
                            CpuAccessFlags = D3D11.CpuAccessFlags.None, // only GPU
                            BindFlags = D3D11.BindFlags.RenderTarget, // to use D2D
                            Format = DXGI.Format.B8G8R8A8_UNorm,
                            Width = (int)(frameSurface.Description.Width * ratio),
                            Height = (int)(frameSurface.Description.Height * ratio),
                            OptionFlags = D3D11.ResourceOptionFlags.None,
                            MipLevels = 1,
                            ArraySize = 1,
                            SampleDescription = { Count = 1, Quality = 0 },
                            Usage = D3D11.ResourceUsage.Default
                        };
                        using (var texture = new D3D11.Texture2D(_device, desc))
                        using (var textureDc = new D2D.DeviceContext(d2dDevice, D2D.DeviceContextOptions.None)) // create a D2D device context
                        using (var textureSurface = texture.QueryInterface<DXGI.Surface>()) // this texture is a DXGI surface
                        using (var textureBitmap = new D2D.Bitmap1(textureDc, textureSurface)) // we can create a GPU bitmap on a DXGI surface
                        {
                            // associate the DC with the GPU texture/surface/bitmap
                            textureDc.Target = textureBitmap;

                            // this is were we draw on the GPU texture/surface
                            textureDc.BeginDraw();

                            // this will automatically resize
                            textureDc.DrawBitmap(
                                frameBitmap,
                                new Interop.RawRectangleF(0, 0, desc.Width, desc.Height),
                                1,
                                D2D.InterpolationMode.HighQualityCubic, // change this for quality vs speed
                                null,
                                null);

                            // commit draw
                            textureDc.EndDraw();

                            // now save the file, create a WIC (jpeg) encoder
                            using (var file = File.OpenWrite("test.jpg"))
                            using (var wic = new WIC.ImagingFactory2())
                            using (var jpegEncoder = new WIC.BitmapEncoder(wic, WIC.ContainerFormatGuids.Jpeg))
                            {
                                jpegEncoder.Initialize(file);
                                using (var jpegFrame = new WIC.BitmapFrameEncode(jpegEncoder))
                                {
                                    jpegFrame.Initialize();

                                    // here we use the ImageEncoder (IWICImageEncoder)
                                    // that can write any D2D bitmap directly
                                    using (var imageEncoder = new WIC.ImageEncoder(wic, d2dDevice))
                                    {
                                        imageEncoder.WriteFrame(textureBitmap, jpegFrame, new WIC.ImageParameters(
                                            new D2D.PixelFormat(desc.Format, D2D.AlphaMode.Premultiplied),
                                            textureDc.DotsPerInch.Width,
                                            textureDc.DotsPerInch.Height,
                                            0,
                                            0,
                                            desc.Width,
                                            desc.Height));
                                    }

                                    // commit
                                    jpegFrame.Commit();
                                    jpegEncoder.Commit();
                                }

                            }
                        }
                    }
                }
                _outputDuplication.ReleaseFrame();
            }
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM