title: How to produce an Adobe DNG image from scratch with LibTiff
body: <p>I am trying to generate an Adobe DNG file using libTiff from scratch for my specfic camera RAW files. For this I wrote an example to generate a DNG which should be completely black. However, i cannot open my result and i am suspecting that there is something wrong or something i missed in my code.</p>
  
  <pre><code>#include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  #include &lt;sys\stat.h&gt;
  #include &lt;time.h&gt;
  #include &lt;math.h&gt;
  #include &lt;errno.h&gt;  
  #include &lt;tiffio.h&gt;
  
  #define HPIXELS 2048            // number of horizontal pixels on PointGrey Grasshopper3 GS3-U3-41C6C sensor
  #define VPIXELS 2048            // number of vertical pixels on PointGrey Grasshopper3 GS3-U3-41C6C sensor
  
  int main(int argc, char **argv) {
  
      static const short CFARepeatPatternDim[] = { 2,2 }; // 2x2 CFA
  
      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 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 neutral[] = { 0.807133, 1.0, 0.913289 };
  
      const int bpp = 8;
      const char* fname = "C:\\tmp\\dngTest\\output.DNG";
  
      long sub_offset = 0;
      long white = 0xffff;
  
      struct stat st;
      struct tm tm;
      char datetime[64];
  
      FILE *ifp;
      TIFF *tif;
  
      // ============================================================================
      stat(fname, &amp;st);
      gmtime_s(&amp;tm, &amp;st.st_mtime);
      sprintf(datetime, "%04d:%02d:%02d %02d:%02d:%02d",
          tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  
      if (!(tif = TIFFOpen("C:\\tmp\\dngTest\\output.dng", "w"))) {
          fclose(ifp);
          exit(-1);
      }
  
      // Set meta data
      TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
      TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,   HPIXELS &gt;&gt; 4);
      TIFFSetField(tif, TIFFTAG_IMAGELENGTH,  VPIXELS &gt;&gt; 4);
      TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
      TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
      TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
      TIFFSetField(tif, TIFFTAG_MAKE, "PointGrey");
      TIFFSetField(tif, TIFFTAG_MODEL, "Grasshopper3 GS3-U3-41C6C");
      TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
      TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
      TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
      TIFFSetField(tif, TIFFTAG_SOFTWARE, "primus_dng");
      TIFFSetField(tif, TIFFTAG_DATETIME, datetime);
      TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &amp;sub_offset);
      TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0");
      TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0");
      TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, "PointGrey Grasshopper3 GS3-U3-41C6C");
      TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, cam_xyz);
      TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
      TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
      // TIFFSetField(tif, TIFFTAG_ORIGINALRAWFILENAME, fname); TODO enable when clear not to crash
  
      // All Black Thumbnail
      {
          unsigned char *buf = (unsigned char *)malloc((int)HPIXELS &gt;&gt; 4);
          memset(buf, 0, (int)HPIXELS &gt;&gt; 4);
          for (int row = 0; row &lt; (int)VPIXELS&gt;&gt; 4; row++)
              TIFFWriteScanline(tif, buf, row, 0); // just leave it black, no software uses the built-in preview, builds off real image.
      }
  
      TIFFWriteDirectory(tif);
  
  
      // fprintf(stderr, "Writing TIFF header for main image...\n");
      TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);          // image
      TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, HPIXELS);     // in pixels
      TIFFSetField(tif, TIFFTAG_IMAGELENGTH, VPIXELS);    // in pixels
      TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);       // int
      TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
      TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, bpp);
      TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
      TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, CFARepeatPatternDim);
      TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\000\001\001\002"); // RGGB // 0 = Red, 1 = Green, 2 = Blue, 3 = Cyan, 4 = Magenta, 5 = Yellow, 6 = White 
      //TIFFSetField(tif, TIFFTAG_LINEARIZATIONTABLE, 256, curve); set it off for now
      TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &amp;white);
  
      fprintf(stderr, "Processing RAW data...\n");
  
      // bit depth
      unsigned char *pLine = (unsigned char*)malloc(sizeof(unsigned char) * HPIXELS);
      memset(pLine, 0, sizeof(unsigned char) * HPIXELS);
      for (int row = 0; row &lt; VPIXELS; row++)
      {
          TIFFWriteScanline(tif, pLine, row, 0); // this writes a single complete row
      }
      free(pLine);
      TIFFClose(tif);
      return 0;
  }
  </code></pre>
  
  <p>The minimal complete example is <a href="https://lab.apertus.org/T759" rel="nofollow noreferrer">leaned on this.</a> I am using a RGGB bayer battern. Switching to the Adobe DNG SDK is something I really want to avoid since building the libraries is a mess using VS2015 or higher.</p>