<commit_before>// +build !windows,!darwin

// 10 june 2014

package ndraw

import (
	"fmt"
	"sync"
	"image"
	"reflect"
	"unsafe"
)

// /* TODO really pangocairo? */
// #cgo pkg-config: cairo pango pangocairo
// #include <pango/pangocairo.h>
// #include <stdlib.h>
import "C"

type sysImage interface {
	// TODO
}

type imagetype struct {
	lock		sync.Mutex
	cr		*C.cairo_t
	cs		*C.cairo_surface_t
}

func cairoerr(status C.cairo_status_t) string {
	return C.GoString(C.cairo_status_to_string(status))
}

func newImage(width int, height int) Image {
	i := new(imagetype)
	i.cs = C.cairo_image_surface_create(
		C.CAIRO_FORMAT_ARGB32,
		C.int(width), C.int(height))
	if status := C.cairo_surface_status(i.cs); status != C.CAIRO_STATUS_SUCCESS {
		panic(fmt.Errorf("error creating cairo surface for image: %v", cairoerr(status)))
	}
	i.cr = C.cairo_create(i.cs)
	if status := C.cairo_status(i.cr); status != C.CAIRO_STATUS_SUCCESS {
		panic(fmt.Errorf("error creating cairo context for image: %v", cairoerr(status)))
	}
	return i
}

func (i *imagetype) Close() {
	i.lock.Lock()
	defer i.lock.Unlock()

	C.cairo_destroy(i.cr)
	C.cairo_surface_destroy(i.cs)
}

func (i *imagetype) Line(x0 int, y0 int, x1 int, y1 int, p Pen) {
	i.lock.Lock()
	defer i.lock.Unlock()

	p.selectInto(i.cr)
	C.cairo_new_path(i.cr)
	C.cairo_move_to(i.cr, C.double(x0), C.double(y0))
	C.cairo_line_to(i.cr, C.double(x1), C.double(y1))
	C.cairo_stroke(i.cr)
	deselectPen(i.cr)
}

func (i *imagetype) Text(str string, x int, y int, f Font, p Pen) {
	i.lock.Lock()
	defer i.lock.Unlock()

	p.selectInto(i.cr)
	C.cairo_save(i.cr)
	C.cairo_move_to(i.cr, C.double(x), C.double(y))
	pl := f.selectInto(i.cr)
	cstr := C.CString(str)
	C.pango_layout_set_text(pl, cstr, -1)
	C.free(unsafe.Pointer(cstr))
	C.pango_cairo_show_layout(i.cr, pl)
	C.cairo_restore(i.cr)
	deselectFont(pl)
	deselectPen(i.cr)
}

func cairoImageData(cs *C.cairo_surface_t) (data []uint32, stride int) {
	var sh reflect.SliceHeader

	C.cairo_surface_flush(cs)			// perform pending drawing
	height := int(C.cairo_image_surface_get_height(cs))
	stride = int(C.cairo_image_surface_get_stride(cs))
	sh.Data = uintptr(unsafe.Pointer(C.cairo_image_surface_get_data(cs)))
	sh.Len = height * stride			// should be correct for uint32
	sh.Cap = sh.Len
	data = *((*[]uint32)(unsafe.Pointer(&sh)))
	return data, stride
}

