『 纸上得来终觉浅,绝知此事要躬行 』


对剪切板图片添加额外效果

2021.08.02 | 本文总阅读量

一直觉得 mac 的截图效果非常好看,之前在 ArchLinux 上用 imagemagick 撸了一个类似的,最近在体验 windows 11,顺便也移植了一份。

1 实现效果

  • 添加阴影
  • 添加圆角
  • 添加水印
  • 添加边框

2 实现流程

2.1 剪切板操作

想要对剪切板图片进行修改首先就要能获取剪切板的图片和向剪切板发送图片,在运行 XorgLinux 上可以很方便地使用 xclip 来操作

# get image from clipboard
xclip -selection clipboard -t image/png -o > /tmp/src.png
# send image to clipboard
xclip -selection clipboard -t image/png -i /tmp/des.png

但在 windows 11 上该如何获取呢?我先是查询了 powershell 的相关 API,然后我发现有现成的 Get-ClipboardSet-Clipboard 两个模块函数可用

# get image from clipboard
$img = Get-Clipboard -Format Image

但很可惜我发现 Get-Content path/to/file | Set-Clipboard 对文本是可以的,但是对图片却会乱码,在网上查询也没找到相关解释,于是我换了一种设置剪切板的方法

# send result to clipboard
Add-Type -Assembly System.Windows.Forms
Add-Type -Assembly System.Drawing

$img = [Drawing.Image]::FromFile($result_image)
[Windows.Forms.Clipboard]::SetImage($img)

我也查询了一下 python 基于 PILwin32clipboard 的操作方法

from PIL import ImageGrab
from io import BytesIO
import win32clipboard

# get image from clipboard
image = ImageGrab.grabclipboard()

# send image to clipboard
output = BytesIO()
border_image.convert('RGB').save(output, 'BMP')
data = output.getvalue()[14:]
output.close()

win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
win32clipboard.CloseClipboard()

2.2 图片处理

2.2.1 imagemagick

对于图片的处理首先想到的就是 imagemagick 这个工具了,在 Linux 使用非常简单

先加圆角,我这里使用的圆角弧度为 8

convert /tmp/src.png \
	  \( +clone -alpha extract \
	  -draw 'fill black polygon 0,0 0,8 8,0 fill white circle 8,8 8,0' \
	  \( +clone -flip \) -compose Multiply -composite \
	  \( +clone -flop \) -compose Multiply -composite \
    \) -alpha off -compose CopyOpacity -composite /tmp/output.png

再加阴影和边框,我设置的边框为 20px,这是为了配合我的水印图片

convert /tmp/output.png -bordercolor none -border 20 \( +clone -background black -shadow 100x15+0+0 \) \
	+swap -background transparent -layers merge +repage /tmp/des.png

再加上自己的水印

composite -gravity SouthEast ~/.local/bin/watermark.png /tmp/des.png /tmp/des.png

但我在 powershell 上尝试使用 imagemagick 的时候发现参数识别出现了问题,+clone 没认出来,还好 win 11 窗口自带阴影和圆角,那我只要打一下水印就好了

$cmd = "composite -gravity SouthEast $watermark $source_image $result_image"
Invoke-Expression $cmd

scoop 安装 imagemagick 的时候我发现还引入了 ffmpeg 这个依赖包,我虽然也经常用它,但毕竟还是有点大,于是我尝试用 PIL 来处理图片。

先加一下边框,我是 nord 配色的忠实用户,所以选用了 “#2E3440” 同时也是我桌面壁纸的主要颜色,这是我用 k-means 算法写的一个提取图片主要颜色的脚本

# add border
border = 20 # border size
border_color = "#2E3440" # border color

border_image = Image.new("RGBA", (image.width + border, image.height + border), border_color)
align = int(border / 2)
border_image.paste(image, (align, align))

然后添加水印

# add watermark
watermark = Image.open(r"C:\Users\zjuyk\Desktop\watermark.png")
watermark = watermark.convert("RGBA")
border_image.paste(watermark, (border_image.width - watermark.width, border_image.height - watermark.height), watermark)

在尝试添加圆角的时候我发现,锯齿非常严重,严重影响美观度,所以没有放进脚本里,阴影也是一样,我打算全用 windows 11 自带的窗口阴影和圆角。如果非要自己实现的话,我建议准备几张 mask image 来做图片遮罩。

3 实现效果

  • imagemagick - Linux
#!/bin/bash -e

# Get the picture from flameshot
flameshot gui -r > /tmp/src.png 
wait $!

# Get the picture from scrot
# scrot --select "/tmp/src.png" 
# wait $!

# xclip -selection clipboard -t image/png -o > /tmp/src.png

# add shadow, round corner, border and watermark
convert /tmp/src.png \
	  \( +clone -alpha extract \
	  -draw 'fill black polygon 0,0 0,8 8,0 fill white circle 8,8 8,0' \
	  \( +clone -flip \) -compose Multiply -composite \
	  \( +clone -flop \) -compose Multiply -composite \
    \) -alpha off -compose CopyOpacity -composite /tmp/output.png

convert /tmp/output.png -bordercolor none -border 20 \( +clone -background black -shadow 100x15+0+0 \) \
	+swap -background transparent -layers merge +repage /tmp/des.png

composite -gravity SouthEast ~/.local/bin/watermark.png /tmp/des.png /tmp/des.png

# Send the picture to clipboard
xclip -selection clipboard -t image/png -i /tmp/des.png

# remove the left pictures
rm /tmp/src.png /tmp/des.png /tmp/output.png
  • imagemagick - PowerShell
# Please add execution policy
# Set-ExecutionPolicy Unrestricted

# get image from clipboard
$img = Get-Clipboard -Format Image
if ($img -eq $null) {
        Write-Host "Clipboard contains no image."
        Exit
}

# save it to desktop
$img.Save("C:\Users\zjuyk\Desktop\src.png")

# add watermark
$watermark = "C:\Users\zjuyk\Desktop\watermark.png"
$source_image = "C:\Users\zjuyk\Desktop\src.png"
$result_image = "C:\Users\zjuyk\Desktop\res.png"

$cmd = "composite -gravity SouthEast $watermark $source_image $result_image"
Invoke-Expression $cmd

# send result to clipboard
Add-Type -Assembly System.Windows.Forms
Add-Type -Assembly System.Drawing

$img = [Drawing.Image]::FromFile($result_image)
[Windows.Forms.Clipboard]::SetImage($img)

# remove temp files (do it manually)
Remove-Item "C:\Users\zjuyk\Desktop\src.png" -Force
# Remove-Item "C:\Users\zjuyk\Desktop\res.png" -Force
  • PIL - Python
from PIL import ImageGrab, Image
from io import BytesIO
import win32clipboard

if __name__ == "__main__":
    # get image from clipboard
    image = ImageGrab.grabclipboard()

    # add border
    border = 20 # border size
    border_color = "#2E3440" # border color

    border_image = Image.new("RGBA", (image.width + border, image.height + border), border_color)
    align = int(border / 2)
    border_image.paste(image, (align, align))

    # add watermark
    watermark = Image.open(r"C:\Users\zjuyk\Desktop\watermark.png")
    watermark = watermark.convert("RGBA")
    border_image.paste(watermark, (border_image.width - watermark.width, border_image.height - watermark.height), watermark)

    # send image to clipboard
    output = BytesIO()
    border_image.convert('RGB').save(output, 'BMP')
    data = output.getvalue()[14:]
    output.close()

    win32clipboard.OpenClipboard()
    win32clipboard.EmptyClipboard()
    win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
    win32clipboard.CloseClipboard()

4 参考

发表评论