Working with TIFF Images

Tag Image File Format (TIFF) files are used for a diverse set of applications such as GIS (geographic information systems), CAD drawing programs, graphic arts, and so forth. Fortunately, a portable library is freely available, from www.libtiff.org, for decoding the information stored in TIFF files. The library also provides software tools that can be extremely helpful for common graphics programming tasks. The rest of this article takes you through the steps required to incorporate the TIFF library into your C++ projects.

JPEG images need no introduction due to their widespread use on the Web, but let’s suppose you are a poor student and you don’t want to pay for an image library; what other possible avenues exist for supporting JPEG images? The answer: another free library available publicly on the Web. This article just wouldn’t be complete without incorporating a free JPEG image library.

If you’ve been working with Windows for a while, you probably have lots of BMP files on your hard drive. Working with BMP files has been well covered on CodeGuru and I see little advantage to providing another implementation. On the other hand, it would be nice to have a complete image library for Windows and it can be frustrating when an article almost covers a topic but leaves out one important aspect. So, I’ll include a brief section on BMP files as well.

The original article was titled “Working with TIFF Images” and the TIFF library is really the motivation for re-writing that article. Of course, there are always comments such as “Why don’t you support format X?” so this revision includes support for more formats, but the TIFF library has really come a long way, especially in the area of the tools that are included with the library and extensions to the library. Unfortunately, there is not enough time to go into each of the TIFF tools, so only the PDF conversion tool is highlighted. A particular extension to the TIFF library will be covered shortly with an article on GeoTIFF images. GeoTIFFs are geographically referenced images, typically the result of a satellite or aerial photo. Stay tuned for more on handling GeoTIFF files.

So, this article covers the following topics:

  1. Working with the TIFF image library
    1. Building the TIFF library
    2. Loading a TIFF image into a DIB (device independent bitmap)
    3. Writing a DIB image to a TIFF image file
    4. Converting TIFF images to PDF (portable document format) files
  2. Working with the JPEG image library
    1. Building the JPEG image library
    2. Loading a JPEG image file into a DIB
    3. Writing a DIB image to a JPEG image file
    4. Using the TIFF library to convert a JPEG image to PDF format
  3. Working with BMP images
    1. Loading a BMP image file into a DIB
    2. Writing a DIB image to a BMP file
  4. Additional information about the sample project

Working with the TIFF Image Library

Building the TIFF image library

If you download the TIFF library from www.libtiff.org, you will have to spend a little time dealing with make files; however, my sample project already has the VC++ project files set up. Using the project files provided in the sample means that you may have to make modifications when new versions of the library are released. The library was written in C and contains all the code necessary to encode and decode TIFF image files. For an application programmer, all that is necessary is to call the appropriate functions from the library.

So, before working with the sample application, open the provided project files for libtiff and tiff2pdf and build them: first libtiff, and then tiff2pdf. For the libtiff project, build both the debug and release mode versions. The debug build produces libtiffd.lib and the release build produces libtiff.lib. For the tiff2pdf project, you may build either the debug or the release version, but there is no need to build both. Once these projects have been built, move the tiff2pdf.exe file from its release folder to the application folder. The application process will pull in the files as needed. Here is the folder structure for the libraries and application:

If you build from the makefiles provided by www.libtiff.org, just remember to build a debug static library called “libtiffd.lib” and a release static library called “libtiff.lib” and place those libraries in the “libs” folder (“libs” is on the same level as VC6).

Loading a TIFF image into a device independent bitmap

The logic necessary to load a TIFF image from a file into a DIB is trivial due to the presence of the TIFFReadRGBAImage library function. This function does the work of converting the underlying file format into a rectangular RGBA block of data. It’s tempting to say, okay we’re done. But that would be selling the library short. Another property of TIFF files is that they can contain more than one image, so a single TIFF file can actually behave like an archive for numerous images. Typically, this may be used when someone has several related images (for example, blueprints showing different angles of a single structure). In that case, it might be more convenient to save them all to a single TIFF file. If you ignore the directory structure of a TIFF file and simply open the file and use the functions to read the data, all that will happen is you get the first image. In most cases, the first image is the only image, but sometimes there are more. The following outline describes the process of reading the first image from the image file. The project contains more code for handling cases easily where a TIFF image file contains multiple images.

  1. Use the “TIFFOpen” call, passing in a filename and io mode.
  2. Use the “TIFFGetField” function to read the image height and width tags, and then allocate the required space (WxHx4).
  3. Use “TIFFReadRGBAImage” to copy the actual pixel data from the file to the space you just allocated.
  4. Copy the raster data to a Device Independent Bitmap (DIB), using care to maintain the picture layout.
  5. Delete the allocated space for the pixel data.
  6. Call “TIFFClose” to free resources allocated in the library.
  7. Draw the DIB to the screen.

The following code sample implements the approach outlined above:

