Skip to content

Instantly share code, notes, and snippets.

@dskinner
Last active December 11, 2015 23:39
Show Gist options
  • Save dskinner/4678567 to your computer and use it in GitHub Desktop.
Save dskinner/4678567 to your computer and use it in GitHub Desktop.
Resizes android xhdpi resources for ldpi, mdpi, and hdpi.
/*
Usage of droidscale:
-interp="Lanzcos3": interpolation to use. NearestNeighbor, Bilinear, Bicubic, MitchellNetravali, Lanzcos2, Lanzcos3.
-out="./out": directory to place resized images.
-path="": path of xhdpi drawables to process, can point to a single file.
Resizes xhdpi resources for ldpi, mdpi, and hdpi. Contents of flag path will
be walked and new files written to flag out will be flattened. This means
you can organize the contents of flag path with subdirectories to target
different interpolations for use on a particular set of images. For example:
droidscale -path="xhdpi/L2" -interp="Lanzcos2"
droidscale -path="xhdpi/L3" -interp="Lanzcos3"
Inspecting the contents of ./out/drawable-mdpi/ and friends will show a flat
list of the resized files.
*/
package main
import (
"flag"
"fmt"
"github.com/nfnt/resize"
"image/png"
"os"
"path/filepath"
"runtime"
)
var flagPath = flag.String("path", "", "path of xhdpi drawables to process, can point to a single file.")
var flagOut = flag.String("out", "./out", "directory to place resized images.")
var flagInterp = flag.String("interp", "Lanzcos3", "interpolation to use. NearestNeighbor, Bilinear, Bicubic, MitchellNetravali, Lanzcos2, Lanzcos3.")
var interp resize.InterpolationFunction
// dpi represents a density by its xhdpi scale factor.
type dpi float64
// scale takes an image dimension and makes suitable for pkg resize.
func (d dpi) scale(n int) uint {
return uint(float64(n) * float64(d))
}
const (
ldpi dpi = 0.375
mdpi = 0.5
hdpi = 0.75
xhdpi = 1
)
var dpiString = map[dpi]string{
ldpi: "drawable-ldpi",
mdpi: "drawable-mdpi",
hdpi: "drawable-hdpi",
xhdpi: "drawable-xhdpi",
}
// visit is a filepath.WalkFn that handles finding and resizing png resources.
func visit(path string, f os.FileInfo, err error) (e error) {
if filepath.Ext(path) != ".png" {
return
}
file, err := os.Open(path)
if err != nil {
fmt.Printf("Failed to open file %s\n", path)
return
}
defer file.Close()
img, err := png.Decode(file)
if err != nil {
fmt.Printf("Failed to decode png %s\n", path)
return
}
fn := func(factor dpi) {
x := factor.scale(img.Bounds().Size().X)
m := resize.Resize(x, 0, img, interp)
p := filepath.Join(*flagOut, dpiString[factor], f.Name())
d := filepath.Dir(p)
os.MkdirAll(d, 0766)
out, err := os.Create(p)
if err != nil {
fmt.Printf("Failed to create new file %s\n", p)
return
}
defer out.Close()
if err := png.Encode(out, m); err != nil {
fmt.Printf("Failed to encode resized %s\n", f.Name())
return
}
}
fmt.Printf("Resizing %s\n", f.Name())
fn(ldpi)
fn(mdpi)
fn(hdpi)
return
}
func usage() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, "\n"+`Resizes xhdpi resources for ldpi, mdpi, and hdpi. Contents of flag path will
be walked and new files written to flag out will be flattened. This means
you can organize the contents of flag path with subdirectories to target
different interpolations for use on a particular set of images. For example:
droidscale -path="xhdpi/L2" -interp="Lanzcos2"
droidscale -path="xhdpi/L3" -interp="Lanzcos3"
Inspecting the contents of ./out/drawable-mdpi/ and friends will show a flat
list of the resized files.`)
}
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
flag.Usage = usage
flag.Parse()
}
func main() {
if *flagPath == "" {
flag.Usage()
return
}
switch *flagInterp {
case "NearestNeighbor":
interp = resize.NearestNeighbor
case "Bilinear":
interp = resize.Bilinear
case "Bicubic":
interp = resize.Bicubic
case "MitchellNetravali":
interp = resize.MitchellNetravali
case "Lanzcos2":
interp = resize.Lanczos2
case "Lanzcos3":
interp = resize.Lanczos3
default:
flag.Usage()
fmt.Printf("\nReceived invalid interp: %s\nSee usage above.\n", *flagInterp)
return
}
filepath.Walk(*flagPath, visit)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment