classdef Tiff
  
  ## -*- texinfo -*-
  ## @deftypefn  {} {@var{t} =} Tiff (@var{filename})
  ## @deftypefnx {} {@var{t} =} Tiff (@var{filename}, @var{mode})
  ## Open a TIFF image file
  ##
  ## Open the TIFF image file @var{filename} in file mode @var{mode}. If
  ## @var{mode} is not provided, the file is opened for reading. Both
  ## @var{filename} and @var{mode} must be strings.
  ##
  ## The @var{mode} parameter can be one of the following options:
  ##
  ## @table @asis
  ## @item @qcode{"r"}
  ## Open file for reading.
  ##
  ## @item @qcode{"w"}
  ## Open file for writing and discard existing data.
  ##
  ## @item @qcode{"a"}
  ## Open or create file for appending.
  ##
  ## @item @qcode{"w8"}
  ## Open BigTIFF file for writing and discard existing data.
  ##
  ## @item @qcode{"r+"}
  ## Open exisiting file for both reading and writing.
  ##
  ## @end table
  ##
  ## The return value @var{t} is an object of the Tiff class.
  ## @seealso{imread, imwrite}
  ## @end deftypefn

  properties (Constant = true)
    TagID = __tiff_make_tagid__ ();

    Compression = struct (
      "None", 1,
      "CCITTRLE", 2,
      "CCITTFax3", 3,
      "CCITTFax4", 4,
      "LZW", 5,
      "OJPEG", 6,
      "JPEG", 7,
      "AdobeDeflate", 8,
      "Next", 32766,
      "CCITTRLEW", 32771,
      "PackBits", 32773,
      "Thunderscan", 32809,
      "IT8CTPad", 32895,
      "IT8LW", 32896,
      "IT8MP", 32897,
      "IT8BL", 32898,
      "PixarFilm", 32908,
      "PixarLog", 32909,
      "Deflate", 32946,
      "DCS", 32947,
      "JBIG", 34661,
      "SGILog", 34676,
      "SGILog24", 34677,
      # Not implemented in LibTIFF
      "JPEG2000", 34712,
      "JPEG2000_SVS_YCbCr", 33003,
      "JPEG2000_SVS_RGB", 33005
    );

    ExtraSamples = struct (
      "Unspecified", 0,
      "AssociatedAlpha", 1,
      "UnassociatedAlpha", 2
    );

    Group3Options = struct (
      "Encoding2D", 1,
      "Uncompressed", 2,
      "FillBits", 4
    );

    InkSet = struct (
      "CMYK", 1,
      "MultiInk", 2
    );

    JPEGColorMode = struct (
      "Raw", 0,
      "RGB", 1
    );

    Orientation = struct (
      "TopLeft", 1,
      "TopRight", 2,
      "BottomRight", 3,
      "BottomLeft", 4,
      "LeftTop", 5,
      "RightTop", 6,
      "RightBottom", 7,
      "LeftBottom", 8
    );

    Photometric = struct (
      "MinIsWhite", 0,
      "MinIsBlack", 1,
      "RGB", 2,
      "Palette", 3,
      "Mask", 4,
      "Separated", 5,
      "YCbCr", 6,
      "CIELab", 8,
      "ICCLab", 9,
      "ITULab", 10,
      "LogL", 32844,
      "LogLUV", 32845,
      "CFA", 32803,
      "LinearRaw", 34892
    );

    PlanarConfiguration = struct (
      "Chunky", 1,
      "Separate", 2
    );

    ResolutionUnit = struct (
      "None", 1,
      "Inch", 2,
      "Centimeter", 3
    );

    SampleFormat = struct (
      "UInt", 1,
      "Int", 2,
      "IEEEFP", 3
    );

    SGILogDataFmt = struct (
      "Float", 0,
      "Bits8", 3
    );

    SubFileType = struct (
      "Default", 0,
      "ReducedImage", 1,
      "Page", 2,
      "Mask", 4
    );

    Thresholding = struct (
      "BiLevel", 1,
      "HalfTone", 2,
      "ErrorDiffuse", 3
    );

    YCbCrPositioning = struct (
      "Centered", 1,
      "Cosited", 2
    );
  endproperties

  properties (Access = private)
    tiff_handle;
  endproperties

  methods
    function t = Tiff (filename, mode="r")
      if (nargin == 0 || nargin > 2)
        print_usage("Tiff");
      endif

      t.tiff_handle = __open_tiff__ (filename, mode);
    endfunction

    function close (t)
    
      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.close ()
      ## Close the TIFF file opened by this object.
      ## @end deftypefn

      __close_tiff__ (t.tiff_handle);
    endfunction

    function tag = getTag (t, tag_name)

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{tag} =} Tiff.getTag (@var{tag_id})
      ## @deftypefnx  {} {@var{tag} =} Tiff.getTag (@var{tag_name})
      ## 
      ## Get the value of a tag in the image identified by either its ID
      ## number @var{tag_id} or its name @var{tag_name}.
      ##
      ## To get the ID of a known tag use the @code{Tiff.TagID} structure.
      ##
      ## An example for getting the image width tag:
      ##
      ## @example
      ## t = Tiff ("example.tif");
      ## width = t.getTag (256);
      ## width = t.getTag (Tiff.TagID.ImageWidth);
      ## width = t.getTag ("ImageWidth");
      ## @end example
      ##
      ## @seealso{setTag, getTagNames}
      ## @end deftypefn

      tag = __tiff_get_tag__ (t.tiff_handle, tag_name);
    endfunction

    function setTag (t, varargin)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.setTag (@var{tag_id}, @var{tag_value})
      ## @deftypefnx  {} Tiff.setTag (@var{tag_name}, @var{tag_value})
      ## @deftypefnx  {} Tiff.setTag (@var{tag_struct})
      ##
      ## Set the value of tag identified by its ID number @var{tag_id} or
      ## by its name @var{tag_name}, or set the value of a list of tags
      ## arranged in a struct @var{tag_struct}.
      ##
      ## To get the ID of a tag use the @code{Tiff.TagID} structure.
      ##
      ## An example of setting the image width:
      ##
      ## @example
      ## t = Tiff ("test.tif", "w");
      ## t.setTag (256, 100);
      ## t.setTag (Tiff.TagID.ImageWidth, 100);
      ## t.setTag ("ImageWidth", 100);
      ## @end example
      ##
      ## An example of setting multiple tags:
      ##
      ## @example
      ## t = Tiff ("test.tif", "w");
      ## tags = struct ("ImageWidth", 100, "ImageLength", 50);
      ## t.setTag (tags);
      ## @end example
      ##
      ## @seealso{getTag, getTagNames}
      ## @end deftypefn

      __tiff_set_tag__ (t.tiff_handle, varargin{:});
    endfunction

    function argout = read (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{img} =} Tiff.read ()
      ##
      ## Reads the image data of the current image directory.
      ##
      ## @seealso{getTag, setTag, write}
      ## @end deftypefn

      argout = __tiff_read__ (t.tiff_handle);
    endfunction

    function stripData = readEncodedStrip (t, stripNumber)

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{strip} =} Tiff.readEncodedStrip (@var{stripNumber})
      ##
      ## Read the data of the strip with index @var{stripNumber} from the
      ## current image directory. @var{stripNumber} index is 1-based.
      ##
      ## Stips are numbered from top to bottom. Images with separate planes
      ## will have all the strips of the first channel first then all the
      ## strips of the second channel and so on.
      ##
      ## The strip data is returned in @var{strip}. The boundary strips in
      ## image might have different dimensions than the rest of the strips
      ## depending on the ImageLength and the RowsPerStrip tags.
      ##
      ## @seealso{read, readEncodedTile}
      ## @end deftypefn

      stripData = __tiff_read_encoded_strip__ (t.tiff_handle, stripNumber);
    endfunction

    function tileData = readEncodedTile (t, tileNumber)

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{tile} =} Tiff.readEncodedTile (@var{tileNumber})
      ##
      ## Read the data of the tile with index @var{tileNumber} from the
      ## current image directory. @var{tileNumber} index is 1-based.
      ##
      ## Tiles are numbered from left to right and from top to bottom. Images
      ## with separate planes will have all the tiles of the first channel
      ## first then all the tiles of the second channel and so on.
      ##
      ## The tile data is returned in @var{tile}. The boundary tiles in
      ## image might have different dimensions than the rest of the tiles
      ## depending on tags: ImageLength, ImageWidth, TileLength and TileWidth.
      ##
      ## @seealso{read, readEncodedStrip}
      ## @end deftypefn
      
      tileData = __tiff_read_encoded_tile__ (t.tiff_handle, tileNumber);
    endfunction

    function [RGB, alpha] = readRGBAImage (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} {[@var{rgb}, @var{alpha}] =} Tiff.readRGBAImage ()
      ## 
      ## Read the image data of the current image directory in RGBA mode.
      ## The image 8-bit RGB data is returned in @var{rgb} and the transprency
      ## data is returned in @var{alpha}. If the image has no transparency
      ## data then the alpha array will be filled with the value 255.
      ##
      ## If the image data is any other format than 8-bit RGB then coversion
      ## to 8-bit RGB is done internally. If you wish to read the actual image
      ## data use the @code{read} method instead.
      ##
      ## This method uses the @qcode{"Orientation"} tag in the image to
      ## correctly flip/rotate the image to match its orientation.
      ##
      ## @seealso{read, readRGBAStrip, readRGBATile}
      ## @end deftypefn

      [RGB, alpha] = __tiff_read_rgba_image__ (t.tiff_handle);
    endfunction

    function [RGB, alpha] = readRGBAStrip (t, row)

      ## -*- texinfo -*-
      ## @deftypefn  {} {[@var{rgb}, @var{alpha}] =} Tiff.readRGBAStrip (@var{row})
      ## 
      ## Read a strip of the image data that contains the given @var{row} in RGBA
      ## mode. The strip 8-bit RGB data is returned in @var{rgb} and the
      ## transprency data is returned in @var{alpha}. If the image has no
      ## transparency data then the alpha array will be filled with the
      ## value 255.
      ##
      ## Boundary strips may have different number of rows than the rest of
      ## the strips in the image depending on the @qcode{"ImageLength"} and
      ## @qcode{"RowsPerStrip"} tags.
      ##
      ## If the strip data is any other format than 8-bit RGB then coversion
      ## to 8-bit RGB is done internally. If you wish to read the actual strip
      ## data use the @code{readEncodedStrip} method instead.
      ##
      ## This method uses the @qcode{"Orientation"} tag in the image to
      ## correctly flip/rotate the strip to match its orientation.
      ##
      ## @seealso{readEncodedStrip, readRGBAImage, readRGBATile}
      ## @end deftypefn

      [RGB, alpha] = __tiff_read_rgba_strip__ (t.tiff_handle, row);
    endfunction

    function [RGB, alpha] = readRGBATile (t, row, col)

      ## -*- texinfo -*-
      ## @deftypefn  {} {[@var{rgb}, @var{alpha}] =} Tiff.readRGBATile (@var{row}, @var{col})
      ## 
      ## Read a tile of the image data that contains the given @var{row} and
      ## @var{col} in RGBA mode. The tile 8-bit RGB data is returned in @var{rgb}
      ## and the transprency data is returned in @var{alpha}. If the image has
      ## no transparency data then the alpha array will be filled with the
      ## value 255.
      ##
      ## Boundary tiles may have different number dimensions than the rest of
      ## the tiles in the image depending on @qcode{"ImageLength"},
      ## @qcode{"ImageWidth"}, @qcode{"TileLength"} and @qcode{"TileWidth"}  tags.
      ##
      ## If the tile data is any other format than 8-bit RGB then coversion to
      ## 8-bit RGB is done internally. If you wish to read the actual tile
      ## data use the @code{readEncodedTile} method instead.
      ##
      ## This method uses the @qcode{"Orientation"} tag in the image to
      ## correctly flip/rotate the tile to match its orientation.
      ##
      ## @seealso{readEncodedTile, readRGBAImage, readRGBAStrip}
      ## @end deftypefn

      [RGB, alpha] = __tiff_read_rgba_tile__ (t.tiff_handle, row, col);
    endfunction

    function write (t, imageData)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.write (@var{imageData})
      ##
      ## Write the image pixel data @var{imageData} to the Tiff file.
      ##
      ## The class as well as the dimensions of the array @var{imageData}
      ## must match the image tag values of @qcode{"ImageLength"},
      ## @qcode{"ImageWidth"}, @qcode{"SamplesPerPixel"}, @qcode{"BitsPerSample"}
      ## and @qcode{"Photometric"} tags.
      ##
      ## For example, for a 10x10 8-bit RGB image, the @var{imageData} array
      ## must be of class @qcode{"uint8"} and must have dimensions 10x10x3.
      ##
      ## @seealso{writeEncodedStrip, writeEncodedTile, read}
      ## @end deftypefn

      __tiff_write__ (t.tiff_handle, imageData);
    endfunction

    function writeEncodedStrip (t, stripNumber, imageData)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.writeEncodedStrip (@var{stripNumber}, @var{stripData})
      ##
      ## Write the pixel data @var{stripData} to the strip of index
      ## @var{stripnumber}. The strips are numbered from top to bottom. If the
      ## image is configured for separate planes, then all the strips of the
      ## first channel are numbered first then all the strips of the second
      ## channel and so on.
      ##
      ## The class of the array @var{stripData} must match the value of the
      ## @qcode{"BitsPerSample"} tag.
      ##
      ## If the array @var{stripData} has less elements than needed, it will
      ## be silently padded with zeroes. If it has more elements than needed
      ## the additional data will be truncated and a warning will be issued.
      ##
      ## @seealso{write, writeEncodedTile, read}
      ## @end deftypefn

      __tiff_write_encoded_strip__ (t.tiff_handle, stripNumber, imageData);
    endfunction

    function writeEncodedTile (t, tileNumber, imageData)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.writeEncodedTile (@var{tileNumber}, @var{tileData})
      ##
      ## Write the pixel data @var{tileData} to the tile of index @var{tileNumber}.
      ## The tiles are numbered from left to right and from top to bottom. If
      ## the image is configured for separate planes, then all the tiles of
      ## the first channel are numbered first then all the tiles of the second
      ## channel and so on.
      ##
      ## The class of the array @var{tileData} must match the value of the
      ## @qcode{"BitsPerSample"} tag.
      ##
      ## If the array @var{tileData} has less elements than needed, it will
      ## be silently padded with zeroes. If it has more elements than needed
      ## the additional data will be truncated and a warning will be issued.
      ##
      ## @seealso{writeEncodedStrip, write, read}
      ## @end deftypefn

      __tiff_write_encoded_tile__ (t.tiff_handle, tileNumber, imageData);
    endfunction

    function tf = isTiled (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{tf} =} Tiff.isTiled ()
      ##
      ## Returns whether or not the image in the current directory is tiled.
      ##
      ## @seealso{numberOfTiles, numberOfStrips}
      ## @end deftypefn

      tf = __tiff_is_tiled__ (t.tiff_handle);
    endfunction

    function numStrips = numberOfStrips (t)
    
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{numStrips} =} Tiff.numberOfStrips ()
      ##
      ## Returns the number of strips in the image of the current directory.
      ##
      ## @seealso{numberOfTiles, isTiled} 
      ## @end deftypefn

      numStrips = __tiff_number_of_strips__ (t.tiff_handle);
    endfunction

    function numTiles = numberOfTiles (t)
    
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{numTiles} =} Tiff.numberOfTiles ()
      ##
      ## Returns the number of tiles in the image of the current directory.
      ##
      ## @seealso{numberOfStrips, isTiled} 
      ## @end deftypefn
      
      numTiles = __tiff_number_of_tiles__ (t.tiff_handle);
    endfunction

    function numDirs = numberOfDirectories (t)
    
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{numDirs} =} Tiff.numberOfDirectories ()
      ##
      ## Returns the number of directories in the image.
      ##
      ## @seealso{numberOfTiles, numberOfStrips} 
      ## @end deftypefn
      
      numDirs = __tiff_number_of_directories__ (t.tiff_handle);
    endfunction

    function stripNumber = computeStrip (t, varargin)
      
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{stripNumber} =} Tiff.computeStrip (@var{row})
      ## @deftypefnx  {} {@var{stripNumber} =} Tiff.computeStrip (@var{row}, @var{plane})
      ##
      ## Return the strip index @var{stripNumber} of the strip containing the
      ## given @var{row}. If the image has separate planes, then an aditional
      ## parameter @var{plane} is required to get the strip containing the @var{row}
      ## in the specific plane.
      ##
      ## Stips are numbered from top to bottom. Images with separate planes
      ## will have all the strips of the first channel first then all the
      ## strips of the second channel and so on.
      ##
      ## @seealso{computeTile, numberOfStrips}
      ## @end deftypefn

      stripNumber = __tiff_compute_strip__ (t.tiff_handle, varargin{:});
    endfunction

    function tileNumber = computeTile (t, varargin)
      
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{tileNumber} =} Tiff.computeTile ([@var{row}, @var{col}])
      ## @deftypefnx  {} {@var{tileNumber} =} Tiff.computeTile ([@var{row}, @var{col}], @var{plane})
      ##
      ## Return the tile index @var{tileNumber} of the tile containing the
      ## given @var{row} and @var{col}. If the image has separate planes, then
      ## an aditional parameter @var{plane} is required to get the tile index
      ## containing the @var{row} and @var{col} in the specific plane.
      ##
      ## Tiles are numbered from left to right and from top to bottom. Images
      ## with separate planes will have all the tiles of the first channel
      ## first then all the tiles of the second channel and so on.
      ##
      ## @seealso{computeStrip, numberOfTiles}
      ## @end deftypefn

      tileNumber = __tiff_compute_tile__ (t.tiff_handle, varargin{:});
    endfunction

    function stripLength = getDefaultStripLength (t)
      
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{stripLength} =} Tiff.getDefaultStripLength ()
      ##
      ## Returns the number of rows @var{stripLength} of a reasonably sized
      ## strip using the image dimensions and configuration tags.
      ##
      ## This value can be used to set the @qcode{"RowsPerStrip"} tag of a new
      ## image but the @qcode{"ImageLength"}, @qcode{"ImageWidth"},
      ## @qcode{"SamplesPerPixel"}, @qcode{"BitsPerSample"}, and optionally
      ## @qcode{"Compression"} tags must be set first for the number of rows
      ## to be calculated from the correct image dimensions and configuration.
      ##
      ## @seealso{computeStrip, computeTile}
      ## @end deftypefn

      stripLength = __tiff_default_strip_length__ (t.tiff_handle);
    endfunction

    function tf = isBigEndian (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{tf} =} Tiff.isBigEndian ()
      ##
      ## Returns whether the current image is of big-endian or little-endian
      ## byte order.
      ##
      ## @seealso{isTiled}
      ## @end deftypefn

      tf = __tiff_is_big_endian__ (t.tiff_handle);
    endfunction

    function dirNum = currentDirectory (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{dirNum} =} Tiff.currentDirectory ()
      ##
      ## Get the 1-based index of the current directory in the file.
      ##
      ## @seealso{lastDirectory, nextDirectory}
      ## @end deftypefn

      dirNum = __tiff_current_directory__ (t.tiff_handle);
    endfunction

    function isLast = lastDirectory (t)
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{isLast} =} Tiff.lastDirectory ()
      ##
      ## Returns whether the current directory is the last directory in the
      ## image file.
      ##
      ## @seealso{currentDirectory, nextDirectory}
      ## @end deftypefn
      isLast = __tiff_last_directory__ (t.tiff_handle);
    endfunction

    function nextDirectory (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.nextDirectory ()
      ##
      ## Switch to the next directory in the image file.
      ##
      ## @seealso{currentDirectory, lastDirectory}
      ## @end deftypefn

      __tiff_next_directory__ (t.tiff_handle);
    endfunction

    function setDirectory (t, dirNum)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.setDirectory (@var{dirNum})
      ##
      ## Switches to the directory with index @var{dirNum}, where the indexing
      ## is 1-based (starts counting at 1).
      ##
      ## @seealso{nextDirectory, currentDirectory}
      ## @end deftypefn

      __tiff_set_directory__ (t.tiff_handle, dirNum);
    endfunction

    function writeDirectory (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.writeDirectory ()
      ##
      ## Write the data of the current directory to file and starts a new
      ## directory for writing.
      ##
      ## Note that this is not needed for writing single-directory images
      ## as it will be called internally when calling the @code{close} method.
      ##
      ## @seealso{setDirectory, rewriteDirectory}
      ## @end deftypefn
    
      __tiff_write_directory__ (t.tiff_handle);
    endfunction

    function rewriteDirectory (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.rewriteDirectory ()
      ##
      ## Rewrite the modifications of the current directory to file and start
      ## a new directory for writing.
      ##
      ## @seealso{writeDirectory, setDirectory}
      ## @end deftypefn

      __tiff_rewrite_directory__ (t.tiff_handle);
    endfunction

    function setSubDirectory (t, offset)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.setSubDirectory (@var{offset})
      ##
      ## Sets the active directory to a sub-directory of the current directory
      ## identified by its @var{offset}.
      ##
      ## Sub-directory offsets can be obtained from the @qcode{"SubIFD"} tag of
      ## a directory. Switching to a sub-directory can only be done when the
      ## parent directory is the current directory.
      ##
      ## @seealso{setDirectory, currentDirecory}
      ## @end deftypefn

      __tiff_set_sub_directory__ (t.tiff_handle, offset);
    endfunction

    function offset = getDirectoryOffset (t)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.getDirectoryOffset ()
      ##
      ## Gets the offset in bytes of the current directory relative to the
      ## start of the Tiff file.
      ##
      ## @seealso{setSubDirectory, setDirectory}
      ## @end deftypefn

      offset = __tiff_get_directory_offset__ (t.tiff_handle);
    endfunction
  endmethods

  methods (Static = true)
    function versionString = getVersion()

      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{versionString} =} Tiff.getVersion ()
      ##
      ## Get a string with the version information of the LibTIFF library
      ## used internally by this interface.
      ##
      ## @end deftypefn

      versionString = __tiff_version__ ();
    endfunction

    function tagNames = getTagNames ()
    
      ## -*- texinfo -*-
      ## @deftypefn  {} {@var{tagNames} =} Tiff.getTagNames ()
      ##
      ## Get a cell array of all the tag names supported.
      ##
      ## @seealso{Tiff.TagID, getTag, setTag}
      ## @end deftypefn

      tagNames = fieldnames (Tiff.TagID);
    endfunction

    function setLibTIFFErrorsEnabled(state)

      ## -*- texinfo -*-
      ## @deftypefn  {} Tiff.setLibTIFFErrorsEnabled (@var{state})
      ##
      ## Enable or disable relaying the internal errors and warnings
      ## issued by LibTIFF (enabled by default).
      ##
      ## @end deftypefn

      __tiff_set_errors_enabled__ (state);
    endfunction
  endmethods
endclassdef

%!function file_wrapper (fn)
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    fn (filename);
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect
%!endfunction

%!function verify_data (filename, data, ex_size)
%!    img = Tiff (filename, "r");
%!    data2 = img.read ();
%!    assert (size (data2), ex_size);
%!    assert (data2, resize (data, size (data2)));
%!    img.close ();
%!endfunction

## test setTag and getTag for scalar tags
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, "ImageWidth", 2);
%!    val = getTag (img, "ImageWidth");
%!    assert (val, 2);
%!    assert (class (val), "double");
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure setTag and getTag unknown tag
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    fail ("getTag (img, \"ImageWidt\")");
%!    fail ("getTag (img, 0xf423f)");
%!    fail ("setTag (img, \"ImageWidt\", 2)");
%!    fail ("setTag (img, 0xf423f, 2)");
%!  endfunction
%!  file_wrapper (@test_fn);

## test setTag structure input
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 2, "ImageWidth", 2));
%!    assert (getTag (img, "ImageLength"), 2);
%!    assert (getTag (img, "ImageWidth"), 2);
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure setTag structure unknown tag
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, "ImageLength", 1);
%!    setTag (img, "ImageWidth", 1);
%!    fail ("setTag (img, struct (\"ImageLength\", 2, \"a\", 1, \"ImageWidth\", 2))");
%!    assert (getTag (img, "ImageLength"), 2);
%!    assert (getTag (img, "ImageWidth"), 1);
%!  endfunction
%!  file_wrapper (@test_fn);

