Skip to content

Commit

Permalink
Get bitmaps working on truecolor.
Browse files Browse the repository at this point in the history
  • Loading branch information
Erik Corry committed Nov 10, 2023
1 parent 9d43b32 commit 244e5f8
Show file tree
Hide file tree
Showing 25 changed files with 252 additions and 41 deletions.
2 changes: 1 addition & 1 deletion src/common.toit
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ abstract class AbstractCanvas:
--alpha/ByteArray // 2-element byte array.
--palette/ByteArray // 6-element byte array.
--source_width/int // In pixels.
--orientation/int
--line_stride/int // In bytes.
TRANSFORM_IDENTITY_ ::= Transform.with_ [1, 0, 0, 1, 0, 0]
TRANSFORM_90_ ::= Transform.with_ [0, -1, 1, 0, 0, 0]
Expand Down
49 changes: 38 additions & 11 deletions src/element.toit
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import .bar_code_impl_
import font show Font
import math

import png_tools.png_reader show PngRandomAccess
import png_tools.png_reader show *

abstract class Element extends ElementOrTexture_:
x_ /int? := null
Expand Down Expand Up @@ -307,9 +307,9 @@ class GradientElement extends ResizableElement:
g = g[o .. o + h]
b = b[o .. o + h]
if canvas is true_color.Canvas:
(canvas as true_color.Canvas).draw_rgb_pixmap (i + x2) y3 --r=r --g=g --b=b --pixmap_width=h --orientation=orientation
(canvas as true_color.Canvas).rgb_pixmap (i + x2) y3 --r=r --g=g --b=b --source_width=h --orientation=orientation
else:
(canvas as one_byte.OneByteCanvas_).draw_pixmap (i + x2) y3 --pixels=b --pixmap_width=h --orientation=orientation
(canvas as one_byte.OneByteCanvas_).gray_pixmap (i + x2) y3 --pixels=b --source_width=h --orientation=orientation
offset += step
else:
// The gradient goes broadly horizontally, and we draw in horizontal strips.
Expand Down Expand Up @@ -348,9 +348,9 @@ class GradientElement extends ResizableElement:
g = g[o .. o + w]
b = b[o .. o + w]
if canvas is true_color.Canvas:
(canvas as true_color.Canvas).draw_rgb_pixmap x3 (i + y2) --r=r --g=g --b=b --pixmap_width=w --orientation=orientation
(canvas as true_color.Canvas).rgb_pixmap x3 (i + y2) --r=r --g=g --b=b --source_width=w --orientation=orientation
else:
(canvas as one_byte.OneByteCanvas_).draw_pixmap x3 (i + y2) --pixels=b --pixmap_width=w --orientation=orientation
(canvas as one_byte.OneByteCanvas_).gray_pixmap x3 (i + y2) --pixels=b --source_width=w --orientation=orientation
offset += step

class FilledRectangleElement extends RectangleElement:
Expand Down Expand Up @@ -1083,10 +1083,10 @@ class RoundedCornerWindowElement extends WindowElement:
if transparency_map is one_byte.OneByteCanvas_:
palette := opacity == 0xff ? #[] : shadow_palette_
draw_corners_ x2 y2 right bottom corner_radius_: | x y orientation |
transparency_map.draw_pixmap x y --pixels=opacities_ --palette=palette --pixmap_width=corner_radius_ --orientation=orientation
transparency_map.gray_pixmap x y --pixels=opacities_ --palette=palette --source_width=corner_radius_ --orientation=orientation
else:
draw_corners_ x2 y2 right bottom corner_radius_: | x y orientation |
transparency_map.draw_bitmap x y --pixels=bit_opacities_ --color=1 --pixmap_width=corner_radius_ --orientation=orientation
transparency_map.draw_bitmap x y --pixels=bit_opacities_ --color=1 --source_width=corner_radius_ --orientation=orientation

draw_corners_ left/int top/int right/int bottom/int corner_radius/int [block]:
// Top left corner:
Expand Down Expand Up @@ -1227,18 +1227,45 @@ class DropShadowWindowElement extends RoundedCornerWindowElement:
class PngElement extends CustomElement:
w/int
h/int
png_/PngRandomAccess
png_/AbstractPng

min_w: return w
min_h: return h

constructor --x/int?=null --y/int?=null png_file/ByteArray:
png_ = PngRandomAccess png_file
info := PngInfo png_file
if info.uncompressed_random_access:
png_ = PngRandomAccess png_file
else:
png_ = Png png_file
if png_.bit_depth > 8: throw "UNSUPPORTED"
if png_.color_type == COLOR_TYPE_TRUECOLOR or png_.color_type == COLOR_TYPE_TRUECOLOR_ALPHA: throw "UNSUPPORTED"
w = png_.width
h = png_.height
super --x=x --y=y

