lin
2025-08-01 633231e833e21d5b8b1c00cb15aedb62b3b78e8f
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
//#define LOG_NDEBUG 0
#define LOG_TAG "DngValidateCamera"
#include <log/log.h>
#include <jni.h>
 
#include <string>
#include <sstream>
#include <iostream>
 
/**
 * Use DNG SDK to validate captured DNG file.
 *
 * This code is largely based on the dng_validate.cpp implementation included
 * with the DNG SDK. The portions of this file that are from the DNG SDK are
 * covered by the the DNG SDK license in /external/dng_sdk/LICENSE
 */
 
#include "dng_color_space.h"
#include "dng_date_time.h"
#include "dng_exceptions.h"
#include "dng_file_stream.h"
#include "dng_globals.h"
#include "dng_host.h"
#include "dng_ifd.h"
#include "dng_image_writer.h"
#include "dng_info.h"
#include "dng_linearization_info.h"
#include "dng_mosaic_info.h"
#include "dng_negative.h"
#include "dng_preview.h"
#include "dng_render.h"
#include "dng_simple_image.h"
#include "dng_tag_codes.h"
#include "dng_tag_types.h"
#include "dng_tag_values.h"
 
// Version of DNG validate referenced for this implementation
#define kDNGValidateVersion "1.4"
 
static bool gFourColorBayer = false;
 
static int32 gMosaicPlane = -1;
 
static uint32 gPreferredSize = 0;
static uint32 gMinimumSize   = 0;
static uint32 gMaximumSize   = 0;
 
static uint32 gProxyDNGSize = 0;
 
static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get();
 
static uint32 gFinalPixelType = ttByte;
 
static dng_string gDumpStage1;
static dng_string gDumpStage2;
static dng_string gDumpStage3;
static dng_string gDumpTIF;
static dng_string gDumpDNG;
 
/**
 * Validate DNG file in provided buffer.
 *
 * Returns dng_error_none (0) on success, otherwise one of the
 * dng_error_code enum values is returned.
 *
 * Warnings and errors found during validation are printed to stderr
 */
