Browse Source

Read more of the DNG tags with the dng_read function

Martijn Braam 2 months ago
parent
commit
0802ff0c1f
4 changed files with 83 additions and 2 deletions
  1. 2 0
      CMakeLists.txt
  2. 2 0
      include/libdng.h
  3. 26 2
      src/libdng.c
  4. 53 0
      tests/check_dng.c

+ 2 - 0
CMakeLists.txt

@@ -6,6 +6,8 @@ set(LIBRARY_VERSION_MAJOR 0)
 set(LIBRARY_VERSION_STRING 0.1)
 set(CMAKE_C_VISIBILITY_PRESET hidden)
 
+add_definitions(-D_XOPEN_SOURCE=700)
+
 add_library(libdng SHARED include/libdng.h src/libdng.c src/dng.h src/mode.c src/repack.c src/repack.h src/dcp.c)
 
 set_target_properties(libdng PROPERTIES

+ 2 - 0
include/libdng.h

@@ -45,6 +45,8 @@ typedef struct {
 
 		uint16_t bit_depth;
 		bool needs_repack;
+		uint32_t width;
+		uint32_t height;
 } libdng_info;
 
 #define LIBDNG_ORIENTATION_TOPLEFT 1

+ 26 - 2
src/libdng.c

@@ -8,6 +8,7 @@
 #include <sys/time.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 
 #define DNG_SUBFILETYPE_ORIGINAL 0
 #define DNG_SUBFILETYPE_THUMBNAIL 1
@@ -141,7 +142,7 @@ libdng_set_datetime(libdng_info *dng, struct tm time)
 	if (dng == NULL)
 		return 0;
 
-	dng->datetime = time;
+	memcpy(&dng->datetime, &time, sizeof(struct tm));
 	return 1;
 }
 
@@ -290,7 +291,6 @@ libdng_write_with_thumbnail(libdng_info *dng, const char *path, unsigned int wid
 	if (!tif) {
 		return 0;
 	}
-	libdng_set_datetime_now(dng);
 
 	char datetime[20] = {0};
 	if (dng->datetime.tm_year) {
@@ -459,6 +459,7 @@ libdng_read(libdng_info *dng, const char *path)
 
 	// Reading the "main" image which is the thumbnail
 	TIFFGetField(tif, TIFFTAG_ORIENTATION, &dng->orientation);
+
 	if (TIFFGetField(tif, TIFFTAG_MAKE, &cvalues) == 1) {
 		dng->camera_make = strdup(cvalues);
 	}
@@ -471,6 +472,10 @@ libdng_read(libdng_info *dng, const char *path)
 	if (TIFFGetField(tif, TIFFTAG_SOFTWARE, &cvalues) == 1) {
 		dng->software = strdup(cvalues);
 	}
+	if (TIFFGetField(tif, TIFFTAG_DATETIME, &cvalues) == 1) {
+		strptime(cvalues, "%Y:%m:%d %H:%M:%S", &dng->datetime);
+	}
+	TIFFGetField(tif, TIFFTAG_ORIENTATION, &dng->orientation);
 
 	if (TIFFGetField(tif, DNGTAG_ASSHOTNEUTRAL, &count, &fvalues) == 1) {
 		for (int i = 0; i < count; i++) {
@@ -507,11 +512,16 @@ libdng_read(libdng_info *dng, const char *path)
 	int subifd_count = 0;
 	void *ptr;
 	toff_t subifd_offsets[2];
+	toff_t exif_offset = 0;
+
+	TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_offset);
 	TIFFGetField(tif, TIFFTAG_SUBIFD, &subifd_count, &ptr);
 	memcpy(subifd_offsets, ptr, subifd_count * sizeof(subifd_offsets[0]));
 	TIFFSetSubDirectory(tif, subifd_offsets[0]);
 
 	TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &dng->bit_depth);
+	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &dng->width);
+	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &dng->height);
 
 	if (TIFFGetField(tif, DNGTAG_WHITELEVEL, &count, &u32values) == 1) {
 		dng->whitelevel = u32values[0];
@@ -528,6 +538,20 @@ libdng_read(libdng_info *dng, const char *path)
 		}
 	}
 
