commit db524fb
delthas
·
2024-07-29 13:10:29 +0000 UTC
parent 34095f1
Add image EXIF rotation support Forgot to git add :-)
1 files changed,
+153,
-0
+153,
-0
1@@ -0,0 +1,153 @@
2+package ui
3+
4+import (
5+ "bytes"
6+ "encoding/binary"
7+ "image"
8+ "io"
9+
10+ "github.com/disintegration/imaging"
11+)
12+
13+func exifOrientation(r io.Reader) int {
14+ // Inspired by: https://github.com/disintegration/imageorient
15+
16+ const (
17+ markerSOI = 0xffd8
18+ markerAPP1 = 0xffe1
19+ exifHeader = 0x45786966
20+ byteOrderBE = 0x4d4d
21+ byteOrderLE = 0x4949
22+ orientationTag = 0x0112
23+ )
24+
25+ // Check if JPEG SOI marker is present.
26+ var soi uint16
27+ if err := binary.Read(r, binary.BigEndian, &soi); err != nil {
28+ return 0
29+ }
30+ if soi != markerSOI {
31+ return 0 // Missing JPEG SOI marker.
32+ }
33+
34+ // Find JPEG APP1 marker.
35+ for {
36+ var marker, size uint16
37+ if err := binary.Read(r, binary.BigEndian, &marker); err != nil {
38+ return 0
39+ }
40+ if err := binary.Read(r, binary.BigEndian, &size); err != nil {
41+ return 0
42+ }
43+ if marker>>8 != 0xff {
44+ return 0 // Invalid JPEG marker.
45+ }
46+ if marker == markerAPP1 {
47+ break
48+ }
49+ if size < 2 {
50+ return 0 // Invalid block size.
51+ }
52+ if _, err := io.CopyN(io.Discard, r, int64(size-2)); err != nil {
53+ return 0
54+ }
55+ }
56+
57+ // Check if EXIF header is present.
58+ var header uint32
59+ if err := binary.Read(r, binary.BigEndian, &header); err != nil {
60+ return 0
61+ }
62+ if header != exifHeader {
63+ return 0
64+ }
65+ if _, err := io.CopyN(io.Discard, r, 2); err != nil {
66+ return 0
67+ }
68+
69+ // Read byte order information.
70+ var (
71+ byteOrderTag uint16
72+ byteOrder binary.ByteOrder
73+ )
74+ if err := binary.Read(r, binary.BigEndian, &byteOrderTag); err != nil {
75+ return 0
76+ }
77+ switch byteOrderTag {
78+ case byteOrderBE:
79+ byteOrder = binary.BigEndian
80+ case byteOrderLE:
81+ byteOrder = binary.LittleEndian
82+ default:
83+ return 0 // Invalid byte order flag.
84+ }
85+ if _, err := io.CopyN(io.Discard, r, 2); err != nil {
86+ return 0
87+ }
88+
89+ // Skip the EXIF offset.
90+ var offset uint32
91+ if err := binary.Read(r, byteOrder, &offset); err != nil {
92+ return 0
93+ }
94+ if offset < 8 {
95+ return 0 // Invalid offset value.
96+ }
97+ if _, err := io.CopyN(io.Discard, r, int64(offset-8)); err != nil {
98+ return 0
99+ }
100+
101+ // Read the number of tags.
102+ var numTags uint16
103+ if err := binary.Read(r, byteOrder, &numTags); err != nil {
104+ return 0
105+ }
106+
107+ // Find the orientation tag.
108+ for i := 0; i < int(numTags); i++ {
109+ var tag uint16
110+ if err := binary.Read(r, byteOrder, &tag); err != nil {
111+ return 0
112+ }
113+ if tag != orientationTag {
114+ if _, err := io.CopyN(io.Discard, r, 10); err != nil {
115+ return 0
116+ }
117+ continue
118+ }
119+ if _, err := io.CopyN(io.Discard, r, 6); err != nil {
120+ return 0
121+ }
122+ var val uint16
123+ if err := binary.Read(r, byteOrder, &val); err != nil {
124+ return 0
125+ }
126+ if val < 1 || val > 8 {
127+ return 0 // Invalid tag value.
128+ }
129+ return int(val)
130+ }
131+ return 0 // Missing orientation tag.
132+}
133+
134+func DecodeImage(r io.Reader) (image.Image, string, error) {
135+ var b bytes.Buffer
136+ tr := io.TeeReader(io.LimitReader(r, 1<<20), &b)
137+ o := exifOrientation(tr)
138+ r = io.MultiReader(&b, r)
139+
140+ img, format, err := image.Decode(r)
141+ if err != nil {
142+ return img, format, err
143+ }
144+
145+ switch o {
146+ case 3:
147+ img = imaging.Rotate180(img)
148+ case 6:
149+ img = imaging.Rotate270(img)
150+ case 8:
151+ img = imaging.Rotate90(img)
152+ }
153+ return img, format, nil
154+}