null54 Posted August 15 Share Posted August 15 My current GDI+ code renders the checkerboard to its own Bitmap, and then performs a double-buffered copy to render it to the target Graphics object. The double buffering is used to prevent flickering when the main image is rendered over the checkerboard. Here is a link to the code for DrawCheckerboardBitmap: https://github.com/0xC0000054/PSFilterPdn/blob/61aef4befe814e8043e208334fb7bfc17aed98b6/src/PSApi/LoadPsFilter.cs#L2526 I did not include a copy of its code to reduce the length of this post. int width = displaySurface.Width; int height = displaySurface.Height; if (checkerBoardBitmap == null || checkerBoardBitmap.Width != width || checkerBoardBitmap.Height != height) { DrawCheckerBoardBitmap(width, height); } // Use a temporary bitmap to prevent flickering when the image is rendered over the checker board. using (Bitmap temp = new(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { Rectangle rect = new(0, 0, width, height); using (Graphics tempGr = Graphics.FromImage(temp)) { tempGr.DrawImageUnscaled(checkerBoardBitmap, rect); tempGr.DrawImageUnscaled(aliasedDisplayPixelsBitmap, rect); } hdc.DrawImageUnscaled(temp, dstX, dstY); } Based on the existence of the BeginDraw and EndDraw methods, I am assuming that Direct2D does not require the manual double buffering. Is there a more efficient method of rendering the checkerboard than creating a full-size image? Looking at the ID2D1RenderTarget documentation, it appears that an ID2D1BitmapBrush could be used with the ID2DRenderTarget FillRectangle method. Quote Plugin Pack | PSFilterPdn | Content Aware Fill | G'MIC | Paint Shop Pro Filetype | RAW Filetype | WebP Filetype The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait Link to comment Share on other sites More sharing options...
Rick Brewster Posted August 16 Share Posted August 16 3 hours ago, null54 said: I am assuming that Direct2D does not require the manual double buffering 100% correct 3 hours ago, null54 said: it appears that an ID2D1BitmapBrush could be used with the ID2DRenderTarget FillRectangle method. That's exactly what I do for the canvas checkerboard. I prepare a regular IBitmap<ColorPbgra32> with only the top-left 2x2 squares of the checkerboard. At 96 DPI this is only an 8x8 bitmap (I use the integer floor of the DPI/scaling factor in order to scale the 8x8 pixel size). Load it into an IDeviceBitmap (aka ID2D1Bitmap1). Then create an IBitmapBrush (aka ID2D1BitmapBrush) around that with InterpolationMode.NearestNeighbor. I then do a using (dc.UseTransform(Matrix3x2Float.Identity, MatrixMultiplyOrder.Replace)) and then FillRectangle with the brush in the appropriate location. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Rick Brewster Posted August 16 Share Posted August 16 Also, if you're not doing any scaling (zooming), you don't need to worry about the transform. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
null54 Posted August 16 Author Share Posted August 16 11 hours ago, Rick Brewster said: I prepare a regular IBitmap<ColorPbgra32> with only the top-left 2x2 squares of the checkerboard. At 96 DPI this is only an 8x8 bitmap (I use the integer floor of the DPI/scaling factor in order to scale the 8x8 pixel size). Load it into an IDeviceBitmap (aka ID2D1Bitmap1). Then create an IBitmapBrush (aka ID2D1BitmapBrush) around that with InterpolationMode.NearestNeighbor. Is it an 8x8 bitmap or 16x16? I had to use 16x16 with the following code, otherwise it rendered as a solid color. int dpiScaleFactor = (int)Math.Floor(UIScaleFactor.Current.Scale); int size = 16 * dpiScaleFactor; bitmap = new WICBitmapSurface<ColorPbgra32>(size, size, imagingFactory); using (Surface checkerboardSurface = new(size, size)) { checkerboardSurface.ClearWithCheckerboardPattern(); RegionPtr<ColorBgra> region = checkerboardSurface.AsRegionPtr(); PixelKernels.SetAlphaChannel(region.Cast<ColorBgra32>(), ColorAlpha8.Opaque); RegionPtr<ColorPbgra32> premultiplied = region.Cast<ColorPbgra32>(); PixelKernels.ConvertBgra32ToPbgra32(premultiplied); using (ISurfaceLock surfaceLock = bitmap.Lock(SurfaceLockMode.Write)) { RegionPtr<ColorPbgra32> dst = new((ColorPbgra32*)surfaceLock.Buffer, surfaceLock.Width, surfaceLock.Height, surfaceLock.BufferStride); premultiplied.CopyTo(dst); } } Quote Plugin Pack | PSFilterPdn | Content Aware Fill | G'MIC | Paint Shop Pro Filetype | RAW Filetype | WebP Filetype The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait Link to comment Share on other sites More sharing options...
Rick Brewster Posted August 16 Share Posted August 16 4 hours ago, null54 said: Is it an 8x8 bitmap or 16x16? Right, it's 16x16 -- each square is 8x8. I misread my code. You don't need to use ClearWithCheckerboardPattern(), which is a weird old method that doesn't give you, or allow you to provide, any layout or colorization information. Just clear with solid color (light or dark), then fill in the diagonal squares with the other color. Also, there's no reason to use ConvertBgra32ToPbgra32() if the pixels are already opaque. You can also use Direct2DPictureBox if you're working with a static image (or mostly static -- you can still update it with InvalidateBitmap(), but it will re-upload the whole thing to the GPU). It has an EnableAlphaCheckerboard property you can enable. You can derive from the class and override OnRenderBackground() and OnRenderForeground() to draw decorations below or above the bitmap. You can also disassemble that class (e.g. ILSpy) and just recreate what it does. You won't be able to use AffineTransform2DEffect2, however, which means you'll need to limit your zooming/scaling so that you don't run into an IntermediateTooLargeException (AffineTransform2DEffect (non-2), and ScaleEffect, cannot scale below a certain size). If necessary I can also look at providing some additional classes in PDN to help out with your scenario(s) Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
null54 Posted August 16 Author Share Posted August 16 3 hours ago, Rick Brewster said: You don't need to use ClearWithCheckerboardPattern(), which is a weird old method that doesn't give you, or allow you to provide, any layout or colorization information. Just clear with solid color (light or dark), then fill in the diagonal squares with the other color. I used ClearWithCheckerboardPattern() because it was the only public API I could find that would allow a plugin get a checkerboard with the same colors as the native PDN one. 4 hours ago, Rick Brewster said: You can also use Direct2DPictureBox if you're working with a static image (or mostly static -- you can still update it with InvalidateBitmap(), but it will re-upload the whole thing to the GPU). It has an EnableAlphaCheckerboard property you can enable. You can derive from the class and override OnRenderBackground() and OnRenderForeground() to draw decorations below or above the bitmap. That would be the way to go for almost any other plugin, but in PSFilterPdn I am implementing a Photoshop plugin API that only provides the plugin host with the pixels to render and the destination HDC. 4 hours ago, Rick Brewster said: If necessary I can also look at providing some additional classes in PDN to help out with your scenario(s) As I mentioned above, PSFilterPdn only requires a checkerboard that matches the PDN canvas. But an ICheckerboardRendererService would be useful for most of the tool plugins, perhaps something like: public enum CheckerboardTileSize { Small, Medium, Large } public interface ICheckerboardRendererService { Render(RegionPtr<ColorBgra32> destination); // Other overloads as necessary Render(RegionPtr<ColorBgra32> destination, Point2Int32 srcOffset, ColorBgr32 light, ColorBgr32 dark, CheckerboardTileSize tileSize); } The single parameter overload would render the same checkerboard as the PDN canvas, with the other overloads allowing the plugin to customize the checkerboard. The proposed API probably need some refinement, I was basing it off the methods in CheckerboardUtil. Another API that would probably be useful for the various tool plugins that show their own dialogs (e.g. Shapemaker) is an ICanvasUIColorsService: public interface ICanvasUIColorsService { // The color surrounding the outside of the canvas ColorBgr32 Background { get; } ColorBgr32 CheckerboardDark { get; } ColorBgr32 CheckerboardLight { get; } } Quote Plugin Pack | PSFilterPdn | Content Aware Fill | G'MIC | Paint Shop Pro Filetype | RAW Filetype | WebP Filetype The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait Link to comment Share on other sites More sharing options...
Rick Brewster Posted August 19 Share Posted August 19 Okay, those services will be available in 5.0.10. It will be called IVisualStylingService. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.