+	if (exif_offset != 0) {
+		TIFFReadEXIFDirectory(tif, exif_offset);
+		TIFFGetField(tif, EXIFTAG_EXPOSURETIME, &dng->exposure_time);
+		TIFFGetField(tif, EXIFTAG_EXPOSUREPROGRAM, &dng->exposure_program);
+		TIFFGetField(tif, EXIFTAG_FNUMBER, &dng->fnumber);
+		TIFFGetField(tif, EXIFTAG_FOCALLENGTH, &dng->focal_length);
+		uint16_t fffocallength;
+		if (TIFFGetField(tif, EXIFTAG_FOCALLENGTHIN35MMFILM, &fffocallength) == 1) {
+			if (fffocallength > 0.0f) {
+				dng->crop_factor = (float) fffocallength / dng->focal_length;
+			}
+		}
+	}
+
 	TIFFClose(tif);
 	return 1;
 }

+ 53 - 0
tests/check_dng.c

@@ -101,9 +101,62 @@ TEST generate_simple_dng(void)
 		PASS();
 }
 
+TEST read_dng(void)
+{
+	// Generate test timestamp
+	struct tm testtime;
+	strptime("2024-08-27 01:02:03", "%Y-%m-%d %H:%M:%S", &testtime);
+
+	// Generate a file to read
+	libdng_info info = {0};
+	libdng_new(&info);
+	libdng_set_datetime(&info, testtime);
+	libdng_set_mode_from_name(&info, "RGGB");
+	libdng_set_make_model(&info, "Make", "Model");
+	libdng_set_software(&info, "Software");
+	libdng_set_orientation(&info, LIBDNG_ORIENTATION_RIGHTBOT);
+	libdng_set_exposure_time(&info, 1.4f);
+	libdng_set_exposure_program(&info, LIBDNG_EXPOSUREPROGRAM_PORTRAIT);
+	libdng_set_fnumber(&info, 1.8f);
+	libdng_set_focal_length(&info, 75.0f, 1.6f);
+
+	uint8_t *data = malloc(1280 * 720);
+	libdng_write(&info, "test.dng", 1280, 720, data, 1280 * 720);
+	free(data);
+	libdng_free(&info);
+
+	// Read the file again
+	libdng_info dng = {0};
+	libdng_read(&dng, "test.dng");
+		ASSERT_EQm("Bit depth", 8, dng.bit_depth);
+		ASSERT_EQm("CFFA[0]", 0, dng.cfapattern[0]);
+		ASSERT_EQm("CFFA[1]", 1, dng.cfapattern[1]);
+		ASSERT_EQm("CFFA[2]", 1, dng.cfapattern[2]);
+		ASSERT_EQm("CFFA[3]", 2, dng.cfapattern[3]);
+		ASSERT_STR_EQm("Make", "Make", dng.camera_make);
+		ASSERT_STR_EQm("Model", "Model", dng.camera_model);
+		ASSERT_STR_EQm("Software", "Software", dng.software);
+		ASSERT_EQm("Orientation", LIBDNG_ORIENTATION_RIGHTBOT, dng.orientation);
+		ASSERT_EQ_FMTm("Exposure time", 1.4f, dng.exposure_time, "%f");
+		ASSERT_EQm("Exposure program", LIBDNG_EXPOSUREPROGRAM_PORTRAIT, dng.exposure_program);
+		ASSERT_EQ_FMTm("FNumber", 1.8f, dng.fnumber, "%f");
+		ASSERT_EQ_FMTm("Focal length", 75.0f, dng.focal_length, "%f");
+		ASSERT_EQ_FMTm("Crop factor", 1.6f, dng.crop_factor, "%f");
+
+		ASSERT_EQm("Year", testtime.tm_year, dng.datetime.tm_year);
+		ASSERT_EQm("Month", testtime.tm_mon, dng.datetime.tm_mon);
+		ASSERT_EQm("Day", testtime.tm_yday, dng.datetime.tm_yday);
+		ASSERT_EQ_FMTm("Hour", testtime.tm_hour, dng.datetime.tm_hour, "%d");
+		ASSERT_EQm("Minute", testtime.tm_min, dng.datetime.tm_min);
+		ASSERT_EQm("Second", testtime.tm_sec, dng.datetime.tm_sec);
+
+		PASS();
+}
+
 SUITE (test_suite)
 {
 		RUN_TEST(generate_simple_dng);
+		RUN_TEST(read_dng);
 }
 
 GREATEST_MAIN_DEFS();