// Redraw routine.
draw canvas/AbstractCanvas:
if (canvas.bounds_analysis x y w h) == AbstractCanvas.ALL_OUTSIDE: return

y2 := 0
while y2 < h: // and (canvas.bounds_analysis x y w (h - y)) != AbstractCanvas.ALL_OUTSIDE:
png_.get_indexed_image_data y2: | y_from/int y_to/int bits_per_pixel/int pixels/ByteArray line_stride/int |
if bits_per_pixel == 1:
// Last line a little shorter because it has no stride padding.
adjust := line_stride - ((round_up w 8) >> 3)
pixels = pixels[0 .. (y_to - y_from) * line_stride - adjust]
canvas.bitmap x (y + y_from)
--pixels=pixels
--alpha=png_.alpha_palette
--palette=png_.palette
--source_width=w
--line_stride=line_stride
y2 = y_to
else:
adjust := line_stride - w
pixels = pixels[0 .. (y_to - y_from) * line_stride - adjust]
(canvas as true_color.Canvas).rgb_pixmap x (y + y_from) --r=pixels --g=pixels --b=pixels
--alpha=png_.alpha_palette
--palette=png_.palette
--source_width=w
--line_stride=line_stride
y2 = y_to
8 changes: 4 additions & 4 deletions src/one_byte.toit
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ class OneByteCanvas_ extends AbstractCanvas:
--alpha/ByteArray // 2-element byte array.
--palette/ByteArray // 6-element byte array.
--source_width/int // In pixels.
--orientation/int:
--line_stride/int: // In bytes.
throw "Not implemented"

draw_pixmap x/int y/int --pixels/ByteArray
gray_pixmap x/int y/int --pixels/ByteArray
--palette/ByteArray=#[]
--pixmap_width/int
--source_width/int
--orientation/int=ORIENTATION_0:
transform.xyo x y orientation: | x2 y2 o2 |
bitmap_draw_bytemap x2 y2 -1 o2 pixels pixmap_width palette pixels_ width_
bitmap_draw_bytemap x2 y2 -1 o2 pixels source_width palette pixels_ width_

class OneByteFilledRectangle_ extends FilledRectangle_:
color_ := ?
Expand Down
48 changes: 27 additions & 21 deletions src/true_color.toit
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class Canvas extends AbstractCanvas:
--alpha/ByteArray // 2-element byte array.
--palette/ByteArray // 6-element byte array.
--source_width/int // In pixels.
--orientation/int:
--line_stride/int: // In bytes.
source_byte_width := (source_width + 7) >> 3
zero_alpha := alpha[0]
// Fast case if the alpha is either 0 or 0xff, because we can use the
Expand All @@ -112,29 +112,35 @@ class Canvas extends AbstractCanvas:
// Draw the zeros.
rectangle x y --w=source_width --h=h --color=(BIG_ENDIAN.uint24 palette 0)
// Draw the ones.
transform.xyo x y orientation: | x2 y2 o2 |
bitmap_draw_bitmap x2 y2 palette[3] o2 pixels 0 source_width red_ width_ true
bitmap_draw_bitmap x2 y2 palette[4] o2 pixels 0 source_width green_ width_ true
bitmap_draw_bitmap x2 y2 palette[5] o2 pixels 0 source_width blue_ width_ true
transform.xyo x y 0: | x2 y2 orientation |
bitmap_draw_bitmap x2 y2 palette[3] orientation pixels 0 source_width line_stride red_ width_ true
bitmap_draw_bitmap x2 y2 palette[4] orientation pixels 0 source_width line_stride green_ width_ true
bitmap_draw_bitmap x2 y2 palette[5] orientation pixels 0 source_width line_stride blue_ width_ true
return
// Unfortunately one of the alpha values is not 0 or 0xff, so we can't use
// the bitmap draw primitive. We can blow it up to bytes, then use the
// bitmap-draw-bytemap.
// bitmap_draw_bytemap.
h := pixels.size / source_byte_width
bytemap := ByteArray source_width * h
bitmap_draw_bitmap 0 0 1 0 pixels 0 source_width bytemap source_width true
bitmap_draw_bitmap 0 0 1 0 pixels 0 source_width line_stride bytemap source_width true
transform.xyo x y 0: | x2 y2 orientation |
bitmap_draw_bytemap x2 y2 alpha orientation bytemap source_width source_width palette red_ width_
bitmap_draw_bytemap x2 y2 alpha orientation bytemap source_width source_width palette[1..] green_ width_
bitmap_draw_bytemap x2 y2 alpha orientation bytemap source_width source_width palette[2..] blue_ width_