static dng_error_code dng_validate(const void* data, uint32_t count) {
 
    ALOGI("Validating DNG buffer");
 
    try {
        dng_stream stream(data, count);
 
        dng_host host;
 
        host.SetPreferredSize(gPreferredSize);
        host.SetMinimumSize(gMinimumSize);
        host.SetMaximumSize(gMaximumSize);
 
        host.ValidateSizes();
 
        if (host.MinimumSize()) {
            host.SetForPreview(true);
            gDumpDNG.Clear();
        }
 
        if (gDumpDNG.NotEmpty()) {
            host.SetSaveDNGVersion(dngVersion_SaveDefault);
            host.SetSaveLinearDNG(false);
            host.SetKeepOriginalFile(false);
        }
 
        // Read into the negative.
 
        AutoPtr<dng_negative> negative;
        {
            dng_info info;
            info.Parse(host, stream);
            info.PostParse(host);
            if (!info.IsValidDNG()) {
                return dng_error_bad_format;
            }
 
            negative.Reset(host.Make_dng_negative());
            negative->Parse(host, stream, info);
            negative->PostParse(host, stream, info);
 
            {
                dng_timer timer("Raw image read time");
                negative->ReadStage1Image(host, stream, info);
            }
 
            if (info.fMaskIndex != -1) {
                dng_timer timer("Transparency mask read time");
                negative->ReadTransparencyMask(host, stream, info);
            }
 
            negative->ValidateRawImageDigest(host);
        }
 
        // Option to write stage 1 image.
 
        if (gDumpStage1.NotEmpty()) {
            dng_file_stream stream2 (gDumpStage1.Get(), true);
            const dng_image &stage1 = *negative->Stage1Image();
            dng_image_writer writer;
 
            writer.WriteTIFF(host,
                    stream2,
                    stage1,
                    stage1.Planes() >= 3 ? piRGB
                    : piBlackIsZero);
 
            gDumpStage1.Clear();
        }
 
        // Metadata.
 
        negative->SynchronizeMetadata();
 
        // Four color Bayer option.
 
        if (gFourColorBayer) {
            negative->SetFourColorBayer();
        }
 
        // Build stage 2 image.
 
        {
            dng_timer timer("Linearization time");
            negative->BuildStage2Image(host);
        }
 
        if (gDumpStage2.NotEmpty()) {
            dng_file_stream stream2(gDumpStage2.Get(), true);
            const dng_image &stage2 = *negative->Stage2Image();
            dng_image_writer writer;
 
            writer.WriteTIFF (host,
                    stream2,
                    stage2,
                    stage2.Planes() >= 3 ? piRGB
                    : piBlackIsZero);
 
            gDumpStage2.Clear();
        }
 
        // Build stage 3 image.
 
        {
            dng_timer timer("Interpolate time");
            negative->BuildStage3Image(host,
                    gMosaicPlane);
        }
 
        // Convert to proxy, if requested.
 
        if (gProxyDNGSize) {
            dng_timer timer("ConvertToProxy time");
            dng_image_writer writer;
 
            negative->ConvertToProxy(host,
                    writer,
                    gProxyDNGSize);
        }
 
        // Flatten transparency, if required.
 
        if (negative->NeedFlattenTransparency(host)) {
            dng_timer timer("FlattenTransparency time");
            negative->FlattenTransparency(host);
        }
 
        if (gDumpStage3.NotEmpty()) {
            dng_file_stream stream2(gDumpStage3.Get(), true);
            const dng_image &stage3 = *negative->Stage3Image();
            dng_image_writer writer;
 
            writer.WriteTIFF (host,
                    stream2,
                    stage3,
                    stage3.Planes () >= 3 ? piRGB
                    : piBlackIsZero);
 
            gDumpStage3.Clear();
        }
 
        // Output DNG file if requested.
 
        if (gDumpDNG.NotEmpty()) {
            // Build the preview list.
            dng_preview_list previewList;
            dng_date_time_info dateTimeInfo;
            CurrentDateTimeAndZone(dateTimeInfo);
 
            for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++) {
 
                // Skip preview if writing a compresssed main image to save space
                // in this example code.
                if (negative->RawJPEGImage() != NULL && previewIndex > 0) {
                    break;
                }
 
                // Report timing.
                dng_timer timer(previewIndex == 0 ? "Build thumbnail time"
                        : "Build preview time");
 
                // Render a preview sized image.
                AutoPtr<dng_image> previewImage;
 
                {
                    dng_render render (host, *negative);
                    render.SetFinalSpace (negative->IsMonochrome() ?
                            dng_space_GrayGamma22::Get() : dng_space_sRGB::Get());
                    render.SetFinalPixelType (ttByte);
                    render.SetMaximumSize (previewIndex == 0 ? 256 : 1024);
 
                    previewImage.Reset (render.Render());
                }
 
                // Don't write the preview if it is same size as thumbnail.
 
                if (previewIndex > 0 &&
                        Max_uint32(previewImage->Bounds().W(),
                                previewImage->Bounds().H()) <= 256) {
                    break;
                }
 
                // If we have compressed JPEG data, create a compressed thumbnail.  Otherwise
                // save a uncompressed thumbnail.
                bool useCompressedPreview = (negative->RawJPEGImage() != NULL) ||
                        (previewIndex > 0);
 
                AutoPtr<dng_preview> preview (useCompressedPreview ?
                        (dng_preview *) new dng_jpeg_preview :
                        (dng_preview *) new dng_image_preview);
 
                // Setup up preview info.
 
                preview->fInfo.fApplicationName.Set("dng_validate");
                preview->fInfo.fApplicationVersion.Set(kDNGValidateVersion);
 
                preview->fInfo.fSettingsName.Set("Default");
 
                preview->fInfo.fColorSpace = previewImage->Planes() == 1 ?
                        previewColorSpace_GrayGamma22 :
                        previewColorSpace_sRGB;
 
                preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601();
 
                if (!useCompressedPreview) {
                    dng_image_preview *imagePreview = static_cast<dng_image_preview *>(preview.Get());
                    imagePreview->fImage.Reset(previewImage.Release());
                } else {
                    dng_jpeg_preview *jpegPreview = static_cast<dng_jpeg_preview *>(preview.Get());
                    int32 quality = (previewIndex == 0 ? 8 : 5);
                    dng_image_writer writer;
                    writer.EncodeJPEGPreview (host,
                            *previewImage,
                            *jpegPreview,
                            quality);
                }
                previewList.Append (preview);
            }
 
            // Write DNG file.
 
            dng_file_stream stream2(gDumpDNG.Get(), true);
 
            {
                dng_timer timer("Write DNG time");
                dng_image_writer writer;
 
                writer.WriteDNG(host,
                        stream2,
                        *negative.Get(),
                        &previewList,
                        dngVersion_Current,
                        false);
            }
 
            gDumpDNG.Clear();
        }
 
        // Output TIF file if requested.
        if (gDumpTIF.NotEmpty()) {
 
            // Render final image.
 
            dng_render render(host, *negative);
 
            render.SetFinalSpace(*gFinalSpace   );
            render.SetFinalPixelType(gFinalPixelType);
 
            if (host.MinimumSize()) {
                dng_point stage3Size = negative->Stage3Image()->Size();
                render.SetMaximumSize (Max_uint32(stage3Size.v,
                                stage3Size.h));
            }
 
            AutoPtr<dng_image> finalImage;
 
            {
                dng_timer timer("Render time");
                finalImage.Reset(render.Render());
            }
 
            finalImage->Rotate(negative->Orientation());
 
            // Now that Camera Raw supports non-raw formats, we should
            // not keep any Camera Raw settings in the XMP around when
            // writing rendered files.
#if qDNGUseXMP
            if (negative->GetXMP()) {
                negative->GetXMP()->RemoveProperties(XMP_NS_CRS);
                negative->GetXMP()->RemoveProperties(XMP_NS_CRSS);
            }
#endif
 
            // Write TIF file.
            dng_file_stream stream2(gDumpTIF.Get(), true);
 
            {
                dng_timer timer("Write TIFF time");
                dng_image_writer writer;
 
                writer.WriteTIFF(host,
                        stream2,
                        *finalImage.Get(),
                        finalImage->Planes() >= 3 ? piRGB
                        : piBlackIsZero,
                        ccUncompressed,
                        negative.Get(),
                        &render.FinalSpace());
            }
            gDumpTIF.Clear();
        }
    } catch (const dng_exception &except) {
        return except.ErrorCode();
    } catch (...) {
        return dng_error_unknown;
    }
 
    ALOGI("DNG validation complete");
 
    return dng_error_none;
}
 
