// tkstexture.cpp
///
/// (c) 1999-2018 Bastian Spiegel <bs@tkscript.de> 
///     - Distributed under terms of the Lesser GNU General Public License (LGPL).
///       See COPYING and <http://www.gnu.org/licenses/licenses.html#LGPL> for further information.
///
///

// #define DEBUG_UPLOAD defined

#define YAC_PRINTF defined
#include <yac.h>

#include <stdlib.h>

#include "inc_opengl.h"

#include "tks-list.h"

#include "bitmap.h"
#include "image.h"
#include "tksopengl.h"
#include "glerror.h"
#include "tkstexture.h"

extern sUI clid_Texture;
extern YAC_Host *yac_host;


extern glGenerateMipmap_t glGenerateMipmap_xref;


// test data:
static const GLubyte tex_data[4* 4*4] = {
   0xFF, 0x88, 0xFF, 0xFF,
   0x00, 0x00, 0xFF, 0xFF,
   0xFF, 0x88, 0xFF, 0xFF,
   0xFF, 0xFF, 0xFF, 0xFF,

   0x00, 0xFF, 0xFF, 0xFF,
   0x00, 0x88, 0xFF, 0xFF,
   0x00, 0xFF, 0xFF, 0xFF,
   0x00, 0x88, 0xFF, 0xFF,

   0xFF, 0x88, 0x00, 0xFF,
   0xFF, 0xFF, 0x00, 0xFF,
   0xFF, 0x88, 0x00, 0xFF,
   0xFF, 0xFF, 0x00, 0xFF,

   0x7F, 0xFF, 0x7F, 0xFF,
   0x7F, 0x88, 0x7F, 0xFF,
   0x7F, 0xFF, 0x7F, 0xFF,
   0x7F, 0x88, 0x7F, 0xFF,
};


_Texture::_Texture(void) {
   gl_nameid    = (sUI)-1;
   flags        = 0;
   priority     = 0.0f;
   target       = 0; // Use flags
   mipmap_level = 0;
   b_autogen_id = 1;
   sz_hint      = 1u;
}

_Texture::~_Texture() {
   // yac_host->printf("[dbg] ::~_Texture 1 (ENTER)\n");
   _free();
   // yac_host->printf("[dbg] ::~_Texture 2 (LEAVE)\n");
}

sUI YAC_VCALL _Texture::yacArrayAlloc(sUI _sx, sUI _sy, sUI, sUI _z) {
   return _alloc((sSI)_sx, (sSI)_sy, (sSI)_z);
}

void YAC_VCALL _Texture::yacArraySet(void *_context, sUI _index, YAC_Value *_value) {
   if(_value->type==YAC_TYPE_FLOAT)
   {
      _value->type=YAC_TYPE_INT;
      _value->value.int_val=(sSI)_value->value.float_val;
   }
   if(_value->type==1)
      if(image.screen&&image.screen->data.any)
      {
         if(((sU32)_index)<((sU32)(image.screen->s.x*image.screen->s.y)))
         {
            switch(image.screen->z)
            {
               case 1:
                  image.screen->data.u1[((sU32)_index)]=(sU8)_value->value.int_val;
                  break;
               case 2:
                  image.screen->data.u2[((sU32)_index)]=(sU16)_value->value.int_val;
                  break;
               case 3:
               {
                  sU32 ti=((sU32)_index)*3;
                  image.screen->data.u1[ti++]=(sU8)(_value->value.int_val>>16);
                  image.screen->data.u1[ti++]=(sU8)(_value->value.int_val>>8);
                  image.screen->data.u1[ti++]=(sU8)(_value->value.int_val);
               }
               break;
               case 4:
                  image.screen->data.u4[((sU32)_index)]=(sU32)_value->value.int_val;
                  break;
            }
         }
      }
}

void YAC_VCALL _Texture::yacArrayGet(void *_context, sUI _index, YAC_Value *_r) {
   _r->deleteme=0;
   _r->type=1;
   
   /*yac_host->printf("yacArrayGet(%i)=%08x sx=%i sy=%i z=%i\n",_index, _r->value.int_val,
     image.screen->s.x,
     image.screen->s.y,
     image.screen->z);*/
               
   if(image.screen&&image.screen->z)
   {
      sUI ids=(image.screen->stride/image.screen->z);
      sUI idy=_index/ids;
      sUI idx=_index-(idy*ids);
      //if(((sU32)_index)<((sU32)((image.screen->stride/image.screen->z)*image.screen->s.y)))
      if((idx<((sUI)image.screen->s.x))&&(idy<((sUI)image.screen->s.y)))
      {
         switch(image.screen->z)
         {
            case 1:
               _r->value.int_val=(sU8)image.screen->data.u1[((sU32)_index)];
               return;
            case 2:
               _r->value.int_val=(sU16)image.screen->data.u2[((sU32)_index)];
               return;
            case 3:
            {
               sU32 ti=((sU32)_index)*3;
               _r->value.int_val=
                  (((sU8)image.screen->data.u1[ti+0])<<16)|
                  (((sU8)image.screen->data.u1[ti+1])<<8 )|
                  (((sU8)image.screen->data.u1[ti+2]     ));
            }
            return;
            case 4:
               _r->value.int_val=(sU32)image.screen->data.u4[((sU32)_index)];
               return;
         }
      }
   }
   _r->value.int_val=0;
}

sUI  YAC_VCALL _Texture::yacArrayGetElementByteSize(void) {
   if(image.screen) return image.screen->z;
   else             return 0; // tags JIT support
}

sUI YAC_VCALL _Texture::yacArrayGetWidth(void) {
   if(image.screen) return image.screen->s.x;
   else             return 0;
}

sUI YAC_VCALL _Texture::yacArrayGetHeight(void) {
   if(image.screen) return image.screen->s.y;
   else             return 0;
}

sUI YAC_VCALL _Texture::yacArrayGetElementType(void) {
   return 1;
}

sUI YAC_VCALL _Texture::yacArrayGetStride(void) {
   if(image.screen) return image.screen->stride;
   else             return 0;
}

void *YAC_VCALL _Texture::yacArrayGetPointer(void) {
   if(image.screen) return image.screen->data.any;
   else             return 0;
}


sUI _Texture::getGlTextureTarget(void) {
   // Get target for "glBindTexture" and similar operations
   if(0 != target)
   {
      if( (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X) && 
          (target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
          )
      {
         return GL_TEXTURE_CUBE_MAP;
      }
      else
      {
         return target;
      }
   }
   else
   {
      if(0 != (flags & TEX_1D))
      {
         return GL_TEXTURE_1D;
      }
      else
      {
         return GL_TEXTURE_2D;
      }
   }
}

sUI _Texture::getGlTextureUploadTarget(void) {
   // Get target for "glTexImage" and similar operations
   if(0 != target)
   {
      return target;
   }
   else
   {
      if(0 != (flags & TEX_1D))
      {
         return GL_TEXTURE_1D;
      }
      else
      {
         return GL_TEXTURE_2D;
      }
   }
}

void _Texture::fixFlags(void) {
   GLenum texTarget = getGlTextureTarget();

   switch(texTarget)
   {
      default:
         break;

      case GL_TEXTURE_RECTANGLE:
         if(0 != (flags & TEX_REPEAT_S))
         {
            flags &= ~TEX_REPEAT_S;
            yac_host->printf("[~~~] Texture::fixFlags: REPEAT_S not supported for GL_TEXTURE_RECTANGLE, removing..\n");
         }

         if(0 != (flags & TEX_REPEAT_T))
         {
            flags &= ~TEX_REPEAT_T;
            yac_host->printf("[~~~] Texture::fixFlags: REPEAT_T not supported for GL_TEXTURE_RECTANGLE, removing..\n");
         }

         flags |= TEX_CLAMPTOEDGE_S;
         flags |= TEX_CLAMPTOEDGE_T;
         break;
   }
}

void _Texture::_setFlags(sUI _fl) {
   flags = (sUI)_fl;

   fixFlags();
}

sUI _Texture::_getFlags(void) {
   return flags;
}

sUI  _Texture::_getStride(void) {
   if(image.screen)
   {
      return image.screen->stride/image.screen->z;
   }
   else
   {
      return 0;
   }
}

void _Texture::_setScreen(YAC_Object *_t) {
   if(YAC_CHK(_t, clid_Texture))
   {
      _Texture *t=(_Texture*)_t;
      image.setScreen(t->image.screen);
   }
   else
   {
      yac_host->printf("\n[---] Texture::setScreen: _t is not a Texture.\n");
   }
}

////void _Texture::_setScreenArea(YAC_Object *_t, YAC_Object *_ivlt, YAC_Object *_ivsz) {
void _Texture::_setScreenArea(YAC_Object *_t, sSI _ltx, sSI _lty, sSI _sx, sSI _sy) {
   if(YAC_CHK(_t, clid_Texture))
   {
      _Texture *t=(_Texture*)_t;
      image.setScreenArea(t->image.screen, _ltx, _lty, _sx, _sy);
   }
   else
   {
      yac_host->printf("\n[---] Texture::setScreenArea: invalid Texture object.\n");
   }
}

sSI _Texture::_loadImage(YAC_String *_s, sSI _scx, sSI _scy, sSI _z) {
   return image.load(0, (char*)_s->chars, _z, 4, _scx, _scy);
}

sSI _Texture::_loadLocalImage(YAC_String *_s, sSI _scx, sSI _scy, sSI _z) {
   return image.load(1, (char*)_s->chars, _z, 4, _scx, _scy);
}

sSI _Texture::_saveImage(YAC_String *_s) {
   yac_host->printf("[...] Texture::saveImage(\"%s\")\n", (char*)_s->chars);
   return image.savePNG((char*)_s->chars);
}

sF32 _Texture::_setPriority(sF32 _p) {
   sF32 r=priority;
   priority=_p;

#ifndef DX_GLES
   if( ((sUI)-1) != gl_nameid )
   {
      // update priority
      ::glPrioritizeTextures(1, (GLuint*)&gl_nameid, &priority);
      Dtraceglerror("glPrioritizeTextures");
   }
#endif // DX_GLES

   return r;
} 

sSI _Texture::_getId(void) {
   return gl_nameid;
}

void _Texture::_setId(sSI _glTextureNameId) {

   // Delete old texture id if it was autogenerated
   _unload();

   if(-1 != _glTextureNameId)
   {
      // Use "shared" texture id
      b_autogen_id = 0;
   }
   else
   {
      // Re-enable automatic texture id 
      b_autogen_id = 1;
   }
   gl_nameid = (sUI) _glTextureNameId;
}


void _Texture::_update(void) {
   upload(0, 0, 0, 0, 0);
}

void _Texture::_updateRegion(sSI _x, sSI _y, sSI _w, sSI _h) {
   if( -1 == ((sSI)gl_nameid) )
   {
      upload(1,0,0,0,0);
   }
   else
   {
      sUI x = (sUI) _x;
      sUI y = (sUI) _y;
      sUI w = (sUI) _w;
      sUI h = (sUI) _h;
      if( (x+w) <= (sUI)image.screen->s.x )
      {
         if( (y+h) <= (sUI)image.screen->s.y )
         {
            upload(YAC_FALSE/*need_newname*/, x, y, w, h);
         }
      }
   }
}

void _Texture::_upload(void) {
   upload(b_autogen_id, 0, 0, 0, 0);
}

void _Texture::upload(sBool need_newname, sUI _x, sUI _y, sUI _w, sUI _h) {
   sBool bFixRGBA = YAC_FALSE;

   fixFlags();

   if(1 == b_autogen_id)
   {
      if(need_newname || ( ((sSI)gl_nameid)==-1) )
      {
         // do not gen new name if texture is re-uploaded
         gl_nameid = _zglGenTexture();

         flags |= TEX_UPLOADED;
      }
   }
   else
   {
      // Use "shared" texture name id
   }

   // ---- onboard textures have no system memory screen ----
   if(NULL != image.screen)
   {
      GLenum texTarget = getGlTextureTarget();
      GLenum texUploadTarget = getGlTextureUploadTarget(); // May be different than texTarget (e.g. cube maps)

      _glBindTexture(texTarget, gl_nameid);
      
      int int_format=0;
      int pix_format=0;
      int type=0;

      switch(image.screen->z)
      {
         default:
         case 0:
            yac_host->printf("\n[---] Texture::upload: invalid color depth.\n");
            break;

         case 1: // alpha
            type = GL_UNSIGNED_BYTE;

            if( (flags & TEX_INTENSITY) || (flags & TEX_ALPHA) )
            {
               /////int_format=GL_INTENSITY;
               /////pix_format=GL_ALPHA;

               // (note) intensity rgba=(v,v,v,v); luminace rgba=(v,v,v,1.0)
#ifdef DX_GLES
               // (note) SGX533/GLES alpha=(0,0,0,v) but shader will fix it to (v,v,v,v)
               int_format = GL_ALPHA;
               pix_format = GL_ALPHA;
#else
               int_format = GL_INTENSITY;
               pix_format = GL_LUMINANCE;
#endif // DX_GLES
            }
            else
            {
#ifdef DX_GLES
               int_format = GL_LUMINANCE;
#else
               int_format = GL_LUMINANCE8;
#endif // DX_GLES
               pix_format = GL_LUMINANCE;
            }
            //yac_host->printf("upload LUMINANCE texture.\n");
            break;

         case 2:
#ifdef DX_GLES
            int_format = GL_RGBA;
            pix_format = GL_RGBA;
            type = GL_UNSIGNED_SHORT_5_6_5;
#else
            int_format = GL_RGB5;
            pix_format = GL_BGRA;
            type = GL_UNSIGNED_SHORT; //GL_5_6_5;
#endif // DX_GLES
            break;

         case 3:
#ifndef DX_GLES
            if(flags & TEX_16BIT)
            {
               int_format = GL_RGB5;
            }
            else
            {
               //int_format=GL_RGB;
               int_format = GL_RGB8; // xxx equivalent to GL_RGB ?!
            }
#else
            int_format = GL_RGB;
#endif // DX_GLES
            pix_format = GL_RGB;
            type=GL_UNSIGNED_BYTE;
            //yac_host->printf("upload RGB texture.\n");
            break;

         case 4:
            if(0 == (flags & TEX_ALPHA))
            {
#ifdef DX_GLES
               ////int_format = GL_RGB;  // (note) does not work on PVR SGX533 when pix_format is GL_RGBA
               int_format = GL_RGBA;
#else
               if(flags & TEX_16BIT)
               {
                  int_format = GL_RGB5; 
               }
               else
               {
                  int_format = 3; // xxx 3? (GL_RGB?)
               }
#endif // DX_GLES
            }
            else
            {
#ifdef DX_GLES
               int_format = GL_RGBA;
#else
               int_format = 4; // xxx 4? (GL_RGBA?)
#endif // DX_GLES
            }

            type = GL_UNSIGNED_BYTE;
         
#ifdef YAC_LITTLE_ENDIAN
            if(flags & TEX_FORCEHOSTBYTEORDER)
            {
               pix_format = GL_BGRA;
            }
            else
            {
               pix_format = GL_RGBA;
               bFixRGBA   = YAC_TRUE;
            }
#else
            pix_format = GL_RGBA;
#endif
            break;
      }
      
      if(0 != (flags & TEX_REPEAT_S))
      {
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
      }
      else if(0 != (flags & TEX_CLAMPTOEDGE_S))
      {
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      }
      else //if(flags&TEX_CLAMP_S)
      {
#ifdef DX_GLES
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
#else
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
#endif // DX_GLES
      }

      if(0 != (flags & TEX_REPEAT_T))
      {
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
      }
      else if(0 != (flags & TEX_CLAMPTOEDGE_T))
      {
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      }
      else //if(flags&TEX_CLAMP_T)
      {
#ifdef DX_GLES
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#else
         _glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP);
#endif // DX_GLES
      }

      // (note) there's currently [26Nov2018] no flag for TEX_REPEAT_R => use clamp
      _glTexParameteri(texTarget, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
      
      
      
      if(0 != (flags & TEX_MODULATE))
      {
         _glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
      }
      else if(0 != (flags & TEX_BLEND)) // slow (2001)
      {
         _glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
      }
      else if(0 != (flags & TEX_DECAL))
      {
         _glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
      }
      else
      {
         _glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
      }
           
      //glTexEnviv(GL_TEXTURE_2D, GL_TEXTURE_ENV_COLOR, envcol);
            
            
      if(bFixRGBA)
      {
         yacmemptr s;
         sSI y;
         sSI isx, isy;

         if(_w)
         {
            s.u1 = &image.screen->data.u1[_y*image.screen->stride + _x];
            isx = _w;
            isy = _h;
         }
         else
         {
            s.u4=image.screen->data.u4;
            isx = image.screen->s.x;
            isy = image.screen->s.y;
         }

         for(y=0; y<isy; y++)
         {
            sSI x;
            sU32 c32;

            for(x=0; x<isx; x++)
            {
               c32 = s.u4[x];
               c32 = 
                  (((c32>>24)&0xFF)<<24 ) |
                  (((c32>>16)&0xFF)     ) |
                  (((c32>> 8)&0xFF)<<8  ) |
                  (((c32    )&0xFF)<<16 ) ;
               s.u4[x] = c32;
            }

            s.u1 += image.screen->stride;
         }
      }


      // Enable/disable automatic mipmap generation
      GLenum error = _glGetError_int();
      if(GL_NO_ERROR != error)
      {
         yac_host->printf("[~~~] Texture::upload: detected GL error %d before glTexParameteri(..GL_GENERATE_MIPMAP..) call\n", error);
      }

#ifndef DX_GLES
      if(flags & TEX_AUTOMIPMAP)
      {
         _glTexParameteri(texTarget, GL_GENERATE_MIPMAP, GL_TRUE);
      }
      else
      {
         _glTexParameteri(texTarget, GL_GENERATE_MIPMAP, GL_FALSE);
      }
#endif // DX_GLES

      error = _glGetError_int();
      if(GL_NO_ERROR != error)
      {
         // Silently ignore GL error in case GL_GENERATE_MIPMAP is not supported
         if(yac_host->yacGetDebugLevel() > 0)
         {
            yac_host->printf("[~~~] Texture::upload: detected GL error %d after glTexParameteri(..GL_GENERATE_MIPMAP..) call\n", error);
         }
      }

      
      if(GL_TEXTURE_1D == texTarget)
      {
         _glTexImage1D_int(GL_TEXTURE_1D,
                           mipmap_level,
                           int_format,
                           image.screen->s.x,
                           0, // border
                           pix_format,
                           type,
                           image.screen->data.any
                           );
      }
      else if((GL_TEXTURE_2D == texTarget) ||
              (GL_TEXTURE_CUBE_MAP == texTarget) ||
              (GL_TEXTURE_RECTANGLE == texTarget) 
              )
      {
#ifndef DX_GLES
         _glPixelStorei_int(GL_UNPACK_ROW_LENGTH, image.screen->stride/image.screen->z);
#endif // DX_GLES
         _glPixelStorei_int(GL_UNPACK_ALIGNMENT, 1); // tightly packed

#ifdef DEBUG_UPLOAD
         yac_host->printf("[dbg] TKSTexture::upload: target=0x%04x mipmap_level=%d int_format=0x%04x pix_format=0x%04x type=0x%04x\n", (sU16)texUploadTarget, mipmap_level, (sU16)int_format, (sU16)pix_format, (sU16)type);
#endif

         if(_w)
         {
            _glTexSubImage2D_int(texUploadTarget,
                                 mipmap_level,
                                 _x,
                                 _y,
                                 _w,
                                 _h,
                                 pix_format,
                                 type,
                                 image.screen->data.u1 + (image.screen->stride * _y) + (_x * image.screen->z)
                                 );
            //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

         }
         else
         {
            _glTexImage2D_int(texUploadTarget,
                              mipmap_level,
                              int_format,
                              image.screen->s.x,
                              image.screen->s.y,
                              0, // border
                              pix_format,
                              type,
                              image.screen->data.any
                              );
         }
      }
      else if((GL_TEXTURE_3D == texTarget) ||
              (GL_TEXTURE_2D_ARRAY == texTarget)
              )
      {
#ifndef DX_GLES
         _glPixelStorei_int(GL_UNPACK_ROW_LENGTH, image.screen->stride/image.screen->z);
#endif // DX_GLES
         _glPixelStorei_int(GL_UNPACK_ALIGNMENT, 1); // tightly packed
         _glPixelStorei_int(GL_UNPACK_IMAGE_HEIGHT, 0); // =use glTexImage3d height param

         // (todo) support w/h/x/y params (texsubimage) (+add z param. add updateRegion3d() method ?!)
         sUI uploadSy = image.screen->s.y / sz_hint;
         sUI uploadSz = sz_hint;

         if(uploadSy > 0u)
         {
            sUI uploadSx = image.screen->s.x;

#ifdef DEBUG_UPLOAD
            yac_host->printf("[dbg] TKSTexture::upload: target=0x%04x mipmap_level=%d int_format=0x%04x pix_format=0x%04x w=%u h=%u d=%u (totalH=%u) type=0x%04x\n", (sU16)texUploadTarget, mipmap_level, (sU16)int_format, (sU16)pix_format, uploadSx, uploadSy, uploadSz, (uploadSy * uploadSz), (sU16)type);
#endif

            _glTexImage3D_int(texUploadTarget,
                              mipmap_level,
                              int_format,
                              uploadSx,
                              uploadSy,
                              uploadSz,
                              0, // border
                              pix_format,
                              type,
                              image.screen->data.any
                              );

         }
         else
         {
            yac_host->printf("[---] Texture::upload: array/depth slice height is 0 (sy=%u, sz=%u)\n", image.screen->s.y, sz_hint);
         }
      }
      else
      {
         yac_host->printf("[---] Texture::upload: unsupported GL texture target 0x%08x. Missing glTexImage*() codepath.\n", texTarget);
         return;
      }
      
      if(bFixRGBA)
      {
         yacmemptr s;
         sSI y;
         sSI isx, isy;

         if(_w)
         {
            s.u1 = &image.screen->data.u1[_y*image.screen->stride + _x];
            isx = _w;
            isy = _h;
         }
         else
         {
            s.u4 = image.screen->data.u4;
            isx = image.screen->s.x;
            isy = image.screen->s.y;
         }

         for(y=0; y<isy; y++)
         {
            sSI x;
            sU32 c32;
            for(x=0; x<isx; x++)
            {
               c32 = s.u4[x];
               c32 = 
                  (((c32>>24)&0xFF)<<24 ) |
                  (((c32>>16)&0xFF)     ) |
                  (((c32>> 8)&0xFF)<<8  ) |
                  (((c32    )&0xFF)<<16 ) ;
               s.u4[x] = c32;
            }

            s.u1 += image.screen->stride;
         }
      }
      
      
      if(flags & TEX_MINFILTERLINEAR)
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      }
      else if(flags & TEX_MINFILTERTRILINEAR)
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
      }
      else
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      }
               
      if(flags & TEX_MAGFILTERLINEAR)
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      }
      else if(flags & TEX_MAGFILTERTRILINEAR)
      {
         // Note: Actually, there is no such such as a trilinear magfilter..
         _glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      }
      else
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      }
           
#ifndef DX_GLES       
      if(priority > 0.0f)
      {
         ::glPrioritizeTextures(1, (GLuint*)&gl_nameid, &priority);
         Dtraceglerror("glPrioritizeTextures");
      }
#endif // DX_GLES

//       if(flags & TEX_AUTOMIPMAP)
//       {
//          // Note: glGenerateMipmap seems to be broken on ATI cards.
//          //       Rumor has it that it wants to be called twice ?
//          if(glGenerateMipmap_xref)
//          {
//             glGenerateMipmap_xref(texTarget);
//          }
//          else
//          {
//             yac_host->printf("[---] Texture::upload: TEX_AUTOMIPMAP: unresolved extension function glGenerateMipmap called.\n");
//          }
//       }
                  
      if(flags & TEX_DELETE)
      {
         _freeImage();
      }
   }
}

void _Texture::_bind(void) 
{
   fixFlags();

   if(gl_nameid == (sUI)-1)
   {
      _upload();
   }

   if(gl_nameid != (sUI)-1)
   {
      GLenum texTarget = getGlTextureTarget();

      _glBindTexture(texTarget, gl_nameid);

      if(0 != (flags & TEX_MODULATE))
      {
         _glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
      }
      else if(0 != (flags & TEX_BLEND)) // slow (2001)
      {
         _glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
      }
      else if(0 != (flags & TEX_DECAL))
      {
         _glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
      }
      else
      {
         _glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
      }

      // How to filter when texture is scaled down
      if(0 != (flags & TEX_MINFILTERLINEAR))
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      }
      else if(0 != (flags & TEX_MINFILTERTRILINEAR))
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
      }
      else
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      }

      // How to filter when texture is scaled up
      if(0 != (flags & TEX_MAGFILTERLINEAR))
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      }
      else if(0 != (flags & TEX_MAGFILTERTRILINEAR))
      {
         // Note: Actually, there is no such such as a trilinear magfilter..
         _glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      }
      else
      {
         _glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      }

      if(0 != (flags & TEX_POINTSPRITE))
      {
         _glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
      }
      else
      {
         _glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_FALSE);
      }

   }
}

void _Texture::_enable(void) {
   if((sUI)-1 != gl_nameid)
   {
      GLenum texTarget = getGlTextureTarget();

      _glEnable(texTarget);
   }
}

void _Texture::_disable(void) {
   if((sUI)-1 != gl_nameid)
   {
      GLenum texTarget = getGlTextureTarget();

      _glDisable(texTarget);
   }
}

void _Texture::_clear(sSI _c) {
   if(image.screen)
   {
      image.sel.s = image.screen->s;
      image.sel.p.x = image.sel.p.y=0;
      sU32 c32=*(sU32*)&_c;
      image.drawFilledRect(0,0,image.screen->s.x, image.screen->s.y, c32);
   }
}

void _Texture::Expand2n(sSI *_sx, sSI *_sy) {

   // WTF!! get rid of the size table and replace this with some bit twiddeling magic :P

   //static sU16 size_table[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
   static sU16 size_table[12]={16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
#define E2N_NUM (sizeof(size_table)/sizeof(sU16))
   if((*_sx)<0||(*_sx)>32768)
      return;
   if((*_sy)<0||(*_sy)>32768)
      return;
   if(*_sx<size_table[0])
      *_sx=size_table[0];
   if(*_sy<size_table[0])
      *_sy=size_table[0];
   sS8 i;
   for(i=0; i<((sS8)E2N_NUM); i++)
   {
      if((*_sx)==size_table[i])
         break;
   }

   if(i==((sS8)E2N_NUM))
   {
      // odd texture size, correct
      for(i=(E2N_NUM-2); i>=0; i--)
      {
         if((*_sx)>size_table[i])
         {
            (*_sx)=size_table[i+1];
            break;
         }
      }
   }

   for(i=0; i<((sS8)E2N_NUM); i++)
   {
      if((*_sy)==size_table[i])
         break;
   }

   if(i==((sS8)E2N_NUM))
   {
      // odd texture size, correct
      for(i=(E2N_NUM-2); i>=0; i--)
      {
         if((*_sy)>size_table[i])
         {
            (*_sy)=size_table[i+1];
            break;
         }
      }
   }

   // Make quadratic:
   /*if(*_sx > *_sy)
    *_sy=*_sx;
    else if(*_sy > *_sx)
    *_sx=*_sy;*/
}

sSI _Texture::_alloc2n(sSI _sx, sSI _sy, sSI _z) {
   tksvec2i s;
   Expand2n(&_sx, &_sy);
   s.x=_sx;
   s.y=_sy;
   return image.allocScreen(s, _z);
}

sSI _Texture::_alloc(sSI _sx, sSI _sy, sSI _z) {
   tksvec2i s;
   s.x=_sx;
   s.y=_sy;
   return image.allocScreen(s, _z);
}

void _Texture::_scaleFrom(YAC_Object *_t) {
   if(YAC_CHK(_t, clid_Texture))
   {
      _Texture *t=(_Texture *)_t;
      image.scaleFrom(&t->image);
   }
   else
   {
      yac_host->printf("\n[---] Texture::scaleFrom: Object is not a Texture.\n");
   }
}

void _Texture::_free(void) {
   _freeImage();
   _unload();
}

void _Texture::_unload(void) {
   if(gl_nameid != (sUI)-1)
   {
      if(1 == b_autogen_id)
      {
         _zglDeleteTexture(gl_nameid);
         gl_nameid = (sUI)-1;
         flags &= ~TEX_UPLOADED;
      }
   }
}

void _Texture::_freeImage(void) {
   image.deleteScreen();
   image.deleteColormap();
}

void _Texture::_setEnvColor(sSI _i) {
   sU32 c32=(sU32)_i;
   sF32 envcol[4];
   envcol[0]=((c32>>16)&0xFF)*(1.0f/255.0f);
   envcol[1]=((c32>> 8)&0xFF)*(1.0f/255.0f);
   envcol[2]=((c32    )&0xFF)*(1.0f/255.0f);
   envcol[3]=((c32>>24)&0xFF)*(1.0f/255.0f);
   // update environment color
    
   _glTexEnvfv_int(getGlTextureTarget(), GL_TEXTURE_ENV_COLOR, envcol);
}


////void _Texture::_copyRegion(YAC_Object *_tex, YAC_Object *_ivecp, YAC_Object *_ivecs, YAC_Object *_ivecd) {
void _Texture::_copyRegion(YAC_Object *_tex, sSI _px, sSI _py, sSI _sx, sSI _sy, sSI _dx, sSI _dy) {
   if(YAC_CHK(_tex, clid_Texture))
   {
      _Texture*t=(_Texture*)_tex;
      image.copyRegion(&t->image, _px, _py, _sx, _sy, _dx, _dy);
   }
}

void _Texture::_setFgcolor(sSI _c32) {
   sU32 c32=(sU32)_c32;
   image.fgcolor.r=(sU8)(c32>>16);
   image.fgcolor.g=(sU8)(c32>> 8);
   image.fgcolor.b=(sU8)(c32    );
   image.fgcolor.a=(sU8)(c32>>24);
}

sSI _Texture::_getFgcolor(void) {
   return (  ((sU32)(image.fgcolor.r<<16)|(image.fgcolor.g<<8)|(image.fgcolor.b)|(image.fgcolor.a<<24)) );
}

sSI _Texture::_getSx(void) {
   if(NULL != image.screen)
   {
      return image.screen->s.x;
   }
   else
   {
      return 0;
   }
}

sSI _Texture::_getSy(void) {
   if(NULL != image.screen)
   {
      return image.screen->s.y;
   }
   else
   {
      return 0;
   }
}

sSI _Texture::_getZ(void) {
   if(NULL != image.screen)
   {
      return image.screen->z;
   }
   else
   {
      return 0;
   }
}

void _Texture::_setSz(sUI _sz) {
   if((_sz > 1u) && (_sz <= 4096u))  // note: GL must support at least 256 depth slices
   {
      sz_hint = _sz;
   }
}

sUI _Texture::_getSz(void) {
   return sz_hint;
}

void _Texture::_drawPixels2i(sSI _x, sSI _y) {
   _drawPixels3f((sF32)_x, (sF32)_y, 0.0f);
}

void _Texture::_drawPixels2f(sF32 _x, sF32 _y) {
   _drawPixels3f(_x, _y, 0.0f);
}

/*void _Texture::_drawPixels(YAC_Object *_rasterposvector) {
  if(YAC_CHK(_rasterposvector, clid_Vector))
  {
  _Vector *rpos=(_Vector*)_rasterposvector;
  _drawPixels3f(rpos->x, rpos->y, rpos->z);
  }
  else
  {
  yac_host->printf("[---] Texture::drawPixels: invalid vector object.\n");
  }
  }*/

void _Texture::_drawPixels3f(sF32 _x, sF32 _y, sF32 _z) {
   if(image.screen)
   {
      if(image.screen->data.any)
      {
         _glRasterPos3f(_x, _y, _z);

         switch(image.screen->z)
         {
            case 1:
               _glDrawPixels_int(image.screen->s.x, image.screen->s.y, GL_LUMINANCE, GL_UNSIGNED_BYTE, image.screen->data.any);
               break;

            case 3:
               _glDrawPixels_int(image.screen->s.x, image.screen->s.y, GL_RGB, GL_UNSIGNED_BYTE, image.screen->data.any);
               break;

            case 4:
               _glDrawPixels_int(image.screen->s.x, image.screen->s.y, GL_BGRA, GL_UNSIGNED_BYTE, image.screen->data.any);
               break;
         }

         _glRasterPos3f(0.0f, 0.0f, 0.0f);
      }
   }
}

void _Texture::_swapScreen(YAC_Object *_tex) {
   if(YAC_CHK(_tex, clid_Texture))
   {
      _Texture*tex=(_Texture*)_tex;
      Bitmap *scr=image.screen;
      image.screen=tex->image.screen;
      tex->image.screen=scr;
      sBool del=image.ownscreen;
      image.ownscreen=tex->image.ownscreen;
      tex->image.ownscreen=del;
      //-------- update current selection --------
      image.sel.p.x=0;
      image.sel.p.y=0;
      tex->image.sel.p.x=0;
      tex->image.sel.p.y=0;
      if(image.screen)
      {
         image.sel.s.x=image.screen->s.x;
         image.sel.s.y=image.screen->s.y;
      }
      if(tex->image.screen)
      {
         tex->image.sel.s.x=tex->image.screen->s.x;
         tex->image.sel.s.y=tex->image.screen->s.y;
      }
   }  
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~                                                                ~
//~ _TextureModBlur                                              ~
//~                                                                ~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define DgetPixel8(a,b) (((a)>=0&&(a)<_i->screen->s.x)&&((b)>=0&&(b)<_i->screen->s.y))?_i->screen->data.u1[((a)*_i->screen->z)+((b)*_i->screen->stride)]:0;
#define DgetPixel32(a,b) (((a)>=0&&(a)<_i->screen->s.x)&&((b)>=0&&(b)<_i->screen->s.y))? *(sU32*)(&_i->screen->data.u1[((a)*_i->screen->z)+((b)*_i->screen->stride)]):0;

void _Texture::simpleBlur(Image *_i, Image *_j) {
   if(_i->screen&&_j->screen)
      if(_i->screen->z==_j->screen->z)
         if(_j->screen->s.x>=_i->screen->s.x)
            if(_j->screen->s.y>=_i->screen->s.y)
               if(_i->screen->data.any&&_j->screen->data.any)
               {
                  yacmemptr s;
                  s.u1=_i->screen->data.u1;
                  yacmemptr d;
                  d.u1=_j->screen->data.u1;
                  //sU16 cr=0;
                  sS16 x,y;
                  //for(; cr<_repeat; cr++)
                  {
                     switch(_i->screen->z)
                     {
                        case 1:
                           for(y=0; y<_i->screen->s.y; y++)
                           {
                              for(x=0; x<_i->screen->s.x; x++)
                              {
                                 sU8 p[4][4];
                                 p[0][0]=DgetPixel8(x-2, y-2);
                                 p[1][0]=DgetPixel8(x-1, y-2);
                                 p[2][0]=DgetPixel8(x-0, y-2);
                                 p[3][0]=DgetPixel8(x+1, y-2);
                                 p[0][1]=DgetPixel8(x-2, y-1);
                                 p[1][1]=DgetPixel8(x-1, y-1);
                                 p[2][1]=DgetPixel8(x-0, y-1);
                                 p[3][1]=DgetPixel8(x+1, y-1);
                                 p[0][2]=DgetPixel8(x-2, y-0);
                                 p[1][2]=DgetPixel8(x-1, y-0);
                                 p[2][2]=DgetPixel8(x-0, y-0);
                                 p[3][2]=DgetPixel8(x+1, y-0);
                                 p[0][3]=DgetPixel8(x-2, y+1);
                                 p[1][3]=DgetPixel8(x-1, y+1);
                                 p[2][3]=DgetPixel8(x-0, y+1);
                                 p[3][3]=DgetPixel8(x+1, y+1);
                                 s.u1[x]=
                                    (
                                       p[0][0]+p[1][0]+p[2][0]+p[3][0]+
                                       p[0][1]+p[1][1]+p[2][1]+p[3][1]+
                                       p[0][2]+p[1][2]+p[2][2]+p[3][2]+
                                       p[0][3]+p[1][3]+p[2][3]+p[3][3])>>4;
                              }
                              s.u1+=_i->screen->stride;
                           }
                           break;
                        case 4:
                        {
                           sU32 p[4][4];
                           sU8 asum;
                           sU8 rsum;
                           sU8 gsum;
                           sU8 bsum;
                           for(y=0; y<_i->screen->s.y; y++)
                           {
                              for(x=0; x<_i->screen->s.x; x++)
                              {
                                 p[0][0]=DgetPixel32(x-2, y-2);
                                 p[1][0]=DgetPixel32(x-1, y-2);
                                 p[2][0]=DgetPixel32(x-0, y-2);
                                 p[3][0]=DgetPixel32(x+1, y-2);
                                 p[0][1]=DgetPixel32(x-2, y-1);
                                 p[1][1]=DgetPixel32(x-1, y-1);
                                 p[2][1]=DgetPixel32(x-0, y-1);
                                 p[3][1]=DgetPixel32(x+1, y-1);
                                 p[0][2]=DgetPixel32(x-2, y-0);
                                 p[1][2]=DgetPixel32(x-1, y-0);
                                 p[2][2]=DgetPixel32(x-0, y-0);
                                 p[3][2]=DgetPixel32(x+1, y-0);
                                 p[0][3]=DgetPixel32(x-2, y+1);
                                 p[1][3]=DgetPixel32(x-1, y+1);
                                 p[2][3]=DgetPixel32(x-0, y+1);
                                 p[3][3]=DgetPixel32(x+1, y+1);                     
                                 asum=
                                    (
                                       ((p[0][0]>>24)&0xFF)+((p[1][0]>>24)&0xFF)+((p[2][0]>>24)&0xFF)+((p[3][0]>>24)&0xFF)+
                                       ((p[0][1]>>24)&0xFF)+((p[1][1]>>24)&0xFF)+((p[2][1]>>24)&0xFF)+((p[3][1]>>24)&0xFF)+
                                       ((p[0][2]>>24)&0xFF)+((p[1][2]>>24)&0xFF)+((p[2][2]>>24)&0xFF)+((p[3][2]>>24)&0xFF)+
                                       ((p[0][3]>>24)&0xFF)+((p[1][3]>>24)&0xFF)+((p[2][3]>>24)&0xFF)+((p[3][3]>>24)&0xFF)
                                     ) >> 4;
                                 rsum=
                                    (
                                       ((p[0][0]>>16)&0xFF)+((p[1][0]>>16)&0xFF)+((p[2][0]>>16)&0xFF)+((p[3][0]>>16)&0xFF)+
                                       ((p[0][1]>>16)&0xFF)+((p[1][1]>>16)&0xFF)+((p[2][1]>>16)&0xFF)+((p[3][1]>>16)&0xFF)+
                                       ((p[0][2]>>16)&0xFF)+((p[1][2]>>16)&0xFF)+((p[2][2]>>16)&0xFF)+((p[3][2]>>16)&0xFF)+
                                       ((p[0][3]>>16)&0xFF)+((p[1][3]>>16)&0xFF)+((p[2][3]>>16)&0xFF)+((p[3][3]>>16)&0xFF)
                                     ) >> 4;
                        
                                 gsum=
                                    (
                                       ((p[0][0]>> 8)&0xFF)+((p[1][0]>> 8)&0xFF)+((p[2][0]>> 8)&0xFF)+((p[3][0]>> 8)&0xFF)+
                                       ((p[0][1]>> 8)&0xFF)+((p[1][1]>> 8)&0xFF)+((p[2][1]>> 8)&0xFF)+((p[3][1]>> 8)&0xFF)+
                                       ((p[0][2]>> 8)&0xFF)+((p[1][2]>> 8)&0xFF)+((p[2][2]>> 8)&0xFF)+((p[3][2]>> 8)&0xFF)+
                                       ((p[0][3]>> 8)&0xFF)+((p[1][3]>> 8)&0xFF)+((p[2][3]>> 8)&0xFF)+((p[3][3]>> 8)&0xFF)
                                     ) >> 4;
                                 bsum=
                                    (
                                       ((p[0][0]    )&0xFF)+((p[1][0]    )&0xFF)+((p[2][0]    )&0xFF)+((p[3][0]    )&0xFF)+
                                       ((p[0][1]    )&0xFF)+((p[1][1]    )&0xFF)+((p[2][1]    )&0xFF)+((p[3][1]    )&0xFF)+
                                       ((p[0][2]    )&0xFF)+((p[1][2]    )&0xFF)+((p[2][2]    )&0xFF)+((p[3][2]    )&0xFF)+
                                       ((p[0][3]    )&0xFF)+((p[1][3]    )&0xFF)+((p[2][3]    )&0xFF)+((p[3][3]    )&0xFF)
                                     ) >> 4;
                        
                                 asum=( asum+((p[2][2]>>24)&0xFF) )>>1;
                                 rsum=( rsum+((p[2][2]>>16)&0xFF) )>>1;
                                 gsum=( gsum+((p[2][2]>> 8)&0xFF) )>>1;
                                 bsum=( bsum+((p[2][2]    )&0xFF) )>>1;
                                 asum=( asum+((p[2][2]>>24)&0xFF) )>>1;
                                 rsum=( rsum+((p[2][2]>>16)&0xFF) )>>1;
                                 gsum=( gsum+((p[2][2]>> 8)&0xFF) )>>1;
                                 bsum=( bsum+((p[2][2]    )&0xFF) )>>1;
                                 d.u4[x]=(asum<<24)|(rsum<<16)|(gsum<<8)|(bsum);
                                 //d.u4[x]=p[2][2];
                                 //d.u4[x]=s.u4[x];
                              }
                              s.u1+=_i->screen->stride;
                              d.u1+=_j->screen->stride;
                           }
                        }
                        break;
                     }
                  }
               }
}

void _Texture::simpleBlurX(Image *_i, Image *_j) {
   if(_i->screen&&_j->screen)
      if(_i->screen->z==_j->screen->z)
         if(_j->screen->s.x>=_i->screen->s.x)
            if(_j->screen->s.y>=_i->screen->s.y)
               if(_i->screen->data.any&&_j->screen->data.any)
               {
                  yacmemptr s;
                  s.u1=_i->screen->data.u1;
                  yacmemptr d;
                  d.u1=_j->screen->data.u1;
                  sS16 x,y;
                  {
                     switch(_i->screen->z)
                     {
                        case 1:
                        {
                           sU8 p[3];
                           sU8 t;
                           for(y=0; y<_i->screen->s.y; y++)
                           {
                              for(x=0; x<_i->screen->s.x; x++)
                              {
                                 p[0]=DgetPixel8(x-1, y);
                                 p[1]=DgetPixel8(x  , y);
                                 p[2]=DgetPixel8(x+1, y);
                                 t=(p[0]+p[2])>>1;
                                 d.u1[x]=(p[1]+t)>>1;
                              }
                              s.u1+=_i->screen->stride;
                              d.u1+=_j->screen->stride;
                           }
                        }
                        break;
                        case 4:
                        {
                           sU8 p[3];
                           sU8 t;
                           sS16 xil;
                           sS16 xim;
                           sS16 xir;
                           for(y=0; y<_i->screen->s.y; y++)
                           {
                              xil=-4;
                              xim=0;
                              xir=4;
                              for(x=0; x<_i->screen->s.x; x++,xil+=4,xim+=4,xir+=4)
                              {
                                 /** blue **/
                                 p[0]=DgetPixel8( xil+0, y);
                                 p[1]=DgetPixel8( xim+0, y);
                                 p[2]=DgetPixel8( xir+0, y);
                                 t=(p[0]+p[2])>>1;
                                 d.u1[xim+0]=(p[1]+t)>>1;
                                 /** green **/
                                 p[0]=DgetPixel8( xil+1, y);
                                 p[1]=DgetPixel8( xim+1, y);
                                 p[2]=DgetPixel8( xir+1, y);
                                 d.u1[xim+1]=(p[1]+t)>>1;
                                 /** red **/
                                 p[0]=DgetPixel8( xil+2, y);
                                 p[1]=DgetPixel8( xim+2, y);
                                 p[2]=DgetPixel8( xir+2, y);
                                 d.u1[xim+2]=(p[1]+t)>>1;
                                 /** alpha **/
                                 p[0]=DgetPixel8( xil+3, y);
                                 p[1]=DgetPixel8( xim+3, y);
                                 p[2]=DgetPixel8( xir+3, y);
                                 d.u1[xim+3]=(p[1]+t)>>1;
                              }
                              s.u1+=_i->screen->stride;
                              d.u1+=_j->screen->stride;
                           }
                        }
                        break;
                     }
                  }
               }
}

void _Texture::simpleBlurY(Image *_i, Image *_j) {
   if(_i->screen&&_j->screen)
      if(_i->screen->z==_j->screen->z)
         if(_j->screen->s.x>=_i->screen->s.x)
            if(_j->screen->s.y>=_i->screen->s.y)
               if(_i->screen->data.any&&_j->screen->data.any)
               {
                  //yacmemptr s;
                  //s.u1=_i->screen->data.u1;
                  //yacmemptr d;
                  //d.u1=_j->screen->data.u1;
                  sS16 x,y;
                  {
                     switch(_i->screen->z)
                     {
                        case 1:
                           for(y=0; y<_i->screen->s.y; y++)
                           {
                              for(x=0; x<_i->screen->s.x; x++)
                              {
                                 // (todo) implement me
                              }
                           }
                           break;
                        case 4:
                           for(y=0; y<_i->screen->s.y; y++)
                           {
                              for(x=0; x<_i->screen->s.x; x++)
                              {
                                 // (todo) implement me
                              }
                           }
                           break;
                     }
                  }
               }
}

void _Texture::_simpleBlur(YAC_Object *_t) {
   if(YAC_CHK(_t, clid_Texture))
   {
      _Texture *t=(_Texture*)_t;
      simpleBlur(&t->image, &image);
   }
}

void _Texture::_simpleBlurX(YAC_Object *_t) {
   if(YAC_CHK(_t, clid_Texture))
   {
      _Texture *t=(_Texture*)_t;
      simpleBlurX(&t->image, &image);
   }
}

void _Texture::_simpleBlurY(YAC_Object *_t) {
   if(YAC_CHK(_t, clid_Texture))
   {
      _Texture *t=(_Texture*)_t;
      simpleBlurY(&t->image, &image);
   }
}

 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~                                                                ~
//~ TextureModColor                                                ~
//~                                                                ~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void _Texture::_applyColorMap(YAC_Object *_t, YAC_Object *_cm) {
   if(
      YAC_CHK(_t, clid_Texture)&&
      YAC_CHK(_cm, clid_Texture)
      )
   {
      _Texture *t=(_Texture*)_t;
      _Texture *cm=(_Texture*)_cm;
      if(cm->image.screen)
         if(cm->image.screen->data.any)
            if(cm->image.screen->z==4)
               if(cm->image.screen->s.x==256)
                  if(image.screen)
                     if(image.screen->data.any)
                        if(t->image.screen)
                           if(t->image.screen->data.any)
                              if(t->image.screen->z==1)
                                 if(t->image.screen->s.x==image.screen->s.x)
                                    if(t->image.screen->s.y==image.screen->s.y)
                                    {
                                       sU16 x;
                                       sU16 y;
                                       yacmemptr s;
                                       yacmemptr d;
                                       yacmemptr c;
                                       s.u1=t->image.screen->data.u1;
                                       d.u4=image.screen->data.u4;
                                       c.u4=cm->image.screen->data.u4;
                                       switch(image.screen->z)
                                       {
                                          case 4:
                                             for(y=0; y<t->image.screen->s.y; y++)
                                             {
                                                for(x=0; x<t->image.screen->s.x; x++)
                                                {
                                                   d.u4[x]=c.u4[s.u1[x]];
                                                }
                                                d.u1+=image.screen->stride;
                                                s.u1+=t->image.screen->stride;
                                             }
                                             break;
                                          case 3:
                                          {
                                             sU32 c32;
                                             sU16 x2;
                                             for(y=0; y<t->image.screen->s.y; y++)
                                             {
                                                x2=0;
                                                for(x=0; x<t->image.screen->s.x; x++)
                                                {
                                                   c32=c.u4[s.u1[x]];
                                                   d.u1[x2++]=(sU8)(c32>>16);
                                                   d.u1[x2++]=(sU8)(c32>>8);
                                                   d.u1[x2++]=(sU8)c32;
                                                }
                                                d.u1+=image.screen->stride;
                                                s.u1+=t->image.screen->stride;
                                             }
                                          }
                                          break;
                                       }
                                    }
   }
}

void _Texture::_colorKeyToAlpha(sSI _ckey) {
   image.colorKeyToAlpha((sU32)_ckey);
}

sSI _Texture::_grayscale(void) {
   return image.grayscale();
}

void _Texture::_interleaveAlpha(YAC_Object *_t) {
   if(YAC_CHK(_t, clid_Texture))
   {
      _Texture *t=(_Texture *)_t;
      if(t->image.screen)
         image.interleaveAlpha(t->image.screen);
   }
}

void _Texture::_adjustGamma(sF32 _g) {
   if(image.screen)
   {
      if(image.screen->data.any)
      {
         sU16 x,y;
         yacmemptr d;
         switch(image.screen->z)
         {
            case 1:
               for(y=0; y<image.screen->s.y; y++)
               {
                  d.u1=image.screen->data.u1+y*image.screen->stride;
                  for(x=0; x<image.screen->s.x; x++)
                  {
                     sF32 c8=d.u1[x]*_g;
                     if(c8>255.0f)
                        c8=255.0f;
                     d.u1[x]=(sU8)c8;
                  }
               }
               break;
            case 3:
            {
               sF32 c8;
               sU16 x2;
               for(y=0; y<image.screen->s.y; y++)
               {
                  x2=0;
                  d.u1=image.screen->data.u1+y*image.screen->stride;
                  for(x=0; x<image.screen->s.x; x++, x2+=3)
                  {
                     //red
                     c8=d.u1[x2+0]*_g;
                     if(c8>255.0f) c8=255.0f;
                     d.u1[x2+0]=(sU8)c8;
                     //green
                     c8=d.u1[x2+1]*_g;
                     if(c8>255.0f) c8=255.0f;
                     d.u1[x2+1]=(sU8)c8;
                     //blue
                     c8=d.u1[x2+2]*_g;
                     if(c8>255.0f) c8=255.0f;
                     d.u1[x2+2]=(sU8)c8;
                  }
               }
            }
            break;
            case 4:
            {
               sU16 x2;
               sF32 c8;
               for(y=0; y<image.screen->s.y; y++)
               {
                  x2=0;
                  d.u1=image.screen->data.u1+y*image.screen->stride;
                  for(x=0; x<image.screen->s.x; x++, x2+=4)
                  {
                     c8=d.u1[x2+0]*_g;
                     if(c8>255.0f) c8=255.0f;
                     d.u1[x2+0]=(sU8)c8;
                     
                     c8=d.u1[x2+1]*_g;
                     if(c8>255.0f) c8=255.0f;
                     d.u1[x2+1]=(sU8)c8;
                     
                     c8=d.u1[x2+2]*_g;
                     if(c8>255.0f) c8=255.0f;
                     d.u1[x2+2]=(sU8)c8;
                     
                     c8=d.u1[x2+3]*_g;
                     if(c8>255.0f) c8=255.0f;
                     d.u1[x2+3]=(sU8)c8;
                  }
               }
            }
            break;
         }
      }
   }
}


void _Texture::_adjustBrightness(sF32 _g) {

   if(image.screen)
   {
      if(image.screen->data.any)
      {
         if(_g>1.0f) _g=1.0f;
         else if(_g<0.0f) _g=0.0f;
         
         sU16 x,y;
         yacmemptr d;
         sF32 c8;
         switch(image.screen->z)
         {
            case 1:
               for(y=0; y<image.screen->s.y; y++)
               {
                  d.u1=image.screen->data.u1+y*image.screen->stride;
                  for(x=0; x<image.screen->s.x; x++)
                  {
                     c8=(255-d.u1[x])*_g;
                     d.u1[x]+=(sU8)c8;
                  }
               }
               break;
            case 3:
            {
               sF32 c8;
               sU16 x2;
               for(y=0; y<image.screen->s.y; y++)
               {
                  x2=0;
                  d.u1=image.screen->data.u1+y*image.screen->stride;
                  for(x=0; x<image.screen->s.x; x++, x2+=3)
                  {
                     //red
                     c8=(255-d.u1[x2+0])*_g;
                     d.u1[x2+0]+=(sU8)c8;
                     //green
                     c8=(255-d.u1[x2+1])*_g;
                     d.u1[x2+1]+=(sU8)c8;
                     //blue
                     c8=(255-d.u1[x2+2])*_g;
                     d.u1[x2+2]+=(sU8)c8;
                  }
               }
            }
            break;
            case 4:
            {
               sU16 x2;
               sF32 c8;
               for(y=0; y<image.screen->s.y; y++)
               {
                  x2=0;
                  d.u1=image.screen->data.u1+y*image.screen->stride;
                  for(x=0; x<image.screen->s.x; x++, x2+=4)
                  {
                     c8=(255-d.u1[x2+0])*_g;
                     d.u1[x2+0]+=(sU8)c8;
                     
                     c8=(255-d.u1[x2+1])*_g;
                     d.u1[x2+1]+=(sU8)c8;
                     
                     c8=(255-d.u1[x2+2])*_g;
                     d.u1[x2+2]+=(sU8)c8;
                     
                     c8=(255-d.u1[x2+3])*_g;
                     d.u1[x2+3]+=(sU8)c8;
                  }
               }
            }
            break;
         }
      }
   }
}

void _Texture::_blend(YAC_Object *_srca, YAC_Object *_srcb, sSI _ratio) {
   if(YAC_CHK(_srca, clid_Texture))
      if(YAC_CHK(_srcb, clid_Texture))
      {
         _Texture *dsrca=(_Texture*)_srca;
         _Texture *dsrcb=(_Texture*)_srcb;

         if(_ratio>255)_ratio=255; else if(_ratio<0)_ratio=0;
         sF32 p=(_ratio/128.0f)-1;
         p=((1.0f-p)/2.0f);
         sU16 ra=(sU16)(255*p);
         sU16 rb=(sU16)(255*(1.0f-p));

         if(image.screen)
            if(dsrca->image.screen)
               if(dsrcb->image.screen)
                  if(image.screen->data.any)
                     if(dsrca->image.screen->data.any)
                        if(dsrcb->image.screen->data.any)
                        {
                           if((image.screen->z==dsrca->image.screen->z)&&(image.screen->z==dsrcb->image.screen->z))
                              if((image.screen->s.x==dsrca->image.screen->s.x)&&(image.screen->s.y==dsrca->image.screen->s.y))
                                 if((image.screen->s.x==dsrcb->image.screen->s.x)&&(image.screen->s.y==dsrcb->image.screen->s.y))
                                 {
                                    yacmemptr sa;
                                    yacmemptr sb;
                                    yacmemptr d;
                                    sa.u1=dsrca->image.screen->data.u1;
                                    sb.u1=dsrcb->image.screen->data.u1;
                                    d.u1=image.screen->data.u1;
                                    sU16 x;
                                    sU16 y;
                                    switch(image.screen->z)
                                    {
                                       case 1:
                                       {
                                          sU16 k;
                                          for(y=0; y<image.screen->s.y; y++)
                                          {
                                             k=0;
                                             for(x=0; x<image.screen->s.x>>3; x++)
                                             {
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                             }
                                             d.u1+=image.screen->stride;
                                             sa.u1+=dsrca->image.screen->stride;
                                             sb.u1+=dsrcb->image.screen->stride;
                                          }
                                       }
                                       break;
                                       case 2:
                                          break;
                                       case 3:
                                       {
                                          sU16 k;
                                          for(y=0; y<image.screen->s.y; y++)
                                          {
                                             k=0;
                                             for(x=0; x<image.screen->s.x>>2; x++)
                                             {
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                             }
                                             d.u1+=image.screen->stride;
                                             sa.u1+=dsrca->image.screen->stride;
                                             sb.u1+=dsrcb->image.screen->stride;
                                          }
                                       }
                                       break;
                                       case 4:
                                       {
                                          sU16 k;
                                          for(y=0; y<image.screen->s.y; y++)
                                          {
                                             k=0;
                                             for(x=0; x<image.screen->s.x; x++)
                                             {
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                                d.u1[k]=(sU8)(((((sUI)sa.u1[k])*ra)+(((sUI)sb.u1[k])*rb))>>9);
                                                k++;
                                             }
                                             d.u1+=image.screen->stride;
                                             sa.u1+=dsrca->image.screen->stride;
                                             sb.u1+=dsrcb->image.screen->stride;
                                          }
                                       }
                                       break;
                                    }
                                 }
                        }
      }
}

sF32 _Texture::_countPixels(sSI _ckey) {
   if(image.screen)
      return image.countPixels((sU32)_ckey);
   return 0.0f;
}

void _Texture::_multiply(YAC_Object *_srca, YAC_Object *_srcb) {
   if(YAC_CHK(_srca, clid_Texture))
      if(YAC_CHK(_srcb, clid_Texture))
      {
         _Texture *dsrca=(_Texture*)_srca;
         _Texture *dsrcb=(_Texture*)_srcb;
         if(image.screen)
            if(dsrca->image.screen)
               if(dsrcb->image.screen)
                  if(image.screen->data.any)
                     if(dsrca->image.screen->data.any)
                        if(dsrcb->image.screen->data.any)
                        {
                           if((image.screen->z==dsrca->image.screen->z)&&(image.screen->z==dsrcb->image.screen->z))
                              if((image.screen->s.x==dsrca->image.screen->s.x)&&(image.screen->s.y==dsrca->image.screen->s.y))
                                 if((image.screen->s.x==dsrcb->image.screen->s.x)&&(image.screen->s.y==dsrcb->image.screen->s.y))
                                 {

                                    yacmemptr sa;
                                    yacmemptr sb;
                                    yacmemptr d;
                                    sa.u1=dsrca->image.screen->data.u1;
                                    sb.u1=dsrcb->image.screen->data.u1;
                                    d.u1=image.screen->data.u1;
                                    sU16 x;
                                    sU16 y;
                                    switch(image.screen->z)
                                    {
                                       case 1:
                                       {
                                          // ---- LUMINANCE, ALPHA ----
                                          for(y=0; y<image.screen->s.y; y++)
                                          {
                                             sU16 k=0;
                                             for(x=0; x<image.screen->s.x>>2; x++)
                                             {
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                             }
                                             d.u1+=image.screen->stride;
                                             sa.u1+=dsrca->image.screen->stride;
                                             sb.u1+=dsrcb->image.screen->stride;
                                          }
                                       }
                                       break;
                                       case 2:
                                          break;
                                       case 3:
                                       {
                                          // ---- RGB ----
                                          for(y=0; y<image.screen->s.y; y++)
                                          {
                                             sU16 k=0;
                                             for(x=0; x<image.screen->s.x>>1; x++)
                                             {
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;

                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                             }
                                             d.u1+=image.screen->stride;
                                             sa.u1+=dsrca->image.screen->stride;
                                             sb.u1+=dsrcb->image.screen->stride;
                                          }
                                       }
                                       break;
                                       case 4:
                                       {
                                          // ---- RGBA, ABGR ----
                                          for(y=0; y<image.screen->s.y; y++)
                                          {
                                             sU16 k=0;
                                             for(x=0; x<image.screen->s.x>>1; x++)
                                             {
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;

                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                                d.u1[k]=(((sU16)sa.u1[k])*sb.u1[k])>>8;
                                                k++;
                                             }
                                             d.u1+=image.screen->stride;
                                             sa.u1+=dsrca->image.screen->stride;
                                             sb.u1+=dsrcb->image.screen->stride;
                                          }
                                       }
                                       break;
                                    }
                                 }
                        }
      }
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~                                                                ~
//~ TextureModPrim                                                 ~
//~                                                                ~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sUI _Texture::_getXY32(sSI _x, sSI _y) {
   Bitmap *scr=0;
   scr=image.screen;
   if(scr)
   {
      if(_x>=0&&_x<scr->s.x&&
         _y>=0&&_y<scr->s.y)
      {
         yacmemptr s;
         s.u1=scr->data.u1+scr->stride*_y+scr->z*_x;
         switch(scr->z)
         {
            case 1:
               // luminance
               return (sUI)s.u1[0];
            case 2:
               // 16bit
            {
               sU16 w565=(sU16)s.u2[0];
               return ((((w565>>11))<<3)<<16)|
                  ((((w565>> 5)&63)<<2)<<8)|
                  ((((w565    )&31)<<3)   );
            }
            case 3:
               // RGB 
               return (s.u1[0]<<16)|
                  (s.u1[1]<< 8)|
                  (s.u1[2]    );
            case 4:
               // ARGB
               return s.u4[0];
         }
      }
   }
   return 0;
}

void _Texture::_setXY32(sSI _x, sSI _y, sSI _v) {
   Bitmap *scr=0;
   scr=image.screen;
   if(scr)
   {
      if(_x>=0&&_x<scr->s.x&&
         _y>=0&&_y<scr->s.y)
      {
         yacmemptr s;
         s.u1=scr->data.u1+scr->stride*_y+scr->z*_x;
         switch(scr->z)
         {
            case 1:
               // luminance
               s.u1[0]=(sU8)_v;
               break;
            case 2:
               // 16bit
            {
               s.u2[0]=
                  ((((_v>>16)&0xFF)>>3)<<11)|
                  ((((_v>> 8)&0xFF)>>2)<<5)|
                  ((((_v    )&0xFF)>>3)   );
            }
            break;
            case 3:
               // RGB 
               s.u1[0]=(_v>>16)&0xFF;
               s.u1[1]=(_v>> 8)&0xFF;
               s.u1[2]=(_v    )&0xFF;
               break;
            case 4:
               // ARGB
               s.u4[0]=_v;
               break;
         }
      }
   }
}

void _Texture::_drawPolygon(YAC_Object *_intarray, sSI _color) {
   if(YAC_BCHK(_intarray, YAC_CLID_INTARRAY))
   {
      YAC_IntArray *ia=(YAC_IntArray*)_intarray;
      image.drawFilledConvexPoly(ia->num_elements/2, ia->elements, (sU32)_color);
   }
}

///YAC_Object *_q, YAC_Object *_p
void _Texture::_drawLine(sSI _qx, sSI _qy, sSI _px, sSI _py, sSI _color) {
   image.drawLine(_qx, _qy, _px, _py, (sU32)_color);
}

////void _Texture::_drawFilledBox(YAC_Object *_p, YAC_Object *_s, sSI _color) {
void _Texture::_drawFilledBox(sSI _px, sSI _py, sSI _sx, sSI _sy, sSI _color) {
   image.drawFilledRect(_px, _py, _sx, _sy, (sU32)_color);
}

void _Texture::_createMask(sSI _colorkey) {
   sUI ckey=(sUI)(_colorkey&0xFFFFFF);
   if(image.screen)
      if(image.screen->data.any)
      {
         sSI x,y;
         sUI str=image.screen->stride;
         yacmemptr s; s.any=image.screen->data.any;
         switch(image.screen->z)
         {
            case 1:
               for(y=0; y<image.screen->s.y; y++)
               {
               
                  for(x=0; x<image.screen->s.x; x++)
                  {
                     s.u1[x]=(s.u1[x]==(sU8)ckey)?0xFF:0;
                  }
                  s.u1+=str;
               }
               break;
            case 4:
               for(y=0; y<image.screen->s.y; y++)
               {
                  for(x=0; x<image.screen->s.x; x++)
                     s.u4[x]=((s.u4[x]&0xFFFFFF)==ckey)?0xFFFFFFff:0;
                  s.u1+=str;
               }
               break;
         }
      }
}




//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~                                                                ~
//~ TextureModTransform                                            ~
//~                                                                ~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void _Texture::_scroll(sSI _dx, sSI _dy) {
   image.scroll(_dx, _dy);
}

