def draw(self, x, y, colour, continue_transaction=False): """ Draw to a pixel. x: The horizontal coordinate to draw to. y: The vertical coordinate to draw to. colour: The colour to draw. continue_transaction: (optional, False) Include this as part of the previous transaction. """ assert 0 <= x < self._width assert 0 <= y < self._height validate_colour(colour) red, green, blue, alpha = colour transaction = self._get_id(continue_transaction, action='draw') self._cursor.execute( 'INSERT INTO draws (' ' transaction_id, x, y, red, green, blue, alpha' ') VALUES (' ' :transaction_id, :x, :y, :red, :green, :blue, :alpha' ');', { 'transaction_id': transaction, 'x': x, 'y': y, 'red': red, 'green': green, 'blue': blue, 'alpha': alpha, } )
def validate_colour(): """ Validate a colour """ from painterm.colour import validate_colour, Colour colours = (Colour(-1, 0, 1, 0), (15, 15, 256, 0), (12, 12, 12), (0, 0, 0, 2)) for colour in colours: with assert_raises(AssertionError): validate_colour(colour) validate_colour(Colour(0, 1, 2, 1.0)) validate_colour((0, 1, 2, 0.5))
def new_image(cls, width, height, path=None, background=BACKGROUND, creation_date=None): """ Create a new image. width: The width of the image (in pixels). height: The height of the image (in pixels). path: (optional, None) A pathlib Path representing the location to save the image. If None, the image will be stored in an in-memory database. background: (optional, 0-alpha white) The starting colour for the pixels. creation_date: (optional, None) The creation date. If not given, utcnow will be used. """ assert isinstance(width, int) and width > 0 assert isinstance(height, int) and height > 0 validate_colour(background) red, green, blue, alpha = background if path: if path.exists(): raise IOError("File already exists") location = path.expanduser().absolute().as_uri() is_uri = True else: location = IN_MEMORY is_uri = False if creation_date is None: creation_date = datetime.utcnow() connection = sqlite3.connect(location, uri=is_uri) cursor = connection.cursor() cursor.executescript(SCHEMA) cursor.execute( 'INSERT INTO meta (' ' major, minor, micro, width, height, creation_date' ') VALUES (' ' ?, ?, ?, ?, ?, ?' ');', ( *SCHEMA_VERISON, width, height, creation_date.strftime(DATE_FORMAT) ) ) cursor.execute( 'INSERT INTO transactions (id, time, action) ' 'VALUES (:id, :time, :action);', {'id': 0, 'time': 0, 'action': 'fill'} ) cursor.executemany( 'INSERT INTO draws (' ' transaction_id, x, y, red, green, blue, alpha' ') VALUES (' ' :transaction_id, :x, :y, :red, :green, :blue, :alpha' ');', ( { 'transaction_id': 0, 'x': x, 'y': y, 'red': red, 'green': green, 'blue': blue, 'alpha': alpha, } for x in range(width) for y in range(height) ) ) cursor.connection.commit() return Image(connection)
def fill(self, x, y, colour, continue_transaction=False): """ flood fill starting at a pixel. Returns a list of (x, y) pairs representing updated pixels x: The horizontal coordinate to start at. y: The vertical coordinate to start at. colour: The colour to draw. continue_transaction: (optional, False) Include this as part of the previous transaction. """ assert 0 <= x < self._width assert 0 <= y < self._height validate_colour(colour) red, green, blue, alpha = colour transaction = self._get_id(continue_transaction, action='fill') old_colour = self.colour_at(x, y) dont_fill = set() fill = {(x, y)} unchecked = set() if x > 0: unchecked.add((x - 1, y)) if x + 1 < self._width: unchecked.add((x + 1, y)) if y > 0: unchecked.add((x, y - 1)) if y + 1 < self._height: unchecked.add((x, y + 1)) # continue to fill while unchecked: curr_x, curr_y = xy = unchecked.pop() if xy in dont_fill or xy in fill: continue # we already handled this pixel if self.colour_at(curr_x, curr_y) == old_colour: fill.add((curr_x, curr_y)) if curr_x > 0: unchecked.add((curr_x - 1, curr_y)) if curr_x + 1 < self._width: unchecked.add((curr_x + 1, curr_y)) if curr_y > 0: unchecked.add((curr_x, curr_y - 1)) if curr_y + 1 < self._height: unchecked.add((curr_x, curr_y + 1)) else: dont_fill.add(xy) self._cursor.executemany( 'INSERT INTO draws (' ' transaction_id, x, y, red, green, blue, alpha' ') VALUES (' ' :transaction_id, :x, :y, :red, :green, :blue, :alpha' ');', ( { 'transaction_id': transaction, 'x': x, 'y': y, 'red': red, 'green': green, 'blue': blue, 'alpha': alpha, } for x, y in fill ) ) return fill