///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
// 
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// *       Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// *       Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// *       Neither the name of Industrial Light & Magic nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//
//	class TiledRgbaOutputFile
//	class TiledRgbaInputFile
//
//-----------------------------------------------------------------------------

#include <ImfTiledRgbaFile.h>
#include <ImfRgbaFile.h>
#include <ImfTiledOutputFile.h>
#include <ImfTiledInputFile.h>
#include <ImfChannelList.h>
#include <ImfTileDescriptionAttribute.h>
#include <ImfStandardAttributes.h>
#include <ImfRgbaYca.h>
#include <ImfArray.h>
#include "IlmThreadMutex.h"
#include "Iex.h"


namespace Imf {

using namespace std;
using namespace Imath;
using namespace RgbaYca;
using namespace IlmThread;

namespace {

void
insertChannels (Header &header,
		RgbaChannels rgbaChannels,
		const char fileName[])
{
    ChannelList ch;

    if (rgbaChannels & (WRITE_Y | WRITE_C))
    {
	if (rgbaChannels & WRITE_Y)
	{
	    ch.insert ("Y", Channel (HALF, 1, 1));
	}

	if (rgbaChannels & WRITE_C)
	{
	    THROW (Iex::ArgExc, "Cannot open file \"" << fileName << "\" "
				"for writing.  Tiled image files do not "
				"support subsampled chroma channels.");
	}
    }
    else
    {
	if (rgbaChannels & WRITE_R)
	    ch.insert ("R", Channel (HALF, 1, 1));

	if (rgbaChannels & WRITE_G)
	    ch.insert ("G", Channel (HALF, 1, 1));

	if (rgbaChannels & WRITE_B)
	    ch.insert ("B", Channel (HALF, 1, 1));
    }

    if (rgbaChannels & WRITE_A)
	ch.insert ("A", Channel (HALF, 1, 1));

    header.channels() = ch;
}


RgbaChannels
rgbaChannels (const ChannelList &ch, const string &channelNamePrefix = "")
{
    int i = 0;

    if (ch.findChannel (channelNamePrefix + "R"))
	i |= WRITE_R;

    if (ch.findChannel (channelNamePrefix + "G"))
	i |= WRITE_G;
    
    if (ch.findChannel (channelNamePrefix + "B"))
	i |= WRITE_B;

    if (ch.findChannel (channelNamePrefix + "A"))
	i |= WRITE_A;

    if (ch.findChannel (channelNamePrefix + "Y"))
	i |= WRITE_Y;

    return RgbaChannels (i);
}


string
prefixFromLayerName (const string &layerName, const Header &header)
{
    if (layerName.empty())
	return "";

    if (hasMultiView (header) && multiView(header)[0] == layerName)
	return "";

    return layerName + ".";
}


V3f
ywFromHeader (const Header &header)
{
    Chromaticities cr;

    if (hasChromaticities (header))
	cr = chromaticities (header);

    return computeYw (cr);
}

} // namespace


class TiledRgbaOutputFile::ToYa: public Mutex
{
  public:

     ToYa (TiledOutputFile &outputFile, RgbaChannels rgbaChannels);

     void	setFrameBuffer (const Rgba *base,
				size_t xStride,
				size_t yStride);

     void	writeTile (int dx, int dy, int lx, int ly);

  private:

     TiledOutputFile &	_outputFile;
     bool		_writeA;
     unsigned int	_tileXSize;
     unsigned int	_tileYSize;
     V3f		_yw;
     Array2D <Rgba>	_buf;
     const Rgba *	_fbBase;
     size_t		_fbXStride;
     size_t		_fbYStride;
};


TiledRgbaOutputFile::ToYa::ToYa (TiledOutputFile &outputFile,
				 RgbaChannels rgbaChannels)
:
    _outputFile (outputFile)
{
    _writeA = (rgbaChannels & WRITE_A)? true: false;
    
    const TileDescription &td = outputFile.header().tileDescription();

    _tileXSize = td.xSize;
    _tileYSize = td.ySize;
    _yw = ywFromHeader (_outputFile.header());
    _buf.resizeErase (_tileYSize, _tileXSize);
    _fbBase = 0;
    _fbXStride = 0;
    _fbYStride = 0;
}


void
TiledRgbaOutputFile::ToYa::setFrameBuffer (const Rgba *base,
					   size_t xStride,
					   size_t yStride)
{
    _fbBase = base;
    _fbXStride = xStride;
    _fbYStride = yStride;
}


void
TiledRgbaOutputFile::ToYa::writeTile (int dx, int dy, int lx, int ly)
{
    if (_fbBase == 0)
    {
	THROW (Iex::ArgExc, "No frame buffer was specified as the "
			    "pixel data source for image file "
			    "\"" << _outputFile.fileName() << "\".");
    }

    //
    // Copy the tile's RGBA pixels into _buf and convert
    // them to luminance/alpha format
    //

    Box2i dw = _outputFile.dataWindowForTile (dx, dy, lx, ly);
    int width = dw.max.x - dw.min.x + 1;

    for (int y = dw.min.y, y1 = 0; y <= dw.max.y; ++y, ++y1)
    {
	for (int x = dw.min.x, x1 = 0; x <= dw.max.x; ++x, ++x1)
	    _buf[y1][x1] = _fbBase[x * _fbXStride + y * _fbYStride];

	RGBAtoYCA (_yw, width, _writeA, _buf[y1], _buf[y1]);
    }

    //
    // Store the contents of _buf in the output file
    //

    FrameBuffer fb;

    fb.insert ("Y", Slice (HALF,				   // type
			   (char *) &_buf[-dw.min.y][-dw.min.x].g, // base
			   sizeof (Rgba),			   // xStride
			   sizeof (Rgba) * _tileXSize));	   // yStride

    fb.insert ("A", Slice (HALF,				   // type
			   (char *) &_buf[-dw.min.y][-dw.min.x].a, // base
			   sizeof (Rgba),			   // xStride
			   sizeof (Rgba) * _tileXSize));	   // yStride

    _outputFile.setFrameBuffer (fb);
    _outputFile.writeTile (dx, dy, lx, ly);
}


TiledRgbaOutputFile::TiledRgbaOutputFile
    (const char name[],
     const Header &header,
     RgbaChannels rgbaChannels,
     int tileXSize,
     int tileYSize,
     LevelMode mode,
     LevelRoundingMode rmode,
     int numThreads)
:
    _outputFile (0),
    _toYa (0)
{
    Header hd (header);
    insertChannels (hd, rgbaChannels, name);
    hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
    _outputFile = new TiledOutputFile (name, hd, numThreads);

    if (rgbaChannels & WRITE_Y)
	_toYa = new ToYa (*_outputFile, rgbaChannels);
}



TiledRgbaOutputFile::TiledRgbaOutputFile
    (OStream &os,
     const Header &header,
     RgbaChannels rgbaChannels,
     int tileXSize,
     int tileYSize,
     LevelMode mode,
     LevelRoundingMode rmode,
     int numThreads)
:
    _outputFile (0),
    _toYa (0)
{
    Header hd (header);
    insertChannels (hd, rgbaChannels, os.fileName());
    hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
    _outputFile = new TiledOutputFile (os, hd, numThreads);

    if (rgbaChannels & WRITE_Y)
	_toYa = new ToYa (*_outputFile, rgbaChannels);
}



TiledRgbaOutputFile::TiledRgbaOutputFile
    (const char name[],
     int tileXSize,
     int tileYSize,
     LevelMode mode,
     LevelRoundingMode rmode,
     const Imath::Box2i &displayWindow,
     const Imath::Box2i &dataWindow,
     RgbaChannels rgbaChannels,
     float pixelAspectRatio,
     const Imath::V2f screenWindowCenter,
     float screenWindowWidth,
     LineOrder lineOrder,
     Compression compression,
     int numThreads)
:
    _outputFile (0),
    _toYa (0)
{
    Header hd (displayWindow,
	       dataWindow.isEmpty()? displayWindow: dataWindow,
	       pixelAspectRatio,
	       screenWindowCenter,
	       screenWindowWidth,
	       lineOrder,
	       compression);

    insertChannels (hd, rgbaChannels, name);
    hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
    _outputFile = new TiledOutputFile (name, hd, numThreads);

    if (rgbaChannels & WRITE_Y)
	_toYa = new ToYa (*_outputFile, rgbaChannels);
}


TiledRgbaOutputFile::TiledRgbaOutputFile
    (const char name[],
     int width,
     int height,
     int tileXSize,
     int tileYSize,
     LevelMode mode,
     LevelRoundingMode rmode,
     RgbaChannels rgbaChannels,
     float pixelAspectRatio,
     const Imath::V2f screenWindowCenter,
     float screenWindowWidth,
     LineOrder lineOrder,
     Compression compression,
     int numThreads)
:
    _outputFile (0),
    _toYa (0)
{
    Header hd (width,
	       height,
	       pixelAspectRatio,
	       screenWindowCenter,
	       screenWindowWidth,
	       lineOrder,
	       compression);

    insertChannels (hd, rgbaChannels, name);
    hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
    _outputFile = new TiledOutputFile (name, hd, numThreads);

    if (rgbaChannels & WRITE_Y)
	_toYa = new ToYa (*_outputFile, rgbaChannels);
}


TiledRgbaOutputFile::~TiledRgbaOutputFile ()
{
    delete _outputFile;
    delete _toYa;
}


void
TiledRgbaOutputFile::setFrameBuffer (const Rgba *base,
				     size_t xStride,
				     size_t yStride)
{
    if (_toYa)
    {
	Lock lock (*_toYa);
	_toYa->setFrameBuffer (base, xStride, yStride);
    }
    else
    {
	size_t xs = xStride * sizeof (Rgba);
	size_t ys = yStride * sizeof (Rgba);

	FrameBuffer fb;

	fb.insert ("R", Slice (HALF, (char *) &base[0].r, xs, ys));
	fb.insert ("G", Slice (HALF, (char *) &base[0].g, xs, ys));
	fb.insert ("B", Slice (HALF, (char *) &base[0].b, xs, ys));
	fb.insert ("A", Slice (HALF, (char *) &base[0].a, xs, ys));

	_outputFile->setFrameBuffer (fb);
    }
}


const Header &
TiledRgbaOutputFile::header () const
{
    return _outputFile->header();
}


const FrameBuffer &
TiledRgbaOutputFile::frameBuffer () const
{
    return _outputFile->frameBuffer();
}


const Imath::Box2i &
TiledRgbaOutputFile::displayWindow () const
{
    return _outputFile->header().displayWindow();
}


const Imath::Box2i &
TiledRgbaOutputFile::dataWindow () const
{
    return _outputFile->header().dataWindow();
}


float	
TiledRgbaOutputFile::pixelAspectRatio () const
{
    return _outputFile->header().pixelAspectRatio();
}


const Imath::V2f
TiledRgbaOutputFile::screenWindowCenter () const
{
    return _outputFile->header().screenWindowCenter();
}


float	
TiledRgbaOutputFile::screenWindowWidth () const
{
    return _outputFile->header().screenWindowWidth();
}


LineOrder
TiledRgbaOutputFile::lineOrder () const
{
    return _outputFile->header().lineOrder();
}


Compression
TiledRgbaOutputFile::compression () const
{
    return _outputFile->header().compression();
}


RgbaChannels
TiledRgbaOutputFile::channels () const
{
    return rgbaChannels (_outputFile->header().channels());
}


unsigned int
TiledRgbaOutputFile::tileXSize () const
{
     return _outputFile->tileXSize();
}


unsigned int
TiledRgbaOutputFile::tileYSize () const
{
     return _outputFile->tileYSize();
}


LevelMode
TiledRgbaOutputFile::levelMode () const
{
     return _outputFile->levelMode();
}


LevelRoundingMode
TiledRgbaOutputFile::levelRoundingMode () const
{
     return _outputFile->levelRoundingMode();
}


int
TiledRgbaOutputFile::numLevels () const
{
     return _outputFile->numLevels();
}


int
TiledRgbaOutputFile::numXLevels () const
{
     return _outputFile->numXLevels();
}


int
TiledRgbaOutputFile::numYLevels () const
{
     return _outputFile->numYLevels();
}


bool
TiledRgbaOutputFile::isValidLevel (int lx, int ly) const
{
    return _outputFile->isValidLevel (lx, ly);
}


int
TiledRgbaOutputFile::levelWidth (int lx) const
{
     return _outputFile->levelWidth (lx);
}


int
TiledRgbaOutputFile::levelHeight (int ly) const
{
     return _outputFile->levelHeight (ly);
}


int
TiledRgbaOutputFile::numXTiles (int lx) const
{
     return _outputFile->numXTiles (lx);
}


int
TiledRgbaOutputFile::numYTiles (int ly) const
{
     return _outputFile->numYTiles (ly);
}


Imath::Box2i
TiledRgbaOutputFile::dataWindowForLevel (int l) const
{
     return _outputFile->dataWindowForLevel (l);
}


Imath::Box2i
TiledRgbaOutputFile::dataWindowForLevel (int lx, int ly) const
{
     return _outputFile->dataWindowForLevel (lx, ly);
}


Imath::Box2i
TiledRgbaOutputFile::dataWindowForTile (int dx, int dy, int l) const
{
     return _outputFile->dataWindowForTile (dx, dy, l);
}


Imath::Box2i
TiledRgbaOutputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
{
     return _outputFile->dataWindowForTile (dx, dy, lx, ly);
}


void
TiledRgbaOutputFile::writeTile (int dx, int dy, int l)
{
    if (_toYa)
    {
	Lock lock (*_toYa);
	_toYa->writeTile (dx, dy, l, l);
    }
    else
    {
	 _outputFile->writeTile (dx, dy, l);
    }
}


void
TiledRgbaOutputFile::writeTile (int dx, int dy, int lx, int ly)
{
    if (_toYa)
    {
	Lock lock (*_toYa);
	_toYa->writeTile (dx, dy, lx, ly);
    }
    else
    {
	 _outputFile->writeTile (dx, dy, lx, ly);
    }
}


void	
TiledRgbaOutputFile::writeTiles
    (int dxMin, int dxMax, int dyMin, int dyMax, int lx, int ly)
{
    if (_toYa)
    {
	Lock lock (*_toYa);

        for (int dy = dyMin; dy <= dyMax; dy++)
            for (int dx = dxMin; dx <= dxMax; dx++)
	        _toYa->writeTile (dx, dy, lx, ly);
    }
    else
    {
        _outputFile->writeTiles (dxMin, dxMax, dyMin, dyMax, lx, ly);
    }
}

void	
TiledRgbaOutputFile::writeTiles
    (int dxMin, int dxMax, int dyMin, int dyMax, int l)
{
    writeTiles (dxMin, dxMax, dyMin, dyMax, l, l);
}


class TiledRgbaInputFile::FromYa: public Mutex
{
  public:

