summaryrefslogtreecommitdiff
path: root/graphics/jpeg/files/patch-rdjpgcom.c
blob: db6bd23c249f57473c29f0b02b86c406352492ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
--- rdjpgcom.c.orig	Sun Oct 12 00:41:04 1997
+++ rdjpgcom.c	Thu Mar 18 06:37:23 2004
@@ -14,6 +14,7 @@
 #define JPEG_CJPEG_DJPEG	/* to get the command-line config symbols */
 #include "jinclude.h"		/* get auto-config symbols, <stdio.h> */
 
+#include <locale.h>		/* to declare setlocale() */
 #include <ctype.h>		/* to declare isupper(), tolower() */
 #ifdef USE_SETMODE
 #include <fcntl.h>		/* to declare setmode()'s parameter macros */
@@ -120,6 +121,7 @@
 #define M_EOI   0xD9		/* End Of Image (end of datastream) */
 #define M_SOS   0xDA		/* Start Of Scan (begins compressed data) */
 #define M_APP0	0xE0		/* Application-specific marker, type N */
+#define M_APP1  0xE1		/* Typically EXIF marker */
 #define M_APP12	0xEC		/* (we don't bother to list all 16 APPn's) */
 #define M_COM   0xFE		/* COMment */
 
@@ -210,6 +212,175 @@
   }
 }
 
+/*
+ * Helper routine to skip the given number of bytes.
+ */
+
+static void
+skip_n (unsigned int length)
+{
+  while (length > 0) {
+    (void) read_1_byte();
+    length--;
+  }
+}
+
+/*
+ * Parses an APP1 marker looking for EXIF data. If EXIF, the orientation is
+ * reported to stdout.
+ */
+
+static void
+process_APP1 (void)
+{
+  unsigned int length, i;
+  int is_motorola; /* byte order indicator */
+  unsigned int offset, number_of_tags, tagnum;
+  int orientation;
+  char *ostr;
+  /* This 64K buffer would probably be best if allocated dynamically, but it's
+   * the only one on this program so it's really not that
+   * important. Allocating on the stack is not an option, as 64K might be too
+   * big for some (crippled) platforms. */
+  static unsigned char exif_data[65536L];
+
+  /* Get the marker parameter length count */
+  length = read_2_bytes();
+  /* Length includes itself, so must be at least 2 */
+  if (length < 2)
+    ERREXIT("Erroneous JPEG marker length");
+  length -= 2;
+
+  /* We only care if APP1 is really an EXIF marker. Minimum length is 6 for
+   * signature plus 12 for an IFD. */
+  if (length < 18) {
+    skip_n(length);
+    return;
+  }
+
+  /* Check for actual EXIF marker */
+  for (i=0; i < 6; i++)
+    exif_data[i] = (unsigned char) read_1_byte();
+  length -= 6;
+  if (exif_data[0] != 0x45 ||
+      exif_data[1] != 0x78 ||
+      exif_data[2] != 0x69 ||
+      exif_data[3] != 0x66 ||
+      exif_data[4] != 0 ||
+      exif_data[5] != 0) {
+    skip_n(length);
+    return;
+  }
+
+  /* Read all EXIF body */
+  for (i=0; i < length; i++)
+    exif_data[i] = (unsigned char) read_1_byte();
+
+  /* Discover byte order */
+  if (exif_data[0] == 0x49 && exif_data[1] == 0x49)
+    is_motorola = 0;
+  else if (exif_data[0] == 0x4D && exif_data[1] == 0x4D)
+    is_motorola = 1;
+  else
+    return;
+
+  /* Check Tag Mark */
+  if (is_motorola) {
+    if (exif_data[2] != 0) return;
+    if (exif_data[3] != 0x2A) return;
+  } else {
+    if (exif_data[3] != 0) return;
+    if (exif_data[2] != 0x2A) return;
+  }
+
+  /* Get first IFD offset (offset to IFD0) */
+  if (is_motorola) {
+    if (exif_data[4] != 0) return;
+    if (exif_data[5] != 0) return;
+    offset = exif_data[6];
+    offset <<= 8;
+    offset += exif_data[7];
+  } else {
+    if (exif_data[7] != 0) return;
+    if (exif_data[6] != 0) return;
+    offset = exif_data[5];
+    offset <<= 8;
+    offset += exif_data[4];
+  }
+  if (offset > length - 2) return; /* check end of data segment */
+
+  /* Get the number of directory entries contained in this IFD */
+  if (is_motorola) {
+    number_of_tags = exif_data[offset];
+    number_of_tags <<= 8;
+    number_of_tags += exif_data[offset+1];
+  } else {
+    number_of_tags = exif_data[offset+1];
+    number_of_tags <<= 8;
+    number_of_tags += exif_data[offset];
+  }
+  if (number_of_tags == 0) return;
+  offset += 2;
+
+  /* Search for Orientation Tag in IFD0 */
+  for (;;) {
+    if (offset > length - 12) return; /* check end of data segment */
+    /* Get Tag number */
+    if (is_motorola) {
+      tagnum = exif_data[offset];
+      tagnum <<= 8;
+      tagnum += exif_data[offset+1];
+    } else {
+      tagnum = exif_data[offset+1];
+      tagnum <<= 8;
+      tagnum += exif_data[offset];
+    }
+    if (tagnum == 0x0112) break; /* found Orientation Tag */
+    if (--number_of_tags == 0) return;
+    offset += 12;
+  }
+
+  /* Get the Orientation value */
+  if (is_motorola) {
+    if (exif_data[offset+8] != 0) return;
+    orientation = exif_data[offset+9];
+  } else {
+    if (exif_data[offset+9] != 0) return;
+    orientation = exif_data[offset+8];
+  }
+  if (orientation == 0 || orientation > 8) return;
+
+  /* Print the orientation (position of the 0th row - 0th column) */
+  switch (orientation) {
+  case 1:
+    ostr = "top-left";
+    break;
+  case 2:
+    ostr = "top-right";
+    break;
+  case 3:
+    ostr = "bottom-right";
+    break;
+  case 4:
+    ostr = "bottom-left";
+    break;
+  case 5:
+    ostr = "left-top";
+    break;
+  case 6:
+    ostr = "right-top";
+    break;
+  case 7:
+    ostr = "right-bottom";
+    break;
+  case 8:
+    ostr = "left-bottom";
+    break;
+  default:
+    return;
+  }
+  printf("EXIF orientation: %s\n",ostr);
+}
 
 /*
  * Process a COM marker.
@@ -231,6 +402,7 @@
     ERREXIT("Erroneous JPEG marker length");
   length -= 2;
 
+  setlocale(LC_ALL, "");
   while (length > 0) {
     ch = read_1_byte();
     /* Emit the character in a readable form.
@@ -363,6 +535,15 @@
 
     case M_COM:
       process_COM();
+      break;
+
+    case M_APP1:
+      /* APP1 is usually the EXIF marker used by digital cameras, attempt to
+       * process it to give some useful info. */
+      if (verbose) {
+        process_APP1();
+      } else
+        skip_variable();
       break;
 
     case M_APP12: