using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenVIII
{
public abstract class Texture_Base
{
#region Fields
private const ushort blue_mask = 0x7C00;
private const ushort green_mask = 0x3E0;
///
/// all bits except the STP bit.
///
private const ushort NotSTP_mask = 0x7FFF;
private const ushort red_mask = 0x1F;
///
/// Special Transparency Processing bit.
///
///
/// The STP (special transparency processing) bit has varying special meanings. Depending on
/// the current transparency processing mode, it denotes if pixels of this color should be
/// treated as transparent or not. If transparency processing is enabled, pixels of this
/// color will be rendered transparent if the STP bit is set. A special case is black pixels
/// (RGB 0,0,0), which by default are treated as transparent by the PlayStation unless the
/// STP bit is set.
///
///
///
private const ushort STP_mask = 0x8000;
#endregion Fields
//#region Enums
//public enum TextureType : byte
//{
// TEX,
// TIM,
// Texture2D,
// PNG
//}
//#endregion Enums
#region Properties
public abstract byte GetBytesPerPixel { get; }
public abstract int GetClutCount { get; }
public abstract int GetClutSize { get; }
public abstract int GetColorsCountPerPalette { get; }
public abstract int GetHeight { get; }
public abstract int GetOrigX { get; }
public abstract int GetOrigY { get; }
public abstract int GetWidth { get; }
#endregion Properties
#region Methods
///
/// Convert ABGR1555 color to RGBA 32bit color
///
///
/// FromPsColor from TEX does the same thing I think. Unsure which is better. I like the masks
///
public static Color ABGR1555toRGBA32bit(ushort pixel, bool ignoreAlpha = false)
{
// alert to let me know if we might need to check something.
if (pixel == 0 && !ignoreAlpha) return Color.TransparentBlack;
//https://docs.microsoft.com/en-us/windows/win32/directshow/working-with-16-bit-rgb
// had the masks. though they were doing rgb but we are doing bgr so i switched red and blue.
var ret = new Color
{
R = (byte)MathHelper.Clamp((pixel & red_mask) << 3, 0, 255),
G = (byte)MathHelper.Clamp(((pixel & green_mask) >> 5) << 3, 0, 255),
B = (byte)MathHelper.Clamp(((pixel & blue_mask) >> 10) << 3, 0, 255),
A = 255
};
//if (GetSTP(pixel))
//{
// //ret.A = Transparency.GetAlpha;
// Debug.WriteLine($"Special Transparency Processing bit set 16 bit color: {pixel & 0x7FFF}, 32 bit color: {ret}");
// //Debug.Assert(false);
//}
//TDW has STP
return ret;
}
///
/// Custom Convert 16BIT color to RGBA 32bit color
///
///
/// TEX file actually has it's own masks and shift varibles. if these are honor'ed could have
/// the 16 bit color in any order. Though I think they are all the same. Unsure if these
/// values also work for 32 bit. or 24 bit. (Needs testing and Tex needs to be adjusted
/// to read those values, they are in the header)
///
public static Color Custom_16BITtoRGBA32bit(ushort pixel, ushort c_red_mask, ushort c_red_shift, ushort c_green_mask, ushort c_green_shift, ushort c_blue_mask, ushort c_blue_shift, bool ignoreAlpha = false)
{
// alert to let me know if we might need to check something.
if (pixel == 0 && !ignoreAlpha) return Color.TransparentBlack;
//https://docs.microsoft.com/en-us/windows/win32/directshow/working-with-16-bit-rgb
// had the masks. though they were doing rgb but we are doing bgr so i switched red and blue.
var ret = new Color
{
R = (byte)MathHelper.Clamp(((pixel & c_red_mask) >> c_red_shift) << 3, 0, 255),
G = (byte)MathHelper.Clamp(((pixel & c_green_mask) >> c_green_shift) << 3, 0, 255),
B = (byte)MathHelper.Clamp(((pixel & c_blue_mask) >> c_blue_shift) << 3, 0, 255),
A = 255
};
return ret;
}
///
/// Get Special Transparency Processing bit.
///
/// 16 bit color
/// true if bit is set and not black, otherwise false.
public static bool GetSTP(ushort pixel) =>
// I set this to always return false for black. As it's transparency is handled in
// conversion method.
(pixel & NotSTP_mask) == 0 ? false : (pixel & STP_mask) != 0;
public static Texture_Base Open(byte[] buffer, uint offset = 0)
{
switch (BitConverter.ToUInt32(buffer, (int)(0 + offset)))
{
case 0x1:
case 0x2:
return new TEX(buffer);
case 0x10:
return new TIM2(buffer, 0);
default:
return null;
}
}
public abstract void ForceSetClutColors(ushort newNumOfColors);
public abstract void ForceSetClutCount(ushort newClut);
public abstract Color[] GetClutColors(ushort clut);
public Texture2D GetTexture(Dictionary colorOverride, sbyte clut = -1)
{
if (colorOverride == null || colorOverride.Count == 0) return null;
var colors = clut > 0 && clut < GetClutCount ? GetClutColors((ushort)clut) : new Color[GetColorsCountPerPalette];
if (colors == null) return null;
colorOverride.Where(x => x.Key < colors.Length).ForEach(x => colors[x.Key] = x.Value);
return GetTexture((colors));
}
public abstract Texture2D GetTexture(Color[] colors);
public abstract Texture2D GetTexture(ushort clut);
public virtual Texture2D GetTexture() => GetTexture(GetClutColors(0));
public abstract void Load(byte[] buffer, uint offset = 0);
public abstract void Save(string path);
public abstract void SaveCLUT(string path);
public virtual void SavePNG(string path, short clut = -1)
{ }
#endregion Methods
//public static class Transparency
//{
// ///
// /// PSX Transparency Mode
// ///
// /// GPU first reads the pixel it wants to write to, and then calculates the color it will
// /// write from the 2 pixels according to the semi-transparency mode selected
// ///
// /// OFF is 100% alpha
// /// Mode 1 is 50%
// /// Mode 2 is supposed to be existing pixel plus value of new one.
// /// Mode 3 is supposed to be existing pixel minus value of new one.
// /// Mode 4 is 25%
// /// Unsure if the pixel is supposed to be 50% alpha before this
// ///
// ///
// //public static readonly byte[] Modes = { 0xFF 0x7F, 0xFF, 0x7F, 0x3F }; // if 100% before calculation
// public static readonly byte[] Modes = { 0xFF, 0x3F, 0x7F, 0x7F, 0x1F }; // if 50% before calculation
//If color is not black and STP bit on, possible semi-transparency.
//http://www.raphnet.net/electronique/psx_adaptor/Playstation.txt
//4 modes psx could use for Semi-Transparency. I donno which one ff8 uses or if it uses only one.
//If Black is transparent when bit is off. And visible when bit is on.
/////
///// Ratio needed to convert 16 bit to 32 bit color
/////
/////
//public const double COEFF_COLOR = (double)255 / 31;
/////
///// converts 16 bit color to 32bit with alpha. alpha needs to be set true manually per pixel
///// unless you know the color value.
/////
///// 16 bit color
///// area is Visible or not
///// byte[4] red green blue alpha, i think
/////
//public static Color FromPsColor(ushort color, bool useAlpha = false) => new Color((byte)Math.Round((color & 31) * COEFF_COLOR), (byte)Math.Round(((color >> 5) & 31) * COEFF_COLOR), (byte)Math.Round(((color >> 10) & 31) * COEFF_COLOR), (byte)(color == 0 && useAlpha ? 0 : 255));
}
}