jingwood / d2dlib

A .NET library for hardware-accelerated, high performance, immediate mode rendering via Direct2D.
MIT License
234 stars 40 forks source link

Suggestion: Add Direct2D counterparts for GetPixel, SetPixel and Graphics.FromImage #61

Open MWstudios opened 3 years ago

MWstudios commented 3 years ago

I need to pick a color from a bitmap, but the GetPixel function only exists in the default Bitmap class. Also graphics from image would allow drawing on top of an existing bitmap and not creating a blank one, which Device.CreateBitmapGraphics is doing.

Edit: You can make an empty graphic and draw the bitmap on it, but I meant that you can't directly edit the existing bitmap, just paste it onto the graphic, and there's no way to get the result back.

MWstudios commented 3 years ago

Okay, so I've created a fork of this project and tried to get at least a bitmap of the graphic. I seem to be pretty close now, as it actually gets a bitmap of the correct size, but the contents are all black.

Context.cpp:

HBITMAP ContextToHbitmap(HANDLE ctx)
{
    RetrieveContext(ctx);
    return CreateCompatibleBitmap(CreateCompatibleDC(GetDC(context->renderTarget->GetHwnd())),
        context->renderTarget->GetSize().width, context->renderTarget->GetSize().height);
}

D2DGraphics.cs:

public HANDLE GetHBitmap()
{
    return D2D.ContextToHbitmap(this.Handle);
}

I'm leaving this here if someone wants to finish it.

jingwood commented 3 years ago

Thanks @MWstudios! I'm also working on a task like this. I will let you know if there is any progress.

jingwood commented 3 years ago

Similar to #27, #44

d2phap commented 2 years ago

Hi @jingwood Any update on this?

MWstudios commented 2 months ago

Bumping an old thread, but if any contributor out there is reading this, there is a way: ID2D1Bitmap1::Map() and ID2D1Bitmap1::Unmap() Which maps the entire bitmap from GPU to CPU memory. The problem is that it must be ID2D1Bitmap1 from a newer DirectX version (though still available on Windows 7) and the bitmap must be made with the flag D2D1_BITMAP_OPTIONS_CPU_READ. This would make deep impacts in the library, and I'm unsure if the flag slows down the performance in any way, but it is possible.

When we're at it, it's probably more effective to add Map() and Unmap(), rather than GetPixel() and SetPixel() and then needlessly map the entire thing on every call.

Charltsing commented 2 months ago

https://github.com/d2phap/ImageGlass/blob/develop/Source/Components/ImageGlass.Base/BHelper/Extensions/ID2D1Bitmap1Extensions.cs

public static Color GetPixelColor(this IComObject? srcBitmap1, IComObject? dc, int x, int y) { if (srcBitmap1 == null || dc == null) return Color.Transparent;

    var bmpProps = new D2D1_BITMAP_PROPERTIES1()
    {
        bitmapOptions = D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_CANNOT_DRAW | D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_CPU_READ,
        pixelFormat = new D2D1_PIXEL_FORMAT()
        {
            alphaMode = D2D1_ALPHA_MODE.D2D1_ALPHA_MODE_PREMULTIPLIED,
            format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
        },
        dpiX = 96.0f,
        dpiY = 96.0f,
    };

    var size = srcBitmap1.GetSize().ToD2D_SIZE_U();
    using var bitmap1 = dc.CreateBitmap<ID2D1Bitmap1>(size, bmpProps);
    bitmap1.CopyFromBitmap(srcBitmap1);

    var map = bitmap1.Map(D2D1_MAP_OPTIONS.D2D1_MAP_OPTIONS_READ);
    var startIndex = (y * map.pitch) + (x * 4);

    var bytes = new byte[4];
    Marshal.Copy((nint)(map.bits + startIndex), bytes, 0, bytes.Length);
    bitmap1.Unmap();

    // since pixel data is D2D1_ALPHA_MODE_PREMULTIPLIED,
    // we need to re-calculate the color values
    var a = bytes[3];
    var alphaPremultiplied = a / 255f;

    var r = (byte)(bytes[2] / alphaPremultiplied);
    var g = (byte)(bytes[1] / alphaPremultiplied);
    var b = (byte)(bytes[0] / alphaPremultiplied);

    var color = Color.FromArgb(a, r, g, b);

    return color;
}