## test setTag array field
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, "YCbCrCoefficients", [1.5, 2.5, 3.5]);
%!    data = getTag (img, "YCbCrCoefficients");
%!    assert (data, [1.5, 2.5, 3.5]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test setTag special field
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, "BitsPerSample", 8);
%!    cmap = reshape (1:768, [256, 3]) / 1024.0;
%!    setTag (img, "ColorMap", cmap);
%!    assert (getTag (img, "ColorMap"), cmap, 1e-5);
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure invalid open mode/ invalid filename
%!testif HAVE_TIFF
%!  fail ("Tiff (\"test.tif\", \"rh\")",
%!        "Invalid mode for openning Tiff file: rh");
%!  fail ("Tiff ([tempname() \".tif\"], \"r\")");

## test r+ mode modifies exisitng images
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct (
%!      "ImageLength", 1, "ImageWidth", 1,
%!      "BitsPerSample", 8
%!    ));
%!    write (img, uint8 ([210]));
%!    img.close ();
%!    img = Tiff (filename, "r+");
%!    setTag (img, "Make", "test_tag");
%!    img.rewriteDirectory ();
%!    img.close ();
%!    img = Tiff (filename);
%!    assert (img.read(), uint8 ([210]));
%!    assert (getTag (img, "Make"), "test_tag");
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn)

