Files
ofdgo/ofdgo_image_tiff_bits.go
T
2026-10-11 18:45:54 +08:00

190 lines
6.2 KiB
Go

// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ofdgo
import (
"cmp"
"context"
"encoding/binary"
"fmt"
"image"
"io"
"math/bits"
"slices"
"sort"
"golang.org/x/image/tiff"
)
// tiffBitOrderInput 仅在读取像素块时反转字节内位序,保留目录和原始数据
type tiffBitOrderInput struct {
source io.ReaderAt
ranges [][2]int64
position int64
}
// tiffBitOrderRanges 收集低位优先的未压缩一位图像块,压缩传真位序由解码器处理
// 入参: ctx 取消上下文,可为nil, data TIFF编码, layout 已校验的主图布局
// 返回: [][2]int64 按偏移排序且不重叠的字节区间, error 字段或布局错误
func tiffBitOrderRanges(ctx context.Context, data []byte, layout tiffImageLayout) ([][2]int64, error) {
if layout.fillOrder != 2 {
return nil, nil
}
var order binary.ByteOrder = binary.LittleEndian
if data[0] == 'M' {
order = binary.BigEndian
}
offset := int(layout.offset)
count := int(order.Uint16(data[offset:]))
entries := make(map[uint16][]byte, count)
for i := range count {
entry := data[offset+2+i*12 : offset+14+i*12]
entries[order.Uint16(entry)] = entry
}
compression := entries[259]
method := uint16(1)
if compression != nil {
if order.Uint16(compression[2:]) != 3 || order.Uint32(compression[4:]) != 1 {
return nil, fmt.Errorf("invalid TIFF compression field")
}
method = order.Uint16(compression[8:])
}
if method == 3 || method == 4 {
return nil, nil
}
if method != 1 {
return nil, fmt.Errorf("unsupported TIFF fill order for compression %d", method)
}
if entry := entries[258]; entry != nil {
if order.Uint16(entry[2:]) != 3 || order.Uint32(entry[4:]) != layout.samples {
return nil, fmt.Errorf("invalid TIFF sample depths")
}
for i := uint32(0); i < layout.samples; i++ {
depth, err := tiffPlaneValue(data, order, entry, i)
if err != nil || depth != 1 {
return nil, fmt.Errorf("unsupported TIFF fill order for sample depth")
}
}
}
locations, lengths := entries[273], entries[279]
value := func(tag uint16, fallback uint32) (uint32, error) {
entry := entries[tag]
if entry == nil {
return fallback, nil
}
if order.Uint32(entry[4:]) != 1 {
return 0, fmt.Errorf("invalid TIFF scalar field %d", tag)
}
return tiffPlaneValue(data, order, entry, 0)
}
rows, err := value(278, ^uint32(0))
if err != nil || rows == 0 {
return nil, fmt.Errorf("invalid TIFF rows per strip")
}
expected := (uint64(layout.height)-1)/uint64(rows) + 1
if entries[322] != nil {
locations, lengths = entries[324], entries[325]
width, err := value(322, 0)
if err != nil || width == 0 || width%16 != 0 {
return nil, fmt.Errorf("invalid TIFF tile width")
}
height, err := value(323, 0)
if err != nil || height == 0 || height%16 != 0 {
return nil, fmt.Errorf("invalid TIFF tile height")
}
expected = ((uint64(layout.width)-1)/uint64(width) + 1) * ((uint64(layout.height)-1)/uint64(height) + 1)
if locations != nil && order.Uint16(locations[2:]) != 4 {
return nil, fmt.Errorf("invalid TIFF tile offsets")
}
}
if layout.planar {
if expected > uint64(^uint32(0))/uint64(layout.samples) {
return nil, fmt.Errorf("too many TIFF image blocks")
}
expected *= uint64(layout.samples)
}
if locations == nil || lengths == nil {
return nil, fmt.Errorf("missing TIFF block arrays")
}
blocks := order.Uint32(locations[4:])
if blocks == 0 || uint64(blocks) != expected || order.Uint32(lengths[4:]) != blocks {
return nil, fmt.Errorf("invalid TIFF block count")
}
var ranges [][2]int64
for i := uint32(0); i < blocks; i++ {
if ctx != nil {
if err := ctx.Err(); err != nil {
return nil, err
}
}
at, err := tiffPlaneValue(data, order, locations, i)
if err != nil {
return nil, err
}
size, err := tiffPlaneValue(data, order, lengths, i)
if err != nil || size == 0 || uint64(at)+uint64(size) > uint64(len(data)) {
return nil, fmt.Errorf("truncated TIFF image block")
}
ranges = append(ranges, [2]int64{int64(at), int64(at) + int64(size)})
}
slices.SortFunc(ranges, func(a, b [2]int64) int { return cmp.Compare(a[0], b[0]) })
merged := ranges[:0]
for _, span := range ranges {
if len(merged) != 0 && span[0] <= merged[len(merged)-1][1] {
merged[len(merged)-1][1] = max(merged[len(merged)-1][1], span[1])
} else {
merged = append(merged, span)
}
}
return merged, nil
}
// decodeTIFFBitOrder 使用只读视图校正位序,不分配整幅编码的副本
// 入参: input 原始解码视图, ranges 需要反转位序的像素区间
// 返回: image.Image 解码图像, error 解码错误
func decodeTIFFBitOrder(input interface {
io.Reader
io.ReaderAt
}, ranges [][2]int64) (image.Image, error) {
if len(ranges) == 0 {
return tiff.Decode(input)
}
return tiff.Decode(&tiffBitOrderInput{source: input, ranges: ranges})
}
// Read 顺序读取并校正像素字节的位序
// 入参: data 接收缓冲
// 返回: int 读取字节数, error 读取错误
func (r *tiffBitOrderInput) Read(data []byte) (int, error) {
n, err := r.ReadAt(data, r.position)
r.position += int64(n)
return n, err
}
// ReadAt 按文件偏移读取,只反转实际读取到的像素字节
// 入参: data 接收缓冲, offset 文件偏移
// 返回: int 读取字节数, error 读取错误
func (r *tiffBitOrderInput) ReadAt(data []byte, offset int64) (int, error) {
n, err := r.source.ReadAt(data, offset)
index := sort.Search(len(r.ranges), func(i int) bool { return r.ranges[i][1] > offset })
for ; index < len(r.ranges) && r.ranges[index][0] < offset+int64(n); index++ {
span := r.ranges[index]
for i := max(int64(0), span[0]-offset); i < min(int64(n), span[1]-offset); i++ {
data[i] = bits.Reverse8(data[i])
}
}
return n, err
}