{"id":"drawing-and-images-canvas","slug":"canvas","title":"Drawing on a canvas","text":"Module 51 drew charts, and JavaFX decided where each line and bar went. This module draws by itself. A **canvas** is an empty area of a window that a program draws on: rectangles, ovals and lines, wherever it wants them and in the colors it picks. Then come **images**: pictures from files, read and changed one pixel at a time. Sound comes at the end.\n\n### Drawings and images in Java Arena\n\nAs with the windows of part 13 (module 48 explained it) and the charts of module 51, Java Arena's JavaFX is a practice version written for this site, and the page draws the window from what your program told JavaFX. A canvas here keeps a list of what the program drew on it, in order and with the colors, and the page draws that list for you, approximately: real JavaFX smooths the edges of shapes in its own way. Under each drawing, \"The window as text\" lists every call, and that list is what the challenges compare. The check that runs every model solution on real JavaFX (module 48) also draws each canvas's recorded calls again on real JavaFX and compares the result with the real canvas, pixel by pixel, and it compares every pixel of each image. What it doesn't check is how this page draws the list.\n\nImages are different: they're read and written **pixel by pixel, exactly**, and the challenges compare every pixel. The images that this module's programs read are small pictures that Java Arena makes for its lessons. Sound can't play on this page at all: step 7 shows sound code to read, and the MOOC's exercise with sound needs your own computer.\n\n### A canvas and its pen\n\nA rowing club (made up for this lesson) wants its flag on a screen:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(150, 100);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n        pen.setFill(Color.NAVY);\n        pen.fillRect(0, 0, 150, 100);\n        pen.setFill(Color.WHITE);\n        pen.fillRect(0, 40, 150, 20);\n        pen.setFill(Color.GOLD);\n        pen.fillOval(55, 30, 40, 40);\n\n        stage.setScene(new Scene(new Pane(canvas)));\n        stage.setTitle(\"Rowing club\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Rowing club\",\"scene\":{\"root\":{\"n\":1,\"type\":\"Pane\",\"children\":[{\"n\":2,\"type\":\"Canvas\",\"width\":150,\"height\":100,\"calls\":[{\"op\":\"fillRect\",\"args\":[0,0,150,100],\"fill\":\"#000080\"},{\"op\":\"fillRect\",\"args\":[0,40,150,20],\"fill\":\"#ffffff\"},{\"op\":\"fillOval\",\"args\":[55,30,40,40],\"fill\":\"#ffd700\"}]}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Rowing club\\\"\\n  Pane\\n    Canvas 150 x 100\\n      fillRect 0 0 150 100, fill #000080\\n      fillRect 0 40 150 20, fill #ffffff\\n      fillOval 55 30 40 40, fill #ffd700\"}\n```\n\n- `new Canvas(150, 100)` is a canvas 150 pixels wide and 100 pixels high. It's a node, like a label, so it goes into a layout. `Pane` (from `javafx.scene.layout`) is the plainest layout: it shows its children and doesn't arrange them, and a canvas needs nothing more.\n- `canvas.getGraphicsContext2D()` returns the canvas's **GraphicsContext**: the object that draws on it, so the code calls it `pen`. Each canvas has one, and the method always returns the same object.\n- `setFill(Color.NAVY)` picks the color for the drawing that follows. The pen keeps it until the next `setFill`.\n- `fillRect(x, y, width, height)` fills a rectangle whose top left corner is at (x, y).\n- `fillOval(x, y, width, height)` fills the oval that just fits inside that same rectangle. A width equal to the height gives a circle: here one 40 pixels across.\n\n### Where the pixels are\n\nEvery point of the canvas has an **x** and a **y**, counted in pixels from the **top left corner**, which is (0, 0). x grows to the right, as in a math class, but y grows **down**: (0, 40) is 40 pixels below the corner. So the white stripe starts 40 pixels from the top and is 20 high, and the flag's last row of pixels is at y 99.\n\nAn oval is given by its rectangle, not by its center. A circle with its center at (75, 50) and a radius of 20 fits in the square from (55, 30), 40 wide and 40 high: `fillOval(75 - 20, 50 - 20, 40, 40)`. That's the gold circle, in the middle of the flag.\n\n**Order matters.** Each call draws over what's already there, like paint. The navy rectangle comes first and covers the whole canvas, the stripe goes over it, and the circle over the stripe. With the navy rectangle drawn last, the flag would be all navy.\n\n### The window as text\n\nFor a canvas, \"The window as text\" lists every drawing call, in order, with its numbers and the color it used:\n\n```text\nWindow \"Rowing club\"\n  Pane\n    Canvas 150 x 100\n      fillRect 0 0 150 100, fill #000080\n      fillRect 0 40 150 20, fill #ffffff\n      fillOval 55 30 40 40, fill #ffd700\n```\n\nColors are written as `#` and six hexadecimal digits: two for red, two for green and two for blue, from `00` (none) to `ff` (all). `#000080` is navy: no red, no green, half the blue. The challenges compare these lines exactly, so a picture that looks the same but is drawn with other calls doesn't pass: a task always says which shapes to draw, where, in which colors and in what order.\n\n### Colors\n\nThe class `Color` (from `javafx.scene.paint`) gives colors in four ways:\n\n- By name: `Color.RED`, `Color.NAVY`, `Color.GOLD`, `Color.DARKGREEN`. JavaFX has 147 of the color names that web pages use, plus `Color.TRANSPARENT`.\n- By their amounts of red, green and blue, each from 0 to 255: `Color.rgb(255, 128, 0)` is orange (all the red, half the green, no blue). Screens mix light, so `Color.rgb(255, 255, 0)` is yellow, `Color.rgb(0, 0, 0)` black and `Color.rgb(255, 255, 255)` white.\n- By the code that web pages use: `Color.web(\"#e63946\")`.\n- With `new Color(red, green, blue, opacity)`, each amount a double from 0.0 to 1.0: `new Color(1.0, 0.5, 0.0, 1.0)` is orange too. An amount outside that range throws an IllegalArgumentException.\n\nPrinting a color shows its code with its **opacity** at the end: `System.out.println(Color.rgb(255, 128, 0))` prints `0xff8000ff`, where the last `ff` means fully opaque.\n\n### Lines and outlines\n\nNext to the fill color, the pen has a **stroke** color for lines and outlines, and a line width. A cottage with a window:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(160, 140);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n        pen.setFill(Color.BURLYWOOD);\n        pen.fillRect(30, 60, 100, 70);\n        pen.setFill(Color.FIREBRICK);\n        double[] roofX = {20, 80, 140};\n        double[] roofY = {60, 15, 60};\n        pen.fillPolygon(roofX, roofY, 3);\n        pen.setFill(Color.SADDLEBROWN);\n        pen.fillRect(90, 90, 20, 40);\n\n        pen.setStroke(Color.BLACK);\n        pen.setLineWidth(2);\n        pen.strokeRect(45, 75, 24, 24);\n        pen.strokeLine(57, 75, 57, 99);\n        pen.strokeLine(45, 87, 69, 87);\n\n        stage.setScene(new Scene(new Pane(canvas)));\n        stage.setTitle(\"Cottage\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Cottage\",\"scene\":{\"root\":{\"n\":1,\"type\":\"Pane\",\"children\":[{\"n\":2,\"type\":\"Canvas\",\"width\":160,\"height\":140,\"calls\":[{\"op\":\"fillRect\",\"args\":[30,60,100,70],\"fill\":\"#deb887\"},{\"op\":\"fillPolygon\",\"xs\":[20,80,140],\"ys\":[60,15,60],\"fill\":\"#b22222\"},{\"op\":\"fillRect\",\"args\":[90,90,20,40],\"fill\":\"#8b4513\"},{\"op\":\"strokeRect\",\"args\":[45,75,24,24],\"stroke\":\"#000000\",\"lineWidth\":2},{\"op\":\"strokeLine\",\"args\":[57,75,57,99],\"stroke\":\"#000000\",\"lineWidth\":2},{\"op\":\"strokeLine\",\"args\":[45,87,69,87],\"stroke\":\"#000000\",\"lineWidth\":2}]}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Cottage\\\"\\n  Pane\\n    Canvas 160 x 140\\n      fillRect 30 60 100 70, fill #deb887\\n      fillPolygon (20, 60) (80, 15) (140, 60), fill #b22222\\n      fillRect 90 90 20 40, fill #8b4513\\n      strokeRect 45 75 24 24, stroke #000000, width 2\\n      strokeLine 57 75 57 99, stroke #000000, width 2\\n      strokeLine 45 87 69 87, stroke #000000, width 2\"}\n```\n\n- `setStroke(Color.BLACK)` and `setLineWidth(2)` set the color and the width (in pixels) of what the stroke methods draw. A new pen strokes in black, 1 pixel wide, and fills in black too.\n- `strokeRect(x, y, width, height)` draws only the outline of a rectangle, and `strokeOval` the outline of an oval.\n- `strokeLine(x1, y1, x2, y2)` draws a line from (x1, y1) to (x2, y2). The two lines here make the cross of the window.\n- `fillPolygon(xs, ys, count)` fills a shape with corners: the first `count` values of the array `xs` are the corners' x values, and those of `ys` their y values, in the same order. The roof is a triangle with corners (20, 60), (80, 15) and (140, 60). `strokePolygon` draws its outline.\n\nIn the window as text, a stroke call shows its stroke color and width (`strokeLine 57 75 57 99, stroke #000000, width 2`), and the polygon its corners.\n\n### Shapes in a loop\n\nThe numbers can be computed, so a loop can draw a row of shapes. A weather map shows a scale of six squares, from yellow for warm to red for hot:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(180, 30);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n        for (int i = 0; i < 6; i++) {\n            pen.setFill(Color.rgb(255, 220 - i * 40, 0));\n            pen.fillRect(i * 30, 0, 30, 30);\n        }\n\n        stage.setScene(new Scene(new Pane(canvas)));\n        stage.setTitle(\"Heat scale\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Heat scale\",\"scene\":{\"root\":{\"n\":1,\"type\":\"Pane\",\"children\":[{\"n\":2,\"type\":\"Canvas\",\"width\":180,\"height\":30,\"calls\":[{\"op\":\"fillRect\",\"args\":[0,0,30,30],\"fill\":\"#ffdc00\"},{\"op\":\"fillRect\",\"args\":[30,0,30,30],\"fill\":\"#ffb400\"},{\"op\":\"fillRect\",\"args\":[60,0,30,30],\"fill\":\"#ff8c00\"},{\"op\":\"fillRect\",\"args\":[90,0,30,30],\"fill\":\"#ff6400\"},{\"op\":\"fillRect\",\"args\":[120,0,30,30],\"fill\":\"#ff3c00\"},{\"op\":\"fillRect\",\"args\":[150,0,30,30],\"fill\":\"#ff1400\"}]}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Heat scale\\\"\\n  Pane\\n    Canvas 180 x 30\\n      fillRect 0 0 30 30, fill #ffdc00\\n      fillRect 30 0 30 30, fill #ffb400\\n      fillRect 60 0 30 30, fill #ff8c00\\n      fillRect 90 0 30 30, fill #ff6400\\n      fillRect 120 0 30 30, fill #ff3c00\\n      fillRect 150 0 30 30, fill #ff1400\"}\n```\n\nSquare `i` starts at x `i * 30`, right where the one before it ends, and each square has 40 less green than the one before, which turns yellow into red step by step.\n","task":"A hiking club's flag, 120 pixels wide and 80 high. Fill in the blanks so that it draws, in this order: a dark green background (`Color.DARKGREEN`) over the whole canvas, a white stripe 20 pixels wide from x 30 down the whole height, and a black circle 30 pixels across whose center is at (90, 40). The window it should show is drawn below.\n","kind":"fill","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(120, 80);\n        GraphicsContext pen = canvas.[[getGraphicsContext2D]]();\n        pen.setFill(Color.DARKGREEN);\n        pen.[[fillRect]](0, 0, 120, 80);\n        pen.setFill(Color.WHITE);\n        pen.fillRect(30, 0, 20, 80);\n        pen.setFill(Color.BLACK);\n        pen.fillOval([[75‖90 - 15]], [[25‖40 - 15]], 30, 30);\n\n        stage.setScene(new Scene(new Pane(canvas)));\n        stage.setTitle(\"Hiking club\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(120, 80);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n        pen.setFill(Color.DARKGREEN);\n        pen.fillRect(0, 0, 120, 80);\n        pen.setFill(Color.WHITE);\n        pen.fillRect(30, 0, 20, 80);\n        pen.setFill(Color.BLACK);\n        pen.fillOval(75, 25, 30, 30);\n\n        stage.setScene(new Scene(new Pane(canvas)));\n        stage.setTitle(\"Hiking club\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["The canvas's GraphicsContext comes from `canvas.getGraphicsContext2D()`, and a filled rectangle is `fillRect(x, y, width, height)`.","`fillOval` takes the top left corner of the square around the circle. The circle is 30 across, so its radius is 15: the corner is at (90 - 15, 40 - 15)."],"tests":[{"name":"Window","stdin":"","expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Hiking club","scene":{"root":{"n":1,"type":"Pane","children":[{"n":2,"type":"Canvas","width":120,"height":80,"calls":[{"op":"fillRect","args":[0,0,120,80],"fill":"#006400"},{"op":"fillRect","args":[30,0,20,80],"fill":"#ffffff"},{"op":"fillOval","args":[75,25,30,30],"fill":"#000000"}]}]}}}],"problems":[]},"outline":"Window \"Hiking club\"\n  Pane\n    Canvas 120 x 80\n      fillRect 0 0 120 80, fill #006400\n      fillRect 30 0 20 80, fill #ffffff\n      fillOval 75 25 30 30, fill #000000"}],"require":[],"forbid":[],"more":[{"task":"A road sign: no entry. Draw it on the canvas of 120 x 160 pixels that the starter code makes, with these calls in this order:\n\n1. a gray post (`Color.GRAY`): a filled rectangle 10 wide and 110 high, from (55, 50)\n2. the sign: a red (`Color.RED`) filled circle 100 pixels across, in the square from (10, 0)\n3. a white (`Color.WHITE`) filled bar 70 wide and 20 high, from (25, 40)\n4. the sign's edge: the outline of the same circle, in dark red (`Color.DARKRED`) and 3 pixels wide\n\nThe post starts in the middle of the sign, so the circle covers its top. Keep the window's title and the Pane. The window it should show is drawn below.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(120, 160);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n\n        stage.setScene(new Scene(new Pane(canvas)));\n        stage.setTitle(\"No entry\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(120, 160);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n        pen.setFill(Color.GRAY);\n        pen.fillRect(55, 50, 10, 110);\n        pen.setFill(Color.RED);\n        pen.fillOval(10, 0, 100, 100);\n        pen.setFill(Color.WHITE);\n        pen.fillRect(25, 40, 70, 20);\n        pen.setStroke(Color.DARKRED);\n        pen.setLineWidth(3);\n        pen.strokeOval(10, 0, 100, 100);\n\n        stage.setScene(new Scene(new Pane(canvas)));\n        stage.setTitle(\"No entry\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Before each filled shape, `pen.setFill(...)` picks its color. The circle is `fillOval(10, 0, 100, 100)`.","The edge uses the stroke color and width: `pen.setStroke(Color.DARKRED);`, `pen.setLineWidth(3);`, then `pen.strokeOval(10, 0, 100, 100);`."],"tests":[{"name":"Window","stdin":"","expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"No entry","scene":{"root":{"n":1,"type":"Pane","children":[{"n":2,"type":"Canvas","width":120,"height":160,"calls":[{"op":"fillRect","args":[55,50,10,110],"fill":"#808080"},{"op":"fillOval","args":[10,0,100,100],"fill":"#ff0000"},{"op":"fillRect","args":[25,40,70,20],"fill":"#ffffff"},{"op":"strokeOval","args":[10,0,100,100],"stroke":"#8b0000","lineWidth":3}]}]}}}],"problems":[]},"outline":"Window \"No entry\"\n  Pane\n    Canvas 120 x 160\n      fillRect 55 50 10 110, fill #808080\n      fillOval 10 0 100 100, fill #ff0000\n      fillRect 25 40 70 20, fill #ffffff\n      strokeOval 10 0 100 100, stroke #8b0000, width 3"}],"require":[],"forbid":[]},{"task":"A paint shop prints cards of color samples. The file `colors.txt` holds one web color code on each line, such as `#e63946`. Show them on a canvas 300 pixels wide and 60 high: for the color on line `i` of the file (counting from 0), a filled square 40 x 40 with its top left corner at x `10 + 50 * i` and y 10, in the color `Color.web(code)`. Under the canvas, in a VBox with no spacing, a label says how many colors there are: `Colors: 3`. The window's title is `Color card`.\n\nFor the file below, the window should look like the drawing under it.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Canvas canvas = new Canvas(300, 60);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n\n        stage.setScene(new Scene(new VBox(canvas)));\n        stage.setTitle(\"Color card\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        Canvas canvas = new Canvas(300, 60);\n        GraphicsContext pen = canvas.getGraphicsContext2D();\n        List<String> codes = Files.readAllLines(Paths.get(\"colors.txt\"));\n        for (int i = 0; i < codes.size(); i++) {\n            pen.setFill(Color.web(codes.get(i)));\n            pen.fillRect(10 + 50 * i, 10, 40, 40);\n        }\n        Label count = new Label(\"Colors: \" + codes.size());\n\n        stage.setScene(new Scene(new VBox(canvas, count)));\n        stage.setTitle(\"Color card\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Read the lines with `Files.readAllLines(Paths.get(\"colors.txt\"))` (and add `throws Exception` to `start`). A loop with an index `i` over the lines gives each square its place, `10 + 50 * i`.","In the loop: `pen.setFill(Color.web(codes.get(i)));` and then `pen.fillRect(10 + 50 * i, 10, 40, 40);`. After it, make the label and put both in the VBox: `new VBox(canvas, count)`."],"tests":[{"name":"Test 1","stdin":"","files":{"colors.txt":"#e63946\n#f1c453\n#2a9d8f\n"},"expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Color card","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Canvas","width":300,"height":60,"calls":[{"op":"fillRect","args":[10,10,40,40],"fill":"#e63946"},{"op":"fillRect","args":[60,10,40,40],"fill":"#f1c453"},{"op":"fillRect","args":[110,10,40,40],"fill":"#2a9d8f"}]},{"n":3,"type":"Label","text":"Colors: 3"}]}}}],"problems":[]},"outline":"Window \"Color card\"\n  VBox\n    Canvas 300 x 60\n      fillRect 10 10 40 40, fill #e63946\n      fillRect 60 10 40 40, fill #f1c453\n      fillRect 110 10 40 40, fill #2a9d8f\n    Label \"Colors: 3\""},{"name":"Test 2","stdin":"","files":{"colors.txt":"#264653\n#8ab17d\n#e9c46a\n#f4a261\n#e76f51\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Color card","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Canvas","width":300,"height":60,"calls":[{"op":"fillRect","args":[10,10,40,40],"fill":"#264653"},{"op":"fillRect","args":[60,10,40,40],"fill":"#8ab17d"},{"op":"fillRect","args":[110,10,40,40],"fill":"#e9c46a"},{"op":"fillRect","args":[160,10,40,40],"fill":"#f4a261"},{"op":"fillRect","args":[210,10,40,40],"fill":"#e76f51"}]},{"n":3,"type":"Label","text":"Colors: 5"}]}}}],"problems":[]},"outline":"Window \"Color card\"\n  VBox\n    Canvas 300 x 60\n      fillRect 10 10 40 40, fill #264653\n      fillRect 60 10 40 40, fill #8ab17d\n      fillRect 110 10 40 40, fill #e9c46a\n      fillRect 160 10 40 40, fill #f4a261\n      fillRect 210 10 40 40, fill #e76f51\n    Label \"Colors: 5\""},{"name":"Test 3","stdin":"","files":{"colors.txt":"#6a4c93\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Color card","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Canvas","width":300,"height":60,"calls":[{"op":"fillRect","args":[10,10,40,40],"fill":"#6a4c93"}]},{"n":3,"type":"Label","text":"Colors: 1"}]}}}],"problems":[]},"outline":"Window \"Color card\"\n  VBox\n    Canvas 300 x 60\n      fillRect 10 10 40 40, fill #6a4c93\n    Label \"Colors: 1\""}],"require":[],"forbid":[]}]}