     FromYa (TiledInputFile &inputFile);

     void	setFrameBuffer (Rgba *base,
				size_t xStride,
				size_t yStride,
				const string &channelNamePrefix);

     void	readTile (int dx, int dy, int lx, int ly);

  private:

     TiledInputFile &	_inputFile;
     unsigned int	_tileXSize;
     unsigned int	_tileYSize;
     V3f		_yw;
     Array2D <Rgba>	_buf;
     Rgba *		_fbBase;
     size_t		_fbXStride;
     size_t		_fbYStride;
};


TiledRgbaInputFile::FromYa::FromYa (TiledInputFile &inputFile)
:
    _inputFile (inputFile)
{
    const TileDescription &td = inputFile.header().tileDescription();

    _tileXSize = td.xSize;
    _tileYSize = td.ySize;
    _yw = ywFromHeader (_inputFile.header());
    _buf.resizeErase (_tileYSize, _tileXSize);
    _fbBase = 0;
    _fbXStride = 0;
    _fbYStride = 0;
}


void
TiledRgbaInputFile::FromYa::setFrameBuffer (Rgba *base,
					    size_t xStride,
					    size_t yStride,
					    const string &channelNamePrefix)
{
    if (_fbBase == 0)
{
	FrameBuffer fb;

	fb.insert (channelNamePrefix + "Y",
		   Slice (HALF,				// type
			  (char *) &_buf[0][0].g,	// base
			  sizeof (Rgba),		// xStride
			  sizeof (Rgba) * _tileXSize,	// yStride
			  1, 1,				// sampling
			  0.0,				// fillValue
			  true, true));			// tileCoordinates

	fb.insert (channelNamePrefix + "A",
		   Slice (HALF,				// type
			  (char *) &_buf[0][0].a,	// base
			  sizeof (Rgba),		// xStride
			  sizeof (Rgba) * _tileXSize,	// yStride
			  1, 1,				// sampling
			  1.0,				// fillValue
			  true, true));			// tileCoordinates

	_inputFile.setFrameBuffer (fb);
    }

    _fbBase = base;
    _fbXStride = xStride;
    _fbYStride = yStride;
}


void
TiledRgbaInputFile::FromYa::readTile (int dx, int dy, int lx, int ly)
{
    if (_fbBase == 0)
    {
	THROW (Iex::ArgExc, "No frame buffer was specified as the "
			    "pixel data destination for image file "
			    "\"" << _inputFile.fileName() << "\".");
    }

    //
    // Read the tile requested by the caller into _buf.
    //
    
    _inputFile.readTile (dx, dy, lx, ly);

    //
    // Convert the luminance/alpha pixels to RGBA
    // and copy them into the caller's frame buffer.
    //

    Box2i dw = _inputFile.dataWindowForTile (dx, dy, lx, ly);
    int width = dw.max.x - dw.min.x + 1;

    for (int y = dw.min.y, y1 = 0; y <= dw.max.y; ++y, ++y1)
    {
	for (int x1 = 0; x1 < width; ++x1)
	{
	    _buf[y1][x1].r = 0;
	    _buf[y1][x1].b = 0;
	}

	YCAtoRGBA (_yw, width, _buf[y1], _buf[y1]);

	for (int x = dw.min.x, x1 = 0; x <= dw.max.x; ++x, ++x1)
	{
	    _fbBase[x * _fbXStride + y * _fbYStride] = _buf[y1][x1];
	}
    }
}


TiledRgbaInputFile::TiledRgbaInputFile (const char name[], int numThreads):
    _inputFile (new TiledInputFile (name, numThreads)),
    _fromYa (0),
    _channelNamePrefix ("")
{
    if (channels() & WRITE_Y)
	_fromYa = new FromYa (*_inputFile);
}


TiledRgbaInputFile::TiledRgbaInputFile (IStream &is, int numThreads):
    _inputFile (new TiledInputFile (is, numThreads)),
    _fromYa (0),
    _channelNamePrefix ("")
{
    if (channels() & WRITE_Y)
	_fromYa = new FromYa (*_inputFile);
}


TiledRgbaInputFile::TiledRgbaInputFile (const char name[],
					const string &layerName,
					int numThreads)
:
    _inputFile (new TiledInputFile (name, numThreads)),
    _fromYa (0),
    _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
{
    if (channels() & WRITE_Y)
	_fromYa = new FromYa (*_inputFile);
}


TiledRgbaInputFile::TiledRgbaInputFile (IStream &is,
					const string &layerName,
					int numThreads)
:
    _inputFile (new TiledInputFile (is, numThreads)),
    _fromYa (0),
    _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
{
    if (channels() & WRITE_Y)
	_fromYa = new FromYa (*_inputFile);
}


TiledRgbaInputFile::~TiledRgbaInputFile ()
{
    delete _inputFile;
    delete _fromYa;
}


void	
TiledRgbaInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
{
    if (_fromYa)
    {
	Lock lock (*_fromYa);
	_fromYa->setFrameBuffer (base, xStride, yStride, _channelNamePrefix);
    }
    else
    {
	size_t xs = xStride * sizeof (Rgba);
	size_t ys = yStride * sizeof (Rgba);

	FrameBuffer fb;

	fb.insert (_channelNamePrefix + "R",
		   Slice (HALF,
			       (char *) &base[0].r,
			       xs, ys,
			       1, 1,	// xSampling, ySampling
			       0.0));	// fillValue

	fb.insert (_channelNamePrefix + "G",
		   Slice (HALF,
			       (char *) &base[0].g,
			       xs, ys,
			       1, 1,	// xSampling, ySampling
			       0.0));	// fillValue

	fb.insert (_channelNamePrefix + "B",
		   Slice (HALF,
			       (char *) &base[0].b,
			       xs, ys,
			       1, 1,	// xSampling, ySampling
			       0.0));	// fillValue

	fb.insert (_channelNamePrefix + "A",
		   Slice (HALF,
			       (char *) &base[0].a,
			       xs, ys,
			       1, 1,	// xSampling, ySampling
			       1.0));	// fillValue

	_inputFile->setFrameBuffer (fb);
    }
}


void		
TiledRgbaInputFile::setLayerName (const std::string &layerName)
{
    delete _fromYa;
    _fromYa = 0;
    
    _channelNamePrefix = prefixFromLayerName (layerName, _inputFile->header());

    if (channels() & WRITE_Y)
	_fromYa = new FromYa (*_inputFile);

    FrameBuffer fb;
    _inputFile->setFrameBuffer (fb);
}


const Header &
TiledRgbaInputFile::header () const
{
    return _inputFile->header();
}


const char *
TiledRgbaInputFile::fileName () const
{
    return _inputFile->fileName();
}


const FrameBuffer &	
TiledRgbaInputFile::frameBuffer () const
{
    return _inputFile->frameBuffer();
}


const Imath::Box2i &
TiledRgbaInputFile::displayWindow () const
{
    return _inputFile->header().displayWindow();
}


const Imath::Box2i &
TiledRgbaInputFile::dataWindow () const
{
    return _inputFile->header().dataWindow();
}


float	
TiledRgbaInputFile::pixelAspectRatio () const
{
    return _inputFile->header().pixelAspectRatio();
}


const Imath::V2f	
TiledRgbaInputFile::screenWindowCenter () const
{
    return _inputFile->header().screenWindowCenter();
}


float	
TiledRgbaInputFile::screenWindowWidth () const
{
    return _inputFile->header().screenWindowWidth();
}


LineOrder
TiledRgbaInputFile::lineOrder () const
{
    return _inputFile->header().lineOrder();
}


Compression
TiledRgbaInputFile::compression () const
{
    return _inputFile->header().compression();
}


RgbaChannels	
TiledRgbaInputFile::channels () const
{
    return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix);
}


