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