private void Decode_COMPRESSED_DXT1() { var colors = new OutputPixel[4]; for (int y = 0, ni = 0; y < _height; y += 4) { for (int x = 0; x < _width; x += 4, ni++) { var block = ((Dxt1Block *)_inputByte)[ni]; colors[0] = Decode_RGBA_5650_Pixel(block.Color0) .Transform((r, g, b, a) => OutputPixel.FromRgba(b, g, r, a)); colors[1] = Decode_RGBA_5650_Pixel(block.Color1) .Transform((r, g, b, a) => OutputPixel.FromRgba(b, g, r, a)); if (block.Color0 > block.Color1) { colors[2] = OutputPixel.OperationPerComponent(colors[0], colors[1], (a, b) => (byte)(a * 2 / 3 + b * 1 / 3)); colors[3] = OutputPixel.OperationPerComponent(colors[0], colors[1], (a, b) => (byte)(a * 1 / 3 + b * 2 / 3)); } else { colors[2] = OutputPixel.OperationPerComponent(colors[0], colors[1], (a, b) => (byte)(a * 1 / 2 + b * 1 / 2)); colors[3] = OutputPixel.FromRgba(0, 0, 0, 0); } var no = 0; for (var y2 = 0; y2 < 4; y2++) { for (var x2 = 0; x2 < 4; x2++, no++) { var color = (block.ColorLookup >> (2 * no)) & 0x3; var rx = x + x2; var ry = y + y2; var n = ry * _width + rx; _output[n] = colors[color]; } } } } }
/// <summary> /// DXT2 and DXT3 (collectively also known as Block Compression 2 or BC2) converts 16 input pixels /// (corresponding to a 4x4 pixel block) into 128 bits of output, consisting of 64 bits of alpha channel data /// (4 bits for each pixel) followed by 64 bits of color data, encoded the same way as DXT1 (with the exception /// that the 4 color version of the DXT1 algorithm is always used instead of deciding which version to use based /// on the relative values of and ). In DXT2, the color data is interpreted as being premultiplied by alpha, in /// DXT3 it is interpreted as not having been premultiplied by alpha. Typically DXT2/3 are well suited to images /// with sharp alpha transitions, between translucent and opaque areas. /// </summary> private void Decode_COMPRESSED_DXT3() { var colors = new OutputPixel[4]; var ni = 0; for (var y = 0; y < _height; y += 4) { for (var x = 0; x < _width; x += 4, ni++) { var block = ((Dxt3Block *)_inputByte)[ni]; colors[0] = Decode_RGBA_5650_Pixel(block.Color0) .Transform((r, g, b, a) => OutputPixel.FromRgba(b, g, r, a)); colors[1] = Decode_RGBA_5650_Pixel(block.Color1) .Transform((r, g, b, a) => OutputPixel.FromRgba(b, g, r, a)); colors[2] = OutputPixel.OperationPerComponent(colors[0], colors[1], (a, b) => (byte)(((a * 2) / 3) + ((b * 1) / 3))); colors[3] = OutputPixel.OperationPerComponent(colors[0], colors[1], (a, b) => (byte)(((a * 1) / 3) + ((b * 2) / 3))); var no = 0; for (var y2 = 0; y2 < 4; y2++) { for (var x2 = 0; x2 < 4; x2++, no++) { var alpha = (block.Alpha >> (4 * no)) & 0xF; var color = (block.ColorLookup >> (2 * no)) & 0x3; var rx = (x + x2); var ry = (y + y2); var n = ry * _width + rx; _output[n] = colors[color]; _output[n].A = (byte)((alpha * 0xFF) / 0xF); } } } } }
private void Decode_COMPRESSED_DXT5() { //Console.Error.WriteLine("Not Implemented: Decode_COMPRESSED_DXT5"); //throw new NotImplementedException(); //_Decode_Unimplemented(); var colors = new OutputPixel[4]; var ni = 0; for (var y = 0; y < _height; y += 4) { for (var x = 0; x < _width; x += 4, ni++) { var block = ((Dxt5Block *)_inputByte)[ni]; colors[0] = Decode_RGBA_5650_Pixel(block.Color0) .Transform((r, g, b, a) => OutputPixel.FromRgba(b, g, r, a)); colors[1] = Decode_RGBA_5650_Pixel(block.Color1) .Transform((r, g, b, a) => OutputPixel.FromRgba(b, g, r, a)); colors[2] = OutputPixel.OperationPerComponent(colors[0], colors[1], (a, b) => (byte)(a * 2 / 3 + b * 1 / 3)); colors[3] = OutputPixel.OperationPerComponent(colors[0], colors[1], (a, b) => (byte)(a * 1 / 3 + b * 2 / 3)); // Create Alpha Lookup var alphaLookup = new byte[8]; var alphas = (ushort)(block.Alpha >> 48); var alpha0 = (byte)((alphas >> 0) & 0xFF); var alpha1 = (byte)((alphas >> 8) & 0xFF); alphaLookup[0] = alpha0; alphaLookup[1] = alpha1; if (alpha0 > alpha1) { alphaLookup[2] = (byte)((6 * alpha0 + alpha1) / 7); alphaLookup[3] = (byte)((5 * alpha0 + 2 * alpha1) / 7); alphaLookup[4] = (byte)((4 * alpha0 + 3 * alpha1) / 7); alphaLookup[5] = (byte)((3 * alpha0 + 4 * alpha1) / 7); alphaLookup[6] = (byte)((2 * alpha0 + 5 * alpha1) / 7); alphaLookup[7] = (byte)((alpha0 + 6 * alpha1) / 7); } else { alphaLookup[2] = (byte)((4 * alpha0 + alpha1) / 5); alphaLookup[3] = (byte)((3 * alpha0 + 2 * alpha1) / 5); alphaLookup[4] = (byte)((2 * alpha0 + 3 * alpha1) / 5); alphaLookup[5] = (byte)((alpha0 + 4 * alpha1) / 5); alphaLookup[6] = (byte)0x00; alphaLookup[7] = (byte)0xFF; } var no = 0; for (var y2 = 0; y2 < 4; y2++) { for (var x2 = 0; x2 < 4; x2++, no++) { var alpha = alphaLookup[(block.Alpha >> (3 * no)) & 0x7]; var color = (block.ColorLookup >> (2 * no)) & 0x3; var rx = x + x2; var ry = y + y2; var n = ry * _width + rx; _output[n] = colors[color]; _output[n].A = alpha; } } } } }
public TTexture Get(GpuStateStruct GpuState) { var TextureMappingState = GpuState.TextureMappingState; var ClutState = TextureMappingState.ClutState; var TextureState = TextureMappingState.TextureState; TTexture Texture; //GC.Collect(); bool Swizzled = TextureState.Swizzled; uint TextureAddress = TextureState.Mipmap0.Address; uint ClutAddress = ClutState.Address; var ClutFormat = ClutState.PixelFormat; var ClutStart = ClutState.Start; var ClutDataStart = PixelFormatDecoder.GetPixelsSize(ClutFormat, ClutStart); ulong Hash1 = TextureAddress | (ulong)((ClutAddress + ClutDataStart) << 32); bool Recheck = false; if (Cache.TryGetValue(Hash1, out Texture)) { if (Texture.RecheckTimestamp != RecheckTimestamp) { Recheck = true; } } else { Recheck = true; } if (Recheck) { //Console.Write("."); //Console.WriteLine("{0:X}", ClutAddress); var TextureFormat = TextureState.PixelFormat; //var Width = TextureState->Mipmap0.TextureWidth; int BufferWidth = TextureState.Mipmap0.BufferWidth; // FAKE! //BufferWidth = TextureState->Mipmap0.TextureWidth; var Height = TextureState.Mipmap0.TextureHeight; var TextureDataSize = PixelFormatDecoder.GetPixelsSize(TextureFormat, BufferWidth * Height); if (ClutState.NumberOfColors > 256) { ClutState.NumberOfColors = 256; } var ClutDataSize = PixelFormatDecoder.GetPixelsSize(ClutFormat, ClutState.NumberOfColors); var ClutCount = ClutState.NumberOfColors; var ClutShift = ClutState.Shift; var ClutMask = ClutState.Mask; //Console.WriteLine(TextureFormat); // INVALID TEXTURE if (!PspMemory.IsRangeValid(TextureAddress, TextureDataSize) || TextureDataSize > 2048 * 2048 * 4) { Console.Error.WriteLineColored(ConsoleColor.DarkRed, "UPDATE_TEXTURE(TEX={0},CLUT={1}:{2}:{3}:{4}:0x{5:X},SIZE={6}x{7},{8},Swizzled={9})", TextureFormat, ClutFormat, ClutCount, ClutStart, ClutShift, ClutMask, BufferWidth, Height, BufferWidth, Swizzled); Console.Error.WriteLineColored(ConsoleColor.DarkRed, "Invalid TEXTURE! TextureAddress=0x{0:X}, TextureDataSize={1}", TextureAddress, TextureDataSize); if (InvalidTexture == null) { InvalidTexture = new TTexture(); InvalidTexture.Init(GpuImpl); int InvalidTextureWidth = 2, InvalidTextureHeight = 2; int InvalidTextureSize = InvalidTextureWidth * InvalidTextureHeight; var Data = new OutputPixel[InvalidTextureSize]; fixed(OutputPixel *DataPtr = Data) { var Color1 = OutputPixel.FromRgba(0xFF, 0x00, 0x00, 0xFF); var Color2 = OutputPixel.FromRgba(0x00, 0x00, 0xFF, 0xFF); for (int n = 0; n < InvalidTextureSize; n++) { DataPtr[n] = (n & 1) != 0 ? Color1 : Color2; } InvalidTexture.SetData(Data, InvalidTextureWidth, InvalidTextureHeight); } } return(InvalidTexture); } //Console.WriteLine("TextureAddress=0x{0:X}, TextureDataSize=0x{1:X}", TextureAddress, TextureDataSize); byte *TexturePointer = null; byte *ClutPointer = null; try { TexturePointer = (byte *)PspMemory.PspAddressToPointerSafe(TextureAddress); ClutPointer = (byte *)PspMemory.PspAddressToPointerSafe(ClutAddress); } catch (PspMemory.InvalidAddressException InvalidAddressException) { throw InvalidAddressException; } TextureCacheKey TextureCacheKey = new TextureCacheKey() { TextureAddress = TextureAddress, TextureFormat = TextureFormat, TextureHash = FastHash(TexturePointer, TextureDataSize), ClutHash = FastHash(&ClutPointer[ClutDataStart], ClutDataSize), ClutAddress = ClutAddress, ClutFormat = ClutFormat, ClutStart = ClutStart, ClutShift = ClutShift, ClutMask = ClutMask, Swizzled = Swizzled, ColorTestEnabled = GpuState.ColorTestState.Enabled, ColorTestRef = GpuState.ColorTestState.Ref, ColorTestMask = GpuState.ColorTestState.Mask, ColorTestFunction = GpuState.ColorTestState.Function, }; if (Texture == null || !Texture.TextureCacheKey.Equals(TextureCacheKey)) { string TextureName = "texture_" + TextureCacheKey.TextureHash + "_" + TextureCacheKey.ClutHash + "_" + TextureFormat + "_" + ClutFormat + "_" + BufferWidth + "x" + Height + "_" + Swizzled; #if DEBUG_TEXTURE_CACHE Console.Error.WriteLine("UPDATE_TEXTURE(TEX={0},CLUT={1}:{2}:{3}:{4}:0x{5:X},SIZE={6}x{7},{8},Swizzled={9})", TextureFormat, ClutFormat, ClutCount, ClutStart, ClutShift, ClutMask, BufferWidth, Height, BufferWidth, Swizzled); #endif Texture = new TTexture(); Texture.Init(GpuImpl); Texture.TextureCacheKey = TextureCacheKey; //Texture.Hash = Hash1; { //int TextureWidth = Math.Max(BufferWidth, Height); //int TextureHeight = Math.Max(BufferWidth, Height); int TextureWidth = BufferWidth; int TextureHeight = Height; int TextureWidthHeight = TextureWidth * TextureHeight; fixed(OutputPixel *TexturePixelsPointer = DecodedTextureBuffer) { if (Swizzled) { fixed(byte *SwizzlingBufferPointer = SwizzlingBuffer) { PointerUtils.Memcpy(SwizzlingBuffer, TexturePointer, TextureDataSize); PixelFormatDecoder.UnswizzleInline(TextureFormat, (void *)SwizzlingBufferPointer, BufferWidth, Height); PixelFormatDecoder.Decode( TextureFormat, (void *)SwizzlingBufferPointer, TexturePixelsPointer, BufferWidth, Height, ClutPointer, ClutFormat, ClutCount, ClutStart, ClutShift, ClutMask, strideWidth: PixelFormatDecoder.GetPixelsSize(TextureFormat, TextureWidth) ); } } else { PixelFormatDecoder.Decode( TextureFormat, (void *)TexturePointer, TexturePixelsPointer, BufferWidth, Height, ClutPointer, ClutFormat, ClutCount, ClutStart, ClutShift, ClutMask, strideWidth: PixelFormatDecoder.GetPixelsSize(TextureFormat, TextureWidth) ); } if (TextureCacheKey.ColorTestEnabled) { byte EqualValue, NotEqualValue; switch (TextureCacheKey.ColorTestFunction) { case ColorTestFunctionEnum.GuAlways: EqualValue = 0xFF; NotEqualValue = 0xFF; break; case ColorTestFunctionEnum.GuNever: EqualValue = 0x00; NotEqualValue = 0x00; break; case ColorTestFunctionEnum.GuEqual: EqualValue = 0xFF; NotEqualValue = 0x00; break; case ColorTestFunctionEnum.GuNotequal: EqualValue = 0x00; NotEqualValue = 0xFF; break; default: throw new NotImplementedException(); } ConsoleUtils.SaveRestoreConsoleState(() => { Console.BackgroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Yellow; Console.Error.WriteLine("{0} : {1}, {2} : ref:{3} : mask:{4}", TextureCacheKey.ColorTestFunction, EqualValue, NotEqualValue, TextureCacheKey.ColorTestRef, TextureCacheKey.ColorTestMask); }); for (int n = 0; n < TextureWidthHeight; n++) { if ((TexturePixelsPointer[n] & TextureCacheKey.ColorTestMask).Equals( TextureCacheKey.ColorTestRef & TextureCacheKey.ColorTestMask)) { TexturePixelsPointer[n].A = EqualValue; } else { TexturePixelsPointer[n].A = NotEqualValue; } if (TexturePixelsPointer[n].A == 0) { //Console.Write("yup!"); } } } var TextureInfo = new TextureHookInfo() { TextureCacheKey = TextureCacheKey, Data = DecodedTextureBuffer, Width = TextureWidth, Height = TextureHeight }; MessageBus.Dispatch(TextureInfo); var Result = Texture.SetData(TextureInfo.Data, TextureInfo.Width, TextureInfo.Height); } } if (Cache.ContainsKey(Hash1)) { Cache[Hash1].Dispose(); } Cache[Hash1] = Texture; } } Texture.RecheckTimestamp = RecheckTimestamp; return(Texture); }