text: #include "raster.h"
  
  #include "../glx/hardext.h"
  #include "blit.h"
  #include "debug.h"
  #include "gl4es.h"
  #include "glstate.h"
  #include "init.h"
  #include "list.h"
  #include "loader.h"
  #include "matvec.h"
  #include "pixel.h"
  
  #define min(a, b)	((a)<b)?(a):(b)
  #define max(a, b)	((a)>(b))?(a):(b)
  
  void gl4es_glRasterPos3f(GLfloat x, GLfloat y, GLfloat z) {
      if (glstate->list.active)
          if (glstate->list.compiling) {
  			NewStage(glstate->list.active, STAGE_RASTER);
  			rlRasterOp(glstate->list.active, 1, x, y, z);
  			noerrorShim();
  			return;
  		} else flush();
  
  	// Transform xyz coordinates with current modelview and projection matrix...
  	GLfloat glmatrix[16], projection[16], modelview[16];
  	GLfloat t[4], transl[4] = {x, y, z, 1.0f};
  	gl4es_glGetFloatv(GL_PROJECTION_MATRIX, glmatrix);
  	matrix_transpose(glmatrix, projection);
  	gl4es_glGetFloatv(GL_MODELVIEW_MATRIX, glmatrix);
  	matrix_transpose(glmatrix, modelview);
  	matrix_vector(modelview, transl, t);
  	matrix_vector(projection, t, transl);
  	GLfloat w2, h2;
  	w2=glstate->raster.viewport.width/2.0f;
  	h2=glstate->raster.viewport.height/2.0f;
  	glstate->raster.rPos.x = transl[0]*w2+w2;
  	glstate->raster.rPos.y = transl[1]*h2+h2;
  	glstate->raster.rPos.z = transl[2];
  }
  
  void gl4es_glWindowPos3f(GLfloat x, GLfloat y, GLfloat z) {
      if (glstate->list.active)
          if (glstate->list.compiling) {
  			NewStage(glstate->list.active, STAGE_RASTER);
  			rlRasterOp(glstate->list.active, 2, x, y, z);
  			noerrorShim();
  			return;
  		} else flush();
  
      glstate->raster.rPos.x = x;
      glstate->raster.rPos.y = y;
      glstate->raster.rPos.z = z;	
  }
  
  void gl4es_glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
      PUSH_IF_COMPILING(glViewport);
      LOAD_GLES(glViewport);
  	if(glstate->raster.viewport.x!=x || glstate->raster.viewport.y!=y || glstate->raster.viewport.width!=width || glstate->raster.viewport.height!=height) {
  		if (glstate->raster.bm_drawing)
  			bitmap_flush();
  		gles_glViewport(x, y, width, height);
  		glstate->raster.viewport.x = x;
  		glstate->raster.viewport.y = y;
  		glstate->raster.viewport.width = width;
  		glstate->raster.viewport.height = height;
  	}
  }
  
  // hacky viewport temporary changes
  void pushViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
      LOAD_GLES(glViewport);
      gles_glViewport(x, y, width, height);
  }
  void popViewport() {
      LOAD_GLES(glViewport);
      gles_glViewport(glstate->raster.viewport.x, glstate->raster.viewport.y, glstate->raster.viewport.width, glstate->raster.viewport.height);
  }
  
  
  void gl4es_glPixelZoom(GLfloat xfactor, GLfloat yfactor) {
      if (glstate->list.active)
          if (glstate->list.compiling) {
  			NewStage(glstate->list.active, STAGE_RASTER);
  			rlRasterOp(glstate->list.active, 3, xfactor, yfactor, 0.0f);
  			noerrorShim();
  			return;
  		} else flush();
  
  	glstate->raster.raster_zoomx = xfactor;
  	glstate->raster.raster_zoomy = yfactor;
  //printf("LIBGL: glPixelZoom(%f, %f)\n", xfactor, yfactor);
  }
  
  void gl4es_glPixelTransferf(GLenum pname, GLfloat param) {
      if (glstate->list.active)
          if (glstate->list.compiling) {
  			NewStage(glstate->list.active, STAGE_RASTER);
  			rlRasterOp(glstate->list.active, pname|0x10000, param, 0.0f, 0.0f);
  			noerrorShim();
  			return;
  		} else flush();
  
  //printf("LIBGL: glPixelTransferf(%04x, %f)\n", pname, param);
  	switch(pname) {
  		case GL_RED_SCALE:
  			glstate->raster.raster_scale[0]=param;
  			break;
  		case GL_RED_BIAS:
  			glstate->raster.raster_bias[0]=param;
  			break;
  		case GL_GREEN_SCALE:
  		case GL_BLUE_SCALE:
  		case GL_ALPHA_SCALE:
  			glstate->raster.raster_scale[(pname-GL_GREEN_SCALE)/2+1]=param;
  			break;
  		case GL_GREEN_BIAS:
  		case GL_BLUE_BIAS:
  		case GL_ALPHA_BIAS:
  			glstate->raster.raster_bias[(pname-GL_GREEN_BIAS)/2+1]=param;
  			break;
  		case GL_INDEX_SHIFT:
  			glstate->raster.index_shift=param;
  			break;
  		case GL_INDEX_OFFSET:
  			glstate->raster.index_offset=param;
  			break;
  		case GL_MAP_COLOR:
  			glstate->raster.map_color=param?1:0;
  			break;
  		/*default:
  			printf("LIBGL: stubbed glPixelTransferf(%04x, %f)\n", pname, param);*/
  	// the other...
  	}
  }
  
  
  void init_raster(int width, int height) {
  	int w, h;
  	w=(hardext.npot>0)?width:npot(width);
  	h=(hardext.npot>0)?height:npot(height);
  	if (glstate->raster.data) {
  		if ((glstate->raster.raster_nwidth<width) || (glstate->raster.raster_nheight<height)) {
  			free(glstate->raster.data);
  			glstate->raster.data = NULL;
  		}
  	}
      if (!glstate->raster.data) {
          glstate->raster.data = (GLubyte *)malloc(4 * w * h * sizeof(GLubyte)); // no need to memset to 0
  		glstate->raster.raster_nwidth = w; glstate->raster.raster_nheight = h;
  	}
  	glstate->raster.raster_x1 = 0; glstate->raster.raster_y1 = 0;
  	glstate->raster.raster_x2 = width; glstate->raster.raster_y2 = height;
  	glstate->raster.raster_width = width; glstate->raster.raster_height = height;
  }
  
  GLubyte raster_transform(GLubyte pix, GLubyte number) {
  	GLfloat a = (GLfloat)pix*(1.0f/255.0f);
  	a=a*glstate->raster.raster_scale[number]+glstate->raster.raster_bias[number];
  	if (a<0.0f) return 0;
  	if (a>1.0f) return 255;
  	return (GLubyte)(a*255.0f);
  }
  GLfloat FASTMATH raster_transformf(GLfloat pix, GLubyte number) {
  	pix=pix*glstate->raster.raster_scale[number]+glstate->raster.raster_bias[number];
  	if (pix<0.0f) return 0.0f;
  	if (pix>1.0f) return 1.0f;
  	return pix;
  }
  
  int raster_need_transform() {
  	for (int i=0; i<4; i++) if (glstate->raster.raster_scale[i]!=1.0f) return 1;
  	for (int i=0; i<4; i++) if (glstate->raster.raster_bias[i]!=0.0f) return 1;
  	return 0;
  }
  
  void raster_to_texture(rasterlist_t *r)
  {
  	renderlist_t *old_list = glstate->list.active;
  	if (old_list) glstate->list.active = NULL;		// deactivate list...
  	GLboolean compiling = glstate->list.compiling;
  	glstate->list.compiling = false;
      gl4es_glPushAttrib(GL_TEXTURE_BIT | GL_ENABLE_BIT );
  	GLuint old_tex_unit, old_tex;
  	old_tex_unit = glstate->texture.active;
  	if (old_tex_unit!=0) gl4es_glActiveTexture(GL_TEXTURE0);
  	old_tex = glstate->texture.bound[0][ENABLED_TEX2D]->glname;
  
  	gl4es_glPixelStorei(GL_PACK_ALIGNMENT, 1);
      gl4es_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      gl4es_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
      gl4es_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
      gl4es_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
  	gl4es_glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  	
  	if(r->texture==0) {
  		gl4es_glEnable(GL_TEXTURE_2D);
  		gl4es_glGenTextures(1, &r->texture);
  		gl4es_glBindTexture(GL_TEXTURE_2D, r->texture);
  
  		gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  		gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); // this is to be sure texture is not npot'ed ...
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);	// ... if not needed
  		gl4es_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glstate->raster.raster_nwidth, glstate->raster.raster_nheight,
  			0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  	} else {
  		gl4es_glBindTexture(GL_TEXTURE_2D, r->texture);
  	}
  	gl4es_glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, glstate->raster.raster_width, glstate->raster.raster_height,
  		GL_RGBA, GL_UNSIGNED_BYTE, glstate->raster.data);
  
  	r->width = glstate->raster.raster_width;
  	r->height = glstate->raster.raster_height;
  	r->nwidth = glstate->raster.raster_nwidth;
  	r->nheight = glstate->raster.raster_nheight;
  	gl4es_glBindTexture(GL_TEXTURE_2D, old_tex);
  	if (old_tex_unit!=0) 
  		gl4es_glActiveTexture(old_tex_unit+GL_TEXTURE0);
  	gl4es_glPopAttrib();
  	if (old_list) glstate->list.active = old_list;
  	glstate->list.compiling = compiling;
  }
  
  void bitmap_flush() {
  	if(!glstate->raster.bm_drawing)
  		return;
  	// draw actual bitmap
  	int old_tex_unit = glstate->texture.active;
  	if(old_tex_unit)
  		gl4es_glActiveTexture(GL_TEXTURE0);
  
  	GLuint old_tex = glstate->texture.bound[0][ENABLED_TEX2D]->glname;
  	GLuint old_active = glstate->enable.texture[0];
  
  	if(IS_TEX1D(old_active)) gl4es_glDisable(GL_TEXTURE_1D);
  	if(!IS_TEX2D(old_active)) gl4es_glEnable(GL_TEXTURE_2D);
  	if(IS_TEX3D(old_active)) gl4es_glDisable(GL_TEXTURE_3D);
  	if(IS_TEXTURE_RECTANGLE(old_active)) gl4es_glDisable(GL_TEXTURE_RECTANGLE_ARB);
  	if(IS_CUBE_MAP(old_active)) gl4es_glDisable(GL_TEXTURE_CUBE_MAP);
  
  	if(!glstate->raster.bm_texture) {
  		gl4es_glGenTextures(1, &glstate->raster.bm_texture);
  		gl4es_glBindTexture(GL_TEXTURE_2D, glstate->raster.bm_texture);
  
  		gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  		gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); // this is to be sure texture is not npot'ed ...
  		gl4es_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);	// ... if not needed
  	} else {
  		gl4es_glBindTexture(GL_TEXTURE_2D, glstate->raster.bm_texture);
  	}
  	if (glstate->raster.bm_tnwidth < glstate->raster.bm_width || glstate->raster.bm_tnheight < glstate->raster.bm_height) {
  		glstate->raster.bm_tnwidth = (hardext.npot)?glstate->raster.bm_width:npot(glstate->raster.bm_width);
  		glstate->raster.bm_tnheight = (hardext.npot)?glstate->raster.bm_height:npot(glstate->raster.bm_height);
  		gl4es_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glstate->raster.bm_tnwidth, glstate->raster.bm_tnheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  	}
  	int sx = glstate->raster.bm_x1;
  	int ex = glstate->raster.bm_x2-glstate->raster.bm_x1;
  	int sy = glstate->raster.bm_y1;
  	int ey = glstate->raster.bm_y2-glstate->raster.bm_y1;
  	if(sx==0 && sy==0 && ex==glstate->raster.bm_width && ey==glstate->raster.bm_height) {
  		gl4es_glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, glstate->raster.bm_width, glstate->raster.bm_height, GL_RGBA, GL_UNSIGNED_BYTE, glstate->raster.bitmap);
  	} else {
  		int alloc = 4*ex*ey;
  		gl4es_scratch(alloc);
  		for (int i=0; i<ey; i++)
  			memcpy(glstate->scratch+4*i*ex, glstate->raster.bitmap+4*(sx+(sy+i)*glstate->raster.bm_width), ex*4);
  		gl4es_glTexSubImage2D(GL_TEXTURE_2D, 0, sx, sy, ex, ey, GL_RGBA, GL_UNSIGNED_BYTE, glstate->scratch);
  	}
  
  	gl4es_blitTexture(
  		glstate->raster.bm_texture, 
  		sx, sy,
  		ex, ey,
  		glstate->raster.bm_tnwidth , glstate->raster.bm_tnheight,
  		1.f, 1.f, 
  		0, 0,	//vp is default here
  		sx, sy,
  		BLIT_ALPHA
  	);
  
  	glstate->raster.bm_drawing = 0;
  
  	if(IS_TEX1D(old_active)) gl4es_glEnable(GL_TEXTURE_1D);
  	if(!IS_TEX2D(old_active)) gl4es_glDisable(GL_TEXTURE_2D);
  	if(IS_TEX3D(old_active)) gl4es_glEnable(GL_TEXTURE_3D);
  	if(IS_TEXTURE_RECTANGLE(old_active)) gl4es_glEnable(GL_TEXTURE_RECTANGLE_ARB);
  	if(IS_CUBE_MAP(old_active)) gl4es_glEnable(GL_TEXTURE_CUBE_MAP);
  
  	if (old_tex!=glstate->raster.bm_texture)
  		gl4es_glBindTexture(GL_TEXTURE_2D, old_tex);
  
  	if(old_tex_unit)
  		gl4es_glActiveTexture(GL_TEXTURE0 + old_tex_unit);
  }
  
  
  void gl4es_glBitmap(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig,
                GLfloat xmove, GLfloat ymove, const GLubyte *bitmap) {
  /*printf("glBitmap, xy={%f, %f}, xyorig={%f, %f}, size={%u, %u}, zoom={%f, %f}, viewport={%i, %i, %i, %i}\n", 	
  	glstate->raster.rPos.x, glstate->raster.rPos.y, xorig, yorig, width, height, glstate->raster.raster_zoomx, glstate->raster.raster_zoomy, glstate->raster.viewport.x, glstate->raster.viewport.y, glstate->raster.viewport.width, glstate->raster.viewport.height);*/
      // TODO: shouldn't be drawn if the raster pos is outside the viewport?
      // TODO: negative width/height mirrors bitmap?
  	noerrorShim();
  	if(glstate->list.active && glstate->list.pending)
  		flush();
  	if(glstate->list.active) {
  		renderlist_t* list = glstate->list.active;
  		if (!list->bitmaps) {
  			list->bitmaps = (bitmaps_t*)malloc(sizeof(bitmaps_t));
  			memset(list->bitmaps, 0, sizeof(bitmaps_t));
  			list->bitmaps->shared = (int*)malloc(sizeof(int)); 
              *list->bitmaps->shared = 0;
  		}
  		if(list->bitmaps->count == list->bitmaps->cap) {
  			list->bitmaps->cap += 8;
  			list->bitmaps->list = (bitmap_list_t*)realloc(list->bitmaps->list, list->bitmaps->cap*sizeof(bitmap_list_t));
  		}
  		bitmap_list_t *l = &list->bitmaps->list[list->bitmaps->count++];
  		l->width = width;
  		l->height = height;
  		l->xorig = xorig;
  		l->yorig = yorig;
  		l->xmove = xmove;
  		l->ymove = ymove;
  		int sz = ((width+7)/8)*height;
  		l->bitmap = (GLubyte*)malloc(sz);
  		memcpy(l->bitmap, bitmap, sz);
  		return;
  	}
      if ((!width && !height) || (bitmap==0)) {
  		glstate->raster.rPos.x += xmove;
  		glstate->raster.rPos.y += ymove;
          return;
      }
  
  	// get start/end of drawed pixel
  	float zoomx = glstate->raster.raster_zoomx;
  	float zoomy = glstate->raster.raster_zoomy;
  	int sx, sy, ex, ey;
  	sx = 0;
  	sy = 0;
  	ex = width*zoomx;
  	ey = height*zoomy;
  
  	int rx = glstate->raster.rPos.x-xorig;
  	int ry = glstate->raster.rPos.y-yorig;
  
  	if (rx+sx<0) sx = -rx;
  	if (ry+sy<0) sy = -ry;
  	if (rx+ex>glstate->raster.viewport.width) ex = glstate->raster.viewport.width-rx;
  	if (ry+ey>glstate->raster.viewport.height) ey = glstate->raster.viewport.height-ry;
  	if( ex<sx) {
  		int tmp =ex;
  		ex = sx;
  		sx = tmp;
  	}
  	if( ey<sy) {
  		int tmp =ey;
  		ey = sy;
  		sy = tmp;
  	}
  	if (ex<0 || ey<0 || sx<0 || sy<0 || sx==ex || sy==ey)	// nothing to draw, no changes
  		return;
  	// create/realloc buffer if needed
  	if(glstate->raster.bm_alloc < glstate->raster.viewport.width*glstate->raster.viewport.height*4) {
  		if(glstate->raster.bitmap)
  			free(glstate->raster.bitmap);
  		glstate->raster.bm_alloc = glstate->raster.viewport.width*glstate->raster.viewport.height*4;
  		glstate->raster.bitmap = (GLubyte*)malloc(glstate->raster.bm_alloc);
  	}
  	// clear buffer if needed
  	if(!glstate->raster.bm_drawing) {
  		memset(glstate->raster.bitmap, 0, glstate->raster.viewport.width*glstate->raster.viewport.height*4);
  		glstate->raster.bm_width = glstate->raster.viewport.width;
  		glstate->raster.bm_height = glstate->raster.viewport.height;
  		glstate->raster.bm_x1 = glstate->raster.bm_width;
  		glstate->raster.bm_y1 = glstate->raster.bm_height;
  		glstate->raster.bm_x2 = 0;
  		glstate->raster.bm_y2 = 0;
  	}
  	glstate->raster.bm_x1 = min(glstate->raster.bm_x1, rx+sx);
  	glstate->raster.bm_y1 = min(glstate->raster.bm_y1, ry+sy);
  	glstate->raster.bm_x2 = max(glstate->raster.bm_x2, rx+ex);
  	glstate->raster.bm_y2 = max(glstate->raster.bm_y2, ry+ey);
  	// draw the scaled bitmap
  	int pixtrans=raster_need_transform();
      const GLubyte *from;
      GLubyte *to;
  	GLubyte col[4];
  	for (int i=0; i<4; i++)
  		col[i] = glstate->color[i]*255.f;
      // copy to pixel data
  	if (pixtrans) {
          for (int y = sy; y < ey; ++y) {
  			int by = floor(y/zoomy);
              from = bitmap + (by * ((width+7)/8));
  			to = glstate->raster.bitmap + 4 * (GLint)(rx+((ry+y) * glstate->raster.bm_width));
              for (int x = sx; x < ex; ++x) {
  				int bx = floor(x/zoomx);
                  GLubyte b = from[(bx / 8)];
                  int p = (b & (1 << (7 - (bx % 8)))) ? 1 : 0;
                  // r, g, b, a
  				*to++ = raster_transform(col[0]*p, 0);
  				*to++ = raster_transform(col[1]*p, 1);
  				*to++ = raster_transform(col[2]*p, 2);
  				*to++ = raster_transform(col[3]*p, 3);
  			}
          }
  	} else {
          for (int y = sy; y < ey; ++y) {
  			int by = floor(y/zoomy);
              from = bitmap + (by * ((width+7)/8));
  			to = glstate->raster.bitmap + 4 * (GLint)(rx+((ry+y) * glstate->raster.bm_width));
              for (int x = sx; x < ex; ++x) {
  				int bx = floor(x/zoomx);
                  GLubyte b = from[(bx / 8)];
                  int p = (b & (1 << (7 - (bx % 8)))) ? 1 : 0;
  				*to++ = col[0]*p;
  				*to++ = col[1]*p;
  				*to++ = col[2]*p;
  				*to++ = col[3]*p;
  			}
          }
      }
  
  	// finished, move...
  	glstate->raster.rPos.x += xmove;
  	glstate->raster.rPos.y += ymove;
  	// draw in buffer...
  	glstate->raster.bm_drawing = 1;
  }
  
  void gl4es_glDrawPixels(GLsizei width, GLsizei height, GLenum format,
                    GLenum type, const GLvoid *data) {
      GLubyte *pixels, *from, *to;
      GLvoid *dst = NULL;
  
  	if(type==GL_BITMAP) {
  		gl4es_glBitmap(width, height, 0, 0, 0, 0, data);
  		return;
  	}
  
      noerrorShim();
  	if(glstate->list.active && glstate->list.pending)
  		flush();
  
      if (glstate->raster.bm_drawing)
          bitmap_flush();
  
  /*printf("glDrawPixels, xy={%f, %f}, size={%i, %i}, format=%s, type=%s, zoom={%f, %f}, viewport={%i, %i, %i, %i}\n", 	
  	glstate->raster.rPos.x, glstate->raster.rPos.y, width, height, PrintEnum(format), PrintEnum(type), glstate->raster.raster_zoomx, glstate->raster.raster_zoomy, glstate->raster.viewport.x, glstate->raster.viewport.y, glstate->raster.viewport.width, glstate->raster.viewport.height);*/
  	// check of unsuported format...
  	if ((format == GL_STENCIL_INDEX) || (format == GL_DEPTH_COMPONENT)) {
          errorShim(GL_INVALID_ENUM);
  		return;
      }
  
      init_raster(width, height);
  
  	GLsizei bmp_width = (glstate->texture.unpack_row_length)?glstate->texture.unpack_row_length:width;
  
      if (! pixel_convert(data, &dst, bmp_width, height,
                          format, type, GL_RGBA, GL_UNSIGNED_BYTE, 0, 1)) {	// pack_align is forced to 1 when drawing
          return;
      }
  					  
      pixels = (GLubyte *)dst;
  	GLint vx, vy;
  	int pixtrans=raster_need_transform();
  
      if (pixtrans) {
          for (int y = 0; y < height; y++) {
              to = glstate->raster.data + 4 * (GLint)(y * glstate->raster.raster_width);
              from = pixels + 4 * (glstate->texture.unpack_skip_pixels + (y + glstate->texture.unpack_skip_rows) * bmp_width);
              for (int x = 0; x < width; x++) {
  				*to++ = raster_transform(*from++, 0);
  				*to++ = raster_transform(*from++, 1);
  				*to++ = raster_transform(*from++, 2);
  				*to++ = raster_transform(*from++, 3);
              }
          }
  	} else {
          for (int y = 0; y < height; y++) {
              to = glstate->raster.data + 4 * (GLint)(y * glstate->raster.raster_width);
              from = pixels + 4 * (glstate->texture.unpack_skip_pixels + (y + glstate->texture.unpack_skip_rows) * bmp_width);
              for (int x = 0; x < width; x++) {
  				*to++ = *from++;
  				*to++ = *from++;
  				*to++ = *from++;
  				*to++ = *from++;
  			}
  		}
  	}
  	if (pixels != data)
          free(pixels);
  	
      rasterlist_t *r;
  	if (glstate->list.active) {
  		NewStage(glstate->list.active, STAGE_RASTER);
  		r = glstate->list.active->raster = (rasterlist_t*)malloc(sizeof(rasterlist_t));
  		memset(r, 0, sizeof(rasterlist_t));
          r->shared = (int*)malloc(sizeof(int));
          *r->shared = 0;
  	} else {
  		r = &glstate->raster.immediate;
  		if(r->texture && (width>r->nwidth || height>r->nheight)) {
  			gl4es_glDeleteTextures(1, &r->texture);
  			r->texture = 0;
  		}
  	}
  	raster_to_texture(r);
  	r->xmove = 0;
  	r->ymove = 0;
  	r->xorig = 0;
  	r->yorig = 0;
  	r->bitmap = false;
  	r->zoomx = glstate->raster.raster_zoomx;
  	r->zoomy = glstate->raster.raster_zoomy;
  	if (!(glstate->list.active)) {
  		render_raster_list(r);
  /*		gles_glDeleteTextures(1, &r->texture);
  		r->texture = 0;*/
  	}
  }
  
  void render_raster_list(rasterlist_t* rast) {
  //printf("render_raster_list, rast->x/y=%f/%f rast->width/height=%i/%i, rPos.x/y/z=%f/%f/%f, rast->zoomxy=%f/%f raster->texture=%u\n", rast->xorig, rast->yorig, rast->width, rast->height, glstate->raster.rPos.x, glstate->raster.rPos.y, glstate->raster.rPos.z, rast->zoomx, rast->zoomy, rast->texture);
  	if (rast->texture)
  		gl4es_blitTexture(
  			rast->texture, 
  			0.f, 0.f,
  			rast->width , rast->height,
  			rast->nwidth, rast->nheight,
  			rast->zoomx, rast->zoomy, 
  			0, 0,	//vp is default here
  			glstate->raster.rPos.x-rast->xorig, glstate->raster.rPos.y-rast->yorig,
  			(rast->bitmap)?BLIT_ALPHA:BLIT_COLOR
  		);
  	glstate->raster.rPos.x += rast->xmove;
  	glstate->raster.rPos.y += rast->ymove;
  }
  
  int map_pixelmap(GLenum map, int* wf, int** size, void** array) {
  	*wf = 1;
  	switch (map) {
  		case GL_PIXEL_MAP_I_TO_I:
  			*wf = 0;
  			*array = (void*)glstate->raster.map_i2i;
  			*size = &glstate->raster.map_i2i_size;
  			break;
  		case GL_PIXEL_MAP_I_TO_R:
  			*array = (void*)glstate->raster.map_i2r;
  			*size = &glstate->raster.map_i2r_size;
  			break;
  		case GL_PIXEL_MAP_I_TO_G:
  			*array = (void*)glstate->raster.map_i2g;
  			*size = &glstate->raster.map_i2g_size;
  			break;
  		case GL_PIXEL_MAP_I_TO_B:
  			*array = (void*)glstate->raster.map_i2b;
  			*size = &glstate->raster.map_i2b_size;
  			break;
  		case GL_PIXEL_MAP_I_TO_A:
  			*array = (void*)glstate->raster.map_i2a;
  			*size = &glstate->raster.map_i2a_size;
  			break;
  		case GL_PIXEL_MAP_S_TO_S:
  		case GL_PIXEL_MAP_R_TO_R:
  		case GL_PIXEL_MAP_G_TO_G:
  		case GL_PIXEL_MAP_B_TO_B:
  		case GL_PIXEL_MAP_A_TO_A:
  			// not handled
  			noerrorShim();
  			return 0;
  		default:
  			errorShim(GL_INVALID_ENUM);
  			return 0;
  	}
  	return 1;
  }
  
  void gl4es_glPixelMapfv(GLenum map, GLsizei mapsize, const GLfloat *values) {
  	if(mapsize>MAX_MAP_SIZE) {
  		errorShim(GL_INVALID_VALUE);
  		return;
  	}
  	int need_pot = 0;
  	if(map==GL_PIXEL_MAP_I_TO_I || map==GL_PIXEL_MAP_S_TO_S 
  		|| map==GL_PIXEL_MAP_I_TO_R || map==GL_PIXEL_MAP_I_TO_G
  		|| map==GL_PIXEL_MAP_I_TO_B || map==GL_PIXEL_MAP_I_TO_A)
  		need_pot=1;
  	if(need_pot && npot(mapsize)!=mapsize) {
  		errorShim(GL_INVALID_VALUE);
  		return;
  	}
  	int wf = 1;
  	void* array = NULL;
  	int* size = NULL;
  	if(!map_pixelmap(map, &wf, &size, &array))
  		return;
  	noerrorShim();
  	if(wf) {
  		GLfloat *p = (GLfloat*)array;
  		for (int i=0; i<mapsize; i++)
  			p[i] = values[i]*255.0f;
  	} else {
  		GLuint *p = (GLuint*)array;
  		for (int i=0; i<mapsize; i++)
  			p[i] = values[i];
  	}
  	*size = mapsize;
  }
  void gl4es_glPixelMapuiv(GLenum map,GLsizei mapsize, const GLuint *values) {
  	if(mapsize>MAX_MAP_SIZE) {
  		errorShim(GL_INVALID_VALUE);
  		return;
  	}
  	int need_pot = 0;
  	if(map==GL_PIXEL_MAP_I_TO_I || map==GL_PIXEL_MAP_S_TO_S 
  		|| map==GL_PIXEL_MAP_I_TO_R || map==GL_PIXEL_MAP_I_TO_G
  		|| map==GL_PIXEL_MAP_I_TO_B || map==GL_PIXEL_MAP_I_TO_A)
  		need_pot=1;
  	if(need_pot && npot(mapsize)!=mapsize) {
  		errorShim(GL_INVALID_VALUE);
  		return;
  	}
  	int wf = 1;
  	void* array = NULL;
  	int* size = NULL;
  	if(!map_pixelmap(map, &wf, &size, &array))
  		return;
  	noerrorShim();
  	if(wf) {
  		GLfloat *p = (GLfloat*)array;
  		for (int i=0; i<mapsize; i++)
  			p[i] = values[i]>>24;
  	} else {
  		GLuint *p = (GLuint*)array;
  		for (int i=0; i<mapsize; i++)
  			p[i] = values[i];
  	}
  	*size = mapsize;
  }
  
  void gl4es_glPixelMapusv(GLenum map,GLsizei mapsize, const GLushort *values) {
  	if(mapsize>MAX_MAP_SIZE) {
  		errorShim(GL_INVALID_VALUE);
  		return;
  	}
  	int need_pot = 0;
  	if(map==GL_PIXEL_MAP_I_TO_I || map==GL_PIXEL_MAP_S_TO_S 
  		|| map==GL_PIXEL_MAP_I_TO_R || map==GL_PIXEL_MAP_I_TO_G
  		|| map==GL_PIXEL_MAP_I_TO_B || map==GL_PIXEL_MAP_I_TO_A)
  		need_pot=1;
  	if(need_pot && npot(mapsize)!=mapsize) {
  		errorShim(GL_INVALID_VALUE);
  		return;
  	}
  	int wf = 1;
  	void* array = NULL;
  	int* size = NULL;
  	if(!map_pixelmap(map, &wf, &size, &array))
  		return;
  	noerrorShim();
  	if(wf) {
  		GLfloat *p = (GLfloat*)array;
  		for (int i=0; i<mapsize; i++)
  			p[i] = values[i]>>8;
  	} else {
  		GLuint *p = (GLuint*)array;
  		for (int i=0; i<mapsize; i++)
  			p[i] = values[i];
  	}
  	*size = mapsize;
  }
  void gl4es_glGetPixelMapfv(GLenum map, GLfloat *data) {
  	int wf = 1;
  	void* array = NULL;
  	int* size = NULL;
  	if(!map_pixelmap(map, &wf, &size, &array))
  		return;
  	noerrorShim();
  	if(wf) {
  		GLfloat *p = (GLfloat*)array;
  		for (int i=0; i<*size; i++)
  			data[i] = p[i]/255.0f;
  	} else {
  		GLuint *p = (GLuint*)array;
  		for (int i=0; i<*size; i++)
  			data[i] = p[i];
  	}
  }
  void gl4es_glGetPixelMapuiv(GLenum map, GLuint *data) {
  	int wf = 1;
  	void* array = NULL;
  	int* size = NULL;
  	if(!map_pixelmap(map, &wf, &size, &array))
  		return;
  	noerrorShim();
  	if(wf) {
  		GLfloat *p = (GLfloat*)array;
  		for (int i=0; i<*size; i++)
  			data[i] = ((GLuint)(p[i]))<<24;
  	} else {
  		GLuint *p = (GLuint*)array;
  		for (int i=0; i<*size; i++)
  			data[i] = p[i];
  	}
  }
  void gl4es_glGetPixelMapusv(GLenum map, GLushort *data) {
  	int wf = 1;
  	void* array = NULL;
  	int* size = NULL;
  	if(!map_pixelmap(map, &wf, &size, &array))
  		return;
  	noerrorShim();
  	if(wf) {
  		GLfloat *p = (GLfloat*)array;
  		for (int i=0; i<*size; i++)
  			data[i] = ((GLuint)(p[i]))<<8;
  	} else {
  		GLuint *p = (GLuint*)array;
  		for (int i=0; i<*size; i++)
  			data[i] = p[i];
  	}
  }
  
  
  //Direct wrapper
  void glBitmap(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap) AliasExport("gl4es_glBitmap");
  void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) AliasExport("gl4es_glDrawPixels");
  void glRasterPos3f(GLfloat x, GLfloat y, GLfloat z) AliasExport("gl4es_glRasterPos3f");
  void glWindowPos3f(GLfloat x, GLfloat y, GLfloat z) AliasExport("gl4es_glWindowPos3f");
  void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) AliasExport("gl4es_glViewport");
  void glPixelZoom(GLfloat xfactor, GLfloat yfactor) AliasExport("gl4es_glPixelZoom");
  void glPixelTransferf(GLenum pname, GLfloat param) AliasExport("gl4es_glPixelTransferf");
  void glPixelMapfv(GLenum map, GLsizei mapsize, const GLfloat *values) AliasExport("gl4es_glPixelMapfv");
  void glPixelMapuiv(GLenum map,GLsizei mapsize, const GLuint *values) AliasExport("gl4es_glPixelMapuiv");
  void glPixelMapusv(GLenum map,GLsizei mapsize, const GLushort *values) AliasExport("gl4es_glPixelMapusv");
  void glGetPixelMapfv(GLenum map, GLfloat *data) AliasExport("gl4es_glGetPixelMapfv");
  void glGetPixelMapuiv(GLenum map, GLuint *data) AliasExport("gl4es_glGetPixelMapuiv");
  void glGetPixelMapusv(GLenum map, GLushort *data) AliasExport("gl4es_glGetPixelMapusv");
  