rgb_pixmap x/int y/int --r/ByteArray --g/ByteArray --b/ByteArray
--alpha=-1
--palette/ByteArray?=null
--source_width/int
--orientation/int=ORIENTATION_0
--line_stride/int=source_width:
palette_r := palette ? palette : #[]
palette_g := palette ? palette[1..] : #[]
palette_b := palette ? palette[2..] : #[]
transform.xyo x y orientation: | x2 y2 o2 |
bitmap_draw_bytemap x2 y2 alpha o2 bytemap source_width palette red_ width_
bitmap_draw_bytemap x2 y2 alpha o2 bytemap source_width palette[1..] green_ width_
bitmap_draw_bytemap x2 y2 alpha o2 bytemap source_width palette[2..] blue_ width_

draw_rgb_pixmap x/int y/int --r/ByteArray --g/ByteArray --b/ByteArray
--pixmap_width/int
--orientation/int=ORIENTATION_0:
transform.xyo x y orientation: | x2 y2 o2 |
bitmap_draw_bytemap x2 y2 -1 o2 r pixmap_width #[] red_ width_
bitmap_draw_bytemap x2 y2 -1 o2 g pixmap_width #[] green_ width_
bitmap_draw_bytemap x2 y2 -1 o2 b pixmap_width #[] blue_ width_
bitmap_draw_bytemap x2 y2 alpha o2 r source_width line_stride palette_r red_ width_
bitmap_draw_bytemap x2 y2 alpha o2 g source_width line_stride palette_g green_ width_
bitmap_draw_bytemap x2 y2 alpha o2 b source_width line_stride palette_b blue_ width_

class FilledRectangle extends FilledRectangle_:
color_ := ?
Expand Down Expand Up @@ -348,9 +354,9 @@ class IndexedPixmapTexture extends PixmapTexture_:
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_
bitmap_draw_bytemap bx by 0 orientation bytes_ w w palette_ canvas.red_ canvas.width_
bitmap_draw_bytemap bx by 0 orientation bytes_ w w green_palette_ canvas.green_ canvas.width_
bitmap_draw_bytemap bx by 0 orientation bytes_ w w blue_palette_ canvas.blue_ canvas.width_

class BarCodeEan13 extends BarCodeEan13_:
constructor code/string x/int y/int transform/Transform:
Expand Down
2 changes: 1 addition & 1 deletion src/two_bit_texture.toit
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class TwoBitCanvas_ extends AbstractCanvas:
--alpha/ByteArray // 2-element byte array.
--palette/ByteArray // 6-element byte array.
--source_width/int // In pixels.
--orientation/int:
--line_stride/int: // In bytes.
throw "Not implemented"


Expand Down
6 changes: 3 additions & 3 deletions src/two_color.toit
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,18 @@ class Canvas extends AbstractCanvas:
--alpha/ByteArray // 2-element byte array.
--palette/ByteArray // 6-element byte array.
--source_width/int // In pixels.
--orientation/int:
--line_stride/int: // In bytes.
throw "Not implemented"

draw_bitmap x/int y/int
--pixels/ByteArray
--color/int
--pixmap_width/int
--source_width/int
--orientation/int:
transform.xyo x y orientation: | x2 y2 o2 |
bytewise := false
offset := 0
bitmap_draw_bitmap x2 y2 1 o2 pixels offset pixmap_width pixels_ width_ bytewise
bitmap_draw_bitmap x2 y2 1 o2 pixels offset source_width pixels_ width_ bytewise

class FilledRectangle extends FilledRectangle_:
color_ := ?
Expand Down
55 changes: 55 additions & 0 deletions tests/bitmap_2_visualized.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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

import host.file
import pixel_display show *
import pixel_display.element show *
import .png_visualizer

SANS := font.Font.get "sans10"

main args:
if args.size != 1:
print "Usage: script.toit png-basename"
exit 1

basename := args[0]

driver := TrueColorPngVisualizer 440 240 basename --outline=0xffffff
display := TrueColorPixelDisplay driver
display.background = 0xe0e080

