//************************************************************** //* OpenGLide - Glide to OpenGL Wrapper //* http://openglide.sourceforge.net //* //* implementation of the PGTexture class //* //* OpenGLide is OpenSource under LGPL license //* Originaly made by Fabio Barros //* Modified by Paul for Glidos (http://www.glidos.net) //* Mac version and additional features by Jens-Olaf Hemprich //************************************************************** #include "FormatConversion.h" #include "Glide.h" #include "GlideApplication.h" #include "GlideSettings.h" #include "Glextensions.h" #include "GLRender.h" #include "OGLTables.h" #include "PGTexture.h" void PGTexture::genPaletteMipmaps( FxU32 width, FxU32 height, const FxU8 *data ) { FxU8 buf[128 * 128]; FxU32 mmwidth = width; FxU32 mmheight = height; FxU32 lod = 0; FxU32 skip = 1; while ((mmwidth > 1) || (mmheight > 1)) { mmwidth = mmwidth > 1 ? mmwidth / 2 : 1; mmheight = mmheight > 1 ? mmheight / 2 : 1; lod += 1; skip *= 2; for (FxU32 y = 0; y < mmheight; y++) { const FxU8* in = data + width * y * skip; FxU8* out = buf + mmwidth * y; for (FxU32 x = 0; x < mmwidth; x++) { out[x] = in[x * skip]; } } glTexImage2D( GL_TEXTURE_2D, lod, GL_COLOR_INDEX8_EXT, mmwidth, mmheight, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, buf ); } } PGTexture::PGTexture(int mem_size) { m_db = new TexDB( mem_size ); m_palette_dirty = true; m_valid = false; m_chromakey_mode = GR_CHROMAKEY_DISABLE; m_tex_memory_size = mem_size; m_memory = (FxU8*) AllocBuffer(mem_size, sizeof(FxU8)); // This is initialized before the rendering context is available and before // the list of OpenGL extensions can be checked against APPLE_client_storage. // As a result the texture buffer is allocated bases on the user setting. // And it might be legal to download textures before grWinOpen() so // using the user config setting is the best we can do if (UserConfig.APPLE_client_storage) { // use client storage to avoid OpenGL-internal copy // OpenGL may still make a copy if the color format isn't supported natively // by the graphics card but all xto8888 conversions should benefit from this // @todo: Not true for OSX, but for now we stay compatible to native OS9 // Alloc 4-times the size of the Glide buffer for texture storage m_textureCache = reinterpret_cast(AllocBuffer(m_tex_memory_size, sizeof(FxU32))); // No memory is wasted here because we save the internal OpenGL copy } else { m_textureCache = NULL; } // Needed for subtexturing and internal conversions m_tex_temp = reinterpret_cast(AllocBuffer(256 * 256, sizeof(FxU32))); m_ncc_select = GR_NCCTABLE_NCC0; #ifdef OGL_DEBUG Num_565_Tex = 0; Num_565_Chromakey_Tex = 0; Num_1555_Tex = 0; Num_1555_Chromakey_Tex = 0; Num_4444_Tex = 0; Num_4444_Chromakey_Tex = 0; Num_332_Tex = 0; Num_8332_Tex = 0; Num_Alpha_Tex = 0; Num_AlphaIntensity88_Tex = 0; Num_AlphaIntensity44_Tex = 0; Num_AlphaPalette_Tex = 0; Num_Palette_Tex = 0; Num_Palette_Chromakey_Tex = 0; Num_Intensity_Tex = 0; Num_YIQ_Tex = 0; Num_AYIQ_Tex = 0; Num_Other_Tex = 0; #endif } PGTexture::~PGTexture( void ) { FreeBuffer(m_memory); FreeBuffer(m_tex_temp); if (m_textureCache) FreeBuffer(m_textureCache); delete m_db; } void PGTexture::DownloadMipMap( FxU32 startAddress, FxU32 evenOdd, const GrTexInfo *info ) { const FxU32 mip_size = MipMapMemRequired(info->smallLod, info->aspectRatio, info->format); const FxU32 mip_offset = startAddress + TextureMemRequired(evenOdd, info); if ( mip_offset <= m_tex_memory_size ) { memcpy( m_memory + mip_offset - mip_size, info->data, mip_size ); } // Any texture based on memory crossing this range // is now out of date m_db->WipeRange( startAddress, mip_offset, 0 ); #ifdef OGL_DEBUG if ( info->smallLod == info->largeLod ) { switch ( info->format ) { case GR_TEXFMT_RGB_332: Num_332_Tex++; break; case GR_TEXFMT_YIQ_422: Num_YIQ_Tex++; break; case GR_TEXFMT_ALPHA_8: Num_Alpha_Tex++; break; case GR_TEXFMT_INTENSITY_8: Num_Intensity_Tex++; break; case GR_TEXFMT_ALPHA_INTENSITY_44: Num_AlphaIntensity44_Tex++; break; case GR_TEXFMT_P_8: m_chromakey_mode ? Num_Palette_Chromakey_Tex++ : Num_Palette_Tex++; break; case GR_TEXFMT_ARGB_8332: Num_8332_Tex++; break; case GR_TEXFMT_AYIQ_8422: Num_AYIQ_Tex++; break; case GR_TEXFMT_RGB_565: m_chromakey_mode ? Num_565_Chromakey_Tex++ : Num_565_Tex++; break; case GR_TEXFMT_ARGB_1555: m_chromakey_mode ? Num_1555_Chromakey_Tex++ : Num_1555_Tex++; break; case GR_TEXFMT_ARGB_4444: m_chromakey_mode ? Num_4444_Chromakey_Tex++ : Num_4444_Tex++; break; case GR_TEXFMT_ALPHA_INTENSITY_88: Num_AlphaIntensity88_Tex++; break; case GR_TEXFMT_AP_88: Num_AlphaPalette_Tex++; break; case GR_TEXFMT_RSVD0: case GR_TEXFMT_RSVD1: case GR_TEXFMT_RSVD2: default: Num_Other_Tex++; break; } } #endif } void PGTexture::DownloadMipMapLevel(FxU32 startAddress, GrLOD_t thisLod, GrLOD_t largeLod, GrAspectRatio_t aspectRatio, GrTextureFormat_t format, FxU32 evenOdd, void *data) { GrTexInfo info; info.smallLod = thisLod; info.largeLod = largeLod; info.aspectRatio = aspectRatio; info.format = format; info.data = data; DownloadMipMap(startAddress, evenOdd, &info); } void PGTexture::DownloadMipMapLevelPartial(FxU32 startAddress, GrLOD_t thisLod, GrLOD_t largeLod, GrAspectRatio_t aspectRatio, GrTextureFormat_t format, FxU32 evenOdd, void *data, int start, int end) { GrTexInfo info; info.smallLod = thisLod; info.largeLod = largeLod; info.aspectRatio = aspectRatio; info.format = format; info.data = data; const FxU32 mip_size = MipMapMemRequired(info.smallLod, info.aspectRatio, info.format); // instead of forwarding evenOdd, GR_MIPMAPLEVELMASK_BOTH might also be appropriate const FxU32 mip_offset = startAddress + TextureMemRequired(evenOdd, &info); // Choose the rows to copy (only largeLod is supported) TexValues texVals; GetTexValues(&texVals); const FxU32 texel_size = info.format >= GR_TEXFMT_16BIT ? 2 : 1; const FxU32 startRow = texVals.width * texel_size * start; const FxU32 rows = texVals.width * texel_size * (end - start); if ( mip_offset <= m_tex_memory_size ) { memcpy(m_memory + mip_offset - mip_size + startRow, info.data, rows); } // Any texture based on memory crossing this range // is now out of date m_db->WipeRange(startAddress, mip_offset, 0); } void PGTexture::Source(FxU32 startAddress, FxU32 evenOdd, const GrTexInfo *info) { m_startAddress = startAddress; m_evenOdd = evenOdd; m_info = *info; m_wAspect = texAspects[ info->aspectRatio ].w; m_hAspect = texAspects[ info->aspectRatio ].h; m_valid = ( ( startAddress + TextureMemRequired( evenOdd, info ) ) <= m_tex_memory_size ); } void PGTexture::DownloadMipmapsToOpenGL(GLint compnum, GLint compformat, GLenum comptype, const void* texdata, TexValues& t, bool build_mipmaps) { glReportErrors("DownloadMipmapsToOpenGL"); glTexImage2D(GL_TEXTURE_2D, t.lod, compnum, t.width, t.height, 0, compformat, comptype, texdata); if (InternalConfig.Mipmapping && build_mipmaps) { gluBuild2DMipmaps(GL_TEXTURE_2D, compnum, t.width, t.height, compformat, comptype, texdata); } glReportError(); } inline void ncc_convert_FxI9_2_FxI16(FxI16* ncc_xRGB) { if (*ncc_xRGB & 0x100) *ncc_xRGB |= 0xff00; } void PGTexture::DownloadTable( GrTexTable_t type, const FxU32 *data, int first, int count ) { if ( type == GR_TEXTABLE_PALETTE ) { for ( int i = count - 1; i >= 0; i-- ) { // Convert palette entry from ARGB to RGBA const FxU32& src = data[ i ]; m_palette[ first + i ] = ((src & 0x00ffffff) << 8) | // RGB ((src & 0xff000000) >> 24); // A } m_palette_dirty = true; } else { // GR_TEXTABLE_NCC0 or GR_TEXTABLE_NCC1 GuNccTable *ncc = &(m_ncc[ type ]); memcpy( ncc, data, sizeof( GuNccTable ) ); for (int i = 0; i < 4; i++ ) { ncc_convert_FxI9_2_FxI16(&ncc->iRGB[ i ][ 0 ]); ncc_convert_FxI9_2_FxI16(&ncc->iRGB[ i ][ 1 ]); ncc_convert_FxI9_2_FxI16(&ncc->iRGB[ i ][ 2 ]); ncc_convert_FxI9_2_FxI16(&ncc->qRGB[ i ][ 0 ]); ncc_convert_FxI9_2_FxI16(&ncc->qRGB[ i ][ 1 ]); ncc_convert_FxI9_2_FxI16(&ncc->qRGB[ i ][ 2 ]); } } } bool PGTexture::MakeReady(TTextureStruct* tex_coords, unsigned long number_of_triangles) { glReportErrors("PGTexture::MakeReady"); if( ! m_valid ) { return false; } FxU32 test_hash = 0; FxU32 wipe_hash = 0; bool palette_changed = false; bool use_mipmap_ext = InternalConfig.Mipmapping && InternalConfig.EXT_SGIS_generate_mipmap; bool use_two_textures = false; bool* pal_change_ptr = NULL; const FxU8* data = m_memory + m_startAddress; FxU32 size = TextureMemRequired(m_evenOdd, &m_info); TexValues texVals; GetTexValues(&texVals); switch (m_info.format) { case GR_TEXFMT_P_8: ApplyKeyToPalette(); // todo: Would work with anisotropic filtering if chromakeying was disabled if (InternalConfig.EXT_paletted_texture && InternalConfig.AnisotropylLevel < 2) { // // OpenGL's mipmap generation doesn't seem // to handle paletted textures. // use_mipmap_ext = false; pal_change_ptr = &palette_changed; } else { wipe_hash = m_palette_hash; } test_hash = m_palette_hash; break; case GR_TEXFMT_AP_88: ApplyKeyToPalette(); if (InternalConfig.EXT_paletted_texture && InternalConfig.ARB_multitexture) { use_mipmap_ext = false; pal_change_ptr = &palette_changed; // Two textures can only be used with the simple coloralpha engine // and if there are enough texture units left to support fog use_two_textures = (OpenGL.ColorAlphaUnit2 == 0) && (OpenGL.FogTextureUnit >= GL_TEXTURE1_ARB); } else { wipe_hash = m_palette_hash; } test_hash = m_palette_hash; break; } // Look if we already have an OpenGL texture to match this TexDB::Record* texture_record; SubTexCoord_t subtexcoords_struct; const bool generate_sub_textures = InternalConfig.GenerateSubTextures && OpenGL.SClampMode != GL_REPEAT && OpenGL.TClampMode != GL_REPEAT; SubTexCoord_t* subtexcoords = generate_sub_textures ? &subtexcoords_struct : NULL; if (subtexcoords) { assert(number_of_triangles == 1); GLfloat min; GLfloat max; const unsigned int grid_snap = 0xffffffff - (InternalConfig.GenerateSubTextures -1); // Get Glide pixel coordinates (prepared in RenderAddTriangle()) const GLfloat as = tex_coords->as; const GLfloat bs = tex_coords->bs; const GLfloat cs = tex_coords->cs; max = as; min = bs; if (max < min) { max = bs; min = as; } if (max < cs) { max = cs; } else if (min > cs) { min = cs; } subtexcoords->smin = min; subtexcoords->smax = max; subtexcoords->left = min; // snap to grid to allow using the left/top values as keys for caching subtexcoords->left &= grid_snap; // snap to 8 pixel grid subtexcoords->smin = subtexcoords->left; // same for t const GLfloat at = tex_coords->at; const GLfloat bt = tex_coords->bt; const GLfloat ct = tex_coords->ct; max = at; min = bt; if (max < min) { max = bt; min = at; } if (max < ct) { max = ct; } else if (min > ct) { min = ct; } subtexcoords->tmin = min; subtexcoords->tmax = max; subtexcoords->top = min; subtexcoords->top &= grid_snap; // snap to 8 pixel grid subtexcoords->tmin = subtexcoords->top; // Calculate width and height of the texture subtexcoords->width = subtexcoords->smax - subtexcoords->left; subtexcoords->height = subtexcoords->tmax - subtexcoords->top; subtexcoords->texImageWidth = max(InternalConfig.GenerateSubTextures, PowerOfTwoCeiling(subtexcoords->width)); subtexcoords->texImageHeight = max(InternalConfig.GenerateSubTextures, PowerOfTwoCeiling(subtexcoords->height)); texture_record = texture_record = m_db->Find(m_startAddress, &m_info, test_hash, pal_change_ptr, subtexcoords); // At this point the subtexcoord texsize might have been updated (by Find()) // Calculate texture coordinates for rendering the subtexture // 1. scale a->s to texture pixel s-space: as = a->tmuvtx[ 0 ].sow / atmuoow; // 2. shift as to origin: as_s = as - subtexcoords.smin; // 3. scale as_s to opengl texture space as_s *= wAspect / subtexcoords.width; // 4. scale as_s to opengl subtexture space: * subtexcoords.width / subtexcoords.texImageWidth // 5. undo dividing by atmoow and scale with maxoow to fit texture edge into object edges // tex_coords->aoow is actually maxoow * aoow what is what we want const GLfloat s_factor = 1.0f / subtexcoords->texImageWidth; tex_coords->as = (as - subtexcoords->smin) * s_factor * tex_coords->aoow; tex_coords->bs = (bs - subtexcoords->smin) * s_factor * tex_coords->boow; tex_coords->cs = (cs - subtexcoords->smin) * s_factor * tex_coords->coow; const GLfloat t_factor = 1.0f / subtexcoords->texImageHeight; tex_coords->at = (at - subtexcoords->tmin) * t_factor * tex_coords->aoow; tex_coords->bt = (bt - subtexcoords->tmin) * t_factor * tex_coords->boow; tex_coords->ct = (ct - subtexcoords->tmin) * t_factor * tex_coords->coow; #ifdef OGL_UTEX GlideMsg("glTexCoords(%g,%g)-(%g,%g)-(%g,%g), ", tex_coords->as, tex_coords->at, tex_coords->bs, tex_coords->bt, tex_coords->cs, tex_coords->ct); GlideMsg("Subtex: min/max(%g,%g)-(%g,%g),", subtexcoords->smin, subtexcoords->smax, subtexcoords->tmin, subtexcoords->tmax); GlideMsg(" w/h(%g,%g), texture(%d,%d)-(%d,%d)\n", subtexcoords->width, subtexcoords->height, subtexcoords->left, subtexcoords->top, subtexcoords->texImageWidth, subtexcoords->texImageHeight); #endif } else { texture_record = texture_record = m_db->Find(m_startAddress, &m_info, test_hash, pal_change_ptr, NULL); } #ifdef OGL_DEBUG if (InternalConfig.EXT_paletted_texture == false && palette_changed) { GlideMsg("Performance Warning: PGTexture palette change for 0x%x causes texture regeneration\n", m_startAddress); } #endif const bool enable_coloralpha_texture_unit_1 = OpenGL.ColorAlphaUnit2 == NULL || OpenGL.ColorAlphaUnitColorEnabledState[0] || OpenGL.ColorAlphaUnitAlphaEnabledState[0]; const bool enable_coloralpha_texture_unit_2 = OpenGL.ColorAlphaUnitColorEnabledState[1] || OpenGL.ColorAlphaUnitAlphaEnabledState[1]; if (texture_record) { if (palette_changed && InternalConfig.EXT_paletted_texture) { glColorTable(GL_TEXTURE_2D, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, m_palette); glReportError(); } GLint texnum2; if (OpenGL.ColorAlphaUnit2 && enable_coloralpha_texture_unit_2) { texnum2 = texture_record->texNum; } else if (use_two_textures) { texnum2 = texture_record->tex2Num; } else { texnum2 = 0; } if (texnum2) { glActiveTextureARB(OpenGL.ColorAlphaUnit1 + 1); glBindTexture(GL_TEXTURE_2D, texnum2); // Only needed if the two textures are different if (use_two_textures) { if (texture_record->SClampMode != OpenGL.SClampMode) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture_record->SClampMode); } if (texture_record->TClampMode != OpenGL.TClampMode) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture_record->TClampMode); } if (InternalConfig.TextureSmoothing == false) { if (texture_record->MinFilterMode != OpenGL.MinFilterMode) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_record->MinFilterMode); } if (texture_record->MagFilterMode != OpenGL.MagFilterMode) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texture_record->MagFilterMode); } } } glActiveTextureARB(OpenGL.ColorAlphaUnit1); glReportError(); } if (enable_coloralpha_texture_unit_1) { glBindTexture(GL_TEXTURE_2D, texture_record->texNum); if (texture_record->SClampMode != OpenGL.SClampMode) { texture_record->SClampMode = OpenGL.SClampMode; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, OpenGL.SClampMode); } if (texture_record->TClampMode != OpenGL.TClampMode) { texture_record->TClampMode = OpenGL.TClampMode; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, OpenGL.TClampMode); } if (InternalConfig.TextureSmoothing == false) { if (texture_record->MinFilterMode != OpenGL.MinFilterMode) { texture_record->MinFilterMode = OpenGL.MinFilterMode; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, OpenGL.MinFilterMode); } if (texture_record->MagFilterMode != OpenGL.MagFilterMode) { texture_record->MagFilterMode = OpenGL.MagFilterMode; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, OpenGL.MagFilterMode); } } glReportError(); } } else { // Any existing textures crossing this memory range // is unlikely to be used, so remove the OpenGL version // of them if (wipe_hash) { // If we don't have GL_PALETTE_EXT, the texture must be rebuilt // after the palette has changed. Any existing textures must be deleted. // This is because Find() above was looking for a texture with // the current palette hash and might not have found it, although // a texture with a different palette exists. // @todo: Find doesn't return handles to TexDB records, so we have // to search again every time a paletted texture is used -> optimise. if (subtexcoords) { // Fall back to non-subtexture behaviour and just // wipe the range if the texture doesn't exist at all if (m_db->Find(m_startAddress, &m_info, test_hash, pal_change_ptr, NULL) == NULL) { m_db->WipeRange(m_startAddress, m_startAddress + size, wipe_hash); } } } else { // Nothing needs to be done because if it's not a paletted texture, // the range has been wiped while downloading the texture // m_db->WipeRange( m_startAddress, m_startAddress + size, wipe_hash); } // Add the new texture to the data base TexDB::TextureMode texturemode = subtexcoords ? (use_two_textures ? TexDB::SubTextureTwo : TexDB::SubTextureOne) : (use_two_textures ? TexDB::Two : TexDB::One); texture_record = m_db->Add(m_startAddress, m_startAddress + size, &m_info, test_hash, texturemode, subtexcoords); texture_record->SClampMode = OpenGL.SClampMode; texture_record->TClampMode = OpenGL.TClampMode; texture_record->MinFilterMode = OpenGL.MinFilterMode; texture_record->MagFilterMode = OpenGL.MagFilterMode; glBindTexture( GL_TEXTURE_2D, texture_record->texNum); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, OpenGL.SClampMode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, OpenGL.TClampMode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, OpenGL.MinFilterMode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, OpenGL.MagFilterMode); const unsigned long anisotropy_level = InternalConfig.AnisotropylLevel; const bool enable_mipmaps = InternalConfig.Mipmapping; // Write only if enabled in config if(InternalConfig.AnisotropylLevel > 1) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy_level); } if(use_mipmap_ext) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, enable_mipmaps); } GLint texnum2; if (OpenGL.ColorAlphaUnit2 && enable_coloralpha_texture_unit_2) { texnum2 = texture_record->texNum; } else if (use_two_textures) { texnum2 = texture_record->tex2Num; } else { texnum2 = 0; } if (texnum2) { glActiveTextureARB(OpenGL.ColorAlphaUnit1 + 1); glBindTexture(GL_TEXTURE_2D, texnum2); // Only needed if the two textures are different if (use_two_textures) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, OpenGL.SClampMode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, OpenGL.TClampMode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, OpenGL.MinFilterMode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, OpenGL.MagFilterMode); if(InternalConfig.AnisotropylLevel > 1) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy_level); } if (use_mipmap_ext) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, enable_mipmaps); } } glActiveTextureARB(OpenGL.ColorAlphaUnit1); } glReportError(); if (subtexcoords) { // APPLE_client_storage doesn't explicitly forbid to adjust pixel unpack :^) glPixelStorei(GL_UNPACK_SKIP_PIXELS, subtexcoords->left); glPixelStorei(GL_UNPACK_SKIP_ROWS, subtexcoords->top); glPixelStorei(GL_UNPACK_ROW_LENGTH, texVals.width); // Must update texVals.width, texVals.height to new texture size texVals.width = subtexcoords->texImageWidth; texVals.height = subtexcoords->texImageHeight; } FxU32* texBuffer; // Which buffer if (m_textureCache) { texBuffer = &m_textureCache[m_startAddress]; } else { texBuffer = m_tex_temp; } // Convert Glide texture data to format understood by OpenGL switch (m_info.format) { case GR_TEXFMT_RGB_565: if (m_chromakey_mode) { // Read about anisotropy and chromakey issues in macFormatConversions.cpp Convert565Kto8888((FxU16*)data, m_chromakey_value_565, texBuffer, texVals.nPixels); DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); } else { DownloadMipmapsToOpenGL(3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data, texVals, !use_mipmap_ext); } break; case GR_TEXFMT_ARGB_4444: DownloadMipmapsToOpenGL(4, GL_BGRA_EXT, GL_UNSIGNED_SHORT_4_4_4_4_REV, data, texVals, !use_mipmap_ext); break; case GR_TEXFMT_ARGB_1555: if (m_chromakey_mode) { // Read about anisotropy and chromakey issues in macFormatConversions.cpp Convert1555Kto8888((FxU16*) data, m_chromakey_value_1555, texBuffer, texVals.nPixels); DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); } else { DownloadMipmapsToOpenGL(4, GL_BGRA_EXT, GL_UNSIGNED_SHORT_1_5_5_5_REV, data, texVals, !use_mipmap_ext); } break; case GR_TEXFMT_P_8: // Read about anisotropy and chromakey issues in macFormatConversions.cpp if (InternalConfig.EXT_paletted_texture && InternalConfig.AnisotropylLevel < 2) { glColorTable(GL_TEXTURE_2D, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, m_palette); glTexImage2D( GL_TEXTURE_2D, texVals.lod, GL_COLOR_INDEX8_EXT, texVals.width, texVals.height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, data ); if (InternalConfig.Mipmapping && !use_mipmap_ext) { genPaletteMipmaps(texVals.width, texVals.height, data); } glReportError(); } else { if (InternalConfig.AnisotropylLevel >= 2) { // minimise anisotropy artefacts ConvertP8Kto8888(data, m_chromakey_value_8888, texBuffer, texVals.nPixels, m_palette); DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); } else { ConvertP8to8888(data, texBuffer, texVals.nPixels, m_palette); DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); } } break; case GR_TEXFMT_AP_88: if (use_two_textures) { FxU32 *texBuffer2 = texBuffer + 256 * 128; glColorTable(GL_TEXTURE_2D, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, m_palette); SplitAP88((FxU16*) data, (FxU8*) texBuffer, (FxU8*) texBuffer2, texVals.nPixels); glTexImage2D(GL_TEXTURE_2D, texVals.lod, GL_COLOR_INDEX8_EXT, texVals.width, texVals.height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, texBuffer); if (InternalConfig.Mipmapping && !use_mipmap_ext) { genPaletteMipmaps(texVals.width, texVals.height, (FxU8*) texBuffer); } glActiveTextureARB(OpenGL.ColorAlphaUnit1 + 1); DownloadMipmapsToOpenGL(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, texBuffer2, texVals, !use_mipmap_ext); glActiveTextureARB(OpenGL.ColorAlphaUnit1); glReportError(); } else { ConvertAP88to8888((FxU16*) data, texBuffer, texVals.nPixels, m_palette ); DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); } break; case GR_TEXFMT_ALPHA_8: // the alpha value is used for red, green, and blue as well (see glide24pgm.pdf) ConvertA8toAP88((FxU8*)data, (FxU16*) texBuffer, texVals.nPixels); DownloadMipmapsToOpenGL(2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); break; case GR_TEXFMT_ALPHA_INTENSITY_88: DownloadMipmapsToOpenGL(2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data, texVals, !use_mipmap_ext); break; case GR_TEXFMT_INTENSITY_8: DownloadMipmapsToOpenGL(1, GL_LUMINANCE, GL_UNSIGNED_BYTE, data, texVals, !use_mipmap_ext); break; case GR_TEXFMT_ALPHA_INTENSITY_44: // @todo: untested // DownloadMipmapsToOpenGL(GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data, &texVals); // @todo: untested ConvertAI44toAP88((FxU8*)data, (FxU16*)texBuffer, texVals.nPixels); DownloadMipmapsToOpenGL(2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); /* ConvertAI44toAP88((FxU8*) data, (FxU16*) texBuffer, texVals.nPixels ); glTexImage2D(GL_TEXTURE_2D, texVals.lod, 2, texVals.width, texVals.height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, texBuffer); if (InternalConfig.Mipmapping && !use_mipmap_ext) { gluBuild2DMipmaps(GL_TEXTURE_2D, 2, texVals.width, texVals.height, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, texBuffer); } glReportError(); */ break; case GR_TEXFMT_8BIT: //GR_TEXFMT_RGB_332 DownloadMipmapsToOpenGL(3, GL_RGB, GL_UNSIGNED_BYTE_3_3_2_EXT, data, texVals, !use_mipmap_ext); break; case GR_TEXFMT_16BIT: //GR_TEXFMT_ARGB_8332: Convert8332to8888( (FxU16*)data, texBuffer, texVals.nPixels ); DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); break; case GR_TEXFMT_YIQ_422: ConvertYIQto8888((FxU8*) data, texBuffer, texVals.nPixels, &(m_ncc[m_ncc_select])); // @todo: Should just be RGB in order to apply constant alpha, shouldn't it? DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); break; case GR_TEXFMT_AYIQ_8422: ConvertAYIQto8888((FxU16*) data, texBuffer, texVals.nPixels, &(m_ncc[m_ncc_select])); DownloadMipmapsToOpenGL(4, GL_RGBA, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); break; case GR_TEXFMT_RSVD0: case GR_TEXFMT_RSVD1: case GR_TEXFMT_RSVD2: default: GlideMsg("Error: grTexDownloadMipMapLevel - Unsupported format(%d)\n", m_info.format); memset(texBuffer, 255, texVals.nPixels * 2); DownloadMipmapsToOpenGL(1, GL_LUMINANCE, GL_UNSIGNED_BYTE, texBuffer, texVals, !use_mipmap_ext); break; } // Cleanup // if (useClientStorage) // { // glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, false); // glReportError(); // } if (subtexcoords) { // restore default values glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } } VERIFY_ACTIVE_TEXTURE_UNIT(OpenGL.ColorAlphaUnit1); return use_two_textures; } FxU32 PGTexture::LodOffset( FxU32 evenOdd, const GrTexInfo *info ) { FxU32 total = 0; for(GrLOD_t i = info->largeLod; i < info->smallLod; i++) { total += MipMapMemRequired( i, info->aspectRatio, info->format); } total = (total + 7) & ~7; return total; } FxU32 PGTexture::TextureMemRequired( FxU32 evenOdd, const GrTexInfo *info ) { // // If the format is one of these: // GR_TEXFMT_RGB_332, GR_TEXFMT_YIQ_422, GR_TEXFMT_ALPHA_8 // GR_TEXFMT_INTENSITY_8, GR_TEXFMT_ALPHA_INTENSITY_44, GR_TEXFMT_P_8 // Reduces the size by 2 // return nSquareTexLod[ info->format < GR_TEXFMT_16BIT ][ info->aspectRatio ][ info->largeLod ][ info->smallLod ]; } FxU32 PGTexture::MipMapMemRequired( GrLOD_t lod, GrAspectRatio_t aspectRatio, GrTextureFormat_t format ) { // // If the format is one of these: // GR_TEXFMT_RGB_332, GR_TEXFMT_YIQ_422, GR_TEXFMT_ALPHA_8 // GR_TEXFMT_INTENSITY_8, GR_TEXFMT_ALPHA_INTENSITY_44, GR_TEXFMT_P_8 // Reduces the size by 2 // return nSquareLod[format >= GR_TEXFMT_16BIT][aspectRatio][lod]; } void PGTexture::GetTexValues( TexValues * tval ) const { tval->width = texInfo[ m_info.aspectRatio ][ m_info.largeLod ].width; tval->height = texInfo[ m_info.aspectRatio ][ m_info.largeLod ].height; tval->nPixels = texInfo[ m_info.aspectRatio ][ m_info.largeLod ].numPixels; tval->lod = 0; } void PGTexture::ChromakeyValue( GrColor_t value ) { m_chromakey_value_8888 = (value & 0xffffff00); // RGB, Alpha ommited m_chromakey_value_565 = (FxU16) (( value & 0xF8000000 ) >> 16 | ( value & 0x00FC0000 ) >> 13 | ( value & 0x0000F800 ) >> 11 ); m_chromakey_value_1555 = (FxU16) (( value & 0xf8000000 ) >> 17 | ( value & 0x00f80000 ) >> 14 | ( value & 0x0000f800 ) >> 11 ); m_palette_dirty = true; } void PGTexture::ChromakeyMode( GrChromakeyMode_t mode ) { m_chromakey_mode = mode; m_palette_dirty = true; } void PGTexture::ApplyKeyToPalette( void ) { if (m_palette_dirty) { FxU32 hash = 0; { for (int i = 0; i < 256; i++) { if ((m_chromakey_mode) && ((m_palette[i] & 0xffffff00) == m_chromakey_value_8888)) { m_palette[i] &= 0xffffff00; } else { m_palette[i] |= 0x000000ff; } hash = ((hash << 5) | (hash >> 27)); hash += (InternalConfig.IgnorePaletteChange ? (m_palette[i] & 0x000000ff) : m_palette[i]); } } m_palette_hash = hash; m_palette_dirty = false; } } void PGTexture::NCCTable( GrNCCTable_t tab ) { switch ( tab ) { case GR_NCCTABLE_NCC0: case GR_NCCTABLE_NCC1: m_ncc_select = tab; } } FxU32 PGTexture::GetMemorySize( void ) const { return m_tex_memory_size; } unsigned int PGTexture::PowerOfTwoCeiling(unsigned int x) { for(unsigned int potc= 1; potc < 0x0fffffff; potc <<= 1) { if (potc >= x) return potc; } return 0; }