85 lines
2.0 KiB
Go
85 lines
2.0 KiB
Go
package itemimage
|
||
|
||
import (
|
||
"bytes"
|
||
"fmt"
|
||
"image"
|
||
"image/jpeg"
|
||
"math"
|
||
|
||
"golang.org/x/image/draw"
|
||
_ "golang.org/x/image/webp"
|
||
|
||
"printer.backend/internal/svgtemplate"
|
||
)
|
||
|
||
// EmbedDPI matches plate PDF rasterization; embedded pixels need not exceed this resolution.
|
||
const EmbedDPI = 300
|
||
|
||
const jpegEmbedQuality = 88
|
||
|
||
// prepareRaster decodes and downscales image data so the resulting SVG stays small enough for rsvg-convert.
|
||
func prepareRaster(data []byte, d svgtemplate.Data) ([]byte, string, error) {
|
||
img, _, err := image.Decode(bytes.NewReader(data))
|
||
if err != nil {
|
||
return nil, "", fmt.Errorf("decode image: %w", err)
|
||
}
|
||
|
||
maxW := mmToPx(d.OuterWidth)
|
||
maxH := mmToPx(d.OuterHeight)
|
||
if maxW < 1 {
|
||
maxW = 1
|
||
}
|
||
if maxH < 1 {
|
||
maxH = 1
|
||
}
|
||
|
||
covered := scaleCover(img, maxW, maxH)
|
||
rgba := image.NewNRGBA(image.Rect(0, 0, maxW, maxH))
|
||
draw.CatmullRom.Scale(rgba, rgba.Bounds(), covered, covered.Bounds(), draw.Over, nil)
|
||
|
||
var buf bytes.Buffer
|
||
if err := jpeg.Encode(&buf, rgba, &jpeg.Options{Quality: jpegEmbedQuality}); err != nil {
|
||
return nil, "", fmt.Errorf("encode jpeg: %w", err)
|
||
}
|
||
return buf.Bytes(), "image/jpeg", nil
|
||
}
|
||
|
||
func mmToPx(mm float64) int {
|
||
return int(math.Ceil(mm / 25.4 * EmbedDPI))
|
||
}
|
||
|
||
// scaleCover returns an image scaled to cover dw×dh (center crop), for preserveAspectRatio slice.
|
||
func scaleCover(src image.Image, dw, dh int) image.Image {
|
||
sb := src.Bounds()
|
||
sw, sh := sb.Dx(), sb.Dy()
|
||
if sw <= 0 || sh <= 0 {
|
||
return image.NewNRGBA(image.Rect(0, 0, dw, dh))
|
||
}
|
||
|
||
scale := math.Max(float64(dw)/float64(sw), float64(dh)/float64(sh))
|
||
nw := int(math.Ceil(float64(sw) * scale))
|
||
nh := int(math.Ceil(float64(sh) * scale))
|
||
|
||
scaled := image.NewNRGBA(image.Rect(0, 0, nw, nh))
|
||
draw.CatmullRom.Scale(scaled, scaled.Bounds(), src, sb, draw.Over, nil)
|
||
|
||
x0 := (nw - dw) / 2
|
||
y0 := (nh - dh) / 2
|
||
if x0 < 0 {
|
||
x0 = 0
|
||
}
|
||
if y0 < 0 {
|
||
y0 = 0
|
||
}
|
||
if nw < dw {
|
||
dw = nw
|
||
}
|
||
if nh < dh {
|
||
dh = nh
|
||
}
|
||
cropped := image.NewNRGBA(image.Rect(0, 0, dw, dh))
|
||
draw.Draw(cropped, cropped.Bounds(), scaled, image.Point{x0, y0}, draw.Src)
|
||
return cropped
|
||
}
|