package generator import ( "encoding/binary" "fmt" "math" ) const ( alphaTransparent uint32 = 64 alphaOpaque uint32 = 940 ) func packAlphaBlock(dst []byte, samples [3]uint32) { const mask uint32 = 0x3ff word := samples[0]&mask | (samples[1]&mask)<<10 | (samples[2]&mask)<<20 binary.LittleEndian.PutUint32(dst, word) } func fillAlphaPlane( dst []byte, width, height int, value uint32, ) error { need := AlphaFrameSize(width, height) if len(dst) < need { return fmt.Errorf( "alpha: destination is too small: got %d bytes, need %d", len(dst), need, ) } stride := AlphaLineSize(width) for y := 0; y < height; y++ { row := dst[y*stride : (y+1)*stride] for x := 0; x < width; x += 3 { var samples [3]uint32 for i := range samples { if x+i < width { samples[i] = value } } packAlphaBlock(row[x/3*4:], samples) } } return nil } func patchAlphaMovingSquare(dst []byte, width, height, frameIndex int) error { need := AlphaFrameSize(width, height) if len(dst) < need { return fmt.Errorf( "alpha: destination is too small: got %d bytes, need %d", len(dst), need, ) } bounds := movingSquareBounds(width, height, frameIndex) firstPixelX := max(0, int(math.Floor(bounds.minX))) lastPixelX := min(width, int(math.Ceil(bounds.maxX))) firstBlockX := firstPixelX / 3 * 3 lastBlockX := min(width, (lastPixelX+2)/3*3) firstY := max(0, int(math.Floor(bounds.minY))) lastY := min(height, int(math.Ceil(bounds.maxY))) stride := AlphaLineSize(width) for y := firstY; y < lastY; y++ { for blockX := firstBlockX; blockX < lastBlockX; blockX += 3 { var samples [3]uint32 for i := range samples { x := blockX + i switch { case x >= width: samples[i] = 0 case bounds.contains(x, y): samples[i] = alphaTransparent default: samples[i] = alphaOpaque } } offset := y*stride + blockX/3*4 packAlphaBlock(dst[offset:], samples) } } return nil }