// Copyright 2026 Nigel Tao.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build ignore

// chroma-scatterplot.go decodes a source image from stdin, plots its pixels as
// a 2-D scatterplot in (Cb, Cr) space and PNG-encodes the result to stdout.
package main

import (
	"bytes"
	"compress/flate"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"io"
	"os"

	_ "image/jpeg"
)

func main() {
	if err := main1(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

func main1() error {
	if len(os.Args) != 1 {
		progName := "chroma-scatterplot"
		if len(os.Args) > 0 {
			progName = os.Args[0]
		}
		return fmt.Errorf("Usage: %s < src.jpeg > dst.png", progName)
	}

	src, _, err := image.Decode(os.Stdin)
	if err != nil {
		return err
	}
	dst := makeChromaScatterplot(src)
	return png.Encode(os.Stdout, dst)
}

func makeChromaScatterplot(src image.Image) *image.RGBA {
	dst := image.NewRGBA(image.Rect(0, 0, 256, 256))

	for i, v := range uncompressedMask {
		w := 0x30 + (v >> 4)
		dst.SetRGBA(i%256, i/256, color.RGBA{w, w, w, 0xFF})
	}

	bgColor := color.RGBA{0x30, 0x30, 0x30, 0xFF}
	for i := range 256 {
		dst.SetRGBA(i, 255-0x40, bgColor)
		dst.SetRGBA(i, 255-0x80, bgColor)
		dst.SetRGBA(i, 255-0xC0, bgColor)
		dst.SetRGBA(0x40, i, bgColor)
		dst.SetRGBA(0x80, i, bgColor)
		dst.SetRGBA(0xC0, i, bgColor)
	}

	if src != nil {
		src1 := src.(image.RGBA64Image)
		bounds := src1.Bounds()
		for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
			for x := bounds.Min.X; x < bounds.Max.X; x++ {
				c := src1.RGBA64At(x, y)
				_, cb, cr := color.RGBToYCbCr(
					uint8(c.R>>8),
					uint8(c.G>>8),
					uint8(c.B>>8))
				dst.Set(int(cb), 255-int(cr), color.YCbCr{0x80, cb, cr})
			}
		}
	}

	return dst
}

func downsample2x(src image.Image) *image.RGBA {
	src1, ok := src.(*image.RGBA)
	if !ok {
		src1 = image.NewRGBA(src.Bounds())
		draw.Draw(src1, src1.Bounds(), src, src.Bounds().Min, draw.Src)
	}

	avg := func(a uint8, b uint8, c uint8, d uint8) uint8 {
		return uint8((int(a) + int(b) + int(c) + int(d) + 2) / 4)
	}

	sBounds := src1.Bounds()
	dBounds := image.Rectangle{
		Min: image.Point{
			sBounds.Min.X / 2,
			sBounds.Min.Y / 2,
		},
		Max: image.Point{
			sBounds.Max.X / 2,
			sBounds.Max.Y / 2,
		},
	}
	dst := image.NewRGBA(dBounds)
	for y := dBounds.Min.Y; y < dBounds.Max.Y; y++ {
		for x := dBounds.Min.X; x < dBounds.Max.X; x++ {
			c0 := src1.RGBAAt((2*x)+0, (2*y)+0)
			c1 := src1.RGBAAt((2*x)+1, (2*y)+0)
			c2 := src1.RGBAAt((2*x)+0, (2*y)+1)
			c3 := src1.RGBAAt((2*x)+1, (2*y)+1)
			dst.SetRGBA(x, y, color.RGBA{
				R: avg(c0.R, c1.R, c2.R, c3.R),
				G: avg(c0.G, c1.G, c2.G, c3.G),
				B: avg(c0.B, c1.B, c2.B, c3.B),
				A: avg(c0.A, c1.A, c2.A, c3.A),
			})
		}
	}
	return dst
}

var uncompressedMask = func() []byte {
	u, _ := io.ReadAll(flate.NewReader(bytes.NewReader(compressedMask[:])))
	return u
}()

func makeCompressedMask() {
	const dimension = 1024

	m := image.NewRGBA(image.Rect(0, 0, dimension, dimension))

	const N = 2 * dimension
	for i := range N + 1 {
		r := float64(i) / N
		for j := range N + 1 {
			g := float64(j) / N
			for k := range N + 1 {
				b := float64(k) / N

				// https://www.w3.org/Graphics/JPEG/jfif3.pdf
				u := (-0.1687 * r) + (-0.3313 * g) + (+0.5000 * b)
				v := (+0.5000 * r) + (-0.4187 * g) + (-0.0813 * b)

				u1 := int(((u + 0.5) * (dimension - 1)) + 0.5)
				v1 := int(((v + 0.5) * (dimension - 1)) + 0.5)

				m.SetRGBA(u1, (dimension-1)-v1, color.RGBA{
					R: 0xFF,
					G: 0xFF,
					B: 0xFF,
					A: 0xFF,
				})
			}
		}
	}

	for m.Bounds().Dx() > 256 {
		m = downsample2x(m)
	}

	mask := make([]byte, 256*256)
	for y := range 256 {
		for x := range 256 {
			c := m.RGBAAt(x, y)
			mask[(256*y)+x] = (c.A >> 4) * 0x11
		}
	}

	buf := &bytes.Buffer{}
	w, _ := flate.NewWriter(buf, flate.BestCompression)
	w.Write(mask)
	w.Close()

	compressed := buf.Bytes()
	for i, v := range compressed {
		fmt.Printf("0x%02X,", v)
		if (i & 15) == 15 {
			fmt.Println()
		}
	}
}

// compressedMask holds the pixels of a 256×256 image.Alpha (or an image.Gray),
// compressed by compress/flate (RFC 1951).
var compressedMask = [...]byte{
	0xEC, 0x9D, 0x0D, 0x91, 0xAC, 0x3A, 0x10, 0x85, 0x63, 0x21, 0x16, 0xB0, 0x10, 0x0B, 0xB1, 0x80,
	0x85, 0x58, 0xC0, 0x02, 0x16, 0x62, 0x01, 0x0B, 0x58, 0x68, 0x0B, 0x58, 0x88, 0x85, 0x7E, 0xF5,
	0x76, 0xEA, 0x55, 0xBD, 0xDD, 0x65, 0x76, 0x60, 0xF2, 0x33, 0xE9, 0x3E, 0xF9, 0x1C, 0x1C, 0xEA,
	0x7E, 0xA7, 0x2F, 0xBD, 0x43, 0x62, 0x2A, 0x10, 0x13, 0xC5, 0xC5, 0x5B, 0x03, 0xCA, 0xC2, 0x0F,
	0x12, 0x6D, 0xAB, 0x9F, 0xE0, 0xE2, 0x7B, 0xFE, 0xCE, 0xBF, 0x4F, 0xC1, 0xE1, 0xC4, 0xB7, 0x89,
	0xCF, 0xA0, 0x7D, 0x9D, 0x21, 0x9E, 0x02, 0xF1, 0x1F, 0xD0, 0xBE, 0x06, 0xAF, 0x3A, 0x7E, 0xE4,
	0xD7, 0x1C, 0xA4, 0xF6, 0x29, 0x04, 0xBE, 0x8C, 0xC6, 0x21, 0xE1, 0xF8, 0x2E, 0xAA, 0x9E, 0xC2,
	0x93, 0xEE, 0xBB, 0x80, 0x8E, 0x51, 0x49, 0x9C, 0x07, 0x6D, 0xA2, 0x87, 0x44, 0xE4, 0x22, 0x48,
	0x1D, 0x95, 0x81, 0x4B, 0x42, 0xD2, 0x86, 0x84, 0x4B, 0x5C, 0x9E, 0xAF, 0x51, 0x29, 0xA2, 0x1E,
	0xED, 0xC1, 0xD5, 0x90, 0x30, 0x24, 0x36, 0xAE, 0x4D, 0xA2, 0xD8, 0xEF, 0x90, 0x58, 0xB9, 0x15,
	0x5D, 0x8E, 0xCA, 0x99, 0x1B, 0xD3, 0xD7, 0x90, 0xA8, 0xD2, 0x7D, 0x72, 0x9E, 0x82, 0x25, 0xFE,
	0x24, 0x1F, 0x7F, 0x9F, 0x8A, 0xDC, 0x01, 0x07, 0xAD, 0x1F, 0x1A, 0x12, 0x0B, 0xF7, 0xC3, 0xD7,
	0xA8, 0xFC, 0xEC, 0xC2, 0xEB, 0xF3, 0x34, 0x1D, 0x0F, 0x53, 0xEA, 0x2E, 0xFF, 0x82, 0xD3, 0x7D,
	0xE7, 0x63, 0x01, 0xAD, 0xFB, 0x3E, 0x27, 0xC0, 0xD2, 0x63, 0xFC, 0x76, 0x02, 0xB8, 0x2E, 0xE3,
	0x37, 0x13, 0xC0, 0xA6, 0x3E, 0xF3, 0xB7, 0x12, 0x80, 0x3A, 0x8D, 0xDF, 0x48, 0x80, 0xD8, 0x6B,
	0xFC, 0x36, 0x02, 0x04, 0xEE, 0x97, 0x09, 0xB6, 0xFB, 0x5A, 0x09, 0xD0, 0x6D, 0xF7, 0x35, 0x12,
	0x60, 0xE7, 0xAE, 0x99, 0x60, 0xBB, 0xAF, 0x89, 0x00, 0xA1, 0xF3, 0xF8, 0x95, 0x05, 0x70, 0xA9,
	0xF7, 0xFC, 0x55, 0x05, 0xA8, 0xB9, 0xEC, 0x97, 0x20, 0xC0, 0xD6, 0x7F, 0xFC, 0x9A, 0x02, 0xAC,
	0x2C, 0x81, 0x09, 0x67, 0xE1, 0xD5, 0x54, 0x80, 0x29, 0xC9, 0xC8, 0x4F, 0x38, 0x0B, 0xAF, 0x96,
	0x02, 0x44, 0x29, 0xF1, 0x39, 0xE2, 0x2C, 0xBC, 0xCE, 0xFF, 0x22, 0x82, 0xDB, 0x7D, 0x0F, 0x1C,
	0x6C, 0xF7, 0x55, 0x12, 0x40, 0x4E, 0xF7, 0xD5, 0x11, 0x20, 0xB2, 0x2C, 0x1C, 0xD8, 0x4B, 0x5F,
	0x5D, 0x01, 0x9C, 0xB4, 0xF8, 0x65, 0x05, 0xB0, 0x49, 0x5C, 0xFE, 0xA2, 0x02, 0x90, 0xBC, 0xF8,
	0x25, 0x05, 0x88, 0x02, 0xE3, 0x17, 0x14, 0x20, 0xB0, 0x48, 0x1C, 0x6C, 0xF7, 0x15, 0x15, 0x40,
	0xC2, 0xC2, 0xAB, 0xA6, 0x00, 0x3B, 0x4B, 0xC5, 0xC1, 0x76, 0x5F, 0x39, 0x01, 0x82, 0xDC, 0xF8,
	0x25, 0x04, 0x70, 0x49, 0x70, 0xFE, 0x7C, 0x01, 0xC4, 0x76, 0x5F, 0x21, 0x01, 0x36, 0xD1, 0xF1,
	0xB3, 0x05, 0x58, 0x59, 0x38, 0x0E, 0x67, 0xE1, 0x75, 0xCA, 0x86, 0xB3, 0xF0, 0x3A, 0x25, 0xE1,
	0x2C, 0xBC, 0xCE, 0x99, 0x21, 0xFF, 0xE3, 0x53, 0x42, 0x80, 0x45, 0x43, 0xFC, 0xF7, 0x05, 0xF0,
	0xAC, 0x83, 0x19, 0x67, 0xE1, 0x55, 0x52, 0x00, 0x52, 0x12, 0xFF, 0x4D, 0x01, 0x22, 0xAB, 0x61,
	0x06, 0x7B, 0xE9, 0x2B, 0x20, 0x80, 0x53, 0x14, 0xFF, 0x0D, 0x01, 0xD4, 0x74, 0xDF, 0x9B, 0x02,
	0x90, 0xAA, 0xF8, 0xB7, 0x05, 0x88, 0xBA, 0xE2, 0xDF, 0x15, 0x20, 0xB0, 0x36, 0x66, 0x9C, 0x85,
	0x57, 0xB6, 0x00, 0xC2, 0x17, 0x5E, 0xD9, 0x02, 0xEC, 0xAC, 0x90, 0x19, 0xB6, 0xFB, 0x1E, 0xEC,
	0xB8, 0xDD, 0xF7, 0xC0, 0xC2, 0x76, 0xDF, 0x83, 0x80, 0xB3, 0xF0, 0xCA, 0x10, 0x60, 0x63, 0xB5,
	0x58, 0x84, 0x65, 0x7F, 0x9E, 0x00, 0x5E, 0x71, 0xFC, 0x0B, 0x02, 0x4C, 0x49, 0x73, 0xFE, 0x97,
	0x02, 0xE8, 0xED, 0xBE, 0x6B, 0x02, 0x44, 0xDD, 0xF1, 0x5F, 0x09, 0xB0, 0xB0, 0x76, 0x2C, 0xCC,
	0xC2, 0xEB, 0xBE, 0x00, 0x36, 0xE9, 0xCF, 0xBF, 0xE3, 0x2C, 0xBC, 0xEE, 0x0A, 0x10, 0x11, 0xE2,
	0x3F, 0x17, 0x20, 0x40, 0xC4, 0x7F, 0x2A, 0x80, 0x63, 0x10, 0x2C, 0x6C, 0xF7, 0xFD, 0x25, 0xC0,
	0x8E, 0x12, 0xFF, 0x5C, 0x80, 0xC8, 0x38, 0x58, 0xD8, 0xEE, 0x7B, 0x26, 0x80, 0x4B, 0x48, 0xF9,
	0x77, 0x84, 0x65, 0xFF, 0x5F, 0x4C, 0xB0, 0xDD, 0xF7, 0x60, 0xC1, 0xED, 0xBE, 0x2F, 0x7E, 0x9C,
	0x8A, 0x90, 0xD0, 0xF2, 0xFF, 0x10, 0x60, 0x83, 0xCB, 0xFF, 0x5D, 0x80, 0x99, 0x87, 0x00, 0x43,
	0x80, 0x21, 0x00, 0xAE, 0x00, 0x07, 0xB8, 0x00, 0x11, 0x5C, 0x00, 0x37, 0x04, 0x18, 0x02, 0x0C,
	0x01, 0x86, 0x00, 0x43, 0x80, 0x21, 0xC0, 0x10, 0x60, 0x08, 0x30, 0x04, 0x18, 0x02, 0x0C, 0x01,
	0x94, 0xB3, 0x82, 0x0B, 0x70, 0xA0, 0x0B, 0xE0, 0x60, 0x7E, 0xF7, 0x79, 0xCE, 0xF7, 0x63, 0x41,
	0x26, 0x74, 0x01, 0x08, 0x5C, 0x80, 0x65, 0x08, 0x30, 0x04, 0x18, 0x02, 0x0C, 0x01, 0x86, 0x00,
	0x43, 0x80, 0x21, 0xC0, 0x10, 0x60, 0x08, 0x30, 0x04, 0x18, 0x02, 0x28, 0x67, 0x06, 0x17, 0x60,
	0x03, 0x17, 0x20, 0x0D, 0x01, 0x86, 0x00, 0x43, 0x00, 0x5C, 0x01, 0xA2, 0x81, 0x16, 0xE0, 0xE7,
	0xAF, 0xE1, 0xC1, 0x04, 0x38, 0xAC, 0x41, 0xFE, 0x35, 0x7C, 0x72, 0xE3, 0x5B, 0x98, 0x9F, 0x5F,
	0x83, 0xE0, 0x76, 0xDF, 0xF8, 0x14, 0x0E, 0x4A, 0x80, 0xF4, 0xE4, 0x53, 0x50, 0xCC, 0xDD, 0x07,
	0x9C, 0x00, 0xE0, 0x5F, 0x82, 0x3F, 0xBF, 0x14, 0x02, 0x42, 0x00, 0x42, 0x3B, 0xFD, 0xEF, 0x52,
	0xF7, 0xC1, 0x08, 0xF0, 0xE7, 0x8D, 0x18, 0xFA, 0x05, 0x58, 0x00, 0x8F, 0xBF, 0xBC, 0xD4, 0x7D,
	0x10, 0x02, 0xD0, 0xCB, 0x63, 0xC0, 0x74, 0x77, 0xDF, 0x84, 0x79, 0xFE, 0xEB, 0x7F, 0x78, 0xDC,
	0x13, 0x50, 0xF9, 0xD7, 0xEF, 0x3E, 0xE1, 0x04, 0xD8, 0x70, 0x0F, 0x40, 0xBE, 0xD4, 0x7D, 0xBA,
	0x05, 0x48, 0x17, 0xAF, 0x02, 0xB3, 0x68, 0x2F, 0x7D, 0x18, 0x02, 0x5C, 0xBF, 0x09, 0x50, 0xE5,
	0x47, 0xE1, 0xFB, 0x8D, 0x23, 0xE0, 0x15, 0x9E, 0x8A, 0x70, 0x58, 0x9C, 0x3B, 0x1F, 0x33, 0xBA,
	0x4F, 0xAB, 0x00, 0xE1, 0xDE, 0x0D, 0x20, 0x09, 0xB6, 0xFB, 0x54, 0x0A, 0x40, 0x77, 0xEF, 0xFF,
	0x99, 0x71, 0x16, 0x5E, 0x00, 0x02, 0x38, 0xB8, 0x5B, 0x7F, 0x73, 0xBA, 0x4F, 0x9B, 0x00, 0xEF,
	0xDD, 0x00, 0x9D, 0x60, 0xBB, 0x4F, 0x97, 0x00, 0x6F, 0x74, 0x9F, 0x2A, 0x01, 0xFC, 0xBB, 0xB7,
	0xBF, 0xEA, 0x10, 0x60, 0x41, 0xBD, 0xF7, 0x3D, 0xA7, 0xFB, 0xD4, 0x08, 0x40, 0x36, 0xE3, 0xF2,
	0x6F, 0x05, 0x57, 0x9F, 0x4F, 0x39, 0x77, 0xBF, 0x6F, 0xB8, 0xDD, 0xA7, 0xE3, 0x54, 0x84, 0xD5,
	0xE4, 0x21, 0xFC, 0x54, 0x84, 0x2D, 0x33, 0xBE, 0xF0, 0x63, 0x41, 0x0E, 0x9B, 0x9B, 0xDF, 0xE1,
	0x2C, 0xBC, 0xF4, 0x09, 0x10, 0xF2, 0xE3, 0x4B, 0x16, 0x20, 0x16, 0x88, 0x2F, 0x58, 0x80, 0xDD,
	0x14, 0xE1, 0x80, 0xED, 0x3E, 0xD9, 0x02, 0xB8, 0x32, 0xF1, 0xA5, 0x0A, 0x10, 0x4C, 0x29, 0x0E,
	0xD8, 0xEE, 0x93, 0x2B, 0x00, 0x95, 0x8B, 0x2F, 0x51, 0x80, 0x64, 0x0B, 0xE6, 0x17, 0x28, 0x80,
	0x2B, 0x19, 0x5F, 0x9E, 0x00, 0xA1, 0x68, 0x7C, 0x71, 0x02, 0x44, 0x53, 0x98, 0x03, 0x67, 0xE1,
	0x25, 0x5F, 0x80, 0xBC, 0x85, 0x97, 0x7C, 0x01, 0xBC, 0x29, 0x8F, 0x20, 0x01, 0x96, 0x0A, 0xF1,
	0x05, 0x09, 0x10, 0x6B, 0xC4, 0x97, 0xF3, 0x51, 0x78, 0xF9, 0xEE, 0x13, 0x75, 0x2A, 0x42, 0x85,
	0xEE, 0x13, 0x75, 0x2A, 0x82, 0xAF, 0x14, 0x5F, 0x88, 0x00, 0xAB, 0xA9, 0x86, 0x04, 0x01, 0xB6,
	0x7A, 0xF1, 0x25, 0x08, 0x70, 0xD8, 0x8A, 0xF9, 0xFB, 0x17, 0x20, 0x39, 0x53, 0x13, 0x02, 0x7B,
	0xE9, 0x93, 0x26, 0x40, 0xAC, 0x1B, 0xBF, 0x77, 0x01, 0x76, 0x53, 0x1B, 0xC2, 0x59, 0x78, 0xC9,
	0x13, 0xC0, 0x55, 0x8F, 0xDF, 0xB5, 0x00, 0xC1, 0x34, 0x80, 0x60, 0xBB, 0xAF, 0x73, 0x01, 0xA8,
	0x49, 0xFC, 0x6E, 0x05, 0x68, 0xD0, 0x7D, 0x5D, 0x0B, 0xE0, 0x1A, 0xC5, 0xEF, 0x54, 0x80, 0xA5,
	0x55, 0xFC, 0x3E, 0x05, 0x88, 0xA6, 0x1D, 0x84, 0xB3, 0xF0, 0x12, 0x22, 0x40, 0xB5, 0x85, 0x97,
	0x10, 0x01, 0xBC, 0x69, 0x8A, 0x5F, 0x22, 0x25, 0xC8, 0xEE, 0xFB, 0x1F, 0xD6, 0x2F, 0x2B, 0x1D,
	0x68, 0xDD, 0xF7, 0xFB, 0xDF, 0x42, 0xF8, 0xF4, 0x53, 0x68, 0xDA, 0x7D, 0xCF, 0xFE, 0x34, 0x38,
	0xAF, 0x3B, 0xA9, 0x5C, 0x78, 0x75, 0xFF, 0x14, 0x66, 0xD3, 0x19, 0x93, 0x5F, 0x37, 0xD2, 0xB0,
	0xEC, 0xCF, 0x7D, 0x0A, 0x2D, 0x86, 0xC4, 0x66, 0xBA, 0xC6, 0x56, 0x1E, 0x95, 0x87, 0x35, 0x02,
	0xB0, 0xB5, 0x86, 0x44, 0x47, 0xDD, 0x77, 0x71, 0x54, 0x92, 0xBC, 0x85, 0x57, 0xBF, 0x43, 0x22,
	0x1A, 0xB1, 0xB8, 0x39, 0x7F, 0x48, 0x90, 0x91, 0x4E, 0xD6, 0xA8, 0x4C, 0xD6, 0xE8, 0xE0, 0xCD,
	0x21, 0xE1, 0x8C, 0x2A, 0xEE, 0x3E, 0x85, 0x60, 0x54, 0x72, 0x75, 0x54, 0x46, 0xA3, 0x19, 0x1F,
	0x5E, 0x0C, 0x09, 0x32, 0x00, 0x3C, 0x1F, 0x95, 0x6A, 0xBA, 0xEF, 0xCA, 0x53, 0x38, 0x19, 0x12,
	0xDE, 0xA0, 0xF1, 0x35, 0x2A, 0xD3, 0x27, 0x17, 0x5E, 0xFD, 0x0C, 0x89, 0x2A, 0xDD, 0xF7, 0x4F,
	0x00, 0x00, 0x00, 0xFF, 0xFF,
}
