Login light
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// See page 234.

// The thumbnail package produces thumbnail-size images from
// larger images.  Only JPEG images are currently supported.
package thumbnail

import (
    "fmt"
    "image"
    "image/jpeg"
    "io"
    "os"
    "path/filepath"
    "strings"
)

// Image returns a thumbnail-size version of src.
func Image(src image.Image) image.Image {
    // Compute thumbnail size, preserving aspect ratio.
    xs := src.Bounds().Size().X
    ys := src.Bounds().Size().Y
    width, height := 128, 128
    if aspect := float64(xs) / float64(ys); aspect < 1.0 {
        width = int(128 * aspect) // portrait
    } else {
        height = int(128 / aspect) // landscape
    }
    xscale := float64(xs) / float64(width)
    yscale := float64(ys) / float64(height)

    dst := image.NewRGBA(image.Rect(0, 0, width, height))

    // a very crude scaling algorithm
    for x := 0; x < width; x++ {
        for y := 0; y < height; y++ {
            srcx := int(float64(x) * xscale)
            srcy := int(float64(y) * yscale)
            dst.Set(x, y, src.At(srcx, srcy))
        }
    }
    return dst
}

// ImageStream reads an image from r and
// writes a thumbnail-size version of it to w.
func ImageStream(w io.Writer, r io.Reader) error {
    src, _, err := image.Decode(r)
    if err != nil {
        return err
    }
    dst := Image(src)
    return jpeg.Encode(w, dst, nil)
}

// ImageFile2 reads an image from infile and writes
// a thumbnail-size version of it to outfile.
func ImageFile2(outfile, infile string) (err error) {
    in, err := os.Open(infile)
    if err != nil {
        return err
    }
    defer in.Close()

    out, err := os.Create(outfile)
    if err != nil {
        return err
    }

    if err := ImageStream(out, in); err != nil {
        out.Close()
        return fmt.Errorf("scaling %s to %s: %s", infile, outfile, err)
    }
    return out.Close()
}

// ImageFile reads an image from infile and writes
// a thumbnail-size version of it in the same directory.
// It returns the generated file name, e.g. "foo.thumb.jpeg".
func ImageFile(infile string) (string, error) {
    ext := filepath.Ext(infile) // e.g., ".jpg", ".JPEG"
    outfile := strings.TrimSuffix(infile, ext) + ".thumb" + ext
    return outfile, ImageFile2(outfile, infile)
}