Browse Source

Generate valid DNG output

Martijn Braam 1 year ago
parent
commit
a2e45c0167
6 changed files with 67 additions and 9 deletions
  1. 3 0
      .gitignore
  2. 1 1
      CMakeLists.txt
  3. 7 0
      include/libdng.h
  4. 4 0
      src/dng.h
  5. 45 5
      src/libdng.c
  6. 7 3
      util/makedng.c

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+/cmake*
+/sdk
+*.dng

+ 1 - 1
CMakeLists.txt

@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.25)
+cmake_minimum_required(VERSION 3.23)
 project(libdng C)
 
 set(CMAKE_C_STANDARD 23)

+ 7 - 0
include/libdng.h

@@ -14,6 +14,13 @@ typedef struct {
 		char *software;
 		uint16_t orientation;
 		struct tm datetime;
+
+		// Raw image data
+		uint16_t bayer_pattern_dimensions[2];
+		float colormatrix1[9];
+		float colormatrix2[9];
+		float neutral[3];
+		uint8_t cfapattern[4];
 } libdng_info;
 
 EXPORT int

+ 4 - 0
src/dng.h

@@ -116,6 +116,8 @@
 
 
 // DNG tags not present in libtiff
+#define DNGTAG_CFAREPEATPATTERNDIM 33421
+#define DNGTAG_CFAPATTERN 33422
 #define DNGTAG_FORWARDMATRIX1 50964
 #define DNGTAG_FORWARDMATRIX2 50965
 #define DNGTAG_COLOR_MATRIX_1 50721
@@ -142,4 +144,6 @@ static const TIFFFieldInfo custom_dng_fields[] = {
 	{DNGTAG_PROFILE_HUE_SAT_MAP_DIMS,   -1, -1, TIFF_FLOAT,     FIELD_CUSTOM, 1, 1, "ProfileHueSatMapDims"},
 	{DNGTAG_PROFILE_HUE_SAT_MAP_DATA_1, -1, -1, TIFF_FLOAT,     FIELD_CUSTOM, 1, 1, "ProfileHueSatMapData1"},
 	{DNGTAG_PROFILE_HUE_SAT_MAP_DATA_2, -1, -1, TIFF_FLOAT,     FIELD_CUSTOM, 1, 1, "ProfileHueSatMapData2"},
+	{DNGTAG_CFAREPEATPATTERNDIM,        -1, -1, TIFF_SHORT,     FIELD_CUSTOM, 1, 1, "CFARepeatPatternDim"},
+	{DNGTAG_CFAPATTERN,                 -1, -1, TIFF_BYTE,      FIELD_CUSTOM, 1, 1, "CFAPattern"},
 };

+ 45 - 5
src/libdng.c

@@ -32,6 +32,21 @@ void
 libdng_new(libdng_info *dng)
 {
 	dng->orientation = 1;
+	dng->bayer_pattern_dimensions[0] = 2;
+	dng->bayer_pattern_dimensions[1] = 2;
+
+	for (size_t i = 0; i < 9; i++) {
+		dng->colormatrix1[i] = 0.0f;
+		dng->colormatrix2[i] = 0.0f;
+	}
+	dng->colormatrix1[0] = 1.0f;
+	dng->colormatrix1[4] = 1.0f;
+	dng->colormatrix1[8] = 1.0f;
+
+	dng->cfapattern[0] = 0;
+	dng->cfapattern[1] = 1;
+	dng->cfapattern[2] = 1;
+	dng->cfapattern[3] = 2;
 }
 
 int
@@ -81,12 +96,16 @@ libdng_write(libdng_info *dng, const char *path, unsigned int width, unsigned in
 	if (!tif) {
 		return -1;
 	}
+	libdng_set_datetime_now(dng);
 
 	char datetime[20] = {0};
 	if (dng->datetime.tm_year) {
 		strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &dng->datetime);
 	}
 
+	uint64_t ifd0_offsets[] = {0L};
+
+
 	// First IFD describes the thumbnail and contains most of the metadata
 	// Tags are in numerical order
 	TIFFSetField(tif, TIFFTAG_SUBFILETYPE, DNG_SUBFILETYPE_THUMBNAIL);
