Jump to content

GetBilinearSample on last pixel in row or column


MJW
 Share

Recommended Posts

I'm confused about how GetBilinearSample is supposed to behave on the last pixel in a row or column.

 

The situation is complicated because pixels need to be numbered from the upper-left corners so that fetching a pixel using GetBilinearSample will return the exact pixel when the coordinates have integer values.  Pixels with non-integer coordinates are interpolated from the four surrounding pixels. The question is: how are samples handled when X is between Width-1 and Width? There are no rightward pixels to interpolate from.

 

The way I thought it worked, and the way I believe it used to work, is that the last pixel in a row was not interpolated in X; the last row was essentially clamped to provide the rightward pixels.  (Which also means the corner pixels are not interpolated at all.)

 

Now, based on what I've seen in some tiling code I wrote, it appears to me that the last row and column fade to transparency. So the rightward row is, in effect, transparent.

 

I can live with either, provided I know what it does, but I think the way I thought it worked is the better of the imperfect options. I think that if an opaque surface has a width of 100, there should be 100 opaque pixels in each row. even when subsampled.

 

 

Link to comment
Share on other sites

The answer is in the code :) I highly recommend getting comfortable with .NET Reflector in order to answer questions like this.

 

I'm not totally sure what the answer is for your question, but here's the code (via SurfaceBgraExtensions). The way it should work may not be the same as how it actually works in this code -- there were some things I learned about this space after its implementation.


        internal static unsafe ColorBgra GetBilinearSample<TSurfaceBgra>(ref TSurfaceBgra surface, float x, float y)
            where TSurfaceBgra : ISurface<ColorBgra>
        {
            if (!x.IsFinite() || !y.IsFinite())
            {
                return ColorBgra.Transparent;
            }

            byte* scan0 = (byte*)surface.Scan0;
            int width = surface.Width;
            int height = surface.Height;
            int stride = surface.Stride;

            float u = x;
            float v = y;

            if (u > -1 && v > -1 && u < width && v < height)
            {
                unchecked
                {
                    int iu = (int)Math.Floor(u);
                    uint sxfrac = (uint)(256 * (u - (float)iu));
                    uint sxfracinv = 256 - sxfrac;

                    int iv = (int)Math.Floor(v);
                    uint syfrac = (uint)(256 * (v - (float)iv));
                    uint syfracinv = 256 - syfrac;

                    uint wul = (uint)(sxfracinv * syfracinv);
                    uint wur = (uint)(sxfrac * syfracinv);
                    uint wll = (uint)(sxfracinv * syfrac);
                    uint wlr = (uint)(sxfrac * syfrac);

                    int sleft = iu;
                    int sright = iu + 1;
                    int stop = iv;
                    int sbottom = iv + 1;

                    ColorBgra* pstop = (ColorBgra*)(scan0 + ((long)stop * stride));
                    ColorBgra* psbottom = (ColorBgra*)(scan0 + ((long)sbottom * stride));

                    ColorBgra cul;
                    if (wul > 0 && sleft >= 0 && stop >= 0)
                    {
                        cul = *(pstop + sleft);
                    }
                    else
                    {
                        cul = ColorBgra.Transparent;
                    }

                    ColorBgra cur;
                    if (wur > 0 && sright < width && stop >= 0)
                    {
                        cur = *(pstop + sright);
                    }
                    else
                    {
                        cur = ColorBgra.Transparent;
                    }

                    ColorBgra cll;
                    if (wll > 0 && sleft >= 0 && sbottom < height)
                    {
                        cll = *(psbottom + sleft);
                    }
                    else
                    {
                        cll = ColorBgra.Transparent;
                    }

                    ColorBgra clr;
                    if (wlr > 0 && sright < width && sbottom < height)
                    {
                        clr = *(psbottom + sright);
                    }
                    else
                    {
                        clr = ColorBgra.Transparent;
                    }

                    ColorBgra c = ColorBgra.BlendColors4W16IP(cul, wul, cur, wur, cll, wll, clr, wlr);
                    return c;
                }
            }
            else
            {
                return ColorBgra.Transparent;
            }
        }

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

Thanks, Rick. I actually had looked at the code with ILSpy, but I wasn't sure what it was trying to achieve. I was hoping to learn the guiding principle behind it. In particular, I wondered whether the fading out of the last pixel in each row and column is intended or a bug.

 

Your comment reminded me that once the left, right, top, and bottom interpolation colors are determined, the actual interpolation can be performed with BlendColors4W16IP. So if GetBilinearSample doesn't serve my needs, it isn't difficult to write my own version.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...