1 #include "lc_global.h"
2 #include "lc_texture.h"
3 #include "lc_application.h"
4 #include "lc_library.h"
5 #include "image.h"
6 #include "lc_glextensions.h"
7 
8 lcTexture* gGridTexture;
9 
lcLoadTexture(const QString & FileName,int Flags)10 lcTexture* lcLoadTexture(const QString& FileName, int Flags)
11 {
12 	lcTexture* Texture = new lcTexture();
13 
14 	if (!Texture->Load(FileName, Flags))
15 	{
16 		delete Texture;
17 		Texture = nullptr;
18 	}
19 	else
20 	{
21 		strcpy(Texture->mName, QFileInfo(FileName).baseName().toLatin1());
22 		Texture->SetTemporary(true);
23 	}
24 
25 	return Texture;
26 }
27 
lcReleaseTexture(lcTexture * Texture)28 void lcReleaseTexture(lcTexture* Texture)
29 {
30 	if (Texture && !Texture->Release())
31 		delete Texture;
32 }
33 
lcTexture()34 lcTexture::lcTexture()
35 {
36 	mTexture = 0;
37 	mRefCount = 0;
38 	mTemporary = false;
39 }
40 
~lcTexture()41 lcTexture::~lcTexture()
42 {
43 	Unload();
44 }
45 
CreateGridTexture()46 void lcTexture::CreateGridTexture()
47 {
48 	constexpr int NumLevels = 9;
49 	mImages.resize(NumLevels);
50 	quint8* Previous = nullptr;
51 
52 	for (int ImageLevel = 0; ImageLevel < NumLevels; ImageLevel++)
53 	{
54 		Image& GridImage = mImages[ImageLevel];
55 		const int GridSize = 256 >> ImageLevel;
56 		GridImage.Allocate(GridSize, GridSize, LC_PIXEL_FORMAT_A8);
57 
58 		if (Previous)
59 		{
60 			const int PreviousGridSize = 2 * GridSize;
61 
62 			for (int y = 0; y < GridSize - 1; y++)
63 			{
64 				for (int x = 0; x < GridSize - 1; x++)
65 				{
66 					const quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize] > 64 ? 255 : 0;
67 					const quint8 b = Previous[x * 2 + 1 + y * 2 * PreviousGridSize] > 64 ? 255 : 0;
68 					const quint8 c = Previous[x * 2 + (y * 2 + 1) * PreviousGridSize] > 64 ? 255 : 0;
69 					const quint8 d = Previous[x * 2 + 1 + (y * 2 + 1) * PreviousGridSize] > 64 ? 255 : 0;
70 					GridImage.mData[x + y * GridSize] = (a + b + c + d) / 4;
71 				}
72 
73 				int x = GridSize - 1;
74 				const quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize];
75 				const quint8 c = Previous[x * 2 + (y * 2 + 1) * PreviousGridSize];
76 				GridImage.mData[x + y * GridSize] = (a + c) / 2;
77 			}
78 
79 			int y = GridSize - 1;
80 			for (int x = 0; x < GridSize - 1; x++)
81 			{
82 				const quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize];
83 				const quint8 b = Previous[x * 2 + 1 + y * 2 * PreviousGridSize];
84 				GridImage.mData[x + y * GridSize] = (a + b) / 2;
85 			}
86 
87 			int x = GridSize - 1;
88 			GridImage.mData[x + y * GridSize] = Previous[x + y * PreviousGridSize];
89 		}
90 		else
91 		{
92 			const float Radius1 = (80 >> ImageLevel) * (80 >> ImageLevel);
93 			const float Radius2 = (72 >> ImageLevel) * (72 >> ImageLevel);
94 			quint8* TempBuffer = new quint8[GridSize * GridSize];
95 
96 			for (int y = 0; y < GridSize; y++)
97 			{
98 				quint8* Pixel = TempBuffer + y * GridSize;
99 				memset(Pixel, 0, GridSize);
100 
101 				const float y2 = (y - GridSize / 2) * (y - GridSize / 2);
102 
103 				if (Radius1 <= y2)
104 					continue;
105 
106 				if (Radius2 <= y2)
107 				{
108 					const int x1 = sqrtf(Radius1 - y2);
109 
110 					for (int x = GridSize / 2 - x1; x < GridSize / 2 + x1; x++)
111 						Pixel[x] = 255;
112 				}
113 				else
114 				{
115 					const int x1 = sqrtf(Radius1 - y2);
116 					const int x2 = sqrtf(Radius2 - y2);
117 
118 					for (int x = GridSize / 2 - x1; x < GridSize / 2 - x2; x++)
119 						Pixel[x] = 255;
120 
121 					for (int x = GridSize / 2 + x2; x < GridSize / 2 + x1; x++)
122 						Pixel[x] = 255;
123 				}
124 			}
125 
126 			for (int y = 0; y < GridSize - 1; y++)
127 			{
128 				for (int x = 0; x < GridSize - 1; x++)
129 				{
130 					const quint8 a = TempBuffer[x + y * GridSize];
131 					const quint8 b = TempBuffer[x + 1 + y * GridSize];
132 					const quint8 c = TempBuffer[x + (y + 1) * GridSize];
133 					const quint8 d = TempBuffer[x + 1 + (y + 1) * GridSize];
134 					GridImage.mData[x + y * GridSize] = (a + b + c + d) / 4;
135 				}
136 
137 				int x = GridSize - 1;
138 				const quint8 a = TempBuffer[x + y * GridSize];
139 				const quint8 c = TempBuffer[x + (y + 1) * GridSize];
140 				GridImage.mData[x + y * GridSize] = (a + c) / 2;
141 			}
142 
143 			int y = GridSize - 1;
144 			for (int x = 0; x < GridSize - 1; x++)
145 			{
146 				const quint8 a = TempBuffer[x + y * GridSize];
147 				const quint8 b = TempBuffer[x + 1 + y * GridSize];
148 				GridImage.mData[x + y * GridSize] = (a + b) / 2;
149 			}
150 
151 			int x = GridSize - 1;
152 			GridImage.mData[x + y * GridSize] = TempBuffer[x + y * GridSize];
153 			delete[] TempBuffer;
154 		}
155 
156 		Previous = GridImage.mData;
157 	}
158 
159 	mRefCount = 1;
160 	mFlags = LC_TEXTURE_WRAPU | LC_TEXTURE_WRAPV | LC_TEXTURE_MIPMAPS | LC_TEXTURE_ANISOTROPIC;
161 
162 	lcContext* Context = lcContext::GetGlobalOffscreenContext();
163 	Context->MakeCurrent();
164 	Upload(Context);
165 }
166 
Load()167 bool lcTexture::Load()
168 {
169 	return lcGetPiecesLibrary()->LoadTexture(this);
170 }
171 
Load(const QString & FileName,int Flags)172 bool lcTexture::Load(const QString& FileName, int Flags)
173 {
174 	mImages.resize(1);
175 
176 	if (!mImages[0].FileLoad(FileName))
177 		return false;
178 
179 	return Load(Flags);
180 }
181 
Load(lcMemFile & File,int Flags)182 bool lcTexture::Load(lcMemFile& File, int Flags)
183 {
184 	mImages.resize(1);
185 
186 	if (!mImages[0].FileLoad(File))
187 		return false;
188 
189 	return Load(Flags);
190 }
191 
SetImage(Image * Image,int Flags)192 void lcTexture::SetImage(Image* Image, int Flags)
193 {
194 	mImages.clear();
195 	mImages.emplace_back(std::move(*Image));
196 
197 	Load(Flags);
198 }
199 
SetImage(std::vector<Image> && Images,int Flags)200 void lcTexture::SetImage(std::vector<Image>&& Images, int Flags)
201 {
202 	mImages = std::move(Images);
203 
204 	Load(Flags);
205 }
206 
Upload(lcContext * Context)207 void lcTexture::Upload(lcContext* Context)
208 {
209 	mWidth = mImages[0].mWidth;
210 	mHeight = mImages[0].mHeight;
211 
212 	if (!mTexture)
213 		glGenTextures(1, &mTexture);
214 
215 	constexpr int Filters[2][5] =
216 	{
217 		{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR },
218 		{ GL_NEAREST, GL_LINEAR, GL_LINEAR, GL_LINEAR, GL_LINEAR  },
219 	};
220 
221 	const int FilterFlags = mFlags & LC_TEXTURE_FILTER_MASK;
222 	const int FilterIndex = FilterFlags >> LC_TEXTURE_FILTER_SHIFT;
223 	const int MipIndex = mFlags & LC_TEXTURE_MIPMAPS ? 0 : 1;
224 
225     unsigned int Faces, Target;
226 
227 	if ((mFlags & LC_TEXTURE_CUBEMAP) == 0)
228 	{
229 		Faces = 1;
230 		Target = GL_TEXTURE_2D;
231 		Context->BindTexture2D(mTexture);
232 	}
233 	else
234 	{
235 		Faces = 6;
236 		Target = GL_TEXTURE_CUBE_MAP;
237 		Context->BindTextureCubeMap(mTexture);
238 	}
239 
240 	glTexParameteri(Target, GL_TEXTURE_WRAP_S, (mFlags & LC_TEXTURE_WRAPU) ? GL_REPEAT : GL_CLAMP_TO_EDGE);
241 	glTexParameteri(Target, GL_TEXTURE_WRAP_T, (mFlags & LC_TEXTURE_WRAPV) ? GL_REPEAT : GL_CLAMP_TO_EDGE);
242 	glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, Filters[MipIndex][FilterIndex]);
243 	glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, Filters[1][FilterIndex]);
244 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
245 
246 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
247 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
248 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
249 
250 	if (gSupportsAnisotropic && FilterFlags == LC_TEXTURE_ANISOTROPIC)
251 		glTexParameterf(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, lcMin(4.0f, gMaxAnisotropy));
252 
253 	int Format;
254 	switch (mImages[0].mFormat)
255 	{
256     default:
257     case LC_PIXEL_FORMAT_INVALID:
258         Format = 0;
259         break;
260     case LC_PIXEL_FORMAT_A8:
261 		Format = GL_ALPHA;
262 		break;
263 	case LC_PIXEL_FORMAT_L8A8:
264 		Format = GL_LUMINANCE_ALPHA;
265 		break;
266 	case LC_PIXEL_FORMAT_R8G8B8:
267 		Format = GL_RGB;
268 		break;
269 	case LC_PIXEL_FORMAT_R8G8B8A8:
270 		Format = GL_RGBA;
271 		break;
272 	}
273 
274 	int CurrentImage = 0;
275 	if (mFlags & LC_TEXTURE_CUBEMAP)
276 		Target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
277 
278     for (size_t FaceIdx = 0; FaceIdx < Faces; FaceIdx++)
279 	{
280 		void* Data = mImages[CurrentImage].mData;
281 		glTexImage2D(Target, 0, Format, mWidth, mHeight, 0, Format, GL_UNSIGNED_BYTE, Data);
282 
283 		if (mFlags & LC_TEXTURE_MIPMAPS || FilterFlags >= LC_TEXTURE_BILINEAR)
284 		{
285 			int Width = mWidth;
286 			int Height = mHeight;
287 			int Components = mImages[CurrentImage].GetBPP();
288 
289 			for (int Level = 1; ((Width != 1) || (Height != 1)); Level++)
290 			{
291 				int RowStride = Width * Components;
292 
293 				Width = lcMax(1, Width >> 1);
294 				Height = lcMax(1, Height >> 1);
295 
296 				if (mImages.size() == Faces)
297 				{
298 					GLubyte *Out, *In;
299 
300 					In = Out = (GLubyte*)Data;
301 
302 					for (int y = 0; y < Height; y++, In += RowStride)
303 						for (int x = 0; x < Width; x++, Out += Components, In += 2 * Components)
304 							for (int c = 0; c < Components; c++)
305 								Out[c] = (In[c] + In[c + Components] + In[RowStride] + In[c + RowStride + Components]) / 4;
306 				}
307 				else
308 					Data = mImages[++CurrentImage].mData;
309 
310 				glTexImage2D(Target, Level, Format, Width, Height, 0, Format, GL_UNSIGNED_BYTE, Data);
311 			}
312 
313 			if (mImages.size() == Faces)
314 				CurrentImage++;
315 		}
316 		else
317 			CurrentImage++;
318 
319 		Target++;
320 	}
321 
322 	if ((mFlags & LC_TEXTURE_CUBEMAP) == 0)
323 		Context->UnbindTexture2D(mTexture);
324 	else
325 		Context->UnbindTextureCubeMap(mTexture);
326 }
327 
Load(int Flags)328 bool lcTexture::Load(int Flags)
329 {
330 	for (Image& Image : mImages)
331 		Image.ResizePow2();
332 	mFlags = Flags;
333 
334 	if (QThread::currentThread() == qApp->thread())
335 	{
336 		lcContext* Context = lcContext::GetGlobalOffscreenContext();
337 		Context->MakeCurrent();
338 		Upload(Context);
339 	}
340 
341 	return true;
342 }
343 
Unload()344 void lcTexture::Unload()
345 {
346 	if (mTexture)
347 		glDeleteTextures(1, &mTexture);
348 	mTexture = 0;
349 }
350