diff --git a/.vscode/launch.json b/.vscode/launch.json index 17869b3..69f6bc4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,13 @@ { "version": "0.2.0", "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}" + }, { "name": "C/C++: clang++ build and debug active file", "type": "cppdbg", diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a74978..1c8d3c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.12) -set(CMAKE_C_STANDARD 17) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror") +set(CMAKE_C_STANDARD 11) project(examples C) diff --git a/examples/c/character.c b/examples/c/character.c index d8ed068..f36497c 100644 --- a/examples/c/character.c +++ b/examples/c/character.c @@ -19,7 +19,7 @@ void blend_tile(const pp_tile_t *t) { for(int32_t y = t->y; y < t->y + t->h; y++) { for(int32_t x = t->x; x < t->x + t->w; x++) { colour alpha_pen = pen; - alpha_pen.rgba.a = alpha(pen.rgba.a, pp_tile_get(t, x, y)); + alpha_pen.a = alpha(pen.a, pp_tile_get(t, x, y)); buffer[y][x] = blend(buffer[y][x], alpha_pen); } } diff --git a/examples/c/geometry.c b/examples/c/geometry.c index 4c59481..8c858a7 100644 --- a/examples/c/geometry.c +++ b/examples/c/geometry.c @@ -18,7 +18,7 @@ void blend_tile(const pp_tile_t *t) { for(int32_t y = t->y; y < t->y + t->h; y++) { for(int32_t x = t->x; x < t->x + t->w; x++) { colour alpha_pen = pen; - alpha_pen.rgba.a = alpha(pen.rgba.a, pp_tile_get(t, x, y)); + alpha_pen.a = alpha(pen.a, pp_tile_get(t, x, y)); buffer[y][x] = blend(buffer[y][x], alpha_pen); } } diff --git a/examples/c/helpers.h b/examples/c/helpers.h index 27f66e7..a523f3c 100644 --- a/examples/c/helpers.h +++ b/examples/c/helpers.h @@ -10,7 +10,7 @@ typedef union { uint8_t g; uint8_t b; uint8_t a; - } rgba; + }; uint32_t c; } colour; @@ -24,22 +24,20 @@ __attribute__((always_inline)) uint8_t blend_channel(uint8_t s, uint8_t d, uint8 colour blend(colour dest, colour src) { - uint16_t a = alpha(src.rgba.a, dest.rgba.a); - - if(src.rgba.a == 0) return dest; - if(src.rgba.a == 255) return src; + if(src.a == 0) return dest; + if(src.a == 255) return src; colour result; - result.rgba.r = blend_channel(src.rgba.r, dest.rgba.r, src.rgba.a); - result.rgba.g = blend_channel(src.rgba.g, dest.rgba.g, src.rgba.a); - result.rgba.b = blend_channel(src.rgba.b, dest.rgba.b, src.rgba.a); - result.rgba.a = dest.rgba.a > src.rgba.a ? dest.rgba.a : src.rgba.a; + result.r = blend_channel(src.r, dest.r, src.a); + result.g = blend_channel(src.g, dest.g, src.a); + result.b = blend_channel(src.b, dest.b, src.a); + result.a = dest.a > src.a ? dest.a : src.a; return result; } colour create_colour(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - return (colour){ .rgba.r = r, .rgba.g = g, .rgba.b = b, .rgba.a = a }; + return (colour){ .r = r, .g = g, .b = b, .a = a }; } colour create_colour_hsv(float h, float s, float v, float a) { diff --git a/examples/c/logo.c b/examples/c/logo.c index c225288..e4587f8 100644 --- a/examples/c/logo.c +++ b/examples/c/logo.c @@ -21,7 +21,7 @@ void blend_tile(const pp_tile_t *t) { for(int32_t y = t->y; y < t->y + t->h; y++) { for(int32_t x = t->x; x < t->x + t->w; x++) { colour alpha_pen = pen; - alpha_pen.rgba.a = alpha(pen.rgba.a, pp_tile_get(t, x, y)); + alpha_pen.a = alpha(pen.a, pp_tile_get(t, x, y)); buffer[y][x] = blend(buffer[y][x], alpha_pen); } } @@ -305,7 +305,7 @@ int main() { if(i == 360) { pp_mat3_t st = t; - pp_mat3_translate(&st, 90, 90); + pp_mat3_translate(&st, 50, 50); pp_transform(&st); set_pen(create_colour(20, 30, 40, 160)); pp_render(&polygon); diff --git a/examples/go/geometry/go.sum b/examples/go/geometry/go.sum new file mode 100644 index 0000000..2194c5f --- /dev/null +++ b/examples/go/geometry/go.sum @@ -0,0 +1,47 @@ +github.com/ebitengine/purego v0.5.0 h1:JrMGKfRIAM4/QVKaesIIT7m/UVjTj5GYhRSQYwfVdpo= +github.com/ebitengine/purego v0.5.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/hajimehoshi/ebiten/v2 v2.6.0 h1:nh09FUhjNGFVcUUPsx6oTMbD1pHerNvTKPE+494y3cU= +github.com/hajimehoshi/ebiten/v2 v2.6.0/go.mod h1:TZtorL713an00UW4LyvMeKD8uXWnuIuCPtlH11b0pgI= +github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk= +github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63 h1:3AGKexOYqL+ztdWdkB1bDwXgPBuTS/S8A4WzuTvJ8Cg= +golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= +golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ= +golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk= +golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57 h1:Q6NT8ckDYNcwmi/bmxe+XbiDMXqMRW1xFBtJ+bIpie4= +golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57/go.mod h1:wEyOn6VvNW7tcf+bW/wBz1sehi2s2BZ4TimyR7qZen4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/examples/go/geometry/main b/examples/go/geometry/main deleted file mode 100755 index 9c96efb..0000000 Binary files a/examples/go/geometry/main and /dev/null differ diff --git a/examples/go/geometry/main.go b/examples/go/geometry/main.go index 12453c3..c8d4342 100644 --- a/examples/go/geometry/main.go +++ b/examples/go/geometry/main.go @@ -5,45 +5,120 @@ package main #include <../../../pretty-poly.h> // wrap tile callback to dereference pointer -extern void blend_tile(pp_tile_t t); -void wrap_blend_tile(const pp_tile_t* t) { - blend_tile(*t); +extern void wrapped_blend_tile_callback_go(pp_tile_t t); +void wrapped_blend_tile_callback_c(const pp_tile_t* t) { + wrapped_blend_tile_callback_go(*t); } */ import "C" import ( + "image/color" + "log" + "math" "runtime" "unsafe" + + "github.com/hajimehoshi/ebiten/v2" ) -type point struct { - x float32 - y float32 -} -type path struct { - points *point - count uint32 -} -type polygon struct { - paths *path - count uint32 +type Game struct{} + +func (g *Game) Update() error { + return nil } -func main() { +var s *ebiten.Image +var angle float32 +var pen color.NRGBA - C.pp_tile_callback(C.pp_tile_callback_t(C.wrap_blend_tile)) - C.pp_antialias(C.PP_AA_X4) - C.pp_clip(0, 0, 320, 240) +func (g *Game) Draw(screen *ebiten.Image) { - points := []point{{-128, -128}, {128, -128}, {128, 128}, {-128, 128}} - paths := []path{{points: &points[0], count: 4}} - poly := polygon{paths: &paths[0], count: 1} + s = screen + angle += 0.5 + points := []C.pp_point_t{{-32, -32}, {32, -32}, {32, 32}, {-32, 32}} + paths := []C.pp_path_t{{points: &points[0], count: 4}} + poly := C.pp_poly_t{paths: &paths[0], count: 1} var pinner runtime.Pinner pinner.Pin(&points[0]) pinner.Pin(&paths[0]) - pinner.Pin(&poly) - C.pp_render((*C.pp_poly_t)(unsafe.Pointer(&poly))) + + for i := float32(0); i < 20; i += 2 { + t := C.pp_mat3_identity() + C.pp_mat3_translate(&t, 80, 60) + a := math.Sin(float64(angle+i)/50) * 360 + C.pp_mat3_rotate(&t, C.float(a)) + C.pp_mat3_scale(&t, C.float(1.5-i/20), C.float(1.5-i/20)) + C.pp_transform(&t) + + pen = color.NRGBA{uint8(i * 12), uint8(i * 5), uint8(i * 3), uint8(255)} + + C.pp_render((*C.pp_poly_t)(unsafe.Pointer(&poly))) + } + pinner.Unpin() } + +func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { + return 160, 120 +} + +func alpha(sa uint32, da uint32) uint32 { + return ((sa + 1) * (da)) >> 8 +} + +func blend_channel(s uint32, d uint32, a uint32) uint8 { + return uint8(d + ((a*(s-d) + 127) >> 8)) +} + +func blend(dest color.RGBA, src color.RGBA) color.RGBA { + if src.A == 0 { + return dest + } + if src.A == 255 { + return src + } + + var result color.RGBA + result.R = blend_channel(uint32(src.R), uint32(dest.R), uint32(src.A)) + result.G = blend_channel(uint32(src.G), uint32(dest.G), uint32(src.A)) + result.B = blend_channel(uint32(src.B), uint32(dest.B), uint32(src.A)) + result.A = max(dest.A, src.A) + + return result + +} + +func blend_tile(t C.pp_tile_t) { + for y := int(t.y); y < int(t.y+t.h); y++ { + for x := int(t.x); x < int(t.x+t.w); x++ { + alpha := int(C.pp_tile_get(&t, C.int(x), C.int(y))) + + d := s.RGBA64At(x, y) + da := color.RGBA{uint8(d.R >> 8), uint8(d.G >> 8), uint8(d.B >> 8), uint8(d.A >> 8)} + + pa := color.RGBA{pen.R, pen.G, pen.B, uint8(alpha)} + c := blend(da, pa) + + s.Set(x, y, c) + } + } + +} + +func main() { + + C.pp_tile_callback(C.pp_tile_callback_t(C.wrapped_blend_tile_callback_c)) + C.pp_antialias(C.PP_AA_X4) + C.pp_clip(0, 0, 160, 120) + + ebiten.SetWindowSize(640, 480) + ebiten.SetWindowTitle("Pretty Poly Go Test") + ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled) + + if err := ebiten.RunGame(&Game{}); err != nil { + log.Fatal(err) + } + +} diff --git a/examples/go/geometry/wrappers.go b/examples/go/geometry/wrappers.go index 20a5787..f333f5f 100644 --- a/examples/go/geometry/wrappers.go +++ b/examples/go/geometry/wrappers.go @@ -5,7 +5,7 @@ package main */ import "C" -//export blend_tile -func blend_tile(t C.pp_tile_t) { - print("in tile callback") +//export wrapped_blend_tile_callback_go +func wrapped_blend_tile_callback_go(t C.pp_tile_t) { + blend_tile(t) } diff --git a/pretty-poly.h b/pretty-poly.h index 647b3c8..62230fc 100644 --- a/pretty-poly.h +++ b/pretty-poly.h @@ -1,28 +1,33 @@ -// Pretty Poly 🦜 - super-sampling polygon renderer for low resource platforms. -// -// Jonathan Williamson, August 2022 -// Examples, source, and more: https://github.com/lowfatcode/pretty-poly -// MIT License https://github.com/lowfatcode/pretty-poly/blob/main/LICENSE -// -// An easy way to render high quality text in embedded applications running -// on resource constrained microcontrollers such as the Cortex M0 and up. -// -// - Renders polygons: concave, self-intersecting, multi contour, holes, etc. -// - C17 header only library: simply copy the header file into your project -// - Tile based renderer: low memory footprint, cache coherency -// - Low memory usage: ~4kB of heap memory required -// - High speed on low resource platforms: optionally no floating point -// - Antialiasing modes: X1 (none), X4 and X16 super sampling -// - Bounds clipping: all results clipped to supplied clip rectangle -// - Pixel format agnostic: renders a "tile" to blend into your framebuffer -// - Support for hardware interpolators on rp2040 (thanks @MichaelBell!) -// -// Contributor bwaaaaaarks! 🦜 -// -// @MichaelBell - lots of bug fixes, performance boosts, and suggestions. -// @gadgetoid - integrating into the PicoVector library and testing. - -#pragma once +/* + + Pretty Poly 🦜 - super-sampling polygon renderer for low resource platforms. + + Jonathan Williamson, August 2022 + Examples, source, and more: https://github.com/lowfatcode/pretty-poly + MIT License https://github.com/lowfatcode/pretty-poly/blob/main/LICENSE + + An easy way to render high quality graphics in embedded applications running + on resource constrained microcontrollers such as the Cortex M0 and up. + + - Renders polygons: concave, self-intersecting, multi contour, holes, etc. + - C11 header only library: simply copy the header file into your project + - Tile based renderer: low memory footprint, cache coherency + - Low memory usage: ~4kB of heap memory required + - High speed on low resource platforms: optionally no floating point + - Antialiasing modes: X1 (none), X4 and X16 super sampling + - Bounds clipping: all results clipped to supplied clip rectangle + - Pixel format agnostic: renders a "tile" to blend into your framebuffer + - Support for hardware interpolators on rp2040 (thanks @MichaelBell!) + + Contributor bwaaaaaarks! 🦜 + + @MichaelBell - lots of bug fixes, performance boosts, and suggestions. + @gadgetoid - integrating into the PicoVector library and testing. + +*/ + +#ifndef PP_INCLUDE_H +#define PP_INCLUDE_H #include #include @@ -30,6 +35,12 @@ #include #include +#ifndef PP_MALLOC +#define PP_MALLOC(size) malloc(size) +#define PP_REALLOC(p, size) realloc(p, size) +#define PP_FREE(p) free(p) +#endif + #ifndef PP_COORD_TYPE #define PP_COORD_TYPE float #endif @@ -121,9 +132,12 @@ extern pp_mat3_t *_pp_transform; void pp_clip(int32_t x, int32_t y, int32_t w, int32_t h); void pp_tile_callback(pp_tile_callback_t callback); void pp_antialias(pp_antialias_t antialias); -void pp_transform(pp_mat3_t *transform); +pp_mat3_t *pp_transform(pp_mat3_t *transform); void pp_render(pp_poly_t *polygon); +pp_rect_t pp_contour_bounds(const pp_path_t *c); +pp_rect_t pp_polygon_bounds(pp_poly_t *p); + #ifdef __cplusplus } #endif @@ -277,7 +291,7 @@ void pp_tile_callback(pp_tile_callback_t callback) { } // maximum tile bounds determined by antialias level -int32_t _pp_tile_width, _pp_tile_height; +uint32_t _pp_tile_width, _pp_tile_height; void pp_antialias(pp_antialias_t antialias) { _pp_antialias = antialias; // recalculate the tile size for rendering based on antialiasing level @@ -285,8 +299,10 @@ void pp_antialias(pp_antialias_t antialias) { _pp_tile_width = (int)(tile_buffer_size / _pp_tile_height); } -void pp_transform(pp_mat3_t *transform) { +pp_mat3_t *pp_transform(pp_mat3_t *transform) { + pp_mat3_t *old = _pp_transform; _pp_transform = transform; + return old; } // write out the tile bits @@ -453,7 +469,7 @@ pp_rect_t render_nodes(uint8_t *buffer, pp_rect_t *tb) { PP_COORD_TYPE aa_scale = (PP_COORD_TYPE)(1 << _pp_antialias); int anitialias_mask = (1 << _pp_antialias) - 1; - for(uint32_t y = 0; y < PP_NODE_BUFFER_HEIGHT; y++) { + for(int32_t y = 0; y < PP_NODE_BUFFER_HEIGHT; y++) { if(node_counts[y] == 0) { if (y == rb.y) ++rb.y; continue; @@ -585,7 +601,7 @@ void pp_render(pp_poly_t *polygon) { pp_tile_t tile = { .x = tb.x, .y = tb.y, .w = tb.w, .h = tb.h, - .stride = _pp_tile_width, + .stride = (uint32_t)_pp_tile_width, .data = tile_buffer + rb.x + _pp_tile_width * rb.y }; @@ -598,4 +614,6 @@ void pp_render(pp_poly_t *polygon) { #endif } -#endif // PP_IMPLEMENTATION \ No newline at end of file +#endif // PP_IMPLEMENTATION + +#endif // PP_INCLUDE_H \ No newline at end of file