## test one-pixel grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 1, "ImageWidth", 1,
%!                         "BitsPerSample", 8));
%!    data = uint8 (randi (intmax ("uint8"), 1, 1));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [1, 1]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure to write to image without dimensions
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    data = uint8 (randi (intmax ("uint8"), 1, 1));
%!    fail ("writeEncodedStrip (img, 1, data)", "Failed to read image width");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test one row grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 1, "ImageWidth", 10,
%!                         "BitsPerSample", 8));
%!    data = uint8 (reshape (1:10, [1, 10]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [1, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test wrong size of row
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 1, "ImageWidth", 10,
%!                         "BitsPerSample", 8));
%!    data = uint8 (reshape (1:13, [1, 13]));
%!    fail ("writeEncodedStrip (img, 1, data)", "warning",
%!          "The image width is 10 but the input has 13 columns.");
%!    img.close ();
%!    verify_data (filename, data, [1, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test one strip grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                         "BitsPerSample", 8));
%!    data = uint8 (reshape (1:100, [10, 10]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test wrong height of strip
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                         "BitsPerSample", 8));
%!    data = uint8 (reshape (1:110, [11, 10]));
%!    fail ("writeEncodedStrip (img, 1, data)", "warning",
%!          "The strip is composed of 10 rows but the input has 11 rows.");
%!    img.close ();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test one strip RGB image chunky planes (RGBRGBRGB)
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                         "BitsPerSample", 8, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 1,
%!                         "PhotometricInterpretation", 2));
%!    data = uint8 (reshape (1:300, [10, 10, 3]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test wrong number of channels
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                         "BitsPerSample", 8, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 1,
%!                         "PhotometricInterpretation", 2));
%!    data = uint8 (reshape (1:400, [10, 10, 4]));
%!    fail ("writeEncodedStrip (img, 1, data)", "warning",
%!          "The strip is composed of 3 channels but the input has 4 channels.");
%!    img.close ();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test one strip RGB image separate planes (RRRGGGBBB)
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                         "BitsPerSample", 8, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 2,
%!                         "PhotometricInterpretation", 2));
%!    data = uint8 (reshape (1:300, [10, 10, 3]));
%!    for i = 1:3
%!      writeEncodedStrip (img, i, data(:,:,i));
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test 1-bit BiLevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test 16-bit grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16));
%!    data = uint16 (reshape (1:400, [20, 20]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [20, 20]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test 32-bit grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 32));
%!    data = uint32 (reshape (1:400, [20, 20]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [20, 20]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test 16-bit RGB image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 1,
%!                         "PhotometricInterpretation", 2));
%!    data = uint16 (reshape (1:1200, [20, 20, 3]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [20, 20, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test 32-bit RGB image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 32, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 1,
%!                         "PhotometricInterpretation", 2));
%!    data = uint32 (reshape (1:1200, [20, 20, 3]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [20, 20, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure unsupported bit-depth
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 24));
%!    data = uint16 (reshape (1:400, [20, 20]));
%!    fail ("writeEncodedStrip (img, 1, data)", "Unsupported bit depth");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure unsupported BiLevel number of channels
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                         "SamplesPerPixel", 3));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
%!    fail ("writeEncodedStrip (img, 1, data)",
%!          "Bi-Level images must have one channel only");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test multi-strip BiLevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                         "RowsPerStrip", 2));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
%!    for strip = 1:5
%!      writeEncodedStrip (img, strip, data(strip * 2 - 1: strip * 2, :));
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test multi-strip grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "RowsPerStrip", 3));
%!    data = uint16 (reshape (1:400, [20, 20]));
%!    for strip = 1:7
%!      writeEncodedStrip (img, strip, data(strip * 3 - 2: min(strip * 3, 20), :));
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [20, 20]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test multi-strip RGB image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                         "RowsPerStrip", 2, "PlanarConfiguration", 1,
%!                         "PhotometricInterpretation", 2));
%!    data = uint16 (reshape (1:1200, [20, 20, 3]));
%!    for strip = 1:10
%!      writeEncodedStrip (img, strip, data(strip * 2 - 1: strip * 2, :, :));
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [20, 20, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test multi-strip RGB separate planes image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                         "RowsPerStrip", 3, "PlanarConfiguration", 2,
%!                         "PhotometricInterpretation", 2));
%!    data = uint16 (reshape (1:1200, [20, 20, 3]));
%!    strip = 1;
%!    for sample = 1:3
%!      for row = 1:3:20
%!        writeEncodedStrip (img, strip, data(row: min(row + 2, 20), :, sample));
%!        strip = strip + 1;
%!      endfor
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [20, 20, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test single-precision image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 32, "SampleFormat", 3));
%!    data = single (reshape (1:400, [20, 20]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [20, 20]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test double-precision image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 64, "SampleFormat", 3));
%!    data = double (reshape (1:400, [20, 20]));
%!    writeEncodedStrip (img, 1, data);
%!    img.close ();
%!    verify_data (filename, data, [20, 20]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure unsupported floating-point bit depth
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SampleFormat", 3));
%!    data = double (reshape (1:400, [20, 20]));
%!    fail ("writeEncodedStrip (img, 1, data)",
%!          "Unsupported bit depth for floating-point images");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure data-type and bit-depth mismatch
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 1,
%!                         "PhotometricInterpretation", 2));
%!    data = uint8 (reshape (1:1200, [20, 20, 3]));
%!    fail ("writeEncodedStrip (img, 1, data)", "Only uint16 data is allowed for uint images with bit depth of 16");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure writing to img read-only file
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 1, "ImageWidth", 1,
%!                         "BitsPerSample", 8));
%!    data = uint8 (1);
%!    writeEncodedStrip (img, 1, data);
%!    img.close();
%!    img = Tiff (filename, "r");
%!    fail ("writeEncodedStrip(img, 1, [1])",
%!          "Can't write data to a file opened in read-only mode");
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure unsupported sample format
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SampleFormat", 5));
%!    data = double (reshape (1:400, [20, 20]));
%!    fail ("writeEncodedStrip (img, 1, data)", "Unsupported sample format");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure wrong strip number
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "RowsPerStrip", 5));
%!    strip_data = uint16 (reshape (1:100, [5, 20]));
%!    fail ("writeEncodedStrip (img, 5, strip_data)",
%!          "Strip number out of range");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure writing strips to tiled image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "TileLength", 16,
%!                         "TileWidth", 16));
%!    data = uint8 (reshape (1:400, [20, 20]));
%!    fail ("writeEncodedStrip (img, 1, data)",
%!          "Can't write strips to a tiled image");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test tiled BiLevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 32, "ImageWidth", 32,
%!                         "TileLength", 16, "TileWidth", 16));
%!    data = logical (repmat ([1,0,0,1,0,1,1,0], [32, 4]));
%!    tile = 1;
%!    for row = 1:16:32
%!      for col = 1:16:32
%!        writeEncodedTile (img, tile, data(row:row+15, col:col+15));
%!        tile = tile + 1;
%!      endfor
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [32, 32]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test tiled grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 32, "ImageWidth", 32,
%!                         "BitsPerSample", 16, "TileLength", 16,
%!                         "TileWidth", 16));
%!    data = uint16 (reshape (1:1024, [32, 32]));
%!    tile = 1;
%!    for row = 1:16:32
%!      for col = 1:16:32
%!        writeEncodedTile (img, tile, data(row:row+15, col:col+15));
%!        tile = tile + 1;
%!      endfor
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [32, 32]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test tiled grayscale image with padding
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "TileLength", 16,
%!                         "TileWidth", 16));
%!    data = uint16 (reshape (1:400, [20, 20]));
%!    tile = 1;
%!    for row = 1:16:32
%!      for col = 1:16:32
%!        writeEncodedTile (img, tile, data(row:min(row+15, 20),
%!                                          col:min(col+15, 20)));
%!        tile = tile + 1;
%!      endfor
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [20, 20]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test tiled RGB image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 1, "TileLength", 16,
%!                         "TileWidth", 16, "PhotometricInterpretation", 2));
%!    data = uint16 (reshape (1:1200, [20, 20, 3]));
%!    tile = 1;
%!    for row = 1:16:32
%!      for col = 1:16:32
%!        writeEncodedTile (img, tile, data(row:min(row+15,20),
%!                                          col:min(col+15,20), :));
%!        tile = tile + 1;
%!      endfor
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [20, 20, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test tiled RGB image separate planes
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 2, "TileLength", 16,
%!                         "TileWidth", 16, "PhotometricInterpretation", 2));
%!    data = uint16 (reshape (1:1200, [20, 20, 3]));
%!    tile = 1;
%!    for channel=1:3
%!      for row = 1:16:32
%!        for col = 1:16:32
%!          writeEncodedTile (img, tile, data(row:min(row+15,20),
%!                                            col:min(col+15,20), channel));
%!          tile = tile + 1;
%!        endfor
%!      endfor
%!    endfor
%!    img.close ();
%!    verify_data (filename, data, [20, 20, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure writing tiles to stripped image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16));
%!    data = uint8 (reshape (1:400, [20, 20]));
%!    fail ("writeEncodedTile (img, 1, data)",
%!          "Can't write tiles to a stripped image");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test tiled image wrong tile dimensions
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                         "PlanarConfiguration", 1, "TileLength", 16,
%!                         "TileWidth", 16, "PhotometricInterpretation", 2));
%!    data = uint16 (reshape (1:1600, [20, 20, 4]));
%!    tile = 1;
%!    for row = 1:16:32
%!      for col = 1:16:32
%!        writeEncodedTile (img, tile, data(row:min(row+15,20),
%!                                          col:min(col+15,20), 1:3));
%!        tile = tile + 1;
%!      endfor
%!    endfor
%!    fail ("writeEncodedTile (img, 1, data(1:18,1:16,1:3))", "warning",
%!          "The tile is composed of 16 rows but input has 18 rows");
%!    fail ("writeEncodedTile (img, 1, data(1:16,1:20,1:3))", "warning",
%!          "The tile is composed of 16 columns but input has 20 columns");
%!    fail ("writeEncodedTile (img, 1, data(1:16,1:16,:))", "warning",
%!          "The tile is composed of 3 channels but input has 4 channels");
%!    img.close ();
%!    verify_data (filename, data, [20, 20, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test failure wrong tile number
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageLength", 20, "ImageWidth", 20,
%!                         "BitsPerSample", 16, "TileWidth", 16,
%!                         "TileLength", 16));
%!    tile_data = uint16 (reshape (1:256, [16, 16]));
%!    fail ("writeEncodedTile (img, 5, tile_data)",
%!          "Tile number out of range");
%!    img.close ();
%!  endfunction
%!  file_wrapper (@test_fn);

