status-go/qrcode/qrcode.go

183 lines
5.1 KiB
Go

package qrcode
import (
"bytes"
"errors"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"net/url"
"os"
xdraw "golang.org/x/image/draw"
"github.com/status-im/status-go/multiaccounts"
)
const (
defaultPadding = 20
)
func GetImageDimensions(imgBytes []byte) (int, int, error) {
// Decode image bytes
img, _, err := image.Decode(bytes.NewReader(imgBytes))
if err != nil {
return 0, 0, err
}
// Get the image dimensions
bounds := img.Bounds()
width := bounds.Max.X - bounds.Min.X
height := bounds.Max.Y - bounds.Min.Y
return width, height, nil
}
func ToLogoImageFromBytes(imageBytes []byte, padding int) []byte {
img, _, err := image.Decode(bytes.NewReader(imageBytes))
if err != nil {
panic(err)
}
bounds := img.Bounds()
newBounds := image.Rect(bounds.Min.X-padding, bounds.Min.Y-padding, bounds.Max.X+padding, bounds.Max.Y+padding)
white := image.NewRGBA(newBounds)
draw.Draw(white, newBounds, &image.Uniform{C: color.White}, image.ZP, draw.Src)
// Create a circular mask
circle := image.NewRGBA(bounds)
draw.DrawMask(circle, bounds, img, image.ZP, &Circle{
X: bounds.Dx() / 2,
Y: bounds.Dy() / 2,
R: bounds.Dx() / 2,
}, image.ZP, draw.Over)
// Calculate the center point of the new image
centerX := (newBounds.Min.X + newBounds.Max.X) / 2
centerY := (newBounds.Min.Y + newBounds.Max.Y) / 2
// Draw the circular image in the center of the new image
draw.Draw(white, bounds.Add(image.Pt(centerX-bounds.Dx()/2, centerY-bounds.Dy()/2)), circle, image.ZP, draw.Over)
// Encode image to png format and save in a bytes
var resultImg bytes.Buffer
err = png.Encode(&resultImg, white)
if err != nil {
return nil
}
resultBytes := resultImg.Bytes()
return resultBytes
}
func SuperimposeImage(imageBytes []byte, qrFilepath []byte) []byte {
// Read the two images from bytes
img1, _, _ := image.Decode(bytes.NewReader(imageBytes))
img2, _, _ := image.Decode(bytes.NewReader(qrFilepath))
// Create a new image with the dimensions of the first image
result := image.NewRGBA(img1.Bounds())
// Draw the first image on the new image
draw.Draw(result, img1.Bounds(), img1, image.ZP, draw.Src)
// Get the dimensions of the second image
img2Bounds := img2.Bounds()
// Calculate the x and y coordinates to center the second image
x := (img1.Bounds().Dx() - img2Bounds.Dx()) / 2
y := (img1.Bounds().Dy() - img2Bounds.Dy()) / 2
// Draw the second image on top of the first image at the calculated coordinates
draw.Draw(result, img2Bounds.Add(image.Pt(x, y)), img2, image.ZP, draw.Over)
// Encode the final image to a desired format
var b bytes.Buffer
if err := png.Encode(&b, result); err != nil {
fmt.Println(err)
}
return b.Bytes()
}
func ResizeImage(imgBytes []byte, width, height int) ([]byte, error) {
// Decode image bytes
img, _, err := image.Decode(bytes.NewReader(imgBytes))
if err != nil {
return nil, err
}
// Create a new image with the desired dimensions
newImg := image.NewNRGBA(image.Rect(0, 0, width, height))
xdraw.BiLinear.Scale(newImg, newImg.Bounds(), img, img.Bounds(), draw.Over, nil)
// Encode the new image to bytes
var newImgBytes bytes.Buffer
if err = png.Encode(&newImgBytes, newImg); err != nil {
return nil, err
}
return newImgBytes.Bytes(), nil
}
func ImageToBytes(imagePath string) ([]byte, error) {
// Open the image file
file, err := os.Open(imagePath)
if err != nil {
return nil, err
}
defer file.Close()
// Decode the image
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
// Create a new buffer to hold the image data
var imgBuffer bytes.Buffer
// Encode the image to the desired format and save it in the buffer
err = png.Encode(&imgBuffer, img)
if err != nil {
return nil, err
}
// Return the image data as a byte slice
return imgBuffer.Bytes(), nil
}
func GetLogoImage(multiaccountsDB *multiaccounts.Database, params url.Values) ([]byte, error) {
var imageFolderBasePath = "../_assets/tests/"
var logoFileStaticPath = imageFolderBasePath + "status.png"
keyUids, ok := params["keyUid"]
if !ok || len(keyUids) == 0 {
return nil, errors.New("no keyUid")
}
imageNames, ok := params["imageName"]
if !ok || len(imageNames) == 0 {
return nil, errors.New("no imageName")
}
identityImageObjectFromDB, err := multiaccountsDB.GetIdentityImage(keyUids[0], imageNames[0])
if err != nil {
return nil, err
}
staticLogoFileBytes, _ := ImageToBytes(logoFileStaticPath)
if identityImageObjectFromDB == nil {
return ToLogoImageFromBytes(staticLogoFileBytes, GetPadding(staticLogoFileBytes)), nil
}
return ToLogoImageFromBytes(identityImageObjectFromDB.Payload, GetPadding(identityImageObjectFromDB.Payload)), nil
}
func GetPadding(imgBytes []byte) int {
size, _, err := GetImageDimensions(imgBytes)
if err != nil {
return defaultPadding
}
return size / 5
}
type Circle struct {
X, Y, R int
}
func (c *Circle) ColorModel() color.Model {
return color.AlphaModel
}
func (c *Circle) Bounds() image.Rectangle {
return image.Rect(c.X-c.R, c.Y-c.R, c.X+c.R, c.Y+c.R)
}
func (c *Circle) At(x, y int) color.Color {
xx, yy, rr := float64(x-c.X)+0.5, float64(y-c.Y)+0.5, float64(c.R)
if xx*xx+yy*yy < rr*rr {
return color.Alpha{255}
}
return color.Alpha{0}
}