使用image.Image或* image.RGBA的Set()方法

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.

reflect.TypeOf(img) gives you the reflection type of the value in the interface img, if its an interface. In this case, img is an interface, an image.Image which contains an *image.RGBA.

You can fix your code by converting img to an *image.RGBA, or more robustly, define an interface type with the right Set method, and convert img to that (the draw.Image interface in "image/draw" works perfectly for this, as noted by @DaveC). The interface type is preferable if you aren't sure that png.Decode will always give you an *image.RGBA for the .png files you have.

img.(*image.RGBA).Set(0, 0, color.RGBA{136, 0, 21, 255})

or

type Setter interface {
    Set(x, y int, c color.Color)
}

img.(Setter).Set(0, 0, color.RGBA{136, 0, 21, 255})

or (probably best):

import "image/draw"

...

img.(draw.Image).Set(0, 0, color.RGBA{136, 0, 21, 255})

The compiler is correct, the image.Image type is an interface that does not include the Set() function.

I am not an expert at the image library but my cursory look at the types seems to suggest you can take an Image type and use the Bounds() method to get a image.Rectangle to create a new RGBA type as done previously in your example code.

// Your current image manipulation
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})

// You can create a image.RGBA type by passing the image.Rectangle
// returned from image.Image.Bounds()
m = image.NewRGBA(img.Bounds())
m.Set(0, 0, color.RGBA{136, 0, 21, 255})

This is a strict answer to your type issue but I don't have any gurantee it accomplishes your end goals.

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).