package canvas import ( "image" "image/color" "image/draw" "fyne.io/fyne/v2" ) // Declare conformity with CanvasObject interface var _ fyne.CanvasObject = (*Raster)(nil) // Raster describes a raster image area that can render in a Fyne canvas type Raster struct { baseObject // Render the raster image from code Generator func(w, h int) image.Image // Set a translucency value > 0.0 to fade the raster Translucency float64 // Specify the type of scaling interpolation applied to the raster if it is not full-size // Since: 1.4.1 ScaleMode ImageScale } // Alpha is a convenience function that returns the alpha value for a raster // based on its Translucency value. The result is 1.0 - Translucency. func (r *Raster) Alpha() float64 { return 1.0 - r.Translucency } // Hide will set this raster to not be visible func (r *Raster) Hide() { r.baseObject.Hide() repaint(r) } // Move the raster to a new position, relative to its parent / canvas func (r *Raster) Move(pos fyne.Position) { r.baseObject.Move(pos) repaint(r) } // Resize on a raster image causes the new size to be set and then calls Refresh. // This causes the underlying data to be recalculated and a new output to be drawn. func (r *Raster) Resize(s fyne.Size) { if s == r.Size() { return } r.baseObject.Resize(s) Refresh(r) } // Refresh causes this raster to be redrawn with its configured state. func (r *Raster) Refresh() { Refresh(r) } // NewRaster returns a new Image instance that is rendered dynamically using // the specified generate function. // Images returned from this method should draw dynamically to fill the width // and height parameters passed to pixelColor. func NewRaster(generate func(w, h int) image.Image) *Raster { return &Raster{Generator: generate} } type pixelRaster struct { r *Raster img draw.Image } // NewRasterWithPixels returns a new Image instance that is rendered dynamically // by iterating over the specified pixelColor function for each x, y pixel. // Images returned from this method should draw dynamically to fill the width // and height parameters passed to pixelColor. func NewRasterWithPixels(pixelColor func(x, y, w, h int) color.Color) *Raster { pix := &pixelRaster{} pix.r = &Raster{ Generator: func(w, h int) image.Image { if pix.img == nil || pix.img.Bounds().Size().X != w || pix.img.Bounds().Size().Y != h { // raster first pixel, figure out color type var dst draw.Image rect := image.Rect(0, 0, w, h) switch pixelColor(0, 0, w, h).(type) { case color.Alpha: dst = image.NewAlpha(rect) case color.Alpha16: dst = image.NewAlpha16(rect) case color.CMYK: dst = image.NewCMYK(rect) case color.Gray: dst = image.NewGray(rect) case color.Gray16: dst = image.NewGray16(rect) case color.NRGBA: dst = image.NewNRGBA(rect) case color.NRGBA64: dst = image.NewNRGBA64(rect) case color.RGBA: dst = image.NewRGBA(rect) case color.RGBA64: dst = image.NewRGBA64(rect) default: dst = image.NewRGBA(rect) } pix.img = dst } for y := 0; y < h; y++ { for x := 0; x < w; x++ { pix.img.Set(x, y, pixelColor(x, y, w, h)) } } return pix.img }, } return pix.r } type subImg interface { SubImage(r image.Rectangle) image.Image } // NewRasterFromImage returns a new Raster instance that is rendered from the Go // image.Image passed in. // Rasters returned from this method will map pixel for pixel to the screen // starting img.Bounds().Min pixels from the top left of the canvas object. // Truncates rather than scales the image. // If smaller than the target space, the image will be padded with zero-pixels to the target size. func NewRasterFromImage(img image.Image) *Raster { return &Raster{ Generator: func(w int, h int) image.Image { bounds := img.Bounds() rect := image.Rect(0, 0, w, h) switch { case w == bounds.Max.X && h == bounds.Max.Y: return img case w >= bounds.Max.X && h >= bounds.Max.Y: // try quickly truncating if sub, ok := img.(subImg); ok { return sub.SubImage(image.Rectangle{ Min: bounds.Min, Max: image.Point{ X: bounds.Min.X + w, Y: bounds.Min.Y + h, }, }) } default: if !rect.Overlaps(bounds) { return image.NewUniform(color.RGBA{}) } bounds = bounds.Intersect(rect) } // respect the user's pixel format (if possible) var dst draw.Image switch i := img.(type) { case *image.Alpha: dst = image.NewAlpha(rect) case *image.Alpha16: dst = image.NewAlpha16(rect) case *image.CMYK: dst = image.NewCMYK(rect) case *image.Gray: dst = image.NewGray(rect) case *image.Gray16: dst = image.NewGray16(rect) case *image.NRGBA: dst = image.NewNRGBA(rect) case *image.NRGBA64: dst = image.NewNRGBA64(rect) case *image.Paletted: dst = image.NewPaletted(rect, i.Palette) case *image.RGBA: dst = image.NewRGBA(rect) case *image.RGBA64: dst = image.NewRGBA64(rect) default: dst = image.NewRGBA(rect) } draw.Draw(dst, bounds, img, bounds.Min, draw.Over) return dst }, } }