Menu

#723 Bug in gfxlib's line statement

closed
nobody
None
gfxlib2
2014-11-06
2014-03-24
glagnar
No

Using fbc 0.90.1 win32 (7-17-2013) for this.

It appears that line has an issue interpreting patterns/bitmasks when the drawing area is too close to the horizontal end of the screen.

dim as unsigned short pat = &b0000000011111111
dim as integer patwidth = 16
dim as integer w = 640, h = 480

screenres w, h, 32

line (0, 0)-(639, 479), rgb(0, 128, 128), bf

line (0, 10)-step(patwidth - 1, 0), rgb(0, 0, 0)
line (w - patwidth + 1, 10)-step(patwidth - 1, 0), rgb(0, 0, 0)

line (0, 10)-step(patwidth - 1, 0), rgb(255, 255, 255), , pat
line (w - patwidth + 1, 10)-step(patwidth - 1, 0), rgb(255, 255, 255), , pat


sleep

Running this will produce two lines using the same pattern, drawing a black unpatterned line first, then a white patterned one over it. The one on the left begins with black, then becomes white when the pattern kicks in. The one on the right is in reverse order. It appears as though the pattern begins reading from the LSB first when the end of the line extends past the end of the screen. Changing the width value in the step pair to patwidth - 2 corrects the issue.

Is this intended behavior, or compatibility with a QB-inherited quirk in some way? (I no longer have an active installation of QB, being on 64-bit Windows, so I couldn't check.)

Discussion

  • glagnar

    glagnar - 2014-03-26

    This also appears to happen near the end of any FB.IMAGE buffer created by imagecreate().

     
  • dkl

    dkl - 2014-03-27

    I can reproduce the issue. This program also shows how the behaviour changes near the screen edges:

    var pat = &b0000000011111111
    var patwidth = 16
    var w = 100, h = 200
    
    screenres w, h, 32
    
    line (0, 0)-(w-1, h-1), rgb(0, 128, 128), bf
    
    var y = 15
    for x as integer = -patwidth to w-1
        line (x, y) - step (patwidth - 1, 0), rgb(0, 0, 0)
        line (x, y) - step (patwidth - 1, 0), rgb(255, 255, 255), , pat
        y += 1
    next
    
    sleep
    

    On the left edge of the screen, when starting drawing with negative x values, I see that the pattern is applied to the actual pixels drawn, excluding the logical pixels that are being clipped. I think that's wrong behaviour. It should apply the pattern to the logically drawn pixels, no matter whether they're being clipped or not.

    On the right edge of the screen, when starting drawing at screenwidth-patternwidth+1 it apparently turns around the pattern (or swaps the pattern bytes) which I think is wrong behaviour too. Here it also should apply the pattern to logically drawn pixels, no matter what clipping happens.

     
  • dkl

    dkl - 2014-05-22

    I've looked at the source code of fb_GfxLine(), and turns out that it optimizes drawing of horizontal and vertical lines, as opposed to diagonal ones.

    Fixing this issue for the horizontal/vertical cases is relatively easy, but fixing it for the diagonal case is a different story, because it's not possible (really? at least not as easy) to calculate how many pixels would have been drawn if they hadn't been clipped. The clipping is done before the drawing algorithm starts...

    The only way I can see to fix it would be to adjust the algorithm to do clipping for every pixel, instead of the whole line at once. However, that is much slower, and to prevent the case when no pattern is used at all from becoming slower, we'd need to duplicate a whole lot of code...

     

    Last edit: dkl 2014-05-22
  • Matthew

    Matthew - 2014-11-06
    • status: open --> closed
     
  • Matthew

    Matthew - 2014-11-06

    I've reimplemented the clipping code in [870ed1], and learned quite a bit about Bresenham's algorithm in the process.

    Here's some mouse-driven testing code:

    #include "fbgfx.bi"
    using fb
    screen 13
    dim as integer x1, x2, y1, y2, b
    
    do
        if b = 0 then
            getmouse(x1, y1, , b)
            view screen: cls
            view screen (160-20,100-20)-(160+19,100+19), , 7
        else
            do: sleep 1: loop while getmouse(x2, y2, , b)
    
            if multikey(sc_lshift) or multikey(sc_rshift) then
                '' hold shift to get exact 45/90 degree angles
                if abs(y2 - y1) > 2 * abs(x2 - x1) then
                    x2 = x1
                elseif abs(y2 - y1) > abs(x2 - x1) then
                    x2 = x1 + abs(y2 - y1) * sgn(x2 - x1)
                elseif abs(x2 - x1) > 2 * abs(y2 - y1) then
                    y2 = y1
                else
                    y2 = y1 + abs(x2 - x1) * sgn(y2 - y1)
                end if
            end if
    
            screenlock
    
            view screen: cls
            line (x1-1,y1-1)-step(2,2), 9, b
    
            line (x1, y1)-(x2, y2), 10, ,&h8632
    
            view screen (160-20,100-20)-(160+19,100+19), , 7
            line (x1, y1)-(x2, y2), 12, ,&h8632
    
            screenunlock
        end if
        sleep 1
    loop until len(inkey)
    

    No green pixels should appear inside the box: the clipped line should perfectly cover the unclipped line.

     

    Related

    Commit: [870ed1]


Log in to post a comment.

MongoDB Logo MongoDB