mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-25 14:02:53 -05:00
173 lines
3.8 KiB
Go
173 lines
3.8 KiB
Go
package screenshot
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"image"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
|
)
|
|
|
|
func BufferToImage(buf *ShmBuffer) *image.RGBA {
|
|
return BufferToImageWithFormat(buf, uint32(FormatARGB8888))
|
|
}
|
|
|
|
func BufferToImageWithFormat(buf *ShmBuffer, format uint32) *image.RGBA {
|
|
img := image.NewRGBA(image.Rect(0, 0, buf.Width, buf.Height))
|
|
data := buf.Data()
|
|
|
|
var swapRB bool
|
|
switch format {
|
|
case uint32(FormatABGR8888), uint32(FormatXBGR8888):
|
|
swapRB = false
|
|
default:
|
|
swapRB = true
|
|
}
|
|
|
|
for y := 0; y < buf.Height; y++ {
|
|
srcOff := y * buf.Stride
|
|
dstOff := y * img.Stride
|
|
for x := 0; x < buf.Width; x++ {
|
|
si := srcOff + x*4
|
|
di := dstOff + x*4
|
|
if si+3 >= len(data) || di+3 >= len(img.Pix) {
|
|
continue
|
|
}
|
|
if swapRB {
|
|
img.Pix[di+0] = data[si+2]
|
|
img.Pix[di+1] = data[si+1]
|
|
img.Pix[di+2] = data[si+0]
|
|
} else {
|
|
img.Pix[di+0] = data[si+0]
|
|
img.Pix[di+1] = data[si+1]
|
|
img.Pix[di+2] = data[si+2]
|
|
}
|
|
img.Pix[di+3] = 255
|
|
}
|
|
}
|
|
return img
|
|
}
|
|
|
|
func EncodePNG(w io.Writer, img image.Image) error {
|
|
enc := png.Encoder{CompressionLevel: png.BestSpeed}
|
|
return enc.Encode(w, img)
|
|
}
|
|
|
|
func EncodeJPEG(w io.Writer, img image.Image, quality int) error {
|
|
return jpeg.Encode(w, img, &jpeg.Options{Quality: quality})
|
|
}
|
|
|
|
func EncodePPM(w io.Writer, img *image.RGBA) error {
|
|
bw := bufio.NewWriter(w)
|
|
bounds := img.Bounds()
|
|
if _, err := fmt.Fprintf(bw, "P6\n%d %d\n255\n", bounds.Dx(), bounds.Dy()); err != nil {
|
|
return err
|
|
}
|
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
|
off := (y-bounds.Min.Y)*img.Stride + (x-bounds.Min.X)*4
|
|
if err := bw.WriteByte(img.Pix[off+0]); err != nil {
|
|
return err
|
|
}
|
|
if err := bw.WriteByte(img.Pix[off+1]); err != nil {
|
|
return err
|
|
}
|
|
if err := bw.WriteByte(img.Pix[off+2]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return bw.Flush()
|
|
}
|
|
|
|
func GenerateFilename(format Format) string {
|
|
t := time.Now()
|
|
ext := "png"
|
|
switch format {
|
|
case FormatJPEG:
|
|
ext = "jpg"
|
|
case FormatPPM:
|
|
ext = "ppm"
|
|
}
|
|
return fmt.Sprintf("screenshot_%s.%s", t.Format("2006-01-02_15-04-05"), ext)
|
|
}
|
|
|
|
func GetOutputDir() string {
|
|
if dir := os.Getenv("DMS_SCREENSHOT_DIR"); dir != "" {
|
|
return dir
|
|
}
|
|
|
|
if xdgPics := getXDGPicturesDir(); xdgPics != "" {
|
|
screenshotDir := filepath.Join(xdgPics, "Screenshots")
|
|
if err := os.MkdirAll(screenshotDir, 0755); err == nil {
|
|
return screenshotDir
|
|
}
|
|
return xdgPics
|
|
}
|
|
|
|
if home := os.Getenv("HOME"); home != "" {
|
|
return home
|
|
}
|
|
return "."
|
|
}
|
|
|
|
func getXDGPicturesDir() string {
|
|
userConfigDir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
log.Error("failed to get user config dir", "err", err)
|
|
return ""
|
|
}
|
|
userDirsFile := filepath.Join(userConfigDir, "user-dirs.dirs")
|
|
data, err := os.ReadFile(userDirsFile)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
for _, line := range strings.Split(string(data), "\n") {
|
|
if len(line) == 0 || line[0] == '#' {
|
|
continue
|
|
}
|
|
const prefix = "XDG_PICTURES_DIR="
|
|
if !strings.HasPrefix(line, prefix) {
|
|
continue
|
|
}
|
|
path := strings.Trim(line[len(prefix):], "\"")
|
|
expanded, err := utils.ExpandPath(path)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return expanded
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func WriteToFile(buf *ShmBuffer, path string, format Format, quality int) error {
|
|
return WriteToFileWithFormat(buf, path, format, quality, uint32(FormatARGB8888))
|
|
}
|
|
|
|
func WriteToFileWithFormat(buf *ShmBuffer, path string, format Format, quality int, pixelFormat uint32) error {
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
img := BufferToImageWithFormat(buf, pixelFormat)
|
|
switch format {
|
|
case FormatJPEG:
|
|
return EncodeJPEG(f, img, quality)
|
|
case FormatPPM:
|
|
return EncodePPM(f, img)
|
|
default:
|
|
return EncodePNG(f, img)
|
|
}
|
|
}
|