diff --git a/go.mod b/go.mod index 730ea6e..cd85dcb 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/tdewolff/font v0.0.0-20261010204837-88a7f6b56918 github.com/xiaoqidun/j2kgo v1.0.1 github.com/xiaoqidun/jbig2 v1.1.7 - github.com/xiaoqidun/pdfgo v0.0.0-20261014151159-d4a0b52ce73b + github.com/xiaoqidun/pdfgo v0.0.0-20261015080813-04cab548eee1 golang.org/x/crypto v0.58.0 golang.org/x/image v0.47.0 golang.org/x/text v0.43.0 diff --git a/go.sum b/go.sum index ae9802f..c6aa328 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/xiaoqidun/j2kgo v1.0.1 h1:W0SytzjvgiOhXwq+JNRSVKLB847fjGOvGWTNKqmwvo0 github.com/xiaoqidun/j2kgo v1.0.1/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA= github.com/xiaoqidun/jbig2 v1.1.7 h1:118d2lQn2ZsupmXazK+2p23Jp5xQ09btBmPUoYoa/+Y= github.com/xiaoqidun/jbig2 v1.1.7/go.mod h1:9jromqVwMqlptUDEt6Yip8vqYA5I1YjhG7FgMl/Z2dA= -github.com/xiaoqidun/pdfgo v0.0.0-20261014151159-d4a0b52ce73b h1:s37CAxoGJpIgU0T/X5v8Oc+AM3goswgVZzp99I+iNiY= -github.com/xiaoqidun/pdfgo v0.0.0-20261014151159-d4a0b52ce73b/go.mod h1:FUQ8+TfNoec6tZCdsckx+rrD25JACm7EzuZyFH37QHg= +github.com/xiaoqidun/pdfgo v0.0.0-20261015080813-04cab548eee1 h1:j0HPne+OoMJt3moQyurnlWfMsVd0SyCmEl/epSOIpG4= +github.com/xiaoqidun/pdfgo v0.0.0-20261015080813-04cab548eee1/go.mod h1:FUQ8+TfNoec6tZCdsckx+rrD25JACm7EzuZyFH37QHg= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= diff --git a/internal/base/image/image.go b/internal/base/image/image.go index de3f605..1941ecd 100644 --- a/internal/base/image/image.go +++ b/internal/base/image/image.go @@ -459,6 +459,11 @@ func GrayAt(img image.Image, x, y int) color.Gray { // 返回: color.NRGBA NRGBA像素 func NRGBAAt(img image.Image, x, y int) color.NRGBA { switch src := img.(type) { + case *image.NRGBA: + return src.NRGBAAt(x, y) + case *image.NRGBA64: + c := src.NRGBA64At(x, y) + return color.NRGBA{R: uint8(c.R >> 8), G: uint8(c.G >> 8), B: uint8(c.B >> 8), A: uint8(c.A >> 8)} case interface{ NRGBAAt(int, int) color.NRGBA }: return src.NRGBAAt(x, y) case interface{ NRGBA64At(int, int) color.NRGBA64 }: @@ -488,6 +493,27 @@ func NRGBAAt(img image.Image, x, y int) color.NRGBA { // 入参: img 图像, x 横坐标, y 纵坐标 // 返回: uint32 十六位透明度 func AlphaAt(img image.Image, x, y int) uint32 { + switch source := img.(type) { + case *image.RGBA: + if !(image.Point{X: x, Y: y}).In(source.Rect) { + return 0 + } + return uint32(source.Pix[source.PixOffset(x, y)+3]) * 257 + case *image.RGBA64: + if !(image.Point{X: x, Y: y}).In(source.Rect) { + return 0 + } + i := source.PixOffset(x, y) + 6 + return uint32(source.Pix[i])<<8 | uint32(source.Pix[i+1]) + case *image.NRGBA: + return uint32(source.NRGBAAt(x, y).A) * 257 + case *image.NRGBA64: + return uint32(source.NRGBA64At(x, y).A) + case *image.Alpha: + return uint32(source.AlphaAt(x, y).A) * 257 + case *image.Alpha16: + return uint32(source.Alpha16At(x, y).A) + } if source, ok := img.(image.RGBA64Image); ok { return uint32(source.RGBA64At(x, y).A) } diff --git a/internal/base/image/scale.go b/internal/base/image/scale.go index 819a3aa..4267953 100644 --- a/internal/base/image/scale.go +++ b/internal/base/image/scale.go @@ -221,16 +221,36 @@ func rasterImageHorizontal(row []rasterImagePixel, src image.RGBA64Image, sample // 入参: dst 目标图像, output 行像素缓冲, values 累积颜色, x 横向起点, y 目标行坐标, op 合成操作 func rasterImageWriteRow(dst draw.Image, output *image.RGBA64, values []rasterImagePixel, x, y int, op draw.Op) { for i, value := range values { - output.SetRGBA64(i, 0, color.RGBA64{ - R: uint16(min(max(min(value.r, value.a)+.5, 0), 65535)), - G: uint16(min(max(min(value.g, value.a)+.5, 0), 65535)), - B: uint16(min(max(min(value.b, value.a)+.5, 0), 65535)), - A: uint16(min(max(value.a+.5, 0), 65535)), - }) + r := rasterImageComponent(value.r, value.a) + g := rasterImageComponent(value.g, value.a) + b := rasterImageComponent(value.b, value.a) + a := rasterImageComponent(value.a, value.a) + pixel := output.Pix[i*8 : i*8+8 : i*8+8] + pixel[0], pixel[1] = uint8(r>>8), uint8(r) + pixel[2], pixel[3] = uint8(g>>8), uint8(g) + pixel[4], pixel[5] = uint8(b>>8), uint8(b) + pixel[6], pixel[7] = uint8(a>>8), uint8(a) } draw.Draw(dst, image.Rect(x, y, x+len(values), y+1), output, image.Point{}, op) } +// rasterImageComponent 按预乘透明度裁切并取整十六位分量 +// 入参: value 采样分量, alpha 采样透明度 +// 返回: uint16 输出分量 +func rasterImageComponent(value, alpha float64) uint16 { + if value > alpha || math.IsNaN(alpha) { + value = alpha + } + value += .5 + if value <= 0 { + return 0 + } + if value >= 65535 { + return 65535 + } + return uint16(value) +} + // DrawPixels 对轴向缩放使用可分离采样,其余变换保留通用路径 // 入参: dst 目标图像, src 源图像, m 按a、b、c、d、e、f排列的像素变换, op 合成操作 func DrawPixels(dst draw.Image, src image.Image, m [6]float64, op draw.Op) { diff --git a/ofdgo_pdf_composite.go b/ofdgo_pdf_composite.go index d535541..19386c0 100644 --- a/ofdgo_pdf_composite.go +++ b/ofdgo_pdf_composite.go @@ -101,6 +101,19 @@ type pdfImageProcessKey struct { softMask bool } +// pdfCompositeImageColor 复用图像颜色变换及相邻相同分量的精确结果 +type pdfCompositeImageColor struct { + mark *pdfgo.ImageMark + space *pdfgo.ColorSpace + source *pdfgo.ColorSpace + intent pdfgo.Name + conversion pdfgo.ColorConversion + converter pdfgo.ColorConverter + input [4]float64 + output [4]float64 + ready bool +} + // pdfCompositeCache 在单页内复用几何、图像分量及蒙版内容 type pdfCompositeCache struct { geometry *renderCache[pdfCompositeSceneKey, pdfCompositeGeometry] @@ -109,6 +122,7 @@ type pdfCompositeCache struct { images map[*pdfgo.Image]*pdfgo.ImageComponents imageMatrix map[*pdfgo.ImageMark]pdfgo.Matrix imageProcesses map[pdfImageProcessKey]*pdfgo.ImageProcess + imageColor pdfCompositeImageColor masks map[*pdfgo.SoftMask][]pdfCompositeNode meshes map[*pdfgo.MeshGradient][]pdfMeshTriangle linearMeshes *renderCache[pdfLinearMeshKey, []pdfgo.MeshTriangle] diff --git a/ofdgo_pdf_composite_draw.go b/ofdgo_pdf_composite_draw.go index c65ad40..47973b3 100644 --- a/ofdgo_pdf_composite_draw.go +++ b/ofdgo_pdf_composite_draw.go @@ -811,8 +811,22 @@ func (c *pdfCompositor) imageColor(mark *pdfgo.ImageMark, point pdfgo.Point, spa if process != nil || space.Equal(source.Space) { return values, alpha, nil } - values, err = space.ConvertWith(values[:source.Space.Components()], source.Space, mark.Style.RenderingIntent, mark.Style.ColorConversion) - return values, alpha, err + color := &c.cache.imageColor + if color.mark != mark || color.space != space || color.source != source.Space || color.intent != mark.Style.RenderingIntent || color.conversion != mark.Style.ColorConversion { + converter, err := space.PrepareConversion(source.Space, mark.Style.RenderingIntent, mark.Style.ColorConversion) + if err != nil { + return [4]float64{}, alpha, err + } + *color = pdfCompositeImageColor{mark: mark, space: space, source: source.Space, intent: mark.Style.RenderingIntent, conversion: mark.Style.ColorConversion, converter: converter} + } + if color.ready && color.input == values { + return color.output, alpha, nil + } + converted, err := color.converter.Convert(values[:source.Space.Components()]) + if err == nil { + color.input, color.output, color.ready = values, converted, true + } + return converted, alpha, err } // constant 判断轴向延伸是否覆盖整个采样区间,不近似渐变内部颜色