Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the Set() method of an image.Image or *image.RGBA

I am doing some practice with the Go image package with my free time this summer.

package main

import (

    "os"
    "image"
    "image/png"
    "image/color"
    "log"
    "fmt"
    "reflect"

)

func main(){

    file , err := os.OpenFile("C:/Sources/go3x3.png", os.O_RDWR, os.FileMode(0777))
    if err != nil {
        log.Fatal(err)
    }

    img , err := png.Decode(file)
    if err != nil {
        log.Fatal(err)
    }


    img.At(0,0).RGBA()
    fmt.Println("type:", reflect.TypeOf(img))

    m := image.NewRGBA(image.Rect(0, 0, 640, 480))
    fmt.Println("type:", reflect.TypeOf(m))
    m.Set(5, 5, color.RGBA{255, 0, 0, 255})
    img.Set(0, 0, color.RGBA{136, 0, 21, 255})
}

The problem here is when I run it with the img.Set commented out I get this result

type: *image.RGBA
type: *image.RGBA

but when it's uncommented I get an error saying

img.Set undefined (type image.Image has no field or method Set)

I'm assuming I'm using reflect wrong, I'm still fully grasping the whole interface and type definitions in Go.

like image 446
Bradley Nowacki Avatar asked Feb 11 '23 03:02

Bradley Nowacki


1 Answers

To expand on a previous answer answer:

png.Decode may create one of several different underlying image types (*image.Gray, *image.RGBA, *image.Paletted, *image.NRGBA, etc). It returns whatever image it created as an image.Image interface which provides read only access to the data.

However, all (most?) of the actual image types it returns do implement the Set method for simple write access. The way you can safely test for and use this method is via the existing draw.Image interface from the image/draw package. It's just this:

// From image/draw:
// Image is an image.Image with a Set method to change a single pixel.
type Image interface {
        image.Image
        Set(x, y int, c color.Color)
}

So you could do something like:

func drawablePNGImage(r io.Reader) (draw.Image, error) {
    img, err := png.Decode(r)
    if err != nil {
        return nil, err
    }
    dimg, ok := img.(draw.Image)
    if !ok {
        return nil, fmt.Errorf("%T is not a drawable image type", img)
    }
    return dimg, nil
}

Playground (shows an example calling all the image.Image methods as well as Set).

Edit for Go1.17+:

Note that Go1.17 added draw.RGBA64Image with a SetRGBA64 method. As with draw.Image, all of the standard image types implement this. The advantage of this method is that the color values are not boxed in the color.Color interface type so doing many pixel operations can be faster. Also note that Go1.18 added optimisations to the draw.Draw and draw.DrawMask fallback implementations for images that implement the optional draw.RGBA64Image and image.RGBA64Image interfaces.

like image 170
Dave C Avatar answered Feb 13 '23 03:02

Dave C