diff --git a/src/common.toit b/src/common.toit index 993abfb..60d4603 100644 --- a/src/common.toit +++ b/src/common.toit @@ -92,6 +92,11 @@ abstract class Canvas: abstract composit frame_opacity frame_canvas/Canvas painting_opacity painting_canvas/Canvas + abstract rectangle x/int y/int --w/int --h/int --color/int -> none + + abstract text x/int y/int --text/string --color/int --font/Font --orientation/int + abstract text x/int y/int --text/string --color/int --font/Font + TRANSFORM_IDENTITY_ ::= Transform.with_ [1, 0, 0, 1, 0, 0] TRANSFORM_90_ ::= Transform.with_ [0, -1, 1, 0, 0, 0] TRANSFORM_180_ ::= Transform.with_ [-1, 0, 0, -1, 0, 0] diff --git a/src/element.toit b/src/element.toit new file mode 100644 index 0000000..9f6d49e --- /dev/null +++ b/src/element.toit @@ -0,0 +1,530 @@ +// 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 .four_gray as four_gray +import .true_color as true_color +import .gray_scale as gray_scale +import .one_byte as one_byte +import .style +import .common +import font show Font +import math + +abstract class Element extends ElementOrTexture_ implements Window: + style_/Style? := ? + classes/List? := ? + id/string? := ? + children/List? := ? + background_ := null + border_/Border? := null + + x_ /int? := null + y_ /int? := null + + x -> int?: return x_ + y -> int?: return y_ + + constructor + --x/int?=null + --y/int?=null + --style/Style?=null + --element_class/string?=null + --.classes/List?=null + --.id/string?=null + --background=null + --border/Border?=null + .children/List?=null: + x_ = x + y_ = y + style_ = style + if element_class: + if not classes: classes = [] + classes.add element_class + background_=background + border_=border + if children: children.do: | child/Element | + child.change_tracker = this + + get_element_by_id id/string: + if id == this.id: return this + if children: + children.do: | child/Element | + found := child.get_element_by_id id + if found: return found + return null + + add element/Element -> none: + if not children: children = [] + children.add element + element.change_tracker = this + element.invalidate + + remove element/Element -> none: + if children: + children.remove element + 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 + it.change_tracker = null + children = null + + x= value/int -> none: + invalidate + x_ = value + invalidate + + y= value/int -> none: + invalidate + y_ = value + invalidate + + move_to x/int y/int: + invalidate + x_ = x + y_ = y + invalidate + + border= value/Border?: + if value != border_: + invalidate + border_ = value + invalidate + + background -> Background?: + return background_ + + background= value/Background?: + if value != background_: + invalidate + background_ = value + invalidate + + write_ canvas -> none: + throw "Can't call write_ on an Element" + + abstract draw canvas/Canvas -> none + + child_invalidated_element x/int y/int w/int h/int -> none: + if change_tracker: + x2 := max x_ (x_ + x) + y2 := max y_ (y_ + y) + right := min (x_ + this.w) (x_ + x + w) + bottom := min (y_ + this.h) (y_ + y + h) + if x2 < right and y2 < bottom: + change_tracker.child_invalidated_element x2 y2 (right - x2) (bottom - y2) + + abstract w -> int? + abstract h -> int? + + set_styles styles/List -> none: + styles_for_children := null + install_style := : | style/Style | + style.matching_styles --type=type --classes=classes --id=id: | style/Style | + style.iterate_properties: | key/string value | + set_attribute key value + if children: + if not styles_for_children: styles_for_children = styles.copy + styles_for_children.add style + styles.do install_style + if style_: + style_.iterate_properties: | key/string value | + set_attribute key value + install_style.call style_ + if children: + children.do: | child/Element | + child.set_styles (styles_for_children or styles) + + set_attribute key/string value -> none: + if key == "background": + if background_ != value: + invalidate + background_ = value + invalidate + else if key == "border": + if border_ != value: + invalidate + border_ = value + invalidate + + abstract type -> string + +interface ColoredElement: + color -> int? + color= value/int -> none + +class Div extends Element: + w_ /int? := null + h_ /int? := null + + type -> string: return "div" + + constructor + --x/int?=null + --y/int?=null + --w/int?=null + --h/int?=null + --style/Style?=null + --element_class/string?=null + --classes/List?=null + --id/string?=null + --background=null + --border/Border?=null + children/List?=null: + w_ = w + h_ = h + super --x=x --y=y --style=style --element_class=element_class --classes=classes --id=id --background=background --border=border children + + invalidate: + if change_tracker and x and y and w and h: + change_tracker.child_invalidated_element x y w h + + w -> int?: return w_ + h -> int?: return h_ + + w= value/int? -> none: + if w_ != value: + if w_: invalidate + w_ = value + if value: invalidate + + h= value/int? -> none: + if h_ != value: + if h_: invalidate + h_ = value + if value: invalidate + + set_size w/int h/int -> none: + if w_ != w or h_ != h: + if w_ and h_: invalidate + w_ = w + h_ = h + invalidate + + set_attribute key/string value -> none: + if key == "width": + w = value + else if key == "height": + h = value + else: + super key value + + draw canvas/Canvas -> none: + old_transform := canvas.transform + canvas.transform = old_transform.translate x_ y_ + Background.draw background_ canvas 0 0 w h --no-autocropped + custom_draw canvas + if border_: border_.draw canvas 0 0 w h + canvas.transform = old_transform + + custom_draw canvas/Canvas -> none: + if children: children.do: it.draw canvas + +class Label extends Element implements ColoredElement: + color_/int := ? + label_/string := ? + alignment_/int := ? + orientation_/int := ? + font_/Font? := ? + left_/int? := null + top_/int? := null + width_/int? := null + height_/int? := null + + type -> string: return "label" + + set_attribute key/string value -> none: + if key == "color": + color = value + else if key == "font": + font = value + + color -> int?: return color_ + + color= value/int -> none: + if color_ != value: + color_ = value + invalidate + + font -> Font?: return font_ + + font= value/Font -> none: + if font_ != value: + font_ = value + left_ = null // Trigger recalculation. + invalidate + + constructor --x/int?=null --y/int?=null --color/int=0 --label/string="" --font/Font?=null --orientation/int=ORIENTATION_0 --alignment/int=ALIGN_LEFT: + color_ = color + label_ = label + alignment_ = alignment + orientation_ = orientation + font_ = font + super --x=x --y=y + + /** + Calls the block with the left, top, width, and height. + For zero sized objects, doesn't call the block. + */ + xywh_ [block]: + if not left_: + extent/List := font_.text_extent label_ + displacement := 0 + if alignment_ != ALIGN_LEFT: + displacement = (font_.pixel_width label_) + if alignment_ == ALIGN_CENTER: displacement >>= 1 + l := extent[2] - displacement + r := extent[2] - displacement + extent[0] + t := -extent[1] - extent[3] + b := extent[3] + if orientation_ == ORIENTATION_0: + left_ = l + top_ = t + width_ = extent[0] + height_ = extent[1] + else if orientation_ == ORIENTATION_90: + left_ = t + top_ = -r + width_ = extent[1] + height_ = extent[0] + else if orientation_ == ORIENTATION_180: + left_ = -r + top_ = b + width_ = extent[0] + height_ = extent[1] + else: + assert: orientation_ == ORIENTATION_270 + left_ = b + top_ = l + width_ = extent[1] + height_ = extent[0] + block.call (x_ + left_) (y_ + top_) width_ height_ + + w -> int: + if not left_: + xywh_: null + return width_ + + h -> int: + if not left_: + xywh_: null + return height_ + + invalidate: + if change_tracker and x and y and font_ and label_: + xywh_: | x y w h | + change_tracker.child_invalidated_element x y w h + + label= value/string -> none: + if value == label_: return + if orientation_ == ORIENTATION_0 and change_tracker and x and y: + text_get_bounding_boxes_ label_ value alignment_ font_: | old/TextExtent_ new/TextExtent_ | + change_tracker.child_invalidated_element (x_ + old.x) (y_ + old.y) old.w old.h + change_tracker.child_invalidated_element (x_ + new.x) (y_ + new.y) new.w new.h + label_ = value + left_ = null // Trigger recalculation. + return + invalidate + label_ = value + left_ = null // Trigger recalculation. + invalidate + + orientation= value/int -> none: + if value == orientation_: return + invalidate + orientation_ = value + left_ = null // Trigger recalculation. + invalidate + + alignment= value/int -> none: + if value == alignment_: return + invalidate + alignment_ = value + left_ = null // Trigger recalculation. + invalidate + + draw canvas /Canvas -> none: + x := x_ + y := y_ + if not (x and y): return + if alignment_ != ALIGN_LEFT: + text_width := font_.pixel_width label_ + if alignment_ == ALIGN_CENTER: text_width >>= 1 + if orientation_ == ORIENTATION_0: + x -= text_width + else if orientation_ == ORIENTATION_90: + y += text_width + else if orientation_ == ORIENTATION_180: + x += text_width + else: + assert: orientation_ == ORIENTATION_270 + y -= text_width + canvas.text x y --text=label_ --color=color_ --font=font_ --orientation=orientation_ + +/** +A superclass for elements that can draw themselves. Override the + $draw method in your subclass to draw on the canvas. The $w + and $h methods/fields are used to determine the size of the element + for redrawing purposes. + +Drawing operations are automatically clipped to w and h. +*/ +abstract class CustomElement extends ClippingDiv: + abstract w -> int? + abstract h -> int? + + constructor --x/int?=null --y/int?=null --w/int?=null --h/int?=null: + super --x=x --y=y --w=w --h=h + + invalidate: + if change_tracker and x and y and w and h: + change_tracker.child_invalidated_element x y w h + + draw canvas/Canvas -> none: + if not (x and y): return + analysis := canvas.bounds_analysis x y w h + if analysis == Canvas.DISJOINT: return + autocropped := analysis == Canvas.CANVAS_IN_AREA or analysis == Canvas.COINCIDENT + old_transform := canvas.transform + canvas.transform = old_transform.translate x_ y_ + Background.draw background_ canvas 0 0 w h --autocropped=autocropped + custom_draw canvas + if border_: border_.draw canvas 0 0 w h + canvas.transform = old_transform + + /** + Override this to draw your custom element. The coordinate system is + the coordinate system of your element, ie the top left is 0, 0. + The background has already been drawn when this is called, and the + frame will be drawn afterwards. + */ + abstract custom_draw canvas/Canvas -> none + +/** +A ClippingDiv is like a div, but it clips any draws inside of it. It can + have a shadow or other drawing outside its raw x y w h area, called the + decoration. +For style purposes it has the type "div", not "clipping-div". +Because it has clipping and compositing, it can have more interesting borders + like rounded corners. +*/ +class ClippingDiv extends Div: + /** + Calls the block with x, y, w, h, which includes the decoration. + */ + extent --x=x_ --y=y_ --w=w_ --h=h_ [block] -> none: + if border_: + border_.invalidation_area x y w h block + else: + block.call x y w h + + /** + Invalidates the whole window including the decoration. + */ + invalidate --x=x_ --y=y_ --w=w_ --h=h_ -> none: + if change_tracker: + extent --x=x --y=y --w=w --h=h: | outer_x outer_y outer_w outer_h | + change_tracker.child_invalidated_element outer_x outer_y outer_w outer_h + + static ALL_TRANSPARENT ::= ByteArray 1: 0 + static ALL_OPAQUE ::= ByteArray 1: 0xff + + static is_all_transparent opacity -> bool: + if opacity is not ByteArray: return false + return opacity.size == 1 and opacity[0] == 0 + + static is_all_opaque opacity -> bool: + if opacity is not ByteArray: return false + return opacity.size == 1 and opacity[0] == 0xff + + constructor + --x/int?=null + --y/int?=null + --w/int?=null + --h/int?=null + --style/Style?=null + --element_class/string?=null + --classes/List?=null + --id/string?=null + --background=null + --border/Border?=null + children/List?=null: + super --x=x --y=y --w=w --h=h --style=style --element_class=element_class --classes=classes --id=id --background=background --border=border children + + // After the textures under us have drawn themselves, we draw on top. + draw canvas/Canvas -> none: + // If we are outside the window and the decorations, there is nothing to do. + extent: | x2 y2 w2 h2 | + if (canvas.bounds_analysis x2 y2 w2 h2) == Canvas.DISJOINT: return + + old_transform := canvas.transform + canvas.transform = old_transform.translate x_ y_ + + content_opacity := content_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. + if is_all_opaque content_opacity: + canvas.transform = old_transform + super canvas // Use the unclipped drawing method from Div. + return + + frame_opacity := frame_map canvas + + if is_all_transparent frame_opacity and is_all_transparent content_opacity: + canvas.transform = old_transform + return + + // The complicated case where we have to composit the tile with the border and decorations. + border_canvas := null + if not is_all_transparent frame_opacity: + border_canvas = canvas.create_similar + if border_: border_.draw border_canvas 0 0 w h + + painting_canvas := canvas.create_similar + Background.draw background_ painting_canvas 0 0 w h --autocropped + custom_draw painting_canvas + + canvas.composit frame_opacity border_canvas content_opacity painting_canvas + + canvas.transform = old_transform + + type -> string: return "div" + + /** + Returns a canvas that is an alpha map for the given area that describes where + the things behind around this window shines through. This is mainly used for + rounded corners, but also for other decorations. + 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. + The coordinate system of the canvas is the coordinate system of the window, so + the top and left edges may be plotted at negative coordinates. + */ + frame_map canvas/Canvas: + if not border_: return ClippingDiv.ALL_TRANSPARENT // No border visible. + return border_.frame_map canvas w h + + /** + Returns a canvas that is an alpha map for the given area that describes where + the window content (background of the window and children) are visible. + 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. + The coordinate system of the canvas is the coordinate system of the window. + */ + content_map canvas/Canvas: + return (border_ or NO_BORDER_).content_map canvas w h + + draw_frame canvas/Canvas: + if border_: border_.draw canvas 0 0 w h diff --git a/src/one_byte.toit b/src/one_byte.toit index 2beb8b9..cc91e6e 100644 --- a/src/one_byte.toit +++ b/src/one_byte.toit @@ -35,8 +35,17 @@ abstract class OneByteCanvas_ extends Canvas: return result composit frame_opacity frame_canvas/OneByteCanvas_? painting_opacity painting_canvas/OneByteCanvas_: - composit_bytes pixels_ frame_opacity (frame_canvas ? frame_canvas.pixels_ : null) painting_opacity painting_canvas.pixels_ false + fo := frame_opacity is ByteArray ? frame_opacity : frame_opacity.pixels_ + po := painting_opacity is ByteArray ? painting_opacity : painting_opacity.pixels_ + composit_bytes pixels_ fo (frame_canvas ? frame_canvas.pixels_ : null) po painting_canvas.pixels_ false + rectangle x/int y/int --w/int --h/int --color/int: + transform.xywh x y w h: | x2 y2 w2 h2 | + bytemap_rectangle x2 y2 color w2 h2 pixels_ width_ + + 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_ := ? diff --git a/src/pixel_display.toit b/src/pixel_display.toit index 8094222..9ec7db7 100644 --- a/src/pixel_display.toit +++ b/src/pixel_display.toit @@ -8,14 +8,16 @@ See https://docs.toit.io/language/sdk/display */ import bitmap show * +import font show Font +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 -import font show Font -import icons show Icon import .four_gray as four_gray import .true_color as true_color import .gray_scale as gray_scale @@ -31,8 +33,6 @@ FLAG_SEVERAL_COLOR ::= 0b10000 FLAG_TRUE_COLOR ::= 0b100000 FLAG_PARTIAL_UPDATES ::= 0b1000000 -class Element: - /** Abstract superclass for all pixel display drivers. For example, implemented by the drivers in diff --git a/src/style.toit b/src/style.toit index 2369b36..9a14bf0 100644 --- a/src/style.toit +++ b/src/style.toit @@ -2,6 +2,85 @@ // Use of this source code is governed by an MIT-style license that can be // found in the LICENSE file. +import .common +import .element as element + ALIGN_LEFT ::= 0 ALIGN_CENTER ::= 1 ALIGN_RIGHT ::= 2 + +class Style: + iterate_properties [block] -> none: + unreachable // Unimplemented. + + matching_styles --type/string?=null --classes/List?=null --id/string?=null [block] -> none: + unreachable // Unimplemented. + +/** +A background is anything that can draw itself on an element as a background. +There is support for just using ints (rgb colors) as backgrounds to save + memory and flash. +*/ +interface Background: + draw canvas/Canvas x/int y/int w/int h/int --autocropped/bool -> none + + /** + We also use colors (ints) as backgrounds, so this helper method will + either just draw the plain color background, or call the draw method + on a real Background object. + */ + static draw background canvas/Canvas x/int y/int w/int h/int --autocropped/bool -> none: + if background is int: + if autocropped: + canvas.set_all_pixels background + else: + canvas.rectangle x y --w=w --h=h --color=background + else if background != null: + (background as Background).draw canvas x y w h --autocropped=autocropped + + static check_valid background -> none: + if background != null and background is not int and background is not Background: + throw "INVALID_ARGUMENT" + +interface Border: + /// Draws the border within the given rectangle. + draw canvas/Canvas x/int y/int w/int h/int -> none + + invalidation_area x/int y/int w/int h/int [block] -> none + + inner_dimensions w/int h/int [block] -> none + + offsets [block] -> none + + // Draws 100% opacity for the border and frame shape. We don't need to carve + // out the window content, there is assumed to be a different alpha map for + // that. + frame_map canvas/Canvas w/int h/int + + // Draws 100% opacity for the window content, a filled rectangle. + content_map canvas/Canvas w/int h/int + +class NoBorder implements Border: + needs_clipping -> bool: return false + + draw canvas x y w h: + // Nothing to draw. + + invalidation_area x/int y/int w/int h/int [block]: + block.call x y w h + + inner_dimensions w h [block] -> none: + block.call w h + + offsets [block] -> none: + block.call 0 0 + + frame_map canvas w h: + return element.ClippingDiv.ALL_TRANSPARENT + + content_map canvas/Canvas w/int h/int: + transparency_map := canvas.make_alpha_map + transparency_map.rectangle 0 0 --w=w --h=h --color=0xff + return transparency_map + +NO_BORDER_/Border ::= NoBorder diff --git a/src/true_color.toit b/src/true_color.toit index 81dc8d9..9b26007 100644 --- a/src/true_color.toit +++ b/src/true_color.toit @@ -78,6 +78,24 @@ class Canvas_ extends Canvas: composit_bytes green_ fo (frame_canvas ? frame_canvas.green_ : null) po painting_canvas.green_ false composit_bytes blue_ fo (frame_canvas ? frame_canvas.blue_ : null) po painting_canvas.blue_ false + rectangle x/int y/int --w/int --h/int --color/int: + transform.xywh x y w h: | x2 y2 w2 h2 | + r := color >> 16 + g := (color >> 8) & 0xff + b := color & 0xff + bytemap_rectangle x2 y2 r w2 h2 red_ width_ + bytemap_rectangle x2 y2 g w2 h2 green_ width_ + bytemap_rectangle x2 y2 b w2 h2 blue_ width_ + + text x/int y/int --text/string --color/int --font/Font --orientation/int=ORIENTATION_0: + transform.xyo x y orientation: | x2 y2 o2 | + r := color >> 16 + g := (color >> 8) & 0xff + b := color & 0xff + 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_ := ? diff --git a/src/two_bit_texture.toit b/src/two_bit_texture.toit index 8b5b16d..752708f 100644 --- a/src/two_bit_texture.toit +++ b/src/two_bit_texture.toit @@ -44,9 +44,23 @@ abstract class Canvas_ extends Canvas: return result composit frame_opacity frame_canvas painting_opacity painting_canvas/Canvas_: - composit_bytes plane_0_ frame_opacity (frame_canvas ? frame_canvas.plane_0_ : null) painting_opacity painting_canvas.plane_0_ true - composit_bytes plane_1_ frame_opacity (frame_canvas ? frame_canvas.plane_1_ : null) painting_opacity painting_canvas.plane_1_ true - + fo := frame_opacity is ByteArray ? frame_opacity : frame_opacity.pixels_ + po := painting_opacity is ByteArray ? painting_opacity : painting_opacity.pixels_ + composit_bytes plane_0_ fo (frame_canvas ? frame_canvas.plane_0_ : null) po painting_canvas.plane_0_ true + composit_bytes plane_1_ fo (frame_canvas ? frame_canvas.plane_1_ : null) po painting_canvas.plane_1_ true + + rectangle x/int y/int --w/int --h/int --color/int: + transform.xywh x y w h: | x2 y2 w2 h2 | + c2 := (color & 2) >> 1 + bitmap_rectangle x2 y2 (color & 1) w2 h2 plane_0_ width_ + bitmap_rectangle x2 y2 c2 w2 h2 plane_1_ width_ + + text x/int y/int --text/string --color/int --font/Font --orientation/int=ORIENTATION_0: + transform.xyo x y orientation: | x2 y2 o2 | + b0 := color & 1 + 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_ := ? diff --git a/src/two_color.toit b/src/two_color.toit index 3135a0c..b57d6f7 100644 --- a/src/two_color.toit +++ b/src/two_color.toit @@ -60,6 +60,13 @@ class Canvas_ extends Canvas: po := painting_opacity is ByteArray ? painting_opacity : painting_opacity.pixels_ composit_bytes pixels_ fo (frame_canvas ? frame_canvas.pixels_ : null) po painting_canvas.pixels_ true + rectangle x/int y/int --w/int --h/int --color/int: + transform.xywh x y w h: | x2 y2 w2 h2 | + bitmap_rectangle x2 y2 color w2 h2 pixels_ width_ + + 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_ := ? diff --git a/tests/4_gray_visualized.toit b/tests/4_gray_visualized.toit index 90f8f0a..98252bc 100644 --- a/tests/4_gray_visualized.toit +++ b/tests/4_gray_visualized.toit @@ -6,7 +6,7 @@ import expect show * import font show Font import pixel_display show * import pixel_display.four_gray show * -import .png-visualizer +import .png_visualizer main args: if args.size != 1: diff --git a/tests/bw_visualized.toit b/tests/bw_visualized.toit index 5c19d98..bb44317 100644 --- a/tests/bw_visualized.toit +++ b/tests/bw_visualized.toit @@ -6,7 +6,7 @@ import expect show * import font show Font import pixel_display show * import pixel_display.two_color show * -import .png-visualizer +import .png_visualizer main args: if args.size != 1: diff --git a/tests/drop_shadow_visualized.toit b/tests/drop_shadow_visualized.toit index e7cc708..dc938f8 100644 --- a/tests/drop_shadow_visualized.toit +++ b/tests/drop_shadow_visualized.toit @@ -7,7 +7,7 @@ import font show Font import pixel_display show * import pixel_display.texture show * import pixel_display.true_color show * -import .png-visualizer +import .png_visualizer main args: sans10 := Font.get "sans10" diff --git a/tests/gold/mixed_text_rotated_visualized.toit.png b/tests/gold/mixed_text_rotated_visualized.toit.png new file mode 100644 index 0000000..da4c528 Binary files /dev/null and b/tests/gold/mixed_text_rotated_visualized.toit.png differ diff --git a/tests/gold/mixed_text_visualized.toit.png b/tests/gold/mixed_text_visualized.toit.png new file mode 100644 index 0000000..c8659ca Binary files /dev/null and b/tests/gold/mixed_text_visualized.toit.png differ diff --git a/tests/gold/mixed_texture_rotated_visualized.toit.png b/tests/gold/mixed_texture_rotated_visualized.toit.png new file mode 100644 index 0000000..e9631ea Binary files /dev/null and b/tests/gold/mixed_texture_rotated_visualized.toit.png differ diff --git a/tests/gold/simple_several_window_visualized.toit.png b/tests/gold/simple_several_window_visualized.toit.png new file mode 100644 index 0000000..4823c33 Binary files /dev/null and b/tests/gold/simple_several_window_visualized.toit.png differ diff --git a/tests/gold/simple_three_color_window_visualized.toit.png b/tests/gold/simple_three_color_window_visualized.toit.png new file mode 100644 index 0000000..560875d Binary files /dev/null and b/tests/gold/simple_three_color_window_visualized.toit.png differ diff --git a/tests/gold/simple_two_color_window_visualized.toit.png b/tests/gold/simple_two_color_window_visualized.toit.png new file mode 100644 index 0000000..a392b1b Binary files /dev/null and b/tests/gold/simple_two_color_window_visualized.toit.png differ diff --git a/tests/gold/text_texture_visualized.toit.png b/tests/gold/text_texture_visualized.toit.png new file mode 100644 index 0000000..991c77c Binary files /dev/null and b/tests/gold/text_texture_visualized.toit.png differ diff --git a/tests/gold/text_visualized.toit.png b/tests/gold/text_visualized.toit.png new file mode 100644 index 0000000..7609d5b Binary files /dev/null and b/tests/gold/text_visualized.toit.png differ diff --git a/tests/gray_scale_visualized.toit b/tests/gray_scale_visualized.toit index 060a311..c60af1c 100644 --- a/tests/gray_scale_visualized.toit +++ b/tests/gray_scale_visualized.toit @@ -6,7 +6,7 @@ import expect show * import font show Font import pixel_display show * import pixel_display.gray_scale show * -import .png-visualizer +import .png_visualizer main args: if args.size != 1: diff --git a/tests/mixed_text_rotated_visualized.toit b/tests/mixed_text_rotated_visualized.toit new file mode 100644 index 0000000..fa06fb4 --- /dev/null +++ b/tests/mixed_text_rotated_visualized.toit @@ -0,0 +1,64 @@ +// 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. + +// 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. +// Uses a rotated frame (portrait mode). + +import bitmap show * +import expect show * +import font show * +import pixel_display show * +import pixel_display.element 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 160 96 args[0] --outline=SEVERAL_WHITE + display := SeveralColorPixelDisplay driver --portrait + display.background = SEVERAL_DARK_GRAY + + 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" + display.add element_text + 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 + display.draw + + element_text.orientation = ORIENTATION_90 + element_text_2.orientation = ORIENTATION_90 + display.draw + + element_text.orientation = ORIENTATION_180 + element_text_2.orientation = ORIENTATION_180 + display.draw + + element_text.orientation = ORIENTATION_270 + element_text_2.orientation = ORIENTATION_270 + display.draw + + driver.write_png diff --git a/tests/mixed_text_visualized.toit b/tests/mixed_text_visualized.toit new file mode 100644 index 0000000..cf8ef60 --- /dev/null +++ b/tests/mixed_text_visualized.toit @@ -0,0 +1,75 @@ +// 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. + +// 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. + +import bitmap show * +import expect show * +import font show * +import pixel_display show * +import pixel_display.element 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-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 + display.add element_text + 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 + display.draw + + element_text.orientation = ORIENTATION_90 + display.draw + + element_text.orientation = ORIENTATION_180 + display.draw + + element_text.orientation = ORIENTATION_270 + display.draw + + element_text.orientation = ORIENTATION_0 + display.draw + + element_text.alignment = TEXT_TEXTURE_ALIGN_CENTER + display.draw + + element_text.alignment = TEXT_TEXTURE_ALIGN_RIGHT + display.draw + + element_text.orientation = ORIENTATION_90 + display.draw + + element_text.orientation = ORIENTATION_180 + display.draw + + element_text.orientation = ORIENTATION_270 + display.draw + + driver.write_png diff --git a/tests/mixed_texture_rotated_visualized.toit b/tests/mixed_texture_rotated_visualized.toit new file mode 100644 index 0000000..52025a7 --- /dev/null +++ b/tests/mixed_texture_rotated_visualized.toit @@ -0,0 +1,45 @@ +// 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. + +// Mixes texture-based and element-based rectangles on the same display to test +// that they are positioned in the same way and the redraw boxes are right. +// Uses a rotated frame (portrait mode). + +import expect show * +import pixel_display show * +import pixel_display.element 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 160 64 args[0] --outline=SEVERAL_WHITE + 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_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 + display.draw + + driver.write_png diff --git a/tests/simple_several_window_visualized.toit b/tests/simple_several_window_visualized.toit new file mode 100644 index 0000000..1d64124 --- /dev/null +++ b/tests/simple_several_window_visualized.toit @@ -0,0 +1,38 @@ +// 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 bitmap show * +import font show Font +import pixel_display show * +import pixel_display.element show * +import .png_visualizer + +main args: + sans10 := Font.get "sans10" + + if args.size != 1: + print "Usage: script.toit png-basename" + exit 1 + driver := SeveralColorPngVisualizer 240 160 args[0] --outline=SEVERAL_BLUE + display := SeveralColorPixelDisplay driver + display.background = SEVERAL_BLUE + + win := ClippingDiv --x=30 --y=30 --w=180 --h=100 --background=SEVERAL_LIGHT_GRAY + display.add win + + text := Label --x=90 --y=55 --label="Hello, World!" --font=sans10 --color=SEVERAL_BLACK + win.add text + + display.draw + + text.move_to 120 65 + + display.draw + + // Window-relative coordinates. + text.move_to -10 7 + + display.draw + + driver.write_png diff --git a/tests/simple_three_color_window_visualized.toit b/tests/simple_three_color_window_visualized.toit new file mode 100644 index 0000000..114c101 --- /dev/null +++ b/tests/simple_three_color_window_visualized.toit @@ -0,0 +1,39 @@ +// 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 bitmap show * +import font show Font +import pixel_display show * +import pixel_display.element show * +import pixel_display.three_color show BLACK WHITE RED +import .png_visualizer + +main args: + sans10 := Font.get "sans10" + + if args.size != 1: + print "Usage: script.toit png-basename" + exit 1 + driver := ThreeColorPngVisualizer 240 160 args[0] --outline=RED + display := ThreeColorPixelDisplay driver + display.background = WHITE + + win := ClippingDiv --x=30 --y=30 --w=180 --h=100 --background=RED + display.add win + + text := Label --x=90 --y=55 --label="Hello, World!" --font=sans10 --color=BLACK + win.add text + + display.draw + + text.move_to 120 65 + + display.draw + + // Window-relative coordinates. + text.move_to -10 7 + + display.draw + + driver.write_png diff --git a/tests/simple_two_color_window_visualized.toit b/tests/simple_two_color_window_visualized.toit new file mode 100644 index 0000000..68c70f2 --- /dev/null +++ b/tests/simple_two_color_window_visualized.toit @@ -0,0 +1,39 @@ +// 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 bitmap show * +import font show Font +import pixel_display show * +import pixel_display.element show * +import pixel_display.two_color show BLACK WHITE +import .png_visualizer + +main args: + sans10 := Font.get "sans10" + + if args.size != 1: + print "Usage: script.toit png-basename" + exit 1 + driver := TwoColorPngVisualizer 240 160 args[0] --outline=BLACK + display := TwoColorPixelDisplay driver + display.background = BLACK + + win := ClippingDiv --x=30 --y=30 --w=180 --h=100 + display.add win + + text := Label --x=90 --y=55 --label="Hello, World!" --font=sans10 --color=BLACK + win.add text + + display.draw + + text.move_to 120 65 + + display.draw + + // Window-relative coordinates. + text.move_to -10 7 + + display.draw + + driver.write_png diff --git a/tests/text_texture_visualized.toit b/tests/text_texture_visualized.toit new file mode 100644 index 0000000..3993927 --- /dev/null +++ b/tests/text_texture_visualized.toit @@ -0,0 +1,49 @@ +// 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/text_visualized.toit b/tests/text_visualized.toit new file mode 100644 index 0000000..6b027d0 --- /dev/null +++ b/tests/text_visualized.toit @@ -0,0 +1,51 @@ +// 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 Label 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.element show * +import pixel_display.style 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" + + element_text := Label --x=30 --y=20 --color=SEVERAL_ORANGE --label="Testing 123" --font=sans10 + element_text_2 := Label --x=180 --y=50 --color=SEVERAL_ORANGE --label="123 Testing" --font=sans10 --alignment=ALIGN_RIGHT + element_text_3 := Label --x=96 --y=80 --color=SEVERAL_ORANGE --label="T 123 For the win" --font=sans10 --alignment=ALIGN_CENTER + display.add element_text + display.add element_text_2 + display.add element_text_3 + display.draw + + element_text.label = "Testing 42" + element_text_2.label = "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. + element_text_3.label = "T MM For the win" + display.draw + + element_text.label = "Test the West" + element_text_2.label = "Test the Folketing" + element_text_3.label = "Test * For the win" + display.draw + + element_text.label = "Test the West" + element_text_2.label = "Test the Folketlng" + element_text_3.label = "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 52abce8..5b921f6 100644 --- a/tests/texture_test_slow.toit +++ b/tests/texture_test_slow.toit @@ -175,7 +175,7 @@ test_simple_three_color: test_simple_four_gray: // A little 8x8 canvas to draw on. - canvas := three_color.Canvas_ 8 8 + canvas := four_gray.Canvas_ 8 8 // Fill the canvas with light gray. canvas.set_all_pixels four_gray.LIGHT_GRAY diff --git a/tests/true_color_portrait_visualized.toit b/tests/true_color_portrait_visualized.toit index e4fd874..050ae11 100644 --- a/tests/true_color_portrait_visualized.toit +++ b/tests/true_color_portrait_visualized.toit @@ -6,7 +6,7 @@ import expect show * import font show Font import pixel_display show * import pixel_display.true_color show * -import .png-visualizer +import .png_visualizer main args: if args.size != 1: diff --git a/tests/true_color_visualized.toit b/tests/true_color_visualized.toit index 9a0b575..aca7f72 100644 --- a/tests/true_color_visualized.toit +++ b/tests/true_color_visualized.toit @@ -6,7 +6,7 @@ import expect show * import font show Font import pixel_display show * import pixel_display.true_color show * -import .png-visualizer +import .png_visualizer main args: if args.size != 1: