|
|
|
@ -3,11 +3,11 @@ package main |
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"os" |
|
|
|
"os" |
|
|
|
"image" |
|
|
|
"image" |
|
|
|
|
|
|
|
"encoding/json" |
|
|
|
"log" |
|
|
|
"log" |
|
|
|
"image/png" |
|
|
|
"image/png" |
|
|
|
"github.com/disintegration/gift" |
|
|
|
"github.com/disintegration/gift" |
|
|
|
"flag" |
|
|
|
"flag" |
|
|
|
"math" |
|
|
|
|
|
|
|
"lcthw.dev/go/jankifier/filters" |
|
|
|
"lcthw.dev/go/jankifier/filters" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
@ -36,44 +36,69 @@ func SaveImage(filename string, img image.Image) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type Opts struct { |
|
|
|
type Opts struct { |
|
|
|
InFile string |
|
|
|
Config string |
|
|
|
OutFile string |
|
|
|
Force bool |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Conversion struct { |
|
|
|
PixelWidth int |
|
|
|
PixelWidth int |
|
|
|
ColorDepth int |
|
|
|
ColorDepth int |
|
|
|
DitherType int |
|
|
|
DitherType int |
|
|
|
|
|
|
|
Width int |
|
|
|
|
|
|
|
Height int |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Settings struct { |
|
|
|
|
|
|
|
Source string |
|
|
|
|
|
|
|
Target string |
|
|
|
|
|
|
|
Base Conversion |
|
|
|
|
|
|
|
Exceptions map[string]Conversion |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func LoadSettings(path string) Settings { |
|
|
|
|
|
|
|
var settings Settings |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config, err := os.ReadFile(path) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
log.Fatalf("invalid config path: %s", path) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = json.Unmarshal(config, &settings) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
log.Fatalf("json format error:", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return settings |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func ParseOpts() Opts { |
|
|
|
func ParseOpts() Opts { |
|
|
|
var opts Opts |
|
|
|
var opts Opts |
|
|
|
|
|
|
|
|
|
|
|
flag.StringVar(&opts.InFile, "input", "", "input file.png") |
|
|
|
flag.StringVar(&opts.Config, "config", "config.json", "config file") |
|
|
|
flag.StringVar(&opts.OutFile, "output", "", "output file.png") |
|
|
|
flag.BoolVar(&opts.Force, "force", false, "force a full convert") |
|
|
|
flag.IntVar(&opts.PixelWidth, "pixel-width", 4, "pixel width") |
|
|
|
|
|
|
|
flag.IntVar(&opts.ColorDepth, "color-depth", 16, "number of colors in the palette") |
|
|
|
|
|
|
|
flag.IntVar(&opts.DitherType, "dither", 0, "0=none, 1=floyd, 2=atkinson") |
|
|
|
|
|
|
|
flag.Parse() |
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
|
|
|
|
if opts.ColorDepth > math.MaxUint8 { |
|
|
|
|
|
|
|
log.Fatalf("color-depth can't be greater than %d", math.MaxUint8); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return opts |
|
|
|
return opts |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
func JankImage(settings Conversion, in_file string, out_file string) { |
|
|
|
opts := ParseOpts() |
|
|
|
src := LoadImage(in_file) |
|
|
|
|
|
|
|
|
|
|
|
src := LoadImage(opts.InFile) |
|
|
|
|
|
|
|
bounds := src.Bounds() |
|
|
|
bounds := src.Bounds() |
|
|
|
|
|
|
|
|
|
|
|
resize := gift.Resize(bounds.Max.X / opts.PixelWidth, 0, gift.NearestNeighborResampling) |
|
|
|
resize := gift.Resize(bounds.Max.X / settings.PixelWidth, 0, gift.NearestNeighborResampling) |
|
|
|
posterize := filters.Posterize(uint16(opts.ColorDepth), opts.DitherType) |
|
|
|
posterize := filters.Posterize(uint16(settings.ColorDepth), settings.DitherType) |
|
|
|
upscale := filters.Upscale(bounds, opts.PixelWidth) |
|
|
|
upscale := filters.Upscale(bounds, settings.PixelWidth) |
|
|
|
sharpen := gift.UnsharpMask(1, 1, 0) |
|
|
|
sharpen := gift.UnsharpMask(1, 1, 0) |
|
|
|
|
|
|
|
|
|
|
|
g := gift.New(resize, posterize, upscale, sharpen) |
|
|
|
g := gift.New(resize, posterize, upscale, sharpen) |
|
|
|
smaller := image.NewNRGBA(g.Bounds(bounds)) |
|
|
|
out_img := image.NewNRGBA(g.Bounds(bounds)) |
|
|
|
g.Draw(smaller, src) |
|
|
|
g.Draw(out_img, src) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SaveImage(out_file, out_img) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
SaveImage(opts.OutFile, smaller) |
|
|
|
func main() { |
|
|
|
|
|
|
|
opts := ParseOpts() |
|
|
|
|
|
|
|
settings := LoadSettings(opts.Config) |
|
|
|
|
|
|
|
JankImage(settings.Base, settings.Source, settings.Target) |
|
|
|
} |
|
|
|
} |
|
|
|
|