B44Compressor::compress (const char *inPtr,
			 int inSize,
			 IMATH_NAMESPACE::Box2i range,
			 const char *&outPtr)
{
    //
    // Compress a block of pixel data:  First copy the input pixels
    // from the input buffer into _tmpBuffer, rearranging them such
    // that blocks of 4x4 pixels of a single channel can be accessed
    // conveniently.  Then compress each 4x4 block of HALF pixel data
    // and append the result to the output buffer.  Copy UINT and
    // FLOAT data to the output buffer without compressing them.
    //

    outPtr = _outBuffer;

    if (inSize == 0)
    {
	//
	// Special case - empty input buffer.
	//

	return 0;
    }

    //
    // For each channel, detemine how many pixels are stored
    // in the input buffer, and where those pixels will be
    // placed in _tmpBuffer.
    //

    int minX = range.min.x;
    int maxX = min (range.max.x, _maxX);
    int minY = range.min.y;
    int maxY = min (range.max.y, _maxY);
    
    unsigned short *tmpBufferEnd = _tmpBuffer;
    int i = 0;

    for (ChannelList::ConstIterator c = _channels.begin();
	 c != _channels.end();
	 ++c, ++i)
    {
	ChannelData &cd = _channelData[i];

	cd.start = tmpBufferEnd;
	cd.end = cd.start;

	cd.nx = numSamples (c.channel().xSampling, minX, maxX);
	cd.ny = numSamples (c.channel().ySampling, minY, maxY);

	tmpBufferEnd += cd.nx * cd.ny * cd.size;
    }

    if (_format == XDR)
    {
	//
	// The data in the input buffer are in the machine-independent
	// Xdr format.  Copy the HALF channels into _tmpBuffer and
	// convert them back into native format for compression.
	// Copy UINT and FLOAT channels verbatim into _tmpBuffer.
	//

	for (int y = minY; y <= maxY; ++y)
	{
	    for (int i = 0; i < _numChans; ++i)
	    {
		ChannelData &cd = _channelData[i];

		if (modp (y, cd.ys) != 0)
		    continue;

		if (cd.type == HALF)
		{
		    for (int x = cd.nx; x > 0; --x)
		    {
			Xdr::read <CharPtrIO> (inPtr, *cd.end);
			++cd.end;
		    }
		}
		else
		{
		    int n = cd.nx * cd.size;
		    memcpy (cd.end, inPtr, n * sizeof (unsigned short));
		    inPtr += n * sizeof (unsigned short);
		    cd.end += n;
		}
	    }
	}
    }
    else
    {
	//
	// The input buffer contains only HALF channels, and they
	// are in native, machine-dependent format.  Copy the pixels
	// into _tmpBuffer.
	//

	for (int y = minY; y <= maxY; ++y)
	{
	    for (int i = 0; i < _numChans; ++i)
	    {
		ChannelData &cd = _channelData[i];

		#if defined (DEBUG)
		    assert (cd.type == HALF);
		#endif

		if (modp (y, cd.ys) != 0)
		    continue;

		int n = cd.nx * cd.size;
		memcpy (cd.end, inPtr, n * sizeof (unsigned short));
		inPtr  += n * sizeof (unsigned short);
		cd.end += n;
	    }
	}
    }

    //
    // The pixels for each channel have been packed into a contiguous
    // block in _tmpBuffer.  HALF channels are in native format; UINT
    // and FLOAT channels are in Xdr format.
    //

    #if defined (DEBUG)

	for (int i = 1; i < _numChans; ++i)
	    assert (_channelData[i-1].end == _channelData[i].start);

	assert (_channelData[_numChans-1].end == tmpBufferEnd);

    #endif

    //
    // For each HALF channel, split the data in _tmpBuffer into 4x4
    // pixel blocks.  Compress each block and append the compressed
    // data to the output buffer.
    //
    // UINT and FLOAT channels are copied from _tmpBuffer into the
    // output buffer without further processing.
    //

    char *outEnd = _outBuffer;

    for (int i = 0; i < _numChans; ++i)
    {
	ChannelData &cd = _channelData[i];
	
	if (cd.type != HALF)
	{
	    //
	    // UINT or FLOAT channel.
	    //

	    int n = cd.nx * cd.ny * cd.size * sizeof (unsigned short);
	    memcpy (outEnd, cd.start, n);
	    outEnd += n;

	    continue;
	}
	
	//
	// HALF channel
	//

	for (int y = 0; y < cd.ny; y += 4)
	{
	    //
	    // Copy the next 4x4 pixel block into array s.
	    // If the width, cd.nx, or the height, cd.ny, of
	    // the pixel data in _tmpBuffer is not divisible
	    // by 4, then pad the data by repeating the
	    // rightmost column and the bottom row.
	    // 

	    unsigned short *row0 = cd.start + y * cd.nx;
	    unsigned short *row1 = row0 + cd.nx;
	    unsigned short *row2 = row1 + cd.nx;
	    unsigned short *row3 = row2 + cd.nx;

	    if (y + 3 >= cd.ny)
	    {
		if (y + 1 >= cd.ny)
		    row1 = row0;

		if (y + 2 >= cd.ny)
		    row2 = row1;

		row3 = row2;
	    }

	    for (int x = 0; x < cd.nx; x += 4)
	    {
		unsigned short s[16];

		if (x + 3 >= cd.nx)
		{
		    int n = cd.nx - x;

		    for (int i = 0; i < 4; ++i)
		    {
			int j = min (i, n - 1);

			s[i +  0] = row0[j];
			s[i +  4] = row1[j];
			s[i +  8] = row2[j];
			s[i + 12] = row3[j];
		    }
		}
		else
		{
		    memcpy (&s[ 0], row0, 4 * sizeof (unsigned short));
		    memcpy (&s[ 4], row1, 4 * sizeof (unsigned short));
		    memcpy (&s[ 8], row2, 4 * sizeof (unsigned short));
		    memcpy (&s[12], row3, 4 * sizeof (unsigned short));
		}

		row0 += 4;
		row1 += 4;
		row2 += 4;
		row3 += 4;

		//
		// Compress the contents of array s and append the
		// results to the output buffer.
		//

		if (cd.pLinear)
		    convertFromLinear (s);

		outEnd += pack (s, (unsigned char *) outEnd,
				_optFlatFields, !cd.pLinear);
	    }
	}
    }

    return outEnd - _outBuffer;
}