view libinterp/dldfcn/__tiff__.cc @ 31103:76b21bed2920

Undocumented and unimplemented tags are explicilty mentioned and handled * __tiff__.cc: Comlpex unimplemented tags now give an error message and tags that are undocumented are explicitly mentioned.
author magedrifaat <magedrifaat@gmail.com>
date Sun, 03 Jul 2022 22:51:08 +0200
parents d6ecf0e8838b
children b5d59c115e52
line wrap: on
line source

#include <string>
#include <iostream>

#include "defun-dld.h"
#include "ov.h"
#include "ovl.h"
#include "error.h"

#include <tiffio.h>

// TODO(maged): Tidy up the formatting to be consistant with octave

namespace octve
{
  // Error if status is not 1 (success status for TIFFGetField)
  void
  validate_tiff_get_field (bool status, void *p_to_free=NULL)
  {
      if (status != 1)
        {
          if (p_to_free != NULL)
            _TIFFfree (p_to_free);
          error ("Failed to read tag");
        }
  }

  // Convert memory buffer into suitable octave values
  // depending on tag_datatype
  octave_value_list
  interpret_data (void *data, uint32_t count, TIFFDataType tag_datatype)
  {
    // TODO(maged): Find the correct way fo returning multivalues
    octave_value_list ovl_data;
    dim_vector arr_dims (1, count);

    switch (tag_datatype)
      {
      case TIFF_BYTE:
      case TIFF_UNDEFINED:
        {
          uint8NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((uint8_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_ASCII:
        {
          ovl_data(0) = *(char **)data;
          break;
        }
      case TIFF_SHORT:
        {
          uint16NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((uint16_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_LONG:
        {
          uint32NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((uint32_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_LONG8:
        {
          uint64NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((uint64_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_RATIONAL:
        {
          NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i+=2)
            {
              arr(i / 2) = (float)((uint32_t *)data)[i] 
                            / (float)((uint32_t *)data)[i+1];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_SBYTE:
        {
          int8NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((int8_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_SSHORT:
        {
          int16NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((int16_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_SLONG:
        {
          int32NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((int32_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_SLONG8:
        {
          int64NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((int64_t *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_FLOAT:
        {
          NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
            {
              arr(i) = ((float *)data)[i];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_DOUBLE:
        {
          NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i++)
          {
              arr(i) = ((double *)data)[i];
          }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_SRATIONAL:
        {
          NDArray arr (arr_dims);
          for (uint32_t i = 0; i < count; i+=2)
            {
              arr(i / 2) = (float)((int32_t *)data)[i] 
                            / (float)((int32_t *)data)[i+1];
            }
          ovl_data(0) = arr;
          break;
        }
      case TIFF_IFD:
      case TIFF_IFD8:
        // TODO(maged): implement IFD datatype?
        error ("Unimplemented IFFD data type");
        break;
      default:
        error ("Unsupported tag data type");
      }

    return ovl_data;
  }

  octave_value_list
  get_scalar_field_data (TIFF *tif, const TIFFField *fip)
  {
    octave_value_list tag_data_ovl;
    uint32_t tag_id = TIFFFieldTag (fip);

    // TIFFFieldReadCount returns VARIABLE for some scalar tags
    // (e.g. Compression) But TIFFFieldPassCount seems consistent
    // Since scalar tags are the last to be handled, any tag that
    // require a count to be passed is an unsupported tag.
    if (TIFFFieldPassCount (fip))
      error ("Unsupported tag");
    
    // TODO(maged): test this function vs actual data type size
    int type_size = TIFFDataWidth (TIFFFieldDataType (fip));
    void *data = _TIFFmalloc (type_size);
    validate_tiff_get_field (TIFFGetField (tif, tag_id, data), data);
    tag_data_ovl = interpret_data (data, 1, TIFFFieldDataType (fip));
    _TIFFfree (data);

    return tag_data_ovl;
  }

  octave_value_list
  get_array_field_data (TIFF *tif, const TIFFField *fip, uint32_t array_size)
  {
    void *data;
    validate_tiff_get_field (TIFFGetField (tif, TIFFFieldTag (fip), &data));
      
    return interpret_data (data, array_size, TIFFFieldDataType (fip));
  }

  octave_value_list
  get_field_data (TIFF *tif, const TIFFField *fip)
  {
    octave_value_list tag_data_ovl;
    uint32_t tag_id = TIFFFieldTag (fip);

    // TODO(maged): find/create images to test the special tags
    switch (tag_id)
      {
      case TIFFTAG_STRIPBYTECOUNTS:
      case TIFFTAG_STRIPOFFSETS:
        tag_data_ovl = get_array_field_data (tif, fip, 
                                            TIFFNumberOfStrips (tif));
        break;
      case TIFFTAG_TILEBYTECOUNTS:
      case TIFFTAG_TILEOFFSETS:
        tag_data_ovl = get_array_field_data (tif, fip, TIFFNumberOfTiles (tif));
        break;
      case TIFFTAG_YCBCRCOEFFICIENTS:
        tag_data_ovl = get_array_field_data (tif, fip, 3);
        break;
      case TIFFTAG_REFERENCEBLACKWHITE:
        tag_data_ovl = get_array_field_data (tif, fip, 6);
        break;
      case TIFFTAG_COLORMAP:
        {
          uint16_t bits_per_sample;
          if (! TIFFGetField (tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample))
            error ("Failed to obtain the bit depth");
                
          if (bits_per_sample > 24)
            error ("Too high bit depth for a palette image");

          uint32_t count = 1 << bits_per_sample;
          uint16_t *red, *green, *blue;
          validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_COLORMAP,
                                                &red, &green, &blue));
          tag_data_ovl(0) 
            = octave_value (interpret_data (red, count,
                                            TIFFFieldDataType (fip)));
          tag_data_ovl(1) 
            = octave_value (interpret_data (green, count,
                                            TIFFFieldDataType (fip)));
          tag_data_ovl(2) 
            = octave_value (interpret_data (blue, count,
                                            TIFFFieldDataType (fip)));
          
          break;
        }
      case TIFFTAG_TRANSFERFUNCTION:
        {
          uint16_t samples_per_pixel;
          if (! TIFFGetField (tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel))
            error ("Failed to obtain the number of samples per pixel");

          uint16_t bits_per_sample;
          if (! TIFFGetField (tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample))
            error ("Failed to obtain the number of samples per pixel");

          uint32_t count = 1 << bits_per_sample;
          uint16_t *ch1, *ch2, *ch3;
          if (samples_per_pixel == 1)
            {
              validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_COLORMAP, &ch1));
              tag_data_ovl(0) 
                = octave_value (interpret_data (ch1, count,
                                                TIFFFieldDataType (fip)));
            }
          else
            {
              validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_COLORMAP,
                                                    &ch1, &ch2, &ch3));
              tag_data_ovl(0)
                = octave_value (interpret_data (ch1, count,
                                                TIFFFieldDataType (fip)));
              tag_data_ovl(1)
                = octave_value (interpret_data (ch2, count,
                                                TIFFFieldDataType (fip)));
              tag_data_ovl(2)
                = octave_value (interpret_data (ch3, count,
                                                TIFFFieldDataType (fip)));
            }
          break;
        }
      case TIFFTAG_PAGENUMBER:
      case TIFFTAG_HALFTONEHINTS:
      case TIFFTAG_DOTRANGE:
      case TIFFTAG_YCBCRSUBSAMPLING:
        {
          uint16_t tag_part1, tag_part2;
          validate_tiff_get_field (TIFFGetField (tif, tag_id,
                                                &tag_part1, &tag_part2));
          tag_data_ovl(0)
            = octave_value (interpret_data (&tag_part1, 1,
                                            TIFFFieldDataType (fip)));
          tag_data_ovl(1)
            = octave_value (interpret_data (&tag_part2, 1,
                                            TIFFFieldDataType (fip)));
          break;
        }
      case TIFFTAG_SUBIFD:
        {
          uint16_t count;
          uint64_t *offsets;
          validate_tiff_get_field (TIFFGetField (tif, tag_id, &count, &offsets));
          tag_data_ovl = interpret_data (offsets, count, TIFFFieldDataType (fip));
          break;
        }
      case TIFFTAG_EXTRASAMPLES:
        {
          uint16_t count;
          uint16_t *types;
          validate_tiff_get_field (TIFFGetField (tif, tag_id, &count, &types));
          tag_data_ovl = interpret_data (types, count, TIFFFieldDataType (fip));
          break;
        }
      // TODO(maged): These tags are more complex to implement
      //  will be implemented and tested later.
      case TIFFTAG_XMLPACKET:
      case TIFFTAG_RICHTIFFIPTC:
      case TIFFTAG_PHOTOSHOP:
      case TIFFTAG_ICCPROFILE:
        {
          error ("Complex Tags not implemented");
          break;
        }
      // Those two tags are not mentioned in the LibTIFF documentation
      // but are handled correctly by the library
      case TIFFTAG_ZIPQUALITY:
      case TIFFTAG_SGILOGDATAFMT:
        {
          tag_data_ovl = get_scalar_field_data (tif, fip);
          break;
        }
      default:
        tag_data_ovl = get_scalar_field_data (tif, fip);
    }
    
    return tag_data_ovl;
  }

  DEFUN_DLD (__open_tiff__, args, nargout,
            "Open a Tiff file and return its handle")
  {
    int nargin = args.length ();

    if (nargin == 0 || nargin > 2)
      {
        // TODO(maged): return invalid object instead??
        error ("No filename supplied\n");
      }

    std::string filename = args (0).string_value ();
    std::string mode = "r";

    // TODO(maged): check valid mode
    if (nargin == 2)
      mode = args (1).string_value ();
    
    // TODO(maged): Look into unwind action
    TIFF *tif = TIFFOpen (filename.c_str (), mode.c_str ());
    
    if (! tif)
      error ("Failed to open Tiff file\n");

    // TODO(maged): use inheritance of octave_base_value instead
    octave_value tiff_ov = octave_value ((uint64_t)tif);
    return octave_value_list (tiff_ov);
  }


  DEFUN_DLD (__close_tiff__, args, nargout,
            "Close a tiff file")
  {
    int nargin = args.length ();

    if (nargin == 0)
      error ("No handle provided\n");
    
    TIFF *tif = (TIFF *)(args (0).uint64_value ());
    TIFFClose (tif);

    return octave_value_list ();
  }


  DEFUN_DLD (__tiff_get_tag__, args, nargout,
            "Get the value of a tag from a tiff image")
  {
      int nargin = args.length ();

      if (nargin == 0)
        error ("No handle provided\n");
      
      if (nargin < 2)
        error ("No tag name provided\n");
      
      TIFF *tif = (TIFF *)(args (0).uint64_value ());

      uint32_t tag_id;
      const TIFFField *fip;
      if (args (1).type_name () == "string")
        {
          std::string tagName = args (1).string_value ();
          fip = TIFFFieldWithName (tif, tagName.c_str ());
          if (! fip)
            error ("Tiff tag not found");
          
          tag_id = TIFFFieldTag (fip);
        }
      else
        {
          tag_id = args (1).int_value ();
          fip = TIFFFieldWithTag (tif, tag_id);
          // TODO(maged): Handle other types of errors (e.g. unsupported tags)
          if (! fip)
            error ("Tiff tag not found");
        }


      octave_value_list tag_data_ovl = get_field_data (tif, fip);

      return tag_data_ovl;
  }
}