void _Texture::_expand2n(void) {
   Bitmap *scr=0;
   scr=image.screen;
   if(scr)
   {
      tksvec2i s;
      s.x=scr->s.x;
      s.y=scr->s.y;
      _Texture::Expand2n(&s.x, &s.y);
      if((s.x!=scr->s.x)||(s.y!=scr->s.y))
      {
         /** resize window to match GL 2^n texture size requirements **/
         Image tmp;
         tmp.allocScreen(s, scr->z);
         tmp.clearScreen();
         tmp.copyRegion(&image, 0, 0, scr->s.x, scr->s.y, 0, 0);
         tmp.ownscreen=0;
         image.setScreen(tmp.screen);
         image.ownscreen=1;
         image.sel.s.x=image.screen->s.x;
         image.sel.s.y=image.screen->s.y;
         image.sel.p.x=image.sel.p.y=0;
      }
   }
}



void _Texture::_renderFixedText(YAC_Object *_fontTex, sSI _charWidth, YAC_Object *_text, sSI _x, sSI _y, 
                                YAC_Object *_colorIntArray, YAC_Object *_attrIntArray) {
   // Attr: 
   //    ----Bit--+---Description-----
   //        0    | inverse
   //        1    | underline
   //
   if(YAC_CHK(_fontTex, clid_Texture))
   {
      if(YAC_BCHK(_colorIntArray, YAC_CLID_INTARRAY))
      {
         if(YAC_BCHK(_attrIntArray, YAC_CLID_INTARRAY))
         {
            if(YAC_BCHK(_text, YAC_CLID_STRING))
            {
               _Texture *fontTex = (_Texture *)_fontTex;
               if( (fontTex->image.screen->z == 1) && (image.screen->z == 4) )
               {
                  YAC_IntArray *colors = (YAC_IntArray *) _colorIntArray;
                  YAC_IntArray *attrs  = (YAC_IntArray *) _attrIntArray;
                  YAC_String *text = (YAC_String *) _text;
                  sUI cw = (sUI) _charWidth;
                  //sUI ch = (sUI) fontTex->image.screen->s.y;
                  sUI sw = image.screen->s.x;
                  sUI sh = image.screen->s.y;
                  sUI fw = fontTex->image.screen->s.x;
                  sUI fh = fontTex->image.screen->s.y;
                  sUI dx = (sUI) _x;
                  sUI dy = (sUI) _y;
                  sUI sstride = fontTex->image.screen->stride - cw;
                  sUI dstride = (image.screen->stride>>2) - cw;
                  if( (dy+fh) <= sh )
                  {
                     sUI i = (sUI) 0;
                     while( (i<text->length) && ((dx+cw)<=sw) && 
                            (i<colors->num_elements) && (i<attrs->num_elements) 
                            )
                     {
                        sU8 c = text->chars[i];
                        //yac_host->printf("xxx tkstexture dx=%i\n",dx);
                        if(c>0)
                        {
                           sU32 *d = &image.screen->data.u4[ dx + _y*(image.screen->stride>>2) ];
                           sUI c32 = (sUI) colors->elements[ i ];
                           sUI attr = (sUI) attrs->elements[ i ];

                           if(c>32)
                           {
                              if( (((c-33)*cw)+cw) <= fw )
                              {
                                 sU8 *s = &fontTex->image.screen->data.u1[ (c-33)*cw ];

                                 switch(attr)
                                 {
                                    default:
                                    case 0: 
                                    {
                                       // Copy + modulate by color
                                       sUI y;
                                       for(y=0; y<fh; y++)
                                       {
                                          sUI x;
                                          for(x=0; x<cw; x++)
                                          {
                                             
                                             if(*s++)
                                             {
                                                *d++ = c32;
                                             }
                                             else
                                             {
                                                d++;
                                             }
                                             //sUI t = (sUI) *s++;
                                             //*d++ = ((t<<24)&c32) | ((t<<16)&c32) | ((t<<8)&c32) | (t&c32);
                                          }
                                          s += sstride;
                                          d += dstride;
                                       }
                                    }
                                    break;

                                    case 1:
                                    {
                                       // Copy inverse + modulate by color
                                       sUI y;
                                       for(y=0; y<fh; y++)
                                       {
                                          sUI x;
                                          for(x=0; x<cw; x++)
                                          {
                                             if(*s++)
                                             {
                                                d++;
                                             }
                                             else
                                             {
                                                *d++ = c32;
                                             }
                                             //sUI t = (sUI) 255-(*s++);
                                             //*d++ = ((t<<24)&c32) | ((t<<16)&c32) | ((t<<8)&c32) | (t&c32);
                                          }
                                          s += sstride;
                                          d += dstride;
                                       }
                                    }
                                    break;

                                    case 2:
                                    {
                                       // Copy + underline + modulate by color
                                       sUI x,y;
                                       for(y=0; y<(fh-1); y++)
                                       {
                                          for(x=0; x<cw; x++)
                                          {
                                             if(*s++)
                                             {
                                                *d++ = c32;
                                             }
                                             else
                                             {
                                                d++;
                                             }
                                          }
                                          s += sstride;
                                          d += dstride;
                                       }
                                       for(x=0; x<cw; x++)
                                       {
                                          *d++ = c32;
                                       }
                                    }
                                    break;
                                 
                                    case 3:
                                    {
                                       // Copy inverse + underline + modulate by color
                                       sUI x,y;
                                       for(y=0; y<(fh-1); y++)
                                       {
                                          for(x=0; x<cw; x++)
                                          {
                                             if(*s++)
                                             {
                                                d++;
                                             }
                                             else
                                             {
                                                *d++ = c32;
                                             }
                                          }
                                          s += sstride;
                                          d += dstride;
                                       }
                                       /*for(x=0; x<cw; x++)
                                         {
                                         *d++ = c32;
                                         }*/
                                    }
                                    break;
                                 } // switch attr

                              }
                           }
                           else
                           {
                              // whitespace
                              switch(attr)
                              {
                                 default:
                                 case 0: 
                                 {
                                    // Copy + modulate by color
                                    // (intentionally left blank)
                                 }
                                 break;
                                 
                                 case 1: // Copy inverse + modulate by color
                                 case 3: // Copy inverse + underline + modulate by color
                                 {
                                    sUI y;
                                    for(y=0; y<fh; y++)
                                    {
                                       sUI x;
                                       for(x=0; x<cw; x++)
                                       {
                                          *d++ = c32;
                                       }
                                       d += dstride;
                                    }
                                 }
                                 break;
                                 
                                 case 2:
                                 {
                                    // Copy + underline + modulate by color
                                    sUI x;
                                    d += dstride * (fh-1);
                                    for(x=0; x<cw; x++)
                                    {
                                       *d++ = c32;
                                    }
                                 }
                                 break;
                                 
                              } // switch attr

                           } // else whitespace

                        } // c > 0
                        else
                        {
                           break;
                        }
                        i++;
                        dx += cw;
                     }
                  }
               }
               else
               {
                  yac_host->printf("[---] Texture::renderFixedText: bpp mismatch.\n");
               }
            }
            else
            {
               yac_host->printf("[---] Texture::renderFixedText: text missing.\n");
            }
         }
         else
         {
            yac_host->printf("[---] Texture::renderFixedText: attributes missing.\n");
         }
      }
      else
      {
         yac_host->printf("[---] Texture::renderFixedText: colors missing.\n");
      }
   }
   else
   {
      yac_host->printf("[---] Texture::renderFixedText: font missing.\n");
   }
}


