Skip to content

Commit

Permalink
Support custom transformations for the abstract driver. (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
floitsch authored Sep 24, 2024
1 parent defbeff commit 0afbfb5
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 41 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/build/
out/
.packages/
*.swp
*.swo
.packages
20 changes: 12 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@ all: test

.PHONY: build/host/CMakeCache.txt
build/CMakeCache.txt:
$(MAKE) rebuild-cmake
@$(MAKE) rebuild-cmake

.PHONY: install-pkgs
install-pkgs: rebuild-cmake
(cd build && ninja install-pkgs)
(cd tests/toit-png-tools && $(MAKE) rebuild-cmake)
(cd tests/toit-png-tools && cmake -DTOITRUN:FILEPATH="$${TOITRUN:-toit.run}" -DTOITPKG:FILEPATH="$${TOITPKG:-toit.pkg}" build)
(cd tests/toit-png-tools && $(MAKE) install-pkgs)
@(cd build && ninja install-pkgs)
@[ -f tests/toit-png-tools/Makefile ] || \
(echo "The 'tests/toit-png-tools/' directory doesn't contain a makefile." && \
echo "Run 'git submodule update --init' to get the submodule." && \
exit 1)
@(cd tests/toit-png-tools && $(MAKE) rebuild-cmake)
@(cd tests/toit-png-tools && cmake -DTOITRUN:FILEPATH="$${TOITRUN:-toit.run}" -DTOITPKG:FILEPATH="$${TOITPKG:-toit.pkg}" build)
@(cd tests/toit-png-tools && $(MAKE) install-pkgs)

test: install-pkgs rebuild-cmake
(cd build && ninja check)
@(cd build && ninja check)

# We rebuild the cmake file all the time.
# We use "glob" in the cmakefile, and wouldn't otherwise notice if a new
# file (for example a test) was added or removed.
# It takes <1s on Linux to run cmake, so it doesn't hurt to run it frequently.
rebuild-cmake:
mkdir -p build
(cd build && cmake .. -G Ninja)
@mkdir -p build
@(cd build && cmake .. -G Ninja)

.PHONY: all test rebuild-cmake install-pkgs
2 changes: 1 addition & 1 deletion package.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk: ^2.0.0-alpha.121
sdk: ^2.0.0-alpha.144
prefixes:
png-tools: toit-png-tools
packages:
Expand Down
72 changes: 45 additions & 27 deletions src/pixel-display-impl_.toit
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ FLAG-PARTIAL-UPDATES ::= 0b1000000
/**
Abstract superclass for all pixel display drivers.
For example, implemented by the drivers in
https://pkg.toit.io/package/color_tft&url=github.com%2Ftoitware%2Ftoit-color-tft&index=latest
https://pkg.toit.io/package/github.com%2Ftoitware%2Ftoit-color-tft
and
https://pkg.toit.io/package/ssd1306&url=github.com%2Ftoitware%2Ftoit-ssd1306&index=latest
https://pkg.toit.io/package/github.com%2Ftoitware%2Ftoit-ssd1306
*/
abstract class AbstractDriver:
abstract width -> int
Expand All @@ -60,6 +60,31 @@ abstract class AbstractDriver:
throw "Not a true-color driver"
close -> none:

/**
The transform that should be used depending on whether $inverted and $portrait is set.
If the parameter $portrait is set to null the driver is free to choose its default orientation.
*/
base-transform --inverted/bool --portrait/bool? -> Transform:
transform_/Transform := ?
rotation := 0
if portrait != null:
if portrait == (width < height):
rotation = inverted ? 180 : 0
else:
rotation = inverted ? 270 : 90
else if inverted:
rotation = 180
if rotation == 0:
transform_ = Transform.identity
else if rotation == 90:
transform_ = (Transform.identity.translate 0 height).rotate-left
else if rotation == 180:
transform_ = (Transform.identity.translate width height).rotate-left.rotate-left
else:
transform_ = (Transform.identity.translate width 0).rotate-right
return transform_

/**
Common code for pixel-based displays connected to devices.
Height and width must be multiples of 8.
Expand Down Expand Up @@ -219,26 +244,9 @@ abstract class PixelDisplay implements Window:
dirty-strips := (height >> 6) + 1 // 8-tall strips of dirty bits.
dirty_ = ByteArray dirty-bytes-per-line_ * dirty-strips

transform_ = driver_.base-transform --inverted=inverted --portrait=portrait
if transform:
if portrait != null or inverted: throw "INVALID_ARGUMENT"
transform_ = transform
else:
rotation := 0
if portrait != null:
if portrait == (driver_.width < driver_.height):
rotation = inverted ? 180 : 0
else:
rotation = inverted ? 270 : 90
else if inverted:
rotation = 180
if rotation == 0:
transform_ = Transform.identity
else if rotation == 90:
transform_ = (Transform.identity.translate 0 driver_.height).rotate-left
else if rotation == 180:
transform_ = (Transform.identity.translate driver_.width driver_.height).rotate-left.rotate-left
else:
transform_ = (Transform.identity.translate driver_.width 0).rotate-right
transform_ = transform_.apply transform

if driver_.flags & FLAG-PARTIAL-UPDATES != 0:
all-is-dirty_
Expand Down Expand Up @@ -1032,7 +1040,7 @@ abstract class Canvas:
--source-width/int
--orientation/int=ORIENTATION-0
--source-line-stride/int=source-width:
throw "Unimplemented"
throw "Unimplemented"

/**
Draws the given 24-bit pixmap on the canvas.
Expand All @@ -1053,10 +1061,10 @@ abstract class Canvas:
--orientation/int=ORIENTATION-0:
throw "UNSUPPORTED" // Only on true color canvas.
TRANSFORM-IDENTITY_ ::= Transform.with_ --x1=1 --x2=0 --y1=0 --y2=1 --tx=0 --ty=0
TRANSFORM-90_ ::= Transform.with_ --x1=0 --x2=-1 --y1=1 --y2=0 --tx=0 --ty=0
TRANSFORM-180_ ::= Transform.with_ --x1=-1 --x2=0 --y1=0 --y2=-1 --tx=0 --ty=0
TRANSFORM-270_ ::= Transform.with_ --x1=0 --x2=1 --y1=-1 --y2=0 --tx=0 --ty=0
TRANSFORM-IDENTITY_ ::= Transform --x1=1 --x2=0 --y1=0 --y2=1 --tx=0 --ty=0
TRANSFORM-90_ ::= Transform --x1=0 --x2=-1 --y1=1 --y2=0 --tx=0 --ty=0
TRANSFORM-180_ ::= Transform --x1=-1 --x2=0 --y1=0 --y2=-1 --tx=0 --ty=0
TRANSFORM-270_ ::= Transform --x1=0 --x2=1 --y1=-1 --y2=0 --tx=0 --ty=0

/**
Classic 3x3 matrix for 2D transformations.
Expand All @@ -1077,14 +1085,21 @@ class Transform:
constructor.identity:
return TRANSFORM-IDENTITY_

constructor.with_ --x1/int --y1/int --x2/int --y2/int --tx/int --ty/int:
constructor --x1/int --y1/int --x2/int --y2/int --tx/int --ty/int:
if x1 * y2 - y1 * x2 != 1:
throw "Unsupported scaling"
x1_ = x1
x2_ = x2
tx_ = tx
y1_ = y1
y2_ = y2
ty_ = ty

constructor.with_ --x1/int --y1/int --x2/int --y2/int --tx/int --ty/int:
if x1 == 1 and y1 == 0 and x2 == 0 and y2 == 1 and tx == 0 and ty == 0:
return TRANSFORM-IDENTITY_
return Transform --x1=x1 --y1=y1 --x2=x2 --y2=y2 --tx=tx --ty=ty

/**
Applies the other transform to this transform.
Returns a transform that does both transformations in one transform.
Expand Down Expand Up @@ -1253,6 +1268,9 @@ class Transform:
height width/int height/int -> int:
return width * y1_ + height * y2_

stringify -> string:
return "Transform x1=$x1_ y1=$y1_ x2=$x2_ y2=$y2_ tx=$tx_ ty=$ty_"

class TextExtent_:
x := 0
y := 0
Expand Down
2 changes: 1 addition & 1 deletion src/png.toit
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Element that draws a PNG image.
The width and height of the element is determined by the image.
The image is given by a byte array, png-file, which must be a valid PNG file.
The PNG file can be converted to a Toit byte array using the
xxxd tool (https://github.com/toitware/xxxd).
xxxd tool (https://github.com/toitware/xxxd).
The PNG file can be compressed (a normal PNG file) or uncompressed.
Uncompressed PNG files are produced by pngunzip, which is part of
the releases of the png-tools package at
Expand Down
9 changes: 6 additions & 3 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ set(TOITPKG toit.pkg${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "The executable us
set(TEST_TIMEOUT 40 CACHE STRING "The maximal amount of time each test is allowed to run")
set(SLOW_TEST_TIMEOUT 200 CACHE STRING "The maximal amount of time each slow test is allowed to run")

message("TPKG: ${TOITPKG}")
add_custom_target(
"install-pkgs"
COMMAND "${TOITPKG}" install
Expand All @@ -29,8 +28,12 @@ add_custom_target(
set(TEST_PREFIX "")
include(fail.cmake OPTIONAL)

message("Failing tests: ${FAILING_TESTS}")
message("Skipped tests: ${SKIP_TESTS}")
if (FAILING_TESTS)
message("Failing tests: ${FAILING_TESTS}")
endif()
if (SKIP_TESTS)
message("Skipped tests: ${SKIP_TESTS}")
endif()

foreach(file ${TESTS})
set(test_name "/tests/${file}")
Expand Down
38 changes: 38 additions & 0 deletions tests/custom-transform-visualized.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2024 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.two-color show BLACK WHITE
import .png-visualizer

class TransformedDriver extends TwoColorPngVisualizer:
constructor width/int height/int path/string --outline/int?=null:
super width height path --outline=outline

base-transform --inverted/bool --portrait/bool? -> Transform:
// Simulate a display that wants its input rotated 90 degrees and inverted.
if portrait == null:
portrait = true
else:
portrait = not portrait
return super --no-inverted --portrait=portrait

main args:
sans10 := Font.get "sans10"

if args.size != 1:
print "Usage: script.toit png-basename"
exit 1
driver := TransformedDriver 240 160 args[0] --outline=BLACK
display := PixelDisplay.two-color driver
display.background = WHITE

text := Label --x=10 --y=55 --text="Hello, World!" --font=sans10 --color=BLACK
display.add text

display.draw

driver.write-png
Binary file added tests/gold/custom-transform-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.

0 comments on commit 0afbfb5

Please sign in to comment.