// Aqsis
// Copyright (c) 1997 - 2001, Paul C. Gregory
//
// Contact: pgregory@aqsis.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/**
 * Copyright (C) 2006-2007  NTT DATA CORPORATION
 * 
 * Version: 1.0.0 2007/04/01
 *  
 */
package net.cellcomputing.himawari.library;

import static net.cellcomputing.himawari.library.EqMapType.MapType_Environment;
import static net.cellcomputing.himawari.library.EqMapType.MapType_Shadow;
import static net.cellcomputing.himawari.library.EqWrapMode.WrapMode_Black;
import static net.cellcomputing.himawari.library.EqWrapMode.WrapMode_Clamp;
import static net.cellcomputing.himawari.library.EqWrapMode.WrapMode_Periodic;
import static net.cellcomputing.himawari.library.RiGlobal.QGetRenderContext;
import static net.cellcomputing.himawari.library.RiGlobal.QGetRenderContextI;

import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;

import javax.imageio.ImageIO;

import net.cellcomputing.himawari.accessory.STLVector;
import net.cellcomputing.himawari.accessory.Valarray;
import net.cellcomputing.himawari.accessory.primitive.p_String;
import net.cellcomputing.himawari.accessory.primitive.p_float;
import net.cellcomputing.himawari.display.ZFileRequest;
import net.cellcomputing.himawari.library.types.CqMatrix;
import net.cellcomputing.himawari.library.types.CqVector3D;
import net.cellcomputing.himawari.library.types.PublicFunctions;
import net.cellcomputing.himawari.main.Globalmain;
import net.cellcomputing.himawari.util.CellCipher;
import net.cellcomputing.himawari.util.HimawariLogger;
/**
 * テクスチャ
 * 
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqTextureMap extends IqTextureMap{
	
	//tiff.hに定義されている定数
	public static final int COMPRESSION_NONE 		= 1;
	public static final int PLANARCONFIG_CONTIG 	= 1;
	

	static	ArrayList<CqTextureMap>	m_TextureMap_Cache = new ArrayList<CqTextureMap>();	///< Static array of loaded textures.

//	static	STLVector<CqTextureMap>	m_TextureMap_Cache = new STLVector<CqTextureMap>(CqTextureMap.class);	///< Static array of loaded textures.
	static STLVector<p_String>	m_ConvertString_Cache = new STLVector<p_String>(p_String.class);///< Static array of filename (after conversion)
	
	int m_Compression;            ///< TIFF Compression model
	int m_Quality;                ///< If Jpeg compression is used than its overall quality
	
	float m_MinZ;                 ///< Minimum Depth
	long	m_XRes;					///< Horizontal resolution.
	long	m_YRes;					///< Vertical resolution.
	int	m_PlanarConfig;			///< TIFF planar configuration type.
	int	m_SamplesPerPixel;		///< Number of samples per pixel.
	int	m_SampleFormat;			///< Format of the sample elements, i.e. RGBA, or IEEE
	int	m_BitsPerSample;		///< Number of bits per sample element, 8 or 16.
	
	EqTexFormat	m_Format;			///< Image storage format type.
	
	String	m_strName;			///< Name of the image.
//	TIFF	m_pImage;			///< Pointer to an opened TIFF image.
	BufferedImage	m_pImage;			///< Pointer to an opened TIFF image.
	boolean	m_IsValid;			///< Indicate whether this image has been successfully opened.
	EqWrapMode m_smode;        ///< Periodic, black, clamp
	EqWrapMode m_tmode;        ///< Periodic, black, clamp
	Method m_FilterFunc;       ///< Catmull-Rom, sinc, disk, ... pixelfilter
	float m_swidth, m_twidth;   ///< for the pixel's filter
	LinkedList<CqTextureMapBuffer>	m_apSegments = new LinkedList<CqTextureMapBuffer>();	///< Array of cache segments related to this image.
	
	CqMatrix	m_matWorldToScreen;		///< Matrix to convert points from world space to screen space.
	
	float		m_sblur;
	float		m_tblur;
	float		m_pswidth;
	float		m_ptwidth;
	float		m_samples;
	
	// Temporary values used during sampling.
	Valarray m_tempval1 = new Valarray();
	Valarray m_tempval2 = new Valarray();
	Valarray m_tempval3 = new Valarray();
	Valarray m_tempval4 = new Valarray();
	Valarray m_low_color = new Valarray();
	Valarray m_high_color = new Valarray();
	
	// in hope to correlate all the microgrids together keep track
	// of the last (non-zero) directory (id, idx) so later we could
	// re-use the same level of mipmap.
	int    m_Directory;
	
	
	//--texturemap.cppに定義されている関数
	public float INTERPOLATE1(float A,float B, float C){
		return (A + (B-A) * C);
	}
	
	public float INTERPOLATE2(float A,float B,float C,float D,float U,float V){
		return INTERPOLATE1(INTERPOLATE1(A,B,U), INTERPOLATE1(C,D,U), V);
	}
	
	//下で利用される変数
	static int correct;
	
	public static int ForceCorrect()
	{

	    correct = -1;
	    if (correct == -1)
	    {
	        int[] pCorrect = (int[])  QGetRenderContextI().GetIntegerOption("limits","texturecorrect");

	        correct = 0;
	        if (pCorrect != null) {
	            correct = pCorrect[0];
	        }
	    }
	    return correct;
	}
	
	public CqTextureMap( final String strName ){
		m_Compression = COMPRESSION_NONE ;
		m_Quality = 70 ;
		m_MinZ = RiGlobal.RI_FLOATMAX ;
		m_XRes = 0 ;
		m_YRes = 0 ;
		m_PlanarConfig = PLANARCONFIG_CONTIG ;
		m_SamplesPerPixel = 3 ;
		m_Format = new EqTexFormat(EqTexFormat.TexFormat_Plain) ;
		m_strName = strName ;
		m_pImage = null ;
		m_IsValid = true ;
		m_smode = new EqWrapMode(WrapMode_Black) ;
		m_tmode = new EqWrapMode(WrapMode_Black) ;
		try {
			m_FilterFunc = RendermanInterface.class.getMethod("RiBoxFilter",float.class, float.class, float.class, float.class)  ;
		} catch (SecurityException e) {
			HimawariLogger.outputException(e);
			m_FilterFunc = null;
		} catch (NoSuchMethodException e) {
			HimawariLogger.outputException(e);
			m_FilterFunc = null;
		}
		m_swidth = 1.0f ; 
		m_twidth = 1.0f ;
		m_tempval1.resize( m_SamplesPerPixel );
		m_tempval2.resize( m_SamplesPerPixel );
		m_tempval3.resize( m_SamplesPerPixel );
		m_tempval4.resize( m_SamplesPerPixel );
		m_low_color.resize( m_SamplesPerPixel );
		m_high_color.resize( m_SamplesPerPixel );
	}
	
	/**
	 * デストラクタ
	 *
	 */
	public void destruct(){
	    // Search for it in the cache and remove the reference

	    for (CqTextureMap i : m_TextureMap_Cache)
	    {
	        if ( ( i ) == this )
	        {
	            m_TextureMap_Cache.remove( i );
	            break;
	        }
	    }


	    // Delete any held cache buffer segments.
	    
	    for (CqTextureMapBuffer s : m_apSegments )
	        s.destruct();

	    m_apSegments.clear();


	}
	
	//下の関数で利用されている
	static int size = -1;
	static CqTextureMap previous = null;
	
	/**
	 * テクスチャマップがキャッシュにあるかチェックする。
	 * キャッシュにあればそのポインタを返す。
	 * なければメモリに空きがあればロードする。
	 * 
	 * @param strName テクスチャの名前
	 * @return
	 */
	public static	CqTextureMap GetTextureMap( final String strName )
	{		
		QGetRenderContext().Stats().IncTextureMisses( 0 );
		
		/* look if the last item return by this function was ok */
		if ( size == (int)( m_TextureMap_Cache.size() ) )
			if ( ( previous != null ) && ( previous.m_strName.equals( strName) ) )
			{
				QGetRenderContext().Stats().IncTextureHits( 0, 0 );
				return previous;
			}
		
		
		
		// First search the texture map cache
		//名前でテクスチャの検索を行う
		for ( CqTextureMap i : m_TextureMap_Cache)
		{
			if ( ( i ).m_strName.equals(strName) )
			{
				if ( ( i ) .Type().getValue() == EqMapType.MapType_Texture )
				{
					previous = i;
					size = m_TextureMap_Cache.size();
					QGetRenderContext().Stats().IncTextureHits( 1, 0 );
					return ( i );
				}
				else
				{
					return ( null );
				}
			}
		}
		// If we got here, it doesn't exist yet, so we must create and load it.
		CqTextureMap pNew = new CqTextureMap( strName );
		pNew.Open();


		// Ensure that it is in the correct format
		if ( pNew.Format().getValue() != EqTexFormat.TexFormat_MIPMAP )
		{
			if( !pNew.CreateMIPMAP( true ) )
				pNew.SetInvalid();
			pNew.Close();
		}
		
//		m_TextureMap_Cache.insertElementAt( pNew ,0);
		m_TextureMap_Cache.add( pNew );
		previous = pNew;
		size = m_TextureMap_Cache.size();

		return ( pNew );
		
	}
    static int env_size = -1;
    static CqTextureMap env_previous = null;
    
	public static	CqTextureMap GetEnvironmentMap( final String strName )
	{


	    QGetRenderContext().Stats().IncTextureMisses( 1 );

	    /* look if the last item return by this function was ok */
	    if ( env_size == ( m_TextureMap_Cache.size() ) )
	        if ( ( env_previous != null ) && ( env_previous.m_strName.equals(strName )) )
	        {
	            QGetRenderContext().Stats().IncTextureHits( 0, 1 );
	            return env_previous;
	        }



	    // First search the texture map cache
	    for ( CqTextureMap i :m_TextureMap_Cache)
	    {
	        if ( ( i ). m_strName == strName )
	        {
	            if ( ( i ). Type().getValue() == MapType_Environment )
	            {
	                env_previous = i;
	                env_size = m_TextureMap_Cache.size();
	                QGetRenderContext(). Stats().IncTextureHits( 1, 1 );
	                return ( i );
	            }
	            else
	            {
	                return ( null);
	            }
	        }
	    }
	    // If we got here, it doesn't exist yet, so we must create and load it.
	    CqTextureMap pNew = new CqEnvironmentMap( strName );
	    m_TextureMap_Cache.add( pNew );
	    pNew.Open();

//	    TqPchar ptexfmt = 0;

	    // Invalid if the m_pImage is not there or it is not cube or latlong env. map file
	    if ( pNew.m_pImage == null/* ||
	            TIFFGetField( pNew.m_pImage, TIFFTAG_PIXAR_TEXTUREFORMAT, ptexfmt ) != 1 ||
	            ( strcmp( ptexfmt, CUBEENVMAP_HEADER ) != 0 ) && ( strcmp( ptexfmt, LATLONG_HEADER ) != 0 )*/ )
	    {
	        HimawariLogger.getLogger().error("Map \""  +strName +  "\" is not an environment map, use RiMakeCubeFaceEnvironment\n");
	        pNew.SetInvalid();
	         pNew.destruct();
	        pNew = null;

	    }
		if (pNew != null &&  pNew.Format().getValue() != EqTexFormat.TexFormat_MIPMAP )
		{
			if( !pNew.CreateMIPMAP( true ) )
				pNew.SetInvalid();
//			pNew.Close();
		}
//	    // remove from the list a LatLong env. map since in shadeops.cpp we will cope with it.
//	    if ( ptexfmt && strcmp( ptexfmt, LATLONG_HEADER ) == 0 )
//	    {
//	        pNew.SetInvalid();
//	        delete pNew;
//	        pNew = NULL;
//	    }

	    env_previous = pNew;
	    env_size = m_TextureMap_Cache.size();
	    return ( pNew );
	}
	
	
	public void shadowOpen(){
	    m_IsValid = false;


	    // Find the file required.
	    CqRiFile fileImage = new CqRiFile( m_strName, "texture" );
	    
	    if ( !fileImage.IsValid() )
	    {
	        HimawariLogger.getLogger().error("Cannot open texture file \"" + m_strName  +"\"\n");
	        return ;
	    }
	    
	    String strRealName  =  fileImage.strRealName();
	    fileImage.Close();
	    //コンバートの必要がないため直接ファイルを開きに行く
	    RandomAccessFile file =null;
	    try {
		    file = new RandomAccessFile(strRealName,"r");
	    	m_SamplesPerPixel=1;
			    // Resize the temporary storage values to the new depth.
			    m_tempval1.resize( m_SamplesPerPixel );
			    m_tempval2.resize( m_SamplesPerPixel );
			    m_tempval3.resize( m_SamplesPerPixel );
			    m_tempval4.resize( m_SamplesPerPixel );
			    m_low_color.resize( m_SamplesPerPixel );
			    m_high_color.resize( m_SamplesPerPixel );

			    /* Aqsis supports a slighty different scheme for MipMap tiff file;
			     * its filtering is kept as a string in 
			     * Texture Wrap Modes: "periodic periodic box 1.000000 1.000000"
			     * where AIR, 3Delight, BMRT, RDC use very basic texture wrap mode description eg.
			     * Texture Wrap Modes: "black,black"
			     * therefore I initialized the value for filtering to be black, black, box, 1.0, 1.0
			     * 
			     */
			    //メタ情報を読み込む
			    String tag_name = file.readLine();
			    if(tag_name!= null && tag_name.equals(ZFileRequest.START_SHADOW_HEADER)){
			    	tag_name = file.readLine();
			    	if(tag_name.contains(ZFileRequest.SHADOW_WIDTH)){
			    		String[] split = tag_name.split("\\W");
			    		m_XRes = Integer.parseInt(split[1]);
			    	}
			    	
			    	tag_name = file.readLine();
			    	if(tag_name != null && tag_name.contains(ZFileRequest.SHADOW_HEIGHT)){
			    		String[] split = tag_name.split("\\W");
			    		m_YRes = Integer.parseInt(split[1]);			    		
			    	}else{
				    	m_IsValid = false;
						//ファイル形式が不正な場合の処理
						HimawariLogger.getLogger().error("invalid image file \""+ m_strName +"\"\n");
						return;
			    		
			    	}
				    m_Format = new EqTexFormat(EqTexFormat.TexFormat_Plain);
				    m_IsValid = true;
				    m_Directory = 0;
			    }else{
			    	m_IsValid = false;
					//ファイル形式が不正な場合の処理
					HimawariLogger.getLogger().error("invalid image file \""+ m_strName +"\"\n");
					return;
						
			    }



		}catch(IOException e){
			HimawariLogger.outputException(e);
		}finally{
			if(file !=null)
				try {
					file.close();
				} catch (IOException e1) {
//					e1.printStackTrace();
					HimawariLogger.outputException(e1);
				}
		}
	}
	
	static int shadow_size = -1;
    static CqTextureMap shadow_previous = null;
	
    public static	CqTextureMap GetShadowMap( final String strName )
	{
	    

	    QGetRenderContext().Stats().IncTextureMisses( 3 );

	    if ( shadow_size == ( m_TextureMap_Cache.size() ) )
	        if ( ( shadow_previous!= null) && ( shadow_previous.m_strName.equals(strName) ) )
	        {
	            QGetRenderContext().Stats().IncTextureHits( 0, 3 );
	            return shadow_previous;
	        }


	    // First search the texture map cache
	    for ( CqTextureMap i : m_TextureMap_Cache)
	    {
	        if ( ( i ).m_strName.equals(strName) )
	        {
	            if ( ( i ).Type().getValue() == MapType_Shadow )
	            {
	                shadow_previous = i;
	                shadow_size = m_TextureMap_Cache.size();
	                QGetRenderContext().Stats().IncTextureHits( 1, 3 );
	                return ( i );
	            }
	            else
	            {
	                return (null );
	            }
	        }
	    }
	    // If we got here, it doesn't exist yet, so we must create and load it.
	    CqShadowMap pNew = new CqShadowMap( strName );
	    m_TextureMap_Cache.add( pNew );
//	    m_TextureMap_Cache.insertElementAt( pNew ,0);
	    pNew.shadowOpen();

//	    TqPchar ptexfmt;
//	    if ( pNew.m_pImage == null ||
//	            TIFFGetField( pNew.m_pImage, TIFFTAG_PIXAR_TEXTUREFORMAT, &ptexfmt ) != 1 ||
//	            /*strcmp( ptexfmt, SHADOWMAP_HEADER ) != 0*/ )
//	    {
//	        std::cerr << error << "Map \"" << strName.c_str() << "\" is not a valid shadow map, use RiMakeShadow" << std::endl;
//	        pNew->SetInvalid();
//	    }
//	    else
	    pNew.ReadMatrices();

	    shadow_previous = pNew;
	    shadow_size = m_TextureMap_Cache.size();
	    return ( pNew );
	}

    static int lat_size = -1;
    static CqTextureMap lat_previous = null;

    public static	CqTextureMap GetLatLongMap( final String strName )
	{
	    QGetRenderContext().Stats().IncTextureMisses( 2 );

	    if ( size == ( m_TextureMap_Cache.size() ) )
	        if ( lat_previous != null&& ( lat_previous.m_strName.equals(strName )) )
	        {
	            QGetRenderContext().Stats().IncTextureHits( 0, 2 );
	            return lat_previous;
	        }



	    // First search the texture map cache
	    for ( CqTextureMap i : m_TextureMap_Cache)
	    {
	        if ( ( i ).m_strName.equals( strName ))
	        {
	            if ( ( i ).Type().getValue() == EqMapType.MapType_LatLong )
	            {
	                lat_previous = i;
	                lat_size = m_TextureMap_Cache.size();
	                QGetRenderContext().Stats().IncTextureHits( 1, 2 );
	                return ( i );
	            }
	            else
	            {
	                return ( null );
	            }
	        }
	    }
	    // If we got here, it doesn't exist yet, so we must create and load it.
	    CqTextureMap pNew = new CqLatLongMap( strName );
	    m_TextureMap_Cache.add( pNew );
	    pNew.Open();


	    // Invalid only if this is not a LatLong Env. map file
	    if ( pNew.m_pImage == null /*||
	            TIFFGetField( pNew->m_pImage, TIFFTAG_PIXAR_TEXTUREFORMAT, &ptexfmt ) != 1 ||
	            strcmp( ptexfmt, LATLONG_HEADER ) != 0*/ )
	    {
	        HimawariLogger.getLogger().error("Map \""  + strName + "\" is not an environment map, use RiMakeLatLongEnvironment\n" );

	        pNew.SetInvalid();
	    }

	    lat_previous = pNew;
	    lat_size = m_TextureMap_Cache.size();
	    return ( pNew );
	}
	
	
	/** 
	 * m_IsValidフラグをfalseに設定する。
	 * イメージファイルのOpenに失敗したことを意味する。
	 * Set the flag indicating that this image has not been successfully opened.
	 */
	void	SetInvalid()
	{
		m_IsValid = false;
	}
	
	public boolean CreateMIPMAP(){
		return CreateMIPMAP(false);
	}
	
