package imageproc import ( "fmt" "image" "image/color" _ "image/gif" _ "image/jpeg" _ "image/png" "io" "os" "github.com/disintegration/imaging" dither "github.com/makeworld-the-better-one/dither" "bleh/internal/mxw01" ) // DecodeImage loads an image from a given path or stdin ("-"). func DecodeImage(path string) (image.Image, error) { if path == "-" { return DecodeImageFromReader(os.Stdin) } img, err := imaging.Open(path, imaging.AutoOrientation(true)) if err != nil { return nil, fmt.Errorf("failed to open image %q: %v", path, err) } return img, nil } func DecodeImageFromReader(r io.Reader) (image.Image, error) { img, _, err := image.Decode(r) if err != nil { return nil, fmt.Errorf("decode error: %v", err) } return img, nil } func PadImageToMinLines(img image.Image, minLines int) image.Image { bounds := img.Bounds() if bounds.Dy() >= minLines { return img } dst := imaging.New(bounds.Dx(), minLines, color.White) dst = imaging.Paste(dst, img, image.Pt(0, 0)) return dst } func LoadImageMonoFromImage(img image.Image, ditherType string) ([]byte, int, error) { b := img.Bounds() if b.Dx() <= 0 || b.Dy() <= 0 { return nil, 0, fmt.Errorf("invalid image bounds: %v", b) } ratio := float64(b.Dx()) / float64(b.Dy()) height := int(float64(mxw01.LinePixels) / ratio) if height <= 0 { return nil, 0, fmt.Errorf("computed invalid height: %d", height) } img = imaging.Resize(img, mxw01.LinePixels, height, imaging.Lanczos) img = imaging.Grayscale(img) if ditherType != "none" { palette := []color.Color{color.Black, color.White} d := dither.NewDitherer(palette) switch ditherType { case "floyd": d.Matrix = dither.FloydSteinberg case "bayer2x2": d.Mapper = dither.Bayer(2, 2, 1.0) case "bayer4x4": d.Mapper = dither.Bayer(4, 4, 1.0) case "bayer8x8": d.Mapper = dither.Bayer(8, 8, 1.0) case "bayer16x16": d.Mapper = dither.Bayer(16, 16, 1.0) case "atkinson": d.Matrix = dither.Atkinson case "jjn": d.Matrix = dither.JarvisJudiceNinke default: return nil, 0, fmt.Errorf("unknown dither type: %s", ditherType) } img = d.DitherCopy(img) } else { img = imaging.AdjustContrast(img, 10) } pixels := make([]byte, (mxw01.LinePixels*height)/8) for y := 0; y < height; y++ { for x := 0; x < mxw01.LinePixels; x++ { gray := color.GrayModel.Convert(img.At(x, y)).(color.Gray) if gray.Y < 128 { idx := (y*mxw01.LinePixels + x) / 8 pixels[idx] |= 1 << (x % 8) } } } return pixels, height, nil } func LoadImage4BitFromImage(img image.Image, ditherType string) ([]byte, int, error) { b := img.Bounds() if b.Dx() <= 0 || b.Dy() <= 0 { return nil, 0, fmt.Errorf("invalid image bounds: %v", b) } ratio := float64(b.Dx()) / float64(b.Dy()) height := int(float64(mxw01.LinePixels) / ratio) if height <= 0 { return nil, 0, fmt.Errorf("computed invalid height: %d", height) } img = imaging.Resize(img, mxw01.LinePixels, height, imaging.Lanczos) img = imaging.Grayscale(img) palette := make([]color.Color, 16) for i := 0; i < 16; i++ { v := uint8(i * 17) palette[i] = color.Gray{Y: 255 - v} } if ditherType != "none" { d := dither.NewDitherer(palette) switch ditherType { case "floyd": d.Matrix = dither.FloydSteinberg case "bayer2x2": d.Mapper = dither.Bayer(2, 2, 0.2) case "bayer4x4": d.Mapper = dither.Bayer(4, 4, 0.2) case "bayer8x8": d.Mapper = dither.Bayer(8, 8, 0.2) case "bayer16x16": d.Mapper = dither.Bayer(16, 16, 0.2) case "atkinson": d.Matrix = dither.Atkinson case "jjn": d.Matrix = dither.JarvisJudiceNinke default: return nil, 0, fmt.Errorf("unknown dither type: %s", ditherType) } img = d.DitherCopy(img) } width := mxw01.LinePixels pixels := make([]byte, (width*height)/2) for y := 0; y < height; y++ { for x := 0; x < width; x++ { gray := color.GrayModel.Convert(img.At(x, y)).(color.Gray) level := (255 - gray.Y) >> 4 idx := (y*width + x) >> 1 shift := uint(((x & 1) ^ 1) << 2) pixels[idx] |= byte(level) << shift } } return pixels, height, nil } func RenderPreviewFrom1bpp(pixels []byte, width, height int) image.Image { img := image.NewGray(image.Rect(0, 0, width, height)) for y := 0; y < height; y++ { for x := 0; x < width; x++ { idx := (y*width + x) / 8 bit := uint(x % 8) if pixels[idx]&(1<> 1 shift := uint(((x & 1) ^ 1) << 2) val := (pixels[idx] >> shift) & 0x0F gray := 255 - val*17 img.SetGray(x, y, color.Gray{Y: gray}) } } return img }