int
TiledRgbaInputFile::version () const
{
    return _inputFile->version();
}


bool
TiledRgbaInputFile::isComplete () const
{
    return _inputFile->isComplete();
}


unsigned int
TiledRgbaInputFile::tileXSize () const
{
     return _inputFile->tileXSize();
}


unsigned int
TiledRgbaInputFile::tileYSize () const
{
     return _inputFile->tileYSize();
}


LevelMode
TiledRgbaInputFile::levelMode () const
{
     return _inputFile->levelMode();
}


LevelRoundingMode
TiledRgbaInputFile::levelRoundingMode () const
{
     return _inputFile->levelRoundingMode();
}


int
TiledRgbaInputFile::numLevels () const
{
     return _inputFile->numLevels();
}


int
TiledRgbaInputFile::numXLevels () const
{
     return _inputFile->numXLevels();
}


int
TiledRgbaInputFile::numYLevels () const
{
     return _inputFile->numYLevels();
}


bool
TiledRgbaInputFile::isValidLevel (int lx, int ly) const
{
    return _inputFile->isValidLevel (lx, ly);
}


int
TiledRgbaInputFile::levelWidth (int lx) const
{
     return _inputFile->levelWidth (lx);
}


int
TiledRgbaInputFile::levelHeight (int ly) const
{
     return _inputFile->levelHeight (ly);
}


