mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 14:53:44 +08:00
1 parent
ee3fd02a0a
commit
64f9ec4241
87 files changed
+2693
-2289
No files matched your search
@@ -0,0 +1,189 @@
|
||||
// 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 image
|
||||
|
||||
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 TIFFLayout) ([][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
|
||||
}
|
||||
Reference in new issue
Block a user