package generator import "math" const movingSquareSize = 150 type baseColorFunc func(x, y, width, height int) YCbCr10 type squareBounds struct { minX float64 maxX float64 minY float64 maxY float64 } func movingSquareBounds(width, height, frameIndex int) squareBounds { const half = float64(movingSquareSize) / 2 centerX := float64(width) / 2 centerY := float64(height) / 2 time := float64(frameIndex) / 25.0 offset := math.Sin(time*0.5) * (centerX - half) return squareBounds{ minX: centerX - half + offset, maxX: centerX + half + offset, minY: centerY - half, maxY: centerY + half, } } func (b squareBounds) contains(x, y int) bool { return float64(x) >= b.minX && float64(x) < b.maxX && float64(y) >= b.minY && float64(y) < b.maxY } func patchMovingSquare( dst []byte, width, height, frameIndex int, baseColor baseColorFunc, ) error { bounds := movingSquareBounds(width, height, frameIndex) firstPixelX := max(0, int(math.Floor(bounds.minX))) lastPixelX := min(width, int(math.Ceil(bounds.maxX))) firstBlockX := firstPixelX / 6 * 6 lastBlockX := min(width, (lastPixelX+5)/6*6) firstY := max(0, int(math.Floor(bounds.minY))) lastY := min(height, int(math.Ceil(bounds.maxY))) stride := V210LineSize(width) for y := firstY; y < lastY; y++ { for x := firstBlockX; x < lastBlockX; x += 6 { var pixels [6]YCbCr10 for i := range pixels { px := x + i color := baseColor(px, y, width, height) if bounds.contains(px, y) { color = invertStudioRange(color) } pixels[i] = color } offset := y*stride + x/6*16 packV210Block(dst[offset:offset+16], pixels) } } return nil } func invertStudioRange(color YCbCr10) YCbCr10 { return YCbCr10{ Y: 1004 - color.Y, Cb: 1024 - color.Cb, Cr: 1024 - color.Cr, } }