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