Experimental Go bindings for Skia
authorStephan Altmueller <stephana@google.com>
Tue, 9 May 2017 19:56:06 +0000 (15:56 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Wed, 10 May 2017 15:32:19 +0000 (15:32 +0000)
This CL implements Go bindings for a subset of the functions in the C API.
It implements a Go version of the C demo program in
experimental/c-api-example/skia-c-example.c and the output is identical.
(Checked by hand).

The main purpose is to establish a pattern of calling the Skia C API that
is memory safe and provides a idiomatic Go interface to Skia.

Follow up CLs will cover the entire C API, add documentation and establish
a pattern to distribute the bindings more easily.

BUG=
Change-Id: I96ff7c3715164c533202ce300ab0312b1b07f884

Change-Id: I96ff7c3715164c533202ce300ab0312b1b07f884
Reviewed-on: https://skia-review.googlesource.com/10032
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Stephan Altmueller <stephana@google.com>

experimental/go-demo/main.go [new file with mode: 0644]
experimental/go-skia/ctypes.go [new file with mode: 0644]
experimental/go-skia/skia.go
experimental/go-skia/types.go [new file with mode: 0644]

diff --git a/experimental/go-demo/main.go b/experimental/go-demo/main.go
new file mode 100644 (file)
index 0000000..7df64d6
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+package main
+
+/*
+       This is a translation of the example code in
+       "experimental/c-api-example/skia-c-example.c" to test the
+       go-skia package.
+*/
+
+import (
+       "log"
+       "os"
+
+       skia "skia.googlesource.com/skia/experimental/go-skia"
+)
+
+func main() {
+       imgInfo := &skia.ImageInfo{
+               Width:     640,
+               Height:    480,
+               ColorType: skia.GetDefaultColortype(),
+               AlphaType: skia.PREMUL_ALPHATYPE,
+       }
+
+       surface, err := skia.NewRasterSurface(imgInfo)
+       if err != nil {
+               log.Fatalln(err)
+       }
+
+       canvas := surface.Canvas()
+
+       fillPaint := skia.NewPaint()
+       fillPaint.SetColor(0xFF0000FF)
+       canvas.DrawPaint(fillPaint)
+       fillPaint.SetColor(0xFF00FFFF)
+
+       rect := skia.NewRect(100, 100, 540, 380)
+       canvas.DrawRect(rect, fillPaint)
+
+       strokePaint := skia.NewPaint()
+       strokePaint.SetColor(0xFFFF0000)
+       strokePaint.SetAntiAlias(true)
+       strokePaint.SetStroke(true)
+       strokePaint.SetStrokeWidth(5.0)
+
+       path := skia.NewPath()
+       path.MoveTo(50, 50)
+       path.LineTo(590, 50)
+       path.CubicTo(-490, 50, 1130, 430, 50, 430)
+       path.LineTo(590, 430)
+       canvas.DrawPath(path, strokePaint)
+
+       fillPaint.SetColor(0x8000FF00)
+       canvas.DrawOval(skia.NewRect(120, 120, 520, 360), fillPaint)
+
+       // // Get a skia image from the surface.
+       skImg := surface.Image()
+
+       // Write new image to file if we have one.
+       if skImg != nil {
+               out, err := os.Create("testimage.png")
+               if err != nil {
+                       log.Fatal(err)
+               }
+               defer out.Close()
+
+               if err := skImg.WritePNG(out); err != nil {
+                       log.Fatalf("Unable to write png: %s", err)
+               }
+       }
+}
diff --git a/experimental/go-skia/ctypes.go b/experimental/go-skia/ctypes.go
new file mode 100644 (file)
index 0000000..64953ed
--- /dev/null
@@ -0,0 +1,58 @@
+//+build ignore
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+package skia
+
+// This file is used to generate 'types.go'
+// from the corresponding type definitions in the C API.
+// Any C struct for which we would like to generate a
+// Go struct with the same memory layout should defined defined here.
+// Any enum that is used in Go should also be listed here, together
+// with the enum values that we want to use.
+
+/*
+#cgo CFLAGS: -I../../include/c
+#include "../../include/c/sk_types.h"
+*/
+import "C"
+
+type Color C.sk_color_t
+
+type ColorType C.sk_colortype_t
+
+const (
+       UNKNOWN_COLORTYPE   ColorType = C.UNKNOWN_SK_COLORTYPE
+       RGBA_8888_COLORTYPE ColorType = C.RGBA_8888_SK_COLORTYPE
+       BGRA_8888_COLORTYPE ColorType = C.BGRA_8888_SK_COLORTYPE
+       ALPHA_8_COLORTYPE   ColorType = C.ALPHA_8_SK_COLORTYPE
+)
+
+type AlphaType C.sk_alphatype_t
+
+const (
+       OPAQUE_ALPHATYPE   AlphaType = C.OPAQUE_SK_ALPHATYPE
+       PREMUL_ALPHATYPE   AlphaType = C.PREMUL_SK_ALPHATYPE
+       UNPREMUL_ALPHATYPE AlphaType = C.UNPREMUL_SK_ALPHATYPE
+)
+
+type PixelGeometry C.sk_pixelgeometry_t
+
+const (
+       UNKNOWN_SK_PIXELGEOMETRY PixelGeometry = C.UNKNOWN_SK_PIXELGEOMETRY
+       RGB_H_SK_PIXELGEOMETRY   PixelGeometry = C.RGB_H_SK_PIXELGEOMETRY
+       BGR_H_SK_PIXELGEOMETRY   PixelGeometry = C.BGR_H_SK_PIXELGEOMETRY
+       RGB_V_SK_PIXELGEOMETRY   PixelGeometry = C.RGB_V_SK_PIXELGEOMETRY
+       BGR_V_SK_PIXELGEOMETRY   PixelGeometry = C.BGR_V_SK_PIXELGEOMETRY
+)
+
+type ImageInfo C.sk_imageinfo_t
+
+type SurfaceProps C.sk_surfaceprops_t
+
+type Rect C.sk_rect_t
index 3073057..e477593 100644 (file)
-package main
-
-// First, build Skia this way:
-//   ./gyp_skia -Dskia_shared_lib=1 && ninja -C out/Debug
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+package skia
 
 /*
-#cgo LDFLAGS: -lGL
-#cgo LDFLAGS: -lGLU
-#cgo LDFLAGS: -lX11
-#cgo LDFLAGS: -ldl
-#cgo LDFLAGS: -lfontconfig
-#cgo LDFLAGS: -lfreetype
-#cgo LDFLAGS: -lgif
-#cgo LDFLAGS: -lm
-#cgo LDFLAGS: -lpng
-#cgo LDFLAGS: -lstdc++
-#cgo LDFLAGS: -lz
-
-#cgo LDFLAGS: -L ../../out/Debug/lib
-#cgo LDFLAGS: -Wl,-rpath=../../out/Debug/lib
+#cgo LDFLAGS: -L${SRCDIR}/../../out/Shared
+#cgo LDFLAGS: -Wl,-rpath=${SRCDIR}/../../out/Shared
 #cgo LDFLAGS: -lskia
-
 #cgo CFLAGS: -I../../include/c
+#include "sk_canvas.h"
+#include "sk_data.h"
+#include "sk_image.h"
+#include "sk_paint.h"
+#include "sk_path.h"
 #include "sk_surface.h"
 */
 import "C"
 
 import (
        "fmt"
+       "io"
+       "runtime"
+       "unsafe"
 )
 
-func main() {
-       p := C.sk_paint_new()
-       defer C.sk_paint_delete(p)
-       fmt.Println("OK!")
+// TODO(stephana): Add proper documentation to the types defined here.
+
+//////////////////////////////////////////////////////////////////////////
+// Surface
+//////////////////////////////////////////////////////////////////////////
+type Surface struct {
+       ptr *C.sk_surface_t
+}
+
+// func NewRasterSurface(width, height int32, alphaType AlphaType) (*Surface, error) {
+func NewRasterSurface(imgInfo *ImageInfo) (*Surface, error) {
+       ptr := C.sk_surface_new_raster(imgInfo.cPointer(), (*C.sk_surfaceprops_t)(nil))
+       if ptr == nil {
+               return nil, fmt.Errorf("Unable to create raster surface.")
+       }
+
+       ret := &Surface{ptr: ptr}
+       runtime.SetFinalizer(ret, func(s *Surface) {
+               C.sk_surface_unref(s.ptr)
+       })
+       return ret, nil
+}
+
+func (s *Surface) Canvas() *Canvas {
+       return &Canvas{
+               ptr:             C.sk_surface_get_canvas(s.ptr),
+               keepParentAlive: s,
+       }
+}
+
+func (s *Surface) Image() *Image {
+       ret := &Image{
+               ptr:             C.sk_surface_new_image_snapshot(s.ptr),
+               keepParentAlive: s,
+       }
+       runtime.SetFinalizer(ret, func(i *Image) {
+               C.sk_image_unref(i.ptr)
+       })
+       return ret
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Image
+//////////////////////////////////////////////////////////////////////////
+type Image struct {
+       ptr             *C.sk_image_t
+       keepParentAlive *Surface
+}
+
+func (i *Image) WritePNG(w io.Writer) error {
+       data := C.sk_image_encode(i.ptr)
+       defer C.sk_data_unref(data)
+
+       dataPtr := C.sk_data_get_data(data)
+       dataSize := C.sk_data_get_size(data)
+       byteSlice := C.GoBytes(dataPtr, C.int(dataSize))
+       _, err := w.Write(byteSlice)
+       if err != nil {
+               return err
+       }
+       return nil
 }
 
-// TODO: replace this with an idiomatic interface to Skia.
+//////////////////////////////////////////////////////////////////////////
+// Canvas
+//////////////////////////////////////////////////////////////////////////
+type Canvas struct {
+       ptr             *C.sk_canvas_t
+       keepParentAlive *Surface
+}
+
+func (c *Canvas) DrawPaint(paint *Paint) {
+       C.sk_canvas_draw_paint(c.ptr, paint.ptr)
+}
+
+func (c *Canvas) DrawOval(rect *Rect, paint *Paint) {
+       // C.sk_canvas_draw_oval(c.ptr, (*C.sk_rect_t)(unsafe.Pointer(rect)), (*C.sk_paint_t)(paint.ptr))
+       C.sk_canvas_draw_oval(c.ptr, rect.cPointer(), paint.ptr)
+}
+
+func (c *Canvas) DrawRect(rect *Rect, paint *Paint) {
+       // C.sk_canvas_draw_rect(c.ptr, (*C.sk_rect_t)(unsafe.Pointer(rect)), (*C.sk_paint_t)(paint.ptr))
+       C.sk_canvas_draw_rect(c.ptr, rect.cPointer(), paint.ptr)
+}
+
+func (c *Canvas) DrawPath(path *Path, paint *Paint) {
+       // C.sk_canvas_draw_path(c.ptr, (*C.sk_path_t)(path.ptr), (*C.sk_paint_t)(paint.ptr))
+       C.sk_canvas_draw_path(c.ptr, path.ptr, paint.ptr)
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Paint
+//////////////////////////////////////////////////////////////////////////
+type Paint struct {
+       ptr *C.sk_paint_t
+}
+
+func NewPaint() *Paint {
+       ret := &Paint{ptr: C.sk_paint_new()}
+       runtime.SetFinalizer(ret, func(p *Paint) {
+               C.sk_paint_delete(p.ptr)
+       })
+       return ret
+}
+
+func (p *Paint) SetColor(color Color) {
+       C.sk_paint_set_color(p.ptr, C.sk_color_t(color))
+}
+
+func (p *Paint) SetAntiAlias(antiAlias bool) {
+       C.sk_paint_set_antialias(p.ptr, C._Bool(antiAlias))
+}
+
+func (p *Paint) SetStroke(val bool) {
+       C.sk_paint_set_stroke(p.ptr, C._Bool(val))
+}
+
+func (p *Paint) SetStrokeWidth(width float32) {
+       C.sk_paint_set_stroke_width(p.ptr, C.float(width))
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Path
+//////////////////////////////////////////////////////////////////////////
+type Path struct {
+       ptr *C.sk_path_t
+}
+
+func NewPath() *Path {
+       ret := &Path{ptr: C.sk_path_new()}
+       runtime.SetFinalizer(ret, func(p *Path) {
+               C.sk_path_delete(p.ptr)
+       })
+       return ret
+}
+
+func (p *Path) MoveTo(x, y float32) {
+       C.sk_path_move_to(p.ptr, C.float(x), C.float(y))
+}
+
+func (p *Path) LineTo(x, y float32) {
+       C.sk_path_line_to(p.ptr, C.float(x), C.float(y))
+}
+
+func (p *Path) QuadTo(x0, y0, x1, y1 float32) {
+       C.sk_path_quad_to(p.ptr, C.float(x0), C.float(y0), C.float(x1), C.float(y1))
+}
+
+func (p *Path) ConicTo(x0, y0, x1, y1, w float32) {
+       C.sk_path_conic_to(p.ptr, C.float(x0), C.float(y0), C.float(x1), C.float(y1), C.float(w))
+}
+
+func (p *Path) CubicTo(x0, y0, x1, y1, x2, y2 float32) {
+       C.sk_path_cubic_to(p.ptr, C.float(x0), C.float(y0), C.float(x1), C.float(y1), C.float(x2), C.float(y2))
+}
+
+func (p *Path) Close() {
+       C.sk_path_close(p.ptr)
+}
+
+// NewRect is a convenience function to define a Rect in a single line.
+func NewRect(left, top, right, bottom float32) *Rect {
+       return &Rect{
+               Left:   left,
+               Top:    top,
+               Right:  right,
+               Bottom: bottom,
+       }
+}
+
+// cPointer casts the pointer to Rect to the corresponding C pointer.
+func (r *Rect) cPointer() *C.sk_rect_t {
+       return (*C.sk_rect_t)(unsafe.Pointer(r))
+}
+
+// cPointer casts the pointer to ImageInfo to the corresponding C pointer.
+func (i *ImageInfo) cPointer() *C.sk_imageinfo_t {
+       return (*C.sk_imageinfo_t)(unsafe.Pointer(i))
+}
+
+// Utility functions.
+func GetDefaultColortype() ColorType {
+       return ColorType(C.sk_colortype_get_default_8888())
+}
diff --git a/experimental/go-skia/types.go b/experimental/go-skia/types.go
new file mode 100644 (file)
index 0000000..637ecee
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Created by cgo -godefs. Enum fields in structs were fixed by hand.
+// command: go tool cgo -godefs ctypes.go > types.go
+//
+// The purpose of this file is to have Go structs with the same memory
+// layout as their C counterparts. For enums we want the underlying primitive
+// types to match.
+//
+// TODO(stephan): Add tests that allow to detect failure on platforms other
+// than Linux and changes in the underlying C types.
+
+package skia
+
+type Color uint32
+
+type ColorType uint32
+
+const (
+       UNKNOWN_COLORTYPE   ColorType = 0x0
+       RGBA_8888_COLORTYPE ColorType = 0x1
+       BGRA_8888_COLORTYPE ColorType = 0x2
+       ALPHA_8_COLORTYPE   ColorType = 0x3
+)
+
+type AlphaType uint32
+
+const (
+       OPAQUE_ALPHATYPE   AlphaType = 0x0
+       PREMUL_ALPHATYPE   AlphaType = 0x1
+       UNPREMUL_ALPHATYPE AlphaType = 0x2
+)
+
+type PixelGeometry uint32
+
+const (
+       UNKNOWN_SK_PIXELGEOMETRY PixelGeometry = 0x0
+       RGB_H_SK_PIXELGEOMETRY   PixelGeometry = 0x1
+       BGR_H_SK_PIXELGEOMETRY   PixelGeometry = 0x2
+       RGB_V_SK_PIXELGEOMETRY   PixelGeometry = 0x3
+       BGR_V_SK_PIXELGEOMETRY   PixelGeometry = 0x4
+)
+
+type ImageInfo struct {
+       Width     int32
+       Height    int32
+       ColorType ColorType
+       AlphaType AlphaType
+}
+
+type SurfaceProps struct {
+       PixelGeometry PixelGeometry
+}
+
+type Rect struct {
+       Left   float32
+       Top    float32
+       Right  float32
+       Bottom float32
+}