// fileread_heic_test.go -- HEIC/AVIF 解码与方向校正测试. // // 测试数据位于 /tmp/exif_test/(前一次 session 从网上下载的真实文件): // - orientation6.heic (675 bytes):AVIF 格式,EXIF orientation=6(顺时针90°), // 物理尺寸 320×240,旋转后应为 240×320 // - test.heic (718KB):真实 HEIC 图片,无特殊旋转 // - exif.heic, sample_exif.heic:带 EXIF 的 HEIC 样本 //go:build libheif && cgo package builtin import ( "bytes" "image" "os" "runtime" "testing" ) // heicTestFile 读取测试文件,不存在则 skip. func heicTestFile(t *testing.T, name string) []byte { t.Helper() if runtime.GOOS != "linux" { t.Skip("CGO libheif 测试仅在 Linux 运行") } data, err := os.ReadFile("/tmp/exif_test/" + name) if err != nil { t.Skipf("测试文件不存在(%s): %v", name, err) } return data } // ───────────────────────────────────────────────────────────────────── // 核心:EXIF Orientation 读取与旋转验证 // ───────────────────────────────────────────────────────────────────── // TestHEIC_ExifOrientation6_Rotated 是关键回归测试: // orientation6.heic 是 AVIF 格式,EXIF orientation=6(顺时针90°旋转). // 物理像素 320×240(横图),旋转后应得到 240×320(竖图). // // 这个测试验证了两阶段方向修正的核心逻辑: // 1. libheif 解码 AVIF 时不自动应用 EXIF orientation // 2. 我们通过比较 ispe(物理)与 logical 尺寸来检测 libheif 是否已旋转 // 3. 若未旋转,读取 EXIF metadata block 并手动调用 applyOrientation func TestHEIC_ExifOrientation6_Rotated(t *testing.T) { data := heicTestFile(t, "orientation6.heic") t.Logf("文件大小: %d bytes", len(data)) // 验证格式检测 mt := detectImageMediaType(data) if mt != "image/heic" { t.Errorf("格式检测期望 image/heic,got %s", mt) } // 解码 img, err := decodeHEIC(data) if err != nil { t.Fatalf("decodeHEIC 失败: %v", err) } b := img.Bounds() t.Logf("解码后尺寸: %dx%d", b.Max.X, b.Max.Y) // orientation=6 (90°CW):物理 320×240 → 旋转后 240×320 // 宽应 < 高(竖图) if b.Max.X != 240 || b.Max.Y != 320 { t.Errorf("方向校正失败:期望 240×320(竖图),实际 %dx%d", b.Max.X, b.Max.Y) if b.Max.X == 320 && b.Max.Y == 240 { t.Error("→ libheif 未应用 EXIF orientation=6,两阶段检测逻辑有问题") } } else { t.Logf("✓ EXIF orientation=6 校正正确:320×240 → 240×320") } } // TestHEIC_ExifOrientation6_JPEGOutput 验证 correctHEICToJPEG 完整流程: // orientation6.heic → JPEG,输出应为竖图(240×320). func TestHEIC_ExifOrientation6_JPEGOutput(t *testing.T) { data := heicTestFile(t, "orientation6.heic") result, outMT := correctHEICToJPEG(data) if outMT != "image/jpeg" { t.Fatalf("期望 image/jpeg,got %s(可能 fail-open)", outMT) } img, _, err := image.Decode(bytes.NewReader(result)) if err != nil { t.Fatalf("输出 JPEG 无法解码: %v", err) } b := img.Bounds() t.Logf("输出 JPEG 尺寸: %dx%d", b.Max.X, b.Max.Y) if b.Max.X >= b.Max.Y { t.Errorf("输出 JPEG 是横图(%dx%d),orientation=6 校正后应为竖图 240×320", b.Max.X, b.Max.Y) } else { t.Logf("✓ JPEG 输出为竖图,方向校正正确") } } // ───────────────────────────────────────────────────────────────────── // 正常文件(无特殊旋转) // ───────────────────────────────────────────────────────────────────── // TestHEIC_NormalFile_Decode 用真实 HEIC(无特殊旋转)验证基础解码路径. func TestHEIC_NormalFile_Decode(t *testing.T) { data := heicTestFile(t, "test.heic") t.Logf("文件大小: %d bytes", len(data)) result, outMT := correctHEICToJPEG(data) if outMT != "image/jpeg" { t.Fatalf("期望 image/jpeg,got %s", outMT) } img, _, err := image.Decode(bytes.NewReader(result)) if err != nil { t.Fatalf("输出 JPEG 无法解码: %v", err) } b := img.Bounds() t.Logf("✓ test.heic → JPEG %dx%d", b.Max.X, b.Max.Y) if b.Max.X == 0 || b.Max.Y == 0 { t.Error("解码后尺寸为零") } }