heater := file.read_content "tests/third_party/pictogrammers/heater.png"
heater-uncompressed := file.read_content "tests/third_party/pictogrammers/heater-uncompressed.png"
heater-4-bit := file.read_content "tests/third_party/pictogrammers/heater-4-bit.png"
heater-4-bit-uncompressed := file.read_content "tests/third_party/pictogrammers/heater-4-bit-uncompressed.png"
heater-2-bit := file.read_content "tests/third_party/pictogrammers/heater-2-bit.png"
heater-2-bit-uncompressed := file.read_content "tests/third_party/pictogrammers/heater-2-bit-uncompressed.png"
heater-bw := file.read_content "tests/third_party/pictogrammers/heater-bw.png"
heater-bw-uncompressed := file.read_content "tests/third_party/pictogrammers/heater-bw-uncompressed.png"
heater-white-bg := file.read_content "tests/third_party/pictogrammers/heater-white-bg.png"
heater-white-bg-uncompressed := file.read_content "tests/third_party/pictogrammers/heater-white-bg-uncompressed.png"

display.add (PngElement --x=16 --y=32 heater)
display.add (PngElement --x=100 --y=32 heater-4-bit)
display.add (PngElement --x=184 --y=32 heater-2-bit)
display.add (PngElement --x=268 --y=32 heater-bw)
display.add (PngElement --x=358 --y=32 heater-white-bg)

display.add (PngElement --x=16 --y=120 heater-uncompressed)
display.add (PngElement --x=100 --y=120 heater-4-bit-uncompressed)
display.add (PngElement --x=184 --y=120 heater-2-bit-uncompressed)
display.add (PngElement --x=268 --y=120 heater-bw-uncompressed)
display.add (PngElement --x=358 --y=120 heater-white-bg-uncompressed)

display.draw

driver.write_png
60 changes: 60 additions & 0 deletions tests/bitmap_visualized.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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

import host.file
import pixel_display show *
import pixel_display.element show *
import .png_visualizer

SANS := font.Font.get "sans10"

main args:
if args.size != 1:
print "Usage: script.toit png-basename"
exit 1

do args[0] 340 320
do "$args[0]-rotated" 320 340

do basename/string w/int h/int:
driver := TrueColorPngVisualizer w h basename --outline=0xc0c0ff
display := TrueColorPixelDisplay driver
display.background = 0x808080

// A 1-bit PNG file that is uncompressed, so we can use the PngRandomAccess
// class to display it straight from flash.
purifier := file.read_content "tests/third_party/pictogrammers/air-purifier-bit-unzip.png"
// A 1-bit PNG file that is compressed, so it takes less flash, but we have
// to decompress it to display it. This file also has an almost-transparent
// background, so we test the code path where we display a 1-bit image with
// a real alpha channel (and it has a gray background in the output).
purifier-compressed := file.read_content "tests/third_party/pictogrammers/air-purifier-bit.png"

gradient := GradientElement --x=0 --y=0 --angle=160 --w=340 --h=320 --specifiers=[
GradientSpecifier --color=0xe0e0ff 10,
GradientSpecifier --color=0x8080c0 90,
]
display.add gradient

display.draw

label := Label --x=44 --y=44 --label="UP ^" --font=SANS --color=0
display.add label
png-element := PngElement --x=36 --y=32 purifier
display.add png-element
display.draw

display.remove png-element
png-element = PngElement --x=36 --y=32 purifier-compressed
display.add png-element
display.draw

driver.write_png
Binary file added tests/gold/bitmap_2_visualized.toit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/gold/bitmap_visualized.toit-rotated.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/gold/bitmap_visualized.toit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions tests/third_party/pictogrammers/COPYING
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Pictogrammers Free License
--------------------------

Last Updated: February 1st, 2023

This package is released as free, open-source, and GPL friendly by
the [Pictogrammers](https://pictogrammers.com/). You may use it
for commercial projects, open-source projects, or anything really.

# Icons: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
Some of the icons are redistributed under the Apache 2.0 license. All other
icons are either redistributed under their respective licenses or are
distributed under the Apache 2.0 license.

# Fonts: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
All web and desktop fonts are distributed under the Apache 2.0 license. Web
and desktop fonts contain some icons that are redistributed under the Apache
2.0 license. All other icons are either redistributed under their respective
licenses or are distributed under the Apache 2.0 license.

# Code: MIT (https://opensource.org/licenses/MIT)
The MIT license applies to all non-font and non-icon files.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/third_party/pictogrammers/air-purifier-bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/third_party/pictogrammers/heater-2-bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/third_party/pictogrammers/heater-4-bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/third_party/pictogrammers/heater-bw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/third_party/pictogrammers/heater-white-bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/third_party/pictogrammers/heater.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 244e5f8

Please sign in to comment.