## test isTiled returns the correct value
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    assert (isTiled (img), logical (0));
%!    setTag (img, "TileLength", 16);
%!    setTag (img, "TileWidth", 16);
%!    assert (isTiled (img), logical (1));
%!  endfunction
%!  file_wrapper (@test_fn);

## test numberOfStrips returns the correct value
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageWidth", 10, "ImageLength", 10));
%!    assert (numberOfStrips (img), 1);
%!    setTag (img, "RowsPerStrip", 2);
%!    assert (numberOfStrips (img), 5);
%!    setTag (img, "RowsPerStrip", 4);
%!    assert (numberOfStrips (img), 3);
%!    setTag (img, "TileLength", 16);
%!    setTag (img, "TileWidth", 16);
%!    fail ("numberOfStrips (img)", "The image is tiled not stripped");
%!  endfunction
%!  file_wrapper (@test_fn);

## test numberOfTiles returns the correct value
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageWidth", 20, "ImageLength", 20,
%!                         "TileLength", 16, "TileWidth", 16));
%!    assert (numberOfTiles (img), 4);
%!    setTag (img, "TileLength", 32);
%!    assert (numberOfTiles (img), 2);
%!    setTag (img, "TileWidth", 32);
%!    assert (numberOfTiles (img), 1);
%!    img = Tiff (filename, "w");
%!    fail ("numberOfTiles (img)", "The image is stripped not tiled");
%!  endfunction
%!  file_wrapper (@test_fn);

