mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
1 parent
73472c5d4b
commit
77ffbe6a72
56 files changed
+2420
-1635
No files matched your search
+307
@@ -21,10 +21,12 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
_ "image/gif"
|
||||
"math"
|
||||
|
||||
canvasimage "github.com/tdewolff/canvas/image"
|
||||
_ "github.com/xiaoqidun/jbig2"
|
||||
_ "golang.org/x/image/bmp"
|
||||
"golang.org/x/image/draw"
|
||||
_ "golang.org/x/image/tiff"
|
||||
)
|
||||
|
||||
@@ -175,3 +177,308 @@ func parseBMPHeader(data []byte) (int, int, int, uint16, uint32, error) {
|
||||
}
|
||||
return offset, width, height, bpp, compression, nil
|
||||
}
|
||||
|
||||
// imageWithMask 应用图片蒙版
|
||||
// 入参: img 图片对象, mask 蒙版图片
|
||||
// 返回: image.Image 蒙版处理后的图片对象
|
||||
func imageWithMask(img, mask image.Image) image.Image {
|
||||
if img == nil || mask == nil {
|
||||
return img
|
||||
}
|
||||
bounds := img.Bounds()
|
||||
if bounds.Empty() || mask.Bounds().Empty() {
|
||||
return img
|
||||
}
|
||||
source := imagePixelSource(img)
|
||||
maskSource := imagePixelSource(mask)
|
||||
maskBounds := maskSource.Bounds()
|
||||
var opacity image.Image = maskSource
|
||||
if bounds.Size() != maskBounds.Size() {
|
||||
resized := image.NewGray(bounds)
|
||||
draw.CatmullRom.Scale(resized, bounds, maskSource, maskBounds, draw.Src, nil)
|
||||
opacity = resized
|
||||
maskBounds = bounds
|
||||
}
|
||||
out := image.NewNRGBA(bounds)
|
||||
if src, ok := source.(*image.NRGBA); ok {
|
||||
for y := 0; y < bounds.Dy(); y++ {
|
||||
offset := src.PixOffset(bounds.Min.X, bounds.Min.Y+y)
|
||||
row := out.Pix[y*out.Stride : (y+1)*out.Stride]
|
||||
copy(row, src.Pix[offset:offset+len(row)])
|
||||
for x := 3; x < len(row); x += 4 {
|
||||
a := imageGrayAt(opacity, maskBounds.Min.X+x/4, maskBounds.Min.Y+y).Y
|
||||
row[x] = uint8(int(row[x]) * int(a) / 255)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
mx := maskBounds.Min.X + x - bounds.Min.X
|
||||
my := maskBounds.Min.Y + y - bounds.Min.Y
|
||||
a := imageGrayAt(opacity, mx, my).Y
|
||||
c := imageNRGBAAt(source, x, y)
|
||||
c.A = uint8(int(c.A) * int(a) / 255)
|
||||
out.SetNRGBA(x, y, c)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// imageWithAlpha 合并图片透明度
|
||||
// 入参: img 图片对象, alpha 对象透明度
|
||||
// 返回: image.Image 合并后的图片对象
|
||||
func imageWithAlpha(img image.Image, alpha *int) image.Image {
|
||||
if img == nil || alpha == nil {
|
||||
return img
|
||||
}
|
||||
a := clampColor(*alpha)
|
||||
if a == 255 {
|
||||
return img
|
||||
}
|
||||
bounds := img.Bounds()
|
||||
out := image.NewNRGBA(bounds)
|
||||
source := imagePixelSource(img)
|
||||
if src, ok := source.(*image.NRGBA); ok {
|
||||
for y := 0; y < bounds.Dy(); y++ {
|
||||
offset := src.PixOffset(bounds.Min.X, bounds.Min.Y+y)
|
||||
row := out.Pix[y*out.Stride : (y+1)*out.Stride]
|
||||
copy(row, src.Pix[offset:offset+len(row)])
|
||||
for x := 3; x < len(row); x += 4 {
|
||||
row[x] = uint8(int(row[x]) * a / 255)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
c := imageNRGBAAt(source, x, y)
|
||||
c.A = uint8(int(c.A) * a / 255)
|
||||
out.SetNRGBA(x, y, c)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// imagePixelSource 获取图片像素源
|
||||
// 入参: img 图片对象
|
||||
// 返回: image.Image 图片像素源
|
||||
func imagePixelSource(img image.Image) image.Image {
|
||||
if src, ok := img.(interface{ Image() (image.Image, error) }); ok {
|
||||
if decoded, err := src.Image(); err == nil {
|
||||
return decoded
|
||||
}
|
||||
}
|
||||
return img
|
||||
}
|
||||
|
||||
// imageGrayAt 获取图片灰度像素
|
||||
// 入参: img 图片对象, x X坐标, y Y坐标
|
||||
// 返回: color.Gray 灰度像素
|
||||
func imageGrayAt(img image.Image, x, y int) color.Gray {
|
||||
if src, ok := img.(*image.Gray); ok {
|
||||
return src.GrayAt(x, y)
|
||||
}
|
||||
if src, ok := img.(*image.Paletted); ok {
|
||||
return color.GrayModel.Convert(src.At(x, y)).(color.Gray)
|
||||
}
|
||||
if src, ok := img.(image.RGBA64Image); ok {
|
||||
r, g, b, _ := src.RGBA64At(x, y).RGBA()
|
||||
return color.Gray{Y: uint8((19595*r + 38470*g + 7471*b + 1<<15) >> 24)}
|
||||
}
|
||||
return color.GrayModel.Convert(img.At(x, y)).(color.Gray)
|
||||
}
|
||||
|
||||
// imageNRGBAAt 获取图片NRGBA像素
|
||||
// 入参: img 图片对象, x X坐标, y Y坐标
|
||||
// 返回: color.NRGBA NRGBA像素
|
||||
func imageNRGBAAt(img image.Image, x, y int) color.NRGBA {
|
||||
if src, ok := img.(*image.NRGBA); ok {
|
||||
return src.NRGBAAt(x, y)
|
||||
}
|
||||
if src, ok := img.(*image.YCbCr); ok {
|
||||
r, g, b, _ := src.YCbCrAt(x, y).RGBA()
|
||||
return color.NRGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: 255}
|
||||
}
|
||||
if src, ok := img.(*image.RGBA); ok {
|
||||
c := src.RGBA64At(x, y)
|
||||
if c.A == 0 {
|
||||
return color.NRGBA{}
|
||||
}
|
||||
r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
|
||||
if a != 0xffff {
|
||||
r = r * 0xffff / a
|
||||
g = g * 0xffff / a
|
||||
b = b * 0xffff / a
|
||||
}
|
||||
return color.NRGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8)}
|
||||
}
|
||||
return color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
|
||||
}
|
||||
|
||||
// transparentEdgeColor 获取透明像素相邻的可见颜色
|
||||
// 入参: img 图片对象, x X坐标, y Y坐标
|
||||
// 返回: color.NRGBA 颜色, bool 是否存在
|
||||
func transparentEdgeColor(img *image.NRGBA, x, y int) (color.NRGBA, bool) {
|
||||
bounds := img.Bounds()
|
||||
var best color.NRGBA
|
||||
for dy := -1; dy <= 1; dy++ {
|
||||
for dx := -1; dx <= 1; dx++ {
|
||||
if dx == 0 && dy == 0 {
|
||||
continue
|
||||
}
|
||||
nx, ny := x+dx, y+dy
|
||||
if nx < bounds.Min.X || nx >= bounds.Max.X || ny < bounds.Min.Y || ny >= bounds.Max.Y {
|
||||
continue
|
||||
}
|
||||
c := img.NRGBAAt(nx, ny)
|
||||
if c.A > best.A {
|
||||
best = c
|
||||
}
|
||||
}
|
||||
}
|
||||
if best.A == 0 {
|
||||
return color.NRGBA{}, false
|
||||
}
|
||||
best.A = 1
|
||||
return best, true
|
||||
}
|
||||
|
||||
// transparentPaddingColor 获取透明补齐颜色
|
||||
// 入参: c 边缘颜色
|
||||
// 返回: color.NRGBA 补齐颜色
|
||||
func transparentPaddingColor(c color.NRGBA) color.NRGBA {
|
||||
c.A = 1
|
||||
return c
|
||||
}
|
||||
|
||||
// clipStampImage 裁剪印章图像
|
||||
// 入参: img 印章图像, box 印章区域, clip 裁剪区域
|
||||
// 返回: image.Image 裁剪后的印章图像
|
||||
func clipStampImage(img image.Image, box, clip Box) image.Image {
|
||||
bounds := img.Bounds()
|
||||
x0 := int(math.Floor(clip.X / box.W * float64(bounds.Dx())))
|
||||
y0 := int(math.Floor(clip.Y / box.H * float64(bounds.Dy())))
|
||||
x1 := int(math.Ceil((clip.X + clip.W) / box.W * float64(bounds.Dx())))
|
||||
y1 := int(math.Ceil((clip.Y + clip.H) / box.H * float64(bounds.Dy())))
|
||||
out := image.NewNRGBA(image.Rect(0, 0, x1-x0, y1-y0))
|
||||
draw.Draw(out, out.Bounds(), img, image.Pt(bounds.Min.X+x0, bounds.Min.Y+y0), draw.Src)
|
||||
return out
|
||||
}
|
||||
|
||||
// stampImageWithTransparentWhite 处理印章图片白色底色
|
||||
// 入参: img 印章图片对象
|
||||
// 返回: image.Image 处理后的印章图片对象
|
||||
func stampImageWithTransparentWhite(img image.Image) image.Image {
|
||||
if opaque, ok := img.(interface{ Opaque() bool }); ok && !opaque.Opaque() {
|
||||
return img
|
||||
}
|
||||
bounds := img.Bounds()
|
||||
out := image.NewNRGBA(bounds)
|
||||
hasAlpha := false
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
|
||||
if c.A < 255 {
|
||||
hasAlpha = true
|
||||
}
|
||||
out.SetNRGBA(x, y, c)
|
||||
}
|
||||
}
|
||||
if hasAlpha {
|
||||
return img
|
||||
}
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
c := out.NRGBAAt(x, y)
|
||||
if c.R >= 250 && c.G >= 250 && c.B >= 250 {
|
||||
c.A = 0
|
||||
out.SetNRGBA(x, y, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// imageWithTransparentEdge 补齐透明图片边缘颜色
|
||||
// 入参: img 图片对象
|
||||
// 返回: image.Image 补齐后的图片对象, int 补齐像素数
|
||||
func imageWithTransparentEdge(img image.Image) (image.Image, int) {
|
||||
if img == nil {
|
||||
return img, 0
|
||||
}
|
||||
bounds := img.Bounds()
|
||||
w, h := bounds.Dx(), bounds.Dy()
|
||||
if w == 0 || h == 0 {
|
||||
return img, 0
|
||||
}
|
||||
if src, ok := img.(*canvasimage.Image); ok && src.Mimetype == "image/jpeg" && src.Mask == nil {
|
||||
return img, 0
|
||||
}
|
||||
source := imagePixelSource(img)
|
||||
if opaque, ok := source.(interface{ Opaque() bool }); ok && opaque.Opaque() {
|
||||
return img, 0
|
||||
}
|
||||
hasZero, hasVisible := false, false
|
||||
src, ok := source.(*image.NRGBA)
|
||||
if ok {
|
||||
srcBounds := src.Bounds()
|
||||
hasZero = src.Pix[3] == 0
|
||||
hasVisible = !hasZero
|
||||
scan:
|
||||
for y := srcBounds.Min.Y; y < srcBounds.Max.Y; y++ {
|
||||
offset := src.PixOffset(srcBounds.Min.X, y) + 3
|
||||
for x := 0; x < w; x++ {
|
||||
if (src.Pix[offset] == 0) != hasZero {
|
||||
hasZero, hasVisible = true, true
|
||||
break scan
|
||||
}
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
} else {
|
||||
src = image.NewNRGBA(image.Rect(0, 0, w, h))
|
||||
for y := 0; y < h; y++ {
|
||||
for x := 0; x < w; x++ {
|
||||
c := imageNRGBAAt(source, bounds.Min.X+x, bounds.Min.Y+y)
|
||||
src.SetNRGBA(x, y, c)
|
||||
if c.A == 0 {
|
||||
hasZero = true
|
||||
} else {
|
||||
hasVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasZero || !hasVisible {
|
||||
return img, 0
|
||||
}
|
||||
srcBounds := src.Bounds()
|
||||
out := image.NewNRGBA(image.Rect(0, 0, w+2, h+2))
|
||||
draw.Draw(out, out.Bounds().Inset(1), src, srcBounds.Min, draw.Src)
|
||||
for y := 0; y < h; y++ {
|
||||
sy := srcBounds.Min.Y + y
|
||||
offset := src.PixOffset(srcBounds.Min.X, sy) + 3
|
||||
for x := 0; x < w; x++ {
|
||||
if src.Pix[offset] == 0 {
|
||||
if edge, ok := transparentEdgeColor(src, srcBounds.Min.X+x, sy); ok {
|
||||
out.SetNRGBA(x+1, y+1, edge)
|
||||
}
|
||||
}
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
for x := 0; x < w; x++ {
|
||||
out.SetNRGBA(x+1, 0, transparentPaddingColor(out.NRGBAAt(x+1, 1)))
|
||||
out.SetNRGBA(x+1, h+1, transparentPaddingColor(out.NRGBAAt(x+1, h)))
|
||||
}
|
||||
for y := 0; y < h; y++ {
|
||||
out.SetNRGBA(0, y+1, transparentPaddingColor(out.NRGBAAt(1, y+1)))
|
||||
out.SetNRGBA(w+1, y+1, transparentPaddingColor(out.NRGBAAt(w, y+1)))
|
||||
}
|
||||
out.SetNRGBA(0, 0, transparentPaddingColor(out.NRGBAAt(1, 1)))
|
||||
out.SetNRGBA(w+1, 0, transparentPaddingColor(out.NRGBAAt(w, 1)))
|
||||
out.SetNRGBA(0, h+1, transparentPaddingColor(out.NRGBAAt(1, h)))
|
||||
out.SetNRGBA(w+1, h+1, transparentPaddingColor(out.NRGBAAt(w, h)))
|
||||
return out, 1
|
||||
}
|
||||
Reference in new issue
Block a user