func (i *imagetype) Image() (img *image.RGBA) {
	i.lock.Lock()
	defer i.lock.Unlock()

	width := int(C.cairo_image_surface_get_width(i.cs))
	height := int(C.cairo_image_surface_get_height(i.cs))
	data, stride := cairoImageData(i.cs)
	img = image.NewRGBA(image.Rect(0, 0, width, height))
	p := 0
	q := 0<commit_msg>Adjusted the cairo backend to act like the Windows backend for text rendering.<commit_after>// +build !windows,!darwin

// 10 june 2014

package ndraw

import (
	"fmt"
	"sync"
	"image"
	"reflect"
	"unsafe"
)

// /* TODO really pangocairo? */
// #cgo pkg-config: cairo pango pangocairo
// #include <pango/pangocairo.h>
// #include <stdlib.h>
import "C"

type sysImage interface {
	// TODO
}

type imagetype struct {
	lock		sync.Mutex
	cr		*C.cairo_t
	cs		*C.cairo_surface_t
}

func cairoerr(status C.cairo_status_t) string {
	return C.GoString(C.cairo_status_to_string(status))
}

func newImage(width int, height int) Image {
	i := new(imagetype)
	i.cs = C.cairo_image_surface_create(
		C.CAIRO_FORMAT_ARGB32,
		C.int(width), C.int(height))
	if status := C.cairo_surface_status(i.cs); status != C.CAIRO_STATUS_SUCCESS {
		panic(fmt.Errorf("error creating cairo surface for image: %v", cairoerr(status)))
	}
	i.cr = C.cairo_create(i.cs)
	if status := C.cairo_status(i.cr); status != C.CAIRO_STATUS_SUCCESS {
		panic(fmt.Errorf("error creating cairo context for image: %v", cairoerr(status)))
	}
	return i
}

func (i *imagetype) Close() {
	i.lock.Lock()
	defer i.lock.Unlock()

	C.cairo_destroy(i.cr)
	C.cairo_surface_destroy(i.cs)
}

func (i *imagetype) Line(x0 int, y0 int, x1 int, y1 int, p Pen) {
	i.lock.Lock()
	defer i.lock.Unlock()

	p.selectInto(i.cr)
	C.cairo_new_path(i.cr)
	C.cairo_move_to(i.cr, C.double(x0), C.double(y0))
	C.cairo_line_to(i.cr, C.double(x1), C.double(y1))
	C.cairo_stroke(i.cr)
	deselectPen(i.cr)
}

// TODO should be cairo_fill() but we need a brush - see https://developer.gnome.org/cairo/1.10/cairo-Paths.html#cairo-glyph-path
// https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#PangoCairoShapeRendererFunc implies that both stroking AND filling are done, but https://git.gnome.org/browse/pango/tree/pango/pangocairo-render.c shows that stroking is only done for unknown character boxes; TODO test
func (i *imagetype) Text(str string, x int, y int, f Font, p Pen) {
	i.lock.Lock()
	defer i.lock.Unlock()

	p.selectInto(i.cr)
	C.cairo_save(i.cr)
	C.cairo_new_path(i.cr)
	C.cairo_move_to(i.cr, C.double(x), C.double(y))
	pl := f.selectInto(i.cr)
	cstr := C.CString(str)
	C.pango_layout_set_text(pl, cstr, -1)
	C.free(unsafe.Pointer(cstr))
	C.pango_cairo_layout_path(i.cr, pl)
	C.cairo_stroke(i.cr)
	C.cairo_restore(i.cr)
	deselectFont(pl)
	deselectPen(i.cr)
}

func cairoImageData(cs *C.cairo_surface_t) (data []uint32, stride int) {
	var sh reflect.SliceHeader

	C.cairo_surface_flush(cs)			// perform pending drawing
	height := int(C.cairo_image_surface_get_height(cs))
	stride = int(C.cairo_image_surface_get_stride(cs))
	sh.Data = uintptr(unsafe.Pointer(C.cairo_image_surface_get_data(cs)))
	sh.Len = height * stride			// should be correct for uint32
	sh.Cap = sh.Len
	data = *((*[]uint32)(unsafe.Pointer(&sh)))
	return data, stride
}

func (i *imagetype) Image() (img *image.RGBA) {
	i.lock.Lock()
	defer i.lock.Unlock()

	width := int(C.cairo_image_surface_get_width(i.cs))
	height := int(C.cairo_image_surface_get_height(i.cs))
	data, stride := cairoImageData(i.cs)
	img = image.NewRGBA(image.Rect(0, 0, width, height))
	p := 0
	q := 0
{'fraction_non_alphanumeric': 0.15483319076133448, 'fraction_numerical': 0.010265183917878529, 'mean_word_length': 4.4533582089552235, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1720623', 'n_tokens_mistral': 2559, 'n_tokens_neox': 2478, 'n_words': 485}