latrokles / yak

personal yak shaving adventures...

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

pixel coordinates beyond width are wrapping around?

latrokles opened this issue · comments

playing with the linepen example I noticed some wraparound pixels (?) when going beyond the window's width.
capture_crop

some ideas:

  • mouse coordinates?
  • incorrect width calculation (off by 1)?
  • incorrect pixel coordinates calculation?
  • sdl stretching?

pfft... this turned out to be an off by one error/bounds checking issue...

  • fb has width and height attributes
  • the addressable x values go from 0 to width - 1
  • the addressable y values go from 0 to height - 1

Since the fb memory is a 1D bytearray I have to convert x, y coordinates to an index in that 1D bytearray. This is done by moving y number of rows through the bytearray and then moving x columns on the last row, which is described by the formula index = (y * width) + x.

There's some bounds checking in the put_pixel method that results in a NOOP if we go beyond the framebuffer bounds, but it had a incorrect bound check that allowed setting a pixel when addressing 1 position beyond the max addressable index in both x and y. This isn't an issue in y because using that formula above we go off the bounds of the 1D bytearray that backs the framebuffer, but with x it results in an off by one error setting the 0th column of the next row (also leaving the 0th row unset).

I confirmed this with a simple sketch that drew a line from width, 0 to width, height and it drew a line along the 0th column of the framebuffer. Technically, that line as requested would be out of bounds and shouldn't be drawn. Fixing the bounds checking code resolved this.

from dataclasses import dataclass

from yakdraw.color import Palette
from yakdraw.draw import draw_line
from yakdraw.sketch import Sketch


@dataclass
class BugFinder(Sketch):
    def setup(self):
        for y in range(self.height):
            for x in range(self.width):
                self.canvas.put_pixel(x, y, Palette.WHITE)

    def draw(self):
        draw_line(self.canvas, self.width - 1, 0, self.width, self.height, Palette.BLUE1)

Commit incoming.