|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "encoding/base64" |
| 5 | + "encoding/binary" |
| 6 | + "fmt" |
| 7 | + "image" |
| 8 | + "image/draw" |
| 9 | + "image/png" |
| 10 | + "io" |
| 11 | + "log" |
| 12 | + "os" |
| 13 | + "path/filepath" |
| 14 | + "sync" |
| 15 | + |
| 16 | + "github.com/atotto/clipboard" |
| 17 | + "github.com/mkideal/cli" |
| 18 | +) |
| 19 | + |
| 20 | +type encodeCmdT struct { |
| 21 | + cli.Helper |
| 22 | + Clipboard bool `cli:"c,clipboard" usage:"write directly to system's clipboard instead of stdout"` |
| 23 | + OutputName string `cli:"o,output" usage:"write to this file instead of stdout"` |
| 24 | + Layer uint `cli:"l,layer" usage:"set blueprint layer: 0=logic, 1=on, 2=off" dft:"0"` |
| 25 | +} |
| 26 | + |
| 27 | +var encodeDesc = fmt.Sprintf( |
| 28 | + `example: %s encode [options] <file> |
| 29 | + Takes in a PNG image containing the circuit and outputs a corresponding blueprint.`, |
| 30 | + filepath.Base(os.Args[0]), |
| 31 | +) |
| 32 | + |
| 33 | +var encodeCmd = &cli.Command{ |
| 34 | + Name: "encode", |
| 35 | + Aliases: []string{"e"}, |
| 36 | + Desc: "Takes in a PNG image containing the circuit and outputs a corresponding blueprint.", |
| 37 | + Text: fmt.Sprintf("example: %s encode [options] <file>", filepath.Base(os.Args[0])), |
| 38 | + Argv: func() interface{} { return new(encodeCmdT) }, |
| 39 | + |
| 40 | + Fn: func(ctx *cli.Context) error { |
| 41 | + argv := ctx.Argv().(*encodeCmdT) |
| 42 | + args := ctx.Args() |
| 43 | + |
| 44 | + var outputFile io.WriteCloser = os.Stdout |
| 45 | + var outputReader io.Reader |
| 46 | + var outputString string |
| 47 | + |
| 48 | + // Open and decode image file. |
| 49 | + if len(ctx.Args()) < 1 { |
| 50 | + ctx.WriteUsage() |
| 51 | + log.Fatalf("Missing file argument\n") |
| 52 | + } |
| 53 | + |
| 54 | + file, err := os.Open(args[0]) |
| 55 | + if err != nil { |
| 56 | + log.Fatalf("Failed to open file (%s): %v\n", args[0], err) |
| 57 | + } |
| 58 | + |
| 59 | + if ctx.IsSet("-o") { |
| 60 | + outputFile, err = os.Create(argv.OutputName) |
| 61 | + if err != nil { |
| 62 | + log.Fatalf("Failed to create output file (%s): %v\n", argv.OutputName, err) |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + var wg sync.WaitGroup |
| 67 | + if argv.Clipboard { |
| 68 | + outputReader, outputFile = io.Pipe() |
| 69 | + wg.Add(1) |
| 70 | + go func() { |
| 71 | + defer wg.Done() |
| 72 | + buf, _ := io.ReadAll(outputReader) |
| 73 | + outputString = string(buf) |
| 74 | + }() |
| 75 | + } |
| 76 | + |
| 77 | + srcimg, err := png.Decode(file) |
| 78 | + if err != nil { |
| 79 | + log.Fatalf("Failed to decode file (%s): %v\n", args[0], err) |
| 80 | + } |
| 81 | + img := image.NewRGBA(srcimg.Bounds()) |
| 82 | + draw.Draw(img, img.Bounds(), srcimg, img.Bounds().Min, draw.Src) |
| 83 | + |
| 84 | + // Create blueprint data. |
| 85 | + var blueprint Blueprint |
| 86 | + |
| 87 | + blueprint.Pixels, err = Compress(img.Pix) |
| 88 | + if err != nil { |
| 89 | + log.Fatalf("Failed to compress pixel data: %v\n", err) |
| 90 | + } |
| 91 | + |
| 92 | + blueprint.Footer.HeightType = 2 |
| 93 | + blueprint.Footer.WidthType = 2 |
| 94 | + blueprint.Footer.BytesType = 2 |
| 95 | + blueprint.Footer.LayerType = 2 |
| 96 | + blueprint.Height = int32(img.Rect.Dy()) |
| 97 | + blueprint.Width = int32(img.Rect.Dx()) |
| 98 | + blueprint.Bytes = int32(len(img.Pix)) |
| 99 | + blueprint.Layer = ToLayer(argv.Layer) |
| 100 | + |
| 101 | + // fmt.Println("Blueprint layer: ", blueprint.Layer) |
| 102 | + |
| 103 | + if blueprint.Layer == -1 { |
| 104 | + log.Fatalf("Got unknown layer type: %d\n", argv.Layer) |
| 105 | + } |
| 106 | + |
| 107 | + w := base64.NewEncoder(base64.StdEncoding, outputFile) |
| 108 | + _, err = w.Write(blueprint.Pixels) |
| 109 | + if err != nil { |
| 110 | + log.Fatalf("Failed to write blueprint data: %v\n", err) |
| 111 | + } |
| 112 | + err = binary.Write(w, binary.LittleEndian, blueprint.Footer) |
| 113 | + if err != nil { |
| 114 | + log.Fatalf("Failed to write blueprint data: %v\n", err) |
| 115 | + } |
| 116 | + w.Close() |
| 117 | + |
| 118 | + fmt.Fprintln(outputFile) |
| 119 | + outputFile.Close() |
| 120 | + |
| 121 | + if argv.Clipboard { |
| 122 | + wg.Wait() |
| 123 | + err = clipboard.WriteAll(outputString) |
| 124 | + if err != nil { |
| 125 | + log.Fatalf("Failed to write to clipboard: %v\n", err) |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + return nil |
| 130 | + }, |
| 131 | +} |
0 commit comments