PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Silvermoon/Silvermoon/OpenGL/Texture.cs

#
C# | 526 lines | 380 code | 94 blank | 52 comment | 43 complexity | 4a6814ccc92bf958e89d8e5ea88f91f2 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. using System.Drawing;
  7. using Silvermoon.Drawing;
  8. using Silvermoon.OpenGL.Native;
  9. using Format = Silvermoon.OpenGL.Native.GlesPixelFormat;
  10. using ValueType = Silvermoon.OpenGL.Native.glValueType;
  11. using System.Drawing.Imaging;
  12. using System.Diagnostics;
  13. using System.Threading;
  14. using System.Reflection;
  15. namespace Silvermoon.OpenGL
  16. {
  17. public class Texture : IDisposable
  18. {
  19. const int one = 65536;
  20. #region statics
  21. public static void Cleanup()
  22. {
  23. }
  24. #endregion
  25. #region Fields
  26. private static int maxTextureSize;
  27. private int[] textCoords = new int[] { one, 0, one, -one, 0, 0, 0, -one };
  28. private static IImagingFactory imageFactory;
  29. #endregion
  30. #region ctor
  31. public Texture()
  32. : base()
  33. {
  34. //Textures.Add(this);
  35. this.Name = gl.GenTexture();
  36. if (this.Name == 0) throw new OpenGlException("glGenTexture.");
  37. }
  38. public virtual void Dispose()
  39. {
  40. GC.SuppressFinalize(this);
  41. if (Name != 0)
  42. {
  43. gl.DeleteTexture(Name);
  44. }
  45. Name = 0;
  46. }
  47. ~Texture()
  48. {
  49. Dispose();
  50. }
  51. #endregion
  52. #region Properties
  53. /// <summary>
  54. /// When set to false, each color in the texture is multiplied with the color value of a shape with the formula value=(textureValue*colorValue)/255,
  55. /// if set to true, each color in the texture is considered relative from color (128,128,128). which means that a color in the texture
  56. /// is calculated as value = (textureValue-128)*colorValue/128.
  57. /// </summary>
  58. public bool IsSigned { get; set; }
  59. /// <summary>
  60. /// Gets the maximum size for textures. This is usually a limit of 1024 pixel, but depends on the hardware.
  61. /// </summary>
  62. public static int MaxTextureSize
  63. {
  64. get
  65. {
  66. if (maxTextureSize == 0)
  67. {
  68. maxTextureSize = gl.GetIntegerValue(ParameterName.MaxTextureSize);
  69. }
  70. return maxTextureSize;
  71. }
  72. }
  73. public uint Name { get; protected set; }
  74. /// <summary>
  75. /// Gets the size that is used to store the image. this is a size that is always 2^n.
  76. /// </summary>
  77. public Size StorageSize { get; private set; }
  78. /// <summary>
  79. /// Gets the original size of the image.
  80. /// </summary>
  81. public Size Size { get; private set; }
  82. /// <summary>
  83. /// Gets the current storage format;
  84. /// </summary>
  85. public Format Format { get; private set; }
  86. /// <summary>
  87. /// Gets the current value type.
  88. /// </summary>
  89. public ValueType ValueType { get; private set; }
  90. public int Width { get { return Size.Width; } }
  91. public int Height { get { return Size.Height; } }
  92. protected static IImagingFactory ImageFactory
  93. {
  94. get
  95. {
  96. if (imageFactory == null)
  97. {
  98. imageFactory = (IImagingFactory)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("327ABDA8-072B-11D3-9D7B-0000F81EF32E")));
  99. }
  100. return imageFactory;
  101. }
  102. }
  103. #endregion
  104. #region private Methods
  105. public DrawingCanvas CreateCanvas(int width, int height, TextureFormat format)
  106. {
  107. return new DrawingCanvas(this, width, height, format, Color.Transparent);
  108. }
  109. /// <summary>
  110. /// Creates a new graphics for drawing. Note that an existing image will be deleted.
  111. /// </summary>
  112. /// <param name="width">The width of the new graphics.</param>
  113. /// <param name="height">The height of the new graphics.</param>
  114. /// <param name="format">The pixel format.</param>
  115. /// <param name="transparentColor">The color that specifies the transparent color.</param>
  116. /// <returns>A new graphics.</returns>
  117. public DrawingCanvas CreateCanvas(int width, int height, TextureFormat format, Color transparentColor)
  118. {
  119. return new DrawingCanvas(this, width, height, format, transparentColor);
  120. }
  121. internal unsafe byte[] GetBytesFromBitmap(Bitmap bitmap, TextureFormat textureFormat, Color transparentColor)
  122. {
  123. Size size = new Size(bitmap.Width, bitmap.Height);
  124. if (size.Width > MaxTextureSize) throw new OverflowException("Width exceeds system limit of " + MaxTextureSize.ToString());
  125. if (size.Height > MaxTextureSize) throw new OverflowException("Height exceeds system limit of " + MaxTextureSize.ToString());
  126. PixelFormat pixFormat = TextureUtil.GetPixelFormatFromTextureFormat(textureFormat);
  127. System.Drawing.Imaging.BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, pixFormat);
  128. try
  129. {
  130. IntPtr scan0;
  131. if (textureFormat != TextureFormat.HiResAlpha)
  132. {
  133. scan0 = ConvertData(data, transparentColor, Size.Width, Size.Height, textureFormat);
  134. glValueType valueType = TextureUtil.GetValueTypeFromTextureFormat(textureFormat);
  135. GlesPixelFormat glFormat = TextureUtil.GetGlFormatFromTextureFormat(textureFormat);
  136. BitmapImageData imgData = new BitmapImageData(data);
  137. ReformatRawData(textureFormat, imgData);
  138. }
  139. else scan0 = data.Scan0;
  140. int n = data.Height * data.Stride;
  141. if (textureFormat == TextureFormat.HiResAlpha) n = n / 3;
  142. byte[] result = new byte[n];
  143. if (textureFormat == TextureFormat.HiResAlpha)
  144. {
  145. byte* cc = (byte*)((int)data.Scan0);
  146. for (int i = 0, j = 0; i < result.Length; i++, j += 3)
  147. {
  148. result[i] = cc[j];
  149. }
  150. }
  151. else
  152. {
  153. byte* cc = (byte*)((int)data.Scan0);
  154. for (int i = 0; i < result.Length; i++)
  155. {
  156. result[i] = *cc;
  157. cc++;
  158. }
  159. }
  160. return result;
  161. }
  162. finally
  163. {
  164. bitmap.UnlockBits(data);
  165. }
  166. }
  167. internal void CopyBitmap(Bitmap bitmap, TextureFormat textureFormat, Color transparentColor, Size origSize)
  168. {
  169. this.Size = origSize;
  170. Size size = new Size(bitmap.Width, bitmap.Height);
  171. if (size.Width > MaxTextureSize) throw new OverflowException("Width exceeds system limit of " + MaxTextureSize.ToString());
  172. if (size.Height > MaxTextureSize) throw new OverflowException("Height exceeds system limit of " + MaxTextureSize.ToString());
  173. PixelFormat pixFormat = TextureUtil.GetPixelFormatFromTextureFormat(textureFormat);
  174. System.Drawing.Imaging.BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, pixFormat);
  175. try
  176. {
  177. IntPtr scan0 = ConvertData(data, transparentColor, origSize.Width, origSize.Height, textureFormat);
  178. glValueType valueType = TextureUtil.GetValueTypeFromTextureFormat(textureFormat);
  179. GlesPixelFormat glFormat = TextureUtil.GetGlFormatFromTextureFormat(textureFormat);
  180. BitmapImageData imgData = new BitmapImageData(data);
  181. ReformatRawData(textureFormat, imgData);
  182. LoadPixels(scan0, size, glFormat, valueType);
  183. }
  184. finally
  185. {
  186. bitmap.UnlockBits(data);
  187. }
  188. }
  189. private IntPtr ConvertData(System.Drawing.Imaging.BitmapData data, Color transparentColor, int width, int height, TextureFormat format)
  190. {
  191. switch (format)
  192. {
  193. //case TextureFormat.Alpha:
  194. // return TextureUtil.GenerateAlphaPlaneFromRgb565(data, width, height);
  195. case TextureFormat.LuminanceAlpha:
  196. return TextureUtil.GenerateAlphaPlaneFromARGB(data, transparentColor, width, height);
  197. case TextureFormat.RGBA5551:
  198. switch (data.PixelFormat)
  199. {
  200. case PixelFormat.Format32bppRgb: return TextureUtil.ConvertRGBATo5551(data, transparentColor);
  201. case PixelFormat.Format16bppRgb555: return TextureUtil.ConvertRGB555toRGBA5551(data, transparentColor);
  202. default: throw new NotSupportedException();
  203. }
  204. default:
  205. return data.Scan0;
  206. }
  207. }
  208. public static Texture FromStream(Stream stream, TextureFormat textureFormat)
  209. {
  210. Texture texture = new Texture();
  211. texture.LoadFromStream(stream, textureFormat);
  212. return texture;
  213. }
  214. public static Texture FromStream(Stream stream)
  215. {
  216. Texture texture = new Texture();
  217. texture.LoadFromStream(stream, TextureFormat.Automatic);
  218. return texture;
  219. }
  220. /// <summary>
  221. /// Loads data from a stream.
  222. /// </summary>
  223. /// <param name="stream">The stream where to get the image data.</param>
  224. /// <param name="textureFormat">The format of the texture.</param>
  225. public void LoadFromStream(Stream stream, TextureFormat textureFormat)
  226. {
  227. if (stream == null) throw new ArgumentNullException("stream");
  228. int bytesLength = (int)stream.Length;
  229. byte[] bytes = TextureUtil.BytesFromStream(stream);
  230. IImage image;
  231. uint hresult = ImageFactory.CreateImageFromBuffer(bytes, (uint)bytesLength, BufferDisposalFlag.None, out image);
  232. try
  233. {
  234. ImageInfo info = GetImageInfo(image);
  235. this.Size = new Size(info.Width, info.Height);
  236. Size imageSize = DetermineSize(info);
  237. if (textureFormat == TextureFormat.Automatic) textureFormat = GetTextureFormat(info);
  238. RawPixelFormat rawPixelFormat = TextureUtil.GetRawPixelFormatFromTextureFormat(textureFormat, (info.Flags & SinkFlags.HasAlpha) != 0);
  239. IBitmapImage bitmap;
  240. ImageFactory.CreateBitmapFromImage(image, (uint)imageSize.Width, (uint)imageSize.Height, rawPixelFormat, InterpolationHint.Default, out bitmap);
  241. try
  242. {
  243. Size textureSize;
  244. bitmap.GetSize(out textureSize);
  245. //StorageSize = textureSize;
  246. RECT rect = new RECT(0, 0, textureSize.Width, textureSize.Height);
  247. GlesPixelFormat glFormat = TextureUtil.GetGlFormatFromTextureFormat(textureFormat);
  248. glValueType valueType = TextureUtil.GetValueTypeFromTextureFormat(textureFormat);
  249. glImageLockMode lockMode = glImageLockMode.Read;
  250. BitmapImageData data = new BitmapImageData();
  251. bitmap.LockBits(ref rect, lockMode, rawPixelFormat, data);
  252. try
  253. {
  254. IntPtr scan0 = data.Scan0;
  255. ReformatRawData(textureFormat, data);
  256. LoadPixels(scan0, textureSize, glFormat, valueType);
  257. }
  258. finally
  259. {
  260. bitmap.UnlockBits(data);
  261. }
  262. }
  263. finally
  264. {
  265. Marshal.FinalReleaseComObject(bitmap);
  266. }
  267. }
  268. finally
  269. {
  270. Marshal.FinalReleaseComObject(image);
  271. }
  272. }
  273. private static ImageInfo GetImageInfo(IImage image)
  274. {
  275. ImageInfo info = new ImageInfo();
  276. image.GetImageInfo(info);
  277. return info;
  278. }
  279. private void ReformatRawData(TextureFormat textureFormat, BitmapImageData data)
  280. {
  281. RawPixelFormat pixelFormat = data.PixelFormat;
  282. switch (textureFormat)
  283. {
  284. case TextureFormat.Alpha:
  285. switch (pixelFormat)
  286. {
  287. case RawPixelFormat.Format16bppRGB565:
  288. TextureUtil.GenerateAlphaPlaneFromRgb565(data, data.Width, data.Height);
  289. break;
  290. case RawPixelFormat.Format32bppARGB:
  291. TextureUtil.GenerateAlphaPlaneFromARGB(data);
  292. break;
  293. default:
  294. throw new NotSupportedException();
  295. }
  296. break;
  297. case TextureFormat.RGB24:
  298. TextureUtil.SwapRedBluePixels(data, 3);
  299. break;
  300. case TextureFormat.RGBA4444:
  301. TextureUtil.SwapRedBluePixels(data, 4);
  302. break;
  303. case TextureFormat.RGBA5551:
  304. switch (pixelFormat)
  305. {
  306. case RawPixelFormat.Format16bppARGB1555: TextureUtil.Convert1555To5551(data); break;
  307. }
  308. break;
  309. case TextureFormat.LuminanceAlpha:
  310. TextureUtil.ConvertRGBAToLuminanceAlpha(data);
  311. break;
  312. case TextureFormat.Luminance:
  313. TextureUtil.ConvertRGBToLuminance(data);
  314. break;
  315. case TextureFormat.HiResAlpha:
  316. TextureUtil.ConvertRGBToLuminance(data);
  317. // TextureUtil.ConvertRGBAToAlpha(data);
  318. break;
  319. }
  320. }
  321. private static TextureFormat GetTextureFormat(ImageInfo info)
  322. {
  323. if ((info.Flags & SinkFlags.HasAlpha) != 0) return TextureFormat.RGBA5551;
  324. return TextureFormat.RGB565;
  325. }
  326. private static Size DetermineSize(ImageInfo info)
  327. {
  328. Size size = new Size(info.Width, info.Height);
  329. size.Width = TextureUtil.ConvertToValidTextureDimension(size.Width);
  330. size.Height = TextureUtil.ConvertToValidTextureDimension(size.Height);
  331. return size;
  332. }
  333. private void LoadPixels(IntPtr pixels, Size size, Format glFormat, ValueType glType)
  334. {
  335. gl.BindTexture(TextureTarget.Texture2D, Name);
  336. gl.TexParameterx(TextureTarget.Texture2D, TextureFilter.Minifying, TextureParam.Linear);
  337. gl.TexParameterx(TextureTarget.Texture2D, TextureFilter.Magnification, TextureParam.Linear);
  338. gl.TexParameterx(TextureTarget.Texture2D, TextureFilter.WrapS, TextureParam.Repeat);
  339. gl.TexParameterx(TextureTarget.Texture2D, TextureFilter.WrapT, TextureParam.Repeat);
  340. gl.GetError();
  341. //gl.PixelStorei(PixelStoreParam.UnpackAlignment, PixelStoreValue.EvenNumberedAlignment);
  342. //gl.GetError();
  343. gl.TexImage2D(TextureTarget.Texture2D, 0, glFormat, size.Width, size.Height, 0, glFormat, glType, pixels);
  344. gl.CheckError();
  345. this.StorageSize = size;
  346. this.Format = glFormat;
  347. this.ValueType = glType;
  348. }
  349. #endregion
  350. #region Methods
  351. public void PreLoad()
  352. {
  353. }
  354. /// <summary>
  355. /// Captures the specified clip from surface.
  356. /// </summary>
  357. /// <param name="clip">The clip rectangle to capture into this texture.</param>
  358. public void Capture(Rectangle clip)
  359. {
  360. Capture(clip, TextureUnit.Texture0);
  361. //this.ImageSize = this.Size = clip.Size;
  362. SetTextCoords();
  363. }
  364. /// <summary>
  365. /// Captures the specified clip from surface.
  366. /// </summary>
  367. /// <param name="clip">The clip rectangle to capture into this texture.</param>
  368. /// <param name="unit">A valid texture unit to use.</param>
  369. private void Capture(Rectangle clip, TextureUnit unit)
  370. {
  371. gl.ActiveTexture(unit);
  372. gl.ClientActiveTexture(unit);
  373. gl.BindTexture(TextureTarget.Texture2D, this.Name);
  374. Size defSize = new Size(TextureUtil.ConvertToValidTextureDimension(clip.Width), TextureUtil.ConvertToValidTextureDimension(clip.Height));
  375. short[] buffer = new short[defSize.Width * defSize.Height];
  376. if (StorageSize != clip.Size)
  377. {
  378. LoadPixels(IntPtr.Zero, defSize, GlesPixelFormat.RGB, glValueType.UnsignedShort565);
  379. }
  380. uint error2 = gl.GetError();
  381. if (error2 != 0) throw new OpenGlException("Capture:" + error2.ToString());
  382. // Note: CopyTexImage2D does not work when the width is larger than the screen width, but CopyTexSubImage2D does.
  383. //gl.CopyTexImage2D(TargetTexture.Texture2D, 0, GlesPixelFormat.RGB, clip.Left, clip.Top, clip.Width, clip.Height, 0);
  384. gl.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, clip.Left, clip.Top, clip.Width, clip.Height);
  385. uint error = gl.GetError();
  386. if (error != 0) throw new OpenGlException("Capture: " + error.ToString());
  387. this.Size = clip.Size;
  388. }
  389. private void SetTextCoords()
  390. {
  391. const int one = 65536;
  392. int w = StorageSize.Width;
  393. int h = StorageSize.Height;
  394. w = w > 0 ? one * Size.Width / w : 0;
  395. h = h > 0 ? one * (Size.Height - h) / h : 0;
  396. textCoords[0] = textCoords[2] = w;
  397. textCoords[1] = textCoords[5] = h;
  398. }
  399. public void Render()
  400. {
  401. gl.BindTexture(TextureTarget.Texture2D, Name);
  402. gl.TexCoordPointer(2, glValueType.Fixed, 0, textCoords);
  403. }
  404. public static Texture FromResource(string resource, TextureFormat format)
  405. {
  406. Stream stream = Assembly.GetCallingAssembly().GetManifestResourceStream(resource);
  407. if (stream == null) throw new SystemException("Resource " + resource + " not found.");
  408. Texture image = Texture.FromStream(stream, format);
  409. return image;
  410. }
  411. public static Texture FromResource(string resource, TextureFormat format, bool isSigned)
  412. {
  413. Stream stream = Assembly.GetCallingAssembly().GetManifestResourceStream(resource);
  414. if (stream == null) throw new SystemException("Resource " + resource + " not found.");
  415. Texture image = Texture.FromStream(stream, format);
  416. image.IsSigned = isSigned;
  417. return image;
  418. }
  419. internal unsafe void FromByteArray(byte[] array, TextureFormat textureFormat, Size size)
  420. {
  421. this.Size = size;
  422. glValueType valueType = TextureUtil.GetValueTypeFromTextureFormat(textureFormat);
  423. GlesPixelFormat glFormat = TextureUtil.GetGlFormatFromTextureFormat(textureFormat);
  424. fixed (byte* b = &array[0])
  425. {
  426. IntPtr ptr = new IntPtr(b);
  427. LoadPixels(ptr, size, glFormat, valueType);
  428. }
  429. }
  430. #endregion
  431. }
  432. }