## test computeStrip returns the correct value
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageWidth", 10, "ImageLength", 10,
%!                         "RowsPerStrip", 2, "BitsPerSample", 8,
%!                         "SamplesPerPixel", 3, "PlanarConfiguration", 1));
%!    assert (computeStrip (img, 1), 1);
%!    assert (computeStrip (img, 2), 1);
%!    assert (computeStrip (img, 0), 1);
%!    assert (computeStrip (img, 10), 5);
%!    assert (computeStrip (img, 11), 5);
%!    setTag (img, "PlanarConfiguration", 2);
%!    ## This is need for the tag to take effect, should be
%!    ## replaced by rewriteDirectory
%!    writeEncodedStrip (img, 1, uint8 (reshape (1:20, [2, 10])));
%!    assert (computeStrip (img, 1, 2), 6);
%!    assert (computeStrip (img, 100, 1), 5);
%!    img = Tiff (filename, "w");
%!    setTag (img, "TileWidth", 16);
%!    setTag (img, "TileLength", 16);
%!    fail ("computeStrip (img, 1, 1)", "The image is tiled not stripped");
%!  endfunction
%!  file_wrapper (@test_fn);

## test computeTile returns the correct value
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct ("ImageWidth", 20, "ImageLength", 20,
%!                         "BitsPerSample", 8, "SamplesPerPixel", 3,
%!                         "TileLength", 16, "TileWidth", 16,
%!                         "PlanarConfiguration", 1));
%!    assert (computeTile (img, [1, 1]), 1);
%!    assert (computeTile (img, [2, 2]), 1);
%!    assert (computeTile (img, [0, 0]), 1);
%!    assert (computeTile (img, [8, 17]), 2);
%!    assert (computeTile (img, [19, 10]), 3);
%!    assert (computeTile (img, [17, 17]), 4);
%!    assert (computeTile (img, [100, 100]), 4);
%!    setTag (img, "PlanarConfiguration", 2);
%!    ## This is need for the tag to take effect, should be
%!    ## replaced by rewriteDirectory
%!    writeEncodedTile (img, 1, uint8 (reshape (1:256, [16, 16])));
%!    assert (computeTile (img, [1, 1], 2), 5);
%!    assert (computeTile (img, [100, 100], 1), 4);
%!    img = Tiff (filename, "w");
%!    fail ("computeTile (img, 1, 1)", "The image is stripped not tiled");
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method one pixel bilevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 1, "ImageWidth", 1));
%!    write (img, logical ([1]));
%!    img.close();
%!    verify_data (filename, logical ([1]), [1, 1]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method one strip bilevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method multi-strip bilevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "RowsPerStrip", 3));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method one pixel grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 1, "ImageWidth", 1,
%!                        "BitsPerSample", 8));
%!    write (img, uint8 ([255]));
%!    img.close();
%!    verify_data (filename, uint8 ([255]), [1, 1]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method one strip grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 8));
%!    data = uint8 (reshape (1:100, [10, 10]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method multi-strip grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 8, "RowsPerStrip", 3));
%!    data = uint8 (reshape (1:100, [10, 10]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method multi-strip RGB image chunky
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 8, "RowsPerStrip", 3,
%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
%!                        "PhotometricInterpretation", 2));
%!    data = uint8 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method multi-strip RGB image separate
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 8, "RowsPerStrip", 3,
%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 2,
%!                        "PhotometricInterpretation", 2));
%!    data = uint8 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method 16-bit multi-strip image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 16, "RowsPerStrip", 3,
%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
%!                        "PhotometricInterpretation", 2));
%!    data = uint16 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method 32-bit multi-strip image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 32, "RowsPerStrip", 3,
%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
%!                        "PhotometricInterpretation", 2));
%!    data = uint32 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method single-precision image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 32, "RowsPerStrip", 3,
%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
%!                        "PhotometricInterpretation", 2, "SampleFormat", 3));
%!    data = single (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method single-precision image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 64, "RowsPerStrip", 3,
%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
%!                        "PhotometricInterpretation", 2, "SampleFormat", 3));
%!    data = double (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write signed integer image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag (img, struct (
%!      "ImageLength", 10, "ImageWidth", 10, "SamplesPerPixel", 3,
%!      "BitsPerSample", 16, "SampleFormat", Tiff.SampleFormat.Int,
%!      "PhotometricInterpretation", Tiff.Photometric.RGB,
%!      "PlanarConfiguration", Tiff.PlanarConfiguration.Chunky));
%!    data = int16 (reshape (-150:149, [10, 10, 3]));
%!    write (img, data);
%!    img.close ();
%!    verify_data (filename, data, [10, 10, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method tiled BiLevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [40, 4]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [40, 40]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method tiled grayscale image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 16));
%!    data = uint16 (reshape (1:1600, [40, 40]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [40, 40]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method tiled RGB image chunky
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2,
%!                        "PlanarConfiguration", 1));
%!    data = uint16 (reshape (1:4800, [40, 40, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [40, 40, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method tiled RGB image separate
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2,
%!                        "PlanarConfiguration", 2));
%!    data = uint16 (reshape (1:4800, [40, 40, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [40, 40, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method 32-bit tiled image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 32, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2,
%!                        "PlanarConfiguration", 1));
%!    data = uint32 (reshape (1:4800, [40, 40, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [40, 40, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method single-precision tiled image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 32, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2,
%!                        "PlanarConfiguration", 1, "SampleFormat", 3));
%!    data = single (reshape (1:4800, [40, 40, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [40, 40, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test write method double-precision tiled image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 64, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2,
%!                        "PlanarConfiguration", 1, "SampleFormat", 3));
%!    data = double (reshape (1:4800, [40, 40, 3]));
%!    write (img, data);
%!    img.close();
%!    verify_data (filename, data, [40, 40, 3]);
%!  endfunction
%!  file_wrapper (@test_fn);

## test readEncodedStrip BiLevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "RowsPerStrip", 6));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
%!    write (img, data);
%!    s1 = readEncodedStrip (img, 1);
%!    s2 = readEncodedStrip (img, 2);
%!    assert ([s1;s2], data);
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn);

## test readEncodedStrip RGB Chunky
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2, "RowsPerStrip", 6,
%!                        "PlanarConfiguration", 1));
%!    data = uint16 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    s1 = readEncodedStrip (img, 1);
%!    s2 = readEncodedStrip (img, 2);
%!    assert ([s1;s2], data);
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn);

## test readEncodedStrip RGB Separated
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2, "RowsPerStrip", 6,
%!                        "PlanarConfiguration", 2));
%!    data = uint16 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    s = cell (6, 1);
%!    for i=1:6
%!      s{i} = readEncodedStrip (img, i);
%!    endfor
%!    data2 = cat (3, [s{1}; s{2}], [s{3}; s{4}], [s{5}; s{6}]);
%!    assert (data2, data);
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn);

## test readEncodedTile BiLevel image
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16));
%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [40, 4]));
%!    write (img, data);
%!    t = cell (9, 1);
%!    for i=1:9
%!      t{i} = readEncodedTile (img, i);
%!    endfor
%!    data2 = cat(1, cat (2, t{1}, t{2}, t{3}), cat (2, t{4}, t{5}, t{6}),
%!                cat (2, t{7}, t{8}, t{9}));
%!    assert (data2, data);
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn);

