(self, lcoord, fill_attr, pattern, border_attr, bg_pattern)
Fill an area defined by a border attribute with a tiled pattern.
Fill an area defined by a border attribute with a tiled pattern.
Fill an area defined by a border attribute with a tiled pattern .
def _flood_fill(self, lcoord, fill_attr, pattern, border_attr, bg_pattern):
        """Fill an area defined by a border attribute with a tiled pattern."""
        # 4-way scanline flood fill: http://en.wikipedia.org/wiki/Flood_fill
        # flood fill stops on border colour in all directions;
        # it also stops on scanlines in fill_colour
        # pattern tiling stops at intervals that equal the pattern to be drawn,
        # unless this pattern is also equal to the background pattern.
        is_solid = (pattern is None)
        bg_tile = None
        if is_solid:
            tile = bytematrix.ByteMatrix(1, 8, fill_attr)
        else:
            tile = self._mode.build_tile(bytearray(pattern)) if pattern else None
            if bg_pattern:
                bg_tile = self._mode.build_tile(bytearray(bg_pattern))
                # only use first row of background
                bg_tile = bg_tile[:1, :]
                # illegal tile/background combo's:
                # all, or more than two consecutive, rows equal background
                for row in range(max(1, tile.height-2)):
                    comptile = tile[row:row+3, :]
                    if comptile == bg_tile.vtile(comptile.height):
                        raise error.BASICError(error.IFC)
        # viewport bounds in viewport coordinates
        bound_x0, bound_y0, bound_x1, bound_y1 = self.graph_view.get_bounds()
        x, y = self._get_window_physical(*lcoord)
        line_seed = [(x, x, y, 0)]
        # paint nothing if seed is out of bounds
        if x < bound_x0 or x > bound_x1 or y < bound_y0 or y > bound_y1:
            return
        self._last_point = x, y
        # paint nothing if we start on border attrib
        if self.graph_view[y, x] == border_attr:
            return
        while len(line_seed) > 0:
            # consider next interval
            x_start, x_stop, y, ydir = line_seed.pop()
            # extend interval as far as it goes to left and right
            x_left = x_start - self._scanline_until(border_attr, y, x_start-1, bound_x0-1).width
            x_right = x_stop + self._scanline_until(border_attr, y, x_stop+1, bound_x1+1).width
            # check next scanlines and add intervals to the list
            if ydir == 0:
                if y + 1 <= bound_y1:
                    line_seed = self._check_scanline(
                        line_seed, x_left, x_right, y+1, tile, is_solid, bg_tile, border_attr, 1
                    )
                if y - 1 >= bound_y0:
                    line_seed = self._check_scanline(
                        line_seed, x_left, x_right, y-1, tile, is_solid, bg_tile, border_attr, -1
                    )
            else:
                # check the same interval one scanline onward in the same direction
                if y+ydir <= bound_y1 and y+ydir >= bound_y0:
                    line_seed = self._check_scanline(
                        line_seed, x_left, x_right, y+ydir, tile, is_solid, bg_tile, border_attr, ydir
                    )
                # check any bit of the interval that was extended one scanline backward
                # this is where the flood fill goes around corners.
                if y-ydir <= bound_y1 and y-ydir >= bound_y0:
                    line_seed = self._check_scanline(
                        line_seed, x_left, x_start-1, y-ydir, tile, is_solid, bg_tile, border_attr, -ydir
                    )
                    line_seed = self._check_scanline(
                        line_seed, x_stop+1, x_right, y-ydir, tile, is_solid, bg_tile, border_attr, -ydir
                    )
            # draw the pixels for the current interval
            if is_solid:
                self.graph_view[y, x_left:x_right+1] = tile[0, 0]
            else:
                # convert tile to a list of attributes
                tilerow = tile[y % tile.height, :]
                n_tiles = 1 + (x_right+1) // tile.width - (x_left // tile.width)
                tiles = bytematrix.hstack((tilerow,) * n_tiles)
                interval = tiles[
                    :,
                    (x_left%tile.width) : (x_left%tile.width) + x_right - x_left + 1
                ]
                # put to screen
                self.graph_view[y, x_left:x_right+1] = interval
            # allow interrupting the paint
            if y % 4 == 0:
                self._input_methods.wait()
        self._last_attr = fill_attr
