Утвердить интерфейс к своему типу - PullRequest
0 голосов
/ 04 сентября 2018

В общем случае я не могу получить пиксели изображения в виде массива.

f, err := os.Open(imgPath)
check(err)
defer f.Close()
img, _, err := image.Decode(bufio.NewReader(f))
check(err)
pixels, err := getPixels(img)
check(err)
// Logic with pixels.

Теперь функция getPixels выглядит так:

func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(*image.NRGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.CMYK); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.NRGBA64); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Paletted); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA64); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}

Но я думаю, что это ужасно. Голанг знает тип изображения, и я бы предпочел что-то вроде этого:

func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(eval(fmt.Sprintf("%T", img))); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}

Я тоже не могу утверждать reflect.TypeOf(img). Может быть, есть способ получить тип из интерфейса reflect.Type?

1 Ответ

0 голосов
/ 04 сентября 2018

Ваша большая if ... else структура может быть упрощена с помощью переключателя типа , например:

func getPixels(img image.Image) ([]uint8, error) {
    switch i := img.(type) {
    case *image.NRGBA:
        return i.Pix, nil
    case *image.Alpha:
        return i.Pix, nil
    case *image.Alpha16:
        return i.Pix, nil
    case *image.CMYK:
        return i.Pix, nil
        // ...
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}

Где вам еще нужно перечислить все возможные типы, но это лучше.

Поскольку все реализации изображений являются указателями структуры, имеющими поле с именем Pix, вы можете использовать отражение, чтобы получить это поле. Эта реализация будет обрабатывать будущие реализации изображений без каких-либо изменений (если они также будут иметь структуру с полем Pix).

Вот как это будет выглядеть:

func getPix(img image.Image) ([]uint8, error) {
    v := reflect.ValueOf(img)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    if v.Kind() == reflect.Struct {
        pv := v.FieldByName("Pix")
        if pv.IsValid() {
            if pix, ok := pv.Interface().([]uint8); ok {
                return pix, nil
            }
        }
    }

    return nil, fmt.Errorf("unknown image type %T", img)
}

Тестирование:

fmt.Println(getPix(&image.NRGBA{}))
fmt.Println(getPix(&image.RGBA{}))

type unknownImage struct{ image.Image }
fmt.Println(getPix(unknownImage{}))

Вывод (попробуйте на Go Playground ):

[] <nil>
[] <nil>
[] unknown image type main.unknownImage
...