void TIFFTestDoc::OnFileOpen()
{
    CString file_types =
       "TIFF Files(*.tiff,*.tif)|*.tif;*.tiff;*.tif;||";

      // create the file open dialog
    CFileDialog dlg(TRUE,
                    NULL,
                    NULL,
                    OFN_HIDEREADONLY | OFN_EXPLORER,
                    (LPCTSTR)file_types,NULL);

       // show the file open dialog
    if (dlg.DoModal() == IDOK)
    {
         // get the selected file from the file-open dialog
        m_filename = dlg.GetPathName();

        // 1) Opening the file
        TIFF * tiff = TIFFOpen((char*)(LPCTSTR)m_filename,"r");
        if (tiff)
        {

            int w=0, h=0;
            // 2)
            // Get the width and height of the image

            TIFFGetField(tiff,TIFFTAG_IMAGEWIDTH, &w);

            TIFFGetField(tiff,TIFFTAG_IMAGELENGTH, &h);

            if ((w > 0) && (h > 0))
            {
              // allocate space for the image

              uint32 * raster =
                  (uint32*)GlobalAlloc(GMEM_FIXED,
                                       (w * h * sizeof (uint32)));

              if (raster)
              {
                // creating DIBSection object that encapsulates
                // DIB functionality

                if (m_dib)
                    delete m_dib;

                 m_dib = new DIBSection;

                 if (m_dib)
                 {
                     m_dib->Create(w,h,32);

                     uint32 dibwidth = m_dib->GetTotalWidth();

                     // 3)
                     // copy all the pixel data from the file into
                     // allocated space

                     if (TIFFReadRGBAImage(tiff, w, h, raster, 0))
                     {
                        // 4)
                        // its tempting to copy straight to the DIB,
                        // however the DIB has an alignment
                        // restriction that is not applicable to
                        // tiff files...so they may have different
                        // widths

                        unsigned long * dest =
                             (unsigned long *)m_dib->GetBits();

                        unsigned long * src =
                             (unsigned long *)raster;

                        for (int row = 0; row < h; row++){

                           void * ptr_dest = dest + row * dibwidth;
                           void * ptr_src = src + row * w;
                           memcpy(ptr_dest,ptr_src,w*sizeof(int));
                        }
                        SetTitle(m_filename);
                        UpdateAllViews(NULL);
                     }
                  }
                  // 5)
                  // free the temporary pixel storage space
                  GlobalFree(raster);
               }
            }
            // 6)
            // notify the TIFF library that we are done
            // with this puppy
            TIFFClose(tiff);
        }
    }
}

void TIFFTestView::OnDraw(CDC* pDC)
{
    TIFFTestDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    DIBSection * dib = pDoc->GetDIB();

    if (pDC->IsPrinting())
    {}
    else
    {
        if (dib)
        {
            CClientDC dc(this);
            CPoint sp = GetScrollPosition();
            dib->Draw(&dc,sp.x,sp.y);
        }
    }
}

Writing DIBs to TIFF image files

Saving a DIB image to a TIFF file is essentially the reverse of opening an image file. The following code snippet provides a simplistic implementation of the process.

int SaveDIB2TIFF(const char * output_file, DIBSection& dib)
{
    int result = 0;

        // get the image information from the provided DIB
        UINT32 w = dib.Width();
        UINT32 h = dib.Height();
        UINT32 total_width = dib.GetTotalWidth();
        UINT32 bitcount = dib.GetBitCount();
        UINT32 bytecount = bitcount / 8;

        if (dib.IsCreated() && (w > 0) && (h > 0))
        {
            double image_gamma = TIFF_GAMMA;

            TIFF * tif;

            // open the output TIFF image for writing
            if ((tif = TIFFOpen(output_file, "w")) == NULL)
            {
                return result;
        }

        // set up the image tags
        TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
        TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
        TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
        TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
        TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
        TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1);
        TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
        TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
        TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);

        unsigned char * psrc = (unsigned char *)dib.GetBits();
        unsigned char * pdst = new unsigned char[(w * 3)];

        UINT32 src_index;
        UINT32 dst_index;

        // now go line by line to write out the image data
        for (int row = 0; row < h; row++ ){

            // initialize the scan line to zero
            memset(pdst,0,(size_t)(w * 3));

            // moving the data from the dib to a row structure that
            // can be used by the tiff library
            for (int col = 0; col < w; col++){
                src_index = (h - row - 1) * total_width * bytecount
                                          + col * bytecount;
                dst_index = col * 3;
                pdst[dst_index++] = psrc[src_index+2];
                pdst[dst_index++] = psrc[src_index+1];
                pdst[dst_index] = psrc[src_index];
                result++;
            }

            // now actually write the row data
            TIFFWriteScanline(tif, pdst, row, 0);
        }

        TIFFClose(tif);
    }

    return result;
}

Converting TIFF images to PDF (portable document format) files

The TIFF library comes with a convenient tool for converting TIFF images into PDF format. Because we now have the ability to save a DIB image as a TIFF file we can essentially convert any image into a PDF file. This fantastic capability is accomplished by another C function provided with the TIFF library called tiff2pdf. For the sample project, I choose to build the tiff2pdf function into a separate executable and launch the executable from the application.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read