//go:build windows package clipboard import ( "bytes" "encoding/binary" "fmt" "image" "image/color" "image/png" ) const ( biRGB = 0 cfDIB = 8 dibHeaderSize = 40 ) // pngToDIB converts a PNG image to a 32bpp bottom-up BI_RGB DIB. The DIB layout // is BITMAPINFOHEADER (40 bytes) followed by tightly packed BGRA rows, which is // the form Windows expects for the CF_DIB clipboard format. func pngToDIB(pngData []byte) ([]byte, error) { img, err := png.Decode(bytes.NewReader(pngData)) if err != nil { return nil, err } b := img.Bounds() w, h := b.Dx(), b.Dy() dib := make([]byte, dibHeaderSize+w*4*h) le := binary.LittleEndian le.PutUint32(dib[0:], dibHeaderSize) // biSize le.PutUint32(dib[4:], uint32(w)) // biWidth le.PutUint32(dib[8:], uint32(h)) // biHeight (positive = bottom-up) le.PutUint16(dib[12:], 1) // biPlanes le.PutUint16(dib[14:], 32) // biBitCount le.PutUint32(dib[16:], biRGB) // biCompression // biSizeImage (20) left 0 for BI_RGB; metrics/clrUsed/clrImportant left 0. px := dib[dibHeaderSize:] for y := 0; y < h; y++ { srcY := h - 1 - y // bottom-up row := px[y*w*4 : (y+1)*w*4] for x := 0; x < w; x++ { r, g, b_, a := img.At(b.Min.X+x, b.Min.Y+srcY).RGBA() row[x*4+0] = uint8(b_ >> 8) // B row[x*4+1] = uint8(g >> 8) // G row[x*4+2] = uint8(r >> 8) // R row[x*4+3] = uint8(a >> 8) // A } } return dib, nil } // dibToPNG converts a BI_RGB DIB (32bpp or 24bpp) to a PNG image. func dibToPNG(dib []byte) ([]byte, error) { if len(dib) < dibHeaderSize { return nil, ErrNoImage } le := binary.LittleEndian w := int(int32(le.Uint32(dib[4:]))) h := int32(le.Uint32(dib[8:])) bitCount := le.Uint16(dib[14:]) compression := le.Uint32(dib[16:]) if w <= 0 || h == 0 { return nil, ErrNoImage } if compression != biRGB { return nil, fmt.Errorf("clipboard: unsupported DIB compression %d", compression) } height := int(h) if h < 0 { height = -height } bottomUp := h > 0 img := image.NewRGBA(image.Rect(0, 0, w, height)) src := dib[dibHeaderSize:] switch bitCount { case 32: stride := w * 4 for y := 0; y < height; y++ { srcY := y if bottomUp { srcY = height - 1 - y } row := src[srcY*stride : (srcY+1)*stride] for x := 0; x < w; x++ { img.SetRGBA(x, y, color.RGBA{row[x*4+2], row[x*4+1], row[x*4+0], row[x*4+3]}) } } case 24: stride := (w*3 + 3) &^ 3 // rows padded to 4-byte boundary for y := 0; y < height; y++ { srcY := y if bottomUp { srcY = height - 1 - y } row := src[srcY*stride : srcY*stride+w*3] for x := 0; x < w; x++ { img.SetRGBA(x, y, color.RGBA{row[x*3+2], row[x*3+1], row[x*3+0], 255}) } } default: return nil, fmt.Errorf("clipboard: unsupported DIB bit count %d", bitCount) } var buf bytes.Buffer if err := png.Encode(&buf, img); err != nil { return nil, err } return buf.Bytes(), nil }