extern "C" jboolean
Java_android_hardware_camera2_cts_DngCreatorTest_validateDngNative(
    JNIEnv* env, jclass /*clazz*/, jbyteArray dngBuffer) {
 
    jbyte* buffer = env->GetByteArrayElements(dngBuffer, NULL);
    jsize bufferCount = env->GetArrayLength(dngBuffer);
    if (buffer == nullptr) {
        ALOGE("Unable to map DNG buffer to native");
        return JNI_FALSE;
    }
 
    // DNG parsing warnings/errors fprintfs are spread throughout the DNG SDK,
    // guarded by the qDNGValidate define flag. To avoid modifying the SDK,
    // redirect stderr to a pipe to capture output locally.
 
    int pipeFds[2];
    int err;
 
    err = pipe(pipeFds);
    if (err != 0) {
        ALOGE("Error redirecting dng_validate output: %d", errno);
        env->ReleaseByteArrayElements(dngBuffer, buffer, 0);
        return JNI_FALSE;
    }
 
    int stderrFd = dup(fileno(stderr));
    dup2(pipeFds[1], fileno(stderr));
    close(pipeFds[1]);
 
    // Actually run the validation
    dng_error_code dng_err = dng_validate(buffer, bufferCount);
 
    env->ReleaseByteArrayElements(dngBuffer, buffer, 0);
 
    // Restore stderr and read out pipe
    dup2(stderrFd, fileno(stderr));
 
    std::stringstream errorStream;
    const size_t BUF_SIZE = 256;
    char readBuf[BUF_SIZE];
 
    ssize_t count = 0;
    while((count = read(pipeFds[0], readBuf, BUF_SIZE)) > 0) {
        errorStream.write(readBuf, count);
    }
    if (count < 0) {
        ALOGE("Error reading from dng_validate output pipe: %d", errno);
        return JNI_FALSE;
    }
    close(pipeFds[1]);
 
    std::string line;
    int lineCount = 0;
    ALOGI("Output from DNG validation:");
    // dng_validate doesn't actually propagate all errors/warnings to the
    // return error code, so look for an error pattern in output to detect
    // problems. Also make sure the output is long enough since some non-error
    // content should always be printed.
    while(std::getline(errorStream, line, '\n')) {
        lineCount++;
        if ( (line.size() > 3) &&
                (line[0] == line[1]) &&
                (line[1] == line[2]) &&
                (line[2] == '*') ) {
            // Found a warning or error, so need to fail the test
            if (dng_err == dng_error_none) {
                dng_err = dng_error_bad_format;
            }
            ALOGE("**|%s", line.c_str());
        } else {
            ALOGI("  |%s", line.c_str());
        }
    }
    // If no output is produced, assume something went wrong
    if (lineCount < 3) {
        ALOGE("Validation output less than expected!");
        dng_err = dng_error_unknown;
    }
    if (dng_err != dng_error_none) {
        ALOGE("DNG validation failed!");
    }
 
    return (dng_err == dng_error_none) ? JNI_TRUE : JNI_FALSE;
}