/////////////////////////////////////////////////////////////////////////////// // p_NxTexture.cpp #include "Gfx/gfxutils.h" #include "Gfx/Nx.h" #include "Gfx/NGPS/p_NxTexture.h" #include "Gfx/NGPS/PaletteGen.h" #include "Gfx/NGPS/nx/scene.h" #include "Gfx/NGPS/nx/sprite.h" #include #include #include #define min(x, y) (((x) > (y))? (y): (x)) #define max(x, y) (((x) < (y))? (y): (x)) #define USE_FAST_COLOR_FIND 1 #define PRINT_TIMES 0 #define PRINT_FAST_TIMES 0 namespace Nx { //////////////////////////////////////////////////////////////////////////////////// // Here's a machine specific implementation of CTexture bool CPs2Texture::s_tables_initialized = false; uint8 CPs2Texture::s_clut8_index_to_offset_table[256]; uint8 CPs2Texture::s_clut8_offset_to_index_table[256]; /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2Texture::CPs2Texture(bool loading_screen) : m_transparent(false), m_loading_screen(loading_screen), mp_single_texture(NULL), mp_group_texture(NULL), mp_orig_temp_32bit_image(NULL), mp_cur_temp_32bit_image(NULL), mp_temp_32bit_single_texture(NULL) { m_width = m_height = m_bitdepth = m_clut_bitdepth = m_num_mipmaps = 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2Texture::CPs2Texture(const CPs2Texture & src_texture) : CTexture(src_texture) { Dbg_MsgAssert(src_texture.mp_group_texture == NULL, ("Can't copy a CPs2Texture that is in a texture group")); m_width = src_texture.m_width; m_height = src_texture.m_height; m_bitdepth = src_texture.m_bitdepth; m_clut_bitdepth = src_texture.m_clut_bitdepth; m_num_mipmaps = src_texture.m_num_mipmaps; m_transparent = src_texture.m_transparent; m_loading_screen = src_texture.m_loading_screen; mp_group_texture = NULL; if (src_texture.mp_orig_temp_32bit_image) { mp_orig_temp_32bit_image = new uint32[m_width * m_height]; memcpy(mp_orig_temp_32bit_image, src_texture.mp_orig_temp_32bit_image, m_width * m_height * sizeof(uint32)); } else { mp_orig_temp_32bit_image = NULL; } if (src_texture.mp_cur_temp_32bit_image) { mp_cur_temp_32bit_image = new uint32[m_width * m_height]; memcpy(mp_cur_temp_32bit_image, src_texture.mp_cur_temp_32bit_image, m_width * m_height * sizeof(uint32)); } else { mp_cur_temp_32bit_image = NULL; } if (src_texture.mp_single_texture) { mp_single_texture = new NxPs2::SSingleTexture(*src_texture.mp_single_texture); } else { mp_single_texture = NULL; } if (src_texture.mp_temp_32bit_single_texture) { mp_temp_32bit_single_texture = new NxPs2::SSingleTexture(*src_texture.mp_temp_32bit_single_texture); } else { mp_temp_32bit_single_texture = NULL; } } /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2Texture::~CPs2Texture() { // delete the actual single texture, if any if (mp_single_texture) { delete mp_single_texture; } if (mp_orig_temp_32bit_image) { delete mp_orig_temp_32bit_image; } if (mp_cur_temp_32bit_image) { delete mp_cur_temp_32bit_image; } if (mp_temp_32bit_single_texture) { delete mp_temp_32bit_single_texture; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::sInitTables() { if (!s_tables_initialized) { uint i, j, idx; idx = 0; for(i = 0; i < 256; i+=32) { for(j = i; j < i+8; j++) s_clut8_index_to_offset_table[j] = (uint8) idx++; for(j = i+16; j < i+16+8; j++) s_clut8_index_to_offset_table[j] = (uint8) idx++; for(j = i+8; j < i+8+8; j++) s_clut8_index_to_offset_table[j] = (uint8) idx++; for(j = i+24; j < i+24+8; j++) s_clut8_index_to_offset_table[j] = (uint8) idx++; } idx = 0; for(i = 0; i < 256; i+=32) { for(j = i; j < i+8; j++) s_clut8_offset_to_index_table[idx++] = (uint8) j; for(j = i+16; j < i+16+8; j++) s_clut8_offset_to_index_table[idx++] = (uint8) j; for(j = i+8; j < i+8+8; j++) s_clut8_offset_to_index_table[idx++] = (uint8) j; for(j = i+24; j < i+24+8; j++) s_clut8_offset_to_index_table[idx++] = (uint8) j; } s_tables_initialized = true; } } ///////////////////////////////////////////////////////////////////////////////////// // Private classes // /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram) { Dbg_Assert(mp_group_texture == NULL); // call engine mp_single_texture = new NxPs2::SSingleTexture(p_texture_name, sprite, alloc_vram, m_loading_screen); if (mp_single_texture) { m_width = mp_single_texture->GetOrigWidth(); // Garrett: This is probably not what we want, but CSprite m_height = mp_single_texture->GetOrigHeight(); // needs this. Probably will need to change CTexture. m_bitdepth = mp_single_texture->GetBitdepth(); m_clut_bitdepth = mp_single_texture->GetClutBitdepth(); m_num_mipmaps = mp_single_texture->GetNumMipmaps(); m_transparent = mp_single_texture->IsTransparent(); } return mp_single_texture != NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, bool sprite, bool alloc_vram) { Dbg_Assert(mp_group_texture == NULL); // call engine mp_single_texture = new NxPs2::SSingleTexture(p_buffer, buffer_size, sprite, alloc_vram, m_loading_screen); if (mp_single_texture) { m_width = mp_single_texture->GetOrigWidth(); // Garrett: This is probably not what we want, but CSprite m_height = mp_single_texture->GetOrigHeight(); // needs this. Probably will need to change CTexture. m_bitdepth = mp_single_texture->GetBitdepth(); m_clut_bitdepth = mp_single_texture->GetClutBitdepth(); m_num_mipmaps = mp_single_texture->GetNumMipmaps(); m_transparent = mp_single_texture->IsTransparent(); } return mp_single_texture != NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_replace_texture(CTexture *p_texture) { Dbg_Assert(p_texture); Dbg_Assert(mp_group_texture); CPs2Texture *p_ps2_texture = static_cast(p_texture); Dbg_Assert(p_ps2_texture->mp_single_texture); if ((m_width != p_ps2_texture->m_width) || (m_height != p_ps2_texture->m_height) || (m_bitdepth != p_ps2_texture->m_bitdepth) || (m_num_mipmaps != p_ps2_texture->m_num_mipmaps)) { Dbg_Message("Original size (%d, %d); Replacement size (%d, %d)", m_width, m_height, p_ps2_texture->m_width, p_ps2_texture->m_height); Dbg_Message("Original bitdepth %d; Replacement bitdepth %d", m_bitdepth, p_ps2_texture->m_bitdepth); Dbg_Message("Original num mipmaps %d; Replacement num mipmaps %d", m_num_mipmaps, p_ps2_texture->m_num_mipmaps); return false; } #if 0 if ((m_bitdepth <= 8) && (m_clut_bitdepth < p_ps2_texture->m_clut_bitdepth)) { Dbg_Message("Original clut bitdepth %d; Replacement clut bitdepth %d", m_clut_bitdepth, p_ps2_texture->m_clut_bitdepth); return false; } #endif // Copy texture uint8 *p_orig_tex_buffer = mp_group_texture->GetTextureBuffer(); uint8 *p_new_tex_buffer = p_ps2_texture->mp_single_texture->GetTextureBuffer(); if (mp_group_texture->HasSwizzleMip()) { mp_group_texture->ReplaceTextureData(p_new_tex_buffer); } else { memcpy(p_orig_tex_buffer, p_new_tex_buffer, p_ps2_texture->mp_single_texture->GetTextureBufferSize()); } // And clut (if any) uint32 *p_orig_clut_buffer = (uint32 *) mp_group_texture->GetClutBuffer(); if (p_orig_clut_buffer) { uint32 *p_new_clut_buffer = (uint32 *) p_ps2_texture->mp_single_texture->GetClutBuffer(); Dbg_Assert(p_new_clut_buffer); if (m_clut_bitdepth == p_ps2_texture->m_clut_bitdepth) { memcpy(p_orig_clut_buffer, p_new_clut_buffer, p_ps2_texture->mp_single_texture->GetClutBufferSize()); } else if ((m_clut_bitdepth == 32) && (p_ps2_texture->m_clut_bitdepth == 16)) { uint16 *p_clut_16_buffer = (uint16 *) p_new_clut_buffer; Image::RGBA new_color; new_color.a = 0x80; int num_colors = p_ps2_texture->mp_single_texture->GetClutBufferSize() / 2; for (int i = 0; i < num_colors; i++) { // Convert from 16 to 32 bit new_color.r = ((*(p_clut_16_buffer) >> 0) & 0x1f) << 3; new_color.g = ((*(p_clut_16_buffer) >> 5) & 0x1f) << 3; new_color.b = ((*(p_clut_16_buffer++) >> 10) & 0x1f) << 3; //Dbg_Message("Replacing color #%d %x with %x", i, *p_orig_clut_buffer, *((uint32 *) &new_color)); *(p_orig_clut_buffer++) = *((uint32 *) &new_color); } } else if ((m_clut_bitdepth == 16) && (p_ps2_texture->m_clut_bitdepth == 32)) { uint16 *p_clut_16_buffer = (uint16 *) p_orig_clut_buffer; uint16 new_color; Image::RGBA *p_full_color; int num_colors = p_ps2_texture->mp_single_texture->GetClutBufferSize() / 4; for (int i = 0; i < num_colors; i++) { p_full_color = (Image::RGBA *) p_new_clut_buffer++; new_color = 0x8000 | (p_full_color->r >> 3) | ( (p_full_color->g >> 3) << 5 ) | ( (p_full_color->b >> 3) << 10 ); *(p_clut_16_buffer++) = new_color; } } else { Dbg_Message("Can't handle this combination: clut bitdepth %d; Replacement clut bitdepth %d", m_clut_bitdepth, p_ps2_texture->m_clut_bitdepth); return false; } //Dbg_Message("Original clut size %d; Replacement clut size %d", mp_group_texture->GetClutBufferSize(), p_ps2_texture->mp_single_texture->GetClutBufferSize()); } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_generate_32bit_image(bool renderable, bool store_original) { Dbg_MsgAssert(!mp_cur_temp_32bit_image, ("Can't generate temp 32-bit image: one already exists")); // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_MsgAssert(m_clut_bitdepth == 32, ("Clut bitdepth is only %d, not 32", m_clut_bitdepth)); Dbg_Assert(mp_single_texture); Dbg_Assert(s_tables_initialized); // Allocate buffer mp_cur_temp_32bit_image = new uint32[m_width * m_height]; uint32 *p_pixel_buffer = mp_cur_temp_32bit_image; // Get source buffers uint8 *p_orig_tex_buffer = mp_single_texture->GetTextureBuffer(); // And clut (if any) uint32 *p_orig_clut_buffer = (uint32 *) mp_single_texture->GetClutBuffer(); // Move texture buffer to start (and flip y) p_orig_tex_buffer += (m_height - 1) * m_width; for (uint h = 0; h < m_height; h++) { for (uint w = 0; w < m_width; w++) { // Copies from 8-bit to 32-bit *p_pixel_buffer++ = p_orig_clut_buffer[ s_clut8_index_to_offset(p_orig_tex_buffer[w]) ]; } p_orig_tex_buffer -= m_width; // Negative because were flipping the lines } // Copy to original buffer if (store_original) { mp_orig_temp_32bit_image = new uint32[m_width * m_height]; memcpy(mp_orig_temp_32bit_image, mp_cur_temp_32bit_image, m_width * m_height * sizeof(uint32)); } // Allocate a SSingleTexture if (renderable) { mp_temp_32bit_single_texture = new NxPs2::SSingleTexture((uint8 *) mp_cur_temp_32bit_image, m_width, m_height, 32, m_clut_bitdepth, m_num_mipmaps, true, true, m_loading_screen); } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_put_32bit_image_into_texture(bool new_palette) { Dbg_MsgAssert(mp_cur_temp_32bit_image, ("No temp 32-bit image")); // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_Assert(m_clut_bitdepth == 32); Dbg_Assert(mp_single_texture); Dbg_Assert(s_tables_initialized); // Generate a new palette if (new_palette) { GeneratePalette((Image::RGBA *) mp_single_texture->GetClutBuffer(), (uint8 *) mp_cur_temp_32bit_image, m_width, m_height, 32, 1 << m_bitdepth); } // Get buffers uint8 *p_tex_buffer = mp_single_texture->GetTextureBuffer(); uint32 *p_pixel_buffer = mp_cur_temp_32bit_image; // Move texture buffer to start (and flip y) p_tex_buffer += (m_height - 1) * m_width; #if USE_FAST_COLOR_FIND #if PRINT_FAST_TIMES uint32 start_time = Tmr::GetTimeInUSeconds(); #endif // PRINT_FAST_TIMES setup_fast_clut_color_find(false); #if PRINT_FAST_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); Dbg_Message("plat_put_32bit_image_into_texture fast setup time %d us", end_time - start_time); #endif // PRINT_FAST_TIMES #endif // USE_FAST_COLOR_FIND for (uint h = 0; h < m_height; h++) { for (uint w = 0; w < m_width; w++) { // Copies from 32-bit to 8-bit #if USE_FAST_COLOR_FIND p_tex_buffer[w] = s_clut8_offset_to_index( fast_find_closest_clut_color( *((Image::RGBA *) p_pixel_buffer++) ) ); #else p_tex_buffer[w] = s_clut8_offset_to_index( find_closest_clut_color( *((Image::RGBA *) p_pixel_buffer++) ) ); #endif // USE_FAST_COLOR_FIND } p_tex_buffer -= m_width; // Negative because were flipping the lines } #if USE_FAST_COLOR_FIND cleanup_fast_clut_color_find(); #if PRINT_FAST_TIMES end_time = Tmr::GetTimeInUSeconds(); Dbg_Message("plat_put_32bit_image_into_texture fast total time %d us", end_time - start_time); #endif // PRINT_FAST_TIMES #endif // USE_FAST_COLOR_FIND // Delete buffers delete [] mp_cur_temp_32bit_image; mp_cur_temp_32bit_image = NULL; if (mp_orig_temp_32bit_image) { delete [] mp_orig_temp_32bit_image; mp_orig_temp_32bit_image = NULL; } if (mp_temp_32bit_single_texture) { delete mp_temp_32bit_single_texture; mp_temp_32bit_single_texture = NULL; } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::s_get_nearest_pixels_from_32bit_texture(Image::RGBA p_nearest[][2], uint32 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 width, uint16 height) { int next_x = (x_pos >= (width - 1)) ? 0 : 1; // Clamp x if at end bool clamp_y = (y_pos >= (height - 1)); // And same for y p_buffer += (y_pos * width) + x_pos; // First row p_nearest[0][0] = *((Image::RGBA *) p_buffer); p_nearest[0][1] = *((Image::RGBA *) &p_buffer[next_x]); if (!clamp_y) { p_buffer += width; } // Second row p_nearest[1][0] = *((Image::RGBA *) p_buffer); p_nearest[1][1] = *((Image::RGBA *) &p_buffer[next_x]); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::s_get_region(uint8 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 tex_width, uint16 tex_height, uint8 *p_region, uint16 region_width, uint16 region_height, uint16 bitdepth) { int bytes_per_pixel = bitdepth / 8; int copy_bytes; int width_in_bytes; int stride_in_bytes; // Clamp region values int clamp_region_width = min(region_width, tex_width - x_pos); int clamp_region_height = min(region_height, tex_height - y_pos); Dbg_Assert(clamp_region_width >= 0); Dbg_Assert(clamp_region_height >= 0); // Move texture buffer to start if (bytes_per_pixel) { p_buffer += ((y_pos * tex_width) + x_pos) * bytes_per_pixel; copy_bytes = clamp_region_width * bytes_per_pixel; width_in_bytes = region_width * bytes_per_pixel; stride_in_bytes = tex_width * bytes_per_pixel; } else // 4-bit { p_buffer += ((y_pos * tex_width) + x_pos) / 2; copy_bytes = clamp_region_width / 2; width_in_bytes = region_width / 2; stride_in_bytes = tex_width / 2; } //Dbg_Message("s_get_region(): xpos %d, ypos %d, tex_width %d, tex_height %d, region_width %d, region_height %d, width_in_bytes %d, stride_in_bytes %d, bitdepth %d", // x_pos, y_pos, tex_width, tex_height, region_width, region_height, width_in_bytes, stride_in_bytes, bitdepth); for (int h = 0; h < clamp_region_height; h++) { // Copy each line memcpy(p_region, p_buffer, copy_bytes); p_region += width_in_bytes; p_buffer += stride_in_bytes; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::s_put_region(uint8 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 tex_width, uint16 tex_height, uint8 *p_region, uint16 region_width, uint16 region_height, uint16 bitdepth) { int bytes_per_pixel = bitdepth / 8; int copy_bytes; int width_in_bytes; int stride_in_bytes; // Clamp region values int clamp_region_width = min(region_width, tex_width - x_pos); int clamp_region_height = min(region_height, tex_height - y_pos); Dbg_Assert(clamp_region_width >= 0); Dbg_Assert(clamp_region_height >= 0); // Move texture buffer to start if (bytes_per_pixel) { p_buffer += ((y_pos * tex_width) + x_pos) * bytes_per_pixel; copy_bytes = clamp_region_width * bytes_per_pixel; width_in_bytes = region_width * bytes_per_pixel; stride_in_bytes = tex_width * bytes_per_pixel; } else // 4-bit { p_buffer += ((y_pos * tex_width) + x_pos) / 2; copy_bytes = clamp_region_width / 2; width_in_bytes = region_width / 2; stride_in_bytes = tex_width / 2; } //Dbg_Message("s_put_region(): xpos %d, ypos %d, tex_width %d, tex_height %d, region_width %d, region_height %d, width_in_bytes %d, stride_in_bytes %d, bitdepth %d", // x_pos, y_pos, tex_width, tex_height, region_width, region_height, width_in_bytes, stride_in_bytes, bitdepth); for (int h = 0; h < clamp_region_height; h++) { // Copy each line memcpy(p_buffer, p_region, copy_bytes); p_region += width_in_bytes; p_buffer += stride_in_bytes; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::s_scale_texture(uint8 *p_buffer, uint16 width, uint16 height, uint8 *p_result_buffer, uint16 new_width, uint16 new_height, uint16 bitdepth) { Dbg_MsgAssert(bitdepth > 8, ("Can't scale a paletted texture. Convert to unpaletted first.")); Dbg_Assert(bitdepth == 32); // We'll worry about 16 and 24 bit later. #if PRINT_TIMES static uint32 total_time = 0; uint32 start_time = Tmr::GetTimeInUSeconds(); #endif float orig_coord[2] = { 0.0f, 0.0f }; float delta_dim[2]; float delta_coord[2] = { 0.0f, 0.0f }; uint16 orig_index[2] = { 0, 0 }; Image::RGBA closest_pixels[2][2]; float pixel_portion[2][2]; Image::RGBA combined_pixel; delta_dim[X] = (float) width / (float) new_width; delta_dim[Y] = (float) height / (float) new_height; for (int h = 0; h < new_height; h++) { orig_index[Y] = (uint16) orig_coord[Y]; // Get integer version delta_coord[Y] = orig_coord[Y] - orig_index[Y]; // And now get fractional part for (int w = 0; w < new_width; w++) { orig_index[X] = (uint16) orig_coord[X]; // Get integer version delta_coord[X] = orig_coord[X] - orig_index[X]; // And now get fractional part // Calculate the pixel portions pixel_portion[0][0] = (1.0f - delta_coord[X]) * (1.0f - delta_coord[Y]); pixel_portion[0][1] = (delta_coord[X]) * (1.0f - delta_coord[Y]); pixel_portion[1][0] = (1.0f - delta_coord[X]) * (delta_coord[Y]); pixel_portion[1][1] = (delta_coord[X]) * (delta_coord[Y]); Dbg_MsgAssert((orig_index[X] + 0) < width, ("X coord out of range: %d", orig_index[X])); Dbg_MsgAssert((orig_index[Y] + 0) < height, ("Y coord out of range: %d", orig_index[Y])); // Get the 4 nearest pixels s_get_nearest_pixels_from_32bit_texture(closest_pixels, (uint32 *) p_buffer, orig_index[X], orig_index[Y], width, height); // Calculate each color combined_pixel.r = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].r) + (pixel_portion[0][1] * (float) closest_pixels[0][1].r) + (pixel_portion[1][0] * (float) closest_pixels[1][0].r) + (pixel_portion[1][1] * (float) closest_pixels[1][1].r)); combined_pixel.g = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].g) + (pixel_portion[0][1] * (float) closest_pixels[0][1].g) + (pixel_portion[1][0] * (float) closest_pixels[1][0].g) + (pixel_portion[1][1] * (float) closest_pixels[1][1].g)); combined_pixel.b = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].b) + (pixel_portion[0][1] * (float) closest_pixels[0][1].b) + (pixel_portion[1][0] * (float) closest_pixels[1][0].b) + (pixel_portion[1][1] * (float) closest_pixels[1][1].b)); combined_pixel.a = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].a) + (pixel_portion[0][1] * (float) closest_pixels[0][1].a) + (pixel_portion[1][0] * (float) closest_pixels[1][0].a) + (pixel_portion[1][1] * (float) closest_pixels[1][1].a)); // And copy *((uint32 *) p_result_buffer)++ = *((uint32 *) &(combined_pixel)); // Advance to next coord orig_coord[X] += delta_dim[X]; } orig_coord[X] = 0.0f; orig_coord[Y] += delta_dim[Y]; } #if PRINT_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); total_time += end_time - start_time; Dbg_Message("scale_texture Update time %d us; Total Time %d", end_time - start_time, total_time); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::s_combine_adjacent_borders(uint8 *p_first_rect, uint8 *p_second_rect, uint16 first_width, uint16 first_height, uint16 second_stride, float first_portion, int split_axis, uint16 bitdepth) { Dbg_MsgAssert(bitdepth > 8, ("Can't adjust the border of a paletted texture. Convert to unpaletted first.")); Dbg_Assert(bitdepth == 32); // We'll worry about 16 and 24 bit later. Image::RGBA *p_rect_pixel[2] = { (Image::RGBA *) p_first_rect, (Image::RGBA *) p_second_rect }; float second_portion = 1.0f - first_portion; int num_pixels, inc_pixels; // Init if (split_axis == X) { num_pixels = first_width; inc_pixels = 1; } else { Dbg_Assert(first_width == second_stride); num_pixels = first_height; inc_pixels = second_stride; } // Combine the pixels for (int pidx = 0; pidx < num_pixels; pidx++) { p_rect_pixel[1]->r = (uint8) ( (first_portion * (float) p_rect_pixel[0]->r) + (second_portion * (float) p_rect_pixel[1]->r) ); p_rect_pixel[1]->g = (uint8) ( (first_portion * (float) p_rect_pixel[0]->g) + (second_portion * (float) p_rect_pixel[1]->g) ); p_rect_pixel[1]->b = (uint8) ( (first_portion * (float) p_rect_pixel[0]->b) + (second_portion * (float) p_rect_pixel[1]->b) ); p_rect_pixel[1]->a = (uint8) ( (first_portion * (float) p_rect_pixel[0]->a) + (second_portion * (float) p_rect_pixel[1]->a) ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ static const int ALPHA_WEIGHT = 2; // how much more weight to give to alpha uint8 CPs2Texture::find_closest_clut_color(Image::RGBA rgba) { // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_Assert(m_clut_bitdepth == 32); Dbg_Assert(mp_single_texture); int clutsize = 1 << m_bitdepth; Image::RGBA *p_clut_buffer = (Image::RGBA *) mp_single_texture->GetClutBuffer(); Dbg_Assert(p_clut_buffer); int delR, delG, delB, delA; int bestDist, curDist; uint8 bestIdx = 0; bestDist = 0x7FFFFFFF; for (int i = 0; i < clutsize; i++) { delR = (int) (p_clut_buffer)->r - (int) rgba.r; delG = (int) (p_clut_buffer)->g - (int) rgba.g; delB = (int) (p_clut_buffer)->b - (int) rgba.b; delA = (int) (p_clut_buffer++)->a - (int) rgba.a; // Calculate 4-dimensional distance between color components curDist = (delR * delR) + (delG * delG) + (delB * delB) + (delA * delA * ALPHA_WEIGHT); // Choose the index with the smallest distance from src color if (curDist < bestDist) { bestDist = curDist; bestIdx = i; } } return bestIdx; } /******************************************************************/ /* */ /* */ /******************************************************************/ // Holds clut index array start and size struct SClutEntries { uint16 m_clut_buffer_index; uint8 m_num_clut_entries; }; static const int s_axis_level_bits = 3; // Significant bits used to break down each color into the tree static const int s_axis_levels = 1 << s_axis_level_bits; static bool s_use_alpha_axis; static uint8 *sp_clut_index_buffer = NULL; //static int s_num_color_searches; //static int s_num_color_compares; // Array is of form [R][G][B][A] static SClutEntries s_palette_octree[s_axis_levels][s_axis_levels][s_axis_levels][s_axis_levels]; void CPs2Texture::setup_fast_clut_color_find(bool use_alpha) { // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_Assert(m_clut_bitdepth == 32); Dbg_Assert(mp_single_texture); Dbg_Assert(!sp_clut_index_buffer); s_use_alpha_axis = use_alpha; int clutsize = 1 << m_bitdepth; int range = clutsize >> s_axis_level_bits; int alpha_axis_levels = (s_use_alpha_axis) ? s_axis_levels : 1; int index_buffer_size = s_axis_levels * s_axis_levels * s_axis_levels * alpha_axis_levels * (clutsize / 2); // Could be made smaller Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); sp_clut_index_buffer = new uint8[index_buffer_size]; Mem::Manager::sHandle().PopContext(); int min_level[4]; int max_level[4]; const int overlap = (range >> 1); const int min_start = -overlap; const int max_start = range + overlap; const int max_alpha_start = (s_use_alpha_axis) ? max_start : clutsize; int buffer_index = 0; min_level[0] = min_start; max_level[0] = max_start; for (int r = 0; r < s_axis_levels; r++) { min_level[1] = min_start; max_level[1] = max_start; for (int g = 0; g < s_axis_levels; g++) { min_level[2] = min_start; max_level[2] = max_start; for (int b = 0; b < s_axis_levels; b++) { min_level[3] = min_start; max_level[3] = max_alpha_start; for (int a = 0; a < alpha_axis_levels; a++) { SClutEntries *p_palette_node = &s_palette_octree[r][g][b][a]; p_palette_node->m_clut_buffer_index = buffer_index; p_palette_node->m_num_clut_entries = 0; Image::RGBA *p_clut_buffer = (Image::RGBA *) mp_single_texture->GetClutBuffer(); Dbg_MsgAssert(buffer_index < (index_buffer_size - clutsize), ("Running out of index buffer space")); for (int index = 0; index < clutsize; index++, p_clut_buffer++) { if ((p_clut_buffer->r >= min_level[0]) && (p_clut_buffer->r <= max_level[0]) && (p_clut_buffer->g >= min_level[1]) && (p_clut_buffer->g <= max_level[1]) && (p_clut_buffer->b >= min_level[2]) && (p_clut_buffer->b <= max_level[2]) && (p_clut_buffer->a >= min_level[3]) && (p_clut_buffer->a <= max_level[3])) { p_palette_node->m_num_clut_entries++; sp_clut_index_buffer[buffer_index++] = index; } } // for index min_level[3] += range; max_level[3] += range; } // for a min_level[2] += range; max_level[2] += range; } // for b min_level[1] += range; max_level[1] += range; } // for g min_level[0] += range; max_level[0] += range; } // for r //s_num_color_searches = 0; //s_num_color_compares = 0; } uint8 CPs2Texture::fast_find_closest_clut_color(Image::RGBA rgba) { // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_Assert(m_clut_bitdepth == 32); Dbg_Assert(mp_single_texture); Dbg_Assert(sp_clut_index_buffer); int range_bits = m_bitdepth - s_axis_level_bits; int r_index = rgba.r >> range_bits; int g_index = rgba.g >> range_bits; int b_index = rgba.b >> range_bits; int a_index = (s_use_alpha_axis) ? rgba.a >> range_bits : 0; SClutEntries *p_palette_node = &s_palette_octree[r_index][g_index][b_index][a_index]; //s_num_color_searches++; if (p_palette_node->m_num_clut_entries == 0) { //s_num_color_compares += 256; return find_closest_clut_color(rgba); } else { uint8 *p_palette_index = &sp_clut_index_buffer[p_palette_node->m_clut_buffer_index]; Image::RGBA *p_clut_buffer = (Image::RGBA *) mp_single_texture->GetClutBuffer(); Dbg_Assert(p_clut_buffer); int delR, delG, delB, delA; int bestDist, curDist; uint8 bestIdx = 0; bestDist = 0x7FFFFFFF; for (int entry_index = 0; entry_index < p_palette_node->m_num_clut_entries; entry_index++, p_palette_index++) { Image::RGBA *p_clut_color = &p_clut_buffer[*p_palette_index]; delR = (int) p_clut_color->r - (int) rgba.r; delG = (int) p_clut_color->g - (int) rgba.g; delB = (int) p_clut_color->b - (int) rgba.b; delA = (int) p_clut_color->a - (int) rgba.a; // Calculate 4-dimensional distance between color components curDist = (delR * delR) + (delG * delG) + (delB * delB) + (delA * delA * ALPHA_WEIGHT); // Choose the index with the smallest distance from src color if (curDist < bestDist) { bestDist = curDist; bestIdx = *p_palette_index; } } //s_num_color_compares += p_palette_node->m_num_clut_entries; return bestIdx; } } void CPs2Texture::cleanup_fast_clut_color_find() { Dbg_Assert(sp_clut_index_buffer); //Dbg_Message("Average color compares per search: %f", (float) s_num_color_compares / (float) s_num_color_searches); delete [] sp_clut_index_buffer; sp_clut_index_buffer = NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::copy_region_to_32bit_buffer(uint32 *p_pixel_buffer, uint16 x_pos, uint16 y_pos, uint16 width, uint16 height) { // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_MsgAssert(m_clut_bitdepth == 32, ("Clut bitdepth is only %d, not 32", m_clut_bitdepth)); Dbg_Assert(mp_single_texture); Dbg_Assert(s_tables_initialized); #if PRINT_TIMES static uint32 total_time = 0; uint32 start_time = Tmr::GetTimeInUSeconds(); #endif if (mp_cur_temp_32bit_image) { // Get source buffer uint32 *p_orig_tex_buffer = mp_cur_temp_32bit_image; // Move texture buffer to start p_orig_tex_buffer += (y_pos * m_width) + x_pos; for (uint h = 0; h < height; h++) { memcpy(p_pixel_buffer, p_orig_tex_buffer, width * sizeof(uint32)); p_pixel_buffer += width; p_orig_tex_buffer += m_width; } } else { // Get source buffers uint8 *p_orig_tex_buffer = mp_single_texture->GetTextureBuffer(); // And clut (if any) uint32 *p_orig_clut_buffer = (uint32 *) mp_single_texture->GetClutBuffer(); // Move texture buffer to start (and flip y) p_orig_tex_buffer += ((m_height - 1 - y_pos) * m_width) + x_pos; for (uint h = 0; h < height; h++) { for (uint w = 0; w < width; w++) { // Copies from 8-bit to 32-bit *p_pixel_buffer++ = p_orig_clut_buffer[ s_clut8_index_to_offset(p_orig_tex_buffer[w]) ]; } p_orig_tex_buffer -= m_width; // Negative because were flipping the lines } } #if PRINT_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); total_time += end_time - start_time; Dbg_Message("copy_region_to_32bit_buffer Update time %d us; Total Time %d", end_time - start_time, total_time); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::blit_32bit_buffer_to_texture(uint32 *p_pixel_buffer, uint16 x_pos, uint16 y_pos, uint16 width, uint16 height) { // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_Assert(m_clut_bitdepth == 32); Dbg_Assert(mp_single_texture); Dbg_Assert(s_tables_initialized); #if PRINT_TIMES static uint32 total_time = 0; uint32 start_time = Tmr::GetTimeInUSeconds(); #endif if (mp_cur_temp_32bit_image) { // Get source buffer uint32 *p_tex_buffer = mp_cur_temp_32bit_image; // Move texture buffer to start p_tex_buffer += (y_pos * m_width) + x_pos; for (uint h = 0; h < height; h++) { memcpy(p_tex_buffer, p_pixel_buffer, width * sizeof(uint32)); p_pixel_buffer += width; p_tex_buffer += m_width; } } else { // Get source buffers uint8 *p_tex_buffer = mp_single_texture->GetTextureBuffer(); // Move texture buffer to start (and flip y) p_tex_buffer += ((m_height - 1 - y_pos) * m_width) + x_pos; for (uint h = 0; h < height; h++) { for (uint w = 0; w < width; w++) { // Copies from 32-bit to 8-bit p_tex_buffer[w] = s_clut8_offset_to_index( find_closest_clut_color( *((Image::RGBA *) p_pixel_buffer++) ) ); } p_tex_buffer -= m_width; // Negative because were flipping the lines } } #if PRINT_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); total_time += end_time - start_time; Dbg_Message("blit_32bit_buffer_to_texture Update time %d us; Total Time %d", end_time - start_time, total_time); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2Texture::set_32bit_pixel_to_texture(uint32 pixel, uint16 x_pos, uint16 y_pos) { // For now, just coded up the 8-bit image with 32-bit clut Dbg_Assert(m_bitdepth == 8); Dbg_Assert(m_clut_bitdepth == 32); Dbg_Assert(mp_single_texture); Dbg_Assert(s_tables_initialized); if (mp_cur_temp_32bit_image) { // Set pixel in buffer mp_cur_temp_32bit_image[(y_pos * m_width) + x_pos] = pixel; } else { // Get source buffers uint8 *p_tex_buffer = mp_single_texture->GetTextureBuffer(); // Move texture buffer to start (and flip y) p_tex_buffer[((m_height - 1 - y_pos) * m_width) + x_pos] = s_clut8_offset_to_index( find_closest_clut_color( *((Image::RGBA *) &pixel) ) ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_offset(int x_pixels, int y_pixels, bool use_fill_color, Image::RGBA fill_color) { int region_start_pos[2]; int region_end_pos[2]; int region_width; int region_height; // Figure out X info if (x_pixels >= 0) { region_start_pos[X] = 0; region_end_pos[X] = x_pixels; region_width = m_width - x_pixels; } else { region_start_pos[X] = -x_pixels; region_end_pos[X] = 0; region_width = m_width - (-x_pixels); } // Figure out Y info if (y_pixels >= 0) { region_start_pos[Y] = 0; region_end_pos[Y] = y_pixels; region_height = m_height - y_pixels; } else { region_start_pos[Y] = -y_pixels; region_end_pos[Y] = 0; region_height = m_height - (-y_pixels); } // Copy texture region into temp buffers in 32-bit format Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); uint32 *p_texture_buffer = new uint32[region_width * region_height]; Mem::Manager::sHandle().PopContext(); // Get section that will be in offset texture copy_region_to_32bit_buffer(p_texture_buffer, region_start_pos[X], region_start_pos[Y], region_width, region_height); // And put that region back into texture blit_32bit_buffer_to_texture(p_texture_buffer, region_end_pos[X], region_end_pos[Y], region_width, region_height); //static uint32 total_time = 0; //uint32 start_time = Tmr::GetTimeInUSeconds(); // Check if we have a fill color if (use_fill_color) { Dbg_MsgAssert(0, ("Fill color not implemented yet")); } else { for (int h = 0, y_pos = -region_end_pos[Y]; h < m_height; h++, y_pos++) { int y_clamp_pos = max(y_pos, 0); y_clamp_pos = min(y_clamp_pos, region_height - 1); int y_buffer_index = y_clamp_pos * region_width; for (int w = 0, x_pos = -region_end_pos[X]; w < m_width; w++, x_pos++) { // Check if we are within the new region if ((x_pos >= 0) && (x_pos < region_width) && (y_pos >= 0) && (y_pos < region_height)) { continue; } int x_clamp_pos = max(x_pos, 0); x_clamp_pos = min(x_clamp_pos, region_width - 1); set_32bit_pixel_to_texture(p_texture_buffer[y_buffer_index + x_clamp_pos], w, h); } } } //uint32 end_time = Tmr::GetTimeInUSeconds(); //total_time += end_time - start_time; //Dbg_Message("offset_texture fill Update time %d us; Total Time %d", end_time - start_time, total_time); delete [] p_texture_buffer; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_adjust_region(uint16 x_pos, uint16 y_pos, uint16 width, uint16 height, int split_axis, uint16 start_point, uint16 end_point) { // Copy original rectangle in a temp buffer in 32-bit format (needed for interpolation) Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); uint32 *p_orig_region_pixels = new uint32[width * height]; Mem::Manager::sHandle().PopContext(); copy_region_to_32bit_buffer(p_orig_region_pixels, x_pos, y_pos, width, height); float first_rect_portion; uint16 rect_pos[2][2]; uint16 rect_new_pos[2]; uint16 rect_orig_width[2]; uint16 rect_orig_height[2]; uint16 rect_new_width[2]; uint16 rect_new_height[2]; // Figure out first rectangle rect_pos[0][X] = 0; rect_pos[0][Y] = 0; if (split_axis == X) { Dbg_Assert(start_point >= x_pos); Dbg_Assert(start_point <= x_pos + width); Dbg_Assert(end_point >= x_pos); Dbg_Assert(end_point <= x_pos + width); // Dimensions of first rectangle rect_orig_width[0] = start_point - x_pos + 1; rect_orig_height[0] = height; rect_new_width[0] = end_point - x_pos + 1; rect_new_height[0] = height; // Second rectangle rect_pos[1][X] = start_point - x_pos; rect_pos[1][Y] = 0; rect_new_pos[X] = end_point - x_pos; rect_new_pos[Y] = 0; rect_orig_width[1] = width - (start_point - x_pos); rect_orig_height[1] = height; rect_new_width[1] = width - (end_point - x_pos); rect_new_height[1] = height; // Figure out portion of first rectangle to whole region first_rect_portion = (float) rect_new_width[0] / (float) width; } else { Dbg_Assert(start_point >= y_pos); Dbg_Assert(start_point <= y_pos + height); Dbg_Assert(end_point >= y_pos); Dbg_Assert(end_point <= y_pos + height); // Dimensions of first rectangle rect_orig_width[0] = width; rect_orig_height[0] = start_point - y_pos + 1; rect_new_width[0] = width; rect_new_height[0] = end_point - y_pos + 1; // Second rectangle rect_pos[1][X] = 0; rect_pos[1][Y] = start_point - y_pos; rect_new_pos[X] = 0; rect_new_pos[Y] = end_point - y_pos; rect_orig_width[1] = width; rect_orig_height[1] = height - (start_point - y_pos); rect_new_width[1] = width; rect_new_height[1] = height - (end_point - y_pos); // Figure out portion of first rectangle to whole region first_rect_portion = (float) rect_new_height[0] / (float) height; } // Make room for the original and new rectangles uint32 *p_orig_rect_pixels[2]; uint32 *p_new_rect_pixels[2]; Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); p_orig_rect_pixels[0] = new uint32[rect_orig_width[0] * rect_orig_height[0]]; p_orig_rect_pixels[1] = new uint32[rect_orig_width[1] * rect_orig_height[1]]; p_new_rect_pixels[0] = new uint32[rect_new_width[0] * rect_new_height[0]]; p_new_rect_pixels[1] = new uint32[rect_new_width[1] * rect_new_height[1]]; Mem::Manager::sHandle().PopContext(); // Get the original rectangles s_get_region((uint8 *) p_orig_region_pixels, rect_pos[0][X], rect_pos[0][Y], width, height, (uint8 *) p_orig_rect_pixels[0], rect_orig_width[0], rect_orig_height[0], 32); s_get_region((uint8 *) p_orig_region_pixels, rect_pos[1][X], rect_pos[1][Y], width, height, (uint8 *) p_orig_rect_pixels[1], rect_orig_width[1], rect_orig_height[1], 32); // Scale each rectangle s_scale_texture((uint8 *) p_orig_rect_pixels[0], rect_orig_width[0], rect_orig_height[0], (uint8 *) p_new_rect_pixels[0], rect_new_width[0], rect_new_height[0], 32); s_scale_texture((uint8 *) p_orig_rect_pixels[1], rect_orig_width[1], rect_orig_height[1], (uint8 *) p_new_rect_pixels[1], rect_new_width[1], rect_new_height[1], 32); // Combine two adjacent side lines into 1 (and put into the second rectangle since it will overwrite the first) s_combine_adjacent_borders((uint8 *) p_new_rect_pixels[0], (uint8 *) p_new_rect_pixels[1], rect_new_width[0], rect_new_height[0], rect_new_width[1], first_rect_portion, split_axis, 32); // Put new rectangles back into region s_put_region((uint8 *) p_orig_region_pixels, rect_pos[0][X], rect_pos[0][Y], width, height, (uint8 *) p_new_rect_pixels[0], rect_new_width[0], rect_new_height[0], 32); s_put_region((uint8 *) p_orig_region_pixels, rect_new_pos[X], rect_new_pos[Y], width, height, (uint8 *) p_new_rect_pixels[1], rect_new_width[1], rect_new_height[1], 32); // And put region back into texture blit_32bit_buffer_to_texture(p_orig_region_pixels, x_pos, y_pos, width, height); // Free all the buffers delete [] p_orig_rect_pixels[0]; delete [] p_orig_rect_pixels[1]; delete [] p_new_rect_pixels[0]; delete [] p_new_rect_pixels[1]; delete [] p_orig_region_pixels; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_pull_to_edge(uint16 point, int axis, int num_pixels) { #if PRINT_TIMES static uint32 total_time = 0; uint32 start_time = Tmr::GetTimeInUSeconds(); #endif uint32 *p_texture_buffer = NULL; uint32 *p_scaled_texture_buffer = NULL; uint16 rect_pos[2]; uint16 rect_orig_width; uint16 rect_orig_height; uint16 rect_new_width; uint16 rect_new_height; uint16 scaled_offset; if (num_pixels == 0) { // Nothing to do return true; } // Cap num_pixels on low end if ((point + num_pixels) <= 0) { num_pixels = -point + 1; } Dbg_Assert((point + num_pixels) >= 0); if (axis == X) { Dbg_Assert(point < m_width); // Cap num_pixels on high end if ((point + num_pixels) >= m_width) { num_pixels = m_width - point - 1; } Dbg_Assert((point + num_pixels) < m_width); bool left = (num_pixels < 0); if (left) { // Figure out orig rectangle rect_pos[X] = 0; rect_pos[Y] = 0; rect_orig_width = point + 1; rect_orig_height = m_height; // Figure out new rectangle rect_new_width = point + (-num_pixels) + 1; rect_new_height = m_height; scaled_offset = -num_pixels; } else { // Figure out orig rectangle rect_pos[X] = point; rect_pos[Y] = 0; rect_orig_width = (m_width - point); rect_orig_height = m_height; // Figure out new rectangle rect_new_width = (m_width - point) + num_pixels; rect_new_height = m_height; scaled_offset = 0; } } else { Dbg_Assert(point < m_height); // Cap num_pixels on high end if ((point + num_pixels) >= m_height) { num_pixels = m_height - point - 1; } Dbg_Assert((point + num_pixels) < m_height); bool top = (num_pixels < 0); if (top) { // Figure out orig rectangle rect_pos[X] = 0; rect_pos[Y] = 0; rect_orig_width = m_width; rect_orig_height = point + 1; // Figure out new rectangle rect_new_width = m_width; rect_new_height = point + (-num_pixels) + 1; scaled_offset = -num_pixels * m_width; } else { // Figure out orig rectangle rect_pos[X] = 0; rect_pos[Y] = point; rect_orig_width = m_width; rect_orig_height = (m_height - point); // Figure out new rectangle rect_new_width = m_width; rect_new_height = (m_height - point) + num_pixels; scaled_offset = 0; } } // Copy texture region into temp buffers in 32-bit format if (Mem::Manager::sHandle().CutsceneTopDownHeap()) { // GJ FIX 9/9/03 FOR SK5:TT13114 - "Failing to allocate // face texture memory in cutscenes" // if the cutscene top down heap exists, we want to use // that first, because there won't be much on the // top down heap during cutscenes... Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneTopDownHeap()); } else { Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); } p_texture_buffer = new uint32[rect_orig_width * rect_orig_height]; p_scaled_texture_buffer = new uint32[rect_new_width * rect_new_height]; Mem::Manager::sHandle().PopContext(); //Dbg_Message("Copying region from (%d, %d) of size (%d, %d)", rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height); copy_region_to_32bit_buffer(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height); // Scale the rectangle s_scale_texture((uint8 *) p_texture_buffer, rect_orig_width, rect_orig_height, (uint8 *) p_scaled_texture_buffer, rect_new_width, rect_new_height, 32); //Dbg_Message("Pull: Original size (%d, %d), new size (%d, %d)", rect_orig_width, rect_orig_height, rect_new_width, rect_new_height); // Crop the rectangle and put back into original buffer s_put_region((uint8 *) p_texture_buffer, 0, 0, rect_orig_width, rect_orig_height, (uint8 *) (p_scaled_texture_buffer + scaled_offset), rect_new_width, rect_new_height, 32); // And put region back into texture blit_32bit_buffer_to_texture(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height); // Free buffers if (p_texture_buffer) { delete [] p_texture_buffer; } if (p_scaled_texture_buffer) { delete [] p_scaled_texture_buffer; } #if PRINT_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); total_time += end_time - start_time; Dbg_Message("pull_to_edge Update time %d us; Total Time %d", end_time - start_time, total_time); #endif return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_push_to_point(uint16 point, int axis, int num_pixels, bool use_fill_color, Image::RGBA fill_color) { #if PRINT_TIMES static uint32 total_time = 0; uint32 start_time = Tmr::GetTimeInUSeconds(); #endif uint32 *p_texture_buffer = NULL; uint32 *p_scaled_texture_buffer = NULL; uint16 rect_pos[2]; uint16 rect_new_offset[2]; uint16 rect_orig_width; uint16 rect_orig_height; uint16 rect_new_width; uint16 rect_new_height; if (num_pixels == 0) { // Nothing to do return true; } //Dbg_Message("Moving %d pixels along %d axis starting at %d", num_pixels, axis, point); // Cap num_pixels on low end if ((point + num_pixels) <= 0) { num_pixels = -point + 1; } Dbg_Assert((point + num_pixels) >= 0); if (axis == X) { Dbg_Assert(point < m_width); // Cap num_pixels on high end if ((point + num_pixels) >= m_width) { num_pixels = m_width - point - 1; } Dbg_Assert((point + num_pixels) < m_width); bool left = (num_pixels < 0); if (left) { // Figure out orig rectangle rect_pos[X] = point; rect_pos[Y] = 0; rect_orig_width = (m_width - point); rect_orig_height = m_height; // Figure out new rectangle rect_new_offset[X] = 0; rect_new_offset[Y] = 0; rect_new_width = (m_width - point) - (-num_pixels); rect_new_height = m_height; } else { // Figure out orig rectangle rect_pos[X] = 0; rect_pos[Y] = 0; rect_orig_width = point + 1; rect_orig_height = m_height; // Figure out new rectangle rect_new_offset[X] = num_pixels; rect_new_offset[Y] = 0; rect_new_width = point - num_pixels + 1; rect_new_height = m_height; } } else { Dbg_Assert(point < m_height); // Cap num_pixels on high end if ((point + num_pixels) >= m_height) { num_pixels = m_height - point - 1; } Dbg_Assert((point + num_pixels) < m_height); bool top = (num_pixels < 0); if (top) { // Figure out orig rectangle rect_pos[X] = 0; rect_pos[Y] = point; rect_orig_width = m_width; rect_orig_height = (m_height - point); // Figure out new rectangle rect_new_offset[X] = 0; rect_new_offset[Y] = 0; rect_new_width = m_width; rect_new_height = (m_height - point) - (-num_pixels); } else { // Figure out orig rectangle rect_pos[X] = 0; rect_pos[Y] = 0; rect_orig_width = m_width; rect_orig_height = point + 1; // Figure out new rectangle rect_new_offset[X] = 0; rect_new_offset[Y] = num_pixels; rect_new_width = m_width; rect_new_height = point - num_pixels + 1; } } Dbg_Assert(rect_new_width); Dbg_Assert(rect_new_height); // Copy texture region into temp buffers in 32-bit format Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); p_texture_buffer = new uint32[rect_orig_width * rect_orig_height]; p_scaled_texture_buffer = new uint32[rect_new_width * rect_new_height]; Mem::Manager::sHandle().PopContext(); //Dbg_Message("Copying region from (%d, %d) of size (%d, %d)", rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height); copy_region_to_32bit_buffer(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height); // Scale the rectangle s_scale_texture((uint8 *) p_texture_buffer, rect_orig_width, rect_orig_height, (uint8 *) p_scaled_texture_buffer, rect_new_width, rect_new_height, 32); // Check if we have a fill color if (use_fill_color) { int num_pixels = rect_orig_width * rect_orig_height; // Just brute force it for now // Garrett: This doesn't work since p_texture_buffer is only a subset of the texture Dbg_MsgAssert(0, ("Fill color not implemented properly yet")); for (int i = 0; i < num_pixels; i++) { p_texture_buffer[i] = *((uint32 *) &fill_color); } } // Put smaller rectangle back into original buffer s_put_region((uint8 *) p_texture_buffer, rect_new_offset[X], rect_new_offset[Y], rect_orig_width, rect_orig_height, (uint8 *) p_scaled_texture_buffer, rect_new_width, rect_new_height, 32); // And put region back into texture blit_32bit_buffer_to_texture(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height); // Cover up old pixels if (!use_fill_color) { if (axis == X) { bool use_left = (num_pixels > 0); int scaled_coord = (use_left) ? 0 : rect_new_width - 1; Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); uint32 *p_strip_buffer = new uint32[m_height]; Mem::Manager::sHandle().PopContext(); for (int pidx = 0; pidx < m_height; pidx++) { p_strip_buffer[pidx] = p_scaled_texture_buffer[(pidx * rect_new_width) + scaled_coord]; } int start = (use_left) ? 0 : m_width - (-num_pixels); int end = (use_left) ? num_pixels - 1 : m_width - 1; for (int line = start; line <= end; line++) { blit_32bit_buffer_to_texture(p_strip_buffer, line, 0, 1, m_height); } delete [] p_strip_buffer; } else { bool use_top = (num_pixels > 0); int scaled_coord = (use_top) ? 0 : rect_new_height - 1; Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); uint32 *p_strip_buffer = new uint32[m_width]; Mem::Manager::sHandle().PopContext(); memcpy(p_strip_buffer, &(p_scaled_texture_buffer[scaled_coord * rect_new_width]), m_width * sizeof(uint32)); int start = (use_top) ? 0 : m_height - (-num_pixels); int end = (use_top) ? num_pixels - 1 : m_height - 1; for (int line = start; line <= end; line++) { blit_32bit_buffer_to_texture(p_strip_buffer, 0, line, m_width, 1); } delete [] p_strip_buffer; } } // Free buffers if (p_texture_buffer) { delete [] p_texture_buffer; } if (p_scaled_texture_buffer) { delete [] p_scaled_texture_buffer; } #if PRINT_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); total_time += end_time - start_time; Dbg_Message("push_to_point Update time %d us; Total Time %d", end_time - start_time, total_time); #endif return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_adjust_brightness(float brightness_scale, bool force_adjust_current) { #if PRINT_TIMES static uint32 total_time = 0; uint32 start_time = Tmr::GetTimeInUSeconds(); #endif Dbg_Assert(mp_single_texture); Dbg_MsgAssert(mp_cur_temp_32bit_image, ("For now, AdjustBrightness() only supports 32-bit mode")); Image::RGBA *p_src_pixels; Image::RGBA *p_dst_pixels = (Image::RGBA *) mp_cur_temp_32bit_image; // Figure out the source if (mp_orig_temp_32bit_image && !force_adjust_current) { p_src_pixels = (Image::RGBA *) mp_orig_temp_32bit_image; } else { p_src_pixels = (Image::RGBA *) mp_cur_temp_32bit_image; } // Adjust each pixel int num_pixels = m_width * m_height; for (int i = 0; i < num_pixels; i++, p_src_pixels++, p_dst_pixels++) { p_dst_pixels->r = (uint8) min(p_src_pixels->r * brightness_scale, 255); p_dst_pixels->g = (uint8) min(p_src_pixels->g * brightness_scale, 255); p_dst_pixels->b = (uint8) min(p_src_pixels->b * brightness_scale, 255); } #if PRINT_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); total_time += end_time - start_time; Dbg_Message("adjust_brightness(%f) Update time %d us; Total Time %d", brightness_scale, end_time - start_time, total_time); #endif return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_adjust_hsv(float h, float s, float v, bool force_adjust_current) { #if PRINT_TIMES static uint32 total_time = 0; uint32 start_time = Tmr::GetTimeInUSeconds(); #endif Dbg_Assert(mp_single_texture); Dbg_MsgAssert(mp_cur_temp_32bit_image, ("For now, AdjustHSV() only supports 32-bit mode")); Dbg_MsgAssert((h >= 0.0f) && (h <= 360.0f), ("h is out of range: %f", h)); Dbg_MsgAssert(s >= 0.0f, ("s is negative: %f", s)); Dbg_MsgAssert(v >= 0.0f, ("v is negative: %f", v)); Image::RGBA *p_src_pixels; Image::RGBA *p_dst_pixels = (Image::RGBA *) mp_cur_temp_32bit_image; // Figure out the source if (mp_orig_temp_32bit_image && !force_adjust_current) { p_src_pixels = (Image::RGBA *) mp_orig_temp_32bit_image; } else { p_src_pixels = (Image::RGBA *) mp_cur_temp_32bit_image; } // Adjust each pixel float pixel_h, pixel_s, pixel_v; float pixel_r, pixel_g, pixel_b; int num_pixels = m_width * m_height; for (int i = 0; i < num_pixels; i++, p_src_pixels++, p_dst_pixels++) { // Convert Gfx::inlineRGBtoHSV(p_src_pixels->r / 255.0f, p_src_pixels->g / 255.0f, p_src_pixels->b / 255.0f, pixel_h, pixel_s, pixel_v); // Adjust pixel_h += h; if (pixel_h > 360.0f) { pixel_h -= 360.0f; } pixel_s = Mth::Min(pixel_s * s, 1.0f); pixel_v = Mth::Min(pixel_v * v, 1.0f); // Convert back Gfx::inlineHSVtoRGB(pixel_r, pixel_g, pixel_b, pixel_h, pixel_s, pixel_v); p_dst_pixels->r = (unsigned char)( pixel_r * 255.0f + 0.5f ); p_dst_pixels->g = (unsigned char)( pixel_g * 255.0f + 0.5f ); p_dst_pixels->b = (unsigned char)( pixel_b * 255.0f + 0.5f ); } #if PRINT_TIMES uint32 end_time = Tmr::GetTimeInUSeconds(); total_time += end_time - start_time; Dbg_Message("adjust_hsv(%f) Update time %d us; Total Time %d", s, end_time - start_time, total_time); #endif return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_add_to_vram() { Dbg_MsgAssert(mp_single_texture, ("CPs2Texture::plat_add_to_vram() only works for sSingleTexture types")); return mp_single_texture->AddToVRAM(); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_remove_from_vram() { Dbg_MsgAssert(mp_single_texture, ("CPs2Texture::plat_remove_from_vram() only works for sSingleTexture types")); return mp_single_texture->RemoveFromVRAM(); } /******************************************************************/ /* */ /* */ /******************************************************************/ uint16 CPs2Texture::plat_get_width() const { return m_width; } /******************************************************************/ /* */ /* */ /******************************************************************/ uint16 CPs2Texture::plat_get_height() const { return m_height; } /******************************************************************/ /* */ /* */ /******************************************************************/ uint8 CPs2Texture::plat_get_bitdepth() const { return m_bitdepth; } /******************************************************************/ /* */ /* */ /******************************************************************/ uint8 CPs2Texture::plat_get_palette_bitdepth() const { return m_clut_bitdepth; } /******************************************************************/ /* */ /* */ /******************************************************************/ uint8 CPs2Texture::plat_get_num_mipmaps() const { return m_num_mipmaps; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_is_transparent() const { return m_transparent; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2Texture::plat_combine_textures(CTexture *p_texture, bool palette_gen) { CPs2Texture *p_ps2_texture = static_cast(p_texture); // Make sure both textures have same attributes Dbg_Assert(m_width == p_ps2_texture->m_width); Dbg_Assert(m_height == p_ps2_texture->m_height); Dbg_Assert(m_bitdepth == p_ps2_texture->m_bitdepth); Dbg_Assert(m_clut_bitdepth == p_ps2_texture->m_clut_bitdepth); Dbg_Assert((m_bitdepth == 8) || (m_bitdepth == 32)); // Maybe support more later Dbg_Assert(mp_single_texture); Dbg_Assert(p_ps2_texture->mp_single_texture); bool paletted = (m_bitdepth <= 8); uint32 *p_texture1_buffer = NULL; uint32 *p_texture2_buffer = NULL; Image::RGBA *p_src; Image::RGBA *p_dst; if (paletted) { // Try temp 32-bit images first p_dst = (Image::RGBA *) mp_cur_temp_32bit_image; p_src = (Image::RGBA *) p_ps2_texture->mp_cur_temp_32bit_image; Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); // Copy original textures iton temp buffers in 32-bit format if (!p_dst) { p_texture1_buffer = new uint32[m_width * m_height]; copy_region_to_32bit_buffer(p_texture1_buffer, 0, 0, m_width, m_height); p_dst = (Image::RGBA *) p_texture1_buffer; } if (!p_src) { p_texture2_buffer = new uint32[p_ps2_texture->m_width * p_ps2_texture->m_height]; p_ps2_texture->copy_region_to_32bit_buffer(p_texture2_buffer, 0, 0, p_ps2_texture->m_width, p_ps2_texture->m_height); p_src = (Image::RGBA *) p_texture2_buffer; } Mem::Manager::sHandle().PopContext(); } else { p_src = (Image::RGBA *) p_ps2_texture->mp_single_texture->GetTextureBuffer(); p_dst = (Image::RGBA *) mp_single_texture->GetTextureBuffer(); } // Do the actual alpha blend uint8 om_src_alpha; int size = m_width * m_height; for (int i = 0; i < size; i++, p_src++, p_dst++) { om_src_alpha = 0x80 - p_src->a; p_dst->r = (uint8) ( ( ((int) p_src->r * p_src->a) + ((int) p_dst->r * om_src_alpha) ) >> 7 /* divide by 0x80 */); p_dst->g = (uint8) ( ( ((int) p_src->g * p_src->a) + ((int) p_dst->g * om_src_alpha) ) >> 7 /* divide by 0x80 */); p_dst->b = (uint8) ( ( ((int) p_src->b * p_src->a) + ((int) p_dst->b * om_src_alpha) ) >> 7 /* divide by 0x80 */); p_dst->a = max(p_src->a, p_dst->a); // Choose the highest alpha (since we don't want solid pixels to become transparent) } // Go back to original bitdepth if necessary if (paletted && !mp_cur_temp_32bit_image) { Dbg_Assert(m_clut_bitdepth == 32); // Generate a new palette if (palette_gen) { GeneratePalette((Image::RGBA *) mp_single_texture->GetClutBuffer(), (uint8 *) p_texture1_buffer, m_width, m_height, 32, 1 << m_bitdepth); } // And repalettize and put back into texture blit_32bit_buffer_to_texture(p_texture1_buffer, 0, 0, m_width, m_height); } // Free buffers if (p_texture1_buffer) { delete [] p_texture1_buffer; } if (p_texture2_buffer) { delete [] p_texture2_buffer; } return false; } //////////////////////////////////////////////////////////////////////////////////// // Here's a machine specific implementation of CTexDict /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2TexDict::CPs2TexDict(uint32 checksum) : CTexDict(checksum) { // Load nothing } /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2TexDict::CPs2TexDict(const char *p_tex_dict_name, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup) : CTexDict(p_tex_dict_name, !is_level_data) { LoadTextureDictionary(p_tex_dict_name, NULL, 0, is_level_data, texDictOffset, isSkin, forceTexDictLookup); // the derived class will does this } /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2TexDict::~CPs2TexDict() { UnloadTextureDictionary(); // the derived class does this } /******************************************************************/ /* */ /* */ /******************************************************************/ NxPs2::sScene * CPs2TexDict::GetEngineTextureDictionary() const { //printf( "Returning scene from GetEngineTextureDictionary %08x\n", (uint32)mp_tex_dict ); return mp_tex_dict; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2TexDict::add_textures_to_hash_table() { for (int i = 0; i < mp_tex_dict->NumTextures; i++) { // Make sure we don't add duplicate textures. We can have the same texture in two // different texture groups (for sorting purposes). This will break texture // replacement, though. if (!GetTexture(mp_tex_dict->pTextures[i].Checksum)) { CPs2Texture *p_texture = new CPs2Texture; p_texture->m_checksum = mp_tex_dict->pTextures[i].Checksum; p_texture->m_width = mp_tex_dict->pTextures[i].GetWidth(); p_texture->m_height = mp_tex_dict->pTextures[i].GetHeight(); p_texture->m_bitdepth = mp_tex_dict->pTextures[i].GetBitdepth(); p_texture->m_clut_bitdepth = mp_tex_dict->pTextures[i].GetClutBitdepth(); p_texture->m_num_mipmaps = mp_tex_dict->pTextures[i].GetNumMipmaps(); p_texture->mp_group_texture = &(mp_tex_dict->pTextures[i]); AddTexture(p_texture); } } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2TexDict::LoadTextureDictionary(const char *p_tex_dict_name, uint32* pData, int dataSize, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup) { // set up the texture dictionary's main parameters bool IsSkin; bool IsInstanceable; bool UsesPip; if ( is_level_data ) { // for level data IsSkin = false; IsInstanceable = false; UsesPip = true; } else if ( !isSkin && texDictOffset==0 ) { // for non-skinned models (unless they // do texture replacement, such as the // boards in the skateshop do) IsSkin = false; IsInstanceable = false; UsesPip = true; } else { // for all skinned models, and for // non-skinned models that do // texture replacement) IsSkin = true; IsInstanceable = true; UsesPip = false; } if ( !is_level_data && isSkin ) { // add all skinned model textures // to the hash table forceTexDictLookup = true; } if ( p_tex_dict_name ) { // either the filename OR the data pointer should have been specified, but not both Dbg_MsgAssert( !pData, ( "You can't specify both a filename %s AND a data pointer", p_tex_dict_name ) ) // p_tex_dict_name is assumed to be the full path name of the texture dictionary mp_tex_dict = NxPs2::LoadTextures(p_tex_dict_name, IsSkin, IsInstanceable, UsesPip, texDictOffset, &m_file_size ); Dbg_Assert( mp_tex_dict ); } else { Dbg_MsgAssert( pData, ( "No data pointer specified" ) ); m_file_size = dataSize; mp_tex_dict = NxPs2::LoadTextures(pData, dataSize, IsSkin, IsInstanceable, UsesPip, texDictOffset ); Dbg_Assert( mp_tex_dict ); } // Add textures to hash table (just do it for non-level dictionaries now) // should this go on bottom up heap? if ( forceTexDictLookup ) { add_textures_to_hash_table(); } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2TexDict::UnloadTextureDictionary() { NxPs2::DeleteTextures(mp_tex_dict); return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ CTexture * CPs2TexDict::plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram) { CPs2Texture *p_texture = new CPs2Texture; if (!p_texture->LoadTexture(p_texture_name, sprite, alloc_vram)) { Dbg_Error("Can't load texture %s", p_texture_name); } return p_texture; } /******************************************************************/ /* */ /* */ /******************************************************************/ CTexture * CPs2TexDict::plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram) { CPs2Texture *p_texture = new CPs2Texture; if (!p_texture->LoadTextureFromBuffer(p_buffer, buffer_size, texture_checksum, sprite, alloc_vram)) { Dbg_MsgAssert(0, ("Can't load texture from buffer %s", Script::FindChecksumName(texture_checksum))); } return p_texture; } /******************************************************************/ /* */ /* */ /******************************************************************/ CTexture * CPs2TexDict::plat_reload_texture(const char *p_texture_name) { return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2TexDict::plat_unload_texture(CTexture *p_texture) { delete p_texture; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2TexDict::plat_add_texture(CTexture *p_texture) { } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPs2TexDict::plat_remove_texture(CTexture *p_texture) { return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ CTexture * CPs2TexDict::plat_copy_texture(uint32 new_texture_checksum, CTexture *p_texture) { CPs2Texture *p_ps2_texture = static_cast(p_texture); return new CPs2Texture(*p_ps2_texture); } } // Namespace Nx