@@ -95,13 +114,16 @@ libdng_write(libdng_info *dng, const char *path, unsigned int width, unsigned in
 	TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
 	TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
 	TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+	TIFFSetField(tif, TIFFTAG_ORIENTATION, dng->orientation);
+	TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+	TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+	TIFFSetField(tif, DNGTAG_COLOR_MATRIX_1, 9, dng->colormatrix1);
+	TIFFSetField(tif, DNGTAG_ASSHOTNEUTRAL, 3, dng->neutral);
 
 	if (dng->camera_make != NULL)
 		TIFFSetField(tif, TIFFTAG_MAKE, dng->camera_make);
 	if (dng->camera_model != NULL)
 		TIFFSetField(tif, TIFFTAG_MODEL, dng->camera_model);
-	if (dng->orientation != 1)
-		TIFFSetField(tif, TIFFTAG_ORIENTATION, dng->orientation);
 	if (dng->software != NULL)
 		TIFFSetField(tif, TIFFTAG_SOFTWARE, dng->software);
 
@@ -120,6 +142,7 @@ libdng_write(libdng_info *dng, const char *path, unsigned int width, unsigned in
 		snprintf(ucm, sizeof(ucm), "%s %s", dng->camera_make, dng->camera_model);
 	}
 	TIFFSetField(tif, DNGTAG_UNIQUECAMERAMODEL, ucm);
+	TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &ifd0_offsets);
 
 	// Write black thumbnail, only windows uses this
 	{
@@ -138,6 +161,14 @@ libdng_write(libdng_info *dng, const char *path, unsigned int width, unsigned in
 	TIFFSetField(tif, TIFFTAG_SUBFILETYPE, DNG_SUBFILETYPE_ORIGINAL);
 	TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
 	TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
+	TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
+	TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
+	TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+	TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+	TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, 2, dng->bayer_pattern_dimensions);
+	TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+	TIFFSetField(tif, DNGTAG_CFAPATTERN, 4, dng->cfapattern);
+
 	unsigned int stride = width;
 	for (int row = 0; row < height; row++) {
 		TIFFWriteScanline(tif, (void *) data + (row * stride), row, 0);
@@ -146,15 +177,23 @@ libdng_write(libdng_info *dng, const char *path, unsigned int width, unsigned in
 		return -1;
 	}
 
-	TIFFCreateEXIFDirectory(tif);
+	if (TIFFCreateEXIFDirectory(tif) != 0) {
+		fprintf(stderr, "Could not create EXIF\n");
+		return -1;
+	}
 
 	if (dng->datetime.tm_year) {
-		TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime);
+		if (!TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime)) {
+			fprintf(stderr, "Could not write datetimeoriginal\n");
+		}
 		TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime);
 	}
 
 	uint64_t exif_offset = 0;
-	TIFFWriteCustomDirectory(tif, &exif_offset);
+	if (!TIFFWriteCustomDirectory(tif, &exif_offset)) {
+		fprintf(stderr, "Can't write EXIF\n");
+		return -1;
+	}
 	TIFFFreeDirectory(tif);
 
 	// Update exif pointer
@@ -162,6 +201,7 @@ libdng_write(libdng_info *dng, const char *path, unsigned int width, unsigned in
 	TIFFSetField(tif, TIFFTAG_EXIFIFD, exif_offset);
 	TIFFRewriteDirectory(tif);
 
+
 	TIFFClose(tif);
 
 	return 0;

+ 7 - 3
util/makedng.c

@@ -25,7 +25,7 @@ main(int argc, char *argv[])
 	long val;
 
 	libdng_init();
-	libdng_info info;
+	libdng_info info = {0};
 	libdng_new(&info);
 	unsigned int width = 0;
 	unsigned int height = 0;
@@ -66,7 +66,7 @@ main(int argc, char *argv[])
 		return 1;
 	}
 
-	printf("Reading %s\n", argv[optind]);
+	printf("Reading %s...\n", argv[optind]);
 	FILE *src = fopen(argv[optind], "r");
 	if (src == NULL) {
 		fprintf(stderr, "Can't open source file: %s\n", strerror(errno));
@@ -79,7 +79,11 @@ main(int argc, char *argv[])
 	fread(data, src_size, 1, src);
 	fclose(src);
 
-	libdng_write(&info, argv[optind + 1], width, height, data, src_size);
+	printf("Writing %s...\n", argv[optind + 1]);
+	if (libdng_write(&info, argv[optind + 1], width, height, data, src_size) < 0) {
+		fprintf(stderr, "Could not write DNG\n");
+		return 1;
+	}
 	free(data);
 	libdng_free(&info);
 	return 0;