//	---------------------------------------------------------------------
	/** Create a mipmap usable for the texturemapping.
	 * MIPMAPを作成する
	 */
	public boolean CreateMIPMAP( boolean fProtectBuffers )
	{
		if ( m_pImage != null )
		{
			// Check if the format is normal scanline, otherwise we are unable to MIPMAP it yet.
		// Read the whole image into a buffer.
		CqTextureMapBuffer  pBuffer = GetBuffer( 0, 0, 0, fProtectBuffers );
		
		int m_xres = (int) m_XRes;
		int m_yres = (int)m_YRes;
		int directory = 0;
		//MIPMAPをメモリに展開する
		do
		{
			//--2007/12/07 add directory0がすでに作成されていたら、skipする 2007/12/03 watanabei
			if(m_apSegments.getFirst().m_Directory ==0 &&
					m_apSegments.getFirst().m_Width ==m_xres &&
					m_apSegments.getFirst().m_Height ==m_yres){
				m_xres /= 2;
				m_yres /= 2;
				directory++;				
			}
			//--
			CqTextureMapBuffer pTMB = CreateBuffer( 0, 0, m_xres, m_yres, directory, fProtectBuffers );
			
			if ( pTMB.pVoidBufferData() != null )
			{
				for ( int y = 0; y < m_yres; y++ )
				{
					//unsigned char accum[ 4 ];
//高速化					STLVector<Float> accum = new STLVector<Float>(Float.class);
					int temp = SamplesPerPixel();
					float[] accum = new float[temp];
					for ( int x = 0; x < m_xres; x++ )
					{
						ImageFilterVal2( pBuffer, x, y, directory, m_xres, m_yres, accum );
						
						for ( int sample = 0; sample < m_SamplesPerPixel; sample++ )
//高速化							pTMB.SetValue( x, y, sample, accum.elementAt( sample ) );
							pTMB.SetValue(x,y,sample,accum[sample ]);
					}
				}
				m_apSegments.add( pTMB );
			}
			m_xres /= 2;
			m_yres /= 2;
			directory++;
		}
		while ( ( m_xres > 2 ) && ( m_yres > 2 ) ) ;
		}
		
		return( true );
	}
	
	/**
	 * 
	 * テクスチャマップのキャッシュをクリアする
	 * 
	 */
	public static void FlushCache() {
		//デストラクタ内でベクターの要素を操作するため変更
		CqTextureMap i;
		while(m_TextureMap_Cache.size() > 0 && (i = m_TextureMap_Cache.get(0)) !=null ){
			i.destruct();
		}
//        for(CqTextureMap i : m_TextureMap_Cache )
//            i.destruct();

        m_TextureMap_Cache.clear();


	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#XRes()
	 */
	@Override
	public long XRes() {
		return m_XRes;
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#YRes()
	 */
	@Override
	public long YRes() {
		return m_YRes;
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#SamplesPerPixel()
	 */
	@Override
	public int SamplesPerPixel() {
		return m_SamplesPerPixel;
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#Format()
	 */
	@Override
	public EqTexFormat Format() {
		return ( m_Format );
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#Type()
	 */
	@Override
	public EqMapType Type() {
		
		return ( IsValid() ? new EqMapType(EqMapType.MapType_Texture) : new EqMapType(EqMapType.MapType_Invalid ));
	}
	
	protected Raster raster = null;
	/**
	 * textureファイルをメモリに読み込む。
	 * この時成功すればm_IsValidをtrueにする。
	 * 
	 */
	public void Open() {

	    m_IsValid = false;


	    // Find the file required.
	    CqRiFile fileImage = new CqRiFile( m_strName, "texture" );
	    
	    if ( !fileImage.IsValid() )
	    {
	        HimawariLogger.getLogger().error("Cannot open texture file \"" + m_strName  +"\"\n");
	        return ;
	    }
	    
	    String strRealName  =  fileImage.strRealName();
	    fileImage.Close();

	    //コンバートの必要がないため直接ファイルを開きに行く
	    InputStream is = null;
	    InputStreamReader isr = null;
	    BufferedReader br = null;
	    try {
//	    	is = new FileInputStream(new File(strRealName));
	    	if(Globalmain.g_enc_flag.value){
	    		is = CellCipher.getInputStream(strRealName);
	    	}
	    	else{
	    		is = new FileInputStream(strRealName);
	    	}
	    	
			m_pImage = ImageIO.read(is);

			//画像が取得できた場合は、続いてメタ情報を読みに行く。
			if ( m_pImage != null )
			{
			    HimawariLogger.getLogger().info("TextureMap: \"" + strRealName + "\" is open\n");

			    //イメージの幅を取得する
			    //イメージの高さを取得する
			    m_XRes = m_pImage.getWidth();
			    m_YRes = m_pImage.getHeight();

			    //SamplePerPixelを取得する
			    int samplesperpixel = 1;
			    samplesperpixel = m_pImage.getRaster().getNumBands();
			    m_SamplesPerPixel = samplesperpixel;
			    //SampleFormatを取得する

			    // Resize the temporary storage values to the new depth.
			    m_tempval1.resize( m_SamplesPerPixel );
			    m_tempval2.resize( m_SamplesPerPixel );
			    m_tempval3.resize( m_SamplesPerPixel );
			    m_tempval4.resize( m_SamplesPerPixel );
			    m_low_color.resize( m_SamplesPerPixel );
			    m_high_color.resize( m_SamplesPerPixel );

			    /* Aqsis supports a slighty different scheme for MipMap tiff file;
			     * its filtering is kept as a string in 
			     * Texture Wrap Modes: "periodic periodic box 1.000000 1.000000"
			     * where AIR, 3Delight, BMRT, RDC use very basic texture wrap mode description eg.
			     * Texture Wrap Modes: "black,black"
			     * therefore I initialized the value for filtering to be black, black, box, 1.0, 1.0
			     * 
			     */
			    //メタ情報を読み込む
			    isr = new InputStreamReader(is);
			    br = new BufferedReader(isr);
			    String pModes = br.readLine();
			    if ( pModes != null )
			    {
			        Interpreted( pModes );
			    }
//			   int tsx;

			    /* First tests; is it stored using tiles ? */
			   //Tileを利用している場合はタイルを取得する

//	        if ( bMipMap )
//	        {
//	            m_Format = TexFormat_MIPMAP;
//	            m_IsValid = TqTrue;
//	        }
//	        else
//	        {
			        m_Format = new EqTexFormat(EqTexFormat.TexFormat_Plain);
			        m_IsValid = true;
//	        }
			}else{
				//イメージファイルが存在しなかった場合の処理
				HimawariLogger.getLogger().error("invalid image file \""+ m_strName +"\"\n");
				return;
				
			}
			m_Directory = 0;
		} catch (FileNotFoundException e) {
			HimawariLogger.outputException(e);
			//すでに直前でファイルがない場合の処理は行われている
			
		} catch (IOException e) {
			HimawariLogger.outputException(e);
		}finally{
			try {
				if(is != null)
					is.close();
				if(isr != null)
					isr.close();
				if(br != null)
					br.close();
			} catch (IOException e) {
				HimawariLogger.outputException(e);
			}
		}
		
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#Close()
	 */
	@Override
	public void Close() {
		m_pImage = null;
		
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#IsValid()
	 */
	@Override
	public boolean IsValid() {
		
		return m_IsValid;
	}
	/** Figure out the value passed by the user in the ribfile:
	 *  blur, widths and samples (please not the m_samples is read but it 
	 *  is never used only in texture(), environment(), shadow(); it is simply used for now.
	 *  m_sblur = m_tblur = 0.0, m_pswidth = m_ptwidth = 1.0 and m_samples = 16.0 (for shadow) 
	 *  by default.
	 *  ribファイルに書かれている値を渡す。
	 *  
	 */
	public void PrepareSampleOptions(HashMap<String, IqShaderData> paramMap) {
		m_sblur = 0.0f;   // Turnoff the blur per texture(), environment() or shadow() by default
		m_tblur = 0.0f;
		m_pswidth = 1.0f; // In case of Trilinear sampling
		m_ptwidth = 1.0f;
		m_samples = 16.0f; // The shadow required to be init. at 16.0 by default
		
		// Get parameters out of the map.
		if ( paramMap.size() != 0 )
		{
			p_float temp_value = new p_float();
			
			if ( paramMap.containsKey( "width" )  )
			{
				paramMap.get( "width" ).GetFloat( temp_value );
				m_pswidth = temp_value.value;
				m_ptwidth = m_pswidth;
			}
			else
			{
				if ( paramMap.containsKey( "swidth" )){
					paramMap.get( "swidth" ) .GetFloat( temp_value);
					m_pswidth = temp_value.value;
				}
				if ( paramMap.containsKey( "twidth" )){
					paramMap.get( "twidth").GetFloat( temp_value);
					m_ptwidth = temp_value.value;
				}
			}
			if ( paramMap.containsKey( "blur" ))
			{
				paramMap.get("blur").GetFloat( temp_value);
				m_sblur = temp_value.value;
				m_sblur = m_sblur / FindBlurRatio();
				m_tblur = m_sblur;
			}
			else
			{
				if ( paramMap.containsKey( "sblur" )) {
					paramMap.get( "sblur" ).GetFloat( temp_value );
					m_sblur = temp_value.value;
					m_sblur = m_sblur / FindBlurRatio();
				}
				if ( paramMap.containsKey( "tblur" )) {
					paramMap.get( "tblur" ).GetFloat( temp_value );
					m_tblur = temp_value.value;
					m_tblur = m_tblur / FindBlurRatio();
				}
			}
			if ( paramMap.containsKey( "samples" )){
				paramMap.get( "samples" ).GetFloat( temp_value );
				m_samples = temp_value.value;
			}
		}
		
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#SampleMap(float, float, float, float, net.cellcomputing.himawari.accessory.Valarray)
	 */
	@Override
	public void SampleMap(float s1, float t1, float swidth, float twidth, Valarray val) {
		// Check the memory and make sure we don't abuse it
		CriticalMeasure();
		
		if ( !IsValid() ) 
			return ;
		
		swidth *= m_pswidth;
		twidth *= m_ptwidth;
		
		// T(s2,t2)-T(s2,t1)-T(s1,t2)+T(s1,t1)
		val.resize( m_SamplesPerPixel );
		val.assignment(0.0f);
		
		float ss1, ss2, tt1, tt2;
		
		if ( m_smode.getValue() == WrapMode_Periodic )
		{
			s1 = s1 % 1.0f ;
			if ( s1 < 0.0 ) s1 += 1.0f;
		}
		if ( m_tmode.getValue() == WrapMode_Periodic )
		{
//			t1 = fmod( t1, 1.0f );
			t1 = t1 % 1.0f ;
			if ( t1 < 0.0 ) t1 += 1.0f;
		}
		
		if ( m_smode.getValue() == WrapMode_Black )
		{
			if ( ( s1 < 0.0f ) ||
					( s1 > 1.0f ) ) return ;
		}
		if ( m_tmode.getValue() == WrapMode_Black )
		{
			if ( ( t1 < 0.0f ) ||
					( t1 > 1.0f ) ) return ;
		}
		
		if ( m_smode.getValue() == WrapMode_Clamp || Type().getValue() == MapType_Environment )
		{
			s1 = PublicFunctions.CLAMP( s1, 0.0f, 1.0f );
		}
		if ( m_tmode.getValue() == WrapMode_Clamp || Type().getValue() == MapType_Environment )
		{
			t1 = PublicFunctions.CLAMP( t1, 0.0f, 1.0f );
		}
		ss1 = s1 - swidth - ( m_sblur * 0.5f );
		tt1 = t1 - twidth - ( m_tblur * 0.5f );
		ss1 = PublicFunctions.CLAMP(ss1, 0.0f, 1.0f);
		tt1 = PublicFunctions.CLAMP( tt1, 0.0f, 1.0f );
		
		ss2 = s1 + swidth + ( m_sblur * 0.5f );
		tt2 = t1 + twidth + ( m_tblur * 0.5f );
		ss2 = PublicFunctions.CLAMP(ss2, 0.0f, 1.0f);
		tt2 = PublicFunctions.CLAMP( tt2, 0.0f, 1.0f );
		
		/* make ss1 is always less or equal to ss2
		 * tt1 is always less or equal to tt2
		 */
		float tmp;
		tmp = ss1;
		ss1 = Math.min(ss1, ss2);
		ss2 = Math.max(tmp, ss2);
		tmp = tt1;
		tt1 = Math.min(tt1, tt2);
		tt2 = Math.max(tmp, tt2);
		
		GetSample( ss1, tt1, ss2, tt2, val );
		
//		//--オリジナルコード
//		同じ仕組みで動くように細工を行う		
//		int[] rgb = new int[4];
//		if(!(s1 * m_pImage.getWidth() < m_pImage.getWidth() && t1 * m_pImage.getHeight() < m_pImage.getHeight()))
//		return;
//		
//		try{
//		raster.getPixel((int)(s1 * m_pImage.getWidth()),(int)( t1 * m_pImage.getHeight()),rgb);
//		
//		}catch(ArrayIndexOutOfBoundsException ae){
//		ae.printStackTrace();
//		System.out.println(""+s1+" "+t1);
//		System.exit(1) ;
//		}
//		val.resize(3);
//		val.setValueAt(0,(rgb[0]/255f));
//		val.setValueAt(1,(rgb[1]/255f));
//		val.setValueAt(2,(rgb[2]/255f));
	}
	
	/**
	 * モーションブラーがonかoffかによって、取得する
	 * pixelの値を変化させる。
	 * 
	 * @param u1
	 * @param v1
	 * @param u2
	 * @param v2
	 * @param val 指定したpixelの値が格納される変数
	 */
	public void GetSample( float u1, float v1, float u2, float v2, Valarray val )
	{
		// You could force it too
		// by defining "Options" "limit" "trilinear" 1 in your rib file
		if (m_sblur != 0 || m_tblur != 0)
		{
			GetSampleTri( u1, v1, u2, v2, val );
		} else {
			// without blur it is not necessary to do GetSampleTri().
			GetSampleSgle( u1, v1, u2, v2, val );
		}
	}
	
	/* CqTextureMap::GetSampleSgle( ) is
	 * This is the implementation based on classical pyramid integration for mipmap;
	 * I don't see wrong with it but is only doing a one shot sampling which is prone to give some 
	 * artifacts when the texture() required some blur or ShadingRate is high. This could not be used as soon as you have some blur or sampling over an area
	 * in case of shadowing per examples. See SampleMap()s for when to use or not use this method.
	 * \param u1,v2 is top/right bottom/left of sample positions.
	 * \param v1,v2.
	 * \param val the result will be stored.
	 */
	public void GetSampleSgle( float u1, float v1, float u2, float v2, Valarray val )
	{
		int c;
		
		QGetRenderContext().Stats().TextureMapTimer().Start();
		
		// u/v is provided to the function now; they are the average of
		// sample positions (u1,v1), (u2, v2).
		
		// Calculate the appropriate mipmap level from the area subtended by the sample.
		float UVArea = (float)Math.sqrt( Math.abs(( u2 - u1 ) * ( v2 - v1 )));
		float u = (u2+u1)/2.0f;
		float v = (v2+v1)/2.0f;
		
		// Find out which two layers the of the pyramid d lies between.
		float d = Math.min(UVArea, 1.0f);
		
		// Adjust u and v for the pyramid level.
		long size = Math.min(m_XRes, m_YRes);
		long id = (long) Math.floor( d * size);
		
		// If we are on the edge of the texture only achive singlelevel mipmap.
		boolean singlelevel = ( id == 0) ||  (id == size);
		
		int umapsize = (int)m_XRes;
		int vmapsize = (int)m_YRes;
		int level = 0;
		
		while(id > 1) {
			id >>= 1;
			umapsize >>= 1;
			vmapsize >>= 1;
			level++;
			
			if (umapsize < 8 ) break;
			if (vmapsize < 8 ) break;
		}
		
		BiLinear(u, v, umapsize, vmapsize, level, m_low_color);
		
		if (singlelevel) {
			for (c = 0; c < m_SamplesPerPixel; c++) {
				val.setValueAt(c,m_low_color.valueAt(c));
			}
		} else {
			size = Math.min(umapsize, vmapsize);
			float a = 1.0f/(float)size;
			float b = 1.0f/(float)(size/2);
			float interp = (d - a) / (b - a);
			umapsize >>= 1;
			vmapsize >>= 1;
			level ++;
			
			
			BiLinear(u, v, umapsize, vmapsize, level, m_high_color);

			for (c = 0; c < m_SamplesPerPixel; c++) {
				val.setValueAt(c,INTERPOLATE1(m_low_color.valueAt(c), m_high_color.valueAt(c), interp));
			}
		}
		
		QGetRenderContext().Stats().TextureMapTimer().Stop();
	}
	
	public CqTextureMapBuffer GetBuffer( long s, long t, int directory){
		return GetBuffer(s,t,directory,false);
	}
//	----------------------------------------------------------------------
	/** Get a pointer to the cache buffer segment which contains the specifed sample point.
	 * 
	 * @param s  Horizontal sample position.
	 * @param t Vertical sample position.
	 * @param directory TIFF directory index.
	 * @param fProt A boolean value, true if the buffer should be protected from removal by the cache management system.
	 */
	
	public CqTextureMapBuffer GetBuffer( long s, long t, int directory, boolean fProt )
	{
		QGetRenderContext().Stats().IncTextureMisses( 4 );
		
		if ( m_apSegments.size() > 0 && m_apSegments.getFirst().IsValid( s, t, directory ) )
		{
			QGetRenderContext().Stats().IncTextureHits( 1, 4 );
			return( m_apSegments.getFirst() );
		}
		
		// Search already cached segments first.
		for ( CqTextureMapBuffer i : m_apSegments)
		{
			if ( ( i ).IsValid( s, t, directory ) )
			{
				QGetRenderContext().Stats().IncTextureHits( 1, 4 );
				// Move this segment to the top of the list, so that next time it is found first. This
				// allows us to take advantage of likely spatial coherence during shading.
				CqTextureMapBuffer pbuffer = i;
//				m_apSegments.erase(i);
				m_apSegments.remove(i);
				m_apSegments.addFirst(pbuffer);
				return ( pbuffer );
			}
		}
		
		// If we got here, segment is not currently loaded, so load the correct segement and store it in the cache.
		CqTextureMapBuffer pTMB = null;
		
		if ( m_pImage == null)
		{
			CqRiFile	fileImage = new CqRiFile( m_strName, "texture" );
			if ( !fileImage.IsValid() )
			{
				HimawariLogger.getLogger().error("Cannot open texture file \""+ m_strName +"\"\n");
				
				return pTMB;
			}
			String strRealName = fileImage.strRealName();
			fileImage.Close();
			
			// Now open it as a tiff file.
//			m_pImage = TIFFOpen( strRealName, "r" );
			try {
				m_pImage = ImageIO.read(new File(strRealName));
			} catch (IOException e) {
//				e.printStackTrace();
				HimawariLogger.outputException(e);
			}
		}
	if(m_pImage != null){
				//--独自実装
			long tsx = m_XRes;
			long tsy = m_YRes;
			long ox = ( s / tsx ) * tsx;
			long oy = ( t / tsy ) * tsy;
				pTMB = CreateBuffer( ox, oy, tsx, tsy, directory, fProt );
				Raster raster = m_pImage.getRaster();
				int[] buf = new int[raster.getNumBands()];
				byte temp_byte;
				//高速化
//				STLArrayList pdata = pTMB.pVoidBufferData();
				Byte[] pdata = (Byte[])pTMB.pVoidBufferData();
				//--カウンタを設置
				int count = 0;
			 	for(int i = 0; i < m_YRes ;i++){
					for(int j=0;j<m_XRes;j++){
						//--テクスチャから色情報を取得する場所

						raster.getPixel(j,i,buf);
						for(int k =0 ; k < buf.length;k++){
	//						buf[k]-=128;
							temp_byte = (byte)(buf[k] -128);
	//						pdata.add(new Byte(""+buf[k]));
	//2006/01/19						pdata.add(temp_byte);
	//高速化				pdata.add(count,temp_byte);
//							pdata.set(count , temp_byte);
							pdata[count] = temp_byte;
							count++;
						}
					}
				}
				m_apSegments.addFirst(pTMB);
				//--ここまで
		}
		return ( pTMB );
	}
	//--これも
    public CqTextureMapBuffer CreateBuffer( long xorigin, long yorigin, long width, long height, int directory  ,boolean fProt){
        CqTextureMapBuffer pRes = new CqTextureMapBuffer();
        pRes.Init( xorigin, yorigin, width, height, m_SamplesPerPixel, directory, fProt );
        return( pRes );
 
    }

    public CqTextureMapBuffer CreateBuffer( long xorigin, long yorigin, long width, long height, int directory ){
        return CreateBuffer(xorigin,yorigin,width,height,directory,false);
 
    }
    public CqTextureMapBuffer CreateBuffer( long xorigin, long yorigin, long width, long height){
        return CreateBuffer(xorigin,yorigin,width,height,0);
 
    }

	
//	----------------------------------------------------------------------
	/**
	 * m_colorに指定された場所の色情報を代入する
	 *  
	 * Bilinear sample of any directory/u/v Color the result is saved into
	 * either m_low_color, or m_high_color depending of the param m_color. 
	 * This is the implementation for the releases < 0.9.2
	 * \param u is average of sample positions.
	 * \param v is average of sample positions.
	 * \param umapsize the mapsize at the directory id
	 * \param vmapsize the mapsize at the direction id
	 * \param id     the directory in the tiff 0...n
	 * \param param m_color the result will be stored in m_low_color or m_high_color.
	 */
	
	public boolean BiLinear(float u, float v, int umapsize, int vmapsize,
			int id, Valarray m_color)
	{
		long umapsize1 = umapsize-1; 
		long vmapsize1 = vmapsize-1;
		
		long iu = (long)Math.floor( u * umapsize1);
		double ru = ((double)u) * umapsize1 - iu;
		long iu_n =(long) Math.floor( u * umapsize1 + 1.0);
		
		
		
		iu = iu % umapsize;		/// \todo This is wrap mode periodic.
		iu_n = iu_n % umapsize;	/// \todo This is wrap mode periodic.
		
		long iv = (long)Math.floor( v * vmapsize1 );
		
		double rv = ((double)v) * vmapsize1 - iv;
		long iv_n = (long)Math.floor( v * vmapsize1 + 1.0);
		iv = iv % vmapsize;		/// \todo This is wrap mode periodic.
		iv_n = iv_n % vmapsize;	/// \todo This is wrap mode periodic.
		
		// Read in the relevant texture tiles.
		int c;
		
		// Read in the relevant texture tiles.
		CqTextureMapBuffer pTMBa = GetBuffer( iu, iv, id );		// Val00
		CqTextureMapBuffer pTMBb = pTMBa;
		if ( iv != iv_n )
		{
			pTMBb = GetBuffer( iu, iv_n, id );	// Val01
		}
		
		CqTextureMapBuffer pTMBc = pTMBa;
		if ( iu_n != iu )
		{
			pTMBc = GetBuffer( iu_n, iv, id );	// Val10
		}
		CqTextureMapBuffer pTMBd = null;
		if ( iv == iv_n ) pTMBd = pTMBc;
		else if ( iu == iu_n ) pTMBd = pTMBb;
		else
			pTMBd = GetBuffer( iu_n, iv_n, id );	// Val11
		
		/* cannot find anything than goodbye */
		if ( pTMBa == null || pTMBb == null || pTMBc == null || pTMBd == null )
		{
			for ( c = 0; c < m_SamplesPerPixel; c++ )
			{
				m_color.setValueAt( c , 1.0f);
			}
			HimawariLogger.getLogger().error("Cannot find value for either pTMPB[a,b,c,d]\n");
			
			return false;
		}
		
		
		// Bilinear interpolate the values at the corners of the sample.
		iu -= pTMBa.sOrigin();
		iu_n -= pTMBc.sOrigin();
		iv -= pTMBa.tOrigin();
		iv_n -= pTMBb.tOrigin();
		
		for ( c = 0; c < m_SamplesPerPixel; c++ )
		{
			float Val00 = pTMBa.GetValue( (int)iu,(int) iv, c );
			float Val01 = pTMBc.GetValue( (int)iu_n,(int) iv, c );
			float Val10 = pTMBb.GetValue( (int)iu, (int)iv_n, c );
			float Val11 = pTMBd.GetValue( (int)iu_n, (int)iv_n, c );

			m_color.setValueAt( c , INTERPOLATE2(Val00, Val01, Val10, Val11,(float) ru ,(float) rv ));
		}
		return true;
	}
	
	
	public void GetSampleTri( float u1, float v1, float u2, float v2, Valarray val ){
	    int c;

	    QGetRenderContext().Stats().TextureMapTimer().Start();

	    float cs = (u1 + u2) * 0.5f;
	    float ct = (v1 + v2) * 0.5f;
	    float ds,dt,diag;

	    Method pFilter  =null;
		try {
			pFilter = RendermanInterface.class.getMethod("RiSincFilter",float.class, float.class, float.class, float.class);
		} catch (SecurityException e) {
//			e.printStackTrace();
			HimawariLogger.outputException(e);
		} catch (NoSuchMethodException e) {
//			e.printStackTrace();
			HimawariLogger.outputException(e);
		}

	    ds = u1 - cs;
	    dt = v1 - ct;
	    diag = (ds *ds * m_XRes * m_XRes) +  (dt * dt * m_YRes * m_YRes);

	    ds = u2 - cs;
	    dt = v2 - ct;
	    diag = Math.min(diag, (ds *ds * m_XRes * m_XRes) +  (dt * dt * m_YRes * m_YRes));

	    float l = (float)(Math.log(diag) / (2.0f * Math.log(2.0f)));
	    l = Math.max(l, 0.0f);
	    int id = (int) Math.floor(l);
	    float offset = l - id;
	    offset = Math.min(offset, 1.0f);
	    int umapsize = (int)m_XRes;
	    int vmapsize = (int)m_YRes;
	    boolean singlelevel = false;

	    int idx;

//--なぞコード
//	#ifdef CORRECTEDVARIOUS
//	    // Important note: if ever the texturesampling determine a certain
//	    // directory could be used other than what directory was already used for any microgrids.
//	    // We will prefer to chose the minimum between the current number and the existant; until
//	    // the texture is zapped. It eliminates texture bleeding at the edge of the primitive
//	    // or dissimilitudes between two different and adjacent microgrids.
	    if (m_Directory != 0 && ForceCorrect() != 0) {
	        if (id > m_Directory) {
	            id = m_Directory;
	        }
        }
//	#endif

	    /* With id lower down the umapsize and vmapsize until idx matches id and
	     * umapsize and vmapsize is bigger than 2.
	     */
	    for (idx=0; (idx < id) && (umapsize > 8) && (vmapsize > 8); idx++, umapsize >>= 1, vmapsize >>= 1);

	    /*
	     * if idx is different than id 
	     * than set id to be idx since we could not met all the conditions
	     */
	    if (idx != id) id = idx;
	    m_low_color.assignment(0);
	    m_Directory = id;
	    if ((umapsize < 3) || (vmapsize < 3)) singlelevel = true;
	    if ((cs == 0.0) || (cs == 1.0)) singlelevel = true;
	    if ((ct == 0.0) || (ct == 1.0)) singlelevel = true;


	    // Will it may make some sense to add some random value between 0..1.0 ?
	    // For now I just put a delta based on the texel deplacement.
	    float mul, div;

	    // Area integration; it will be the ideal placed to put to the contribution
	    // of each texel the filter factor.
	    float u, v;

	    m_low_color.assignment(0);
	    div = 0.0f;
	    float deltau;
	    float deltav;

	    deltau = 1.0f/(m_pswidth * umapsize);
	    deltav = 1.0f/(m_ptwidth * vmapsize);

	    for (u = u1; u <= u2; u += deltau) {
	        for (v = v1; v <= v2; v += deltav)
	        {
	            BiLinear(u, v, umapsize, vmapsize, id, m_tempval1);

	            /* Yes we use Disk filter */
//	#ifdef WITHFILTER
	            try {
					mul = (Float)pFilter.invoke((u-cs), (v-ct), 2.0 * cs, 2.0 * ct);
				} catch (IllegalArgumentException e) {
					HimawariLogger.outputException(e);
					mul = 1f;
				} catch (IllegalAccessException e) {
					HimawariLogger.outputException(e);
					mul = 1f;
				} catch (InvocationTargetException e) {
					HimawariLogger.outputException(e);
					mul = 1f;
				}
//	#else
//	            mul = 1.0;
//	#endif
	            if (mul < Float_h.FLT_EPSILON) continue;
	            div += mul;
	        for ( c = 0; c < m_SamplesPerPixel; c++ )
	                m_low_color.setValueAt(c,m_low_color.valueAt(c) + (mul * m_tempval1.valueAt(c)));
	        }
	        }

	    for (c = 0; c < m_SamplesPerPixel; c++ )
	        m_low_color.setValueAt(c,m_low_color.valueAt(c) / div);


	    // This is never the case since even at the border a valid texel will be provided
	    // but do singlelevel = 1 to debug the supersampling, filtering
	    if (singlelevel)
	    {
	        /*
	        *  we will store the value back to val if we are in singlelevel mode
	        *  
	        */
	        for ( c = 0; c < m_SamplesPerPixel; c++ )
	            val.setValueAt( c ,m_low_color.valueAt(c));

	    } else {


	        //
	        // umapsize and vmapsize must be at least 2 pixels here; right they are garantee to
	        // at least bigger than 64 (size of unique tile).
	        //
	        umapsize >>= 1;
	        vmapsize >>= 1;
	        id ++;

	        m_high_color.assignment(0);
	        div = 0.0f;
	        deltau = 1.0f/(m_pswidth * umapsize);
	        deltav = 1.0f/(m_ptwidth * vmapsize);

	        // Recompute the delta based on the new pyramid level; but
	        // this is also the place to put as contribution factor the filter correction.
	        for (u = u1; u <= u2; u += deltau){
	            for (v = v1; v <= v2; v += deltav)
	            {
	                BiLinear(u, v, umapsize, vmapsize, id, m_tempval1);
//	#ifdef WITHFILTER
	                try {
						mul = (Float)(pFilter).invoke((u-cs), (v-ct), 2.0 * cs, 2.0 * ct);
					} catch (IllegalArgumentException e) {
						mul = 1.0f;
						HimawariLogger.outputException(e);
					} catch (IllegalAccessException e) {
						mul = 1.0f;
						HimawariLogger.outputException(e);
					} catch (InvocationTargetException e) {
						mul = 1.0f;
						HimawariLogger.outputException(e);
					}
//	#else
//	                mul = 1.0;
//	#endif
	                if (mul < Float_h.FLT_EPSILON) continue;
	                div += mul;
	                for (c = 0; c < m_SamplesPerPixel; c++ )
	                    m_high_color.setValueAt(c ,m_high_color.valueAt(c) + (mul * m_tempval1.valueAt(c)));
	            }
	    }

	        for (c = 0; c < m_SamplesPerPixel; c++ )
	            m_high_color.setValueAt(c , m_high_color.valueAt(c) / div);


	        // Linearly interpolate between low_color and high_color by dinterp.
	        // Please note the m_low_color value is the best value; and m_high_color
	        // is less precise.

	        for (c = 0; c < m_SamplesPerPixel; c++ )
	            val.setValueAt(c , ((1.0f - offset) * m_low_color.valueAt(c)) + ((offset) * m_high_color.valueAt(c)));

	    }
	    QGetRenderContext().Stats().TextureMapTimer().Stop(); 
	}
	
	
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#SampleMap(float, float, float, float, float, float, float, float, net.cellcomputing.himawari.accessory.Valarray)
	 */
	@Override
	public void SampleMap(float s1, float t1, float s2, float t2, float s3, float t3, float s4, float t4, Valarray val) {
		
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#SampleMap(net.cellcomputing.himawari.library.types.CqVector3D, net.cellcomputing.himawari.library.types.CqVector3D, net.cellcomputing.himawari.library.types.CqVector3D, net.cellcomputing.himawari.accessory.Valarray, int, float[], float[])
	 */
	@Override
	public void SampleMap(CqVector3D R, CqVector3D swidth, CqVector3D twidth, Valarray val, int index, p_float average_depth, p_float shadow_depth) {
		
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#SampleMap(net.cellcomputing.himawari.library.types.CqVector3D, net.cellcomputing.himawari.library.types.CqVector3D, net.cellcomputing.himawari.library.types.CqVector3D, net.cellcomputing.himawari.library.types.CqVector3D, net.cellcomputing.himawari.accessory.Valarray, int, float[], float[])
	 */
	@Override
	public void SampleMap(CqVector3D R1, CqVector3D R2, CqVector3D R3, CqVector3D R4, Valarray val, int index, p_float average_depth, p_float shadow_depth) {
		
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#GetMatrix(int, int)
	 */
	@Override
	public CqMatrix GetMatrix(int which, int index) {
		return null;
	}
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqTextureMap#NumPages()
	 */
	@Override
	public int NumPages() {
		return 1;
	}
	
	/** this is used for remove any memory exceed the command Option "limits" "texturememory"
	 * directive
	 *   
	 * It zaps the m_apSegments for this TextureMap object completely.
	 * The idea here is to erase any "GetBuffer()" memory to respect the directive
	 * It looks into the big m_TextureMap_Cache release every things
	 **/
	
	public void CriticalMeasure()
	{
		final int[] poptMem = QGetRenderContextI().GetIntegerOption( "limits", "texturememory" );
		int current, limit, now;
//		Iterator<CqTextureMap> i;
//		Iterator<CqTextureMapBuffer> j;
		limit = RiGlobal.MEG1;
		
		if ( poptMem != null )
			limit = poptMem[ 0 ] * 1024;
		
		now = QGetRenderContext().Stats().GetTextureMemory();
		
		if ( RiGlobal.m_critical )
		{
			
			/* Extreme case no more memory to play */
			
			/* It is time to delete some tile's memory associated with
			 * texturemap objects.
			 * テクスチャマップのオブジェクトに関するメモリを削除する
			 * 
			 * In principle the oldest texturemaps are freed first 
			 * 原則として、古い順にテクスチャマップが開放される。
			 * (現在それが使われているかどうかは考慮しない)
			 * それゆえ、このメソッドはtexturemapbufferが
			 * GetBuffer()メソッドに影響しないようなものについて。
			 * のみ呼ばれる。
			 * 
			 * (regardless of their current usage. Therefore this method 
			 * could only be 
			 * called at a place where the fact of release 
			 * texturemapbuffer will not impact the subsequent 
			 * GetBuffer() calls.
			 *
			 */
			//--2007/12/07 add texture
			ArrayList<CqTextureMap> tempList = new ArrayList<CqTextureMap>();
			//--
			for (CqTextureMap i :m_TextureMap_Cache)
			{
				/**
				 * 自分自身であれば削除しない(追加)
				 */
				if(i == this)
					continue;
				for (CqTextureMapBuffer j : i.m_apSegments )
				{
					// Only release if not protected.
//2007/12/07 del					if ( !( j ) .fProtected() )
						( j ) .Release();
				}
//				( i ).m_apSegments.resize( 0 );
				( i ).m_apSegments.clear();
				//--2007/12/07 add
				tempList.add(i);
				//--
				//それとGetTextureMemoryで本当にメモリ量が管理できるかチェック
				current = QGetRenderContext().Stats().GetTextureMemory();
				if ( ( now - current ) > ( limit / 4 ) ) break;
			}
			
			//--2007/12/07 add
			for(CqTextureMap map :tempList){
				m_TextureMap_Cache.remove(map);
			}
			//--
			
		}
		current = QGetRenderContext().Stats().GetTextureMemory();
		
		RiGlobal.m_critical = false;
		
//		デバッグコード
//		#ifdef _DEBUG
//		
//		if ( now - current )
//		{
//		///! \todo Review this debug message
//		std::cerr << info << "I was forced to zap the tile segment buffers for " << (int)( now - current ) / 1024 << "K" << std::endl;
//		}
//		#endif
		
	}
	
	
//	----------------------------------------------------------------------
	/** 
	 * filterの名前を解釈する。
	 * tifファイルに書いてあるモードを解釈する(のだと思う2006/
	 * 
	 * this is used for re-intrepreted the filter/wrap mode when using
	 *  RiMakeTextureV() for downsampling/filter the tif file
	 *
	 **/
	public void Interpreted( String mode )
	{
	    String filter = "";
	    String smode = "";
	    String tmode = "";
	    String sep = "[,\\s\\t]";

	    // Take a copy of the string before processing it.
//	    String string = new char[strlen(mode)+1];
//	    strcpy( string, mode );

/* 2007/12/10 テクスチャ 岩渕修正
	    final String[] token;
	    token = mode.split(sep);
	    if( token.length > 0 )
	    {
	        smode = token[0];
	        if( token.length > 1 )
	        {
	            tmode = token[1];
	            if( token.length > 2  )
	            {
	                filter = token[2];
	                if( token.length > 3  )
	                {
	                    m_swidth = Float.parseFloat(token[3] );
	                    if( token.length > 4  )
	                    {
	                        m_twidth = Float.parseFloat( token[4] );
	                    }
	                }
	            }
	        }
	    }
*/
	    //フッターの最初の項目が文字化けするため
	    //壊れても良いデータを0番に入れている。
	    final String[] token;
	    token = mode.split(sep);
	    if( token.length > 1 )
	    {
	        smode = token[1];
	        if( token.length > 2 )
	        {
	            tmode = token[2];
	            if( token.length > 3  )
	            {
	                filter = token[3];
	                if( token.length > 4  )
	                {
	                    m_swidth = Float.parseFloat(token[4] );
	                    if( token.length > 5  )
	                    {
	                        m_twidth = Float.parseFloat( token[5] );
	                    }
	                }
	            }
	        }
	    }
	    
	    //sscanf( mode, "%s %s %s %f %f", smode, tmode, filter, &m_swidth, &m_twidth );

	    try {
			m_FilterFunc =RendermanInterface.class.getMethod("RiBoxFilter",float.class, float.class, float.class, float.class);
			if ( filter.equals("gaussian")) m_FilterFunc = RendermanInterface.class.getMethod("RiGaussianFilter",float.class, float.class, float.class, float.class);;
			if ( filter.equals("box") ) m_FilterFunc = RendermanInterface.class.getMethod("RiBoxFilter",float.class, float.class, float.class, float.class);;
			if ( filter.equals("triangle") ) m_FilterFunc = RendermanInterface.class.getMethod("RiTriangleFilter",float.class, float.class, float.class, float.class);;
			if ( filter.equals("catmull-rom")  ) m_FilterFunc = RendermanInterface.class.getMethod("RiCatmullRomFilter",float.class, float.class, float.class, float.class);;
			if ( filter.equals("sinc")  ) m_FilterFunc = RendermanInterface.class.getMethod("RiSincFilter",float.class, float.class, float.class, float.class);;
			if ( filter.equals("disk")  ) m_FilterFunc = RendermanInterface.class.getMethod("RiDiskFilter",float.class, float.class, float.class, float.class);;
			if ( filter.equals("bessel")  ) m_FilterFunc = RendermanInterface.class.getMethod("RiBesselFilter",float.class, float.class, float.class, float.class);;
		} catch (SecurityException e) {
			m_FilterFunc = null;
			HimawariLogger.outputException(e);
		} catch (NoSuchMethodException e) {
			m_FilterFunc = null;
			HimawariLogger.outputException(e);
		}

	    m_smode = m_tmode = new EqWrapMode(WrapMode_Clamp);
	    if ( smode.equals(RendermanInterface.RI_PERIODIC ))
	        m_smode = new EqWrapMode(WrapMode_Periodic);
	    else if ( smode.equals(RendermanInterface.RI_CLAMP ))
	        m_smode = new EqWrapMode(WrapMode_Clamp);
	    else if ( smode.equals(RendermanInterface.RI_BLACK ))
	        m_smode = new EqWrapMode(WrapMode_Black);

	    if ( tmode.equals(RendermanInterface.RI_PERIODIC ))
	        m_tmode = new EqWrapMode(WrapMode_Periodic);
	    else if ( tmode.equals(RendermanInterface.RI_CLAMP ))
	        m_tmode = new EqWrapMode(WrapMode_Clamp);
	    else if ( tmode.equals(RendermanInterface.RI_BLACK ))
	        m_tmode = new EqWrapMode(WrapMode_Black);

	}

	
//	  ----------------------------------------------------------------------
    /** 
     * Filterメソッドを用いてテクスチャにフィルタをかける
     * 
     * this is used for downsampling the texture at lower resolution
      *   
      * it will use the filtervalues. breakdown rgba values in floats. 
      * Accumulate the floating value rgba and ponderate the sum with the filter values;
      * and convert back to uint32 the rgba floating values.
      * The values of the current filterfunc/swrap/twrap are used ; if ever swrap or twrap is equal to
      * 1.0 than the filterfunc will be executed (at lower resolutions) using the points 
      *   (X1,Y1) and (X2,Y2) (-0.5, -0.5) ... (0.5, 0.5) giving 9 samples -0.5, 0.0, 0.5 in X and in Y.
      *
      **/
    public void ImageFilterVal( CqTextureMapBuffer pData, int x, int y, int directory,  int m_xres, int m_yres, STLVector<Float> accum )
    {
        Method pFilter = m_FilterFunc;

        int delta = ( 1 << directory );
        float div = 0.0f;
        float mul = 0f;
        float fx, fy;
        int isample;
        int xdelta = (int)Math.max(Math.floor(m_swidth) * (delta/2), 1);
        int ydelta = (int)Math.max(Math.floor(m_twidth) * (delta/2), 1);
        int xdelta2 = xdelta * 2;
        int ydelta2 = ydelta * 2;


        // Increase the precision after the middle (0.5 and up make sure we will hit the borner at 1.0)
        fx = (float) (x)/ (float) (m_xres - 1);
       

        // Increase the precision after the middle (0.5 and up make sure we will hit the borner at 1.0)
        fy = (float) (y)/ (float) (m_yres - 1 );
       

        // Clear the accumulator
        accum.assign( SamplesPerPixel(), 0.0f );

        if ( directory != 0)
        {
            int i, j;

            for ( isample = 0; isample < SamplesPerPixel(); isample++ )
                accum.setElementAt( 0f , isample );

            /* From -twidth to +twidth */
            for ( j = - ydelta; j <= ydelta; j++ )
            {
                /* From -swidth to +swidth */
                for ( i = -xdelta; i <= xdelta; i++)
                {
                    /* find the filter value */
                    try {
						mul =  (Float)pFilter.invoke(null,  (float) i, (float) j, (float) xdelta2, (float) ydelta2 );
					} catch (IllegalArgumentException e) {
//						e.printStackTrace();
						HimawariLogger.outputException(e);
					} catch (IllegalAccessException e) {
//						e.printStackTrace();
						HimawariLogger.outputException(e);
					} catch (InvocationTargetException e) {
//						e.printStackTrace();
						HimawariLogger.outputException(e);
					}
                    if (mul == 0.0) continue;

                    /* find the value in the original image */
                    int ypos = (int) (fy*m_YRes-1) + j;
                    int xpos = (int) (fx*m_XRes-1) + i;
                    if (ypos < 0) continue;
                    if (xpos < 0) continue;
                    if (ypos > (int) m_YRes - 1)continue;
                    if (xpos > (int) m_XRes - 1) continue;
                    /* ponderate the value */
                    for ( isample = 0; isample < SamplesPerPixel(); isample++ )
                        accum.setElementAt((pData.GetValue( xpos, ypos, isample ) * mul) + accum.elementAt(isample), isample) ;

                    /* accumulate the ponderation factor */
                    div += mul;
                }
            }

            /* use the accumulated ponderation factor */
            for ( isample = 0; isample < SamplesPerPixel(); isample++ )
                accum.setElementAt(accum.elementAt(isample) / (float)( div ) , isample );
        }
        else
        {
            /* copy the byte don't bother much */
            for ( isample = 0; isample < SamplesPerPixel(); isample++ )
                accum.setElementAt( pData.GetValue( x, y, isample ) , isample );

        }

    }

	
	//下のメソッドで利用される
	static float sqr = -1.0f;
	
//	FindBlurRatio() find a better ratio
//	Option "limits" "float textureblur" 1.2 by default it is 1.2
//	FindBlurRatio()　はより良い比率を見つける。 
	//
	
	public static float FindBlurRatio()
	{
		
		
		if (sqr < 0.0)
		{
			final float[] pCorrect =  QGetRenderContextI().GetFloatOption("limits","textureblur");
			
			sqr = 1.2f;
			if (pCorrect != null) {
				sqr = Math.max(0.1f , pCorrect[0]);
			}
		}
		return sqr;
	}
	
	/**
	 * テスト高速化を行ったImageFilter
	 * @param pData
	 * @param x
	 * @param y
	 * @param directory
	 * @param m_xres
	 * @param m_yres
	 * @param accum
	 */
    public void ImageFilterVal2( CqTextureMapBuffer pData, int x, int y, int directory,  int m_xres, int m_yres, float[] accum )
    {
        Method pFilter = m_FilterFunc;

        int delta = ( 1 << directory );
        float div = 0.0f;
        float mul = 0f;
        float fx, fy;
        int isample;
        int xdelta = (int)Math.max(Math.floor(m_swidth) * (delta/2), 1);
        int ydelta = (int)Math.max(Math.floor(m_twidth) * (delta/2), 1);
        int xdelta2 = xdelta * 2;
        int ydelta2 = ydelta * 2;


        // Increase the precision after the middle (0.5 and up make sure we will hit the borner at 1.0)
        fx = (float) (x)/ (float) (m_xres - 1);
       

        // Increase the precision after the middle (0.5 and up make sure we will hit the borner at 1.0)
        fy = (float) (y)/ (float) (m_yres - 1 );
       

        // Clear the accumulator
//        accum.assign( SamplesPerPixel(), 0.0f );
        for(int i=0; i < accum.length;i++)
        	accum[i] = 0.0f;
        if ( directory != 0)
        {
            int i, j;

            for ( isample = 0; isample < SamplesPerPixel(); isample++ )
//                accum.setElementAt( 0f , isample );
            	accum[isample] = 0;
            /* From -twidth to +twidth */
            for ( j = - ydelta; j <= ydelta; j++ )
            {
                /* From -swidth to +swidth */
                for ( i = -xdelta; i <= xdelta; i++)
                {
                    /* find the filter value */
                    try {
						mul =  (Float)pFilter.invoke(null,  (float) i, (float) j, (float) xdelta2, (float) ydelta2 );
					} catch (IllegalArgumentException e) {
//						e.printStackTrace();
						HimawariLogger.outputException(e);
					} catch (IllegalAccessException e) {
//						e.printStackTrace();
						HimawariLogger.outputException(e);
					} catch (InvocationTargetException e) {
//						e.printStackTrace();
						HimawariLogger.outputException(e);
					}
                    if (mul == 0.0) continue;

                    /* find the value in the original image */
                    int ypos = (int) (fy*m_YRes-1) + j;
                    int xpos = (int) (fx*m_XRes-1) + i;
                    if (ypos < 0) continue;
                    if (xpos < 0) continue;
                    if (ypos > (int) m_YRes - 1)continue;
                    if (xpos > (int) m_XRes - 1) continue;
                    /* ponderate the value */
                    for ( isample = 0; isample < SamplesPerPixel(); isample++ )
//                        accum.setElementAt((pData.GetValue( xpos, ypos, isample ) * mul) + accum.elementAt(isample), isample) ;
                    	accum[isample] = (pData.GetValue( xpos, ypos, isample ) * mul) + accum[isample];
                    /* accumulate the ponderation factor */
                    div += mul;
                }
            }

            /* use the accumulated ponderation factor */
            for ( isample = 0; isample < SamplesPerPixel(); isample++ )
//                accum.setElementAt(accum.elementAt(isample) / (float)( div ) , isample );
            	if(div!= 0)	//追加 2006/01/27 白い枠線が出るバグ
            		accum[isample] = accum[isample] / (float)( div ) ;

        }
        else
        {
            /* copy the byte don't bother much */
            for ( isample = 0; isample < SamplesPerPixel(); isample++ )
//                accum.setElementAt( pData.GetValue( x, y, isample ) , isample );
            	accum[isample] = pData.GetValue( x, y, isample );
        }

    }
    public int Compression()
    {
        return ( m_Compression );
    }

    public 	void SetCompression( int Compression )
    {
        m_Compression = Compression;
    }
    
    public int Quality()
    {
        return ( m_Quality );
    }

    public 	void SetQuality( int Quality )
    {
        m_Quality = Quality;
    }

    public float	MinZ()
    {
        return ( m_MinZ );
    }

    public void	SetMinZ( float minz )
    {
        if ( minz <= m_MinZ ) m_MinZ = minz;
    }

	
}
