/* * $Id$ * * $Date$ * $Revision$ * * (C) 1999 by Hyperion * All rights reserved * * This file is part of the MiniGL library project * See the file Licence.txt for more details * */ #include "sysinc.h" #include #include static char rcsid[] = "$Id$"; #ifndef __PPC__ extern struct ExecBase *SysBase; #endif void tex_FreeTextures(GLcontext context); void tex_SetEnv(GLcontext context, GLenum env); ULONG tex_GLFilter2W3D(GLenum filter); void tex_SetFilter(GLcontext context, GLenum min, GLenum mag); void tex_SetWrap(GLcontext context, GLenum wrap_s, GLenum wrap_t); void RGBA_RGB(GLcontext context, GLubyte *input, UWORD *output, int width, int height); void RGBA_ARGB(GLcontext context, GLubyte *input, UWORD *output, int width, int height); void RGB_RGB(GLcontext context, GLubyte *input, UWORD *output, int width, int height); void RGB_ARGB(GLcontext context, GLubyte *input, UWORD *output, int width, int height); ULONG MGLConvert(GLcontext context, const GLvoid *inputp, UWORD *output, int width, int height, GLenum internalformat, GLenum format); static ULONG Allocated_Size = 0; static ULONG Peak_Size = 0; void *tex_Alloc(ULONG size) { ULONG *x; Allocated_Size += size+4; x=(ULONG *)malloc(size+4); *x = size; if (Allocated_Size > Peak_Size) Peak_Size = Allocated_Size; return x+1; } void tex_Free(void *chunk) { ULONG *mem = (ULONG *)chunk; mem--; Allocated_Size -= *mem; Allocated_Size -= 4; free(mem); } void tex_Statistic(void) { printf("Peak Allocation Size: %ld\n", Peak_Size); } void MGLTexMemStat(GLcontext context, GLint *Current, GLint *Peak) { if (Current) *Current = (GLint)Allocated_Size; if (Peak) *Peak = (GLint)Peak_Size; } void GLDeleteTextures(GLcontext context, GLsizei n, const GLuint *textures) { int i; for (i=0; iw3dTexBuffer[j]) { W3D_FreeTexObj(context->w3dContext, context->w3dTexBuffer[j]); context->w3dTexBuffer[j] = NULL; } if (context->w3dTexMemory[j]) { void *x = context->w3dTexMemory[j]; tex_Free(x); context->w3dTexMemory[j] = NULL; } } } void GLGenTextures(GLcontext context, GLsizei n, GLuint *textures) { int i,j; j = 1; for (i=0; iTexBufferSize) { if (context->w3dTexBuffer[j] == NULL) break; j++; } GLFlagError(context, j == context->TexBufferSize, GL_INVALID_OPERATION); *textures = j; textures++; } } void tex_FreeTextures(GLcontext context) { int i; for (i=0; iTexBufferSize; i++) { if (context->w3dTexBuffer[i]) { W3D_FreeTexObj(context->w3dContext, context->w3dTexBuffer[i]); } if (context->w3dTexMemory[i]) { tex_Free(context->w3dTexMemory[i]); } context->w3dTexBuffer[i] = 0; context->w3dTexMemory[i] = 0; } W3D_FreeAllTexObj(context->w3dContext); } void tex_SetEnv(GLcontext context, GLenum env) { W3D_Texture *tex = context->w3dTexBuffer[context->CurrentBinding]; if (!tex) return; switch(env) { case GL_MODULATE: W3D_SetTexEnv(context->w3dContext, tex, W3D_MODULATE, NULL); break; case GL_DECAL: W3D_SetTexEnv(context->w3dContext, tex, W3D_DECAL, NULL); break; case GL_REPLACE: W3D_SetTexEnv(context->w3dContext, tex, W3D_REPLACE, NULL); break; default: break; } } ULONG tex_GLFilter2W3D(GLenum filter) { switch(filter) { case GL_NEAREST: return W3D_NEAREST; case GL_LINEAR: return W3D_LINEAR; case GL_NEAREST_MIPMAP_NEAREST: return W3D_NEAREST_MIP_NEAREST; case GL_LINEAR_MIPMAP_NEAREST: return W3D_LINEAR_MIP_NEAREST; case GL_NEAREST_MIPMAP_LINEAR: return W3D_NEAREST_MIP_LINEAR; case GL_LINEAR_MIPMAP_LINEAR: return W3D_LINEAR_MIP_LINEAR; } return 0; } void tex_SetFilter(GLcontext context, GLenum min, GLenum mag) { ULONG minf, magf; W3D_Texture *tex = context->w3dTexBuffer[context->CurrentBinding]; if (!tex) return; minf = tex_GLFilter2W3D(min); magf = tex_GLFilter2W3D(mag); W3D_SetFilter(context->w3dContext, tex, minf, magf); } void tex_SetWrap(GLcontext context, GLenum wrap_s, GLenum wrap_t) { ULONG Ws,Wt; W3D_Texture *tex = context->w3dTexBuffer[context->CurrentBinding]; if (!tex) return; if (wrap_s == GL_REPEAT) Ws = W3D_REPEAT; else Ws = W3D_CLAMP; if (wrap_t == GL_REPEAT) Wt = W3D_REPEAT; else Wt = W3D_CLAMP; W3D_SetWrapMode(context->w3dContext, tex, Ws, Wt, NULL); } void GLTexEnvi(GLcontext context, GLenum target, GLenum pname, GLint param) { //LOG(2, glTexEnvi, "%d %d", pname, param); tex_SetEnv(context, param); context->TexEnv = (GLenum)param; } void GLTexParameteri(GLcontext context, GLenum target, GLenum pname, GLint param) { GLenum min, mag; GLenum wraps, wrapt; //LOG(2, glTexParameteri, "%d %d %d", target, pname, param); switch(pname) { case GL_TEXTURE_MIN_FILTER: mag = context->MagFilter; tex_SetFilter(context, mag, (GLenum)param); context->MinFilter = (GLenum)param; break; case GL_TEXTURE_MAG_FILTER: min = context->MinFilter; tex_SetFilter(context, (GLenum)param, min); context->MagFilter = (GLenum)param; break; case GL_TEXTURE_WRAP_S: wrapt = context->WrapT; tex_SetWrap(context, (GLenum)param, wrapt); context->WrapS = (GLenum)param; break; case GL_TEXTURE_WRAP_T: wraps = context->WrapS; tex_SetWrap(context, wraps, (GLenum)param); context->WrapT = (GLenum)param; break; default: GLFlagError(context, 1, GL_INVALID_ENUM); } } void GLPixelStorei(GLcontext context, GLenum pname, GLint param) { switch(pname) { case GL_PACK_ALIGNMENT: context->PackAlign = param; break; case GL_UNPACK_ALIGNMENT: context->UnpackAlign = param; break; default: // Others here break; } } void GLBindTexture(GLcontext context, GLenum target, GLuint texture) { //LOG(2, glBindTexture, "%d %d", target, texture); GLFlagError(context, target != GL_TEXTURE_2D, GL_INVALID_ENUM); context->CurrentBinding = texture; if (context->w3dTexBuffer[context->CurrentBinding] == NULL) { // Set to default for unbound objects context->TexEnv = GL_MODULATE; context->MinFilter = GL_NEAREST; context->MagFilter = GL_NEAREST; context->WrapS = GL_REPEAT; context->WrapT = GL_REPEAT; } } /* ** There are two possible output formats: ** - RGB ** - RGBA ** ** There are two possible input formats: ** - RGB ** - RGBA ** ** Thus there must be four conversion routines, since the ** routine must be able to add or remove the alpha component ** ** The following set of routines assumes that input is always given as ** a stream of GL_UNSIGNED_BYTE with either three or four components. ** ** ROOM FOR IMPROVMENT ** These routines assume way too much to be considered anything else but ** special cases. The whole texture stuff should be reworked to include ** possible convertion routines for all kinds of textures. This could, of ** course, be left to Warp3D, but this would mean there has to be two sets ** of textures in memory, which is clearly too much. Perhaps we should ** change the behaviour of Warp3D in this point - room for discussion. */ #define CORRECT_ALIGN \ if ((int)input % context->PackAlign) \ { \ input += context->PackAlign - ((int)input % context->PackAlign);\ } \ #define ARGBFORM(a,r,g,b) \ (UWORD)(((( a & 0xf0 ) << 8) \ |(( r & 0xf0 ) << 4) \ |(( g & 0xf0 ) ) \ |(( b & 0xf0 ) >> 4))) #define RGBFORM(r,g,b) \ (UWORD)(0x8000 | ((r & 0xF8) << 7) \ | ((g & 0xF8) << 2) \ | ((b & 0xF8) >> 3)) #define REDBYTE(rgb) (((UWORD)rgb & 0x7C00) >> 7) #define GREENBYTE(rgb) (((UWORD)rgb & 0x03E0) >> 2) #define BLUEBYTE(rgb) (((UWORD)rgb & 0x001F) << 3) #define RGB_GET(i) ((UBYTE *)(context->PaletteData)+3*i) #define ARGB_GET(i) ((UBYTE *)(context->PaletteData)+4*i) /* ** This function converts a non-alpha texture buffer into a ** alpha'ed texture buffer. This is used in case additive blending ** was selected but the texture does not have alpha, and additive blending ** is not */ void tex_AddAlpha(UWORD *output, int width, int height) { int size = width*height; int r,g,b,a; UWORD x; while (size) { x=*output; r = REDBYTE(x); g = GREENBYTE(x); b = BLUEBYTE(x); /*a=(r+g+b)/3;*/ a=(r>g)?r:g; a=(a>b)?a:b; // These should emulate additive blending, so ensure it's in some appropriate range /*/ if (a > 240) { a = 240; }*/ a *= 0.8; *output = ARGBFORM(a,r,g,b); output++; size--; } } /* ** Convert the currently bound texture to a format that has an alpha channel. ** Also sets the texture parameters according to the current settings. */ void tex_ConvertTexture(GLcontext context) { W3D_Texture *newtex; W3D_Texture *oldtex = context->w3dTexBuffer[context->CurrentBinding]; UWORD *output = (UWORD *)context->w3dTexMemory[context->CurrentBinding]; struct TagItem AllocTags[20]; if (!oldtex) return; if (oldtex->texfmtsrc == context->w3dAlphaFormat) return; tex_AddAlpha(output, oldtex->texwidth, oldtex->texheight); AllocTags[0].ti_Tag = W3D_ATO_IMAGE; AllocTags[0].ti_Data = (ULONG)context->w3dTexMemory[context->CurrentBinding]; AllocTags[1].ti_Tag = W3D_ATO_FORMAT; AllocTags[1].ti_Data = context->w3dAlphaFormat; AllocTags[2].ti_Tag = W3D_ATO_WIDTH; AllocTags[2].ti_Data = oldtex->texwidth; AllocTags[3].ti_Tag = W3D_ATO_HEIGHT; AllocTags[3].ti_Data = oldtex->texheight; AllocTags[4].ti_Tag = TAG_DONE; AllocTags[4].ti_Data = 0; newtex = W3D_AllocTexObj(context->w3dContext, NULL, AllocTags); if (!newtex) return; W3D_FreeTexObj(context->w3dContext, oldtex); context->w3dTexBuffer[context->CurrentBinding] = newtex; tex_SetEnv(context, context->TexEnv); tex_SetFilter(context, context->MinFilter, context->MagFilter); tex_SetWrap(context, context->WrapS, context->WrapT); } void RGBA_RGB(GLcontext context, GLubyte *input, UWORD *output, int width, int height) { int i,j; UBYTE r,g,b,a; for (i=0; iPaletteFormat == GL_RGB) { for (i=0; iPaletteFormat == GL_RGB) { for (i=0; iw3dFormat; case GL_RGBA: RGBA_RGB(context, (GLubyte *)input, output, width, height); return context->w3dFormat; case GL_COLOR_INDEX: INDEX_RGB(context, (GLubyte *)input, output, width, height); return context->w3dFormat; } break; case 4: case GL_RGBA: switch(format) // format of the pixel (input) data { case GL_RGB: RGB_ARGB(context, (GLubyte *)input, output, width, height); return context->w3dAlphaFormat; case GL_RGBA: RGBA_ARGB(context, (GLubyte *)input, output, width, height); return context->w3dAlphaFormat; case GL_COLOR_INDEX: INDEX_ARGB(context, (GLubyte *)input, output, width, height); return context->w3dAlphaFormat; } break; } } void GLTexImage2DNoMIP(GLcontext context, GLenum gltarget, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); void GLTexImage2D(GLcontext context, GLenum gltarget, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { int current = context->CurrentBinding; ULONG w,h; ULONG targetsize; ULONG error; UBYTE *target; int i,iw,ih; void *miparray[16]; ULONG useFormat; struct TagItem AllocTags[20]; if (context->NoMipMapping == GL_TRUE) { GLTexImage2DNoMIP(context, gltarget, level, internalformat, width, height, border, format, type, pixels); return; } //LOG(3, glTexImage2D, "target = %d level=%d, size=%d×%d", gltarget, level, width, height); GLFlagError(context, type != GL_UNSIGNED_BYTE, GL_INVALID_OPERATION); GLFlagError(context, gltarget != GL_TEXTURE_2D, GL_INVALID_ENUM); /* ** We will use width and height only when the mipmap level ** is really 0. Otherwise, we need to upscale the values ** according to the level. ** ** Note this will most likely be difficult with non-square ** textures, as the first side to reach one will remain ** there. For example, consider the sequence ** 8x4, 4x2, 2x1, 1x1 ** 0 1 2 3 ** If the 1x1 mipmap is given, upscaling will yield 8x8, not 8x4 */ w=(ULONG)width; h=(ULONG)height; if (level) { int i=level; while (i) { w*=2; h*=2; i--; } } if (context->w3dTexBuffer[current] == NULL) { /* ** Create a new texture object ** Get the memory */ targetsize = (w * h * context->w3dBytesPerTexel * 4) / 3; if (context->w3dTexMemory[current]) tex_Free(context->w3dTexMemory[current]); context->w3dTexMemory[current] = (GLubyte *)tex_Alloc(targetsize); if (!context->w3dTexMemory[current]) return; } /* ** Find the starting address for the given mipmap level in the ** texture memory area */ target = context->w3dTexMemory[current]; i = level; iw = w; ih=h; while (i) { target += iw * ih * context->w3dBytesPerTexel; i -- ; if (iw>1) iw/=2; if (ih>1) ih/=2; } /* ** Convert the data to the target address */ useFormat = MGLConvert(context, pixels, (UWORD *)target, width, height, internalformat, format); /* ** Create a new W3D_Texture if none was present, using the converted ** data. ** Otherwise, call W3D_UpdateTexImage */ if (context->w3dTexBuffer[current] == NULL) { i=0; iw=w; ih=h; target = context->w3dTexMemory[current]; while (1) { miparray[i++] = target; if (iw == 1 && ih == 1) break; target += iw * ih * context->w3dBytesPerTexel; if (iw > 1) iw /= 2; if (ih > 1) ih /= 2; } AllocTags[0].ti_Tag = W3D_ATO_IMAGE; AllocTags[0].ti_Data = (ULONG)context->w3dTexMemory[current]; AllocTags[1].ti_Tag = W3D_ATO_FORMAT; AllocTags[1].ti_Data = useFormat; AllocTags[2].ti_Tag = W3D_ATO_WIDTH; AllocTags[2].ti_Data = w; AllocTags[3].ti_Tag = W3D_ATO_HEIGHT; AllocTags[3].ti_Data = h; AllocTags[4].ti_Tag = W3D_ATO_MIPMAP; AllocTags[4].ti_Data = 0; AllocTags[5].ti_Tag = W3D_ATO_MIPMAPPTRS; AllocTags[5].ti_Data = (ULONG)miparray; AllocTags[6].ti_Tag = TAG_DONE; AllocTags[6].ti_Data = 0; context->w3dTexBuffer[current] = W3D_AllocTexObj(context->w3dContext, &error, AllocTags); if (context->w3dTexBuffer[current] == NULL || error != W3D_SUCCESS) return; /* ** Set the appropriate wrap modes, texture env, and filters */ tex_SetWrap(context, context->WrapS, context->WrapT); tex_SetFilter(context, context->MinFilter, context->MagFilter); tex_SetEnv(context, context->TexEnv); } else { W3D_UpdateTexImage(context->w3dContext, context->w3dTexBuffer[current], target, level, NULL); } } void GLTexImage2DNoMIP(GLcontext context, GLenum gltarget, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { int current = context->CurrentBinding; ULONG w,h; ULONG targetsize; ULONG error; UBYTE *target; ULONG useFormat; static struct TagItem AllocTags[20]; //LOG(3, glTexImage2DNoMIP, "target = %d level=%d, size=%d×%d", gltarget, level, width, height); GLFlagError(context, type != GL_UNSIGNED_BYTE, GL_INVALID_OPERATION); GLFlagError(context, gltarget != GL_TEXTURE_2D, GL_INVALID_ENUM); if (level != 0) return; w=(ULONG)width; h=(ULONG)height; if (context->w3dTexBuffer[current] == NULL) { /* ** Create a new texture object ** Get the memory */ targetsize = (w * h * context->w3dBytesPerTexel); if (context->w3dTexMemory[current]) tex_Free(context->w3dTexMemory[current]); context->w3dTexMemory[current] = (GLubyte *)tex_Alloc(targetsize); if (!context->w3dTexMemory[current]) return; } target = context->w3dTexMemory[current]; /* ** Convert the data to the target address */ useFormat = MGLConvert(context, pixels, (UWORD *)target, width, height, internalformat, format); /* ** Create a new W3D_Texture if none was present, using the converted ** data. ** Otherwise, call W3D_UpdateTexImage */ if (context->w3dTexBuffer[current] == NULL) { W3D_Texture *tex; AllocTags[0].ti_Tag = W3D_ATO_IMAGE; AllocTags[0].ti_Data = (ULONG)context->w3dTexMemory[current]; AllocTags[1].ti_Tag = W3D_ATO_FORMAT; AllocTags[1].ti_Data = useFormat; AllocTags[2].ti_Tag = W3D_ATO_WIDTH; AllocTags[2].ti_Data = w; AllocTags[3].ti_Tag = W3D_ATO_HEIGHT; AllocTags[3].ti_Data = h; AllocTags[4].ti_Tag = TAG_DONE; AllocTags[4].ti_Data = 0; tex = W3D_AllocTexObj(context->w3dContext, &error, AllocTags); if (tex == NULL || error != W3D_SUCCESS) return; context->w3dTexBuffer[current] = tex; /* ** Set the appropriate wrap modes, texture env, and filters */ tex_SetWrap(context, context->WrapS, context->WrapT); tex_SetFilter(context, context->MinFilter, context->MagFilter); tex_SetEnv(context, context->TexEnv); } else { W3D_UpdateTexImage(context->w3dContext, context->w3dTexBuffer[current], context->w3dTexMemory[current], 0, NULL); } } inline void tex_UpdateScanlineAlpha(UWORD *start, UBYTE *pixels, int numpixels) { int i; UBYTE r,g,b,a; for (i=0; iCurrentBinding; if (context->NoMipMapping == GL_TRUE) { GLTexSubImage2DNoMIP(context, target, level, xoffset, yoffset, width, height, format, type, (void *)pixels); return; } GLFlagError(context, target!=GL_TEXTURE_2D, GL_INVALID_ENUM); GLFlagError(context, context->w3dTexBuffer[current] == NULL, GL_INVALID_OPERATION); // GLFlagError(context, 1, GL_INVALID_OPERATION); } void GLTexSubImage2DNoMIP(GLcontext context, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) { UBYTE *where; UBYTE *from = pixels; int current = context->CurrentBinding; int linelength; int sourcelength, sourceunit; int i; GLFlagError(context, target!=GL_TEXTURE_2D, GL_INVALID_ENUM); GLFlagError(context, context->w3dTexBuffer[context->CurrentBinding] == NULL, GL_INVALID_OPERATION); GLFlagError(context, type != GL_UNSIGNED_BYTE, GL_INVALID_ENUM); switch(format) { case GL_LUMINANCE: sourceunit = 1; break; case GL_RGBA: case 4: sourceunit = 4; break; case GL_RGB: case 3: sourceunit = 3; break; } linelength = context->w3dTexBuffer[current]->texwidth * context->w3dBytesPerTexel; sourcelength = width * sourceunit; where = (UBYTE *)context->w3dTexMemory[current] + linelength * yoffset + context->w3dBytesPerTexel * xoffset; if (context->w3dTexBuffer[current]->texfmtsrc == context->w3dFormat) { for (i=0; iw3dTexBuffer[current]->texfmtsrc == context->w3dAlphaFormat) { for (i=0; iw3dContext, context->w3dTexBuffer[current], context->w3dTexMemory[current], 0, NULL); } void GLTexGeni(GLcontext context, GLenum coord, GLenum mode, GLenum map) { // GLFlagError(context, 1, GL_INVALID_OPERATION); } void GLColorTable(GLcontext context, GLenum target, GLenum internalformat, GLint width, GLenum format, GLenum type, GLvoid *data) { int i; GLubyte *palette; GLubyte *where; GLubyte a,r,g,b; GLFlagError(context, width>256, GL_INVALID_VALUE); GLFlagError(context, target!=GL_COLOR_TABLE, GL_INVALID_OPERATION); palette = (GLubyte *)data; where = (GLubyte *)context->PaletteData; GLFlagError(context, where == NULL, GL_INVALID_OPERATION); switch(internalformat) { case 4: case GL_RGBA: // convert to argb from... switch(format) { case GL_RGB: // ...RGB, ignoring alpha for (i=0; iPaletteFormat = format; context->PaletteSize = width; }