Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
MWstudios opened this issue Apr 7, 2021 · 6 comments

Comments

@MWstudios
Copy link

MWstudios commented Apr 7, 2021

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
Copy link
Author

MWstudios commented Apr 8, 2021

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
Copy link
Owner

jingwood commented Apr 8, 2021

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

@jingwood
Copy link
Owner

jingwood commented Apr 8, 2021

Similar to #27, #44

@d2phap
Copy link

d2phap commented Jun 11, 2022

Hi @jingwood
Any update on this?

@MWstudios
Copy link
Author

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
Copy link

Charltsing commented May 3, 2024

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;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants