1 /*
2  * Copyright (C) 2016 The Android Open Source Project
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 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "DngValidateCamera"
19 #include <log/log.h>
20 #include <jni.h>
21 
22 #include <string>
23 #include <sstream>
24 #include <iostream>
25 
26 /**
27  * Use DNG SDK to validate captured DNG file.
28  *
29  * This code is largely based on the dng_validate.cpp implementation included
30  * with the DNG SDK. The portions of this file that are from the DNG SDK are
31  * covered by the the DNG SDK license in /external/dng_sdk/LICENSE
32  */
33 
34 #include "dng_color_space.h"
35 #include "dng_date_time.h"
36 #include "dng_exceptions.h"
37 #include "dng_file_stream.h"
38 #include "dng_globals.h"
39 #include "dng_host.h"
40 #include "dng_ifd.h"
41 #include "dng_image_writer.h"
42 #include "dng_info.h"
43 #include "dng_linearization_info.h"
44 #include "dng_mosaic_info.h"
45 #include "dng_negative.h"
46 #include "dng_preview.h"
47 #include "dng_render.h"
48 #include "dng_simple_image.h"
49 #include "dng_tag_codes.h"
50 #include "dng_tag_types.h"
51 #include "dng_tag_values.h"
52 
53 // Version of DNG validate referenced for this implementation
54 #define kDNGValidateVersion "1.4"
55 
56 static bool gFourColorBayer = false;
57 
58 static int32 gMosaicPlane = -1;
59 
60 static uint32 gPreferredSize = 0;
61 static uint32 gMinimumSize   = 0;
62 static uint32 gMaximumSize   = 0;
63 
64 static uint32 gProxyDNGSize = 0;
65 
66 static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get();
67 
68 static uint32 gFinalPixelType = ttByte;
69 
70 static dng_string gDumpStage1;
71 static dng_string gDumpStage2;
72 static dng_string gDumpStage3;
73 static dng_string gDumpTIF;
74 static dng_string gDumpDNG;
75 
76 /**
77  * Validate DNG file in provided buffer.
78  *
79  * Returns dng_error_none (0) on success, otherwise one of the
80  * dng_error_code enum values is returned.
81  *
82  * Warnings and errors found during validation are printed to stderr
83  */
dng_validate(const void * data,uint32_t count)84 static dng_error_code dng_validate(const void* data, uint32_t count) {
85 
86     ALOGI("Validating DNG buffer");
87 
88     try {
89         dng_stream stream(data, count);
90 
91         dng_host host;
92 
93         host.SetPreferredSize(gPreferredSize);
94         host.SetMinimumSize(gMinimumSize);
95         host.SetMaximumSize(gMaximumSize);
96 
97         host.ValidateSizes();
98 
99         if (host.MinimumSize()) {
100             host.SetForPreview(true);
101             gDumpDNG.Clear();
102         }
103 
104         if (gDumpDNG.NotEmpty()) {
105             host.SetSaveDNGVersion(dngVersion_SaveDefault);
106             host.SetSaveLinearDNG(false);
107             host.SetKeepOriginalFile(false);
108         }
109 
110         // Read into the negative.
111 
112         AutoPtr<dng_negative> negative;
113         {
114             dng_info info;
115             info.Parse(host, stream);
116             info.PostParse(host);
117             if (!info.IsValidDNG()) {
118                 return dng_error_bad_format;
119             }
120 
121             negative.Reset(host.Make_dng_negative());
122             negative->Parse(host, stream, info);
123             negative->PostParse(host, stream, info);
124 
125             {
126                 dng_timer timer("Raw image read time");
127                 negative->ReadStage1Image(host, stream, info);
128             }
129 
130             if (info.fMaskIndex != -1) {
131                 dng_timer timer("Transparency mask read time");
132                 negative->ReadTransparencyMask(host, stream, info);
133             }
134 
135             negative->ValidateRawImageDigest(host);
136         }
137 
138         // Option to write stage 1 image.
139 
140         if (gDumpStage1.NotEmpty()) {
141             dng_file_stream stream2 (gDumpStage1.Get(), true);
142             const dng_image &stage1 = *negative->Stage1Image();
143             dng_image_writer writer;
144 
145             writer.WriteTIFF(host,
146                     stream2,
147                     stage1,
148                     stage1.Planes() >= 3 ? piRGB
149                     : piBlackIsZero);
150 
151             gDumpStage1.Clear();
152         }
153 
154         // Metadata.
155 
156         negative->SynchronizeMetadata();
157 
158         // Four color Bayer option.
159 
160         if (gFourColorBayer) {
161             negative->SetFourColorBayer();
162         }
163 
164         // Build stage 2 image.
165 
166         {
167             dng_timer timer("Linearization time");
168             negative->BuildStage2Image(host);
169         }
170 
171         if (gDumpStage2.NotEmpty()) {
172             dng_file_stream stream2(gDumpStage2.Get(), true);
173             const dng_image &stage2 = *negative->Stage2Image();
174             dng_image_writer writer;
175 
176             writer.WriteTIFF (host,
177                     stream2,
178                     stage2,
179                     stage2.Planes() >= 3 ? piRGB
180                     : piBlackIsZero);
181 
182             gDumpStage2.Clear();
183         }
184 
185         // Build stage 3 image.
186 
187         {
188             dng_timer timer("Interpolate time");
189             negative->BuildStage3Image(host,
190                     gMosaicPlane);
191         }
192 
193         // Convert to proxy, if requested.
194 
195         if (gProxyDNGSize) {
196             dng_timer timer("ConvertToProxy time");
197             dng_image_writer writer;
198 
199             negative->ConvertToProxy(host,
200                     writer,
201                     gProxyDNGSize);
202         }
203 
204         // Flatten transparency, if required.
205 
206         if (negative->NeedFlattenTransparency(host)) {
207             dng_timer timer("FlattenTransparency time");
208             negative->FlattenTransparency(host);
209         }
210 
211         if (gDumpStage3.NotEmpty()) {
212             dng_file_stream stream2(gDumpStage3.Get(), true);
213             const dng_image &stage3 = *negative->Stage3Image();
214             dng_image_writer writer;
215 
216             writer.WriteTIFF (host,
217                     stream2,
218                     stage3,
219                     stage3.Planes () >= 3 ? piRGB
220                     : piBlackIsZero);
221 
222             gDumpStage3.Clear();
223         }
224 
225         // Output DNG file if requested.
226 
227         if (gDumpDNG.NotEmpty()) {
228             // Build the preview list.
229             dng_preview_list previewList;
230             dng_date_time_info dateTimeInfo;
231             CurrentDateTimeAndZone(dateTimeInfo);
232 
233             for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++) {
234 
235                 // Skip preview if writing a compresssed main image to save space
236                 // in this example code.
237                 if (negative->RawJPEGImage() != NULL && previewIndex > 0) {
238                     break;
239                 }
240 
241                 // Report timing.
242                 dng_timer timer(previewIndex == 0 ? "Build thumbnail time"
243                         : "Build preview time");
244 
245                 // Render a preview sized image.
246                 AutoPtr<dng_image> previewImage;
247 
248                 {
249                     dng_render render (host, *negative);
250                     render.SetFinalSpace (negative->IsMonochrome() ?
251                             dng_space_GrayGamma22::Get() : dng_space_sRGB::Get());
252                     render.SetFinalPixelType (ttByte);
253                     render.SetMaximumSize (previewIndex == 0 ? 256 : 1024);
254 
255                     previewImage.Reset (render.Render());
256                 }
257 
258                 // Don't write the preview if it is same size as thumbnail.
259 
260                 if (previewIndex > 0 &&
261                         Max_uint32(previewImage->Bounds().W(),
262                                 previewImage->Bounds().H()) <= 256) {
263                     break;
264                 }
265 
266                 // If we have compressed JPEG data, create a compressed thumbnail.  Otherwise
267                 // save a uncompressed thumbnail.
268                 bool useCompressedPreview = (negative->RawJPEGImage() != NULL) ||
269                         (previewIndex > 0);
270 
271                 AutoPtr<dng_preview> preview (useCompressedPreview ?
272                         (dng_preview *) new dng_jpeg_preview :
273                         (dng_preview *) new dng_image_preview);
274 
275                 // Setup up preview info.
276 
277                 preview->fInfo.fApplicationName.Set("dng_validate");
278                 preview->fInfo.fApplicationVersion.Set(kDNGValidateVersion);
279 
280                 preview->fInfo.fSettingsName.Set("Default");
281 
282                 preview->fInfo.fColorSpace = previewImage->Planes() == 1 ?
283                         previewColorSpace_GrayGamma22 :
284                         previewColorSpace_sRGB;
285 
286                 preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601();
287 
288                 if (!useCompressedPreview) {
289                     dng_image_preview *imagePreview = static_cast<dng_image_preview *>(preview.Get());
290                     imagePreview->fImage.Reset(previewImage.Release());
291                 } else {
292                     dng_jpeg_preview *jpegPreview = static_cast<dng_jpeg_preview *>(preview.Get());
293                     int32 quality = (previewIndex == 0 ? 8 : 5);
294                     dng_image_writer writer;
295                     writer.EncodeJPEGPreview (host,
296                             *previewImage,
297                             *jpegPreview,
298                             quality);
299                 }
300                 previewList.Append (preview);
301             }
302 
303             // Write DNG file.
304 
305             dng_file_stream stream2(gDumpDNG.Get(), true);
306 
307             {
308                 dng_timer timer("Write DNG time");
309                 dng_image_writer writer;
310 
311                 writer.WriteDNG(host,
312                         stream2,
313                         *negative.Get(),
314                         &previewList,
315                         dngVersion_Current,
316                         false);
317             }
318 
319             gDumpDNG.Clear();
320         }
321 
322         // Output TIF file if requested.
323         if (gDumpTIF.NotEmpty()) {
324 
325             // Render final image.
326 
327             dng_render render(host, *negative);
328 
329             render.SetFinalSpace(*gFinalSpace   );
330             render.SetFinalPixelType(gFinalPixelType);
331 
332             if (host.MinimumSize()) {
333                 dng_point stage3Size = negative->Stage3Image()->Size();
334                 render.SetMaximumSize (Max_uint32(stage3Size.v,
335                                 stage3Size.h));
336             }
337 
338             AutoPtr<dng_image> finalImage;
339 
340             {
341                 dng_timer timer("Render time");
342                 finalImage.Reset(render.Render());
343             }
344 
345             finalImage->Rotate(negative->Orientation());
346 
347             // Now that Camera Raw supports non-raw formats, we should
348             // not keep any Camera Raw settings in the XMP around when
349             // writing rendered files.
350 #if qDNGUseXMP
351             if (negative->GetXMP()) {
352                 negative->GetXMP()->RemoveProperties(XMP_NS_CRS);
353                 negative->GetXMP()->RemoveProperties(XMP_NS_CRSS);
354             }
355 #endif
356 
357             // Write TIF file.
358             dng_file_stream stream2(gDumpTIF.Get(), true);
359 
360             {
361                 dng_timer timer("Write TIFF time");
362                 dng_image_writer writer;
363 
364                 writer.WriteTIFF(host,
365                         stream2,
366                         *finalImage.Get(),
367                         finalImage->Planes() >= 3 ? piRGB
368                         : piBlackIsZero,
369                         ccUncompressed,
370                         negative.Get(),
371                         &render.FinalSpace());
372             }
373             gDumpTIF.Clear();
374         }
375     } catch (const dng_exception &except) {
376         return except.ErrorCode();
377     } catch (...) {
378         return dng_error_unknown;
379     }
380 
381     ALOGI("DNG validation complete");
382 
383     return dng_error_none;
384 }
385 
386 extern "C" jboolean
Java_android_hardware_camera2_cts_DngCreatorTest_validateDngNative(JNIEnv * env,jclass,jbyteArray dngBuffer)387 Java_android_hardware_camera2_cts_DngCreatorTest_validateDngNative(
388     JNIEnv* env, jclass /*clazz*/, jbyteArray dngBuffer) {
389 
390     jbyte* buffer = env->GetByteArrayElements(dngBuffer, NULL);
391     jsize bufferCount = env->GetArrayLength(dngBuffer);
392     if (buffer == nullptr) {
393         ALOGE("Unable to map DNG buffer to native");
394         return JNI_FALSE;
395     }
396 
397     // DNG parsing warnings/errors fprintfs are spread throughout the DNG SDK,
398     // guarded by the qDNGValidate define flag. To avoid modifying the SDK,
399     // redirect stderr to a pipe to capture output locally.
400 
401     int pipeFds[2];
402     int err;
403 
404     err = pipe(pipeFds);
405     if (err != 0) {
406         ALOGE("Error redirecting dng_validate output: %d", errno);
407         env->ReleaseByteArrayElements(dngBuffer, buffer, 0);
408         return JNI_FALSE;
409     }
410 
411     int stderrFd = dup(fileno(stderr));
412     dup2(pipeFds[1], fileno(stderr));
413     close(pipeFds[1]);
414 
415     // Actually run the validation
416     dng_error_code dng_err = dng_validate(buffer, bufferCount);
417 
418     env->ReleaseByteArrayElements(dngBuffer, buffer, 0);
419 
420     // Restore stderr and read out pipe
421     dup2(stderrFd, fileno(stderr));
422 
423     std::stringstream errorStream;
424     const size_t BUF_SIZE = 256;
425     char readBuf[BUF_SIZE];
426 
427     ssize_t count = 0;
428     while((count = read(pipeFds[0], readBuf, BUF_SIZE)) > 0) {
429         errorStream.write(readBuf, count);
430     }
431     if (count < 0) {
432         ALOGE("Error reading from dng_validate output pipe: %d", errno);
433         return JNI_FALSE;
434     }
435     close(pipeFds[1]);
436 
437     std::string line;
438     int lineCount = 0;
439     ALOGI("Output from DNG validation:");
440     // dng_validate doesn't actually propagate all errors/warnings to the
441     // return error code, so look for an error pattern in output to detect
442     // problems. Also make sure the output is long enough since some non-error
443     // content should always be printed.
444     while(std::getline(errorStream, line, '\n')) {
445         lineCount++;
446         if ( (line.size() > 3) &&
447                 (line[0] == line[1]) &&
448                 (line[1] == line[2]) &&
449                 (line[2] == '*') ) {
450             // Found a warning or error, so need to fail the test
451             if (dng_err == dng_error_none) {
452                 dng_err = dng_error_bad_format;
453             }
454             ALOGE("**|%s", line.c_str());
455         } else {
456             ALOGI("  |%s", line.c_str());
457         }
458     }
459     // If no output is produced, assume something went wrong
460     if (lineCount < 3) {
461         ALOGE("Validation output less than expected!");
462         dng_err = dng_error_unknown;
463     }
464     if (dng_err != dng_error_none) {
465         ALOGE("DNG validation failed!");
466     }
467 
468     return (dng_err == dng_error_none) ? JNI_TRUE : JNI_FALSE;
469 }
470