mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
3f797e656b
commit
91e5b57e24
43 files changed
+2018
-384
No files matched your search
+337
-67
@@ -33,34 +33,73 @@ type tiffPlaneInput struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// planarTIFFImage 组合独立分量或映射调色板,不额外分配交错像素缓冲
|
||||
// planarTIFFImage 组合颜色与Alpha分量,保留交错步长并按需映射调色板
|
||||
type planarTIFFImage struct {
|
||||
planes []image.Image
|
||||
bounds image.Rectangle
|
||||
depth uint32
|
||||
packed [4]uint32
|
||||
alpha uint32
|
||||
gray bool
|
||||
palette []color.NRGBA64
|
||||
planes []image.Image
|
||||
bounds image.Rectangle
|
||||
depth uint32
|
||||
packed [5]uint32
|
||||
offsets [5]int
|
||||
alpha uint32
|
||||
gray bool
|
||||
cmyk bool
|
||||
dotRange [4][2]uint16
|
||||
palette []color.NRGBA64
|
||||
stride int
|
||||
invert bool
|
||||
predictorWidth int
|
||||
}
|
||||
|
||||
// decodeTIFFImage 按存储方式解码主图并统一显示方向
|
||||
// 入参: ctx 取消上下文,可为nil, data TIFF编码, layout 主图布局
|
||||
// 返回: image.Image 图像, error 解码或取消错误
|
||||
func decodeTIFFImage(ctx context.Context, data []byte, layout tiffImageLayout) (image.Image, error) {
|
||||
if layout.compression >= 2 && layout.compression <= 4 {
|
||||
pixels, err := decodeTIFFFax(ctx, data, layout)
|
||||
if err == nil && layout.orientation != 1 {
|
||||
pixels = &orientedTIFFImage{source: pixels, tiffImageLayout: layout}
|
||||
// needsSampleView 判断是否需通过分量视图处理存储布局,供尺寸读取和像素解码共用
|
||||
// 返回: bool 是否需要分量视图
|
||||
func (layout tiffImageLayout) needsSampleView() bool {
|
||||
return layout.mask || layout.auxiliary || layout.photometric == 5 || layout.planar && layout.samples > 1 || layout.samples == 2 || layout.depth < 8 && (layout.samples > 1 || layout.depth > 1 || layout.predictor == 2)
|
||||
}
|
||||
|
||||
// decodedPixelBytes 估算每像素解码缓冲,低位深分量按逐位展开后的字节数计量
|
||||
// 返回: uint64 每像素预算字节数
|
||||
func (layout tiffImageLayout) decodedPixelBytes() uint64 {
|
||||
samples := uint64(layout.samples)
|
||||
if layout.planar {
|
||||
if layout.photometric == 5 && samples > 4 {
|
||||
return 20
|
||||
}
|
||||
return pixels, err
|
||||
return 16
|
||||
}
|
||||
ranges, err := tiffBitOrderRanges(ctx, data, layout)
|
||||
if !layout.needsSampleView() {
|
||||
return 8
|
||||
}
|
||||
bytes := uint64(1)
|
||||
if layout.depth == 2 || layout.depth == 4 {
|
||||
bytes = uint64(layout.depth)
|
||||
} else if layout.depth > 8 {
|
||||
bytes = 2
|
||||
}
|
||||
return max(uint64(8), samples*bytes)
|
||||
}
|
||||
|
||||
// decodeTIFFPixels 按存储方式解码指定目录并统一颜色和显示方向
|
||||
// 入参: ctx 取消上下文,可为nil, data TIFF编码, layout 目录布局
|
||||
// 返回: image.Image 图像, error 解码或取消错误
|
||||
func decodeTIFFPixels(ctx context.Context, data []byte, layout tiffImageLayout) (image.Image, error) {
|
||||
profile, err := readTIFFColorProfile(ctx, data, layout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ranges [][2]int64
|
||||
if layout.compression < 2 || layout.compression > 4 {
|
||||
ranges, err = tiffBitOrderRanges(ctx, data, layout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var pixels image.Image
|
||||
if layout.planar && layout.samples > 1 || layout.samples == 1 && (layout.depth == 2 || layout.depth == 4) {
|
||||
if layout.compression >= 2 && layout.compression <= 4 {
|
||||
pixels, err = decodeTIFFFax(ctx, data, layout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if layout.needsSampleView() {
|
||||
result, directories, err := tiffPlaneDirectories(ctx, data, layout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -74,6 +113,11 @@ func decodeTIFFImage(ctx context.Context, data []byte, layout tiffImageLayout) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.predictorWidth != 0 && (result.stride > 1 || result.packed[len(result.planes)] != 0) {
|
||||
if err := restoreTIFFPrediction(ctx, plane, max(result.stride, 1), result.predictorWidth, result.packed[len(result.planes)]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
result.planes = append(result.planes, plane)
|
||||
}
|
||||
pixels = result
|
||||
@@ -86,6 +130,12 @@ func decodeTIFFImage(ctx context.Context, data []byte, layout tiffImageLayout) (
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if profile != nil {
|
||||
pixels, err = applyTIFFColorProfile(ctx, pixels, profile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if ctx != nil {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
@@ -97,7 +147,7 @@ func decodeTIFFImage(ctx context.Context, data []byte, layout tiffImageLayout) (
|
||||
return pixels, nil
|
||||
}
|
||||
|
||||
// tiffPlaneDirectories 为颜色、透明度或调色板索引构建单通道TIFF目录
|
||||
// tiffPlaneDirectories 为颜色、Alpha或调色板索引构建单通道目录,跳过无显示用途的独立平面
|
||||
// 入参: ctx 取消上下文,可为nil, data TIFF编码, layout 已校验的主图布局
|
||||
// 返回: *planarTIFFImage 像素布局, [][]byte 各分量目录, error 字段或布局错误
|
||||
func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayout) (*planarTIFFImage, [][]byte, error) {
|
||||
@@ -131,11 +181,11 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
return tiffPlaneValue(data, order, entry, 0)
|
||||
}
|
||||
samples, err := value(277, 1)
|
||||
if err != nil || samples < 1 || samples > 4 {
|
||||
if err != nil || samples < 1 || samples > math.MaxUint16 {
|
||||
return nil, nil, fmt.Errorf("invalid TIFF plane count")
|
||||
}
|
||||
photo, err := value(262, math.MaxUint32)
|
||||
if err != nil || photo > 3 || photo == 3 && samples != 1 {
|
||||
if err != nil || photo > 5 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF planar color model")
|
||||
}
|
||||
compression, err := value(259, 1)
|
||||
@@ -143,22 +193,55 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
return nil, nil, err
|
||||
}
|
||||
fill, err := value(266, 1)
|
||||
if err != nil || fill != 1 && (fill != 2 || compression != 1 && compression != 3 && compression != 4) {
|
||||
if err != nil || fill != 1 && (fill != 2 || compression != 1 && (compression < 2 || compression > 4)) {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF plane fill order")
|
||||
}
|
||||
predictor, err := value(317, 1)
|
||||
if err != nil || predictor != 1 && predictor != 2 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF plane predictor")
|
||||
}
|
||||
result := &planarTIFFImage{bounds: image.Rect(0, 0, layout.width, layout.height), gray: photo < 2}
|
||||
result := &planarTIFFImage{bounds: image.Rect(0, 0, layout.width, layout.height), gray: photo < 2 || photo == 4, invert: photo == 0, cmyk: photo == 5}
|
||||
if result.cmyk {
|
||||
for _, tag := range []uint16{332, 334} {
|
||||
want := uint32(1)
|
||||
if tag == 334 {
|
||||
want = 4
|
||||
}
|
||||
got, err := value(tag, want)
|
||||
if err != nil || got != want || entries[tag] != nil && order.Uint16(entries[tag][2:]) != 3 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF ink configuration")
|
||||
}
|
||||
}
|
||||
}
|
||||
planes := samples
|
||||
if !layout.planar && samples > 1 {
|
||||
result.stride = int(samples)
|
||||
planes = 1
|
||||
}
|
||||
colors := uint32(3)
|
||||
if photo != 2 {
|
||||
if result.cmyk {
|
||||
colors = 4
|
||||
} else if photo != 2 {
|
||||
colors = 1
|
||||
}
|
||||
if samples != colors {
|
||||
result.alpha, err = value(338, 0)
|
||||
if err != nil || samples != colors+1 || result.alpha != 1 && result.alpha != 2 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF extra planes")
|
||||
entry := entries[338]
|
||||
if samples < colors || entry == nil || order.Uint16(entry[2:]) != 3 || order.Uint32(entry[4:]) != samples-colors {
|
||||
return nil, nil, fmt.Errorf("invalid TIFF extra samples")
|
||||
}
|
||||
for i := uint32(0); i < samples-colors; i++ {
|
||||
if ctx != nil {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
alpha, err := tiffPlaneValue(data, order, entry, i)
|
||||
if err != nil || alpha > 2 || alpha != 0 && result.alpha != 0 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF extra samples")
|
||||
}
|
||||
if alpha != 0 {
|
||||
result.alpha, result.offsets[colors] = alpha, int(i)
|
||||
}
|
||||
}
|
||||
} else if entries[338] != nil {
|
||||
return nil, nil, fmt.Errorf("unexpected TIFF extra samples")
|
||||
@@ -183,16 +266,16 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
}
|
||||
blocks = (uint64(layout.height)-1)/uint64(rows) + 1
|
||||
}
|
||||
if blocks > math.MaxUint32/uint64(samples) {
|
||||
if blocks > math.MaxUint32/uint64(planes) {
|
||||
return nil, nil, fmt.Errorf("too many TIFF plane blocks")
|
||||
}
|
||||
for _, tag := range []uint16{locations, lengths} {
|
||||
entry := entries[tag]
|
||||
if entry == nil || uint64(order.Uint32(entry[4:])) != blocks*uint64(samples) || tag == 324 && order.Uint16(entry[2:]) != 4 {
|
||||
if entry == nil || uint64(order.Uint32(entry[4:])) != blocks*uint64(planes) || tag == 324 && order.Uint16(entry[2:]) != 4 {
|
||||
return nil, nil, fmt.Errorf("invalid TIFF plane block count")
|
||||
}
|
||||
}
|
||||
for i := uint32(0); i < uint32(blocks)*samples; i++ {
|
||||
for i := uint32(0); i < uint32(blocks)*planes; i++ {
|
||||
if ctx != nil {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
@@ -213,7 +296,21 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
}
|
||||
}
|
||||
var directories [][]byte
|
||||
var firstDepth uint32
|
||||
for plane := uint32(0); plane < samples; plane++ {
|
||||
if ctx != nil {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
slot := plane
|
||||
visible := plane < colors || result.alpha != 0 && plane == colors+uint32(result.offsets[colors])
|
||||
if !visible && result.stride == 0 {
|
||||
continue
|
||||
}
|
||||
if plane >= colors {
|
||||
slot = colors
|
||||
}
|
||||
depth := uint32(1)
|
||||
if entry := entries[258]; entry != nil {
|
||||
depth, err = tiffPlaneValue(data, order, entry, plane)
|
||||
@@ -221,26 +318,80 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
if err != nil || depth != 1 && depth != 2 && depth != 4 && depth != 8 && depth != 16 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF plane depth")
|
||||
}
|
||||
if plane == 0 {
|
||||
firstDepth = depth
|
||||
} else if result.stride > 1 && depth != firstDepth {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF mixed interleaved sample depths")
|
||||
}
|
||||
if !visible {
|
||||
continue
|
||||
}
|
||||
result.depth = max(result.depth, depth)
|
||||
if depth < 8 && predictor != 1 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF packed plane predictor")
|
||||
if result.cmyk && plane < 4 {
|
||||
low, high := uint32(0), uint32(1<<depth-1)
|
||||
if entry := entries[336]; entry != nil {
|
||||
n := order.Uint32(entry[4:])
|
||||
if kind := order.Uint16(entry[2:]); kind != 1 && kind != 3 || n != 2 && n != 2*samples {
|
||||
return nil, nil, fmt.Errorf("invalid TIFF dot range")
|
||||
}
|
||||
i := plane * 2
|
||||
if n == 2 {
|
||||
i = 0
|
||||
}
|
||||
if order.Uint16(entry[2:]) == 1 {
|
||||
values := entry[8:]
|
||||
if n > 4 {
|
||||
at := uint64(order.Uint32(values))
|
||||
if at+uint64(n) > uint64(len(data)) {
|
||||
return nil, nil, fmt.Errorf("truncated TIFF dot range")
|
||||
}
|
||||
values = data[at : at+uint64(n)]
|
||||
}
|
||||
low, high = uint32(values[i]), uint32(values[i+1])
|
||||
} else {
|
||||
low, err = tiffPlaneValue(data, order, entry, i)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
high, err = tiffPlaneValue(data, order, entry, i+1)
|
||||
}
|
||||
if err != nil || low == high || low > 1<<depth-1 || high > 1<<depth-1 {
|
||||
return nil, nil, fmt.Errorf("invalid TIFF dot range")
|
||||
}
|
||||
}
|
||||
result.dotRange[plane] = [2]uint16{uint16(low * 65535 / (1<<depth - 1)), uint16(high * 65535 / (1<<depth - 1))}
|
||||
}
|
||||
factor := uint64(max(result.stride, 1))
|
||||
if depth == 1 && predictor == 2 {
|
||||
result.packed[slot] = 1
|
||||
}
|
||||
if depth == 2 || depth == 4 {
|
||||
result.packed[plane] = depth
|
||||
if uint64(layout.width)*uint64(depth) > uint64(math.MaxInt)/uint64(layout.height) || uint64(layout.width)*uint64(depth) > math.MaxUint32 || uint64(width)*uint64(depth) > math.MaxUint32 {
|
||||
return nil, nil, fmt.Errorf("TIFF plane dimensions exceed format limit")
|
||||
}
|
||||
result.packed[slot] = depth
|
||||
factor *= uint64(depth)
|
||||
if compression == 2 || compression == 3 || compression == 4 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF packed plane compression")
|
||||
}
|
||||
}
|
||||
pixelBytes := factor
|
||||
if depth > 8 {
|
||||
pixelBytes *= 2
|
||||
}
|
||||
if uint64(layout.width)*pixelBytes > uint64(math.MaxInt)/uint64(layout.height) || uint64(layout.width)*factor > math.MaxUint32 || uint64(width)*factor > math.MaxUint32 {
|
||||
return nil, nil, fmt.Errorf("TIFF plane dimensions exceed format limit")
|
||||
}
|
||||
if predictor == 2 && (result.stride > 1 || depth < 8) {
|
||||
result.predictorWidth = layout.width
|
||||
if width != 0 {
|
||||
result.predictorWidth = int(width)
|
||||
}
|
||||
}
|
||||
if entry := entries[339]; entry != nil {
|
||||
format, err := tiffPlaneValue(data, order, entry, plane)
|
||||
if err != nil || format != 1 {
|
||||
return nil, nil, fmt.Errorf("unsupported TIFF sample format")
|
||||
}
|
||||
}
|
||||
if photo == 3 {
|
||||
if photo == 3 && plane == 0 {
|
||||
entry := entries[320]
|
||||
if depth > 8 || entry == nil || order.Uint16(entry[2:]) != 3 || order.Uint32(entry[4:]) != 3<<depth {
|
||||
return nil, nil, fmt.Errorf("invalid TIFF color map")
|
||||
@@ -262,6 +413,9 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
result.palette[i] = color.NRGBA64{R: components[0], G: components[1], B: components[2], A: 65535}
|
||||
}
|
||||
}
|
||||
if result.stride > 1 && plane != 0 {
|
||||
continue
|
||||
}
|
||||
directory := make([]byte, 2, 6+count*12)
|
||||
for i := range count {
|
||||
if ctx != nil {
|
||||
@@ -271,33 +425,34 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
}
|
||||
entry := bytes.Clone(data[offset+2+i*12 : offset+14+i*12])
|
||||
tag := order.Uint16(entry)
|
||||
if tag == 338 || tag == 320 {
|
||||
if tag == 338 || tag == 320 || tag >= 332 && tag <= 337 || tag == 34675 {
|
||||
continue
|
||||
}
|
||||
scalar := uint32(0)
|
||||
switch tag {
|
||||
case 258:
|
||||
scalar = depth
|
||||
if result.packed[plane] != 0 {
|
||||
if result.packed[slot] != 0 {
|
||||
scalar = 1
|
||||
}
|
||||
case 256, 322:
|
||||
if result.packed[plane] != 0 {
|
||||
if factor != 1 {
|
||||
original, err := tiffPlaneValue(data, order, entry, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
order.PutUint16(entry[2:], 4)
|
||||
order.PutUint32(entry[4:], 1)
|
||||
order.PutUint32(entry[8:], original*depth)
|
||||
order.PutUint32(entry[8:], original*uint32(factor))
|
||||
}
|
||||
case 262:
|
||||
scalar = 1
|
||||
if result.gray && plane == 0 && photo == 0 {
|
||||
scalar = 0
|
||||
}
|
||||
case 277, 284, 339:
|
||||
scalar = 1
|
||||
case 317:
|
||||
if predictor == 2 && (result.stride > 1 || depth < 8) {
|
||||
order.PutUint16(entry[8:], 1)
|
||||
}
|
||||
case locations, lengths:
|
||||
entry, err = tiffPlaneArray(data, order, entry, uint32(blocks)*plane, uint32(blocks))
|
||||
if err != nil {
|
||||
@@ -318,6 +473,64 @@ func tiffPlaneDirectories(ctx context.Context, data []byte, layout tiffImageLayo
|
||||
return result, directories, nil
|
||||
}
|
||||
|
||||
// restoreTIFFPrediction 在解码缓冲内恢复水平差分,各行及瓦片独立计算
|
||||
// 入参: ctx 取消上下文,可为nil, pixels 单通道解码视图, stride 每像素分量数, width 原始条带或瓦片宽度, packed 展开为一位采样的原位深,零表示未展开
|
||||
// 返回: error 图像类型或取消错误
|
||||
func restoreTIFFPrediction(ctx context.Context, pixels image.Image, stride, width int, packed uint32) error {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
var data []byte
|
||||
var pitch, step int
|
||||
switch pixels := pixels.(type) {
|
||||
case *image.Gray:
|
||||
data, pitch, step = pixels.Pix, pixels.Stride, max(int(packed), 1)
|
||||
case *image.Gray16:
|
||||
data, pitch, step = pixels.Pix, pixels.Stride, 2
|
||||
default:
|
||||
return fmt.Errorf("unsupported TIFF predictor image")
|
||||
}
|
||||
bounds := pixels.Bounds()
|
||||
columns := bounds.Dx() / max(int(packed), 1)
|
||||
block := width * stride
|
||||
for y := range bounds.Dy() {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
row := data[y*pitch : y*pitch+columns*step]
|
||||
for start := 0; start < columns; {
|
||||
end := start + min(block, columns-start)
|
||||
for x := start + stride; x < end; {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
limit := x + min(4096, end-x)
|
||||
for ; x < limit; x++ {
|
||||
at, previous := x*step, (x-stride)*step
|
||||
if packed != 0 {
|
||||
var current, prior uint16
|
||||
for bit := range step {
|
||||
current = current<<1 | uint16(row[at+bit]>>7)
|
||||
prior = prior<<1 | uint16(row[previous+bit]>>7)
|
||||
}
|
||||
value := current + prior
|
||||
for bit := range step {
|
||||
row[at+bit] = byte(value>>uint(step-1-bit)&1) * 255
|
||||
}
|
||||
} else if step == 2 {
|
||||
value := binary.BigEndian.Uint16(row[at:]) + binary.BigEndian.Uint16(row[previous:])
|
||||
binary.BigEndian.PutUint16(row[at:], value)
|
||||
} else {
|
||||
row[at] += row[previous]
|
||||
}
|
||||
}
|
||||
}
|
||||
start = end
|
||||
}
|
||||
}
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// tiffPlaneValue 读取SHORT或LONG数组中的一项,校验完整数组范围
|
||||
// 入参: data 原编码, order 字节序, entry 目录项, index 元素序号
|
||||
// 返回: uint32 元素值, error 类型或越界错误
|
||||
@@ -452,37 +665,94 @@ func (p *planarTIFFImage) components(x, y int) [4]uint16 {
|
||||
if !image.Pt(x, y).In(p.bounds) {
|
||||
return [4]uint16{}
|
||||
}
|
||||
values := [4]uint16{0, 0, 0, 65535}
|
||||
for i, plane := range p.planes {
|
||||
var value uint16
|
||||
switch plane := plane.(type) {
|
||||
case *image.Gray:
|
||||
if bits := p.packed[i]; bits != 0 {
|
||||
for bit := range int(bits) {
|
||||
value = value<<1 | uint16(plane.GrayAt(x*int(bits)+bit, y).Y>>7)
|
||||
}
|
||||
value *= 65535 / uint16(1<<bits-1)
|
||||
} else {
|
||||
value = uint16(plane.GrayAt(x, y).Y) * 257
|
||||
if p.cmyk {
|
||||
ink := p.inksAt(x, y)
|
||||
maximum := uint32(65535)
|
||||
if p.alpha == 1 {
|
||||
maximum = uint32(ink[4])
|
||||
if maximum == 0 {
|
||||
return [4]uint16{}
|
||||
}
|
||||
case *image.Gray16:
|
||||
value = plane.Gray16At(x, y).Y
|
||||
default:
|
||||
v, _, _, _ := plane.At(x, y).RGBA()
|
||||
value = uint16(v)
|
||||
}
|
||||
if len(p.palette) != 0 {
|
||||
black := maximum - min(uint32(ink[3]), maximum)
|
||||
var values [4]uint16
|
||||
for i := range 3 {
|
||||
values[i] = uint16((maximum - min(uint32(ink[i]), maximum)) * black / maximum)
|
||||
}
|
||||
values[3] = ink[4]
|
||||
return values
|
||||
}
|
||||
values := [4]uint16{0, 0, 0, 65535}
|
||||
count := 3
|
||||
if p.gray || len(p.palette) != 0 {
|
||||
count = 1
|
||||
}
|
||||
if p.alpha != 0 {
|
||||
count++
|
||||
}
|
||||
for i := range count {
|
||||
value := p.sampleAt(i, x, y)
|
||||
if i == 0 && p.invert {
|
||||
value = 65535 - value
|
||||
}
|
||||
if len(p.palette) != 0 && i == 0 {
|
||||
c := p.palette[uint32(value)*uint32(len(p.palette)-1)/65535]
|
||||
return [4]uint16{c.R, c.G, c.B, c.A}
|
||||
}
|
||||
if p.gray && i == 0 {
|
||||
values = [4]uint16{c.R, c.G, c.B, c.A}
|
||||
} else if p.gray && i == 0 {
|
||||
values[0], values[1], values[2] = value, value, value
|
||||
} else if p.gray {
|
||||
} else if p.gray || len(p.palette) != 0 {
|
||||
values[3] = value
|
||||
} else {
|
||||
values[i] = value
|
||||
}
|
||||
}
|
||||
if p.alpha == 1 && values[3] == 0 {
|
||||
return [4]uint16{}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// sampleAt 读取一个分量并扩展为十六位,交错布局复用同一像素缓冲
|
||||
// 入参: i 分量序号, x 横坐标, y 纵坐标
|
||||
// 返回: uint16 归一化采样值
|
||||
func (p *planarTIFFImage) sampleAt(i, x, y int) uint16 {
|
||||
plane, sx := p.planes[0], x
|
||||
if p.stride > 1 {
|
||||
sx = x*p.stride + i + p.offsets[i]
|
||||
} else {
|
||||
plane = p.planes[i]
|
||||
}
|
||||
switch plane := plane.(type) {
|
||||
case *image.Gray:
|
||||
if bits := p.packed[i]; bits != 0 {
|
||||
var value uint16
|
||||
for bit := range int(bits) {
|
||||
value = value<<1 | uint16(plane.GrayAt(sx*int(bits)+bit, y).Y>>7)
|
||||
}
|
||||
return value * (65535 / uint16(1<<bits-1))
|
||||
}
|
||||
return uint16(plane.GrayAt(sx, y).Y) * 257
|
||||
case *image.Gray16:
|
||||
return plane.Gray16At(sx, y).Y
|
||||
default:
|
||||
v, _, _, _ := plane.At(sx, y).RGBA()
|
||||
return uint16(v)
|
||||
}
|
||||
}
|
||||
|
||||
// inksAt 按网点范围读取CMYK覆盖量,范围外采样按端点截断,透明度独立归一化
|
||||
// 入参: x 横坐标, y 纵坐标
|
||||
// 返回: [5]uint16 青、品红、黄、黑及透明度
|
||||
func (p *planarTIFFImage) inksAt(x, y int) [5]uint16 {
|
||||
values := [5]uint16{0, 0, 0, 0, 65535}
|
||||
for i := range 4 {
|
||||
value := int64(p.sampleAt(i, x, y))
|
||||
low, high := int64(p.dotRange[i][0]), int64(p.dotRange[i][1])
|
||||
values[i] = uint16(max(0, min(65535, (value-low)*65535/(high-low))))
|
||||
}
|
||||
if p.alpha != 0 {
|
||||
values[4] = p.sampleAt(4, x, y)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user