Home | History | Annotate | Download | only in libctscamera2jni
      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  */
     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
    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