Encoding an Image to JPEG in Go
Asked Answered
W

2

6

I have a struct called SpriteImage which is defined like this:

type SpriteImage struct {
    dimentions      image.Point
    lastImgPosition image.Point
    sprite          *image.NRGBA
}

In my flow, I first initiate a new such struct:

func NewSpriteImage(width, height int) SpriteImage {
    c := color.RGBA{0xff, 0xff, 0xff, 0xff}
    blankImage := imaging.New(width, height, c)

    return SpriteImage{
        dimentions:      image.Point{X: width, Y: height},
        lastImgPosition: image.Point{X: 0, Y: 0},
        sprite:          blankImage,
    }
}

And then I add images to this SpriteImage like so:

func (s *SpriteImage) AddImage(img image.Image) error {
    imgWidth := img.Bounds().Dx()
    imgHeight := img.Bounds().Dy()

    // Make sure new image will fit into the sprite.
    if imgWidth != s.dimentions.X {
        return fmt.Errorf("image width %d mismatch sprite width %d", imgWidth, s.dimentions.X)
    }

    spriteHeightLeft := s.dimentions.Y - s.lastImgPosition.Y
    if imgHeight > spriteHeightLeft {
        return fmt.Errorf("image height %d won't fit into sprite, sprite free space %d ", imgHeight, s.dimentions.Y)
    }

    // add image to sprite
    s.sprite = imaging.Paste(s.sprite, img, s.lastImgPosition)

    // update next image position within sprite
    s.lastImgPosition = s.lastImgPosition.Add(image.Point{X: 0, Y: imgHeight})

    return nil
}

Eventually, I want to take this SpriteImage and encode it as JPEG. But it doesn't seem to work. The native JPEG Encode function takes up an image, but I have an image.NRGBA. So I'm using github.com/disintegration/imaging lib like so:

func (s SpriteImage) GetBytes() ([]byte, error) {
    var b bytes.Buffer
    w := bufio.NewWriter(&b)

    if s.sprite == nil {
        return nil, fmt.Errorf("sprite is nil")
    }

    if err := imaging.Encode(w, s.sprite, imaging.JPEG); err != nil {
        return nil, err
    }

    return b.Bytes(), nil
}

However is seems that the bytes returned are not in fact JPEG. The native Go JPEG lib will not decode those bytes to a Go image struct. If I try to decode those bytes to image like so:

     m, _, err := image.Decode(reader)
     if err != nil {
        log.Fatal(err)
     }

I'm getting err:

image: unknown format

Any ideas?

Wilt answered 19/9, 2016 at 15:57 Comment(0)
W
3

So, it looks like this may be an issue with the github.com/disintegration/imaging lib. I posted an issue about it in the repo's page.

If I change my GetBytes() function to this, I get valid JPEG:

func (s SpriteImage) GetBytes() ([]byte, error) {
    var b bytes.Buffer
    w := bufio.NewWriter(&b)

    if s.sprite == nil {
        return nil, fmt.Errorf("sprite is nil")
    }

    im := s.sprite
    err := jpeg.Encode(w, im, &jpeg.Options{jpegCompression})

    if err != nil {
        fmt.Println(err)
    }

    return b.Bytes(), nil
}
Wilt answered 19/9, 2016 at 16:22 Comment(0)
A
2

Maybe something to check: The image: unknown format is a common error when you don't include the image/jpeg lib and specifically decode it with it:

import (
  "image/jpeg"
  "io"
)

...    
  img, err := jpeg.Decode(r)
  if err != nil {
     return err
  }
...

Some try to use image.Decode, but you still need to include the image/jpeg lib.

I believe it works with a blind include:

import (
  _ "image/jpeg"
  "image"
  "io"
)

...    
  img, err := image.Decode(r)
  if err != nil {
     return err
  }
...
Arkose answered 19/9, 2016 at 16:8 Comment(1)
Thank you for this suggestion, this was my problem. I wonder why this is not mentioned in the image.Decode docs.Sematic

© 2022 - 2024 — McMap. All rights reserved.