mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
84a67d17df
commit
5d599dc835
5 files changed
+80
-231
No files matched your search
@@ -10,7 +10,7 @@ require (
|
|||||||
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
||||||
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
||||||
github.com/xiaoqidun/jbig2 v1.1.1
|
github.com/xiaoqidun/jbig2 v1.1.1
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261003171053-bd8834324b6d
|
github.com/xiaoqidun/pdfgo v0.0.0-20261004025424-ce051c731285
|
||||||
golang.org/x/crypto v0.57.0
|
golang.org/x/crypto v0.57.0
|
||||||
golang.org/x/image v0.46.0
|
golang.org/x/image v0.46.0
|
||||||
golang.org/x/text v0.42.0
|
golang.org/x/text v0.42.0
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
|||||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI=
|
github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261003171053-bd8834324b6d h1:8JS9LgB+i9n9/g997JaWObw4aVDPGLrRQASHdWSusGE=
|
github.com/xiaoqidun/pdfgo v0.0.0-20261004025424-ce051c731285 h1:KApg/AQoRuFj6UzFyNY5yOmb1di749tzYuOx9VqY0vU=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261003171053-bd8834324b6d/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
github.com/xiaoqidun/pdfgo v0.0.0-20261004025424-ce051c731285/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||||
|
|||||||
@@ -465,6 +465,12 @@ func (n pdfCompositeNode) direct() bool {
|
|||||||
if !child.direct() {
|
if !child.direct() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if g.Alpha != 1 && child.group == nil {
|
||||||
|
style, _, _ := child.style()
|
||||||
|
if style.SoftMask != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-226
@@ -132,6 +132,10 @@ func (c *pdfCompositor) drawPaint(node pdfCompositeNode, outline bool, paint pdf
|
|||||||
step := 25.4 / c.importer.rasterDPI
|
step := 25.4 / c.importer.rasterDPI
|
||||||
uniform := node.image == nil && paint.Axial == nil && paint.Radial == nil && paint.Function == nil && paint.Tiling == nil && paint.Mesh == nil
|
uniform := node.image == nil && paint.Axial == nil && paint.Radial == nil && paint.Function == nil && paint.Tiling == nil && paint.Mesh == nil
|
||||||
compatible := overprint && !processOverprint && style.OverprintMode == 1 && uniform && paint.SourceSpace == "DeviceCMYK" && space.Model == "DeviceCMYK" && !space.Calibrated()
|
compatible := overprint && !processOverprint && style.OverprintMode == 1 && uniform && paint.SourceSpace == "DeviceCMYK" && space.Model == "DeviceCMYK" && !space.Calibrated()
|
||||||
|
var marked *[4]bool
|
||||||
|
if processOverprint {
|
||||||
|
marked = &processMask
|
||||||
|
}
|
||||||
var pattern []pdfCompositePixel
|
var pattern []pdfCompositePixel
|
||||||
if paint.Tiling != nil && (node.image == nil || node.image.Image.ImageMask) {
|
if paint.Tiling != nil && (node.image == nil || node.image.Image.ImageMask) {
|
||||||
backdrop := pixels
|
backdrop := pixels
|
||||||
@@ -241,26 +245,14 @@ func (c *pdfCompositor) drawPaint(node pdfCompositeNode, outline bool, paint pdf
|
|||||||
}
|
}
|
||||||
if initial != nil {
|
if initial != nil {
|
||||||
pixel := initial[index]
|
pixel := initial[index]
|
||||||
if processOverprint {
|
if err := pdfCompositePaintOver(&pixel, values, opacity, space, mode, compatible, marked); err != nil {
|
||||||
for c := range values {
|
return err
|
||||||
if !processMask[c] {
|
|
||||||
values[c] = pixel.values[c] * pixel.alpha
|
|
||||||
}
|
}
|
||||||
}
|
if err := pdfCompositeInterpolate(&pixels[index], pixel, shape); err != nil {
|
||||||
}
|
|
||||||
if err := pdfCompositePaintOver(&pixel, values, opacity, space, mode, compatible); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pdfCompositeInterpolate(&pixels[index], pixel, shape)
|
|
||||||
} else {
|
} else {
|
||||||
if processOverprint {
|
if err := pdfCompositePaintOver(&pixels[index], values, shape*opacity, space, mode, compatible, marked); err != nil {
|
||||||
for c := range values {
|
|
||||||
if !processMask[c] {
|
|
||||||
values[c] = pixels[index].values[c] * pixels[index].alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := pdfCompositePaintOver(&pixels[index], values, shape*opacity, space, mode, compatible); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,15 +267,14 @@ func (c *pdfCompositor) drawPaint(node pdfCompositeNode, outline bool, paint pdf
|
|||||||
|
|
||||||
// pdfCompositeInterpolate 按形状覆盖混合预乘颜色及累计透明度
|
// pdfCompositeInterpolate 按形状覆盖混合预乘颜色及累计透明度
|
||||||
// 入参: target 原像素与输出, source 替换像素, shape 覆盖率
|
// 入参: target 原像素与输出, source 替换像素, shape 覆盖率
|
||||||
func pdfCompositeInterpolate(target *pdfCompositePixel, source pdfCompositePixel, shape float64) {
|
// 返回: error 像素或覆盖率错误
|
||||||
alpha := target.alpha*(1-shape) + source.alpha*shape
|
func pdfCompositeInterpolate(target *pdfCompositePixel, source pdfCompositePixel, shape float64) error {
|
||||||
if alpha != 0 {
|
pixel := pdfgo.ColorantPixel{Values: target.values[:], Alpha: target.alpha, Shape: target.shape, Effect: target.effect}
|
||||||
for i := range target.values {
|
if err := pixel.Interpolate(pdfgo.ColorantPixel{Values: source.values[:], Alpha: source.alpha, Shape: source.shape, Effect: source.effect}, shape); err != nil {
|
||||||
target.values[i] = (target.values[i]*target.alpha*(1-shape) + source.values[i]*source.alpha*shape) / alpha
|
return err
|
||||||
}
|
}
|
||||||
}
|
target.alpha, target.effect = pixel.Alpha, pixel.Effect
|
||||||
target.alpha = alpha
|
return nil
|
||||||
target.effect = target.effect*(1-shape) + source.effect*shape
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawGroup 按隔离标志保留初始背景,移除背景贡献后应用组透明度
|
// drawGroup 按隔离标志保留初始背景,移除背景贡献后应用组透明度
|
||||||
@@ -391,7 +382,9 @@ func (c *pdfCompositor) drawKnockout(nodes []pdfCompositeNode, initial, pixels [
|
|||||||
for y := region.Min.Y; y < region.Max.Y; y++ {
|
for y := region.Min.Y; y < region.Max.Y; y++ {
|
||||||
for x := region.Min.X; x < region.Max.X; x++ {
|
for x := region.Min.X; x < region.Max.X; x++ {
|
||||||
i := y*c.width + x
|
i := y*c.width + x
|
||||||
pdfCompositeKnockout(&pixels[i], initial[i], candidate[i])
|
if err := pdfCompositeKnockout(&pixels[i], initial[i], candidate[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,51 +393,46 @@ func (c *pdfCompositor) drawKnockout(nodes []pdfCompositeNode, initial, pixels [
|
|||||||
|
|
||||||
// pdfCompositeKnockout 移除未覆盖区域的初始背景贡献,保留形状与不透明度差异
|
// pdfCompositeKnockout 移除未覆盖区域的初始背景贡献,保留形状与不透明度差异
|
||||||
// 入参: target 累计组结果, initial 初始背景, source 单个对象合成结果
|
// 入参: target 累计组结果, initial 初始背景, source 单个对象合成结果
|
||||||
func pdfCompositeKnockout(target *pdfCompositePixel, initial, source pdfCompositePixel) {
|
// 返回: error 像素错误
|
||||||
f := source.shape
|
func pdfCompositeKnockout(target *pdfCompositePixel, initial, source pdfCompositePixel) error {
|
||||||
if f == 0 {
|
pixel := pdfgo.ColorantPixel{Values: target.values[:], Alpha: target.alpha, Shape: target.shape, Effect: target.effect}
|
||||||
return
|
if err := pixel.Knockout(
|
||||||
|
pdfgo.ColorantPixel{Values: initial.values[:], Alpha: initial.alpha, Shape: initial.shape, Effect: initial.effect},
|
||||||
|
pdfgo.ColorantPixel{Values: source.values[:], Alpha: source.alpha, Shape: source.shape, Effect: source.effect},
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
alpha := target.alpha*(1-f) + source.alpha - initial.alpha*(1-f)
|
target.alpha, target.effect, target.shape = pixel.Alpha, pixel.Effect, pixel.Shape
|
||||||
if alpha > 0 {
|
return nil
|
||||||
for j := range target.values {
|
|
||||||
target.values[j] = math.Max(0, math.Min(1, (target.values[j]*target.alpha*(1-f)+source.values[j]*source.alpha-initial.values[j]*initial.alpha*(1-f))/alpha))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target.alpha = math.Max(0, math.Min(1, alpha))
|
|
||||||
target.effect = target.effect*(1-f) + source.effect
|
|
||||||
target.shape += f * (1 - target.shape)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pdfCompositeGroup 移除组初始背景贡献后,将组结果合成到父空间
|
// pdfCompositeGroup 移除组初始背景贡献后,将组结果合成到父空间
|
||||||
// 入参: pixels 父组输出, initial 初始背景, result 组结果, space 组空间, parent 父空间, opacity 组不透明度, mask 蒙版, mode 混合模式, intent 渲染意图, conversion 设备转换函数
|
// 入参: pixels 父组输出, initial 初始背景, result 组结果, space 组空间, parent 父空间, opacity 组不透明度, mask 蒙版, mode 混合模式, intent 渲染意图, conversion 设备转换函数
|
||||||
// 返回: error 颜色或混合错误
|
// 返回: error 颜色或混合错误
|
||||||
func pdfCompositeGroup(pixels, initial, result []pdfCompositePixel, space, parent *pdfgo.ColorSpace, opacity float64, mask []float64, mode, intent pdfgo.Name, conversion pdfgo.ColorConversion) error {
|
func pdfCompositeGroup(pixels, initial, result []pdfCompositePixel, space, parent *pdfgo.ColorSpace, opacity float64, mask []float64, mode, intent pdfgo.Name, conversion pdfgo.ColorConversion) error {
|
||||||
sameSpace := space.Equal(parent)
|
if len(pixels) != len(initial) || len(pixels) != len(result) || mask != nil && len(mask) != len(pixels) {
|
||||||
components := space.Components()
|
return fmt.Errorf("invalid PDF transparency group buffers")
|
||||||
for i, pixel := range result {
|
|
||||||
pixels[i].shape += pixel.shape * (1 - pixels[i].shape)
|
|
||||||
if pixel.effect == 0 {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
values := pixel.values
|
group, destination := pdfgo.ColorantGroup{Space: space}, pdfgo.ColorantGroup{Space: parent}
|
||||||
for j := 0; j < components; j++ {
|
compositor, err := destination.PrepareGroup(&group, mode, intent, conversion)
|
||||||
values[j] = math.Max(0, math.Min(1, (pixel.values[j]*pixel.alpha-initial[i].values[j]*initial[i].alpha*(1-pixel.effect))/pixel.effect))
|
|
||||||
}
|
|
||||||
if !sameSpace {
|
|
||||||
var err error
|
|
||||||
values, err = parent.ConvertWith(values[:components], space, intent, conversion)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
components, parentComponents := space.Components(), parent.Components()
|
||||||
alpha := pixel.effect * opacity
|
for i, pixel := range result {
|
||||||
|
alpha := opacity
|
||||||
if mask != nil {
|
if mask != nil {
|
||||||
alpha *= mask[i]
|
alpha *= mask[i]
|
||||||
}
|
}
|
||||||
if err := pdfCompositeOver(&pixels[i], values, alpha, parent, mode); err != nil {
|
target := pdfgo.ColorantPixel{Values: pixels[i].values[:parentComponents], Alpha: pixels[i].alpha, Shape: pixels[i].shape, Effect: pixels[i].effect}
|
||||||
|
if err := compositor.Composite(&target,
|
||||||
|
pdfgo.ColorantPixel{Values: initial[i].values[:components], Alpha: initial[i].alpha, Shape: initial[i].shape, Effect: initial[i].effect},
|
||||||
|
pdfgo.ColorantPixel{Values: pixel.values[:components], Alpha: pixel.alpha, Shape: pixel.shape, Effect: pixel.effect},
|
||||||
|
alpha,
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
pixels[i].alpha, pixels[i].effect, pixels[i].shape = target.Alpha, target.Effect, target.Shape
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -737,17 +725,34 @@ func pdfCompositeColor(paint pdfgo.Paint, point pdfgo.Point, space *pdfgo.ColorS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pdfCompositePaintOver 以兼容叠印计算零色料分量,再应用对象混合模式
|
// pdfCompositePaintOver 以兼容叠印计算零色料分量,再应用对象混合模式
|
||||||
// 入参: backdrop 背景及输出, source 源分量, alpha 源透明度, space 混合空间, mode 混合模式, compatible 是否保留零色料背景
|
// 入参: backdrop 背景及输出, source 源分量, alpha 源透明度, space 混合空间, mode 混合模式, compatible 是否保留零色料背景, marked 原生过程通道,nil使用普通空间
|
||||||
// 返回: error 未支持的混合模式
|
// 返回: error 未支持的混合模式
|
||||||
func pdfCompositePaintOver(backdrop *pdfCompositePixel, source [4]float64, alpha float64, space *pdfgo.ColorSpace, mode pdfgo.Name, compatible bool) error {
|
func pdfCompositePaintOver(backdrop *pdfCompositePixel, source [4]float64, alpha float64, space *pdfgo.ColorSpace, mode pdfgo.Name, compatible bool, marked *[4]bool) error {
|
||||||
|
if !compatible && marked == nil {
|
||||||
|
return pdfCompositeOver(backdrop, source, alpha, space, mode)
|
||||||
|
}
|
||||||
|
if alpha == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mask := [4]bool{true, true, true, true}
|
||||||
|
if marked != nil {
|
||||||
|
mask = *marked
|
||||||
|
}
|
||||||
if compatible {
|
if compatible {
|
||||||
for i := range source {
|
for i := range source {
|
||||||
if source[i] == 0 {
|
if source[i] == 0 {
|
||||||
source[i] = backdrop.values[i] * backdrop.alpha
|
mask[i] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pdfCompositeOver(backdrop, source, alpha, space, mode)
|
values, combined, err := space.CompositeOverprint(backdrop.values[:space.Components()], source[:space.Components()], backdrop.alpha, alpha, mode, mask[:space.Components()])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(backdrop.values[:space.Components()], values[:space.Components()])
|
||||||
|
backdrop.alpha = combined
|
||||||
|
backdrop.effect = alpha + backdrop.effect*(1-alpha)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// pdfCompositeOver 按PDF透明模型合成源颜色,四色混合使用分量补色
|
// pdfCompositeOver 按PDF透明模型合成源颜色,四色混合使用分量补色
|
||||||
@@ -757,178 +762,12 @@ func pdfCompositeOver(backdrop *pdfCompositePixel, source [4]float64, alpha floa
|
|||||||
if alpha == 0 {
|
if alpha == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
combined := alpha + backdrop.alpha*(1-alpha)
|
values, combined, err := space.Composite(backdrop.values[:space.Components()], source[:space.Components()], backdrop.alpha, alpha, mode)
|
||||||
nonseparable := mode == "Hue" || mode == "Saturation" || mode == "Color" || mode == "Luminosity"
|
|
||||||
var blend [4]float64
|
|
||||||
if nonseparable {
|
|
||||||
blend = pdfNonseparableBlend(backdrop.values, source, space, mode)
|
|
||||||
}
|
|
||||||
for i := 0; i < space.Components(); i++ {
|
|
||||||
mixed := blend[i]
|
|
||||||
if !nonseparable {
|
|
||||||
b, s := backdrop.values[i], source[i]
|
|
||||||
if space.Model == "DeviceCMYK" {
|
|
||||||
b, s = 1-b, 1-s
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
mixed, err = pdfSeparableBlend(b, s, mode)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if space.Model == "DeviceCMYK" {
|
copy(backdrop.values[:space.Components()], values[:space.Components()])
|
||||||
mixed = 1 - mixed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
backdrop.values[i] = math.Max(0, math.Min(1, ((1-alpha)*backdrop.alpha*backdrop.values[i]+alpha*((1-backdrop.alpha)*source[i]+backdrop.alpha*mixed))/combined))
|
|
||||||
}
|
|
||||||
backdrop.alpha = combined
|
backdrop.alpha = combined
|
||||||
backdrop.effect = alpha + backdrop.effect*(1-alpha)
|
backdrop.effect = alpha + backdrop.effect*(1-alpha)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// pdfNonseparableBlend 按PDF非分离混合规则计算颜色,CMYK黑版独立取值
|
|
||||||
// 入参: b 背景颜色, s 源颜色, space 混合空间, mode 混合模式
|
|
||||||
// 返回: [4]float64 混合分量
|
|
||||||
func pdfNonseparableBlend(b, s [4]float64, space *pdfgo.ColorSpace, mode pdfgo.Name) [4]float64 {
|
|
||||||
if space.Model == "DeviceGray" {
|
|
||||||
if mode == "Luminosity" {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
if space.Model == "DeviceCMYK" {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
b[i], s[i] = 1-b[i], 1-s[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result := b
|
|
||||||
switch mode {
|
|
||||||
case "Hue":
|
|
||||||
result = pdfSetLuminosity(pdfSetSaturation(s, pdfSaturation(b)), pdfLuminosity(b))
|
|
||||||
case "Saturation":
|
|
||||||
result = pdfSetLuminosity(pdfSetSaturation(b, pdfSaturation(s)), pdfLuminosity(b))
|
|
||||||
case "Color":
|
|
||||||
result = pdfSetLuminosity(s, pdfLuminosity(b))
|
|
||||||
case "Luminosity":
|
|
||||||
result = pdfSetLuminosity(b, pdfLuminosity(s))
|
|
||||||
}
|
|
||||||
if space.Model == "DeviceCMYK" {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
result[i] = 1 - result[i]
|
|
||||||
}
|
|
||||||
result[3] = b[3]
|
|
||||||
if mode == "Luminosity" {
|
|
||||||
result[3] = s[3]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// pdfLuminosity 计算非分离混合使用的亮度
|
|
||||||
// 入参: c 加色分量
|
|
||||||
// 返回: float64 亮度
|
|
||||||
func pdfLuminosity(c [4]float64) float64 { return .3*c[0] + .59*c[1] + .11*c[2] }
|
|
||||||
|
|
||||||
// pdfSaturation 计算非分离混合使用的饱和度
|
|
||||||
// 入参: c 加色分量
|
|
||||||
// 返回: float64 饱和度
|
|
||||||
func pdfSaturation(c [4]float64) float64 {
|
|
||||||
return math.Max(c[0], math.Max(c[1], c[2])) - math.Min(c[0], math.Min(c[1], c[2]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// pdfSetSaturation 保持分量顺序并调整饱和度
|
|
||||||
// 入参: c 加色分量, saturation 目标饱和度
|
|
||||||
// 返回: [4]float64 调整后分量
|
|
||||||
func pdfSetSaturation(c [4]float64, saturation float64) [4]float64 {
|
|
||||||
minimum := math.Min(c[0], math.Min(c[1], c[2]))
|
|
||||||
span := pdfSaturation(c)
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
if span == 0 {
|
|
||||||
c[i] = 0
|
|
||||||
} else {
|
|
||||||
c[i] = (c[i] - minimum) * saturation / span
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// pdfSetLuminosity 调整亮度并按PDF ClipColor规则压缩超界分量
|
|
||||||
// 入参: c 加色分量, luminosity 目标亮度
|
|
||||||
// 返回: [4]float64 调整后分量
|
|
||||||
func pdfSetLuminosity(c [4]float64, luminosity float64) [4]float64 {
|
|
||||||
delta := luminosity - pdfLuminosity(c)
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
c[i] += delta
|
|
||||||
}
|
|
||||||
minimum, maximum := math.Min(c[0], math.Min(c[1], c[2])), math.Max(c[0], math.Max(c[1], c[2]))
|
|
||||||
if minimum < 0 {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
c[i] = luminosity + (c[i]-luminosity)*luminosity/(luminosity-minimum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if maximum > 1 {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
c[i] = luminosity + (c[i]-luminosity)*(1-luminosity)/(maximum-luminosity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// pdfSeparableBlend 计算标准可分离混合函数
|
|
||||||
// 入参: b 背景分量, s 源分量, mode 混合模式
|
|
||||||
// 返回: float64 混合分量, error 未支持的模式
|
|
||||||
func pdfSeparableBlend(b, s float64, mode pdfgo.Name) (float64, error) {
|
|
||||||
switch mode {
|
|
||||||
case "", "Normal", "Compatible":
|
|
||||||
return s, nil
|
|
||||||
case "Multiply":
|
|
||||||
return b * s, nil
|
|
||||||
case "Screen":
|
|
||||||
return b + s - b*s, nil
|
|
||||||
case "Overlay":
|
|
||||||
if b <= .5 {
|
|
||||||
return 2 * b * s, nil
|
|
||||||
}
|
|
||||||
return 1 - 2*(1-b)*(1-s), nil
|
|
||||||
case "Darken":
|
|
||||||
return math.Min(b, s), nil
|
|
||||||
case "Lighten":
|
|
||||||
return math.Max(b, s), nil
|
|
||||||
case "ColorDodge":
|
|
||||||
if b == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
if s == 1 {
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
return math.Min(1, b/(1-s)), nil
|
|
||||||
case "ColorBurn":
|
|
||||||
if b == 1 {
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
if s == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return 1 - math.Min(1, (1-b)/s), nil
|
|
||||||
case "HardLight":
|
|
||||||
if s <= .5 {
|
|
||||||
return 2 * b * s, nil
|
|
||||||
}
|
|
||||||
return 1 - 2*(1-b)*(1-s), nil
|
|
||||||
case "SoftLight":
|
|
||||||
if s <= .5 {
|
|
||||||
return b - (1-2*s)*b*(1-b), nil
|
|
||||||
}
|
|
||||||
d := math.Sqrt(b)
|
|
||||||
if b <= .25 {
|
|
||||||
d = ((16*b-12)*b + 4) * b
|
|
||||||
}
|
|
||||||
return b + (2*s-1)*(d-b), nil
|
|
||||||
case "Difference":
|
|
||||||
return math.Abs(b - s), nil
|
|
||||||
case "Exclusion":
|
|
||||||
return b + s - 2*b*s, nil
|
|
||||||
default:
|
|
||||||
return 0, &pdfgo.UnsupportedError{Feature: "blend mode " + string(mode)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -176,7 +176,7 @@ func (p *pdfImporter) compositePattern(paint pdfgo.Paint) (*pdfCompositePattern,
|
|||||||
return pattern, nil
|
return pattern, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// directCompositeNode 检查图元及其图案单元能否独立保留,不忽略单元内的背景混合
|
// directCompositeNode 检查图元、透明组和图案单元能否独立保留,逐层核对背景混合
|
||||||
// 入参: node 图元, space 混合空间
|
// 入参: node 图元, space 混合空间
|
||||||
// 返回: bool 是否可直接转换, error 图案解析错误
|
// 返回: bool 是否可直接转换, error 图案解析错误
|
||||||
func (p *pdfImporter) directCompositeNode(node pdfCompositeNode, space *pdfgo.ColorSpace) (bool, error) {
|
func (p *pdfImporter) directCompositeNode(node pdfCompositeNode, space *pdfgo.ColorSpace) (bool, error) {
|
||||||
@@ -219,7 +219,11 @@ func (p *pdfImporter) directCompositeNode(node pdfCompositeNode, space *pdfgo.Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !node.opaque(space) && !(space.SRGBEquivalent() && node.direct()) {
|
directGroup := false
|
||||||
|
if group := node.group; group != nil && !group.Knockout && group.Alpha == 1 && group.SoftMask == nil && pdfNormalBlend(group.BlendMode) && (group.ColorSpace == nil || group.ColorSpace.SRGBEquivalent()) {
|
||||||
|
directGroup = true
|
||||||
|
}
|
||||||
|
if !node.opaque(space) && !(space.SRGBEquivalent() && (directGroup || node.direct())) {
|
||||||
if node.textObject != nil && space.SRGBEquivalent() {
|
if node.textObject != nil && space.SRGBEquivalent() {
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
if direct, err := p.directCompositeNode(child, space); err != nil || !direct {
|
if direct, err := p.directCompositeNode(child, space); err != nil || !direct {
|
||||||
|
|||||||
Reference in new issue
Block a user