readDeepTile(T& in,bool reduceMemory , bool reduceTime)
{
    bool threw = false;
    try
    {
        const Header& fileHeader = in.header();

        Array2D<unsigned int> localSampleCount;

        Box2i dataWindow = fileHeader.dataWindow();

        int height = dataWindow.size().y+1;
        int width = dataWindow.size().x+1;

        const TileDescription& td = in.header().tileDescription();
        int tileWidth = td.xSize;
        int tileHeight = td.ySize;
        int numYLevels = in.numYLevels();
        int numXLevels = in.numXLevels();


        localSampleCount.resizeErase(height, width);

        int channelCount=0;
        for(ChannelList::ConstIterator i=fileHeader.channels().begin();i!=fileHeader.channels().end();++i, channelCount++);

        Array<Array2D< float* > > data(channelCount);

        for (int i = 0; i < channelCount; i++)
        {
            data[i].resizeErase(height, width);
        }

        DeepFrameBuffer frameBuffer;

        int memOffset = dataWindow.min.x + dataWindow.min.y * width;
        frameBuffer.insertSampleCountSlice (Slice (UINT,
                                                   (char *) (&localSampleCount[0][0] - memOffset),
                                                   sizeof (unsigned int) * 1,
                                                   sizeof (unsigned int) * width,
                                                   0.0, // fill
                                                   1 , 1, // x/ysampling
                                                   true,  // relative x
                                                   true  // relative y
                                                  )
                                                   );

        int channel = 0;
         for (ChannelList::ConstIterator i=fileHeader.channels().begin();i!=fileHeader.channels().end();++i, ++channel)
         {
             int sampleSize  = sizeof (float);

             int pointerSize = sizeof (char *);

             frameBuffer.insert (i.name(),
                                 DeepSlice (FLOAT,
                                            (char *) (&data[channel][0][0] - memOffset),
                                            pointerSize * 1,
                                            pointerSize * width,
                                            sampleSize,
                                            0.0,
                                            1 , 1,
                                            true,
                                            true
                                           ) );
         }

         in.setFrameBuffer(frameBuffer);
         size_t step = 1;

         if (reduceTime && in.numXTiles(0) * in.numYTiles(0) > 2000)
         {
            step = max(1, (in.numXTiles(0) * in.numYTiles(0)) / 2000 );
         }

         int tileIndex = 0;
         bool isRipMap = td.mode == RIPMAP_LEVELS;


         vector<float> pixelBuffer;


         for (int ly = 0; ly < numYLevels; ly++)
         {
             for (int lx = 0; lx < numXLevels; lx++)
             {


                //
                // read all tiles from all levels.
                //
                for (int ylevel = 0; ylevel < numYLevels; ++ylevel )
                {
                    for (int xlevel = 0; xlevel < numXLevels; ++xlevel )
                    {
                        for(int y  = 0 ; y < in.numYTiles(ylevel) ; ++y )
                        {
                            for(int x = 0 ; x < in.numXTiles(xlevel) ; ++x )
                            {
                                if(tileIndex % step == 0)
                                {
                                    try
                                    {

                                        in.readPixelSampleCounts( x , y , x , y, lx, ly);


                                        size_t bufferSize = 0;

                                        for (int ty = 0 ; ty < tileHeight ; ++ty )
                                        {
                                            for (int tx = 0 ; tx < tileWidth ; ++tx )
                                            {
                                                if (!reduceMemory || localSampleCount[ty][tx] <  100)
                                                {
                                                    bufferSize += channelCount * localSampleCount[ty][tx];
                                                }
                                            }
                                        }

                                        // limit total samples allocated for this tile
                                        if (!reduceMemory || bufferSize < 1<<12)
                                        {

                                            pixelBuffer.resize( bufferSize );
                                            size_t bufferIndex = 0;

                                            for (int ty = 0 ; ty < tileHeight ; ++ty )
                                            {
                                                for (int tx = 0 ; tx < tileWidth ; ++tx )
                                                {
                                                    if (!reduceMemory || localSampleCount[ty][tx] <  100)
                                                    {
                                                        for (int k = 0 ; k < channelCount ; ++k )
                                                        {
                                                           data[k][ty][tx] = &pixelBuffer[bufferIndex];
                                                           bufferIndex += localSampleCount[ty][tx];
                                                        }
                                                    }
                                                    else
                                                    {
                                                        for (int k = 0 ; k < channelCount ; ++k )
                                                        {
                                                            data[k][ty][tx] = nullptr;
                                                        }
                                                    }
                                                }
                                            }


                                            in.readTile ( x, y, xlevel , ylevel);
                                        }
                                    }

                                    catch(...)
                                    {
                                        //
                                        // for one level and mipmapped images,
                                        // xlevel must match ylevel,
                                        // otherwise an exception is thrown
                                        // ignore that exception
                                        //
                                        if (isRipMap || xlevel==ylevel)
                                        {
                                            threw = true;
                                        }

                                    }
                                }
                                tileIndex++;
                            }
                        }
                    }
                }
             }
         }
    }catch(...)
    {
        threw = true;
    }
    return threw;
}
readDeepTile(T& in,bool reduceMemory , bool reduceTime)
{
    bool threw = false;
    try
    {
        const Header& fileHeader = in.header();

        Array2D<unsigned int> localSampleCount;

        Box2i dataWindow = fileHeader.dataWindow();

        int height = dataWindow.size().y+1;
        int width = dataWindow.size().x+1;

        const TileDescription& td = in.header().tileDescription();
        int tileWidth = td.xSize;
        int tileHeight = td.ySize;
        int numYLevels = in.numYLevels();
        int numXLevels = in.numXLevels();


        localSampleCount.resizeErase(height, width);

        int channelCount=0;
        for(ChannelList::ConstIterator i=fileHeader.channels().begin();i!=fileHeader.channels().end();++i, channelCount++);

        Array<Array2D< float* > > data(channelCount);

        for (int i = 0; i < channelCount; i++)
        {
            data[i].resizeErase(height, width);
        }

        DeepFrameBuffer frameBuffer;

        int memOffset = dataWindow.min.x + dataWindow.min.y * width;
        frameBuffer.insertSampleCountSlice (Slice (UINT,
                                                   (char *) (&localSampleCount[0][0] - memOffset),
                                                   sizeof (unsigned int) * 1,
                                                   sizeof (unsigned int) * width,
                                                   0.0,  
                                                   1 , 1,  
                                                   true,   
                                                   true   
                                                  )
                                                   );

        int channel = 0;
         for (ChannelList::ConstIterator i=fileHeader.channels().begin();i!=fileHeader.channels().end();++i, ++channel)
         {
             int sampleSize  = sizeof (float);

             int pointerSize = sizeof (char *);

             frameBuffer.insert (i.name(),
                                 DeepSlice (FLOAT,
                                            (char *) (&data[channel][0][0] - memOffset),
                                            pointerSize * 1,
                                            pointerSize * width,
                                            sampleSize,
                                            0.0,
                                            1 , 1,
                                            true,
                                            true
                                           ) );
         }

         in.setFrameBuffer(frameBuffer);
         size_t step = 1;

         if (reduceTime && in.numXTiles(0) * in.numYTiles(0) > 2000)
         {
            step = max(1, (in.numXTiles(0) * in.numYTiles(0)) / 2000 );
         }

         int tileIndex = 0;
         bool isRipMap = td.mode == RIPMAP_LEVELS;


         vector<float> pixelBuffer;


         for (int ly = 0; ly < numYLevels; ly++)
         {
             for (int lx = 0; lx < numXLevels; lx++)
             {


                 
                 
                 
                for (int ylevel = 0; ylevel < numYLevels; ++ylevel )
                {
                    for (int xlevel = 0; xlevel < numXLevels; ++xlevel )
                    {
                        for(int y  = 0 ; y < in.numYTiles(ylevel) ; ++y )
                        {
                            for(int x = 0 ; x < in.numXTiles(xlevel) ; ++x )
                            {
                                if(tileIndex % step == 0)
                                {
                                    try
                                    {

                                        in.readPixelSampleCounts( x , y , x , y, lx, ly);


                                        size_t bufferSize = 0;

                                        for (int ty = 0 ; ty < tileHeight ; ++ty )
                                        {
                                            for (int tx = 0 ; tx < tileWidth ; ++tx )
                                            {
                                                if (!reduceMemory || localSampleCount[ty][tx] <  100)
                                                {
                                                    bufferSize += channelCount * localSampleCount[ty][tx];
                                                }
                                            }
                                        }

                                         
                                        if (!reduceMemory || bufferSize < 1<<12)
                                        {

                                            pixelBuffer.resize( bufferSize );
                                            size_t bufferIndex = 0;

                                            for (int ty = 0 ; ty < tileHeight ; ++ty )
                                            {
                                                for (int tx = 0 ; tx < tileWidth ; ++tx )
                                                {
                                                    if (!reduceMemory || localSampleCount[ty][tx] <  100)
                                                    {
                                                        for (int k = 0 ; k < channelCount ; ++k )
                                                        {
                                                           data[k][ty][tx] = &pixelBuffer[bufferIndex];
                                                           bufferIndex += localSampleCount[ty][tx];
                                                        }
                                                    }
                                                    else
                                                    {
                                                        for (int k = 0 ; k < channelCount ; ++k )
                                                        {
                                                            data[k][ty][tx] = nullptr;
                                                        }
                                                    }
                                                }
                                            }


                                            in.readTile ( x, y, xlevel , ylevel);
                                        }
                                    }

                                    catch(...)
                                    {
                                         
                                         
                                         
                                         
                                         
                                         
                                        if (isRipMap || xlevel==ylevel)
                                        {
                                            threw = true;
                                        }

                                    }
                                }
                                tileIndex++;
                            }
                        }
                    }
                }
             }
         }
    }catch(...)
    {
        threw = true;
    }
    return threw;
}