From c19dcfaa0b96bc1ba5932f2d30323e919ebf7e7f Mon Sep 17 00:00:00 2001 From: Erik Corry Date: Wed, 6 Dec 2023 19:29:41 +0100 Subject: [PATCH] Remove the textures, now it's Elements only. (#52) --- src/common.toit | 28 +- src/element.toit | 3 - src/four_gray.toit | 186 --- src/gray_scale.toit | 215 +--- src/histogram.toit | 197 --- src/one_byte.toit | 156 --- src/pixel_display.toit | 443 +------ src/several_color.toit | 105 +- src/texture.toit | 1102 ----------------- src/three_color.toit | 80 -- src/true_color.toit | 332 +---- src/two_bit_texture.toit | 167 --- src/two_color.toit | 247 +--- tests/4_gray_visualized.toit | 37 - tests/bw_visualized.toit | 37 - tests/drop_shadow_test.toit | 47 - tests/drop_shadow_visualized.toit | 40 - tests/gold/4_gray_visualized.toit.png | Bin 1259 -> 0 bytes tests/gold/bw_visualized.toit.png | Bin 1001 -> 0 bytes tests/gold/drop_shadow_visualized.toit.png | Bin 4878 -> 0 bytes .../mixed_text_rotated_visualized.toit.png | Bin 4408 -> 2552 bytes tests/gold/mixed_text_visualized.toit.png | Bin 3552 -> 2900 bytes .../mixed_texture_rotated_visualized.toit.png | Bin 770 -> 481 bytes tests/gold/text_texture_visualized.toit.png | Bin 2242 -> 0 bytes tests/gray_scale_visualized.toit | 17 +- tests/graymap.toit | 79 -- tests/indexed.toit | 101 -- tests/mixed_text_rotated_visualized.toit | 14 +- tests/mixed_text_visualized.toit | 16 +- tests/mixed_texture_rotated_visualized.toit | 10 +- tests/pbm_test.toit | 183 --- tests/text_texture_visualized.toit | 49 - tests/texture_test_slow.toit | 453 +------ tests/transform_test.toit | 2 +- tests/true_color_portrait_visualized.toit | 23 +- tests/true_color_visualized.toit | 17 +- 36 files changed, 103 insertions(+), 4283 deletions(-) delete mode 100644 src/histogram.toit delete mode 100644 src/texture.toit delete mode 100644 tests/4_gray_visualized.toit delete mode 100644 tests/bw_visualized.toit delete mode 100644 tests/drop_shadow_test.toit delete mode 100644 tests/drop_shadow_visualized.toit delete mode 100644 tests/gold/4_gray_visualized.toit.png delete mode 100644 tests/gold/bw_visualized.toit.png delete mode 100644 tests/gold/drop_shadow_visualized.toit.png delete mode 100644 tests/gold/text_texture_visualized.toit.png delete mode 100644 tests/graymap.toit delete mode 100644 tests/indexed.toit delete mode 100644 tests/pbm_test.toit delete mode 100644 tests/text_texture_visualized.toit diff --git a/src/common.toit b/src/common.toit index 31311ec..26cf5e6 100644 --- a/src/common.toit +++ b/src/common.toit @@ -2,27 +2,26 @@ // Use of this source code is governed by an MIT-style license that can be // found in the LICENSE file. -// Common things between textures and elements. +// TODO: Absorb this into pixel_display.toit now that textures are gone. import font show Font import bitmap show ORIENTATION_0 ORIENTATION_90 ORIENTATION_180 ORIENTATION_270 +import .element show Element import .style /** A display or a window within a display. -You can add and remove texture objects to a Window. They will be drawn - in the order they were added, where the first textures are at the back - and are overwritten by textures added later. +You can add and remove element objects to a Window. They will be drawn + in the order they were added, where the first elements are at the back + and are overwritten by elements added later. */ interface Window: - add element/ElementOrTexture_ -> none - remove element/ElementOrTexture_ -> none + add element/Element -> none + remove element/Element -> none remove_all -> none // Called by elements that have been added to this. - child_invalidated x/int y/int w/int h/int -> none - // Called by elements that have been added to this. child_invalidated_element x/int y/int w/int h/int -> none @@ -47,11 +46,9 @@ abstract class ElementOrTexture_: abstract invalidate -> none abstract class Canvas: - width_ / int // Used by both Textures and Elements. - height_ / int // Only used by Textures. - x_offset_ / int := 0 // Only used by Textures. - y_offset_ / int := 0 // Only used by Textures. - transform / Transform? := null // Only used by Elements. + width_ / int + height_ / int + transform / Transform? := null constructor .width_ .height_: @@ -211,8 +208,7 @@ class Transform: a horizontal line (if you specify the $y coordinate) or a vertical line (if you specify the $x coordinate). - You cannot specify both $x and $y. Not all textures support - reflected transforms. In particular, text and icons cannot be reflected. + You cannot specify both $x and $y. We do not support reflected transforms. Most of this library is integer-only, but for this operation you may need to use a half-pixel line depending on whether the thing you want to reflect @@ -271,7 +267,7 @@ class TextExtent_: // Gets the graphical extent of a string nominally positioned at (0, 0), // Where the nominal position of the text is relative to the letters depends // on the alignment, but it is always somewhere on the textual baseline. - // Some confusion is caused here by the fact that the y-axis of textures + // Some confusion is caused here by the fact that the y-axis of elements // grows towards the bottom, whereas the y-axis of fonts grows towards the // top. constructor text font alignment: diff --git a/src/element.toit b/src/element.toit index b7565ff..fa3376a 100644 --- a/src/element.toit +++ b/src/element.toit @@ -74,9 +74,6 @@ abstract class Element extends ElementOrTexture_ implements Window: element.invalidate element.change_tracker = null - child_invalidated x/int y/int w/int h/int -> none: - unreachable // This is only for textures, but we don't allow those. - remove_all -> none: children.do: it.invalidate diff --git a/src/four_gray.toit b/src/four_gray.toit index 800ede8..2af8d0d 100644 --- a/src/four_gray.toit +++ b/src/four_gray.toit @@ -12,7 +12,6 @@ import font show Font import icons show Icon import .common import .pixel_display show FourGrayPixelDisplay // For the doc comment. -import .texture import .two_bit_texture import .two_bit_texture as two_bit @@ -41,188 +40,3 @@ class Canvas_ extends two_bit.Canvas_: result := Canvas_ width_ height_ result.transform = transform return result - -class FilledRectangle extends TwoBitFilledRectangle_: - constructor color x/int y/int w/int h/int transform/Transform: - super color x y w h transform - - /// A line from $x1,$y1 to $x2,$y2. The line must be horizontal or vertical. - constructor.line color x1/int y1/int x2/int y2/int transform/Transform: - return FilledRectangle_.line_ x1 y1 x2 y2: | x y w h | - FilledRectangle color x y w h transform - -class TextTexture extends TwoBitTextTexture_: - /** - The coordinates given here to the constructor (and move_to) are the bottom - left of the first letter in the string (for left alignment). Once the - string has been rotated and aligned, and overhanging letter shapes have - been taken into account, the top left of the bounding box (properties $x, - $y, inherited from $SizedTexture) reflects the actual top left position - in the coordinate system of the transform. In the coordinates of the - display the getters $display_x, $display_y, $display_w and $display_h - are available. - */ - constructor text_x/int text_y/int transform/Transform alignment/int text/string font color: - super text_x text_y transform alignment text font color - -class IconTexture extends TwoBitTextTexture_: - constructor icon_x/int icon_y/int transform/Transform alignment/int icon/Icon font/Font color/int: - super icon_x icon_y transform alignment icon.stringify icon.font_ color - - icon= new_icon/Icon -> none: - text = new_icon.stringify - font = new_icon.font_ - -/** -A texture that contains an uncompressed 2-color image. -Initially all pixels are transparent, but pixels can be given the color - with $set_pixel. -*/ -class BitmapTexture extends TwoBitBitmapTexture_: - constructor x/int y/int w/int h/int transform/Transform color/int: - super x y w h transform color - -/** -A two color bitmap texture where foreground and background pixels in the - texture are both drawn. -Initially all pixels have the background color. -Use $set_pixel to paint with the foreground, and $clear_pixel to paint with - the background. -*/ -class OpaqueBitmapTexture extends TwoBitOpaqueBitmapTexture_: - - constructor x/int y/int w/int h/int transform/Transform foreground_color/int background_color: - super x y w h transform foreground_color background_color - -/// A four-color pixmap texture. -/// Use $set_all_pixels to set a background color and $set_pixel to draw. -class OpaquePixmapTexture extends BitmapTextureBase_: - bytes_/ByteArray ::= ? - bytes_2_/ByteArray ::= ? - - constructor x/int y/int w/int h/int transform/Transform initial_color/int=0: - bytes_per_plane := h * ((w + 7) >> 3) // Divide by 8, rounding up. - bytes_ = ByteArray bytes_per_plane - bytes_2_ = ByteArray bytes_per_plane - super x y w h transform - set_all_pixels initial_color - - pixel_color x/int y/int -> int: - index_and_mask_ x y: | index bit | - lo := (bytes_[index] & bit) == 0 ? 0 : 1 - hi := (bytes_2_[index] & bit) == 0 ? 0 : 2 - return lo + hi - unreachable - - set_pixel x/int y/int color/int -> none: - index_and_mask_ x y: | index bit | - if color & 1 == 0: - bytes_[index] &= bit ^ 0b1111_1111 - else: - bytes_[index] |= bit - if color & 2 == 0: - bytes_2_[index] &= bit ^ 0b1111_1111 - else: - bytes_2_[index] |= bit - - set_all_pixels color/int -> none: - bitmap_zap bytes_ color & 1 - bitmap_zap bytes_2_ (color & 2) >> 1 - - write2_ canvas/Canvas_: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - x := x2 - canvas.x_offset_ - y := y2 - canvas.y_offset_ - // Zero out the area of the Pixmap. - bitmap_rectangle x y 0 w2 h2 canvas.plane_0_ canvas.width_ - bitmap_rectangle x y 0 w2 h2 canvas.plane_1_ canvas.width_ - super canvas // Calls draw_ - - draw_ bx by orientation canvas/Canvas_: - // The area was already zeroed, add in the 1s as needed. - bitmap_draw_bitmap bx by 1 orientation bytes_ 0 w canvas.plane_0_ canvas.width_ false - bitmap_draw_bitmap bx by 1 orientation bytes_2_ 0 w canvas.plane_1_ canvas.width_ false - -// A texture backed by a P4 (binary two-level) PBM file. The white areas -// (zeros) are rendered transparent and the black areas (ones) are rendered in -// an arbitrary color. -class PbmTexture extends PbmTexture_: - // The byte array passed in must be a valid binary-mode (P4) PBM file. - // If $bytes is a literal containing constants then it is used directly - // from flash. However if the pixel drawing methods on this are used then - // $bytes is moved to RAM and modified. This could cause an out-of-memory - // on very large PBM files. - constructor x/int y/int transform/Transform color/int bytes/ByteArray: - super x y transform color bytes - -class BarCodeEan13 extends TwoBitBarCodeEan13_: - constructor code/string x/int y/int transform/Transform: - super code x y transform BLACK WHITE - -/** -A rectangular window with a fixed width colored border. -The border is subtracted from the visible area inside the window. -*/ -class SimpleWindow extends TwoBitSimpleWindow_: - constructor x/int y/int w/int h/int transform/Transform border_width/int border_color/int background_color/int: - super x y w h transform border_width border_color background_color - -class RoundedCornerWindow extends RoundedCornerWindow_: - background_color := ? - - constructor x y w h transform corner_radius .background_color: - super x y w h transform corner_radius - - make_alpha_map_ canvas/Canvas_ padding: - return ByteArray ((canvas.width_ + padding) * (canvas.height_ + padding)) >> 3 - - make_opaque_ x y w h map map_width --frame/bool: - assert: not frame - bytemap_rectangle x y 0xff w h map map_width - - set_opacity_ x y opacity map map_width --frame/bool: - assert: not frame - if 0 <= x < map_width: - y_offset := y * map_width - if 0 <= y_offset < map.size: - map[x + y_offset] = opacity - - draw_background canvas/Canvas_: - bytemap_zap canvas.plane_0_ (background_color & 1) - bytemap_zap canvas.plane_1_ (background_color & 2) >> 1 - - draw_frame canvas/Canvas_: - throw "UNREACHABLE" - -class DropShadowWindow extends DropShadowWindow_: - background_color := ? - max_shadow_opacity_ := ? - - constructor x y w h transform .background_color --corner_radius=5 --blur_radius=5 --drop_distance_x=10 --drop_distance_y=10 --shadow_opacity_percent=25: - // Scale the 0-100% opacity percentage to cover the 8 bit unsigned integer - // range 0-255. - max_shadow_opacity_ = (shadow_opacity_percent * 2.5500001).to_int - super x y w h transform corner_radius blur_radius drop_distance_x drop_distance_y - - make_alpha_map_ canvas/Canvas_ padding: - return ByteArray (canvas.width_ + padding) * (canvas.height_ + padding) - - make_opaque_ x y w h map map_width --frame/bool: - bytemap_rectangle x y (frame ? max_shadow_opacity_ : 255) w h map map_width - - set_opacity_ x y opacity map map_width --frame/bool: - if 0 <= x < map_width: - y_offset := y * map_width - if 0 <= y_offset < map.size: - if frame: - map[x + y_offset] = (opacity * max_shadow_opacity_) >> 8 - else: - map[x + y_offset] = opacity - - draw_background canvas/Canvas_: - bytemap_zap canvas.plane_0_ (background_color & 1) - bytemap_zap canvas.plane_1_ (background_color & 2) >> 1 - - draw_frame canvas/Canvas_: - bytemap_zap canvas.plane_0_ 0 - bytemap_zap canvas.plane_1_ 0 diff --git a/src/gray_scale.toit b/src/gray_scale.toit index 55a1d64..168ef47 100644 --- a/src/gray_scale.toit +++ b/src/gray_scale.toit @@ -11,7 +11,6 @@ import bitmap show * import font show Font import icons show Icon import .pixel_display show GrayScalePixelDisplay // For the doc comment. -import .texture import .one_byte WHITE ::= 255 @@ -29,221 +28,9 @@ class Canvas_ extends OneByteCanvas_: gray_scale -> bool: return true /** - Creates a blank texture with the same dimensions as this one. + Creates a blank canvas with the same dimensions as this one. */ create_similar: result := Canvas_ width_ height_ result.transform=transform return result - -class FilledRectangle extends OneByteFilledRectangle_: - constructor color/int x/int y/int w/int h/int transform/Transform: - super color x y w h transform - - /// A line from x1,y1 to x2,y2. The line must be horizontal or vertical. - constructor.line color x1/int y1/int x2/int y2/int transform/Transform: - return FilledRectangle_.line_ x1 y1 x2 y2: | x y w h | - FilledRectangle color x y w h transform - -class TextTexture extends OneByteTextTexture_: - /** - The coordinates given here to the constructor (and move_to) are the bottom - left of the first letter in the string (for left alignment). Once the - string has been rotated and aligned, and overhanging letter shapes have - been taken into account, the top left of the bounding box (properties $x, - $y, inherited from $SizedTexture) reflects the actual top left position - in the coordinate system of the transform. In the coordinates of the - display the getters $display_x, $display_y, $display_w and $display_h - are available. - */ - constructor text_x/int text_y/int transform/Transform alignment/int text/string font color/int: - super text_x text_y transform alignment text font color - -class IconTexture extends TextTexture: - constructor icon_x/int icon_y/int transform/Transform alignment/int icon/Icon font/Font color/int: - super icon_x icon_y transform alignment icon.stringify icon.font_ color - - icon= new_icon/Icon -> none: - text = new_icon.stringify - font = new_icon.font_ - -/** -A texture that contains an uncompressed 2-color image. Initially all pixels - are transparent, but pixels can be given the color with $set_pixel. -*/ -class BitmapTexture extends OneByteBitmapTexture_: - constructor x/int y/int w/int h/int transform/Transform color/int: - super x y w h transform color - -/** -A two color bitmap texture where foreground and background pixels in the - texture are both drawn. -Initially all pixels have the background color. -Use $set_pixel to paint with the foreground, and $clear_pixel to paint with - the background. -*/ -class OpaqueBitmapTexture extends OneByteOpaqueBitmapTexture_: - constructor x/int y/int w/int h/int transform/Transform foreground_color/int background_color/int: - super x y w h transform foreground_color background_color - -/** -A texture backed by a P4 (binary two-level) PBM file. -The white areas (zeros) are rendered transparent and the black areas - (ones) are rendered in an arbitrary color. -*/ -class PbmTexture extends OneBytePbmTexture_: - /** - The byte array passed in must be a valid binary-mode (P4) PBM file. - If $bytes is a literal containing constants then it is used directly - from flash. However if the pixel drawing methods on this are used then - $bytes is moved to RAM and modified. This could cause an out-of-memory - on very large PBM files. - */ - constructor x/int y/int transform/Transform color/int bytes/ByteArray: - super x y transform color bytes - -/** -A rectangular pixmap that can be drawn in any of 4 orientations on a canvas. -*/ -class PixmapTexture extends PixmapTexture_: - bytes_/ByteArray - palette_/ByteArray ::= #[] - transparency_/bool - - /** - Creates a pixmap. All pixels are initially transparent. - */ - constructor x/int y/int w/int h/int transform/Transform: - transparency_ = true - bytes_ = ByteArray w * h: 42 - super x y w h transform - - /** - Creates a pixmap with the given pixels. No transparency is supported. - The pixel byte array should have the size $w * $h. - */ - constructor x/int y/int w/int h/int transform/Transform .bytes_: - if bytes_.size != w * h: throw "INVALID_ARGUMENT" - transparency_ = false - super x y w h transform - - /** - Returns the brightness value of the gray shade at the given coordinates. - Returns -1 if the pixel is transparent at that coordinate. - */ - get_pixel x/int y/int -> int: - result := bytes_[x + y * w] - if not transparency_: - return result - return result == 42 ? -1 : result - - /** - Sets the brightness value of the gray shade at the given coordinates - between 0 and 255. - Setting the brightness to -1 makes the pixel transparent for a pixmap - that supports transparency. For transparency-supporting pixmaps, - one value cannot be set because it is reserved, so if you set the - pixel to a brightness of 42 it will silently use 41 instead. - */ - set_pixel x/int y/int brightness/int -> none: - if not transparency_: - if brightness == 42: - brightness = 41 - else if brightness == -1: - brightness = 42 - if not 0 <= brightness <= 0xff: throw "Invalid pixel" - bytes_[x + y * w] = brightness - - /** - Sets a pixel to transparent. - This instance must have been created with transparency. - */ - clear_pixel x/int y/int -> none: - if not transparency_: throw "No transparency" - set_pixel x y 42 - - /** - Sets the brightness value of the gray shade on the entire pixmap - between 0 and 255. - Setting the brightness to -1 makes the pixmap transparent for a pixmap - that supports transparency. For transparency-supporting pixmaps, - one value cannot be set because it is reserved, so if you set the - pixmap to a brightness of 42 it will silently use 41 instead. - */ - set_all_pixels brightness/int -> none: - if not transparency_: - if brightness == 42: - brightness = 41 - else if brightness == -1: - brightness = 42 - if not 0 <= brightness <= 0xff: throw "Invalid pixel" - bitmap_zap bytes_ brightness - - /** - Sets all pixels to transparent. - This instance must have been created with transparency. - */ - clear_all_pixels -> none: - if not transparency_: throw "No transparency" - bitmap_zap bytes_ 42 - - draw_ bx by orientation canvas/Canvas_: - if transparency_: - bitmap_draw_bytemap bx by 42 orientation bytes_ w palette_ canvas.pixels_ canvas.width_ - else: - bitmap_draw_bytemap bx by -1 orientation bytes_ w palette_ canvas.pixels_ canvas.width_ - -class BarCodeEan13 extends OneByteBarCodeEan13_: - constructor code/string x/int y/int transform/Transform: - super code x y transform - - white_ -> int: return 0xff - black_ -> int: return 0 - -class SimpleWindow extends OneByteSimpleWindow_: - /** - * A rectangular window with a fixed width colored border. The border is - * subtracted from the visible area inside the window. - */ - constructor x y w h transform border_width border_color/int background_color/int: - super x y w h transform border_width border_color background_color - -class RoundedCornerWindow extends OneByteRoundedCornerWindow_: - constructor x y w h transform corner_radius background_color/int: - super x y w h transform corner_radius background_color - - set_opacity_ x y opacity map map_width --frame/bool: - assert: not frame - if 0 <= x < map_width: - y_offset := y * map_width - if 0 <= y_offset < map.size: - map[x + y_offset] = opacity - -class DropShadowWindow extends DropShadowWindow_: - background_color := ? - max_shadow_opacity_ := ? - - constructor x y w h transform .background_color --corner_radius=5 --blur_radius=5 --drop_distance_x=10 --drop_distance_y=10 --shadow_opacity_percent=25: - max_shadow_opacity_ = (shadow_opacity_percent * 2.5500001).to_int - super x y w h transform corner_radius blur_radius drop_distance_x drop_distance_y - - make_alpha_map_ canvas/Canvas_ padding: - return ByteArray (canvas.width_ + padding) * (canvas.height_ + padding) - - make_opaque_ x y w h map map_width --frame/bool: - bytemap_rectangle x y (frame ? max_shadow_opacity_ : 255) w h map map_width - - set_opacity_ x y opacity map map_width --frame/bool: - if 0 <= x < map_width: - y_offset := y * map_width - if 0 <= y_offset < map.size: - if frame: - map[x + y_offset] = (opacity * max_shadow_opacity_) >> 8 - else: - map[x + y_offset] = opacity - - draw_background canvas/Canvas_: - bytemap_zap canvas.pixels_ background_color - - draw_frame canvas/Canvas_: - bytemap_zap canvas.pixels_ 0 diff --git a/src/histogram.toit b/src/histogram.toit deleted file mode 100644 index 3b3577d..0000000 --- a/src/histogram.toit +++ /dev/null @@ -1,197 +0,0 @@ -import bitmap show * -import .texture show * -import .true_color as true_color - -abstract class Histogram extends SizedTexture: - values_ := ? - fullness_ := 0 - scale_ := 0 - h_ := 0 - - /** - Constructs a new histogram. - All samples are multiplied by the scale. - The histogram is plotted in the color with one pixel of width per sample. - After $width samples have been added, the histogram starts scrolling. - If $reflected is true then the histogram is plotted with the Y axis - at the top and the bars projecting downwards. - */ - constructor x/int y/int width/int .h_/int transform/Transform .scale_/num reflected/bool=false: - values_ = List width 0 - tr := transform - if reflected: - tr = transform.reflect_around --y=(y + h_ / 2.0) - super x y width h_ tr - - add sample -> none: - if values_.size == 0: return - if fullness_ == values_.size: - update_: | x | - (x + 1 < values_.size) ? values_[x + 1] : sample - else: - coord := sample_to_coordinate_ sample - invalidate (x + fullness_) (y + coord) 1 (h_ - coord) - values_[fullness_++] = sample - - update_ [new_value_block] -> none: - List.chunk_up 0 values_.size 16: | from to size | - first_inval := 10000000 - last_inval := -1 - top_inval := 10000000 - bottom_inval := -1 - for x := from; x < to; x++: - coord_old := sample_to_coordinate_ values_[x] - new := new_value_block.call x - values_[x] = new - coord_new := sample_to_coordinate_ new - if coord_old != coord_new: - top_inval = min top_inval (min coord_old coord_new) - bottom_inval = max bottom_inval (max coord_old coord_new) - first_inval = min first_inval x - last_inval = max last_inval x - if first_inval <= last_inval and top_inval <= bottom_inval: - invalidate (x + first_inval) (y + top_inval) (last_inval + 1 - first_inval) (bottom_inval + 1 - top_inval) - - /** - Adjust all samples up or down by a fixed amount. This allows you - to change the Y axis without redrawing the whole histogram. - */ - adjust_all adjustment/int -> none: - if adjustment == 0: return - update_: | x | values_[x] + adjustment - - sample_to_coordinate_ sample: - scaled := (scale_ * sample).to_int - clamped := max 0 (min h_ scaled) - return h_ - clamped - - write2_ canvas: - List.chunk_up 0 fullness_ 16: | from to size | - worth_plotting := false - transform.xywh (x + from) y size h_: | x2 y2 w2 h2 | - if w2 > 0 and h2 > 0 and x2 - canvas.x_offset_ + w2 > 0 and x2 - canvas.x_offset_ < canvas.width_: - worth_plotting = true - if worth_plotting: - for hx := from; hx < to; hx++: - wx := x + hx - coord := y + (sample_to_coordinate_ values_[hx]) - wy1 := coord - wy2 := y + h_ - transform.xywh wx wy1 1 (wy2 - wy1): | x2 y2 w2 h2 | - if w2 > 0 and h2 > 0: - draw_rectangle - x2 - canvas.x_offset_ - y2 - canvas.y_offset_ - w2 - h2 - canvas - - abstract draw_rectangle x y w h canvas - -class TwoColorHistogram extends Histogram: - color_ := 0 - - constructor x/int y/int width/int height/int transform/Transform scale/num .color_: - super x y width height transform scale - - /** - Constructs a new histogram. - All samples are multiplied by the scale. - The histogram is plotted in the color with one pixel of width per sample. - After $width samples have been added, the histogram starts scrolling. - If $reflected is true then the histogram is plotted with the Y axis - at the top and the bars projecting downwards. - */ - constructor --x/int --y/int --width/int --height/int --transform/Transform --scale/num=1 --color/int --reflected/bool=false: - color_ = color - super x y width height transform scale reflected - - draw_rectangle x y w h canvas: - bitmap_rectangle - x - y - color_ - w - h - canvas.pixels_ - canvas.width_ - -class TwoBitColorHistogram_ extends Histogram: - color_ := 0 - - constructor x/int y/int width/int height/int transform/Transform scale/num .color_ reflected/bool=false: - super x y width height transform scale reflected - - draw_rectangle x y w h canvas: - bitmap_rectangle - x - y - color_ & 1 - w - h - canvas.plane_0_ - canvas.width_ - bitmap_rectangle - x - y - (color_ & 2) >> 1 - w - h - canvas.plane_1_ - canvas.width_ - -class ThreeColorHistogram extends TwoBitColorHistogram_: - constructor x/int y/int width/int height/int transform/Transform scale/num color/int: - assert: color != 3 - super x y width height transform scale color - - /** - Constructs a new histogram. - All samples are multiplied by the scale. - The histogram is plotted in the color with one pixel of width per sample. - After $width samples have been added, the histogram starts scrolling. - If $reflected is true then the histogram is plotted with the Y axis - at the top and the bars projecting downwards. - */ - constructor --x/int --y/int --width/int --height/int --transform/Transform --scale/num=1 --color/int --reflected/bool=false: - assert: color != 3 - super x y width height transform scale color reflected - -class FourGrayHistogram extends TwoBitColorHistogram_: - constructor x/int y/int width/int height/int transform/Transform scale/num color/int: - super x y width height transform scale color - - /** - Constructs a new histogram. - All samples are multiplied by the scale. - The histogram is plotted in the color with one pixel of width per sample. - After $width samples have been added, the histogram starts scrolling. - If $reflected is true then the histogram is plotted with the Y axis - at the top and the bars projecting downwards. - */ - constructor --x/int --y/int --width/int --height/int --transform/Transform --scale/num=1 --color/int --reflected/bool=false: - super x y width height transform scale color reflected - -class TrueColorHistogram extends Histogram: - color_ := 0 - - constructor x/int y/int width/int height/int transform/Transform scale/num .color_: - super x y width height transform scale - - /** - Constructs a new histogram. - All samples are multiplied by the scale. - The histogram is plotted in the color with one pixel of width per sample. - After $width samples have been added, the histogram starts scrolling. - If $reflected is true then the histogram is plotted with the Y axis - at the top and the bars projecting downwards. - */ - constructor --x/int --y/int --width/int --height/int --transform/Transform --scale/num=1 --color/int --reflected/bool=false: - color_ = color - super x y width height transform scale reflected - - draw_rectangle x y w h canvas: - c := color_ - if bytemap_rectangle x y (true_color.red_component c) w h canvas.red_ canvas.width_: - bytemap_rectangle x y (true_color.green_component c) w h canvas.green_ canvas.width_ - bytemap_rectangle x y (true_color.blue_component c) w h canvas.blue_ canvas.width_ diff --git a/src/one_byte.toit b/src/one_byte.toit index cc91e6e..79ec764 100644 --- a/src/one_byte.toit +++ b/src/one_byte.toit @@ -11,7 +11,6 @@ import font show Font import icons show Icon import .common import .gray_scale as gray_scale_ -import .texture // The canvas contains a ByteArray. // Initially all pixels are 0. @@ -46,158 +45,3 @@ abstract class OneByteCanvas_ extends Canvas: text x/int y/int --text/string --color/int --font/Font --orientation/int=ORIENTATION_0: transform.xyo x y orientation: | x2 y2 o2 | bytemap_draw_text x2 y2 color o2 text font pixels_ width_ - -class OneByteFilledRectangle_ extends FilledRectangle_: - color_ := ? - - constructor .color_ x/int y/int w/int h/int transform/Transform: - assert: 0 <= color_ <= 0xff - super x y w h transform - - translated_write_ x y w h canvas/OneByteCanvas_: - bytemap_rectangle x y color_ w h canvas.pixels_ canvas.width_ - -class OneByteTextTexture_ extends TextTexture_: - color_ := 0 - - /** - The coordinates given here to the constructor (and move_to) are the bottom - left of the first letter in the string (for left alignment). Once the - string has been rotated and aligned, and overhanging letter shapes have - been taken into account, the top left of the bounding box (properties $x, - $y, inherited from $SizedTexture) reflects the actual top left position - in the coordinate system of the transform. In the coordinates of the - display the getters $display_x, $display_y, $display_w and $display_h - are available. - */ - constructor text_x/int text_y/int transform/Transform alignment/int text/string font .color_: - assert: 0 <= color_ <= 0xff - super text_x text_y transform alignment text font - - color= new_color -> none: - if color_ == new_color: return - color_ = new_color - invalidate - - draw_ bx by orientation canvas/OneByteCanvas_: - bytemap_draw_text bx by color_ orientation string_ font_ canvas.pixels_ canvas.width_ - -/** -A texture that contains an uncompressed 2-color image. Initially all pixels - are transparent, but pixels can be given the color with $set_pixel. -*/ -class OneByteBitmapTexture_ extends BitmapTexture_: - color_ := 0 - - constructor x/int y/int w/int h/int transform/Transform .color_/int: - super x y w h transform - - draw_ bx by orientation canvas/OneByteCanvas_: - bitmap_draw_bitmap bx by color_ orientation bytes_ 0 w canvas.pixels_ canvas.width_ true - -/** -A two color bitmap texture where foreground and background pixels in the - texture are both drawn. -Initially all pixels have the background color. -Use $set_pixel to paint with the foreground, and $clear_pixel to paint with - the background. -*/ -class OneByteOpaqueBitmapTexture_ extends OneByteBitmapTexture_: - background_color_ := 0 - - constructor x/int y/int w/int h/int transform/Transform foreground_color/int .background_color_: - super x y w h transform foreground_color - - write2_ canvas/OneByteCanvas_: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - x := x2 - canvas.x_offset_ - y := y2 - canvas.y_offset_ - bytemap_rectangle x y background_color_ w2 h2 canvas.pixels_ canvas.width_ - super canvas // Draw foreground. - -/** -A texture backed by a P4 (binary two-level) PBM file. -The white areas (zeros) are rendered transparent and the black areas - (ones) are rendered in an arbitrary color. -*/ -class OneBytePbmTexture_ extends BitmapTexture_: - width_ := 0 - height_ := 0 - color_ := 0 - bytes_ := ? - - /** - The byte array passed in must be a valid binary-mode (P4) PBM file. - If $bytes is a literal containing constants then it is used directly - from flash. However if the pixel drawing methods on this are used then - $bytes is moved to RAM and modified. This could cause an out-of-memory - on very large PBM files. - */ - constructor x/int y/int transform/Transform .color_/int bytes/ByteArray: - parser := PbmParser_ bytes - parser.parse_ - bytes_ = bytes[parser.image_data_offset..] - super.no_allocate_ x y parser.width parser.height transform - - draw_ bx by orientation canvas/OneByteCanvas_: - bitmap_draw_bitmap bx by color_ orientation bytes_ 0 w canvas.pixels_ canvas.width_ true - -abstract class OneByteBarCodeEan13_ extends BarCodeEan13_: - constructor code/string x/int y/int transform/Transform: - super code x y transform - - abstract white_ -> int - abstract black_ -> int - - white_square_ x y w h canvas/OneByteCanvas_: - white ::= 0xff - bytemap_rectangle x y white_ w h canvas.pixels_ canvas.width_ - - digit_ digit x y canvas orientation -> none: - if digit == "": return - bytemap_draw_text x y black_ orientation digit sans10_ canvas.pixels_ canvas.width_ - - block_ x y width height canvas/OneByteCanvas_: - bytemap_rectangle x y black_ width height canvas.pixels_ canvas.width_ - -class OneByteSimpleWindow_ extends SimpleWindow_: - background_color := ? - border_color := ? - - /** - A rectangular window with a fixed width colored border. The border is - subtracted from the visible area inside the window. - */ - constructor x y w h transform border_width .border_color .background_color: - super x y w h transform border_width - - draw_frame canvas/OneByteCanvas_: - bytemap_zap canvas.pixels_ border_color - - draw_background canvas/OneByteCanvas_: - bytemap_zap canvas.pixels_ background_color - - make_alpha_map_ canvas/OneByteCanvas_: - return ByteArray canvas.width_ * canvas.height_ - - make_opaque_ x y w h map map_width: - bytemap_rectangle x y 0xff w h map map_width - -abstract class OneByteRoundedCornerWindow_ extends RoundedCornerWindow_: - background_color := ? - - constructor x y w h transform corner_radius .background_color: - super x y w h transform corner_radius - - make_alpha_map_ canvas padding: - return ByteArray (canvas.width_ + padding) * (canvas.height_ + padding) - - make_opaque_ x y w h map map_width --frame/bool: - assert: not frame - bytemap_rectangle x y 0xff w h map map_width - - draw_background canvas/OneByteCanvas_: - bytemap_zap canvas.pixels_ background_color - - draw_frame canvas/OneByteCanvas_: - throw "UNREACHABLE" diff --git a/src/pixel_display.toit b/src/pixel_display.toit index 9533e39..51889b7 100644 --- a/src/pixel_display.toit +++ b/src/pixel_display.toit @@ -14,7 +14,6 @@ import icons show Icon import .bar_code import .common import .element -import .texture import .two_color as two_color import .three_color as three_color import .two_bit_texture as two_bit @@ -62,42 +61,6 @@ abstract class AbstractDriver: throw "Not a true-color driver" close -> none: -/** -Current settings for adding textures to a display. -I think we are getting rid of this. -*/ -class GraphicsContext: - alignment /int ::= TEXT_TEXTURE_ALIGN_LEFT - color /int ::= 0 - background /int ::= 0 - font /Font? ::= null - transform /Transform ::= Transform.identity - - constructor: - - constructor.private_ .alignment .color .font .transform .background: - - /// Returns a copy of this GraphicsContext, but with the given changes. - with -> GraphicsContext - --alignment/int=alignment - --font/Font?=font - --color/int=color - --transform/Transform?=transform - --translate_x/int=0 - --translate_y/int=0 - --rotation/int=0 - --background/int=background: - if rotation != 0: - rotation %= 360 - if rotation < 0: rotation += 360 - if rotation % 90 != 0: throw "INVALID_ARGUMENT" - while rotation != 0: - transform = transform.rotate_left - rotation -= 90 - if translate_x != 0 or translate_y != 0: - transform = transform.translate translate_x translate_y - return GraphicsContext.private_ alignment color font transform background - /** Common code for pixel-based displays connected to devices. Height and width must be multiples of 8. @@ -108,7 +71,7 @@ See https://docs.toit.io/language/sdk/display */ abstract class PixelDisplay implements Window: // The image to display. - textures_ := {} + elements_ := {} background_ := null width: return driver_.width height: return driver_.width @@ -180,33 +143,6 @@ abstract class PixelDisplay implements Window: abstract default_draw_color_ -> int abstract default_background_color_ -> int - /** - Returns a graphics context for the screen. - With `--landscape=false` the context will use the display in landscape mode (wider than tall). - With `--landscape=true` the context will use the display in portrait mode (taller than wide). - With `--inverted=true` the context will use the display rotated 180 degrees. - The default $color depends on the display. - */ - context --landscape/bool?=null --inverted/bool=false --color/int=default_draw_color_ --alignment/int=TEXT_TEXTURE_ALIGN_LEFT --font/Font?=null --translate_x/int=0 --translate_y/int=0 --background/int=default_background_color_ -> GraphicsContext: - transform/Transform ::= ? - if landscape == null: - transform = Transform.identity - if inverted: throw "INVALID_ARGUMENT" - else if landscape: - if inverted: - transform = inverted_landscape - else: - transform = this.landscape - else: - if inverted: - transform = inverted_portrait - else: - transform = portrait - translated := transform - if translate_x != 0 or translate_y != 0: - translated = transform.translate translate_x translate_y - return GraphicsContext.private_ alignment color font translated background - /** Returns a transform that uses the display in portrait mode. */ portrait -> Transform: if not portrait_: @@ -255,39 +191,44 @@ abstract class PixelDisplay implements Window: abstract background= color/int -> none + set_styles styles/List -> none: + elements_.do: + if it is Element: + element := it as Element + element.set_styles styles + /** - Adds a texture to a display. The next time the display is refreshed, this - texture will be drawn. Textures added to this display are drawn in the - order they were added, so the first-added textures are at the back and the - last-added are at the front. However you can add textures via a - TextureGroup. This enables you to later add textures that are not at the - front, by adding them to a TextureGroup that is not at the front. + Adds an element to a display. The next time the display is refreshed, this + element will be drawn. Elements added to this display are drawn in the + order they were added, so the first-added elements are at the back and the + last-added are at the front. However elements can have children. + This enables you to later add elements that are not at the + front, by adding them as children of an Element that is not at the front. */ - add texture/ElementOrTexture_ -> none: - textures_.add texture - texture.change_tracker = this - texture.invalidate + add element/Element -> none: + elements_.add element + element.change_tracker = this + element.invalidate /** - Removes a texture that was previously added. You cannot remove a background - texture. Instead you should set a new background with @background=. + Removes an element that was previously added. */ - remove texture/ElementOrTexture_ -> none: - textures_.remove texture - texture.invalidate - texture.change_tracker = null + remove element/Element -> none: + elements_.remove element + element.invalidate + element.change_tracker = null - /** Removes all textures. */ + /** Removes all elements. */ remove_all: - textures_.do: it.change_tracker = null - if textures_.size != 0: child_invalidated 0 0 driver_.width driver_.height - textures_ = {} + elements_.do: it.change_tracker = null + if elements_.size != 0: child_invalidated_element 0 0 driver_.width driver_.height + elements_ = {} child_invalidated_element x/int y/int w/int h/int -> none: transform_.xywh x y w h: | x2 y2 w2 h2 | - child_invalidated x2 y2 w2 h2 + child_invalidated_ x2 y2 w2 h2 - child_invalidated x/int y/int w/int h/int -> none: + child_invalidated_ x/int y/int w/int h/int -> none: if not dirty_: return // Some devices don't use the dirty array to track changes. dirty_left_ = min dirty_left_ x dirty_right_ = max dirty_right_ (x + w) @@ -327,28 +268,22 @@ abstract class PixelDisplay implements Window: y_rounding_ canvas := create_canvas_ w step List.chunk_up 0 (round_up driver_.height y_rounding_) step: | top bottom | - // For the Textures. - canvas.x_offset_ = 0 - canvas.y_offset_ = top // For the Elements. // To get the translation for this tile in the driver coordinates instead // of the display coordinates, we invert the 2d transform, then translate // it, then invert it again. canvas.transform = (transform_.invert.translate 0 top).invert canvas.set_all_pixels background_ - textures_.do: - if it is SizedTexture: - it.write canvas - else: - it.draw canvas + elements_.do: + it.draw canvas draw_ 0 top driver_.width bottom canvas driver_.commit 0 0 driver_.width driver_.height /** - Draws the elements and textures. + Draws the elements. After changing the display, for example by adding, removing or moving - textures, call this to refresh the screen. Optionally give a $speed + elements, call this to refresh the screen. Optionally give a $speed between 0 and 100 to indicate the speed-image quality tradeoff. */ draw --speed/int=50 -> none: @@ -501,9 +436,6 @@ abstract class PixelDisplay implements Window: redraw_rect_ left/int top/int right/int bottom/int -> none: canvas := create_canvas_ (right - left) (bottom - top) - // For the Textures: - canvas.x_offset_ = left - canvas.y_offset_ = top // For the Elements: // To get the translation for this tile in the driver coordinates instead // of the display coordinates, we invert the 2d transform, then translate @@ -511,11 +443,8 @@ abstract class PixelDisplay implements Window: canvas.transform = (transform_.invert.translate left top).invert canvas.set_all_pixels background_ - textures_.do: - if it is SizedTexture: - it.write canvas - else: - it.draw canvas + elements_.do: + it.draw canvas draw_ left top right bottom canvas @@ -558,73 +487,7 @@ class TwoColorPixelDisplay extends PixelDisplay: background= color/int -> none: if background_ != color: background_ = color - child_invalidated 0 0 driver_.width driver_.height - - text context/GraphicsContext x/int y/int text/string -> two_color.TextTexture: - if context.font == null: throw "NO_FONT_GIVEN" - texture := two_color.TextTexture x y context.transform context.alignment text context.font context.color - add texture - return texture - - icon context/GraphicsContext x/int y/int icon/Icon -> two_color.IconTexture: - texture := two_color.IconTexture x y context.transform context.alignment icon icon.font_ context.color - add texture - return texture - - filled_rectangle context/GraphicsContext x/int y/int width/int height/int -> two_color.FilledRectangle: - texture := two_color.FilledRectangle context.color x y width height context.transform - add texture - return texture - - /// A line from $x1,$y1 to $x2,$y2. - /// The line must be horizontal or vertical. - line context/GraphicsContext x1/int y1/int x2/int y2/int -> two_color.FilledRectangle: - texture := two_color.FilledRectangle.line context.color x1 y1 x2 y2 context.transform - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels are transparent, but pixels can be given the color from the context - with set_pixel. - */ - bitmap context/GraphicsContext x/int y/int width/int height/int -> two_color.BitmapTexture: - texture := two_color.BitmapTexture x y width height context.transform context.color - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels have the background color from the context. Pixels can be set to - the context color with set_pixel, or set to the context background color - with clear_pixel. - */ - opaque_bitmap context/GraphicsContext x/int y/int width/int height/int -> two_color.OpaqueBitmapTexture: - texture := two_color.OpaqueBitmapTexture x y width height context.transform context.color context.background - add texture - return texture - - /** - A texture backed by a P4 (binary two-level) PBM file. The white areas - (zeros) are rendered transparent and the black areas (ones) are rendered in - an the color from the context. This is normally more efficient than the - Pbm class, but it cannot scale the image. - */ - pbm context/GraphicsContext x/int y/int bytes/ByteArray -> two_color.PbmTexture: - texture := two_color.PbmTexture x y context.transform context.color bytes - add texture - return texture - - /** - A texture backed by a P4 (binary two-level) PBM file. The colors in the - context are ignored, and pixels are rendered with the colors they have in - the file. This is normally more efficient than the Pbm class, but it - cannot scale the image. - */ - opaque_pbm context/GraphicsContext x/int y/int bytes/ByteArray -> two_color.OpaquePbmTexture: - texture := two_color.OpaquePbmTexture x y context.transform bytes - add texture - return texture + child_invalidated_ 0 0 driver_.width driver_.height max_canvas_height_ width/int -> int: height := 0 @@ -661,7 +524,7 @@ class FourGrayPixelDisplay extends TwoBitPixelDisplay_: background= color/int -> none: if background_ != color: background_ = color - child_invalidated 0 0 driver_.width driver_.height + child_invalidated_ 0 0 driver_.width driver_.height default_draw_color_ -> int: return four_gray.BLACK @@ -672,60 +535,6 @@ class FourGrayPixelDisplay extends TwoBitPixelDisplay_: create_canvas_ w/int h/int -> Canvas: return four_gray.Canvas_ w h - text context/GraphicsContext x/int y/int text/string -> four_gray.TextTexture: - if context.font == null: throw "NO_FONT_GIVEN" - texture := four_gray.TextTexture x y context.transform context.alignment text context.font context.color - add texture - return texture - - icon context/GraphicsContext x/int y/int icon/Icon -> four_gray.IconTexture: - texture := four_gray.IconTexture x y context.transform context.alignment icon icon.font_ context.color - add texture - return texture - - filled_rectangle context/GraphicsContext x/int y/int width/int height/int -> four_gray.FilledRectangle: - texture := four_gray.FilledRectangle context.color x y width height context.transform - add texture - return texture - - /// A line from x1,y1 to x2,y2. The line must be horizontal or vertical. - line context/GraphicsContext x1/int y1/int x2/int y2/int -> four_gray.FilledRectangle: - texture := four_gray.FilledRectangle.line context.color x1 y1 x2 y2 context.transform - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels are transparent, but pixels can be given the color from the - context with set_pixel. - */ - bitmap context/GraphicsContext x/int y/int width/int height/int -> four_gray.BitmapTexture: - texture := four_gray.BitmapTexture x y width height context.transform context.color - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels have the background color from the context. Pixels can be set to - the context color with set_pixel, or set to the context background color - with clear_pixel. - */ - opaque_bitmap context/GraphicsContext x/int y/int width/int height/int -> four_gray.OpaqueBitmapTexture: - texture := four_gray.OpaqueBitmapTexture x y width height context.transform context.color context.background - add texture - return texture - - /** - A texture that contains an uncompressed 4-gray image. Initially all - pixels have the background color from the context. Pixels can be set to - any gray level with the context color with set_pixel, or set to the context - background color with clear_pixel. - */ - pixmap context/GraphicsContext x/int y/int width/int height/int -> four_gray.OpaquePixmapTexture: - texture := four_gray.OpaquePixmapTexture x y width height context.transform context.background - add texture - return texture - /** Pixel-based display with black, white, and red, connected to a device. Height and width must be multiples of 8. @@ -742,7 +551,7 @@ class ThreeColorPixelDisplay extends TwoBitPixelDisplay_: background= color/int -> none: if background_ != color: background_ = color - child_invalidated 0 0 driver_.width driver_.height + child_invalidated_ 0 0 driver_.width driver_.height default_draw_color_ -> int: return three_color.BLACK @@ -753,49 +562,6 @@ class ThreeColorPixelDisplay extends TwoBitPixelDisplay_: create_canvas_ w/int h/int -> Canvas: return three_color.Canvas_ w h - text context/GraphicsContext x/int y/int text/string -> three_color.TextTexture: - if context.font == null: throw "NO_FONT_GIVEN" - texture := three_color.TextTexture x y context.transform context.alignment text context.font context.color - add texture - return texture - - icon context/GraphicsContext x/int y/int icon/Icon -> three_color.IconTexture: - texture := three_color.IconTexture x y context.transform context.alignment icon icon.font_ context.color - add texture - return texture - - filled_rectangle context/GraphicsContext x/int y/int width/int height/int -> three_color.FilledRectangle: - texture := three_color.FilledRectangle context.color x y width height context.transform - add texture - return texture - - /// A line from x1,y1 to x2,y2. The line must be horizontal or vertical. - line context/GraphicsContext x1/int y1/int x2/int y2/int -> three_color.FilledRectangle: - texture := three_color.FilledRectangle.line context.color x1 y1 x2 y2 context.transform - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels are transparent, but pixels can be given the color from the context - with set_pixel. - */ - bitmap context/GraphicsContext x/int y/int width/int height/int -> three_color.BitmapTexture: - texture := three_color.BitmapTexture x y width height context.transform context.color - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels have the background color from the context. Pixels can be set to - the context color with set_pixel, or set to the context background color - with clear_pixel. - */ - opaque_bitmap context/GraphicsContext x/int y/int width/int height/int -> three_color.OpaqueBitmapTexture: - texture := three_color.OpaqueBitmapTexture x y width height context.transform context.color context.background - add texture - return texture - abstract class TwoBitPixelDisplay_ extends PixelDisplay: background_ := three_color.WHITE @@ -834,50 +600,7 @@ class GrayScalePixelDisplay extends PixelDisplay: background= color/int -> none: if background_ != color: background_ = color - child_invalidated 0 0 driver_.width driver_.height - - text context/GraphicsContext x/int y/int text/string -> gray_scale.TextTexture: - if context.font == null: throw "NO_FONT_GIVEN" - texture := gray_scale.TextTexture x y context.transform context.alignment text context.font context.color - add texture - return texture - - icon context/GraphicsContext x/int y/int icon/Icon -> gray_scale.IconTexture: - texture := gray_scale.IconTexture x y context.transform context.alignment icon icon.font_ context.color - add texture - return texture - - filled_rectangle context/GraphicsContext x/int y/int width/int height/int -> gray_scale.FilledRectangle: - texture := gray_scale.FilledRectangle context.color x y width height context.transform - add texture - return texture - - /// A line from x1,y1 to x2,y2. The line must be horizontal or vertical. - line context/GraphicsContext x1/int y1/int x2/int y2/int -> gray_scale.FilledRectangle: - texture := gray_scale.FilledRectangle.line context.color x1 y1 x2 y2 context.transform - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels are transparent, but pixels can be given the color from the context - with set_pixel. - */ - bitmap context/GraphicsContext x/int y/int width/int height/int -> gray_scale.BitmapTexture: - texture := gray_scale.BitmapTexture x y width height context.transform context.color - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels have the background color from the context. Pixels can be set to - the context color with set_pixel, or set to the context background color - with clear_pixel. - */ - opaque_bitmap context/GraphicsContext x/int y/int width/int height/int -> gray_scale.OpaqueBitmapTexture: - texture := gray_scale.OpaqueBitmapTexture x y width height context.transform context.color context.background - add texture - return texture + child_invalidated_ 0 0 driver_.width driver_.height default_draw_color_ -> int: return gray_scale.BLACK @@ -914,50 +637,7 @@ class SeveralColorPixelDisplay extends PixelDisplay: background= color/int -> none: if background_ != color: background_ = color - child_invalidated 0 0 driver_.width driver_.height - - text context/GraphicsContext x/int y/int text/string -> several_color.TextTexture: - if context.font == null: throw "NO_FONT_GIVEN" - texture := several_color.TextTexture x y context.transform context.alignment text context.font context.color - add texture - return texture - - icon context/GraphicsContext x/int y/int icon/Icon -> several_color.IconTexture: - texture := several_color.IconTexture x y context.transform context.alignment icon icon.font_ context.color - add texture - return texture - - filled_rectangle context/GraphicsContext x/int y/int width/int height/int -> several_color.FilledRectangle: - texture := several_color.FilledRectangle context.color x y width height context.transform - add texture - return texture - - /// A line from x1,y1 to x2,y2. The line must be horizontal or vertical. - line context/GraphicsContext x1/int y1/int x2/int y2/int -> several_color.FilledRectangle: - texture := several_color.FilledRectangle.line context.color x1 y1 x2 y2 context.transform - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels are transparent, but pixels can be given the color from the context - with set_pixel. - */ - bitmap context/GraphicsContext x/int y/int width/int height/int -> several_color.BitmapTexture: - texture := several_color.BitmapTexture x y width height context.transform context.color - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels have the background color from the context. Pixels can be set to - the context color with set_pixel, or set to the context background color - with clear_pixel. - */ - opaque_bitmap context/GraphicsContext x/int y/int width/int height/int -> several_color.OpaqueBitmapTexture: - texture := several_color.OpaqueBitmapTexture x y width height context.transform context.color context.background - add texture - return texture + child_invalidated_ 0 0 driver_.width driver_.height default_draw_color_ -> int: return 1 @@ -994,50 +674,7 @@ class TrueColorPixelDisplay extends PixelDisplay: background= color/int -> none: if background_ != color: background_ = color - child_invalidated 0 0 driver_.width driver_.height - - text context/GraphicsContext x/int y/int text/string -> true_color.TextTexture: - if context.font == null: throw "NO_FONT_GIVEN" - texture := true_color.TextTexture x y context.transform context.alignment text context.font context.color - add texture - return texture - - icon context/GraphicsContext x/int y/int icon/Icon -> true_color.IconTexture: - texture := true_color.IconTexture x y context.transform context.alignment icon icon.font_ context.color - add texture - return texture - - filled_rectangle context/GraphicsContext x/int y/int width/int height/int -> true_color.FilledRectangle: - texture := true_color.FilledRectangle context.color x y width height context.transform - add texture - return texture - - /// A line from x1,y1 to x2,y2. The line must be horizontal or vertical. - line context/GraphicsContext x1/int y1/int x2/int y2/int -> true_color.FilledRectangle: - texture := true_color.FilledRectangle.line context.color x1 y1 x2 y2 context.transform - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels are transparent, but pixels can be given the color from the context - with set_pixel. - */ - bitmap context/GraphicsContext x/int y/int width/int height/int -> true_color.BitmapTexture: - texture := true_color.BitmapTexture x y width height context.transform context.color - add texture - return texture - - /** - A texture that contains an uncompressed 2-color image. Initially all - pixels have the background color from the context. Pixels can be set to - the context color with set_pixel, or set to the context background color - with clear_pixel. - */ - opaque_bitmap context/GraphicsContext x/int y/int width/int height/int -> true_color.OpaqueBitmapTexture: - texture := true_color.OpaqueBitmapTexture x y width height context.transform context.color context.background - add texture - return texture + child_invalidated_ 0 0 driver_.width driver_.height default_draw_color_ -> int: return true_color.BLACK diff --git a/src/several_color.toit b/src/several_color.toit index 02e777b..760ce21 100644 --- a/src/several_color.toit +++ b/src/several_color.toit @@ -11,7 +11,6 @@ import bitmap show * import font show Font import icons show Icon import .pixel_display show SeveralColorPixelDisplay // For the doc comment. -import .texture import .one_byte // The canvas contains a ByteArray. @@ -24,111 +23,9 @@ class Canvas_ extends OneByteCanvas_: gray_scale -> bool: return false /** - Creates a blank texture with the same dimensions as this one. + Creates a blank canvas with the same dimensions as this one. */ create_similar: result := Canvas_ width_ height_ result.transform=transform return result - -class FilledRectangle extends OneByteFilledRectangle_: - constructor color/int x/int y/int w/int h/int transform/Transform: - super color x y w h transform - - /// A line from x1,y1 to x2,y2. The line must be horizontal or vertical. - constructor.line color x1/int y1/int x2/int y2/int transform/Transform: - return FilledRectangle_.line_ x1 y1 x2 y2: | x y w h | - FilledRectangle color x y w h transform - -class TextTexture extends OneByteTextTexture_: - /** - The coordinates given here to the constructor (and move_to) are the bottom - left of the first letter in the string (for left alignment). Once the - string has been rotated and aligned, and overhanging letter shapes have - been taken into account, the top left of the bounding box (properties $x, - $y, inherited from $SizedTexture) reflects the actual top left position - in the coordinate system of the transform. In the coordinates of the - display the getters $display_x, $display_y, $display_w and $display_h - are available. - */ - constructor text_x/int text_y/int transform/Transform alignment/int text/string font color/int: - super text_x text_y transform alignment text font color - -class IconTexture extends TextTexture: - constructor icon_x/int icon_y/int transform/Transform alignment/int icon/Icon font/Font color/int: - super icon_x icon_y transform alignment icon.stringify icon.font_ color - - icon= new_icon/Icon -> none: - text = new_icon.stringify - font = new_icon.font_ - -/** -A texture that contains an uncompressed 2-color image. Initially all pixels - are transparent, but pixels can be given the color with $set_pixel. -*/ -class BitmapTexture extends OneByteBitmapTexture_: - constructor x/int y/int w/int h/int transform/Transform color/int: - super x y w h transform color - -/** -A two color bitmap texture where foreground and background pixels in the - texture are both drawn. -Initially all pixels have the background color. -Use $set_pixel to paint with the foreground, and $clear_pixel to paint with - the background. -*/ -class OpaqueBitmapTexture extends OneByteOpaqueBitmapTexture_: - constructor x/int y/int w/int h/int transform/Transform foreground_color/int background_color/int: - super x y w h transform foreground_color background_color - -/** -A texture backed by a P4 (binary two-level) PBM file. -The white areas (zeros) are rendered transparent and the black areas - (ones) are rendered in an arbitrary color. -*/ -class PbmTexture extends OneBytePbmTexture_: - /** - The byte array passed in must be a valid binary-mode (P4) PBM file. - If $bytes is a literal containing constants then it is used directly - from flash. However if the pixel drawing methods on this are used then - $bytes is moved to RAM and modified. This could cause an out-of-memory - on very large PBM files. - */ - constructor x/int y/int transform/Transform color/int bytes/ByteArray: - super x y transform color bytes - -class BarCodeEan13 extends OneByteBarCodeEan13_: - constructor code/string x/int y/int transform/Transform: - super code x y transform - - white_ -> int: return 1 - black_ -> int: return 0 - -class SimpleWindow extends OneByteSimpleWindow_: - /** - * A rectangular window with a fixed width colored border. The border is - * subtracted from the visible area inside the window. - */ - constructor x y w h transform border_width border_color/int background_color/int: - super x y w h transform border_width border_color background_color - - draw_frame canvas/Canvas_: - bytemap_zap canvas.pixels_ border_color - - draw_background canvas/Canvas_: - bytemap_zap canvas.pixels_ background_color - -class RoundedCornerWindow extends OneByteRoundedCornerWindow_: - constructor x y w h transform corner_radius background_color/int: - super x y w h transform corner_radius background_color - - set_opacity_ x y opacity map map_width --frame/bool: - if opacity < 0x80: - opacity = 0 - else: - opacity = 0xff - assert: not frame - if 0 <= x < map_width: - y_offset := y * map_width - if 0 <= y_offset < map.size: - map[x + y_offset] = opacity diff --git a/src/texture.toit b/src/texture.toit deleted file mode 100644 index ce7f697..0000000 --- a/src/texture.toit +++ /dev/null @@ -1,1102 +0,0 @@ -// Copyright (C) 2023 Toitware ApS. All rights reserved. -// Use of this source code is governed by an MIT-style license that can be -// found in the LICENSE file. - -import binary show LITTLE_ENDIAN -import bitmap show * -import font show Font -import math - -import .bar_code -import .common -import .one_byte as one_byte -import .style -import .true_color as true_color - -export Transform - -/** -Something you can draw on a canvas. It could be a text string, a pixmap or - a geometric figure. They can be stacked up and will be drawn from back to - front, with transparency. -*/ -abstract class Texture extends ElementOrTexture_: - -/** -Most $Texture s have a size and know their own position in the scene, and are - thus SizedTextures. A sized texture keeps track of the coordinate system that - it lives in, via the $transform_. It also tracks the untransformed left, - top, width and height of the texture, and the transformed left, top, width - and height of the texture with the current transform. -*/ -abstract class SizedTexture extends Texture: - x_ /int := 0 - y_ /int := 0 - w_ /int := 0 - h_ /int := 0 - transform_ /Transform := Transform.identity - - transform -> Transform: return transform_ - - /** - Create a new SizedTexture with the given position and size in the - coordinate system of the given transform. - $x_: The left of the texture. - $y_: The top of the texture. - $w_: The width of the texture. - $h_: The height of the texture. - $transform_: The coordinate system of the texture. - */ - constructor .x_ .y_ .w_ .h_ .transform_: - - /** - Returns the left edge of the texture in the display coordinates. - */ - display_x -> int: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - return x2 - unreachable - - /** - Returns the top edge of the texture in the display coordinates. - */ - display_y -> int: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - return y2 - unreachable - - /** - Returns the width of the texture in the display coordinates. - */ - display_w -> int: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - return w2 - unreachable - - /** - Returns the height of the texture in the display coordinates. - */ - display_h -> int: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - return h2 - unreachable - - /** - Returns the left edge of the texture in the coordinate system of the transform. - */ - x -> int: - return x_ - - /** - Returns the top edge of the texture in the coordinate system of the transform. - */ - y -> int: - return y_ - - /** - Invalidates (mark for redrawing) the entire area of this texture. - */ - invalidate: - if change_tracker: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - change_tracker.child_invalidated x2 y2 w2 h2 - - /** - Sets a new coordinate system for the texture. This can cause it to move or rotate. - */ - set_transform new_transform/Transform -> none: - invalidate - transform_ = new_transform - invalidate - - /** - Invalidates (mark for redrawing) a part of this texture. Coordinates are - in the transforms coordinate system. - $x: The left edge of the area to redraw. - $y: The top edge of the area to redraw. - $w: The width of the area to redraw. - $h: The height of the area to redraw. - */ - invalidate x/int y/int w/int h/int -> none: - if change_tracker: - transform_.xywh x y w h: | x2 y2 w2 h2 | - change_tracker.child_invalidated x2 y2 w2 h2 - - /** - Moves to a new position in the coordinate system of the texture's transform. - $new_x: New left edge in the texture's own coordinate system. - $new_y: New top edge in the texture's own coordinate system. - */ - move_to new_x/int new_y/int -> none: - if new_x != x_ or new_y != y_: - invalidate - x_ = new_x - y_ = new_y - invalidate - - // Redraws in a tile that will be copied to a part of the display. - write_ canvas/Canvas -> none: - win_w := canvas.width_ - win_h := canvas.height_ - win_x := canvas.x_offset_ - win_y := canvas.y_offset_ - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - if win_x + win_w <= x2: return // Right hand side of window is to the left of this texture. - if win_x >= x2 + w2: return // Left hand side of window is to the right of this texture. - if win_y + win_h <= y2: return // Bottom of window is above this texture. - if win_y >= y2 + h2: return // Top of window is below this texture. - write2_ canvas - - /** - # Inheritance - Override with method that will redraw in a file that will be copied to a - part of the display. Is not called for tiles that are wholly disjoint - with the texture. - */ - abstract write2_ canvas - -abstract class ResizableTexture extends SizedTexture: - constructor x/int y/int w/int h/int transform/Transform: - super x y w h transform - - width= new_width/int: - if new_width != w_: - invalidate - w_ = new_width - invalidate - - height= new_height/int: - if new_height != h_: - invalidate - h_ = new_height - invalidate - -// Texture that draws a standard EAN-13 bar code. TODO: Other scales. -abstract class BarCodeEan13_ extends SizedTexture: - sans10_ ::= Font.get "sans10" - number_height_ := EAN_13_BOTTOM_SPACE - - code_ := ? // 13 digit code as a string. - - /** - $code_: The 13 digit product code. - $x: The left edge of the barcode in the coordinate system of the transform. - $y: The top edge of the barcode in the coordinate system of the transform. - $transform: The coordinate system of the barcode. - */ - constructor .code_/string x/int y/int transform/Transform: - // The numbers go below the bar code in a way that depends on the size - // of the digits, so we need to take that into account when calculating - // the bounding box. - number_height_ = (sans10_.text_extent "8")[1] - height := EAN_13_HEIGHT + number_height_ - EAN_13_BOTTOM_SPACE - w := EAN_13_WIDTH - h := height + 1 - super x y w h transform - - l_ digit: - return EAN_13_L_CODES_[digit & 0xf] - - g_ digit: - return EAN_13_G_CODES_[digit & 0xf] - - r_ digit: - return (l_ digit) ^ 0x7f - - // Make a white background behind the bar code and draw the digits along the bottom. - draw_background_ canvas/Canvas: - // Background of a bar code is always white (0 color). - transform_.xywh x y w_ h_: | x2 y2 w2 h2 | - white_square_ - x2 - canvas.x_offset_ - y2 - canvas.y_offset_ - w2 - h2 - canvas - - text_orientation := transform_.orientation_ - - // Bar code coordinates. - text_x := x + EAN_13_QUIET_ZONE_WIDTH + EAN_13_START_WIDTH - text_y := y + EAN_13_HEIGHT + number_height_ - EAN_13_BOTTOM_SPACE + 1 - - // Canvas coordinates. - canvas_x := (transform_.x x + 1 text_y) - canvas.x_offset_ - canvas_y := (transform_.y x + 1 text_y) - canvas.y_offset_ - digit_ (code_.copy 0 1) canvas_x canvas_y canvas text_orientation - - canvas_x = (transform_.x text_x text_y) - canvas.x_offset_ - canvas_y = (transform_.y text_x text_y) - canvas.y_offset_ - step_x := (transform_.width 1 0) - step_y := (transform_.height 1 0) - (code_.copy 1 7).split "": - if it != "": - digit_ it canvas_x canvas_y canvas text_orientation - canvas_x += EAN_13_DIGIT_WIDTH * step_x - canvas_y += EAN_13_DIGIT_WIDTH * step_y - canvas_x += (EAN_13_MIDDLE_WIDTH - 1) * step_x - canvas_y += (EAN_13_MIDDLE_WIDTH - 1) * step_y - (code_.copy 7 13).split "": - if it != "": - digit_ it canvas_x canvas_y canvas text_orientation - canvas_x += EAN_13_DIGIT_WIDTH * step_x - canvas_y += EAN_13_DIGIT_WIDTH * step_y - marker_width := (sans10_.text_extent ">")[0] - canvas_x += (EAN_13_START_WIDTH + EAN_13_QUIET_ZONE_WIDTH - marker_width) * step_x - canvas_y += (EAN_13_START_WIDTH + EAN_13_QUIET_ZONE_WIDTH - marker_width) * step_y - digit_ ">" canvas_x canvas_y canvas text_orientation - - // Draw a black rectangle from x,y sized width,height in canvas coordinates. - abstract block_ x y width height canvas - - // Draw a white background rectangle from x,y sized width,height in canvas - // coordinates. - abstract white_square_ x y w h canvas - - // Draw a digit at x,y in canvas coordinates. - abstract digit_ digit x y canvas orientation - - // Line in transform coordinates. - line_ x top bottom canvas/Canvas transform: - height := bottom - top - transform.xywh x top 1 height: | x y w h | - block_ x - canvas.x_offset_ y - canvas.y_offset_ w h canvas - - // Redraw routine. - write2_ canvas/Canvas: - win_x := canvas.x_offset_ - win_y := canvas.y_offset_ - draw_background_ canvas - - x := x_ + EAN_13_QUIET_ZONE_WIDTH - top := y_ - - bottom := y_ + EAN_13_HEIGHT - y := bottom - EAN_13_BOTTOM_SPACE - // Start bars: 101. - line_ x top bottom canvas transform_ - line_ x + 2 top bottom canvas transform_ - x += 3 - first_code := EAN_13_FIRST_CODES_[code_[0] & 0xf] - // Left digits using the L or G mapping. - for i := 1; i < 7; i++: - digit := code_[i] - code := ((first_code >> (6 - i)) & 1) == 0 ? (l_ digit) : (g_ digit) - for b := 6; b >= 0; b--: - if ((1 << b) & code) != 0: - line_ x top y canvas transform_ - x++ - // Middle bars: 01010 - line_ x + 1 top bottom canvas transform_ - line_ x + 3 top bottom canvas transform_ - x += 5 - // Left digits using the R mapping. - for i := 7; i < 13; i++: - digit := code_[i] - code := r_ digit - for b := 6; b >= 0; b--: - if ((1 << b) & code) != 0: - line_ x top y canvas transform_ - x++ - // End bars: 101. - line_ x top bottom canvas transform_ - line_ x + 2 top bottom canvas transform_ - -TEXT_TEXTURE_ALIGN_LEFT ::= 0 -TEXT_TEXTURE_ALIGN_CENTER ::= 1 -TEXT_TEXTURE_ALIGN_RIGHT ::= 2 - -/** -A texture that represents the graphical display of a string. -*/ -abstract class TextTexture_ extends SizedTexture: - // Position in the coordinate system of the transform of the textual origin. - // The actual text may extend below and to the left of this point, especially - // with an alignment that is not TEXT_TEXTURE_ALIGN_LEFT. - text_x_ := 0 - text_y_ := 0 - alignment_ := 0 - displacement := 0 - string_ := ? - font_ := ? - - /** - $text_x_: The X coordinate of the origin of the text string. For left aligned - text this is the left of the text string (though some characters may overhang the left - edge, depending on the font). - $text_y_: The Y coordinate of the origin of the text string. This is the baseline - of the text string (though some characters extend below the baseline). - $transform: The coordinate system in which the string is placed. - $string_: The actual text to be displayed. - $font_: The font to display the string with. - */ - constructor .text_x_/int .text_y_/int transform/Transform .alignment_/int .string_/string .font_/Font: - extent := TextExtent_ string_ font_ alignment_ - displacement = extent.displacement - super - text_x_ + extent.x - text_y_ + extent.y - extent.w - extent.h - transform - - /** - Returns the current text being displayed. - */ - text -> string: - return string_ - - /** - Returns the current alignment of the string, either - TEXT_TEXTURE_ALIGN_LEFT, TEXT_TEXTURE_ALIGN_CENTER, or - TEXT_TEXTURE_ALIGN_RIGHT. - */ - alignment: - return alignment_ - - /** - Set a new string for this TextTexture to display. If the string - is related to the previous string, it tries to find the minimum screen - area that needs updating. - */ - text= new_string/string -> none: - if new_string == string_: return - if not change_tracker: - string_ = new_string - fix_bounding_box_ - return - - text_get_bounding_boxes_ string_ new_string alignment_ font_: | changed_extent_old/TextExtent_ changed_extent_new/TextExtent_ | - invalidate_extent_ changed_extent_old - invalidate_extent_ changed_extent_new - string_ = new_string - fix_bounding_box_ - return - string_ = new_string - update_ - - invalidate_extent_ ex: - invalidate - text_x_ + ex.x - text_y_ + ex.y - ex.w - ex.h - - /** - Sets a new alignment for this TextTexture to display. - */ - alignment= new_alignment/int -> none: - if new_alignment == alignment_: return - alignment_ = alignment - update_ - - /** - Sets a new font for this TextTexture to display. - */ - font= new_font/Font -> none: - if new_font == font_: return - font_ = new_font - update_ - - /** - Sets the alignment of this TextTexture to "center". - */ - center -> none: - alignment = TEXT_TEXTURE_ALIGN_CENTER - - /** - Sets the alignment of this TextTexture to "right". - */ - align_right -> none: - alignment = TEXT_TEXTURE_ALIGN_RIGHT - - /** - Sets the alignment of this TextTexture to "left". - */ - align_left -> none: - alignment = TEXT_TEXTURE_ALIGN_LEFT - - /** - Moves the text origin to new coordinates. - */ - move_to new_x/int new_y/int -> none: - if new_x == text_x_ and new_y == text_y_: return - text_x_ = new_x - text_y_ = new_y - update_ - - fix_bounding_box_: - extent := TextExtent_ string_ font_ alignment_ - displacement = extent.displacement - x_ = text_x_ + extent.x - y_ = text_y_ + extent.y - w_ = extent.w - h_ = extent.h - - update_ -> none: - invalidate - fix_bounding_box_ - invalidate - - // After the textures under us have drawn themselves, we draw on top. - write2_ canvas/Canvas: - x2 := transform_.x text_x_ + displacement text_y_ - y2 := transform_.y text_x_ + displacement text_y_ - draw_ - x2 - canvas.x_offset_ - y2 - canvas.y_offset_ - transform_.orientation_ - canvas - - abstract draw_ bx by orientation canvas - -/** -A rectangle in a solid color. -*/ -abstract class FilledRectangle_ extends ResizableTexture: - constructor x/int y/int w/int h/int transform/Transform: - super x y w h transform - - // Draw a colored rectangle at x,y on the canvas. - write2_ canvas/Canvas: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - start_x := x2 - canvas.x_offset_ - start_y := y2 - canvas.y_offset_ - translated_write_ start_x start_y w2 h2 canvas - - // Takes canvas coordinates. - abstract translated_write_ x y w h canvas - - // A helper method to make lines out of our rectangle primitives. - static line_ x1 y1 x2 y2 [build]: - if x1 == x2: - if y1 <= y2: - return build.call x1 y1 1 y2 - y1 - else: - return build.call x1 y2 1 y1 - y2 - else if y1 == y2: - if x1 <= x2: - return build.call x1 y1 x2 - x1 1 - else: - return build.call x2 y1 x1 - x2 1 - else: - throw "LINE_NOT_HORIZONTAL_OR_VERTICAL" - - -/** -A collections of textures which can be added to a display as a single texture. - It is not visible, only the textures it contains are visible. It has no - dimensions. Textures added to this group are drawn in the order they were - added, so the first-added textures are at the back and the last-added are - at the front. -*/ -class TextureGroup extends Texture implements Window: - elements_ := [] - inner_width -> int?: unreachable // Not used by textures, only elements. - inner_height -> int?: unreachable - - add element -> none: - elements_.add element - element.change_tracker = this - element.invalidate - - remove element -> none: - element.invalidate - elements_.remove element - element.change_tracker = null - - remove_all -> none: - elements_.do: - it.invalidate - it.change_tracker = null - elements_ = [] - - draw canvas/Canvas -> none: - write_ canvas - - // After the textures under us have drawn themselves, we draw on top. - write_ canvas/Canvas -> none: - elements_.do: it.write canvas - - // We don't crop anything, just pass on the invalidation to the next higher Window. - child_invalidated x/int y/int w/int h/int -> none: - if change_tracker: - change_tracker.child_invalidated x y w h - - child_invalidated_element x/int y/int w/int h/int -> none: - throw "NOT_IMPLEMENTED" - - invalidate -> none: - elements_.do: it.invalidate - -abstract class BorderlessWindow_ extends ResizableTexture implements Window: - elements_ := {} - inner_width -> int?: unreachable // Not used by textures, only elements. - inner_height -> int?: unreachable - - constructor x/int y/int w/int h/int transform: - this.transform = transform.translate x y - super x y w h transform - - add element/Texture -> none: - elements_.add element - element.change_tracker = this - element.invalidate - - remove element/Texture -> none: - elements_.remove element - element.invalidate - element.change_tracker = null - - remove_all -> none: - elements_.do: - it.invalidate - it.change_tracker = null - elements_.remove_all - - transform /Transform := ? - - child_invalidated_element x/int y/int w/int h/int -> none: - throw "NOT_IMPLEMENTED" - - child_invalidated x/int y/int w/int h/int -> none: - right := x + w - bottom := y + h - // We got the dimensions of the invalidation in driver frame of reference. - // We must trim them to fit the window, and pass them on. - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - // Now we have our own coordinates in driver frame of reference. - left2 := max x x2 - top2 := max y y2 - right2 := min right (x2 + w2) - bottom2 := min bottom (y2 + h2) - if right2 > left2 and bottom2 > top2: - if change_tracker: - change_tracker.child_invalidated left2 top2 (right2 - left2) (bottom2 - top2) - -/** -A WindowTexture_ is a collections of textures. It is modeled like a painting hung on - a wall. It consists (from back to front) of a wall, a frame and the painting - itself. The optional frame extends around and behind the picture, and can be - partially transparent on true-color displays, which enables drop shadows. The - painting can also be partially transparent. -*/ -abstract class WindowTexture_ extends BorderlessWindow_ implements Window: - inner_x_ /int := ? - inner_y_ /int := ? - inner_w_ /int := ? - inner_h_ /int := ? - - /** - Changes the inner width (without any borders) of the window. - */ - width= new_width/int: - if new_width != inner_w_: - inner_w_ = new_width - update_ - - /** - Changes the inner height (without any borders) of the window. - */ - height= new_height/int: - if new_height != inner_h_: - inner_h_ = new_height - update_ - - /** - Changes the top left corner (without any borders) of the window. - */ - move_to new_x/int new_y/int -> none: - if new_x != inner_x_ or new_y != inner_y_: - inner_x_ = new_x - inner_y_ = new_y - update_ - - update_ -> none: - invalidate - fix_bounding_box_ - invalidate - - abstract fix_bounding_box_ -> none - - static ALL_TRANSPARENT ::= ByteArray 1: 0 - static ALL_OPAQUE ::= ByteArray 1: 0xff - - static is_all_transparent opacity/ByteArray -> bool: - return opacity.size == 1 and opacity[0] == 0 - - static is_all_opaque opacity/ByteArray -> bool: - return opacity.size == 1 and opacity[0] == 0xff - - /** - Returns a canvas that is an alpha map for the given area that describes where - the wall around this window shines through. This defines the edges and - shadows of a window frame. For 2-color and 3-color textures this is a - bitmap with 0 for transparent and 1 for opaque. For true-color and - gray-scale textures it is a bytemap with 0 for transparent and 0xff for - opaque. As a special case it may return a single-entry byte array, which - means all pixels have the same transparency. - */ - abstract frame_map canvas/Canvas -> ByteArray - - /** - Returns a canvas that is an alpha map for the given area that describes where - the painting is visible. This defines the edges of the content of this - window. For 2-color and 3-color textures this is a bitmap with 0 for - transparent and 1 for opaque. For true-color and gray-scale textures it is - a bytemap with 0 for transparent and 0xff for opaque. As a special case it - may return a single-entry byte array, which means all pixels have the same - transparency. - */ - abstract painting_map canvas/Canvas -> ByteArray - - /** - Draws the background on the canvas. This represents the interior wall color - and other interior objects will be draw on top of this. Does not need to - take the frame_map or painting_map into account: The canvas this function - draws on will be composited using them afterwards. - */ - abstract draw_background canvas/Canvas -> none - - /** - Expected to draw the frame on the canvas. This represents the window frame - color. Does not need to take the frame_map or painting_map into account: The - return value from this function will be composited using them afterwards. - */ - abstract draw_frame canvas - - constructor .inner_x_ .inner_y_ .inner_w_ .inner_h_ x/int y/int w/int h/int transform: - super x y w h transform - - // After the textures under us have drawn themselves, we draw on top. - write2_ canvas/Canvas: - win_w := canvas.width_ - win_h := canvas.height_ - - painting_opacity := painting_map canvas - - // If the window is 100% painting at these coordinates then we can draw the - // elements of the window and no compositing is required. We merely draw - // the window background color and then draw the textures. - if is_all_opaque painting_opacity: - draw_background canvas - elements_.do: it.write_ canvas - return - - frame_opacity := frame_map canvas - - // The complicated case where we have to composite the tile from the wall, - // the frame, and the painting_opacity. - frame_canvas := null - if not is_all_transparent frame_opacity: - frame_canvas = canvas.create_similar - frame_canvas.x_offset_ = canvas.x_offset_ - frame_canvas.y_offset_ = canvas.y_offset_ - draw_frame frame_canvas - - painting_canvas := canvas.create_similar - painting_canvas.x_offset_ = canvas.x_offset_ - painting_canvas.y_offset_ = canvas.y_offset_ - draw_background painting_canvas - elements_.do: it.write_ painting_canvas - - canvas.composit frame_opacity frame_canvas painting_opacity painting_canvas - -/** -A rectangular window with a fixed width colored border. The border is - added to the visible area inside the window. -*/ -abstract class SimpleWindow_ extends WindowTexture_: - border_width_ := 0 - abstract make_alpha_map_ canvas - abstract make_opaque_ x y w h map map_width - - constructor x y w h transform .border_width_: - super x y w h // Inner dimensions. - // Actual dimensions. - x - border_width_ - y - border_width_ - w + border_width_ * 2 - h + border_width_ * 2 - transform - - border_width -> int: return border_width_ - - border_width= new_width/int -> none: - if new_width != border_width_: - border_width_ = new_width - update_ - - fix_bounding_box_ -> none: - // The border is on the outside of the drawable area. - x_ = inner_x_ - border_width_ - y_ = inner_y_ - border_width_ - w_ = inner_w_ + border_width_ * 2 - h_ = inner_h_ + border_width_ * 2 - - // Draws 100% opacity for the frame shape, a filled rectangle. - // (The frame is behind the painting, so this doesn't mean we only - // see the frame.) - frame_map canvas/Canvas: - if border_width_ == 0: return WindowTexture_.ALL_TRANSPARENT // The frame is not visible anywhere. - // Transform inner dimensions not including border - transform_.xywh inner_x_ inner_y_ inner_w_ inner_h_: | x y w2 h2 | - x2 := x - canvas.x_offset_ - y2 := y - canvas.y_offset_ - if x2 <= 0 and y2 <= 0 and x2 + w2 >= canvas.width_ and y2 + h2 >= canvas.height_: - // In the middle, the window content is 100% opaque and draw on top of the - // frame. There is no need to provide a frame alpha map, so for efficiency we - // just return 0 which indicates the frame is 100% transparent. - return WindowTexture_.ALL_TRANSPARENT - // Transform outer dimensions including border. - transform_.xywh x_ y_ w_ h_: | x y w2 h2 | - x2 := x - canvas.x_offset_ - y2 := y - canvas.y_offset_ - // We need to create a bitmap to describe the frame's extent. - transparency_map := make_alpha_map_ canvas - // Declare the whole area inside the frame's extent opaque. The window content will - // draw on top of this as needed. - make_opaque_ x2 y2 w2 h2 transparency_map canvas.width_ - return transparency_map - unreachable - - // Draws 100% opacity for the window content, a filled rectangle. - painting_map canvas/Canvas: - transform_.xywh inner_x_ inner_y_ inner_w_ inner_h_: | x y w2 h2 | - x2 := x - canvas.x_offset_ - y2 := y - canvas.y_offset_ - if x2 <= 0 and y2 <= 0 and x2 + w2 >= canvas.width_ and y2 + h2 >= canvas.height_: - return WindowTexture_.ALL_OPAQUE // The content is 100% opaque in the middle. - // We need to create a bitmap to describe the content's extent. - transparency_map := make_alpha_map_ canvas - make_opaque_ x2 y2 w2 h2 transparency_map canvas.width_ - return transparency_map - unreachable - -/** A rectangular window with rounded corners. */ -abstract class RoundedCornerWindow_ extends WindowTexture_: - corner_radius_ := 0 - opacities_ := null - abstract make_alpha_map_ canvas/Canvas padding - abstract make_opaque_ x y w h map map_width --frame/bool - abstract set_opacity_ x y opacity map map_width --frame/bool - - constructor x y w h transform .corner_radius_: - if corner_radius_ > TABLE_SIZE_: throw "OUT_OF_RANGE" - super x y w h x y w h transform - - constructor.protected_ inner_x inner_y inner_w inner_h x y w h transform .corner_radius_: - if corner_radius_ > TABLE_SIZE_: throw "OUT_OF_RANGE" - super inner_x inner_y inner_w inner_h x y w h transform - - corner_radius -> int: return corner_radius_ - - corner_radius= new_radius/int -> none: - if not 0 <= new_radius <= TABLE_SIZE_: throw "OUT_OF_RANGE" - if new_radius != corner_radius_: - invalid_radius := max corner_radius_ new_radius - corner_radius_ = new_radius - if change_tracker: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - change_tracker.child_invalidated x2 y2 invalid_radius invalid_radius - change_tracker.child_invalidated x2 + w2 - invalid_radius y2 invalid_radius invalid_radius - change_tracker.child_invalidated x2 y2 + h2 - invalid_radius invalid_radius invalid_radius - change_tracker.child_invalidated x2 + w2 + invalid_radius y2 + h2 - invalid_radius invalid_radius invalid_radius - - fix_bounding_box_ -> none: - // There's no border outside the drawable area. - x_ = inner_x_ - y_ = inner_y_ - w_ = inner_w_ - h_ = inner_h_ - - ensure_opacities_: - if opacities_: return - opacities_ = ByteArray corner_radius_ * corner_radius_ - downsample := TABLE_SIZE_ / corner_radius_ // For example 81 for a corner_radius of 3. - steps := List corner_radius_: - (it * TABLE_SIZE_) / corner_radius - corner_radius_.repeat: | j | - b := steps[j] - corner_radius_.repeat: | i | - a := steps[i] - idx := j * corner_radius_ + i - // Set the opacity according to whether the downsample x downsample - // square is fully outside the circle, fully inside the circle or on - // the edge. - if QUARTER_CIRCLE_[b + downsample - 1] >= a + downsample: - opacities_[idx] = 0xff // Inside quarter circle. - else if QUARTER_CIRCLE_[b] < a: - opacities_[idx] = 0 // Outside quarter circle. - else: - // Edge of quarter circle. - total := 0 - downsample.repeat: | small_y | - extent := QUARTER_CIRCLE_[b + small_y] - if extent >= a + downsample: - total += downsample - else if extent > a: - total += extent - a - opacities_[idx] = (0xff * total) / (downsample * downsample) - - frame_map canvas/Canvas: - return WindowTexture_.ALL_TRANSPARENT // No frame on these windows. - - static TABLE_SIZE_ ::= 256 - // The heights of a top-right quarter circle of radius [TABLE_SIZE_]. - static QUARTER_CIRCLE_ ::= create_quarter_circle_array_ TABLE_SIZE_ - - static create_quarter_circle_array_ size: - array := ByteArray size - hypotenuse := (size - 1) * (size - 1) - size.repeat: - array[it] = (hypotenuse - it * it).sqrt.to_int - return array - - // Draws 100% opacity for the window content, a filled rounded-corner rectangle. - painting_map canvas/Canvas: - transform_.xywh inner_x_ inner_y_ inner_w_ inner_h_: | x y w2 h2 | - x2 := x - canvas.x_offset_ - y2 := y - canvas.y_offset_ - if x2 + corner_radius_ <= 0 and y2 + corner_radius_ <= 0 and x2 + w2 - corner_radius_ >= canvas.width_ and y2 + h2 - corner_radius_ >= canvas.height_: - return WindowTexture_.ALL_OPAQUE // The content is 100% opaque in the middle. - // We need to create a bitmap to describe the content's extent. - transparency_map := make_alpha_map_ canvas 0 - draw_rounded_corners_ transparency_map canvas.width_ x2 y2 w2 h2 --frame=false - return transparency_map - unreachable - - draw_rounded_corners_ transparency_map map_width x2 y2 w2 h2 --frame/bool: - // Part 1 of a cross of opacity (the rounded rectangle minus its corners). - make_opaque_ (x2 + corner_radius_) y2 (w2 - 2 * corner_radius_) h2 transparency_map map_width --frame=frame - if corner_radius_ <= 0: return - ensure_opacities_ - // Part 2 of the cross. - make_opaque_ x2 (y2 + corner_radius_) w2 (h2 - 2 * corner_radius_) transparency_map map_width --frame=frame - // The rounded corners: - left := x2 + corner_radius_ - 1 - right := x2 + w2 - corner_radius_ - top := y2 + corner_radius_ - 1 - bottom := y2 + h2 - corner_radius_ - corner_radius_.repeat: | j | - corner_radius_.repeat: | i | - opacity := opacities_[i + j * corner_radius_] - set_opacity_ (left - i) (top - j) opacity transparency_map map_width --frame=frame - set_opacity_ (right + i) (top - j) opacity transparency_map map_width --frame=frame - set_opacity_ (left - i) (bottom + j) opacity transparency_map map_width --frame=frame - set_opacity_ (right + i) (bottom + j) opacity transparency_map map_width --frame=frame - -/** A rectangular window with rounded corners and a drop shadow */ -abstract class DropShadowWindow_ extends RoundedCornerWindow_: - blur_radius := 0 - drop_distance_x := 0 - drop_distance_y := 0 - - constructor x y w h transform corner_radius .blur_radius .drop_distance_x .drop_distance_y: - if not 0 <= blur_radius <= 6: throw "OUT_OF_RANGE" - extension_left := blur_radius > drop_distance_x ? blur_radius - drop_distance_x : 0 - extension_top := blur_radius > drop_distance_y ? blur_radius - drop_distance_y : 0 - extension_right := blur_radius > -drop_distance_x ? blur_radius + drop_distance_x : 0 - extension_bottom := blur_radius > -drop_distance_y ? blur_radius + drop_distance_y : 0 - super.protected_ - x - y - w - h - x - extension_left - y - extension_top - w + extension_left + extension_right - h + extension_top + extension_bottom - transform - corner_radius - - frame_map canvas/Canvas: - win_x := canvas.x_offset_ - win_y := canvas.y_offset_ - - // Transform inner dimensions excluding shadow to determine if the canvas - // is wholly inside the window. - transform_.xywh inner_x_ inner_y_ inner_w_ inner_h_: | x y w2 h2 | - x2 := x - win_x - y2 := y - win_y - if x2 + corner_radius_ <= 0 and y2 + corner_radius_ <= 0 and x2 + w2 - corner_radius_ >= canvas.width_ and y2 + h2 - corner_radius_ >= canvas.height_: - // In the middle, the window content is 100% opaque and draw on top of the - // frame. There is no need to provide a frame alpha map, so for efficiency we - // just return 0 which indicates the frame is 100% transparent. - return WindowTexture_.ALL_TRANSPARENT - - // Transform outer dimensions including border to determine if the canvas - // is wholly outside the window and its shadow. - transform_.xywh x_ y_ w_ h_: | x y w2 h2 | - x2 := x - win_x - y2 := y - win_y - if x2 + w2 <= 0 or y2 + h2 <= 0 or x2 >= canvas.width_ or y2 >= canvas.height_: - return WindowTexture_.ALL_TRANSPARENT // The frame is not opaque outside the shadow - - // Create a bitmap to describe the frame's extent. It needs to be padded - // relative to the canvas size so we can use the Gaussian blur. - transparency_map := make_alpha_map_ canvas blur_radius * 2 - map_width := canvas.width_ + blur_radius * 2 - - transform_.xywh (inner_x_ + drop_distance_x) (inner_y_ + drop_distance_y) inner_w_ inner_h_: | x y w2 h2 | - x2 := x + blur_radius - win_x - y2 := y + blur_radius - win_y - - // Transform the unblurred dimensions of the shadow so we can plot that on the - // transparency map. - draw_rounded_corners_ transparency_map map_width x2 y2 w2 h2 --frame=true - - if blur_radius == 0: return transparency_map - - // Blur the shadow. - bytemap_blur transparency_map map_width blur_radius - - // Crop off the extra that was added to blur. - transparency_map_unpadded := make_alpha_map_ canvas 0 - canvas.height_.repeat: - source_index := (it + blur_radius) * map_width + blur_radius - transparency_map_unpadded.replace (it*canvas.width_) transparency_map source_index source_index + canvas.width_ - return transparency_map_unpadded -abstract class BitmapTextureBase_ extends SizedTexture: - w := 0 - h := 0 - bytes_per_line_ /int - - constructor x/int y/int .w .h transform/Transform: - bytes_per_line_ = (w + 7) >> 3 // Divide by 8, rounding up. - super x y w h transform - - index_and_mask_ x y [block]: - if not 0 <= x < w: throw "OUT_OF_RANGE" - if not 0 <= y < h: throw "OUT_OF_RANGE" - index := (x >> 3) + (y * bytes_per_line_) - bit := 0x80 >> (x & 7) - block.call index bit - - // After the textures under us have drawn themselves, we draw on top. - write2_ canvas/Canvas: - if w == 0 or h == 0: return - x2 := transform_.x x_ y_ - y2 := transform_.y x_ y_ - draw_ - x2 - canvas.x_offset_ - y2 - canvas.y_offset_ - transform_.orientation_ - canvas - - abstract draw_ bx by orientation canvas - -abstract class BitmapTexture_ extends BitmapTextureBase_: - bytes_ /ByteArray? ::= null - - constructor x/int y/int w/int h/int transform/Transform: - bytes_per_line := (w + 7) >> 3 // Divide by 8, rounding up. - bytes_ = ByteArray h * bytes_per_line - super x y w h transform - - constructor.no_allocate_ x/int y/int w/int h/int transform/Transform: - super x y w h transform - - pixel_is_set x/int y/int -> bool: - index_and_mask_ x y: | index bit | - return (bytes_[index] & bit) != 0 - unreachable - - set_pixel x/int y/int -> none: - index_and_mask_ x y: | index bit | - bytes_[index] |= bit - - clear_pixel x/int y/int -> none: - index_and_mask_ x y: | index bit | - bytes_[index] &= 0xff ^ bit - - set_all_pixels -> none: - bitmap_zap bytes_ 1 - - clear_all_pixels -> none: - bitmap_zap bytes_ 0 - -abstract class PixmapTexture_ extends SizedTexture: - w /int - h /int - - constructor x/int y/int .w .h transform/Transform: - super x y w h transform - - // After the textures under us have drawn themselves, we draw on top. - write2_ canvas/Canvas: - if w == 0 or h == 0: return - x2 := transform_.x x_ y_ - y2 := transform_.y x_ y_ - draw_ - x2 - canvas.x_offset_ - y2 - canvas.y_offset_ - transform_.orientation_ - canvas - - abstract draw_ bx by orientation canvas - -class PbmParser_: - static INVALID_PBM_ ::= "INVALID PBM" - - bytes_ /ByteArray - next_ := 0 - - width := 0 - height := 0 - rows_ := null - row_byte_length_ := 0 - image_data_offset := 0 - - constructor bytes/ByteArray: - bytes_ = bytes - - parse_: - parse_magic_number_ - parse_multiple_whitespace_ - width = parse_number_ - parse_multiple_whitespace_ --at_least_one - height = parse_number_ - parse_whitespace_ - image_data_offset = next_ - parse_bit_map_ - - parse_magic_number_: - if not (bytes_.size > next_ + 1 and bytes_[next_] == 'P' and bytes_[next_ + 1] == '4'): throw INVALID_PBM_ - next_ += 2 - - parse_whitespace_: - if not (bytes_.size > next_ and is_pbm_whitespace_ bytes_[next_]): throw INVALID_PBM_ - next_++ - - parse_multiple_whitespace_ --at_least_one=false: - start := next_ - while bytes_.size > next_ and is_pbm_whitespace_ bytes_[next_]: next_++ - if bytes_.size > next_ and bytes_[next_] == '#': // Skip comment. - while bytes_.size > next_ and bytes_[next_] != '\n': next_++ - if bytes_.size > next_: next_++ - if at_least_one and start == next_: throw INVALID_PBM_ - - parse_number_ -> int: - next_ws := next_ - while next_ws < bytes_.size and not is_pbm_whitespace_ bytes_[next_ws]: next_ws++ - if not bytes_.is_valid_string_content next_ next_ws: throw INVALID_PBM_ - number_string := bytes_.to_string next_ next_ws - number := int.parse number_string --on_error=: throw INVALID_PBM_ - next_ = next_ws - return number - - parse_bit_map_: - row_byte_length_ = width / 8 + (width % 8 == 0 ? 0 : 1) - if not bytes_.size > row_byte_length_ * height: throw INVALID_PBM_ - - rows: - if not rows_: - rows_ = List height: - from := next_ - next_ = next_ + row_byte_length_ - bytes_.copy from next_ - return rows_ - - is_pbm_whitespace_ byte -> bool: - return byte == '\t' or byte == '\v' or byte == ' ' or byte == '\n' or byte == '\r' or byte == '\f' diff --git a/src/three_color.toit b/src/three_color.toit index 7171b26..8d95016 100644 --- a/src/three_color.toit +++ b/src/three_color.toit @@ -10,7 +10,6 @@ For use with e-paper black, white, and red displays. import font show Font import icons show Icon import .pixel_display show ThreeColorPixelDisplay // For the doc comment. -import .texture import .two_bit_texture import .two_bit_texture as two_bit @@ -39,82 +38,3 @@ class Canvas_ extends two_bit.Canvas_: result := Canvas_ width_ height_ result.transform = transform return result - -class FilledRectangle extends TwoBitFilledRectangle_: - constructor color x/int y/int w/int h/int transform/Transform: - assert: color != 3 // Invalid color. - super color x y w h transform - - /// A line from $x1,$y1 to $x2,$y2. The line must be horizontal or vertical. - constructor.line color x1/int y1/int x2/int y2/int transform/Transform: - return FilledRectangle_.line_ x1 y1 x2 y2: | x y w h | - FilledRectangle color x y w h transform - -class TextTexture extends TwoBitTextTexture_: - /** - The coordinates given here to the constructor (and move_to) are the bottom - left of the first letter in the string (for left alignment). Once the - string has been rotated and aligned, and overhanging letter shapes have - been taken into account, the top left of the bounding box (properties $x, - $y, inherited from $SizedTexture) reflects the actual top left position - in the coordinate system of the transform. In the coordinates of the - display the getters $display_x, $display_y, $display_w and $display_h - are available. - */ - constructor text_x/int text_y/int transform/Transform alignment/int text/string font color: - assert: color != 3 // Invalid color. - super text_x text_y transform alignment text font color - -class IconTexture extends TextTexture: - constructor icon_x/int icon_y/int transform/Transform alignment/int icon/Icon font/Font color/int: - super icon_x icon_y transform alignment icon.stringify icon.font_ color - - icon= new_icon/Icon -> none: - text = new_icon.stringify - font = new_icon.font_ - -/** -A texture that contains an uncompressed 2-color image. Initially all pixels - are transparent, but pixels can be given the color with $set_pixel. -*/ -class BitmapTexture extends TwoBitBitmapTexture_: - constructor x/int y/int w/int h/int transform/Transform color/int: - assert: color != 3 // Invalid color. - super x y w h transform color - -// A two color bitmap texture. Initially all pixels have the background color. -// Use set_pixel to paint with the foreground, and clear_pixel to paint with -// the background. -class OpaqueBitmapTexture extends TwoBitOpaqueBitmapTexture_: - constructor x/int y/int w/int h/int transform/Transform foreground_color/int background_color: - assert: background_color != 3 // Invalid color. - super x y w h transform foreground_color background_color - -// A texture backed by a P4 (binary two-level) PBM file. The white areas -// (zeros) are rendered transparent and the black areas (ones) are rendered in -// an arbitrary color. -class PbmTexture extends PbmTexture_: - // The byte array passed in must be a valid binary-mode (P4) PBM file. - // If $bytes is a literal containing constants then it is used directly - // from flash. However if the pixel drawing methods on this are used then - // $bytes is moved to RAM and modified. This could cause an out-of-memory - // on very large PBM files. - constructor x/int y/int transform/Transform color/int bytes/ByteArray: - assert: color != 3 // Invalid color. - super x y transform color bytes - -class BarCodeEan13 extends TwoBitBarCodeEan13_: - constructor code/string x/int y/int transform/Transform: - super code x y transform BLACK WHITE - -/** -A rectangular window with a fixed width colored border. -The border is subtracted from the visible area inside the window. -*/ -class SimpleWindow extends TwoBitSimpleWindow_: - constructor x y w h transform border_width border_color background_color: - super x y w h transform border_width border_color background_color - -class RoundedCornerWindow extends TwoBitRoundedCornerWindow_: - constructor x y w h transform corner_radius background_color: - super x y w h transform corner_radius background_color diff --git a/src/true_color.toit b/src/true_color.toit index 9b26007..7751fea 100644 --- a/src/true_color.toit +++ b/src/true_color.toit @@ -12,7 +12,6 @@ import icons show Icon import .common import .gray_scale as gray_scale_ import .pixel_display show TrueColorPixelDisplay // For the doc comment. -import .texture get_rgb r/int g/int b/int -> int: return (r << 16) | (g << 8) | b @@ -59,7 +58,7 @@ class Canvas_ extends Canvas: return get_rgb red_[idx] green_[idx] blue_[idx] /** - Creates a blank texture with the same dimensions as this one. + Creates a blank canvas with the same dimensions as this one. */ create_similar: result := Canvas_ width_ height_ @@ -95,332 +94,3 @@ class Canvas_ extends Canvas: bytemap_draw_text x2 y2 r o2 text font red_ width_ bytemap_draw_text x2 y2 g o2 text font green_ width_ bytemap_draw_text x2 y2 b o2 text font blue_ width_ - -class FilledRectangle extends FilledRectangle_: - color_ := ? - - constructor .color_ x/int y/int w/int h/int transform/Transform: - assert: color_ <= 0xff_ff_ff // Not transparent. - super x y w h transform - - /// A line from $x1,$y1 to $x2,$y2. The line must be horizontal or vertical. - constructor.line color x1/int y1/int x2/int y2/int transform/Transform: - return FilledRectangle_.line_ x1 y1 x2 y2: | x y w h | - FilledRectangle color x y w h transform - - translated_write_ x y w h canvas/Canvas_: - if bytemap_rectangle x y (red_component color_) w h canvas.red_ canvas.width_: - bytemap_rectangle x y (green_component color_) w h canvas.green_ canvas.width_ - bytemap_rectangle x y (blue_component color_) w h canvas.blue_ canvas.width_ - -class TextTexture extends TextTexture_: - color_ := 0 - - /** - The coordinates given here to the constructor (and move_to) are the bottom - left of the first letter in the string (for left alignment). Once the - string has been rotated and aligned, and overhanging letter shapes have - been taken into account, the top left of the bounding box (properties $x, - $y, inherited from $SizedTexture) reflects the actual top left position - in the coordinate system of the transform. In the coordinates of the - display the getters $display_x, $display_y, $display_w and $display_h - are available. - */ - constructor text_x/int text_y/int transform/Transform alignment/int text/string font .color_: - assert: color_ <= 0xff_ff_ff // No transparent color. - super text_x text_y transform alignment text font - - color= new_color -> none: - if color_ == new_color: return - color_ = new_color - invalidate - - draw_ bx by orientation canvas/Canvas_: - bytemap_draw_text bx by (red_component color_) orientation string_ font_ canvas.red_ canvas.width_ - bytemap_draw_text bx by (green_component color_) orientation string_ font_ canvas.green_ canvas.width_ - bytemap_draw_text bx by (blue_component color_) orientation string_ font_ canvas.blue_ canvas.width_ - -class IconTexture extends TextTexture: - constructor icon_x/int icon_y/int transform/Transform alignment/int icon/Icon font/Font color/int: - super icon_x icon_y transform alignment icon.stringify icon.font_ color - - icon= new_icon/Icon -> none: - text = new_icon.stringify - font = new_icon.font_ - -/** -A texture that contains an uncompressed 2-color image. Initially all pixels - are transparent, but pixels can be given the color with $set_pixel. -*/ -class BitmapTexture extends BitmapTexture_: - color_ := 0 - - constructor x/int y/int w/int h/int transform/Transform .color_/int: - super x y w h transform - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_bitmap bx by (red_component color_) orientation bytes_ 0 w canvas.red_ canvas.width_ true - bitmap_draw_bitmap bx by (green_component color_) orientation bytes_ 0 w canvas.green_ canvas.width_ true - bitmap_draw_bitmap bx by (blue_component color_) orientation bytes_ 0 w canvas.blue_ canvas.width_ true - -/** -A two color bitmap texture where foreground and background pixels in the - texture are both drawn. -Initially all pixels have the background color. -Use $set_pixel to paint with the foreground, and $clear_pixel to paint with - the background. -*/ -class OpaqueBitmapTexture extends BitmapTexture: - background_color_ := 0 - - constructor x/int y/int w/int h/int transform/Transform foreground_color/int .background_color_: - super x y w h transform foreground_color - - write2_ canvas/Canvas_: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - x := x2 - canvas.x_offset_ - y := y2 - canvas.y_offset_ - bytemap_rectangle x y (red_component background_color_) w2 h2 canvas.red_ canvas.width_ - bytemap_rectangle x y (green_component background_color_) w2 h2 canvas.green_ canvas.width_ - bytemap_rectangle x y (blue_component background_color_) w2 h2 canvas.blue_ canvas.width_ - super canvas // Draw foreground. - -// A texture backed by a P4 (binary two-level) PBM file. The white areas -// (zeros) are rendered transparent and the black areas (ones) are rendered in -// an arbitrary color. -class PbmTexture extends BitmapTexture_: - width_ := 0 - height_ := 0 - color_ := 0 - bytes_ := ? - - // The byte array passed in must be a valid binary-mode (P4) PBM file. - // If $bytes is a literal containing constants then it is used directly - // from flash. However if the pixel drawing methods on this are used then - // $bytes is moved to RAM and modified. This could cause an out-of-memory - // on very large PBM files. - constructor x/int y/int transform/Transform .color_/int bytes/ByteArray: - parser := PbmParser_ bytes - parser.parse_ - bytes_ = bytes[parser.image_data_offset..] - super.no_allocate_ x y parser.width parser.height transform - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_bitmap bx by (red_component color_) orientation bytes_ 0 w canvas.red_ canvas.width_ true - bitmap_draw_bitmap bx by (green_component color_) orientation bytes_ 0 w canvas.green_ canvas.width_ true - bitmap_draw_bitmap bx by (blue_component color_) orientation bytes_ 0 w canvas.blue_ canvas.width_ true - -/** -A rectangular pixmap that can be drawn in any of 4 orientations on a canvas. -Up to 255 different colors can be represented. Each color to be used - is allocated and given an index by $allocate_color. In addition there is - a transparent index, always index 0. -*/ -class IndexedPixmapTexture extends PixmapTexture_: - bytes_/ByteArray ::= ? - palette_/ByteArray := ? - green_palette_/ByteArray? := null - blue_palette_/ByteArray? := null - used_indices_/int := 1 - - /** - Creates a pixmap. All pixels are initially transparent. - */ - constructor x/int y/int w/int h/int transform/Transform: - bytes_ = ByteArray w * h - palette_ = #[0] - super x y w h transform - - /** - Creates a pixmap with the given pixels and palette. - Transparent pixels are represented by zero bytes. - The palette byte array contains 3 bytes (r, g, b) for each - index in use. The first three entries in the palette are - ignored since they correspond to the transparent index. - */ - constructor x/int y/int w/int h/int transform/Transform .bytes_/ByteArray .palette_/ByteArray: - if bytes_.size != w * h: throw "INVALID_ARGUMENT" - if palette_.size % 3 != 0: throw "INVALID_ARGUMENT" - used_indices_ = palette_.size / 3 - super x y w h transform - - /** - Gets the index in the palette for a given color, expressed with components from 0-255. - This can be quite slow if there are a lot of colors. - At most 255 colors can be allocated. - */ - allocate_color r/int g/int b/int -> int: - for i := 1; i < used_indices_; i++: - if r == (red_component i) and g == (green_component i) and b == (blue_component i): return i - if used_indices_ == 0x100: throw "No more colors available" - if palette_.size / 3 <= (round_up used_indices_ 16): - old := palette_ - palette_ = ByteArray palette_.size + 48 - palette_.replace 0 old - green_palette_ = null - blue_palette_ = null - palette_[used_indices_ * 3] = r - palette_[used_indices_ * 3 + 1] = g - palette_[used_indices_ * 3 + 2] = b - return used_indices_++ - - /** - Gets the index in the palette for a given color, expressed as a 6 digit hex value 0xrrggbb. - This can be quite slow if there are a lot of colors. - At most 255 colors can be allocated. - */ - allocate_color color/int -> int: - return allocate_color - color >> 16 - (color >> 8) & 0xff - color & 0xff - - /// Looks up the index in the palette. - red_component index/int -> int: - return palette_[index * 3] - - /// Looks up the index in the palette. - green_component index/int -> int: - return palette_[index * 3 + 1] - - /// Looks up the index in the palette. - blue_component index/int -> int: - return palette_[index * 3 + 2] - - /// Returns the index value of the color at the given coordinates. - get_pixel x/int y/int -> int: - return bytes_[x + y * w] - - set_pixel x/int y/int index/int -> none: - bytes_[x + y * w] = index - - /// Sets the pixel at the given coordinates to transparent. - clear_pixel x/int y/int -> none: - set_pixel x y 0 - - set_all_pixels index/int -> none: - bitmap_zap bytes_ index - - /// Sets all pixels to transparent. - clear_all_pixels -> none: - bitmap_zap bytes_ 0 - - draw_ bx by orientation canvas/Canvas_: - if not green_palette_: green_palette_ = palette_[1..] - if not blue_palette_: blue_palette_ = palette_[2..] - bitmap_draw_bytemap bx by 0 orientation bytes_ w palette_ canvas.red_ canvas.width_ - bitmap_draw_bytemap bx by 0 orientation bytes_ w green_palette_ canvas.green_ canvas.width_ - bitmap_draw_bytemap bx by 0 orientation bytes_ w blue_palette_ canvas.blue_ canvas.width_ - -class BarCodeEan13 extends BarCodeEan13_: - constructor code/string x/int y/int transform/Transform: - super code x y transform - - white_square_ x y w h canvas/Canvas_: - white ::= 0xff - if bytemap_rectangle x y white w h canvas.red_ canvas.width_: - bytemap_rectangle x y white w h canvas.green_ canvas.width_ - bytemap_rectangle x y white w h canvas.blue_ canvas.width_ - - digit_ digit x y canvas orientation -> none: - if digit == "": return - black ::= 0 - bytemap_draw_text x y black orientation digit sans10_ canvas.red_ canvas.width_ - bytemap_draw_text x y black orientation digit sans10_ canvas.green_ canvas.width_ - bytemap_draw_text x y black orientation digit sans10_ canvas.blue_ canvas.width_ - - block_ x y width height canvas/Canvas_: - black ::= 0 - if bytemap_rectangle x y black width height canvas.red_ canvas.width_: - bytemap_rectangle x y black width height canvas.green_ canvas.width_ - bytemap_rectangle x y black width height canvas.blue_ canvas.width_ - -/** -A rectangular window with a fixed width colored border. -The border is subtracted from the visible area inside the window. -*/ -class SimpleWindow extends SimpleWindow_: - background_color := ? - border_color := ? - - constructor x y w h transform border_width .border_color .background_color: - super x y w h transform border_width - - draw_frame canvas/Canvas_: - bytemap_zap canvas.red_ (red_component border_color) - bytemap_zap canvas.green_ (green_component border_color) - bytemap_zap canvas.blue_ (blue_component border_color) - - draw_background canvas/Canvas_: - bytemap_zap canvas.red_ (red_component background_color) - bytemap_zap canvas.green_ (green_component background_color) - bytemap_zap canvas.blue_ (blue_component background_color) - - make_alpha_map_ canvas/Canvas_: - return ByteArray canvas.width_ * canvas.height_ - - make_opaque_ x y w h map map_width: - bytemap_rectangle x y 0xff w h map map_width - -class RoundedCornerWindow extends RoundedCornerWindow_: - background_color := ? - - constructor x y w h transform corner_radius .background_color: - super x y w h transform corner_radius - - make_alpha_map_ canvas padding: - return ByteArray (canvas.width_ + padding) * (canvas.height_ + padding) - - make_opaque_ x y w h map map_width --frame/bool: - assert: not frame - bytemap_rectangle x y 0xff w h map map_width - - set_opacity_ x y opacity map map_width --frame/bool: - assert: not frame - if 0 <= x < map_width: - y_offset := y * map_width - if 0 <= y_offset < map.size: - map[x + y_offset] = opacity - - draw_background canvas/Canvas_: - bytemap_zap canvas.red_ (red_component background_color) - bytemap_zap canvas.green_ (green_component background_color) - bytemap_zap canvas.blue_ (blue_component background_color) - - draw_frame canvas/Canvas_: - throw "UNREACHABLE" - -class DropShadowWindow extends DropShadowWindow_: - background_color := ? - max_shadow_opacity_ := ? - - constructor x y w h transform .background_color --corner_radius=5 --blur_radius=5 --drop_distance_x=10 --drop_distance_y=10 --shadow_opacity_percent=25: - // Scale the 0-100% opacity percentage to cover the 8 bit unsigned integer - // range 0-255. - max_shadow_opacity_ = (shadow_opacity_percent * 2.5500001).to_int - super x y w h transform corner_radius blur_radius drop_distance_x drop_distance_y - - make_alpha_map_ canvas padding: - return ByteArray (canvas.width_ + padding) * (canvas.height_ + padding) - - make_opaque_ x y w h map map_width --frame/bool: - bytemap_rectangle x y (frame ? max_shadow_opacity_ : 255) w h map map_width - - set_opacity_ x y opacity map map_width --frame/bool: - if 0 <= x < map_width: - y_offset := y * map_width - if 0 <= y_offset < map.size: - if frame: - map[x + y_offset] = (opacity * max_shadow_opacity_) >> 8 - else: - map[x + y_offset] = opacity - - draw_background canvas/Canvas_: - bytemap_zap canvas.red_ (red_component background_color) - bytemap_zap canvas.green_ (green_component background_color) - bytemap_zap canvas.blue_ (blue_component background_color) - - draw_frame canvas/Canvas_: - bytemap_zap canvas.red_ 0 - bytemap_zap canvas.green_ 0 - bytemap_zap canvas.blue_ 0 diff --git a/src/two_bit_texture.toit b/src/two_bit_texture.toit index 752708f..9eb569d 100644 --- a/src/two_bit_texture.toit +++ b/src/two_bit_texture.toit @@ -10,7 +10,6 @@ import bitmap show * import font show Font import .common import .common as common -import .texture import .two_color as two_color // The canvas contains two bitmapped ByteArrays, for up to 4 colors or gray @@ -61,169 +60,3 @@ abstract class Canvas_ extends Canvas: b1 := (color >> 1) & 1 bitmap_draw_text x2 y2 b0 o2 text font plane_0_ width_ bitmap_draw_text x2 y2 b1 o2 text font plane_1_ width_ - -class TwoBitFilledRectangle_ extends FilledRectangle_: - color_ := ? - - constructor .color_ x/int y/int w/int h/int transform/Transform: - super x y w h transform - - translated_write_ x y w h canvas/Canvas_: - bitmap_rectangle x y (color_ & 1) w h canvas.plane_0_ canvas.width_ - bitmap_rectangle x y ((color_ & 2) >> 1) w h canvas.plane_1_ canvas.width_ - -class TwoBitTextTexture_ extends TextTexture_: - color_ := 0 - - // The coordinates given here to the constructor (and move_to) are the bottom - // left of the first letter in the string (for left alignment). Once the - // string has been rotated and aligned, and overhanging letter shapes have - // been taken into account, the bounding box (properties x, y, w, h, - // inherited from SizedTexture) reflects the actual bounding box of the - // text string. - constructor text_x/int text_y/int transform/Transform alignment/int text/string font .color_: - super text_x text_y transform alignment text font - - color= new_color -> none: - if color_ == new_color: return - color_ = new_color - invalidate - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_text bx by color_&1 orientation string_ font_ canvas.plane_0_ canvas.width_ - bitmap_draw_text bx by (color_&2) >> 1 orientation string_ font_ canvas.plane_1_ canvas.width_ - -/** -A texture that contains an uncompressed 2-color image. Initially all pixels - are transparent, but pixels can be given the color with $set_pixel. -*/ -class TwoBitBitmapTexture_ extends BitmapTexture_: - color_ := 0 - - constructor x/int y/int w/int h/int transform/Transform .color_/int: - super x y w h transform - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_bitmap bx by (color_ & 1) orientation bytes_ 0 w canvas.plane_0_ canvas.width_ false - bitmap_draw_bitmap bx by ((color_ & 2) >> 1) orientation bytes_ 0 w canvas.plane_1_ canvas.width_ false - -/** -A two color bitmap texture where foreground and background pixels in the - texture are both drawn. -Initially all pixels have the background color. -Use $set_pixel to paint with the foreground, and $clear_pixel to paint with - the background. -*/ -class TwoBitOpaqueBitmapTexture_ extends TwoBitBitmapTexture_: - background_color_ := 0 - - constructor x/int y/int w/int h/int transform/Transform foreground_color/int .background_color_: - super x y w h transform foreground_color - - write2_ canvas/Canvas_: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - x := x2 - canvas.x_offset_ - y := y2 - canvas.y_offset_ - bitmap_rectangle x y (background_color_ & 1) w2 h2 canvas.plane_0_ canvas.width_ - bitmap_rectangle x y ((background_color_ & 2) >> 1) w2 h2 canvas.plane_1_ canvas.width_ - super canvas // Draw foreground. - -// A texture backed by a P4 (binary two-level) PBM file. The white areas -// (zeros) are rendered transparent and the black areas (ones) are rendered in -// an arbitrary color. -class PbmTexture_ extends BitmapTexture_: - width_ := 0 - height_ := 0 - color_ := 0 - bytes_ := ? - - // The byte array passed in must be a valid binary-mode (P4) PBM file. - // If $bytes is a literal containing constants then it is used directly - // from flash. However if the pixel drawing methods on this are used then - // $bytes is moved to RAM and modified. This could cause an out-of-memory - // on very large PBM files. - constructor x/int y/int transform/Transform .color_/int bytes/ByteArray: - parser := PbmParser_ bytes - parser.parse_ - bytes_ = bytes[parser.image_data_offset..] - super.no_allocate_ x y parser.width parser.height transform - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_bitmap bx by (color_ & 1) orientation bytes_ 0 w canvas.plane_0_ canvas.width_ false - bitmap_draw_bitmap bx by ((color_ & 2) >> 1) orientation bytes_ 0 w canvas.plane_1_ canvas.width_ false - -class TwoBitBarCodeEan13_ extends BarCodeEan13_: - black_ := 0 - white_ := 0 - - constructor code/string x/int y/int transform/Transform .black_/int .white_/int: - super code x y transform - - white_square_ x y w h canvas/Canvas_: - bitmap_rectangle x y (white_ & 1) w h canvas.plane_0_ canvas.width_ - bitmap_rectangle x y ((white_ & 2) >> 1) w h canvas.plane_1_ canvas.width_ - - digit_ digit x y canvas orientation -> none: - if digit == "": return - bitmap_draw_text x y (black_ & 1) orientation digit sans10_ canvas.plane_0_ canvas.width_ - bitmap_draw_text x y ((black_ & 2) >> 1) orientation digit sans10_ canvas.plane_1_ canvas.width_ - - block_ x y width height canvas/Canvas_: - bitmap_rectangle x y (black_ & 1) width height canvas.plane_0_ canvas.width_ - bitmap_rectangle x y ((black_ & 2) >> 1) width height canvas.plane_1_ canvas.width_ - -/** -A rectangular window with a fixed width colored border. -The border is subtracted from the visible area inside the window. -*/ -class TwoBitSimpleWindow_ extends SimpleWindow_: - background_color := ? - border_color := ? - - constructor x y w h transform border_width .border_color .background_color: - super x y w h transform border_width - - draw_frame canvas/Canvas_: - bitmap_zap canvas.plane_0_ (border_color & 1) - bitmap_zap canvas.plane_1_ (border_color & 2) >> 1 - - draw_background canvas/Canvas_: - bitmap_zap canvas.plane_0_ (background_color & 1) - bitmap_zap canvas.plane_1_ (background_color & 2) >> 1 - - make_alpha_map_ canvas/Canvas_: - return ByteArray (canvas.width_ * canvas.height_) >> 3 - - make_opaque_ x y w h map map_width: - // Paint the border mask with 1's. - bitmap_rectangle x y 1 w h map map_width - -class TwoBitRoundedCornerWindow_ extends RoundedCornerWindow_: - background_color := ? - - constructor x y w h transform corner_radius .background_color: - super x y w h transform corner_radius - - make_alpha_map_ canvas padding: - return ByteArray (canvas.width_ + padding) * ((canvas.height_ + padding + 7) >> 3) - - make_opaque_ x y w h map map_width --frame/bool: - assert: not frame - bitmap_rectangle x y 1 w h map map_width - - set_opacity_ x y opacity map map_width --frame/bool: - assert: not frame - if 0 <= x < map_width: - y_offset := (y >> 3) * map_width - if 0 <= y_offset < map.size: - if opacity < 128: - map[x + y_offset] &= ~(1 << (y & 7)) - else: - map[x + y_offset] |= 1 << (y & 7) - - draw_background canvas/Canvas_: - bitmap_zap canvas.plane_0_ (background_color & 1) - bitmap_zap canvas.plane_1_ (background_color & 2) >> 1 - - draw_frame canvas/Canvas_: - throw "UNREACHABLE" diff --git a/src/two_color.toit b/src/two_color.toit index b57d6f7..506da6c 100644 --- a/src/two_color.toit +++ b/src/two_color.toit @@ -13,7 +13,6 @@ import font show Font import icons show Icon import .common import .pixel_display show TwoColorPixelDisplay // For the doc comment. -import .texture WHITE ::= 0 BLACK ::= 1 @@ -43,7 +42,7 @@ class Canvas_ extends Canvas: return (pixels_[idx] & bit) == 0 ? 0 : 1 /** - Creates a blank texture with the same dimensions as this one. + Creates a blank canvas with the same dimensions as this one. */ create_similar -> Canvas_: result := Canvas_ width_ height_ @@ -67,247 +66,3 @@ class Canvas_ extends Canvas: text x/int y/int --text/string --color/int --font/Font --orientation/int=ORIENTATION_0: transform.xyo x y orientation: | x2 y2 o2 | bitmap_draw_text x2 y2 color o2 text font pixels_ width_ - -class FilledRectangle extends FilledRectangle_: - color_ := ? - - constructor .color_ x/int y/int w/int h/int transform/Transform: - assert: color_ < 2 // Not transparent. - super x y w h transform - - /// A line from $x1,$y1 to $x2,$y2. The line must be horizontal or vertical. - constructor.line color x1/int y1/int x2/int y2/int transform/Transform: - return FilledRectangle_.line_ x1 y1 x2 y2: | x y w h | - FilledRectangle color x y w h transform - - translated_write_ x/int y/int w/int h/int canvas/Canvas_: - bitmap_rectangle x y color_ w h canvas.pixels_ canvas.width_ - -class TextTexture extends TextTexture_: - color_/int := 0 - - /** - The coordinates given here to the constructor (and move_to) are the bottom - left of the first letter in the string (for left alignment). Once the - string has been rotated and aligned, and overhanging letter shapes have - been taken into account, the top left of the bounding box (properties $x, - $y, inherited from $SizedTexture) reflects the actual top left position - in the coordinate system of the transform. In the coordinates of the - display the getters $display_x, $display_y, $display_w and $display_h - are available. - */ - constructor text_x/int text_y/int transform/Transform alignment/int text/string font/Font .color_: - assert: color_ < 2 // No transparent color. - super text_x text_y transform alignment text font - - color= new_color/int -> none: - if color_ == new_color: return - color_ = new_color - invalidate - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_text bx by color_ orientation string_ font_ canvas.pixels_ canvas.width_ - -class IconTexture extends TextTexture: - constructor icon_x/int icon_y/int transform/Transform alignment/int icon/Icon font/Font color/int: - super icon_x icon_y transform alignment icon.stringify font color - - icon= new_icon/Icon -> none: - text = new_icon.stringify - font = new_icon.font_ - -/** -A texture that contains an uncompressed 2-color image. Initially all pixels - are transparent, but pixels can be given the color with $set_pixel. -*/ -class BitmapTexture extends BitmapTexture_: - color_ := 0 - - constructor x/int y/int w/int h/int transform/Transform .color_/int: - super x y w h transform - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_bitmap bx by color_ orientation bytes_ 0 w canvas.pixels_ canvas.width_ false - -/** -A two color bitmap texture where foreground and background pixels in the - texture are both drawn. -Initially all pixels have the background color. -Use $set_pixel to paint with the foreground, and $clear_pixel to paint with - the background. -*/ -class OpaqueBitmapTexture extends BitmapTexture: - foreground_color_/int := ? - background_color_/int := ? - - constructor x/int y/int w/int h/int transform/Transform .foreground_color_/int=BLACK .background_color_/int=WHITE: - super x y w h transform foreground_color_ - - write2_ canvas/Canvas_: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - x := x2 - canvas.x_offset_ - y := y2 - canvas.y_offset_ - bitmap_rectangle x y background_color_ w2 h2 canvas.pixels_ canvas.width_ - super canvas // Draw foreground. - -class BarCodeEan13 extends BarCodeEan13_: - constructor code/string x/int y/int transform/Transform: - super code x y transform - - white_square_ x y w h canvas/Canvas_: - white ::= 0 - bitmap_rectangle x y white w h canvas.pixels_ canvas.width_ - - digit_ digit x y canvas orientation -> none: - if digit == "": return - black ::= 1 - bitmap_draw_text x y black orientation digit sans10_ canvas.pixels_ canvas.width_ - - block_ x y width height canvas/Canvas_: - black ::= 1 - bitmap_rectangle x y black width height canvas.pixels_ canvas.width_ - -/** -A rectangular window with a fixed width colored border. -The border is subtracted from the visible area inside the window. -*/ -class SimpleWindow extends SimpleWindow_: - background_color/int := ? - border_color/int := ? - - constructor x/int y/int w/int h/int transform/Transform border_width/int .border_color .background_color: - super x y w h transform border_width - - draw_frame canvas/Canvas_: - bitmap_zap canvas.pixels_ border_color - - draw_background canvas/Canvas_: - bitmap_zap canvas.pixels_ background_color - - make_alpha_map_ canvas/Canvas_: - return ByteArray (canvas.width_ * canvas.height_) >> 3 - - make_opaque_ x y w h map map_width: - // Paint the border mask with 1's. - bitmap_rectangle x y 1 w h map map_width - -class RoundedCornerWindow extends RoundedCornerWindow_: - background_color := ? - - constructor x/int y/int w/int h/int transform/Transform corner_radius/int .background_color: - super x y w h transform corner_radius - - make_alpha_map_ canvas padding: - return ByteArray (canvas.width_ + padding) * ((canvas.height_ + padding + 7) >> 3) - - make_opaque_ x y w h map map_width --frame/bool: - assert: not frame - bitmap_rectangle x y 1 w h map map_width - - set_opacity_ x y opacity map map_width --frame/bool: - assert: not frame - if 0 <= x < map_width: - y_offset := (y >> 3) * map_width - if 0 <= y_offset < map.size: - if opacity < 128: - map[x + y_offset] &= ~(1 << (y & 7)) - else: - map[x + y_offset] |= 1 << (y & 7) - - draw_background canvas/Canvas_: - bitmap_zap canvas.pixels_ background_color - - draw_frame canvas/Canvas_: - throw "UNREACHABLE" - -// Pbm documentation: http://netpbm.sourceforge.net/doc/pbm.html -MOST_SIGNIFICANT_BIT_OF_BYTE_MASK_ ::= 0b10000000 -draw_pbm texture/BitmapTexture pbm/Pbm --scale=1: - // TODO(Lau): Check whether the texture is large enough for the bit map. - pbm.height.repeat: | y | - row := pbm.row y - x := 0 - row.size.repeat: - byte := row[it] - number_of_bits := 8 - if it == row.size - 1 and pbm.width % 8 != 0: number_of_bits = pbm.width % 8 - number_of_bits.repeat: - color := (byte & MOST_SIGNIFICANT_BIT_OF_BYTE_MASK_) == 0 ? 0 : 1 - scale_and_set_ color x++ y scale texture - byte = (byte << 1) & 0xFF - -scale_and_set_ color x y scale texture: - scale.repeat: - texture_x := scale * x + it - scale.repeat: - texture_y := scale * y + it - if color == 1: - texture.set_pixel texture_x texture_y - else: - texture.clear_pixel texture_x texture_y - -class Pbm: - width_ := 0 - height_ := 0 - rows_ := null - parser_ := ? - - constructor.parse bytes: - parser_ = PbmParser_ bytes - parser_.parse_ - width_ = parser_.width - height_ = parser_.height - - height: return height_ - - width: return width_ - - row index/int: - if not rows_: - rows_ = parser_.rows - if not 0 <= index < rows_.size: throw "OUT OF BOUNDS" - return rows_[index] - -// A texture backed by a P4 (binary two-level) PBM file. The white areas -// (zeros) are rendered transparent and the black areas (ones) are rendered in -// an arbitrary color. This is normally more efficient than the Pbm class, but -// it cannot scale the image. -class PbmTexture extends BitmapTexture_: - width_ := 0 - height_ := 0 - color_ := 0 - bytes_ := ? - - // The byte array passed in must be a valid binary-mode (P4) PBM file. - // If $bytes is a constant literal then it is used directly from - // flash. However if the pixel drawing methods on this are used then - // $bytes is moved to RAM and modified. This could cause an out-of-memory - // on very large PBM files. - constructor x/int y/int transform/Transform .color_/int bytes/ByteArray: - parser := PbmParser_ bytes - parser.parse_ - bytes_ = bytes[parser.image_data_offset..] - super.no_allocate_ x y parser.width parser.height transform - - draw_ bx by orientation canvas/Canvas_: - bitmap_draw_bitmap bx by color_ orientation bytes_ 0 w canvas.pixels_ canvas.width_ false - -// A texture backed by a P4 (binary two-level) PBM file. This is normally more -// efficient than the Pbm class, but it cannot scale the image. -class OpaquePbmTexture extends PbmTexture: - // The byte array passed in must be a valid binary-mode (P4) PBM file. - // If $bytes is a constant literal then it is used directly from - // flash. However if the pixel drawing methods on this are used then - // $bytes is moved to RAM and modified. This could cause an out-of-memory - // on very large PBM files. - constructor x/int y/int transform/Transform bytes/ByteArray: - foreground ::= 1 - super x y transform foreground bytes - - write2_ canvas/Canvas_: - transform_.xywh x_ y_ w_ h_: | x2 y2 w2 h2 | - x := x2 - canvas.x_offset_ - y := y2 - canvas.y_offset_ - background ::= 0 - bitmap_rectangle x y background w2 h2 canvas.pixels_ canvas.width_ - super canvas diff --git a/tests/4_gray_visualized.toit b/tests/4_gray_visualized.toit deleted file mode 100644 index 98252bc..0000000 --- a/tests/4_gray_visualized.toit +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2023 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the TESTS_LICENSE file. - -import expect show * -import font show Font -import pixel_display show * -import pixel_display.four_gray show * -import .png_visualizer - -main args: - if args.size != 1: - print "Usage: script.toit png-basename" - exit 1 - driver := FourGrayPngVisualizer 160 64 args[0] --outline=WHITE - display := FourGrayPixelDisplay driver - display.background = BLACK - - sans10 := Font.get "sans10" - - ctx := display.context --landscape --color=LIGHT_GRAY --font=sans10 - - display.filled_rectangle (ctx.with --color=DARK_GRAY) 10 20 30 40 - display.text ctx 50 20 "Testing" - middle_line := display.text ctx 50 40 "the display" - display.draw - display.text ctx 50 60 "for the win" - - display.draw - - middle_line.move_to 60 40 - display.draw - - middle_line.text = "the DISPLAY" - display.draw - - driver.write_png diff --git a/tests/bw_visualized.toit b/tests/bw_visualized.toit deleted file mode 100644 index bb44317..0000000 --- a/tests/bw_visualized.toit +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2023 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the TESTS_LICENSE file. - -import expect show * -import font show Font -import pixel_display show * -import pixel_display.two_color show * -import .png_visualizer - -main args: - if args.size != 1: - print "Usage: script.toit png-basename" - exit 1 - driver := TwoColorPngVisualizer 160 64 args[0] --outline=WHITE - display := TwoColorPixelDisplay driver - display.background = BLACK - - sans10 := Font.get "sans10" - - ctx := display.context --landscape --color=WHITE --font=sans10 - - display.filled_rectangle ctx 10 20 30 40 - display.text ctx 50 20 "Testing" - middle_line := display.text ctx 50 40 "the display" - display.draw - display.text ctx 50 60 "for the win" - - display.draw - - middle_line.move_to 60 40 - display.draw - - middle_line.text = "the DISPLAY" - display.draw - - driver.write_png diff --git a/tests/drop_shadow_test.toit b/tests/drop_shadow_test.toit deleted file mode 100644 index 2ecddb8..0000000 --- a/tests/drop_shadow_test.toit +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2020 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the TESTS_LICENSE file. - -import bitmap show * -import pixel_display.texture show * -import pixel_display.true_color -import pixel_display.gray_scale - -main: - error := catch: - ba := ByteArray 25 - bytemap_blur ba 5 2 - if error == "UNIMPLEMENTED": - // Not all builds have the byte display primitives for true color displays. - return - else if error: - throw error - - true_color_test - gray_scale_test - -true_color_test -> none: - canvas := true_color.Canvas_ 64 48 - tr := Transform.identity - window := true_color.DropShadowWindow 30 20 140 100 tr (true_color.get_rgb 255 255 153) --corner_radius=7 --blur_radius=4 --drop_distance_x=-10 --drop_distance_y=-10 - canvas.set_all_pixels (true_color.get_rgb 23 200 230) - window.write canvas - 48.repeat: | y | - line := "" - 64.repeat: | x | - pixel := canvas.get_pixel_ x y - line += "$(%3d true_color.red_component pixel) $(%3d true_color.green_component pixel) $(%3d true_color.blue_component pixel) "; - print line - -gray_scale_test -> none: - canvas := gray_scale.Canvas_ 64 48 - tr := Transform.identity - window := gray_scale.DropShadowWindow 30 20 140 100 tr gray_scale.DARK_GRAY --corner_radius=7 --blur_radius=4 --drop_distance_x=-10 --drop_distance_y=-10 - canvas.set_all_pixels gray_scale.LIGHT_GRAY - window.write canvas - 48.repeat: | y | - line := "" - 64.repeat: | x | - pixel := canvas.get_pixel_ x y - line += "$(%3d pixel) "; - print line diff --git a/tests/drop_shadow_visualized.toit b/tests/drop_shadow_visualized.toit deleted file mode 100644 index dc938f8..0000000 --- a/tests/drop_shadow_visualized.toit +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2020 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the TESTS_LICENSE file. - -import bitmap show * -import font show Font -import pixel_display show * -import pixel_display.texture show * -import pixel_display.true_color show * -import .png_visualizer - -main args: - sans10 := Font.get "sans10" - - if args.size != 1: - print "Usage: script.toit png-basename" - exit 1 - driver := TrueColorPngVisualizer 240 160 args[0] --outline=0x101010 - display := TrueColorPixelDisplay driver - display.background = get_rgb 120 160 200 - - transform := display.landscape - - win := DropShadowWindow 30 30 180 100 transform 0xe0e0ff --corner_radius=10 --blur_radius=6 --drop_distance_x=10 --drop_distance_y=10 --shadow_opacity_percent=30 - display.add win - - text := TextTexture 90 55 transform TEXT_TEXTURE_ALIGN_CENTER "Hello, World!" sans10 0x101010 - win.add text - - display.draw - - text.move_to 100 65 - - display.draw - - text.move_to 15 37 - - display.draw - - driver.write_png diff --git a/tests/gold/4_gray_visualized.toit.png b/tests/gold/4_gray_visualized.toit.png deleted file mode 100644 index ed7508b6789f150d7ffdeca3e1f27d9fd6ccc383..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1259 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 diff --git a/tests/gold/mixed_text_rotated_visualized.toit.png b/tests/gold/mixed_text_rotated_visualized.toit.png index da4c528385e91cef2dbaac8430ed2745d8f052df..f38c14aeb1c78ea1e922ddcd3d9ebf6e52105377 100644 GIT binary patch literal 2552 zcmY*b3sjQX7G_Kv-vg*QS^Y~}u^OMaF0s{7e(S8mB3sTYr6E(xNqF6eIc z`_QgM8RzU^ef-+^$_dw~?svVxc#owLRnVMD-60#2)fvsOFuZdwtIyfhONLQ4iBf_J zi4Ir4tuoIH30KacQjQI(>y-qJdA_#ms@9U-T-DEPsqcB)In77Oxk0+$N{gsa7Ow<& zcnGiaei+6&|FNM)B6Wl0v+G($5#UMkc)kkJLyB!06Eb~t;kA;_;lcXY@}$ zrl^xKS#2?g61zh?r;_Ai7^%nwwa$}(P%GH!$UkKR{bvUy3{h3u;Aow+K9Xq)Sm zQ`b|6cjo0!OQ%Wf%NMVS&;~E2l0GfSbfJuY&3vSgB9@lz)e2YwVJfACoXT88jLZ$8 zD#>1Sb+%W|z}&|G?u%gj!QXz>df%6#u8%VBRlJ_+6*3nG<7TREr@7J8Ars`Ma4}K5 zTN6?#AY_J|IM?5w9BT0n<#haKiLDdXGBXMtI&}Bjj$H- zw=8zMD+AZ@!Lg`}xo(2|#6dD8pbgC-a%#pF1GpQoWF&ZGq`=KGt`?00Q^6OCcN=`o z0{DU)wVVPl^=u)JDleJA;&>?ieI68m)wWZ@yg{GhV7QhIXbDsG3%2!J2X2;x`SZRm z_(xBI%L3k0vgdadM40szlq(7HXED%2cF1z#W0e4+JGcN==Jk{ zGMgX^R4kQk+Ss_q8x`x|g4_XO!zhjNC)tlPL^3l=4F9!+DdG$iQJD9vr@k0F=16Xd zIGCJc8!2kFUysF$c%(Bq{DE12heuun+$2Z%4>0*TJAE8S>07mhLTf%Vy=DP_>i=s^ zF(go1%|Mp?Y9lp{IVHIQt+Hd3L!yk5*^SR77`KT!r0@E1a&)X%Dqwl{AOr_BiXbe5 z=ngi*@c$JPCgQy2fBP|P%oK}=YP@||p6^#qtvLFhYvxmusysqJLs(c^%!0YYKp z4?&r7RBOa2Oit?p#1yf~HnIpibY(@b0AEfqRM% zzCkusk~7jmN*tF}ZrRW$i{J;<*BJ`wD z?nCbc^b-n=qiPO)e$x8AMKz`Wn$U;c@CP{XPl;ynL*GUFjqabUxRKubHzH}u(D6OYCW`mFVI5?ATdc`aQH+>J1T#)i~blasA61Op^k&J43Qyo38&T5n*! zR5?OF^>IV2d1kTmg(fRY3ukNNM%M{#Kwq3m=TG{`%}^H&Zko;@_CQLlix-uQspP?o zCV<`i?rq7CGw^cmP03acJ!(xi_IzY^aeQ^jKiew;uHCQaD+%~EFXMtd;wEUljOaeg z;%6_Klwo=woG=~@6Kv?bkMC}mq5ay;+ zZJo?_3`SnUxFzo3nyZ}_U}fOTYA2ChRyfx$s}$bIB(Fh&9Z-Z*D?QrFs}1Br5g+ZY zIc}M8zOORY#C=+cvR)F<86=#VRWL)ZScoGe>S~yy@`P6Oo#)I-@J61-#+1 zR7(6Wa_FHA@80UuKfhsLTLjKr12Opd9Z~7<{m7NNj^Xbe!fE2q7n6`#VWXA}>5MM2 zKbs!78hHP9-bY-t=Aj}3T8%jYaqzcyO;j8ZOp)X`Um_jrYd4(wKiN| zdMSL}RFS(&Rv_l?`=e%1K;Rpcy7R}@7Tx0lu^e)xRc3c5D76dZP)zt*>q9Qr2jI!W zDC2-W%UY_fIl2Rl-VzY_K>yU*vJu2i69@yL%+M4^oLSoWPrimpQ|hwSo1N@! z8wBgUO1Iy-k@K7{LphFx?>mp_40`3cr?zc>6HJW!+zaV$=Aq5;nBbpzcSF~Gfg-P} f_oVPwY|t&YwypXlY9D(q^)v3jJrJnG9j5*VMj+t# literal 4408 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 diff --git a/tests/gold/mixed_text_visualized.toit.png b/tests/gold/mixed_text_visualized.toit.png index c8659cae11d405c6a79086a5adf2770d9a3ef18a..98303df6d1d8005a9e2f637fdfda776fbdf7caa6 100644 GIT binary patch literal 2900 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 O4h~1I zyG;Qqn&9c;7*Y}U=F~<`1_ut7gZeKI{Hy-$ap3@8+NRc8X`pJ;r)$3azqz$}az~>< z!=W;TB=&5^F3khp2bfYe=={FX?YhD|(ckRRJ(UHE#(*E92ft$16UGL}S z|G#|>1ym+)e(`E~!mqE_Lqs%#mb}QFyK{cH$o23JF7tg$rsOSN@#xpCMFPH}i~foN zr9CDpxpuYX>xIwUoH%{*nU~jZ-tQ`VzE~i(!p&AxlZ(q$ZJ#R07_d4KUgJywMv$+* z8Ag>UC@Cpr{eHbR_RE|~jSjxm8I${SCxg`aYc8EFpj%St1Tq__S!7CglUm?T4T~p0 zzTEM`J^$n<>Z`ke0LZL{r9L%4=C{`!Kp?g?%>gXqa^+-Q>~5gQiF2HSAOKQ&AR|%? z$jog&QyA_#zsa{7WZnALXJYk#mj+%rZPyI35bWBoi$b66`KEfm$U7bAP^CrvWe;Y6 j%t)N@H@JE|vks7H(G~F2{?Bnvc96KItDnm{r-UW|so%cw literal 770 zcmeAS@N?(olHy`uVBq!ia0y~yU|aygEX+U=If->yK#D)WC&cyt|NlTH1Bm$V;NTGN z^>Pyf1JgQB7srr_xHo4O@-i@Ruo$xZK7WRRrTOo*xT2{*5g=&TS9|Lhe|6b}CIcRp z0Fx{|g?k62J!~Bg-uv&9SK@K7WT9--lfS9l!fo#6XSWnB4ZhVlTR&uZ-ed9iZcZ&6 zf=XYUTh;}x{vdJhZuCOMD1le!`k#HC-In2)ah5s%dH8hAQ?uslfBAW!aB}N6eU}aa zMX)9)<7(yc6=ri6-72fNZgcOmQ)=SEOOY#nwADCsf>fRQJ#(J%|NZaU@9+yMp=%Xf zr9EBuOGBq}s4Qm|p@;GLns!L|YDb(g10 z78DeA`fs|oKF0Oeueoz~-<+qyDfr3>>@2q4HB*me?w`0dWMQvoiYP5F{$=q`MG@h2 z4zTM$#D^w#4K8q4Y>HpEZpz(j51w%RHEs`x_2*LMfY|yaYsxu~nSYB8zCX^b$_)_% zI|odp?7w5!Y8KS;sPz%dnIY5No*UWfrRQA+X3nH5lNx>;uAMsd!;W(aa~^P3**Z3{a0;npXi3*e zxva36bDtd=8;_@LH~SOxtZwJO@87w1`Zpq53Q8Fee;;DM+WI%*zS(nUurVGJ6oBU0 qzCFOm2y{c!f`9AtIyRVq9mK%5O{gVxm)^4{Ag-sYpUXO@geCyj5+>3B diff --git a/tests/gold/text_texture_visualized.toit.png b/tests/gold/text_texture_visualized.toit.png deleted file mode 100644 index 991c77c71db0f5780e4f5b42b2fff293407f0f99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2242 zcmX|D30TtE7RM#cC>3oXC9zy{9EEWzQv)l_T#y#G%q{3mPbp1P$59cJR4j|id1a|t zO>UtQsY&I=h|7HW+Dy$tA<$70K?)RE=Wk!{-S2+)-tXLV?stCY{La0%j50XwbGq@0;U}7-SV^==&_gbaKxjT) zmX{v-@e5ssmANrizH8dbb~Ze zBTrZ?E-R@q3yud%)ADV?0rBX~rdb$Am<*3gRvT;!O?xKWqMW7Kk0OjOAuawiUqv}j zFUXqIRMs=Lc5t4ab&T1hp+A7#BlXkHMWJ zb#bQEzO&uOeH)X79x9Rdt*Bm@s526RFRdz2*_h+uj+xJna#xwS2u5dNr?=S0Dg9a` z={Rr8ZnN1&!^pMuODAw|U_KDSnj&^^^}oM_Ljok#Mlb|&@dO|LXsP6VRNLZuehv2L zWf#iu{hQ5&pLexYZ8W$|gE2qJijgR`R>(dFJ&&m{^AG)s`cQ~G`d)DmSZ-l&cAm};OQ z`CP3wl|WTAY6M~Hr$~m-n5{-0P_;*zl-=Oqi?gVn@#<>=9B6d8ODZLgwLcArfrvgT z2aXxPL-%(*$vK_wn#1;t`tOW#AW_w;!HZ}PruW_dMTHs%R2yjUo;?NH)Ixf}`(J-q z(eqTc){rV4_U_d|mp@ScNO8?(Z(xl_Vnp7r(aqAfZZ8asB-bJx-Ll!Igz&F&&x;j? zWUM85v$|3ShUo6~b@}nX%BWRAxxQ4W(kiJ(7lE#a@1-(j>sln18bRGmox+D~gEV!! zP*7dw3&^*~%&YsgsWzZ&wj2dh7w>p^8zQ3wS62`(YXm+Z8O1ucTwyD=#lNvDVwKUG zNu|a-?Mg;%-3Fs%loMxV#D^r9WI^^$PCbWMAj&TpuH?(eqnx$2_Wzl5zNitjtO8^r zI2QdPHt%QHEPTc#_K>v|q31EoHXo`;Drn!XI3w7|5(rt>bLAR*#UyW2YBCB)6s?|; zJZ!BN^gk7@rnF$ou=dNJRRhA(@UQ(8lD<1o}x+MPPi(g zp@Ab96bC(7%PYz9!ttkGu>OkVqVq^(ZlKqVq%WVeQp}3swdMDC{?uF^SE->PcT8FolqiCDz zP0CcvWiv7S1#;xW?{^9eR`(mUBBPMfr37yGnQS4cT?M7*sF>HdhjgCH9mkZV2Qj*+ zbZF(JQB+XVVyDSu?@Cm0o3t%NUE-(acCpm4w0?a>F4>LPUwHdS<80o{JdA^3r{>GI znD(PW8U>hb5pB_X8$X=lhBVqTp}ah(`9-}SQ6WBS-Afb-%onCK<>*2pH)i}4JLfgp zt+{YZ`XjL>x?WF%pW4rclcEQ+5;gdc+;79~AJ&!x0)X+?%Gtk7fL&s%mrx^j!Qmef_deH#A#_-3`woOXFTz2TT=)Y7xnachjx6X*#pTK5%Y6yMY=7*i&7 zMmHtXwQ;7-qijFUUso6XAtuE(M($&sckkndQY95eYxiGi064Uifep|5Sik#*kN?#C z-}7~>@v&Pq?#z7h*Jryt29eCUkqbEACNoRg;<*JyRTLg!`?3pmdV}F6gBT?5lb*R9 z+%a@6Zrjf7Ov%V@ze=J@F!v{De4TLPtFgq+uDJO4KCS>x$f^<{j0sM+7yZqLk~aU3 zog(DR#dXyHGe8{(kGF?@ zr}r-ivd((0za`>pinKLoW^PU%z?y#P980(O`y86bOIuZ@HmPTwO zT=yYxbr%=Iej32x_p)D1VEf8>MXW@0hqjfZW?)a~m>kUmkEDVOR$`c2`%VQynzm%Q zVMpAOx6^a+21C*m@M4OcTs#C#s>6MnYOyKQSJ|4w!1 none: - w := right - left - (bottom - top).repeat: | iy | - w.repeat: | ix | - buffer[left + ix + (top + iy) * width] = pixels[ix + iy * w] - - brightness_at x y: - return buffer[x + y * width] - -main: - driver := TestDriver - display := GrayScalePixelDisplay driver - display.background = 0 - - ctx := display.context --landscape --color=64 - ctx2 := display.context --landscape=false --color=64 - - display.filled_rectangle ctx 10 20 30 40 - - image := PixmapTexture 10 20 40 24 ctx.transform - display.add image - image2 := PixmapTexture 10 20 40 24 ctx2.transform - display.add image2 - - 10.repeat: - x := random 1 39 - y := random 2 22 - c := random 256 - image.set_pixel (x + 1) y c - image.set_pixel x (y - 2) c - image.set_pixel x (y - 1) c - image.set_pixel (x - 1) y c - image.set_pixel x (y + 1) c - image.set_pixel x (y + 1) c - image2.set_pixel (x + 1) y c - image2.set_pixel x (y - 2) c - image2.set_pixel x (y - 1) c - image2.set_pixel (x - 1) y c - image2.set_pixel x (y + 1) c - image2.set_pixel x (y + 1) c - - for x := 11; x < 50; x++: - display.draw - p driver - - sleep --ms=40 - - image.move_to x 20 - image2.move_to x 20 - -p driver: - driver.height.repeat: | y | - line := "" - driver.width.repeat: | x | - br := driver.brightness_at x y - if br == 64: - line += "-" - else if br < 64: - line += " " - else if br > 192: - line += "@" - else: - line += "#" - print line diff --git a/tests/indexed.toit b/tests/indexed.toit deleted file mode 100644 index 8824b74..0000000 --- a/tests/indexed.toit +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) 2021 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the TESTS_LICENSE file. - -import expect show * -import font show Font -import pixel_display show * -import pixel_display.true_color show * - -class TestDriver extends AbstractDriver: - buffer := ByteArray 3 * 64 * 128 - - width ::= 128 - height ::= 64 - flags ::= FLAG_TRUE_COLOR | FLAG_PARTIAL_UPDATES - draw_true_color left/int top/int right/int bottom/int r/ByteArray g/ByteArray b/ByteArray -> none: - w := right - left - (bottom - top).repeat: | iy | - w.repeat: | ix | - buffer[0 + 3 * (left + ix + (top + iy) * width)] = r[ix + iy * w] - buffer[1 + 3 * (left + ix + (top + iy) * width)] = g[ix + iy * w] - buffer[2 + 3 * (left + ix + (top + iy) * width)] = b[ix + iy * w] - - red_at x y: - return buffer[0 + 3 * (x + y * width)] - - green_at x y: - return buffer[1 + 3 * (x + y * width)] - - blue_at x y: - return buffer[2 + 3 * (x + y * width)] - - reddish x y: - return (red_at x y) > (green_at x y) * 2 and (red_at x y) > (blue_at x y) * 2 - - yellowish x y: - return (red_at x y) > (blue_at x y) * 2 and (green_at x y) > (blue_at x y) * 2 - - pinkish x y: - return (red_at x y) > 224 and 128 <= (green_at x y) <= 200 and 128 <= (blue_at x y) <= 200 - -main: - driver := TestDriver - display := TrueColorPixelDisplay driver - display.background = get_rgb 0 1 2 - - ctx := display.context --landscape --color=(get_rgb 255 120 0) - ctx2 := display.context --landscape=false --color=(get_rgb 255 120 0) - - display.filled_rectangle ctx 10 20 30 40 - - image := IndexedPixmapTexture 10 20 40 24 ctx.transform - display.add image - image2 := IndexedPixmapTexture 10 20 40 24 ctx2.transform - display.add image2 - red := image.allocate_color 255 0 0 - image2.allocate_color 255 0 0 - pink := image.allocate_color 255 192 192 - image2.allocate_color 255 192 192 - yellow := image.allocate_color 255 255 0 - image2.allocate_color 255 255 0 - - 10.repeat: - x := random 1 39 - y := random 2 22 - c := random 1 4 - image.set_pixel (x + 1) y c - image.set_pixel x (y - 2) c - image.set_pixel x (y - 1) c - image.set_pixel (x - 1) y c - image.set_pixel x (y + 1) c - image.set_pixel x (y + 1) c - image2.set_pixel (x + 1) y c - image2.set_pixel x (y - 2) c - image2.set_pixel x (y - 1) c - image2.set_pixel (x - 1) y c - image2.set_pixel x (y + 1) c - image2.set_pixel x (y + 1) c - - for x := 11; x < 50; x++: - display.draw - p driver - - sleep --ms=30 - - image.move_to x 20 - image2.move_to x 20 - -p driver: - driver.height.repeat: | y | - line := "" - driver.width.repeat: | x | - if driver.reddish x y: - line += "r" - else if driver.yellowish x y: - line += "y" - else if driver.pinkish x y: - line += "p" - else: - line += " " - print line diff --git a/tests/mixed_text_rotated_visualized.toit b/tests/mixed_text_rotated_visualized.toit index fa06fb4..1deb5af 100644 --- a/tests/mixed_text_rotated_visualized.toit +++ b/tests/mixed_text_rotated_visualized.toit @@ -2,8 +2,7 @@ // Use of this source code is governed by a Zero-Clause BSD license that can // be found in the TESTS_LICENSE file. -// Mixes texture-based and element-based text on the same display to test -// that they are positioned in the same way and the redraw boxes are right. +// Element-based text is rotated and positioned. // Uses a rotated frame (portrait mode). import bitmap show * @@ -11,7 +10,7 @@ import expect show * import font show * import pixel_display show * import pixel_display.element show * -import pixel_display.texture show * +import pixel_display.style show * import .png_visualizer main args: @@ -24,11 +23,6 @@ main args: sans10 := Font.get "sans10" - ctx := display.context --landscape=false --color=SEVERAL_ORANGE --font=sans10 - - // Texture-based text. - texture_text := display.text (ctx.with --color=SEVERAL_BLUE) 10 20 "Texture 1" - texture_text_2 := display.text (ctx.with --color=SEVERAL_BLUE) 10 65 "Texture 2" // Element-based text. element_text := Label --x=10 --y=30 --color=SEVERAL_GREEN --font=sans10 --label="Element 1" element_text_2 := Label --x=10 --y=110 --color=SEVERAL_GREEN --font=sans10 --label="Element 2" @@ -36,17 +30,15 @@ main args: display.add element_text_2 display.draw - texture_text.move_to 20 20 element_text.move_to 20 30 display.draw - texture_text_2.move_to 20 65 display.draw element_text_2.move_to 20 110 display.draw - element_text.alignment = TEXT_TEXTURE_ALIGN_RIGHT + element_text.alignment = ALIGN_RIGHT display.draw element_text.orientation = ORIENTATION_90 diff --git a/tests/mixed_text_visualized.toit b/tests/mixed_text_visualized.toit index cf8ef60..79e1f0f 100644 --- a/tests/mixed_text_visualized.toit +++ b/tests/mixed_text_visualized.toit @@ -2,15 +2,14 @@ // Use of this source code is governed by a Zero-Clause BSD license that can // be found in the TESTS_LICENSE file. -// Mixes texture-based and element-based text on the same display to test -// that they are positioned in the same way and the redraw boxes are right. +// Element-based text is rotated and positioned. import bitmap show * import expect show * import font show * import pixel_display show * import pixel_display.element show * -import pixel_display.texture show * +import pixel_display.style show * import .png_visualizer main args: @@ -23,11 +22,6 @@ main args: sans10 := Font.get "sans10" - ctx := display.context --landscape --color=SEVERAL_ORANGE --font=sans10 - - // Texture-based text. - texture_text := display.text (ctx.with --color=SEVERAL_BLUE) 30 20 "Texture" - texture_text_2 := display.text (ctx.with --color=SEVERAL_BLUE) 80 20 "Texture" // Element-based rectangles. element_text := Label --x=30 --y=30 --color=SEVERAL_ORANGE --label="joo%" --font=sans10 element_text_2 := Label --x=130 --y=20 --color=SEVERAL_ORANGE --label="joo%" --font=sans10 @@ -35,11 +29,9 @@ main args: display.add element_text_2 display.draw - texture_text.move_to 30 30 element_text.move_to 31 40 display.draw - texture_text_2.move_to 80 30 display.draw element_text_2.move_to 130 30 @@ -57,10 +49,10 @@ main args: element_text.orientation = ORIENTATION_0 display.draw - element_text.alignment = TEXT_TEXTURE_ALIGN_CENTER + element_text.alignment = ALIGN_CENTER display.draw - element_text.alignment = TEXT_TEXTURE_ALIGN_RIGHT + element_text.alignment = ALIGN_RIGHT display.draw element_text.orientation = ORIENTATION_90 diff --git a/tests/mixed_texture_rotated_visualized.toit b/tests/mixed_texture_rotated_visualized.toit index 52025a7..d210647 100644 --- a/tests/mixed_texture_rotated_visualized.toit +++ b/tests/mixed_texture_rotated_visualized.toit @@ -9,7 +9,6 @@ import expect show * import pixel_display show * import pixel_display.element show * -import pixel_display.texture show * import .png_visualizer main args: @@ -20,23 +19,16 @@ main args: display := SeveralColorPixelDisplay driver --inverted --portrait display.background = SEVERAL_DARK_GRAY - ctx := display.context --inverted --landscape=false --color=SEVERAL_ORANGE - - // Texture-based rectangle. - texture_rect := display.filled_rectangle (ctx.with --color=SEVERAL_BLUE) 10 20 30 20 - texture_rect_2 := display.filled_rectangle (ctx.with --color=SEVERAL_BLUE) 10 65 30 20 - // Slightly smaller element-based rectangle. + // Element-based rectangle. element_rect := Div --x=11 --y=21 --w=28 --h=18 --background=SEVERAL_BLACK element_rect_2 := Div --x=10 --y=110 --w=30 --h=20 --background=SEVERAL_GREEN display.add element_rect display.add element_rect_2 display.draw - texture_rect.move_to 20 20 element_rect.move_to 21 21 display.draw - texture_rect_2.move_to 20 65 display.draw element_rect_2.move_to 20 110 diff --git a/tests/pbm_test.toit b/tests/pbm_test.toit deleted file mode 100644 index 0cb3906..0000000 --- a/tests/pbm_test.toit +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (C) 2020 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the TESTS_LICENSE file. - -import expect show * -import pixel_display.two_color -import pixel_display.gray_scale -import pixel_display.texture show Transform PbmParser_ - -toit_logo := #[ - 'P', '4', '\n', '6', '2', ' ', '4', '0', '\n', - 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfc, - 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfc, - 0x1f, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xe0, - 0x01, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0xfe, 0x00, - 0x00, 0x0f, 0xff, 0xc0, 0x0f, 0xff, 0xc0, 0x00, - 0x00, 0x01, 0xff, 0xfc, 0xff, 0xfe, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3c, 0xf0, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x38, 0x70, 0x00, 0x00, 0x08, - 0xe0, 0x00, 0x00, 0x78, 0x78, 0x00, 0x00, 0x1c, - 0xf0, 0x00, 0x00, 0xf0, 0x3c, 0x00, 0x00, 0x3c, - 0x78, 0x00, 0x00, 0xe0, 0x1c, 0x00, 0x00, 0x78, - 0x3c, 0x00, 0x01, 0xe0, 0x1e, 0x00, 0x00, 0xf0, - 0x1e, 0x00, 0x03, 0xc0, 0x0f, 0x00, 0x01, 0xe0, - 0x0f, 0x00, 0x03, 0xc0, 0x0f, 0x00, 0x03, 0xc0, - 0x07, 0x00, 0x07, 0x80, 0x07, 0x80, 0x03, 0x80, - 0x07, 0x80, 0x07, 0x00, 0x03, 0x80, 0x07, 0x80, - 0x03, 0xc0, 0x0f, 0x00, 0x03, 0xc0, 0x0f, 0x00, - 0x01, 0xe0, 0x1e, 0x00, 0x01, 0xe0, 0x1e, 0x00, - 0x00, 0xf0, 0x3c, 0x00, 0x00, 0xf0, 0x3c, 0x00, - 0x00, 0x7c, 0x7c, 0x00, 0x00, 0xf8, 0xf8, 0x00, - 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x7f, 0xf0, 0x00, - 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x3f, 0xe0, 0x00, - 0x00, 0x07, 0xc0, 0x00, 0x00, 0x0f, 0x80, 0x00 - ] - -main: - test_parse_magic_number - test_parse_whitespace - test_parse_multiple_whitespace - test_parse_toit_logo - test_draw_on_pbm - -test_parse_magic_number: - incorrect_magic_number := ByteArray 2: ['O','4'][it] - expect_throw "INVALID PBM": (PbmParser_ incorrect_magic_number).parse_magic_number_ - - incorrect_magic_number = ByteArray 2: ['P','3'][it] - expect_throw "INVALID PBM": (PbmParser_ incorrect_magic_number).parse_magic_number_ - - correct_magic_number := ByteArray 2: ['P','4'][it] - parser := PbmParser_ correct_magic_number - expect_no_throw: parser.parse_magic_number_ - expect_equals 2 parser.next_ - -test_parse_whitespace char: - expect_no_throw: - parser := PbmParser_ (ByteArray 1: char) - parser.parse_whitespace_ - -test_parse_whitespace: - test_parse_whitespace '\t' - test_parse_whitespace '\v' - test_parse_whitespace ' ' - test_parse_whitespace '\n' - test_parse_whitespace '\r' - test_parse_whitespace '\f' - expect_throw "INVALID PBM": (PbmParser_ (ByteArray 1: 'A')).parse_whitespace_ - -test_parse_multiple_whitespace: - parser := PbmParser_ (ByteArray 6: ['\t', '\v', ' ', '\n', '\r', '\f'][it]) - expect_no_throw: parser.parse_multiple_whitespace_ - expect_equals 6 parser.next_ - - parser = PbmParser_ (ByteArray 6: ['\t', '\v', ' ', '\n', '\r', '\f'][it]) - expect_no_throw: parser.parse_multiple_whitespace_ --at_least_one - expect_equals 6 parser.next_ - - parser = PbmParser_ (ByteArray 1: ['\t'][it]) - expect_no_throw: parser.parse_multiple_whitespace_ --at_least_one - expect_equals 1 parser.next_ - - parser = PbmParser_ (ByteArray 7: ['\t', '\v', ' ', 'A', '\n', '\r', '\f'][it]) - expect_no_throw: parser.parse_multiple_whitespace_ - expect_equals 3 parser.next_ - - parser = PbmParser_ (ByteArray 6: ['A', '\v', ' ', '\n', '\r', '\f'][it]) - expect_no_throw: parser.parse_multiple_whitespace_ - expect_equals 0 parser.next_ - - parser = PbmParser_ (ByteArray 6: ['A', '\v', ' ', '\n', '\r', '\f'][it]) - expect_throw "INVALID PBM": parser.parse_multiple_whitespace_ --at_least_one - -test_parse_number: - parser := PbmParser_ (ByteArray 2: ['1', ' '][it]) - number := parser.parse_number_ - expect_equals 1 number - - parser = PbmParser_ (ByteArray 3: ['1', '2', ' '][it]) - number = parser.parse_number_ - expect_equals 12 number - - parser = PbmParser_ (ByteArray 3: ['1', 'A', ' '][it]) - expect_throw "INVALID PBM": number = parser.parse_number_ - - parser = PbmParser_ (ByteArray 2: ['1', '2'][it]) - expect_throw "INVALID PBM": number = parser.parse_number_ - -test_parse_toit_logo: - logo_width ::= 62 - logo_height ::= 40 - pbm := two_color.Pbm.parse toit_logo - expect_equals logo_width pbm.width - expect_equals logo_height pbm.height - expect_bytes_equal - pbm.row 0 - ByteArray 8: [0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38][it] - expect_bytes_equal - pbm.row logo_height - 1 - ByteArray 8: [0x00, 0x07, 0xc0, 0x00, 0x00, 0x0f, 0x80, 0x00][it] - -test_draw_on_pbm: - texture := two_color.PbmTexture 0 0 Transform.identity 1 toit_logo - texture.set_all_pixels - - // It still parses with all pixels overwritten. - texture2 := two_color.PbmTexture 0 0 Transform.identity 1 toit_logo - - toit_logo[10..].do: - expect_equals 0xff it - - texture.h.repeat: - texture.clear_pixel 0 it - - toit_logo[10..].do: - expect - it == 0xff or it == 0x7f - - expect_throw "OUT_OF_RANGE": texture.clear_pixel -1 5 - expect_throw "OUT_OF_RANGE": texture.clear_pixel 5 -1 - expect_throw "OUT_OF_RANGE": texture.set_pixel 62 5 - expect_throw "OUT_OF_RANGE": texture.set_pixel 5 40 - - // We set all pixels including the ones that were on the right hand side and - // were only there because the backing was rounded up to the next multiple of - // 8. - texture.set_all_pixels - - // Clear all the visible pixels in the 62x40 area. - 62.repeat: | x | - 40.repeat: | y | - texture.clear_pixel x y - - canvas := two_color.Canvas_ 128 128 - - // Draw on a two color canvas. - texture.write canvas - - // Check that the rounded up pixels off the edge of the PBM are not drawn. - canvas.pixels_.do: expect_equals 0 it - - // The same for a byte-oriented texture. - texture_gray := gray_scale.PbmTexture 0 0 Transform.identity 255 toit_logo - canvas_gray := gray_scale.Canvas_ 128 128 - texture_gray.write canvas_gray - canvas_gray.pixels_.do: expect_equals 0 it diff --git a/tests/text_texture_visualized.toit b/tests/text_texture_visualized.toit deleted file mode 100644 index 3993927..0000000 --- a/tests/text_texture_visualized.toit +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2023 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the TESTS_LICENSE file. - -// Tests for TextTexture that the change box is smaller when we only -// change part of the text. - -import bitmap show * -import expect show * -import font show * -import pixel_display show * -import pixel_display.texture show * -import .png_visualizer - -main args: - if args.size != 1: - print "Usage: script.toit png-basename" - exit 1 - driver := SeveralColorPngVisualizer 192 96 args[0] --outline=SEVERAL_WHITE - display := SeveralColorPixelDisplay driver - display.background = SEVERAL_DARK_GRAY - - sans10 := Font.get "sans10" - - ctx := display.context --landscape --color=SEVERAL_ORANGE --font=sans10 - - texture_text := display.text ctx 30 20 "Testing 123" - texture_text_2 := display.text (ctx.with --alignment=TEXT_TEXTURE_ALIGN_RIGHT) 180 50 "123 Testing" - texture_text_3 := display.text (ctx.with --alignment=TEXT_TEXTURE_ALIGN_CENTER) 96 80 "T 123 For the win" - display.draw - - texture_text.text = "Testing 42" - texture_text_2.text = "42 Testing" - // The "MM" has the same pixel width as the "123" above, so we can test the - // case where the ends are unchanged, but the middle changes. - texture_text_3.text = "T MM For the win" - display.draw - - texture_text.text = "Test the West" - texture_text_2.text = "Test the Folketing" - texture_text_3.text = "Test * For the win" - display.draw - - texture_text.text = "Test the West" - texture_text_2.text = "Test the Folketïng" - texture_text_3.text = "Test * For the win" // Both ends move because its centered. - display.draw - - driver.write_png diff --git a/tests/texture_test_slow.toit b/tests/texture_test_slow.toit index 5b921f6..cbd0c77 100644 --- a/tests/texture_test_slow.toit +++ b/tests/texture_test_slow.toit @@ -7,9 +7,8 @@ import expect show * import bitmap show * import crypto.sha1 as crypto import font show * -import pixel_display.histogram show * -import pixel_display.texture show * +import pixel_display.common show Transform import pixel_display.four_gray import pixel_display.two_bit_texture as two_bit import pixel_display.three_color @@ -31,15 +30,10 @@ skip_version version/int -> bool: main: feature_detect - barcode_test test_simple_three_color test_simple_four_gray test_simple_two_color test_simple_true_color - test_simple_scene - test_with_transparency - composite_test - test_bounding_box bitmap_primitives_present := true bytemap_primitives_present := true @@ -58,38 +52,6 @@ feature_detect: identity ::= Transform.identity -filled_rectangle_factory version color x y w h: - if version == THREE_COLOR: - return three_color.FilledRectangle color x y w h identity - else if version == TWO_COLOR: - return two_color.FilledRectangle color x y w h identity - else if version == FOUR_GRAY: - return four_gray.FilledRectangle color x y w h identity - else: - return true_color.FilledRectangle color x y w h identity - -bitmap_texture_factory version x y orientation w h color: - orientation_to_transform x y orientation: | x y transform | - if version == THREE_COLOR: - return three_color.BitmapTexture x y w h transform color - else if version == TWO_COLOR: - return two_color.BitmapTexture x y w h transform color - else if version == FOUR_GRAY: - return four_gray.BitmapTexture x y w h transform color - else: - return true_color.BitmapTexture x y w h transform color - unreachable - -opaque_bitmap_texture_factory version x y w h foreground background: - if version == THREE_COLOR: - return three_color.OpaqueBitmapTexture x y w h identity foreground background - else if version == TWO_COLOR: - return two_color.OpaqueBitmapTexture x y w h identity foreground background - else if version == FOUR_GRAY: - return four_gray.OpaqueBitmapTexture x y w h identity foreground background - else: - return true_color.OpaqueBitmapTexture x y w h identity foreground background - orientation_to_transform x y orientation [block]: transform := null if orientation == ORIENTATION_0: @@ -106,47 +68,6 @@ orientation_to_transform x y orientation [block]: transform = transform.rotate_left block.call x y transform -text_texture_factory version x y orientation alignment text font color: - orientation_to_transform x y orientation: | x y transform | - if version == THREE_COLOR: - return three_color.TextTexture x y transform alignment text font color - else if version == TWO_COLOR: - return two_color.TextTexture x y transform alignment text font color - else if version == FOUR_GRAY: - return four_gray.TextTexture x y transform alignment text font color - else: - return true_color.TextTexture x y transform alignment text font color - unreachable - -barcode_factory version code x y orientation: - orientation_to_transform x y orientation: | x y transform | - if version == THREE_COLOR: - return three_color.BarCodeEan13 code x y transform - else if version == TWO_COLOR: - return two_color.BarCodeEan13 code x y transform - else if version == FOUR_GRAY: - return four_gray.BarCodeEan13 code x y transform - else: - return true_color.BarCodeEan13 code x y transform - unreachable - -histogram_factory version x y w h color orientation: - orientation_to_transform x y orientation: | x y transform | - texture := null - reflected := (random 2) == 0 ? false : true - if version == THREE_COLOR: - texture = ThreeColorHistogram --x=x --y=y --width=w --height=h --transform=transform --scale=0.4 --color=color --reflected=reflected - else if version == TWO_COLOR: - texture = TwoColorHistogram --x=x --y=y --width=w --height=h --transform=transform --scale=0.4 --color=color --reflected=reflected - else if version == FOUR_GRAY: - texture = FourGrayHistogram --x=x --y=y --width=w --height=h --transform=transform --scale=0.4 --color=color --reflected=reflected - else if version == TRUE_COLOR: - texture = TrueColorHistogram --x=x --y=y --width=w --height=h --transform=transform --scale=0.4 --color=color --reflected=reflected - 40.repeat: - texture.add (random 0 100) - 20 - return texture - unreachable - canvas_factory version w h x y: result := ? if version == THREE_COLOR: @@ -212,375 +133,3 @@ test_simple_true_color: 8.repeat: | x | 8.repeat: | y | expect (canvas.get_pixel_ x y) == bluish - -test_simple_scene: - // The scene has an 8x8 red square at 0, 0. - VERSIONS.repeat: | version | - if skip_version version: continue.repeat - red_square := bitmap_texture_factory version 0 0 ORIENTATION_0 8 8 (get_red version) - - 8.repeat: | x | 8.repeat: | y | red_square.set_pixel x y - - // Render the scene onto some canvases. - 8.repeat: | square_x | 8.repeat: | square_y | - red_square.move_to square_x square_y - // The canvas is always 8-aligned relative to the scene. - 3.repeat: | xi | 3.repeat: | yi | - x := (xi - 1) * 8 - y := (yi - 1) * 8 - canvas := canvas_factory version 16 16 (x - 8) (y - 8) - // The canvas is placed in various places and we render our scene onto it. - red_square.write canvas - // Check there is a red square on the canvas from 8-x,8-y to 16-x,16-y - 16.repeat: | x2 | 16.repeat: | y2 | - if 8 - x + square_x <= x2 < 16 - x + square_x and 8 - y + square_y <= y2 < 16 - y + square_y: - expect (canvas.get_pixel_ x2 y2) == (get_red version) - else: - expect (canvas.get_pixel_ x2 y2) == 0 - -hash := crypto.sha1 "Toitware!" - -pseudo_random x: - mod := hash.size << 3 - x %= mod - if x < 0: x += mod - return hash[x >> 3] & (1 << (x & 7)) != 0 - -test_with_transparency: - if not bitmap_primitives_present: return - // The scene has a noisy 8x8 image, initially at 0,0. Some pixels are black, others are transparent. - prime_image := three_color.BitmapTexture 0 0 8 8 Transform.identity three_color.BLACK - 8.repeat: | x | 8.repeat: | y | - if (pseudo_random y * 8 + x): - prime_image.set_pixel x y - else: - prime_image.clear_pixel x y - - // The scene has a red circle, centered on 6,6, under the noisy 8x8 image. Outside the circle is transparency. - red_circle := three_color.BitmapTexture 0 0 12 16 Transform.identity three_color.RED - 12.repeat: | x | 16.repeat: | y | - r2 := (x - 6) * (x - 6) + (y - 6) * (y - 6) - if r2 > 36: - red_circle.clear_pixel x y - else: - red_circle.set_pixel x y - - // Render the scene onto some canvases. - 8.repeat: | texture_x | 8.repeat: | texture_y | - // The prime numbers illustration moves down to the right, and the red circle goes the other way. - prime_image.move_to texture_x texture_y - red_circle.move_to (8 - texture_x) (8 - texture_y) - // The canvas is always 8-aligned relative to the scene. - 3.repeat: | xi | 3.repeat: | yi | - x := (xi - 1) * 8 - y := (yi - 1) * 8 - canvas := three_color.Canvas_ 16 16 // Starts off white. - canvas.x_offset_ = x - canvas.y_offset_ = y - // The canvas is placed in various places (8-aligned) and we render our scene onto it. - red_circle.write canvas - prime_image.write canvas - - // Check the scene rendered right onto the canvas. - 16.repeat: | x2 | 16.repeat: | y2 | - scene_x := x2 + x - scene_y := y2 + y - prime_x := scene_x - texture_x - prime_y := scene_y - texture_y - if 0 <= prime_x < 8 and 0 <= prime_y < 8 and pseudo_random prime_y * 8 + prime_x: - expect (canvas.get_pixel_ x2 y2) == three_color.BLACK - else: - circle_x := scene_x - 8 + texture_x - circle_y := scene_y - 8 + texture_y - if 0 <= circle_x < red_circle.w_ and 0 <= circle_y < red_circle.h_: - if (red_circle.pixel_is_set circle_x circle_y): - expect (canvas.get_pixel_ x2 y2) == three_color.RED - else: - expect (canvas.get_pixel_ x2 y2) == three_color.WHITE // Initial color of canvas. - -get_white version: - if version == THREE_COLOR: - return three_color.WHITE - if version == TWO_COLOR: - return two_color.WHITE - if version == FOUR_GRAY: - return four_gray.WHITE - return true_color.get_rgb 0xff 0xff 0xff - -get_black version: - if version == THREE_COLOR: - return three_color.BLACK - if version == TWO_COLOR: - return two_color.BLACK - if version == FOUR_GRAY: - return four_gray.BLACK - return true_color.get_rgb 0 0 0 - -get_red version: - if version == THREE_COLOR: - return three_color.RED - if version == TWO_COLOR: - return two_color.BLACK // No red on two-color - if version == FOUR_GRAY: - return four_gray.DARK_GRAY // No red on four-gray - return true_color.get_rgb 0xff 0 0 - -bullseye_get_color version x y: - r2 := (x - 6.5) * (x - 6.5) + (y - 6.5) * (y - 6.5) - if r2 < 42: - if r2 < 11: - return false // Transparent in center - else: - return true // Colored ring - else: - return false - -get_j_pixel x y j_bitmap -> bool: - if not 0 <= x < 16: return false - if not 0 <= y < 16: return false - return (j_bitmap[x + (y >> 3) * 16] & (1 << (y & 7))) != 0 - -composite_test: - if not bitmap_primitives_present: - return // This test always uses bitmap_draw_text - VERSIONS.repeat: | version | - if skip_version version: continue.repeat - - red_dot := bitmap_texture_factory version 0 0 ORIENTATION_0 13 16 (get_red version) - - 13.repeat: | x | 16.repeat: | y | - if (bullseye_get_color version x y): - red_dot.set_pixel x y - else: - red_dot.clear_pixel x y - - sans10 := Font.get "sans10" - - j_color := 0 - if version == TRUE_COLOR: - j_color = true_color.get_rgb 4 5 6 - else: - j_color = get_black version - letter_j := text_texture_factory version 0 0 0 TEXT_TEXTURE_ALIGN_LEFT "j" sans10 j_color - - j_bitmap := ByteArray 32 // 16x16 bitmap. - bitmap_draw_text 4 12 1 0 "j" sans10 j_bitmap 16 - - set_random_seed "mustard" - - 100.repeat: - // Add a random colored box. - box_w := random 3 17 - box_h := random 3 17 - box_color := random 0 [3, 2, 4, 0x1000000][version] - box := filled_rectangle_factory version box_color 0 0 box_w box_h - box_x := random -20 25 - box_y := random -20 25 - box.move_to box_x box_y - - // Move the red dot to a random place. - rd_x := random -20 25 - rd_y := random -20 25 - red_dot.move_to rd_x rd_y - - // Move the letter j to a random place. - j_x := random -20 25 - j_y := random -20 25 - letter_j.move_to j_x j_y - - // Stack them in a random order. - order := random 0 6 - seq := null - if order == 0: seq = [red_dot, letter_j, box] - else if order == 1: seq = [red_dot, box, letter_j] - else if order == 2: seq = [box, letter_j, red_dot] - else if order == 3: seq = [box, red_dot, letter_j] - else if order == 4: seq = [letter_j, box, red_dot] - else if order == 5: seq = [letter_j, red_dot, box] - - x_offset := (random 0 3) * 8 - y_offset := (random 0 3) * 8 - - // Draw onto a window that is positioned at a random aligned place in the scene. - canvas := canvas_factory version 24 24 x_offset y_offset - - seq[2].write canvas - seq[1].write canvas - seq[0].write canvas - - 24.repeat: | x | 24.repeat: | y | - actual_pixel := canvas.get_pixel_ x y - scene_x := x + x_offset - scene_y := y + y_offset - done := false - for idx := 0; not done; idx++: - if idx >= seq.size: - expect actual_pixel == 0 - done = true - else if seq[idx] == red_dot: - is_red := bullseye_get_color version scene_x - rd_x scene_y - rd_y - if is_red: - expect (get_red version) == actual_pixel - done = true - else if seq[idx] == box: - in_x := box_x <= scene_x < box_x + box_w - in_y := box_y <= scene_y < box_y + box_h - if in_x and in_y: - expect actual_pixel == box_color - done = true - else: - expect seq[idx] == letter_j - bit := get_j_pixel (4 + scene_x - j_x) (12 + scene_y - j_y) j_bitmap - if bit: - expect actual_pixel == j_color - done = true - -barcode_test: - if not bitmap_primitives_present: - return - barcode := three_color.BarCodeEan13 "5017239191589" 10 10 Transform.identity - canvas := three_color.Canvas_ 128 104 // Starts off white. - barcode.write canvas - - expect (barcode.l_ 0) == 0x0d - expect (barcode.g_ 0) == 0x27 - expect (barcode.r_ 0) == 0x72 - - line := "" - canvas.width_.repeat: | x | - line += (canvas.get_pixel_ x 60) == 0 ? " " : "*" - - expect line == " * * ** * ** ** * * * ** **** * * *** * * ** ** *** * ** ** * *** * * *** * * * " - - canvas.height_.repeat: | y | - if y > 80: - str := "" - canvas.width_.repeat: | x | - str += (canvas.get_pixel_ x y) == 0 ? " " : "*" - print str - -random_pixel_ version x y: - hash1 := (x ^ y) + (y << 7) - hash2 := (x + y) ^ ((x ^ y) >> 5) ^ ((x - y) << 3) - return (pseudo_random hash1) - -test_bounding_box: - VERSIONS.repeat: | version | - if skip_version version: continue.repeat - WIDTH ::= version == TRUE_COLOR ? 48 : 128 - HEIGHT ::= version == TRUE_COLOR ? 48 : 128 - noisy_background := opaque_bitmap_texture_factory version 0 0 WIDTH HEIGHT (get_red version) (get_white version) - white_background := null - if version == THREE_COLOR: - white_background = three_color.WHITE - else if version == TWO_COLOR: - white_background = two_color.WHITE - else if version == FOUR_GRAY: - white_background = four_gray.WHITE - else: - white_background = true_color.get_rgb 0xff 0xff 0xff - sans10 := Font.get "sans10" - WIDTH.repeat: | x | HEIGHT.repeat: | y | - if random_pixel_ version x y: - noisy_background.set_pixel x y // Make pixel red (or black for two-color) - - 100.repeat: - type := random 0 5 - texture := null - x := random 0 WIDTH - y := random 0 HEIGHT - w := random 0 32 - h := random 0 32 - color := random 0 [3, 2, 4, 0x1000000][version] - orientation := random 0 4 - if type == 0: - alignment := random 0 3 - text := ["ji", "AV", "VA", "AA", "To", "X", "x", "a"][random 0 8] - texture = text_texture_factory version x y orientation alignment text sans10 color - w = texture.display_w - h = texture.display_h - // Text is positioned by the bottom left corner, but the max extent is - // determined by the top left corner, after rotation, so we read that - // back. - x = texture.display_x - y = texture.display_y - else if type == 1: - code := "$(%06d (random 0 1000000))$(%07d (random 0 10000000))" - texture = barcode_factory version code x y orientation - w = texture.display_w - h = texture.display_h - x = texture.display_x - y = texture.display_y - color = -1 - else if type == 2: - texture = filled_rectangle_factory version color x y w h - else if type == 3: - texture = bitmap_texture_factory version x y orientation w h color - w.repeat: | i | h.repeat: | j | - texture.set_pixel i j - if w != 0 and h != 0 and version == THREE_COLOR: - 10.repeat: - texture.clear_pixel (random 0 w) (random 0 h) - w = texture.display_w - h = texture.display_h - x = texture.display_x - y = texture.display_y - else if type == 4: - texture = histogram_factory version x y w h color orientation - w = texture.display_w - h = texture.display_h - x = texture.display_x - y = texture.display_y - - canvas_x_offset := (random 0 16) << 3 - canvas_y_offset := (random 0 16) << 3 - canvas := canvas_factory version WIDTH HEIGHT 0 0 - canvas.set_all_pixels white_background - noisy_background.write canvas - canvas.x_offset_ = canvas_x_offset - canvas.y_offset_ = canvas_y_offset - texture.write canvas - - // Sample some random points to see if they are OK. - 50.repeat: - i := random 0 WIDTH - j := random 0 HEIGHT - - scene_x := i + canvas_x_offset - scene_y := j + canvas_y_offset - noisy_pixel := (random_pixel_ version i j) ? (get_red version) : (get_white version) - if scene_x < x or scene_x >= x+w or scene_y < y or scene_y >= y+h: - // If we are outside the texture, then the canvas should be untouched. - expect (canvas.get_pixel_ i j) == noisy_pixel - else: - // Inside the texture the pixels should either be the texture color or - // the original pattern. - if color == -1: - // Barcodes are black and white and have a solid background. - white := get_white version - black := get_black version - expect ((canvas.get_pixel_ i j) == black or (canvas.get_pixel_ i j) == white) - else: - // Other textures have a single color in this test. - if (canvas.get_pixel_ i j) != color: - expect (canvas.get_pixel_ i j) == noisy_pixel - - // Trace a line around the perimeter of the texture to see that the pixels - // outside the box are untouched. - w.repeat: | i | - canvas_x := x + i - canvas_x_offset - if 0 <= canvas_x < WIDTH: - 2.repeat: | one_or_zero | - canvas_y := (y - 1) - canvas_y_offset + (one_or_zero * (h + 1)) - noisy_pixel := (random_pixel_ version canvas_x canvas_y) ? (get_red version) : (get_white version) - if 0 <= canvas_y < HEIGHT: - expect (canvas.get_pixel_ canvas_x canvas_y) == noisy_pixel - - h.repeat: | i | - canvas_y := y + i - canvas_y_offset - if 0 <= canvas_y < HEIGHT: - 2.repeat: | one_or_zero | - canvas_x := (x - 1) - canvas_x_offset + (one_or_zero * (w + 1)) - noisy_pixel := (random_pixel_ version canvas_x canvas_y) ? (get_red version) : (get_white version) - if 0 <= canvas_x < WIDTH: - expect (canvas.get_pixel_ canvas_x canvas_y) == noisy_pixel diff --git a/tests/transform_test.toit b/tests/transform_test.toit index 60a3c28..c85ff30 100644 --- a/tests/transform_test.toit +++ b/tests/transform_test.toit @@ -4,7 +4,7 @@ import expect show * -import pixel_display.texture show * +import pixel_display.common show * main: simple_texture_test diff --git a/tests/true_color_portrait_visualized.toit b/tests/true_color_portrait_visualized.toit index 050ae11..254665d 100644 --- a/tests/true_color_portrait_visualized.toit +++ b/tests/true_color_portrait_visualized.toit @@ -5,6 +5,7 @@ import expect show * import font show Font import pixel_display show * +import pixel_display.element show * import pixel_display.true_color show * import .png_visualizer @@ -13,26 +14,32 @@ main args: print "Usage: script.toit png-basename" exit 1 driver := TrueColorPngVisualizer 129 89 args[0] --outline=0xffff00 - display := TrueColorPixelDisplay driver + display := TrueColorPixelDisplay driver --portrait display.background = get_rgb 0 1 2 sans10 := Font.get "sans10" - ctx := display.context --landscape=false --color=(get_rgb 255 120 0) --font=sans10 + foreground := get_rgb 255 120 0 - display.filled_rectangle (ctx.with --color=0x4040ff) 10 20 30 40 - display.filled_rectangle (ctx.with --color=0x4040ff) 20 70 30 40 - display.text ctx 5 80 "Testing" - middle_line := display.text ctx 5 100 "the display" + display.add + Div --x=10 --y=20 --w=30 --h=40 --background=0x4040ff + display.add + Div --x=20 --y=70 --w=30 --h=40 --background=0x4040ff + display.add + Label --x=5 --y=80 --label="Testing" --font=sans10 --color=foreground + middle_line := Label --x=5 --y=100 --label="the display" --font=sans10 --color=foreground + display.add middle_line display.draw - display.text ctx 5 120 "for the win" + + display.add + Label --x=5 --y=120 --label="for the win" --font=sans10 --color=foreground display.draw middle_line.move_to 10 100 display.draw - middle_line.text = "the DisplaY" + middle_line.label = "the DisplaY" display.draw driver.write_png diff --git a/tests/true_color_visualized.toit b/tests/true_color_visualized.toit index aca7f72..91bbba4 100644 --- a/tests/true_color_visualized.toit +++ b/tests/true_color_visualized.toit @@ -5,6 +5,7 @@ import expect show * import font show Font import pixel_display show * +import pixel_display.element show * import pixel_display.true_color show * import .png_visualizer @@ -18,20 +19,24 @@ main args: sans10 := Font.get "sans10" - ctx := display.context --landscape --color=(get_rgb 255 120 0) --font=sans10 + foreground := get_rgb 255 120 0 - display.filled_rectangle (ctx.with --color=0x4040ff) 10 20 30 40 - display.text ctx 50 20 "Testing" - middle_line := display.text ctx 50 40 "the display" + display.add + Div --x=10 --y=20 --w=30 --h=40 --background=0x4040ff + display.add + Label --x=50 --y=20 --label="Testing" --font=sans10 --color=foreground + middle_line := Label --x=50 --y=40 --label="the display" --font=sans10 --color=foreground + display.add middle_line display.draw - display.text ctx 50 60 "for the win" + display.add + Label --x=50 --y=60 --label="for the win" --font=sans10 --color=foreground display.draw middle_line.move_to 60 40 display.draw - middle_line.text = "the DISPLAY" + middle_line.label = "the DISPLAY" display.draw driver.write_png