void _Texture::_setMipMapLevel(sSI _level) {
   mipmap_level = (sUI) _level;
}

sSI _Texture::_getMipMapLevel(void) {
   return (sSI) mipmap_level;
}

void _Texture::_setTarget(sSI _glTextureTarget) {
   target = (sUI) _glTextureTarget;

   fixFlags(); // add CLAMPTOEDGE_*
}

sSI _Texture::_getTarget(void) {
   return (sSI) target;
}

void _Texture::_flipY(void) {
   image.flipY();
}

void _Texture::_visitBuffer(YAC_Object *_buffer, sUI _w, sUI _h, sUI _bpp) {

   _freeImage();

   if(YAC_BCHK(_buffer, YAC_CLID_BUFFER))
   {
      YAC_Buffer *b = (YAC_Buffer *)_buffer;

      sUI reqSize = (_w * _h * _bpp);
      sUI availSize = b->size - b->io_offset;
      
      if( reqSize <= availSize)
      {
         image.visitBuffer(b->buffer + b->io_offset, _w, _h, _bpp);        
      }
      else
      {
         yac_host->printf("[---] Texture::visitBuffer: buffer size is exceeded (%u * %u * %u (%u) > (%u - %u (%u)))\n", 
                          _w, _h, _bpp, reqSize, b->size, b->io_offset, (b->size - b->io_offset));
      }
   }
}