int
TiledRgbaInputFile::numXTiles (int lx) const
{
     return _inputFile->numXTiles(lx);
}


int
TiledRgbaInputFile::numYTiles (int ly) const
{
     return _inputFile->numYTiles(ly);
}


Imath::Box2i
TiledRgbaInputFile::dataWindowForLevel (int l) const
{
     return _inputFile->dataWindowForLevel (l);
}


Imath::Box2i
TiledRgbaInputFile::dataWindowForLevel (int lx, int ly) const
{
     return _inputFile->dataWindowForLevel (lx, ly);
}


Imath::Box2i
TiledRgbaInputFile::dataWindowForTile (int dx, int dy, int l) const
{
     return _inputFile->dataWindowForTile (dx, dy, l);
}


Imath::Box2i
TiledRgbaInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
{
     return _inputFile->dataWindowForTile (dx, dy, lx, ly);
}


void
TiledRgbaInputFile::readTile (int dx, int dy, int l)
{
    if (_fromYa)
    {
	Lock lock (*_fromYa);
	_fromYa->readTile (dx, dy, l, l);
    }
    else
    {
	 _inputFile->readTile (dx, dy, l);
    }
}


void
TiledRgbaInputFile::readTile (int dx, int dy, int lx, int ly)
{
    if (_fromYa)
    {
	Lock lock (*_fromYa);
	_fromYa->readTile (dx, dy, lx, ly);
    }
    else
    {
	 _inputFile->readTile (dx, dy, lx, ly);
    }
}


void	
TiledRgbaInputFile::readTiles (int dxMin, int dxMax, int dyMin, int dyMax,
                               int lx, int ly)
{
    if (_fromYa)
    {
	Lock lock (*_fromYa);

        for (int dy = dyMin; dy <= dyMax; dy++)
            for (int dx = dxMin; dx <= dxMax; dx++)
	        _fromYa->readTile (dx, dy, lx, ly);
    }
    else
    {
        _inputFile->readTiles (dxMin, dxMax, dyMin, dyMax, lx, ly);
    }
}

void	
TiledRgbaInputFile::readTiles (int dxMin, int dxMax, int dyMin, int dyMax,
                               int l)
{
    readTiles (dxMin, dxMax, dyMin, dyMax, l, l);
}


void		
TiledRgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
{
    _outputFile->updatePreviewImage (newPixels);
}


void	
TiledRgbaOutputFile::breakTile  (int dx, int dy, int lx, int ly,
				 int offset, int length, char c)
{
    _outputFile->breakTile (dx, dy, lx, ly, offset, length, c);
}


} // namespace Imf
