Code provided by Spirit532 on IRC:
Output example:
TIFF *tif; static const short bayerPatternDimensions[] = { 2, 2 }; static const double sRGB[] = { 3.6156, -0.8100, -0.0837, -0.3094, 1.5500, -0.5439, 0.0967, -0.4700, 1.9805 }; // sRGB profile static const float AsShotNeutral[] = { 1.0, 1.0, 1.0, }; // needs this unsigned short curve[256]; static const float cam_xyz[] = { 2.005,-0.771,-0.269, -0.752,1.688,0.064, -0.149,0.283,0.745 }; // xyz static const float neutral[] = { 0.807133, 1.0, 0.913289 }; tif = TIFFOpen("C:\image.dng", "w"); for (int i = 0; i < 256; i++) curve[i] = 0x3fff * pow(i / 255.0, 100 / 80) + 0.5; int bpp = 8; // bits per pixel, integer TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1); // preview TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (int)uiWidth >> 4); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (int)uiHeight >> 4); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); // should be 8, this is the preview part TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_MAKE, "indeed a make"); TIFFSetField(tif, TIFFTAG_MODEL, "..."); TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_SOFTWARE, "Software"); //TIFFSetField(tif, TIFFTAG_DATETIME, datetime); // not needed, unless embedded FS TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset); TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0"); TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0"); TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, "Camera"); TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, cam_xyz); TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral); TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21); TIFFSetField(tif, TIFFTAG_ORIGINALRAWFILENAME, "image.dng"); // haven't seen any software use the { unsigned char *buf = (unsigned char *)malloc((int)uiWidth >> 4); memset(buf, 0, (int)uiWidth >> 4); for (row = 0; row < (int)uiHeight >> 4; row++) TIFFWriteScanline(tif, buf, row, 0); // just leave it black, no software uses the built-in preview, builds off real image. } TIFFWriteDirectory(tif); TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0); // image TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (int)uiWidth); // in pixels TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (int)uiHeight); // in pixels TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bpp); // int TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, bayerPatternDimensions); TIFFSetField(tif, TIFFTAG_CFAPATTERN, 4, "\001\002\000\001"); // the ", 4," is important, crashes otherwise, didn't investigate TIFFSetField(tif, TIFFTAG_LINEARIZATIONTABLE, 256, curve); TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &white); // bit depth if (rPic.m_eDataType == extypeUInt8) { quint8 *pLine; for (uint row = 0; row < uiHeight; row++) { pLine = ROW_U8(rPic, row); TIFFWriteScanline(tif, pLine, row, 0); // this writes a single complete row } } // bit depth else if (rPic.m_eDataType == extypeUInt16) { quint16 *pLine; for (uint row = 0; row < uiHeight; row++) { pLine = ROW_U16(rPic, row); TIFFWriteScanline(tif, pLine, row, 0); } } TIFFClose(tif);