## test readEncodedTile RGB Chunky
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2,
%!                        "PlanarConfiguration", 1));
%!    data = uint16 (reshape (1:4800, [40, 40, 3]));
%!    write (img, data);
%!    t = cell (9, 1);
%!    for i=1:9
%!      t{i} = readEncodedTile (img, i);
%!    endfor
%!    data2 = cat(1, cat (2, t{1}, t{2}, t{3}), cat (2, t{4}, t{5}, t{6}),
%!                cat (2, t{7}, t{8}, t{9}));
%!    assert (data2, data);
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn);

## test readEncodedTile RGB Separated
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct ("ImageLength", 40, "ImageWidth", 40,
%!                        "TileWidth", 16, "TileLength", 16,
%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
%!                        "PhotometricInterpretation", 2,
%!                        "PlanarConfiguration", 2));
%!    data = uint16 (reshape (1:4800, [40, 40, 3]));
%!    write (img, data);
%!    data2 = uint16 (zeros (40, 40, 3));
%!    tile = 1;
%!    for sample=1:3
%!      for row=1:16:40
%!        for col=1:16:40
%!          data2(row: min(40, row + 15), col: min(40, col + 15), sample)...
%!            = readEncodedTile(img, tile);
%!          tile += 1;
%!        endfor
%!      endfor
%!    endfor
%!    assert (data2, data);
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBAImage
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1
%!    ));
%!    data = uint8 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBAImage (img);
%!    assert (rgb, data);
%!    assert (alpha, uint8 (repmat ([255], [10, 10])));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBAImage with orientation
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "Orientation", Tiff.Orientation.BottomRight
%!    ));
%!    data = uint8 (reshape (1:300, [10, 10, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBAImage (img);
%!    assert (rgb, data(end:-1:1, end:-1:1, :));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBAImage with alpha
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SamplesPerPixel", 4,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "ExtraSamples", 1
%!    ));
%!    data = uint8 (randi ([0,255], [10, 10, 4]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBAImage (img);
%!    assert (rgb, data(:,:,1:3));
%!    assert (alpha, data(:,:,4));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBAStrip
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "RowsPerStrip", 3
%!    ));
%!    data = uint8 (randi ([0,255], [10, 10, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBAStrip (img, 1);
%!    assert (rgb, data(1:3,:,:));
%!    assert (alpha, uint8 (repmat ([255], [3, 10])));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBAStrip with orientation
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "RowsPerStrip", 3,
%!      "Orientation", Tiff.Orientation.BottomRight
%!    ));
%!    data = uint8 (randi ([0,255], [10, 10, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBAStrip (img, 1);
%!    assert (rgb, data(3:-1:1,end:-1:1,:));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBAStrip boundary strip
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "RowsPerStrip", 3
%!    ));
%!    data = uint8 (randi ([0,255], [10, 10, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBAStrip (img, 10);
%!    assert (rgb, data(10,:,:));
%!    assert (alpha, uint8 (repmat ([255], [1, 10])));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBAStrip with alpha
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SamplesPerPixel", 4,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "RowsPerStrip", 3,
%!      "ExtraSamples", 1
%!    ));
%!    data = uint8 (randi ([0,255], [10, 10, 4]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBAStrip (img, 1);
%!    assert (rgb, data(1:3,:, 1:3));
%!    assert (alpha, data (1:3, :, 4));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBATile
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 40, "ImageWidth", 40,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "TileLength", 16, "TileWidth", 32
%!    ));
%!    data = uint8 (randi ([0,255], [40, 40, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBATile (img, 1, 1);
%!    assert (rgb, data(1:16,1:32,:));
%!    assert (alpha, uint8 (repmat ([255], [16, 32])));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBATile ith orientation
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 40, "ImageWidth", 40,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "TileLength", 16, "TileWidth", 32,
%!      "Orientation", Tiff.Orientation.BottomRight
%!    ));
%!    data = uint8 (randi ([0,255], [40, 40, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBATile (img, 1, 1);
%!    assert (rgb, data(16:-1:1,32:-1:1,:));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBATile boundary tile
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 40, "ImageWidth", 40,
%!      "BitsPerSample", 8, "SamplesPerPixel", 3,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "TileLength", 16, "TileWidth", 32
%!    ));
%!    data = uint8 (randi ([0,255], [40, 40, 3]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBATile (img, 40, 40);
%!    assert (rgb, data(33:end,33:end,:));
%!    assert (alpha, uint8 (repmat ([255], [8, 8])));
%!  endfunction
%!  file_wrapper (@test_fn);

## test readRGBATile ith alpha
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct (
%!      "ImageLength", 40, "ImageWidth", 40,
%!      "BitsPerSample", 8, "SamplesPerPixel", 4,
%!      "PhotometricInterpretation", 2,
%!      "PlanarConfiguration", 1,
%!      "TileLength", 16, "TileWidth", 32,
%!      "ExtraSamples", 1
%!    ));
%!    data = uint8 (randi ([0,255], [40, 40, 4]));
%!    write (img, data);
%!    [rgb, alpha] = readRGBATile (img, 1, 1);
%!    assert (rgb, data(1:16,1:32,1:3));
%!    assert (alpha, data(1:16,1:32,4));
%!  endfunction
%!  file_wrapper (@test_fn);

## test directory manipulation
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    tags = struct (
%!      "ImageLength", 10, "ImageWidth", 10, "BitsPerSample", 8
%!    );
%!    setTag (img, tags);
%!    data = uint8 (reshape (1:100, [10, 10]));
%!    write(img, data);
%!    img.writeDirectory ();
%!    setTag (img, tags);
%!    write(img, data);
%!    img.close();
%!    img = Tiff (filename);
%!    assert (img.currentDirectory, 1);
%!    assert (img.lastDirectory, logical (0));
%!    img.nextDirectory ();
%!    assert (img.currentDirectory, 2);
%!    assert (img.lastDirectory, logical (1));
%!    img.setDirectory (1);
%!    assert (img.currentDirectory, 1);
%!  endfunction
%!  file_wrapper (@test_fn)

## test creating and reading subdirectories
%!testif HAVE_TIFF
%!  function test_fn (filename)
%!    img = Tiff (filename, "w");
%!    setTag(img, struct(
%!      "ImageLength", 10, "ImageWidth", 10,
%!      "BitsPerSample", 8, "SubIFD", 1
%!    ));
%!    data = uint8 (reshape (1:100, [10, 10]));
%!    write (img, data);
%!    img.writeDirectory ();
%!    setTag(img, struct(
%!      "ImageLength", 15, "ImageWidth", 15,
%!      "BitsPerSample", 8
%!    ));
%!    data_subdir = uint8 (reshape (1:225, [15, 15]));
%!    write (img, data_subdir);
%!    img.close();
%!    img = Tiff (filename);
%!    assert (img.read(), data);
%!    offsets = getTag (img, "SubIFD");
%!    img.setSubDirectory (offsets (1));
%!    assert (img.read(), data_subdir);
%!    img.close();
%!  endfunction
%!  file_wrapper (@test_fn)