improved cache code
This commit is contained in:
parent
896af05ba4
commit
9d1e5b306a
3 changed files with 119 additions and 93 deletions
79
canvas.go
79
canvas.go
|
@ -3,10 +3,9 @@
|
|||
package canvas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/golang/freetype/truetype"
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
|
@ -26,7 +25,9 @@ type Canvas struct {
|
|||
state drawState
|
||||
stateStack []drawState
|
||||
|
||||
images map[interface{}]*Image
|
||||
images map[interface{}]*Image
|
||||
fonts map[interface{}]*Font
|
||||
fontCtxs map[fontKey]*frContext
|
||||
|
||||
shadowBuf [][2]float64
|
||||
}
|
||||
|
@ -122,9 +123,11 @@ const (
|
|||
var Performance = struct {
|
||||
IgnoreSelfIntersections bool
|
||||
AssumeConvex bool
|
||||
ImageCacheSize int
|
||||
|
||||
// CacheSize is only approximate
|
||||
CacheSize int
|
||||
}{
|
||||
ImageCacheSize: 16_000_000,
|
||||
CacheSize: 16_000_000,
|
||||
}
|
||||
|
||||
// New creates a new canvas with the given viewport coordinates.
|
||||
|
@ -136,6 +139,7 @@ func New(backend backendbase.Backend) *Canvas {
|
|||
b: backend,
|
||||
stateStack: make([]drawState, 0, 20),
|
||||
images: make(map[interface{}]*Image),
|
||||
fonts: make(map[interface{}]*Font),
|
||||
}
|
||||
cv.state.lineWidth = 1
|
||||
cv.state.lineAlpha = 1
|
||||
|
@ -286,25 +290,26 @@ func (cv *Canvas) SetFont(src interface{}, size float64) {
|
|||
if src == nil {
|
||||
cv.state.font = defaultFont
|
||||
} else {
|
||||
switch v := src.(type) {
|
||||
case *Font:
|
||||
cv.state.font = v
|
||||
case *truetype.Font:
|
||||
cv.state.font = &Font{font: v}
|
||||
case string:
|
||||
if f, ok := fonts[v]; ok {
|
||||
cv.state.font = f
|
||||
} else {
|
||||
f, err := cv.LoadFont(v)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading font %s: %v\n", v, err)
|
||||
fonts[v] = nil
|
||||
} else {
|
||||
fonts[v] = f
|
||||
cv.state.font = f
|
||||
}
|
||||
}
|
||||
}
|
||||
cv.state.font = cv.getFont(src)
|
||||
// switch v := src.(type) {
|
||||
// case *Font:
|
||||
// cv.state.font = v
|
||||
// case *truetype.Font:
|
||||
// cv.state.font = &Font{font: v}
|
||||
// case string:
|
||||
// if f, ok := fonts[v]; ok {
|
||||
// cv.state.font = f
|
||||
// } else {
|
||||
// f, err := cv.LoadFont(v)
|
||||
// if err != nil {
|
||||
// fmt.Fprintf(os.Stderr, "Error loading font %s: %v\n", v, err)
|
||||
// fonts[v] = nil
|
||||
// } else {
|
||||
// fonts[v] = f
|
||||
// cv.state.font = f
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
cv.state.fontSize = size
|
||||
|
||||
|
@ -481,3 +486,29 @@ func (cv *Canvas) IsPointInStroke(x, y float64) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (cv *Canvas) reduceCache(keepSize int) {
|
||||
var total int
|
||||
for _, img := range cv.images {
|
||||
w, h := img.img.Size()
|
||||
total += w * h * 4
|
||||
}
|
||||
if total <= keepSize {
|
||||
return
|
||||
}
|
||||
list := make([]*Image, 0, len(cv.images))
|
||||
for _, img := range cv.images {
|
||||
list = append(list, img)
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].lastUsed.Before(list[j].lastUsed)
|
||||
})
|
||||
pos := 0
|
||||
for total > keepSize {
|
||||
img := list[pos]
|
||||
pos++
|
||||
delete(cv.images, img.src)
|
||||
w, h := img.img.Size()
|
||||
total -= w * h * 4
|
||||
}
|
||||
}
|
||||
|
|
93
images.go
93
images.go
|
@ -7,7 +7,6 @@ import (
|
|||
"image"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -22,32 +21,6 @@ type Image struct {
|
|||
lastUsed time.Time
|
||||
}
|
||||
|
||||
func (cv *Canvas) reduceCache(keepSize int) {
|
||||
var total int
|
||||
for _, img := range cv.images {
|
||||
w, h := img.img.Size()
|
||||
total += w * h * 4
|
||||
}
|
||||
if total <= keepSize {
|
||||
return
|
||||
}
|
||||
list := make([]*Image, 0, len(cv.images))
|
||||
for _, img := range cv.images {
|
||||
list = append(list, img)
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].lastUsed.Before(list[j].lastUsed)
|
||||
})
|
||||
pos := 0
|
||||
for total > keepSize {
|
||||
img := list[pos]
|
||||
pos++
|
||||
delete(cv.images, img.src)
|
||||
w, h := img.img.Size()
|
||||
total -= w * h * 4
|
||||
}
|
||||
}
|
||||
|
||||
// LoadImage loads an image. The src parameter can be either an image from the
|
||||
// standard image package, a byte slice that will be loaded, or a file name
|
||||
// string. If you want the canvas package to load the image, make sure you
|
||||
|
@ -62,7 +35,7 @@ func (cv *Canvas) LoadImage(src interface{}) (*Image, error) {
|
|||
return img, nil
|
||||
}
|
||||
}
|
||||
cv.reduceCache(Performance.ImageCacheSize)
|
||||
cv.reduceCache(Performance.CacheSize)
|
||||
var srcImg image.Image
|
||||
switch v := src.(type) {
|
||||
case image.Image:
|
||||
|
@ -100,54 +73,38 @@ func (cv *Canvas) LoadImage(src interface{}) (*Image, error) {
|
|||
}
|
||||
|
||||
func (cv *Canvas) getImage(src interface{}) *Image {
|
||||
if img, ok := src.(*Image); ok {
|
||||
return img
|
||||
} else if img, ok := cv.images[src]; ok {
|
||||
return img
|
||||
}
|
||||
cv.reduceCache(Performance.ImageCacheSize)
|
||||
switch v := src.(type) {
|
||||
case *Image:
|
||||
return v
|
||||
case image.Image:
|
||||
img, err := cv.LoadImage(v)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading image: %v\n", err)
|
||||
cv.images[src] = nil
|
||||
return nil
|
||||
if cv2, ok := src.(*Canvas); ok {
|
||||
if !cv.b.CanUseAsImage(cv2.b) {
|
||||
w, h := cv2.Size()
|
||||
return cv.getImage(cv2.GetImageData(0, 0, w, h))
|
||||
}
|
||||
cv.images[v] = img
|
||||
return img
|
||||
case string:
|
||||
img, err := cv.LoadImage(v)
|
||||
if err != nil {
|
||||
bimg := cv2.b.AsImage()
|
||||
if bimg == nil {
|
||||
w, h := cv2.Size()
|
||||
return cv.getImage(cv2.GetImageData(0, 0, w, h))
|
||||
}
|
||||
return &Image{cv: cv, img: bimg}
|
||||
}
|
||||
|
||||
img, err := cv.LoadImage(src)
|
||||
if err != nil {
|
||||
cv.images[src] = nil
|
||||
switch v := src.(type) {
|
||||
case image.Image:
|
||||
fmt.Fprintf(os.Stderr, "Error loading image: %v\n", err)
|
||||
case string:
|
||||
if strings.Contains(strings.ToLower(err.Error()), "format") {
|
||||
fmt.Fprintf(os.Stderr, "Error loading image %s: %v\nIt may be necessary to import the appropriate decoder, e.g.\nimport _ \"image/jpeg\"\n", v, err)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error loading image %s: %v\n", v, err)
|
||||
}
|
||||
cv.images[src] = nil
|
||||
return nil
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Failed to load image: %v\n", err)
|
||||
}
|
||||
cv.images[v] = img
|
||||
return img
|
||||
case *Canvas:
|
||||
if !cv.b.CanUseAsImage(v.b) {
|
||||
w, h := v.Size()
|
||||
return cv.getImage(v.GetImageData(0, 0, w, h))
|
||||
}
|
||||
bimg := v.b.AsImage()
|
||||
if bimg == nil {
|
||||
w, h := v.Size()
|
||||
return cv.getImage(v.GetImageData(0, 0, w, h))
|
||||
}
|
||||
img := &Image{cv: cv, img: bimg}
|
||||
cv.images[v] = img
|
||||
return img
|
||||
} else {
|
||||
cv.images[src] = img
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Unknown image type: %T\n", src)
|
||||
cv.images[src] = nil
|
||||
return nil
|
||||
return img
|
||||
}
|
||||
|
||||
// Width returns the width of the image
|
||||
|
|
40
text.go
40
text.go
|
@ -2,9 +2,11 @@ package canvas
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
|
@ -20,7 +22,16 @@ type Font struct {
|
|||
font *truetype.Font
|
||||
}
|
||||
|
||||
var fonts = make(map[string]*Font)
|
||||
type fontKey struct {
|
||||
font *Font
|
||||
size fixed.Int26_6
|
||||
}
|
||||
|
||||
type fontCache struct {
|
||||
font *Font
|
||||
frctx *frContext
|
||||
}
|
||||
|
||||
var zeroes [alphaTexSize]byte
|
||||
var textImage *image.Alpha
|
||||
|
||||
|
@ -29,6 +40,14 @@ var defaultFont *Font
|
|||
// LoadFont loads a font and returns the result. The font
|
||||
// can be a file name or a byte slice in TTF format
|
||||
func (cv *Canvas) LoadFont(src interface{}) (*Font, error) {
|
||||
if f, ok := src.(*Font); ok {
|
||||
return f, nil
|
||||
} else if _, ok := src.([]byte); !ok {
|
||||
if f, ok := cv.fonts[src]; ok {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
|
||||
var f *Font
|
||||
switch v := src.(type) {
|
||||
case *truetype.Font:
|
||||
|
@ -55,9 +74,28 @@ func (cv *Canvas) LoadFont(src interface{}) (*Font, error) {
|
|||
if defaultFont == nil {
|
||||
defaultFont = f
|
||||
}
|
||||
|
||||
if _, ok := src.([]byte); !ok {
|
||||
cv.fonts[src] = f
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (cv *Canvas) getFont(src interface{}) *Font {
|
||||
f, err := cv.LoadFont(src)
|
||||
if err != nil {
|
||||
cv.fonts[src] = nil
|
||||
fmt.Fprintf(os.Stderr, "Error loading font: %v\n", err)
|
||||
} else {
|
||||
cv.fonts[src] = f
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// func (cv *Canvas) getFRContext(src interface{}, size float64) *Font {
|
||||
|
||||
// }
|
||||
|
||||
// FillText draws the given string at the given coordinates
|
||||
// using the currently set font and font height
|
||||
func (cv *Canvas) FillText(str string, x, y float64) {
|
||||
|
|
Loading…
Reference in a new issue