1 #region Copyright & License Information
2 /*
3  * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4  * This file is part of OpenRA, which is free software. It is made
5  * available to you under the terms of the GNU General Public License
6  * as published by the Free Software Foundation, either version 3 of
7  * the License, or (at your option) any later version. For more
8  * information, see COPYING.
9  */
10 #endregion
11 
12 using System;
13 using System.IO;
14 using OpenRA.Primitives;
15 
16 namespace OpenRA.Platforms.Default
17 {
18 	sealed class Texture : ThreadAffine, ITextureInternal
19 	{
20 		uint texture;
21 		TextureScaleFilter scaleFilter;
22 
23 		public uint ID { get { return texture; } }
24 		public Size Size { get; private set; }
25 
26 		bool disposed;
27 
28 		public TextureScaleFilter ScaleFilter
29 		{
30 			get
31 			{
32 				return scaleFilter;
33 			}
34 
35 			set
36 			{
37 				VerifyThreadAffinity();
38 				if (scaleFilter == value)
39 					return;
40 
41 				scaleFilter = value;
42 				PrepareTexture();
43 			}
44 		}
45 
Texture()46 		public Texture()
47 		{
48 			OpenGL.glGenTextures(1, out texture);
49 			OpenGL.CheckGLError();
50 		}
51 
PrepareTexture()52 		void PrepareTexture()
53 		{
54 			OpenGL.CheckGLError();
55 			OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, texture);
56 			OpenGL.CheckGLError();
57 
58 			var filter = scaleFilter == TextureScaleFilter.Linear ? OpenGL.GL_LINEAR : OpenGL.GL_NEAREST;
59 			OpenGL.glTexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, filter);
60 			OpenGL.CheckGLError();
61 			OpenGL.glTexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, filter);
62 			OpenGL.CheckGLError();
63 
64 			OpenGL.glTexParameterf(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_S, OpenGL.GL_CLAMP_TO_EDGE);
65 			OpenGL.CheckGLError();
66 			OpenGL.glTexParameterf(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_T, OpenGL.GL_CLAMP_TO_EDGE);
67 			OpenGL.CheckGLError();
68 
69 			OpenGL.glTexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_BASE_LEVEL, 0);
70 			OpenGL.CheckGLError();
71 			OpenGL.glTexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAX_LEVEL, 0);
72 			OpenGL.CheckGLError();
73 		}
74 
SetData(IntPtr data, int width, int height)75 		void SetData(IntPtr data, int width, int height)
76 		{
77 			PrepareTexture();
78 			var glInternalFormat = OpenGL.Profile == GLProfile.Embedded ? OpenGL.GL_BGRA : OpenGL.GL_RGBA8;
79 			OpenGL.glTexImage2D(OpenGL.GL_TEXTURE_2D, 0, glInternalFormat, width, height,
80 				0, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, data);
81 			OpenGL.CheckGLError();
82 		}
83 
SetData(byte[] colors, int width, int height)84 		public void SetData(byte[] colors, int width, int height)
85 		{
86 			VerifyThreadAffinity();
87 			if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
88 				throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
89 
90 			Size = new Size(width, height);
91 			unsafe
92 			{
93 				fixed (byte* ptr = &colors[0])
94 					SetData(new IntPtr(ptr), width, height);
95 			}
96 		}
97 
98 		// An array of RGBA
SetData(uint[,] colors)99 		public void SetData(uint[,] colors)
100 		{
101 			VerifyThreadAffinity();
102 			var width = colors.GetUpperBound(1) + 1;
103 			var height = colors.GetUpperBound(0) + 1;
104 
105 			if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
106 				throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
107 
108 			Size = new Size(width, height);
109 			unsafe
110 			{
111 				fixed (uint* ptr = &colors[0, 0])
112 					SetData(new IntPtr(ptr), width, height);
113 			}
114 		}
115 
GetData()116 		public byte[] GetData()
117 		{
118 			VerifyThreadAffinity();
119 			var data = new byte[4 * Size.Width * Size.Height];
120 
121 			// GLES doesn't support glGetTexImage so data must be read back via a frame buffer
122 			if (OpenGL.Profile == GLProfile.Embedded)
123 			{
124 				// Query the active framebuffer so we can restore it afterwards
125 				int lastFramebuffer;
126 				OpenGL.glGetIntegerv(OpenGL.GL_FRAMEBUFFER_BINDING, out lastFramebuffer);
127 
128 				uint framebuffer;
129 				OpenGL.glGenFramebuffers(1, out framebuffer);
130 				OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, framebuffer);
131 				OpenGL.CheckGLError();
132 
133 				OpenGL.glFramebufferTexture2D(OpenGL.GL_FRAMEBUFFER, OpenGL.GL_COLOR_ATTACHMENT0, OpenGL.GL_TEXTURE_2D, texture, 0);
134 				OpenGL.CheckGLError();
135 
136 				var canReadBGRA = OpenGL.Features.HasFlag(OpenGL.GLFeatures.ESReadFormatBGRA);
137 
138 				unsafe
139 				{
140 					fixed (byte* ptr = &data[0])
141 					{
142 						var intPtr = new IntPtr(ptr);
143 
144 						var format = canReadBGRA ? OpenGL.GL_BGRA : OpenGL.GL_RGBA;
145 						OpenGL.glReadPixels(0, 0, Size.Width, Size.Height, format, OpenGL.GL_UNSIGNED_BYTE, intPtr);
146 						OpenGL.CheckGLError();
147 					}
148 				}
149 
150 				// Convert RGBA to BGRA
151 				if (!canReadBGRA)
152 				{
153 					for (var i = 0; i < 4 * Size.Width * Size.Height; i += 4)
154 					{
155 						var temp = data[i];
156 						data[i] = data[i + 2];
157 						data[i + 2] = temp;
158 					}
159 				}
160 
161 				OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, (uint)lastFramebuffer);
162 				OpenGL.glDeleteFramebuffers(1, ref framebuffer);
163 				OpenGL.CheckGLError();
164 			}
165 			else
166 			{
167 				OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, texture);
168 				unsafe
169 				{
170 					fixed (byte* ptr = &data[0])
171 					{
172 						var intPtr = new IntPtr((void*)ptr);
173 						OpenGL.glGetTexImage(OpenGL.GL_TEXTURE_2D, 0, OpenGL.GL_BGRA,
174 							OpenGL.GL_UNSIGNED_BYTE, intPtr);
175 					}
176 				}
177 
178 				OpenGL.CheckGLError();
179 			}
180 
181 			return data;
182 		}
183 
SetEmpty(int width, int height)184 		public void SetEmpty(int width, int height)
185 		{
186 			VerifyThreadAffinity();
187 			if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
188 				throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
189 
190 			Size = new Size(width, height);
191 			SetData(IntPtr.Zero, width, height);
192 		}
193 
Dispose()194 		public void Dispose()
195 		{
196 			Dispose(true);
197 			GC.SuppressFinalize(this);
198 		}
199 
Dispose(bool disposing)200 		void Dispose(bool disposing)
201 		{
202 			if (disposed)
203 				return;
204 			disposed = true;
205 			OpenGL.glDeleteTextures(1, ref texture);
206 		}
207 	}
208 }
209