def _flood_fill ( self , lcoord , fill_attr , pattern , border_attr , bg_pattern ) : # 4-way scanline flood fill: http://en.wikipedia.org/wiki/Flood_fill # flood fill stops on border colour in all directions; # it also stops on scanlines in fill_colour # pattern tiling stops at intervals that equal the pattern to be drawn, # unless this pattern is also equal to the background pattern. is_solid = ( pattern is None ) bg_tile = None if is_solid : tile = bytematrix . ByteMatrix ( 1 , 8 , fill_attr ) else : tile = self . _mode . build_tile ( bytearray ( pattern ) ) if pattern else None if bg_pattern : bg_tile = self . _mode . build_tile ( bytearray ( bg_pattern ) ) # only use first row of background bg_tile = bg_tile [ : 1 , : ] # illegal tile/background combo's: # all, or more than two consecutive, rows equal background for row in range ( max ( 1 , tile . height - 2 ) ) : comptile = tile [ row : row + 3 , : ] if comptile == bg_tile . vtile ( comptile . height ) : raise error . BASICError ( error . IFC ) # viewport bounds in viewport coordinates bound_x0 , bound_y0 , bound_x1 , bound_y1 = self . graph_view . get_bounds ( ) x , y = self . _get_window_physical ( * lcoord ) line_seed = [ ( x , x , y , 0 ) ] # paint nothing if seed is out of bounds if x < bound_x0 or x > bound_x1 or y < bound_y0 or y > bound_y1 : return self . _last_point = x , y # paint nothing if we start on border attrib if self . graph_view [ y , x ] == border_attr : return while len ( line_seed ) > 0 : # consider next interval x_start , x_stop , y , ydir = line_seed . pop ( ) # extend interval as far as it goes to left and right x_left = x_start - self . _scanline_until ( border_attr , y , x_start - 1 , bound_x0 - 1 ) . width x_right = x_stop + self . _scanline_until ( border_attr , y , x_stop + 1 , bound_x1 + 1 ) . width # check next scanlines and add intervals to the list if ydir == 0 : if y + 1 <= bound_y1 : line_seed = self . _check_scanline ( line_seed , x_left , x_right , y + 1 , tile , is_solid , bg_tile , border_attr , 1 ) if y - 1 >= bound_y0 : line_seed = self . _check_scanline ( line_seed , x_left , x_right , y - 1 , tile , is_solid , bg_tile , border_attr , - 1 ) else : # check the same interval one scanline onward in the same direction if y + ydir <= bound_y1 and y + ydir >= bound_y0 : line_seed = self . _check_scanline ( line_seed , x_left , x_right , y + ydir , tile , is_solid , bg_tile , border_attr , ydir ) # check any bit of the interval that was extended one scanline backward # this is where the flood fill goes around corners. if y - ydir <= bound_y1 and y - ydir >= bound_y0 : line_seed = self . _check_scanline ( line_seed , x_left , x_start - 1 , y - ydir , tile , is_solid , bg_tile , border_attr , - ydir ) line_seed = self . _check_scanline ( line_seed , x_stop + 1 , x_right , y - ydir , tile , is_solid , bg_tile , border_attr , - ydir ) # draw the pixels for the current interval if is_solid : self . graph_view [ y , x_left : x_right + 1 ] = tile [ 0 , 0 ] else : # convert tile to a list of attributes tilerow = tile [ y % tile . height , : ] n_tiles = 1 + ( x_right + 1 ) // tile . width - ( x_left // tile . width ) tiles = bytematrix . hstack ( ( tilerow , ) * n_tiles ) interval = tiles [ : , ( x_left % tile . width ) : ( x_left % tile . width ) + x_right - x_left + 1 ] # put to screen self . graph_view [ y , x_left : x_right + 1 ] = interval # allow interrupting the paint if y % 4 == 0 : self . _input_methods . wait ( ) self . _last_attr = fill_attr