1 | /*
|
---|
2 | * Copyright 2002-2019 Drew Noakes and contributors
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | * More information about this project is available at:
|
---|
17 | *
|
---|
18 | * https://drewnoakes.com/code/exif/
|
---|
19 | * https://github.com/drewnoakes/metadata-extractor
|
---|
20 | */
|
---|
21 | package com.drew.metadata.exif.makernotes;
|
---|
22 |
|
---|
23 | import com.drew.lang.Rational;
|
---|
24 | import com.drew.lang.annotations.NotNull;
|
---|
25 | import com.drew.lang.annotations.Nullable;
|
---|
26 | import com.drew.metadata.TagDescriptor;
|
---|
27 |
|
---|
28 | import static com.drew.metadata.exif.makernotes.FujifilmMakernoteDirectory.*;
|
---|
29 |
|
---|
30 | /**
|
---|
31 | * Provides human-readable string representations of tag values stored in a {@link FujifilmMakernoteDirectory}.
|
---|
32 | * <p>
|
---|
33 | * Fujifilm added their Makernote tag from the Year 2000's models (e.g.Finepix1400,
|
---|
34 | * Finepix4700). It uses IFD format and start from ASCII character 'FUJIFILM', and next 4
|
---|
35 | * bytes (value 0x000c) points the offset to first IFD entry.
|
---|
36 | * <pre><code>
|
---|
37 | * :0000: 46 55 4A 49 46 49 4C 4D-0C 00 00 00 0F 00 00 00 :0000: FUJIFILM........
|
---|
38 | * :0010: 07 00 04 00 00 00 30 31-33 30 00 10 02 00 08 00 :0010: ......0130......
|
---|
39 | * </code></pre>
|
---|
40 | * There are two big differences to the other manufacturers.
|
---|
41 | * <ul>
|
---|
42 | * <li>Fujifilm's Exif data uses Motorola align, but Makernote ignores it and uses Intel align.</li>
|
---|
43 | * <li>
|
---|
44 | * The other manufacturer's Makernote counts the "offset to data" from the first byte of TIFF header
|
---|
45 | * (same as the other IFD), but Fujifilm counts it from the first byte of Makernote itself.
|
---|
46 | * </li>
|
---|
47 | * </ul>
|
---|
48 | *
|
---|
49 | * @author Drew Noakes https://drewnoakes.com
|
---|
50 | */
|
---|
51 | @SuppressWarnings("WeakerAccess")
|
---|
52 | public class FujifilmMakernoteDescriptor extends TagDescriptor<FujifilmMakernoteDirectory>
|
---|
53 | {
|
---|
54 | public FujifilmMakernoteDescriptor(@NotNull FujifilmMakernoteDirectory directory)
|
---|
55 | {
|
---|
56 | super(directory);
|
---|
57 | }
|
---|
58 |
|
---|
59 | @Override
|
---|
60 | @Nullable
|
---|
61 | public String getDescription(int tagType)
|
---|
62 | {
|
---|
63 | switch (tagType) {
|
---|
64 | case TAG_MAKERNOTE_VERSION:
|
---|
65 | return getMakernoteVersionDescription();
|
---|
66 | case TAG_SHARPNESS:
|
---|
67 | return getSharpnessDescription();
|
---|
68 | case TAG_WHITE_BALANCE:
|
---|
69 | return getWhiteBalanceDescription();
|
---|
70 | case TAG_COLOR_SATURATION:
|
---|
71 | return getColorSaturationDescription();
|
---|
72 | case TAG_TONE:
|
---|
73 | return getToneDescription();
|
---|
74 | case TAG_CONTRAST:
|
---|
75 | return getContrastDescription();
|
---|
76 | case TAG_NOISE_REDUCTION:
|
---|
77 | return getNoiseReductionDescription();
|
---|
78 | case TAG_HIGH_ISO_NOISE_REDUCTION:
|
---|
79 | return getHighIsoNoiseReductionDescription();
|
---|
80 | case TAG_FLASH_MODE:
|
---|
81 | return getFlashModeDescription();
|
---|
82 | case TAG_FLASH_EV:
|
---|
83 | return getFlashExposureValueDescription();
|
---|
84 | case TAG_MACRO:
|
---|
85 | return getMacroDescription();
|
---|
86 | case TAG_FOCUS_MODE:
|
---|
87 | return getFocusModeDescription();
|
---|
88 | case TAG_SLOW_SYNC:
|
---|
89 | return getSlowSyncDescription();
|
---|
90 | case TAG_PICTURE_MODE:
|
---|
91 | return getPictureModeDescription();
|
---|
92 | case TAG_EXR_AUTO:
|
---|
93 | return getExrAutoDescription();
|
---|
94 | case TAG_EXR_MODE:
|
---|
95 | return getExrModeDescription();
|
---|
96 | case TAG_AUTO_BRACKETING:
|
---|
97 | return getAutoBracketingDescription();
|
---|
98 | case TAG_FINE_PIX_COLOR:
|
---|
99 | return getFinePixColorDescription();
|
---|
100 | case TAG_BLUR_WARNING:
|
---|
101 | return getBlurWarningDescription();
|
---|
102 | case TAG_FOCUS_WARNING:
|
---|
103 | return getFocusWarningDescription();
|
---|
104 | case TAG_AUTO_EXPOSURE_WARNING:
|
---|
105 | return getAutoExposureWarningDescription();
|
---|
106 | case TAG_DYNAMIC_RANGE:
|
---|
107 | return getDynamicRangeDescription();
|
---|
108 | case TAG_FILM_MODE:
|
---|
109 | return getFilmModeDescription();
|
---|
110 | case TAG_DYNAMIC_RANGE_SETTING:
|
---|
111 | return getDynamicRangeSettingDescription();
|
---|
112 | default:
|
---|
113 | return super.getDescription(tagType);
|
---|
114 | }
|
---|
115 | }
|
---|
116 |
|
---|
117 | @Nullable
|
---|
118 | private String getMakernoteVersionDescription()
|
---|
119 | {
|
---|
120 | return getVersionBytesDescription(TAG_MAKERNOTE_VERSION, 2);
|
---|
121 | }
|
---|
122 |
|
---|
123 | @Nullable
|
---|
124 | public String getSharpnessDescription()
|
---|
125 | {
|
---|
126 | final Integer value = _directory.getInteger(TAG_SHARPNESS);
|
---|
127 | if (value == null)
|
---|
128 | return null;
|
---|
129 | switch (value) {
|
---|
130 | case 1: return "Softest";
|
---|
131 | case 2: return "Soft";
|
---|
132 | case 3: return "Normal";
|
---|
133 | case 4: return "Hard";
|
---|
134 | case 5: return "Hardest";
|
---|
135 | case 0x82: return "Medium Soft";
|
---|
136 | case 0x84: return "Medium Hard";
|
---|
137 | case 0x8000: return "Film Simulation";
|
---|
138 | case 0xFFFF: return "N/A";
|
---|
139 | default:
|
---|
140 | return "Unknown (" + value + ")";
|
---|
141 | }
|
---|
142 | }
|
---|
143 |
|
---|
144 | @Nullable
|
---|
145 | public String getWhiteBalanceDescription()
|
---|
146 | {
|
---|
147 | final Integer value = _directory.getInteger(TAG_WHITE_BALANCE);
|
---|
148 | if (value == null)
|
---|
149 | return null;
|
---|
150 | switch (value) {
|
---|
151 | case 0x000: return "Auto";
|
---|
152 | case 0x100: return "Daylight";
|
---|
153 | case 0x200: return "Cloudy";
|
---|
154 | case 0x300: return "Daylight Fluorescent";
|
---|
155 | case 0x301: return "Day White Fluorescent";
|
---|
156 | case 0x302: return "White Fluorescent";
|
---|
157 | case 0x303: return "Warm White Fluorescent";
|
---|
158 | case 0x304: return "Living Room Warm White Fluorescent";
|
---|
159 | case 0x400: return "Incandescence";
|
---|
160 | case 0x500: return "Flash";
|
---|
161 | case 0xf00: return "Custom White Balance";
|
---|
162 | case 0xf01: return "Custom White Balance 2";
|
---|
163 | case 0xf02: return "Custom White Balance 3";
|
---|
164 | case 0xf03: return "Custom White Balance 4";
|
---|
165 | case 0xf04: return "Custom White Balance 5";
|
---|
166 | case 0xff0: return "Kelvin";
|
---|
167 | default:
|
---|
168 | return "Unknown (" + value + ")";
|
---|
169 | }
|
---|
170 | }
|
---|
171 |
|
---|
172 | @Nullable
|
---|
173 | public String getColorSaturationDescription()
|
---|
174 | {
|
---|
175 | final Integer value = _directory.getInteger(TAG_COLOR_SATURATION);
|
---|
176 | if (value == null)
|
---|
177 | return null;
|
---|
178 | switch (value) {
|
---|
179 | case 0x000: return "Normal";
|
---|
180 | case 0x080: return "Medium High";
|
---|
181 | case 0x100: return "High";
|
---|
182 | case 0x180: return "Medium Low";
|
---|
183 | case 0x200: return "Low";
|
---|
184 | case 0x300: return "None (B&W)";
|
---|
185 | case 0x301: return "B&W Green Filter";
|
---|
186 | case 0x302: return "B&W Yellow Filter";
|
---|
187 | case 0x303: return "B&W Blue Filter";
|
---|
188 | case 0x304: return "B&W Sepia";
|
---|
189 | case 0x8000: return "Film Simulation";
|
---|
190 | default:
|
---|
191 | return "Unknown (" + value + ")";
|
---|
192 | }
|
---|
193 | }
|
---|
194 |
|
---|
195 | @Nullable
|
---|
196 | public String getToneDescription()
|
---|
197 | {
|
---|
198 | final Integer value = _directory.getInteger(TAG_TONE);
|
---|
199 | if (value == null)
|
---|
200 | return null;
|
---|
201 | switch (value) {
|
---|
202 | case 0x000: return "Normal";
|
---|
203 | case 0x080: return "Medium High";
|
---|
204 | case 0x100: return "High";
|
---|
205 | case 0x180: return "Medium Low";
|
---|
206 | case 0x200: return "Low";
|
---|
207 | case 0x300: return "None (B&W)";
|
---|
208 | case 0x8000: return "Film Simulation";
|
---|
209 | default:
|
---|
210 | return "Unknown (" + value + ")";
|
---|
211 | }
|
---|
212 | }
|
---|
213 |
|
---|
214 | @Nullable
|
---|
215 | public String getContrastDescription()
|
---|
216 | {
|
---|
217 | final Integer value = _directory.getInteger(TAG_CONTRAST);
|
---|
218 | if (value == null)
|
---|
219 | return null;
|
---|
220 | switch (value) {
|
---|
221 | case 0x000: return "Normal";
|
---|
222 | case 0x100: return "High";
|
---|
223 | case 0x300: return "Low";
|
---|
224 | default:
|
---|
225 | return "Unknown (" + value + ")";
|
---|
226 | }
|
---|
227 | }
|
---|
228 |
|
---|
229 | @Nullable
|
---|
230 | public String getNoiseReductionDescription()
|
---|
231 | {
|
---|
232 | final Integer value = _directory.getInteger(TAG_NOISE_REDUCTION);
|
---|
233 | if (value == null)
|
---|
234 | return null;
|
---|
235 | switch (value) {
|
---|
236 | case 0x040: return "Low";
|
---|
237 | case 0x080: return "Normal";
|
---|
238 | case 0x100: return "N/A";
|
---|
239 | default:
|
---|
240 | return "Unknown (" + value + ")";
|
---|
241 | }
|
---|
242 | }
|
---|
243 |
|
---|
244 | @Nullable
|
---|
245 | public String getHighIsoNoiseReductionDescription()
|
---|
246 | {
|
---|
247 | final Integer value = _directory.getInteger(TAG_HIGH_ISO_NOISE_REDUCTION);
|
---|
248 | if (value == null)
|
---|
249 | return null;
|
---|
250 | switch (value) {
|
---|
251 | case 0x000: return "Normal";
|
---|
252 | case 0x100: return "Strong";
|
---|
253 | case 0x200: return "Weak";
|
---|
254 | default:
|
---|
255 | return "Unknown (" + value + ")";
|
---|
256 | }
|
---|
257 | }
|
---|
258 |
|
---|
259 | @Nullable
|
---|
260 | public String getFlashModeDescription()
|
---|
261 | {
|
---|
262 | return getIndexedDescription(
|
---|
263 | TAG_FLASH_MODE,
|
---|
264 | "Auto",
|
---|
265 | "On",
|
---|
266 | "Off",
|
---|
267 | "Red-eye Reduction",
|
---|
268 | "External"
|
---|
269 | );
|
---|
270 | }
|
---|
271 |
|
---|
272 | @Nullable
|
---|
273 | public String getFlashExposureValueDescription()
|
---|
274 | {
|
---|
275 | Rational value = _directory.getRational(TAG_FLASH_EV);
|
---|
276 | return value == null ? null : value.toSimpleString(false) + " EV (Apex)";
|
---|
277 | }
|
---|
278 |
|
---|
279 | @Nullable
|
---|
280 | public String getMacroDescription()
|
---|
281 | {
|
---|
282 | return getIndexedDescription(TAG_MACRO, "Off", "On");
|
---|
283 | }
|
---|
284 |
|
---|
285 | @Nullable
|
---|
286 | public String getFocusModeDescription()
|
---|
287 | {
|
---|
288 | return getIndexedDescription(TAG_FOCUS_MODE, "Auto Focus", "Manual Focus");
|
---|
289 | }
|
---|
290 |
|
---|
291 | @Nullable
|
---|
292 | public String getSlowSyncDescription()
|
---|
293 | {
|
---|
294 | return getIndexedDescription(TAG_SLOW_SYNC, "Off", "On");
|
---|
295 | }
|
---|
296 |
|
---|
297 | @Nullable
|
---|
298 | public String getPictureModeDescription()
|
---|
299 | {
|
---|
300 | final Integer value = _directory.getInteger(TAG_PICTURE_MODE);
|
---|
301 | if (value == null)
|
---|
302 | return null;
|
---|
303 | switch (value) {
|
---|
304 | case 0x000: return "Auto";
|
---|
305 | case 0x001: return "Portrait scene";
|
---|
306 | case 0x002: return "Landscape scene";
|
---|
307 | case 0x003: return "Macro";
|
---|
308 | case 0x004: return "Sports scene";
|
---|
309 | case 0x005: return "Night scene";
|
---|
310 | case 0x006: return "Program AE";
|
---|
311 | case 0x007: return "Natural Light";
|
---|
312 | case 0x008: return "Anti-blur";
|
---|
313 | case 0x009: return "Beach & Snow";
|
---|
314 | case 0x00a: return "Sunset";
|
---|
315 | case 0x00b: return "Museum";
|
---|
316 | case 0x00c: return "Party";
|
---|
317 | case 0x00d: return "Flower";
|
---|
318 | case 0x00e: return "Text";
|
---|
319 | case 0x00f: return "Natural Light & Flash";
|
---|
320 | case 0x010: return "Beach";
|
---|
321 | case 0x011: return "Snow";
|
---|
322 | case 0x012: return "Fireworks";
|
---|
323 | case 0x013: return "Underwater";
|
---|
324 | case 0x014: return "Portrait with Skin Correction";
|
---|
325 | // skip 0x015
|
---|
326 | case 0x016: return "Panorama";
|
---|
327 | case 0x017: return "Night (Tripod)";
|
---|
328 | case 0x018: return "Pro Low-light";
|
---|
329 | case 0x019: return "Pro Focus";
|
---|
330 | // skip 0x01a
|
---|
331 | case 0x01b: return "Dog Face Detection";
|
---|
332 | case 0x01c: return "Cat Face Detection";
|
---|
333 | case 0x100: return "Aperture priority AE";
|
---|
334 | case 0x200: return "Shutter priority AE";
|
---|
335 | case 0x300: return "Manual exposure";
|
---|
336 | default:
|
---|
337 | return "Unknown (" + value + ")";
|
---|
338 | }
|
---|
339 | }
|
---|
340 |
|
---|
341 | @Nullable
|
---|
342 | public String getExrAutoDescription()
|
---|
343 | {
|
---|
344 | return getIndexedDescription(TAG_EXR_AUTO, "Auto", "Manual");
|
---|
345 | }
|
---|
346 |
|
---|
347 | @Nullable
|
---|
348 | public String getExrModeDescription()
|
---|
349 | {
|
---|
350 | final Integer value = _directory.getInteger(TAG_EXR_MODE);
|
---|
351 | if (value == null)
|
---|
352 | return null;
|
---|
353 | switch (value) {
|
---|
354 | case 0x100: return "HR (High Resolution)";
|
---|
355 | case 0x200: return "SN (Signal to Noise Priority)";
|
---|
356 | case 0x300: return "DR (Dynamic Range Priority)";
|
---|
357 | default:
|
---|
358 | return "Unknown (" + value + ")";
|
---|
359 | }
|
---|
360 | }
|
---|
361 |
|
---|
362 | @Nullable
|
---|
363 | public String getAutoBracketingDescription()
|
---|
364 | {
|
---|
365 | return getIndexedDescription(
|
---|
366 | TAG_AUTO_BRACKETING,
|
---|
367 | "Off",
|
---|
368 | "On",
|
---|
369 | "No Flash & Flash"
|
---|
370 | );
|
---|
371 | }
|
---|
372 |
|
---|
373 | @Nullable
|
---|
374 | public String getFinePixColorDescription()
|
---|
375 | {
|
---|
376 | final Integer value = _directory.getInteger(TAG_FINE_PIX_COLOR);
|
---|
377 | if (value == null)
|
---|
378 | return null;
|
---|
379 | switch (value) {
|
---|
380 | case 0x00: return "Standard";
|
---|
381 | case 0x10: return "Chrome";
|
---|
382 | case 0x30: return "B&W";
|
---|
383 | default:
|
---|
384 | return "Unknown (" + value + ")";
|
---|
385 | }
|
---|
386 | }
|
---|
387 |
|
---|
388 | @Nullable
|
---|
389 | public String getBlurWarningDescription()
|
---|
390 | {
|
---|
391 | return getIndexedDescription(
|
---|
392 | TAG_BLUR_WARNING,
|
---|
393 | "No Blur Warning",
|
---|
394 | "Blur warning"
|
---|
395 | );
|
---|
396 | }
|
---|
397 |
|
---|
398 | @Nullable
|
---|
399 | public String getFocusWarningDescription()
|
---|
400 | {
|
---|
401 | return getIndexedDescription(
|
---|
402 | TAG_FOCUS_WARNING,
|
---|
403 | "Good Focus",
|
---|
404 | "Out Of Focus"
|
---|
405 | );
|
---|
406 | }
|
---|
407 |
|
---|
408 | @Nullable
|
---|
409 | public String getAutoExposureWarningDescription()
|
---|
410 | {
|
---|
411 | return getIndexedDescription(
|
---|
412 | TAG_AUTO_EXPOSURE_WARNING,
|
---|
413 | "AE Good",
|
---|
414 | "Over Exposed"
|
---|
415 | );
|
---|
416 | }
|
---|
417 |
|
---|
418 | @Nullable
|
---|
419 | public String getDynamicRangeDescription()
|
---|
420 | {
|
---|
421 | return getIndexedDescription(
|
---|
422 | TAG_DYNAMIC_RANGE,
|
---|
423 | 1,
|
---|
424 | "Standard",
|
---|
425 | null,
|
---|
426 | "Wide"
|
---|
427 | );
|
---|
428 | }
|
---|
429 |
|
---|
430 | @Nullable
|
---|
431 | public String getFilmModeDescription()
|
---|
432 | {
|
---|
433 | final Integer value = _directory.getInteger(TAG_FILM_MODE);
|
---|
434 | if (value == null)
|
---|
435 | return null;
|
---|
436 | switch (value) {
|
---|
437 | case 0x000: return "F0/Standard (Provia) ";
|
---|
438 | case 0x100: return "F1/Studio Portrait";
|
---|
439 | case 0x110: return "F1a/Studio Portrait Enhanced Saturation";
|
---|
440 | case 0x120: return "F1b/Studio Portrait Smooth Skin Tone (Astia)";
|
---|
441 | case 0x130: return "F1c/Studio Portrait Increased Sharpness";
|
---|
442 | case 0x200: return "F2/Fujichrome (Velvia)";
|
---|
443 | case 0x300: return "F3/Studio Portrait Ex";
|
---|
444 | case 0x400: return "F4/Velvia";
|
---|
445 | case 0x500: return "Pro Neg. Std";
|
---|
446 | case 0x501: return "Pro Neg. Hi";
|
---|
447 | default:
|
---|
448 | return "Unknown (" + value + ")";
|
---|
449 | }
|
---|
450 | }
|
---|
451 |
|
---|
452 | @Nullable
|
---|
453 | public String getDynamicRangeSettingDescription()
|
---|
454 | {
|
---|
455 | final Integer value = _directory.getInteger(TAG_DYNAMIC_RANGE_SETTING);
|
---|
456 | if (value == null)
|
---|
457 | return null;
|
---|
458 | switch (value) {
|
---|
459 | case 0x000: return "Auto (100-400%)";
|
---|
460 | case 0x001: return "Manual";
|
---|
461 | case 0x100: return "Standard (100%)";
|
---|
462 | case 0x200: return "Wide 1 (230%)";
|
---|
463 | case 0x201: return "Wide 2 (400%)";
|
---|
464 | case 0x8000: return "Film Simulation";
|
---|
465 | default:
|
---|
466 | return "Unknown (" + value + ")";
|
---|
467 | }
|
---|
468 | }
|
---|
469 | }
|
---|