mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
201082daa0
commit
c250ca914e
7 files changed
+185
-17
No files matched your search
@@ -11,7 +11,7 @@ require (
|
|||||||
github.com/xiaoqidun/guigo v0.0.0-20261020075840-622fd8853d72
|
github.com/xiaoqidun/guigo v0.0.0-20261020075840-622fd8853d72
|
||||||
github.com/xiaoqidun/j2kgo v1.0.2
|
github.com/xiaoqidun/j2kgo v1.0.2
|
||||||
github.com/xiaoqidun/jbig2 v1.1.8
|
github.com/xiaoqidun/jbig2 v1.1.8
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261021045903-4d52abc4d1c4
|
github.com/xiaoqidun/pdfgo v0.0.0-20261021071541-3fdb0cab9d95
|
||||||
golang.org/x/crypto v0.58.0
|
golang.org/x/crypto v0.58.0
|
||||||
golang.org/x/image v0.47.0
|
golang.org/x/image v0.47.0
|
||||||
golang.org/x/text v0.43.0
|
golang.org/x/text v0.43.0
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ github.com/xiaoqidun/j2kgo v1.0.2 h1:OAjjEOfaBaQzBNcj9wx+eM68UI5pewR6SzNZa1R1DIw
|
|||||||
github.com/xiaoqidun/j2kgo v1.0.2/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA=
|
github.com/xiaoqidun/j2kgo v1.0.2/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.8 h1:dERPW7F8gs18a/DOD9gXHR3fP1Vl6KhCNsLnq5l3BIo=
|
github.com/xiaoqidun/jbig2 v1.1.8 h1:dERPW7F8gs18a/DOD9gXHR3fP1Vl6KhCNsLnq5l3BIo=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.8/go.mod h1:9jromqVwMqlptUDEt6Yip8vqYA5I1YjhG7FgMl/Z2dA=
|
github.com/xiaoqidun/jbig2 v1.1.8/go.mod h1:9jromqVwMqlptUDEt6Yip8vqYA5I1YjhG7FgMl/Z2dA=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261021045903-4d52abc4d1c4 h1:RXZ4mH75yoSxdNuXdgvRsoz6DHJGYz71+r1A14qLoj8=
|
github.com/xiaoqidun/pdfgo v0.0.0-20261021071541-3fdb0cab9d95 h1:Fcr2BVlFYc+BZ62OhrN55QIdkcH47ryxKWnFqCRcoE4=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261021045903-4d52abc4d1c4/go.mod h1:AaabzsObZ8Z76AcaWbo6swRROrJsblyIGB7HbieeJA4=
|
github.com/xiaoqidun/pdfgo v0.0.0-20261021071541-3fdb0cab9d95/go.mod h1:AaabzsObZ8Z76AcaWbo6swRROrJsblyIGB7HbieeJA4=
|
||||||
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=
|
||||||
|
|||||||
@@ -260,6 +260,21 @@ func SanitizeCFF(data []byte) ([]byte, error) {
|
|||||||
if kind == 1 {
|
if kind == 1 {
|
||||||
return normalizeCFFType1(data, topDict)
|
return normalizeCFFType1(data, topDict)
|
||||||
}
|
}
|
||||||
|
private, err := readType2Private(data, topDict[18], &type2Font{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
count := len(private)
|
||||||
|
if err := normalizeCFFPrivate(private); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(private) != count {
|
||||||
|
font, err := readType2Font(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rebuildNamedCFF(data, font, font.chars, nil)
|
||||||
|
}
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
fdArrOff, err := cffDictOffset(data, topDict, 1236)
|
fdArrOff, err := cffDictOffset(data, topDict, 1236)
|
||||||
@@ -342,17 +357,21 @@ func SanitizeCFF(data []byte) ([]byte, error) {
|
|||||||
return encodeSanitizedCFF(data[:hdrSize], nameIndexData, topDict, stringIndexData, globalSubrIndexData, charStringsData, finalPrivData, localSubrData), nil
|
return encodeSanitizedCFF(data[:hdrSize], nameIndexData, topDict, stringIndexData, globalSubrIndexData, charStringsData, finalPrivData, localSubrData), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalizeCFFPrivate 移除旧版CFF中不生效的缺省字段,拒绝需要额外解释的取值
|
// normalizeCFFPrivate 移除废弃的加粗阈值及未加密标记,拒绝加密字形程序
|
||||||
// 入参: dict 私有字典
|
// 入参: dict 私有字典
|
||||||
// 返回: error 不支持的旧版行为
|
// 返回: error 不支持的旧版行为
|
||||||
func normalizeCFFPrivate(dict cffDict) error {
|
func normalizeCFFPrivate(dict cffDict) error {
|
||||||
for op, value := range map[int]float64{1215: 0, 1216: -1} {
|
if values, ok := dict[1215]; ok {
|
||||||
if values, ok := dict[op]; ok {
|
if len(values) != 1 || !finite(values[0]) {
|
||||||
if len(values) != 1 || values[0] != value {
|
return fmt.Errorf("invalid legacy CFF bold threshold")
|
||||||
return fmt.Errorf("unsupported legacy CFF private operator %d", op)
|
|
||||||
}
|
|
||||||
delete(dict, op)
|
|
||||||
}
|
}
|
||||||
|
delete(dict, 1215)
|
||||||
|
}
|
||||||
|
if values, ok := dict[1216]; ok {
|
||||||
|
if len(values) != 1 || values[0] != -1 {
|
||||||
|
return fmt.Errorf("unsupported legacy CFF private operator 1216")
|
||||||
|
}
|
||||||
|
delete(dict, 1216)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,6 +231,9 @@ func rebuildNamedCFF(data []byte, font *type2Font, chars [][]byte, ids []uint16)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := normalizeCFFPrivate(private); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
privateData, err := encodeCFFSubsetPrivate(private)
|
privateData, err := encodeCFFSubsetPrivate(private)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ func (f *Outliner) phantomPoints(id uint16, data []byte) [4]ttPoint {
|
|||||||
return [4]ttPoint{{x: left * 64, ox: left * 64}, {x: (left + int32(f.Font.GlyphAdvance(id))) * 64, ox: (left + int32(f.Font.GlyphAdvance(id))) * 64}, {y: top * 64, oy: top * 64}, {y: (top - int32(f.Font.GlyphVerticalAdvance(id))) * 64, oy: (top - int32(f.Font.GlyphVerticalAdvance(id))) * 64}}
|
return [4]ttPoint{{x: left * 64, ox: left * 64}, {x: (left + int32(f.Font.GlyphAdvance(id))) * 64, ox: (left + int32(f.Font.GlyphAdvance(id))) * 64}, {y: top * 64, oy: top * 64}, {y: (top - int32(f.Font.GlyphVerticalAdvance(id))) * 64, oy: (top - int32(f.Font.GlyphVerticalAdvance(id))) * 64}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ttRecoverableHint 判断栈参数、指令引用或控制流错误是否允许保留执行前轮廓
|
// ttRecoverableHint 判断栈参数、区点轮廓及函数引用或控制流错误是否允许保留执行前轮廓
|
||||||
// 入参: err 指令错误
|
// 入参: err 指令错误
|
||||||
// 返回: bool 是否可恢复
|
// 返回: bool 是否可恢复
|
||||||
func ttRecoverableHint(err error) bool {
|
func ttRecoverableHint(err error) bool {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
// errTTStackUnderflow 表示字形程序缺少必需的栈参数
|
// errTTStackUnderflow 表示字形程序缺少必需的栈参数
|
||||||
var errTTStackUnderflow = errors.New("TrueType instruction stack underflow")
|
var errTTStackUnderflow = errors.New("TrueType instruction stack underflow")
|
||||||
|
|
||||||
// errTTInvalidReference 表示字形指令引用不存在的点或函数
|
// errTTInvalidReference 表示字形指令引用不存在的区、点、轮廓或函数
|
||||||
var errTTInvalidReference = errors.New("invalid TrueType instruction reference")
|
var errTTInvalidReference = errors.New("invalid TrueType instruction reference")
|
||||||
|
|
||||||
// errTTInvalidControlFlow 表示条件分支未闭合或跳转超出字节码范围
|
// errTTInvalidControlFlow 表示条件分支未闭合或跳转超出字节码范围
|
||||||
@@ -380,7 +380,8 @@ func (v *ttInterpreter) execute(code []byte, depth int) error {
|
|||||||
case op >= 0x13 && op <= 0x16:
|
case op >= 0x13 && op <= 0x16:
|
||||||
z := v.pop()
|
z := v.pop()
|
||||||
if z < 0 || z > 1 {
|
if z < 0 || z > 1 {
|
||||||
return fmt.Errorf("invalid TrueType zone")
|
v.err = fmt.Errorf("%w: zone %d", errTTInvalidReference, z)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if op == 0x16 {
|
if op == 0x16 {
|
||||||
g.zone = [3]int32{z, z, z}
|
g.zone = [3]int32{z, z, z}
|
||||||
@@ -854,7 +855,7 @@ func (v *ttInterpreter) shift(op byte) {
|
|||||||
if op <= 0x35 {
|
if op <= 0x35 {
|
||||||
contour := v.pop()
|
contour := v.pop()
|
||||||
if contour < 0 || int64(contour) >= int64(len(v.ends)) {
|
if contour < 0 || int64(contour) >= int64(len(v.ends)) {
|
||||||
v.err = fmt.Errorf("invalid TrueType contour reference")
|
v.err = fmt.Errorf("%w: contour %d", errTTInvalidReference, contour)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if contour > 0 {
|
if contour > 0 {
|
||||||
@@ -864,7 +865,7 @@ func (v *ttInterpreter) shift(op byte) {
|
|||||||
} else {
|
} else {
|
||||||
target = v.pop()
|
target = v.pop()
|
||||||
if target < 0 || target > 1 {
|
if target < 0 || target > 1 {
|
||||||
v.err = fmt.Errorf("invalid TrueType zone")
|
v.err = fmt.Errorf("%w: zone %d", errTTInvalidReference, target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
to = len(v.zones[target])
|
to = len(v.zones[target])
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func (s rasterImageSource) RGBA64At(x, y int) color.RGBA64 {
|
|||||||
return color.RGBA64{R: uint16(r), G: uint16(g), B: uint16(b), A: uint16(a)}
|
return color.RGBA64{R: uint16(r), G: uint16(g), B: uint16(b), A: uint16(a)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawRasterAxisImage 按像素中心进行轴向缩放,逐行复用横向采样并保留镜像与小数坐标
|
// drawRasterAxisImage 轴向缩放复用逐行采样,细轴按面积覆盖并保留镜像与小数坐标
|
||||||
// 入参: dst 目标图像, src 源图像, m 像素变换, op 合成操作
|
// 入参: dst 目标图像, src 源图像, m 像素变换, op 合成操作
|
||||||
// 返回: bool 是否使用轴向采样
|
// 返回: bool 是否使用轴向采样
|
||||||
func drawRasterAxisImage(dst draw.Image, src image.Image, m [6]float64, op draw.Op) bool {
|
func drawRasterAxisImage(dst draw.Image, src image.Image, m [6]float64, op draw.Op) bool {
|
||||||
@@ -78,8 +78,14 @@ func drawRasterAxisImage(dst draw.Image, src image.Image, m [6]float64, op draw.
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if float64(r.Dx())*math.Min(float64(r.Dy()), math.Ceil(4*math.Max(1, sy))+2) > rasterScaleBufferLimit/32 {
|
if float64(r.Dx())*math.Min(float64(r.Dy()), math.Ceil(4*math.Max(1, sy))+2) > rasterScaleBufferLimit/32 {
|
||||||
|
if sx*float64(b.Dx()) < 1 || sy*float64(b.Dy()) < 1 {
|
||||||
|
return drawRasterFootprintImage(dst, src, m, op, r)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if !rasterFootprintWeightsFit(b, r, m) {
|
||||||
|
return drawRasterFootprintImage(dst, src, m, op, r)
|
||||||
|
}
|
||||||
xs := rasterImageAxis(r.Min.X, r.Max.X, b.Min.X, b.Max.X, m[0], m[4])
|
xs := rasterImageAxis(r.Min.X, r.Max.X, b.Min.X, b.Max.X, m[0], m[4])
|
||||||
ys := rasterImageAxis(r.Min.Y, r.Max.Y, b.Min.Y, b.Max.Y, m[3], m[5])
|
ys := rasterImageAxis(r.Min.Y, r.Max.Y, b.Min.Y, b.Max.Y, m[3], m[5])
|
||||||
if len(xs) == 0 || len(ys) == 0 {
|
if len(xs) == 0 || len(ys) == 0 {
|
||||||
@@ -139,10 +145,13 @@ func drawRasterAxisImage(dst draw.Image, src image.Image, m [6]float64, op draw.
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// rasterImageAxis 按连续源坐标生成CatmullRom权重,不对图像几何边界取整
|
// rasterImageAxis 按连续源坐标生成CatmullRom权重,亚像素细轴使用面积覆盖权重
|
||||||
// 入参: first 目标起点, end 目标终点, source 源起点, limit 源终点, scale 缩放比例, offset 平移距离
|
// 入参: first 目标起点, end 目标终点, source 源起点, limit 源终点, scale 缩放比例, offset 平移距离
|
||||||
// 返回: []rasterAxisSample 轴向采样表
|
// 返回: []rasterAxisSample 轴向采样表
|
||||||
func rasterImageAxis(first, end, source, limit int, scale, offset float64) []rasterAxisSample {
|
func rasterImageAxis(first, end, source, limit int, scale, offset float64) []rasterAxisSample {
|
||||||
|
if math.Abs(scale)*float64(limit-source) < 1 {
|
||||||
|
return rasterImageFootprintAxis(first, end, source, limit, scale, offset)
|
||||||
|
}
|
||||||
result := make([]rasterAxisSample, 0, end-first)
|
result := make([]rasterAxisSample, 0, end-first)
|
||||||
argument := math.Min(1, math.Abs(scale))
|
argument := math.Min(1, math.Abs(scale))
|
||||||
support := draw.CatmullRom.Support / argument
|
support := draw.CatmullRom.Support / argument
|
||||||
@@ -183,6 +192,142 @@ func rasterImageAxis(first, end, source, limit int, scale, offset float64) []ras
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rasterFootprintWeightsFit 检查细轴权重表的合计预算,乘法前排除溢出
|
||||||
|
// 入参: source 源边界, target 目标范围, m 像素变换
|
||||||
|
// 返回: bool 权重表是否符合预算
|
||||||
|
func rasterFootprintWeightsFit(source, target image.Rectangle, m [6]float64) bool {
|
||||||
|
budget := rasterScaleBufferLimit / 8
|
||||||
|
for _, axis := range [...]struct {
|
||||||
|
source, target int
|
||||||
|
scale float64
|
||||||
|
}{{source.Dx(), target.Dx(), m[0]}, {source.Dy(), target.Dy(), m[3]}} {
|
||||||
|
if math.Abs(axis.scale)*float64(axis.source) < 1 && axis.target > 0 {
|
||||||
|
n, samples := axis.source, min(2, axis.target)
|
||||||
|
if n > budget/samples {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
budget -= n * samples
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// rasterImageWeightSpan 提供单个目标像素的源范围和权重,不分配整轴权重表
|
||||||
|
// 入参: pixel 目标像素, source 源起点, limit 源终点, scale 缩放比例, offset 平移距离
|
||||||
|
// 返回: int 源起点, int 源终点, func(int)float64 源权重
|
||||||
|
func rasterImageWeightSpan(pixel, source, limit int, scale, offset float64) (int, int, func(int) float64) {
|
||||||
|
if math.Abs(scale)*float64(limit-source) < 1 {
|
||||||
|
left, right := (float64(pixel)-offset)/scale, (float64(pixel+1)-offset)/scale
|
||||||
|
if right < left {
|
||||||
|
left, right = right, left
|
||||||
|
}
|
||||||
|
if right <= float64(source) || left >= float64(limit) {
|
||||||
|
return 0, 0, nil
|
||||||
|
}
|
||||||
|
return source, limit, func(x int) float64 {
|
||||||
|
return math.Max(0, math.Min(float64(x)+1, right)-math.Max(float64(x), left)) * math.Abs(scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
center := (float64(pixel) + .5 - offset) / scale
|
||||||
|
if center < float64(source) || center >= float64(limit) {
|
||||||
|
return 0, 0, nil
|
||||||
|
}
|
||||||
|
center -= .5
|
||||||
|
argument := math.Min(1, math.Abs(scale))
|
||||||
|
support := draw.CatmullRom.Support / argument
|
||||||
|
start := int(max(float64(source), math.Floor(center-support)))
|
||||||
|
stop := int(min(float64(limit), math.Ceil(center+support)))
|
||||||
|
weight := func(x int) float64 {
|
||||||
|
distance := math.Abs((float64(x) - center) * argument)
|
||||||
|
if distance < draw.CatmullRom.Support {
|
||||||
|
return draw.CatmullRom.At(distance)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var total float64
|
||||||
|
for x := start; x < stop; x++ {
|
||||||
|
total += weight(x)
|
||||||
|
}
|
||||||
|
return start, stop, func(x int) float64 { return weight(x) / total }
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawRasterFootprintImage 在细轴权重超预算时按原累加顺序逐点绘制
|
||||||
|
// 入参: dst 目标图像, src 源图像, m 像素变换, op 合成操作, target 目标范围
|
||||||
|
// 返回: bool 是否完成绘制
|
||||||
|
func drawRasterFootprintImage(dst draw.Image, src image.Image, m [6]float64, op draw.Op, target image.Rectangle) bool {
|
||||||
|
pixels, ok := src.(image.RGBA64Image)
|
||||||
|
if !ok {
|
||||||
|
pixels = rasterImageSource{src}
|
||||||
|
}
|
||||||
|
b := src.Bounds()
|
||||||
|
output := image.NewRGBA64(image.Rect(0, 0, 1, 1))
|
||||||
|
var value [1]rasterImagePixel
|
||||||
|
for y := target.Min.Y; y < target.Max.Y; y++ {
|
||||||
|
firstY, endY, wy := rasterImageWeightSpan(y, b.Min.Y, b.Max.Y, m[3], m[5])
|
||||||
|
if firstY == endY {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for x := target.Min.X; x < target.Max.X; x++ {
|
||||||
|
firstX, endX, wx := rasterImageWeightSpan(x, b.Min.X, b.Max.X, m[0], m[4])
|
||||||
|
if firstX == endX {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value[0] = rasterImagePixel{}
|
||||||
|
for sy := firstY; sy < endY; sy++ {
|
||||||
|
var row rasterImagePixel
|
||||||
|
for sx := firstX; sx < endX; sx++ {
|
||||||
|
p, weight := pixels.RGBA64At(sx, sy), wx(sx)
|
||||||
|
if weight != 0 {
|
||||||
|
row.r += float64(p.R) * weight
|
||||||
|
row.g += float64(p.G) * weight
|
||||||
|
row.b += float64(p.B) * weight
|
||||||
|
row.a += float64(p.A) * weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if weight := wy(sy); weight != 0 {
|
||||||
|
value[0].r += row.r * weight
|
||||||
|
value[0].g += row.g * weight
|
||||||
|
value[0].b += row.b * weight
|
||||||
|
value[0].a += row.a * weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rasterImageWriteRow(dst, output, value[:], x, y, op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// rasterImageFootprintAxis 对不足一个目标像素的整轴保留真实交叠面积及预乘透明度
|
||||||
|
// 入参: first 目标起点, end 目标终点, source 源起点, limit 源终点, scale 缩放比例, offset 平移距离
|
||||||
|
// 返回: []rasterAxisSample 轴向覆盖权重
|
||||||
|
func rasterImageFootprintAxis(first, end, source, limit int, scale, offset float64) []rasterAxisSample {
|
||||||
|
lo, hi := float64(source)*scale+offset, float64(limit)*scale+offset
|
||||||
|
if hi < lo {
|
||||||
|
lo, hi = hi, lo
|
||||||
|
}
|
||||||
|
result := make([]rasterAxisSample, 0, min(2, end-first))
|
||||||
|
for pixel := first; pixel < end; pixel++ {
|
||||||
|
if math.Min(float64(pixel+1), hi) > math.Max(float64(pixel), lo) {
|
||||||
|
result = append(result, rasterAxisSample{pixel: pixel, first: source})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer := make([]float64, len(result)*(limit-source))
|
||||||
|
for index := range result {
|
||||||
|
sample := &result[index]
|
||||||
|
left, right := (float64(sample.pixel)-offset)/scale, (float64(sample.pixel+1)-offset)/scale
|
||||||
|
if right < left {
|
||||||
|
left, right = right, left
|
||||||
|
}
|
||||||
|
n := limit - source
|
||||||
|
sample.weights, buffer = buffer[:n:n], buffer[n:]
|
||||||
|
for i := range sample.weights {
|
||||||
|
x := float64(source + i)
|
||||||
|
sample.weights[i] = math.Max(0, math.Min(x+1, right)-math.Max(x, left)) * math.Abs(scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// rasterImageHorizontal 按源列复用像素,保留各目标像素的权重累加顺序与中间精度
|
// rasterImageHorizontal 按源列复用像素,保留各目标像素的权重累加顺序与中间精度
|
||||||
// 入参: row 横向缓冲, src 源像素, samples 采样表, y 源行坐标
|
// 入参: row 横向缓冲, src 源像素, samples 采样表, y 源行坐标
|
||||||
func rasterImageHorizontal(row []rasterImagePixel, src image.RGBA64Image, samples []rasterAxisSample, y int) {
|
func rasterImageHorizontal(row []rasterImagePixel, src image.RGBA64Image, samples []rasterAxisSample, y int) {
|
||||||
|
|||||||
Reference in new issue
Block a user