blob: 1ea1ed397f3b148a9414a3a264e359f0c359ea8f [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
11#include "Texture.h"
12
daniel@transgaming.com16973022010-03-11 19:22:19 +000013#include <algorithm>
14
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000015#include "main.h"
16#include "mathutil.h"
17#include "debug.h"
18
19namespace gl
20{
21Texture::Texture() : Colorbuffer(0)
22{
23 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
24 mMagFilter = GL_LINEAR;
25 mWrapS = GL_REPEAT;
26 mWrapT = GL_REPEAT;
27}
28
29Texture::~Texture()
30{
31}
32
33// Returns true on successful filter state update (valid enum parameter)
34bool Texture::setMinFilter(GLenum filter)
35{
36 switch (filter)
37 {
38 case GL_NEAREST:
39 case GL_LINEAR:
40 case GL_NEAREST_MIPMAP_NEAREST:
41 case GL_LINEAR_MIPMAP_NEAREST:
42 case GL_NEAREST_MIPMAP_LINEAR:
43 case GL_LINEAR_MIPMAP_LINEAR:
44 mMinFilter = filter;
45 return true;
46 default:
47 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000048 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000049}
50
51// Returns true on successful filter state update (valid enum parameter)
52bool Texture::setMagFilter(GLenum filter)
53{
54 switch (filter)
55 {
56 case GL_NEAREST:
57 case GL_LINEAR:
58 mMagFilter = filter;
59 return true;
60 default:
61 return false;
62 }
63}
64
65// Returns true on successful wrap state update (valid enum parameter)
66bool Texture::setWrapS(GLenum wrap)
67{
68 switch (wrap)
69 {
70 case GL_REPEAT:
71 case GL_CLAMP_TO_EDGE:
72 case GL_MIRRORED_REPEAT:
73 mWrapS = wrap;
74 return true;
75 default:
76 return false;
77 }
78}
79
80// Returns true on successful wrap state update (valid enum parameter)
81bool Texture::setWrapT(GLenum wrap)
82{
83 switch (wrap)
84 {
85 case GL_REPEAT:
86 case GL_CLAMP_TO_EDGE:
87 case GL_MIRRORED_REPEAT:
88 mWrapT = wrap;
89 return true;
90 default:
91 return false;
92 }
93}
94
95GLenum Texture::getMinFilter()
96{
97 return mMinFilter;
98}
99
100GLenum Texture::getMagFilter()
101{
102 return mMagFilter;
103}
104
105GLenum Texture::getWrapS()
106{
107 return mWrapS;
108}
109
110GLenum Texture::getWrapT()
111{
112 return mWrapT;
113}
114
115// Copies an Image into an already locked Direct3D 9 surface, performing format conversions as necessary
116void Texture::copyImage(D3DLOCKED_RECT &lock, D3DFORMAT format, Image &image)
117{
118 if (lock.pBits && image.pixels)
119 {
120 for (int y = 0; y < image.height; y++)
121 {
122 unsigned char *source = (unsigned char*)image.pixels + y * image.width * pixelSize(image);
123 unsigned short *source16 = (unsigned short*)source;
124 unsigned char *dest = (unsigned char*)lock.pBits + y * lock.Pitch;
125
126 for (int x = 0; x < image.width; x++)
127 {
128 unsigned char r;
129 unsigned char g;
130 unsigned char b;
131 unsigned char a;
132
133 switch (image.format)
134 {
135 case GL_ALPHA:
136 UNIMPLEMENTED();
137 break;
138 case GL_LUMINANCE:
139 UNIMPLEMENTED();
140 break;
141 case GL_LUMINANCE_ALPHA:
142 UNIMPLEMENTED();
143 break;
144 case GL_RGB:
145 switch (image.type)
146 {
147 case GL_UNSIGNED_BYTE: UNIMPLEMENTED(); break;
148 case GL_UNSIGNED_SHORT_5_6_5:
149 {
150 unsigned short rgba = source16[x];
151
152 a = 0xFF;
153 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
154 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
155 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
156 }
157 break;
158 default: UNREACHABLE();
159 }
160 break;
161 case GL_RGBA:
162 switch (image.type)
163 {
164 case GL_UNSIGNED_BYTE:
165 r = source[x * 4 + 0];
166 g = source[x * 4 + 1];
167 b = source[x * 4 + 2];
168 a = source[x * 4 + 3];
169 break;
170 case GL_UNSIGNED_SHORT_4_4_4_4:
171 {
172 unsigned short rgba = source16[x];
173
174 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
175 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
176 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
177 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
178 }
179 break;
180 case GL_UNSIGNED_SHORT_5_5_5_1: UNIMPLEMENTED(); break;
181 default: UNREACHABLE();
182 }
183 break;
184 default: UNREACHABLE();
185 }
186
187 switch (format)
188 {
189 case D3DFMT_A8R8G8B8:
190 dest[4 * x + 0] = b;
191 dest[4 * x + 1] = g;
192 dest[4 * x + 2] = r;
193 dest[4 * x + 3] = a;
194 break;
195 default: UNREACHABLE();
196 }
197 }
198 }
199 }
200}
201
202// Selects an internal Direct3D 9 format for storing an Image
203D3DFORMAT Texture::selectFormat(const Image &image)
204{
205 switch (image.format)
206 {
207 case GL_ALPHA:
208 UNIMPLEMENTED();
209 break;
210 case GL_LUMINANCE:
211 UNIMPLEMENTED();
212 break;
213 case GL_LUMINANCE_ALPHA:
214 UNIMPLEMENTED();
215 break;
216 case GL_RGB:
217 switch (image.type)
218 {
219 case GL_UNSIGNED_BYTE: UNIMPLEMENTED();
220 case GL_UNSIGNED_SHORT_5_6_5: return D3DFMT_A8R8G8B8;
221 default: UNREACHABLE();
222 }
223 break;
224 case GL_RGBA:
225 switch (image.type)
226 {
227 case GL_UNSIGNED_BYTE: return D3DFMT_A8R8G8B8;
228 case GL_UNSIGNED_SHORT_4_4_4_4: return D3DFMT_A8R8G8B8;
229 case GL_UNSIGNED_SHORT_5_5_5_1: UNIMPLEMENTED();
230 default: UNREACHABLE();
231 }
232 break;
233 default: UNREACHABLE();
234 }
235
236 return D3DFMT_UNKNOWN;
237}
238
239// Returns the size, in bytes, of a single texel in an Image
240int Texture::pixelSize(const Image &image)
241{
242 switch (image.type)
243 {
244 case GL_UNSIGNED_BYTE:
245 switch (image.format)
246 {
247 case GL_ALPHA: return sizeof(unsigned char);
248 case GL_LUMINANCE: return sizeof(unsigned char);
249 case GL_LUMINANCE_ALPHA: return sizeof(unsigned char) * 2;
250 case GL_RGB: return sizeof(unsigned char) * 3;
251 case GL_RGBA: return sizeof(unsigned char) * 4;
252 default: UNREACHABLE();
253 }
254 break;
255 case GL_UNSIGNED_SHORT_4_4_4_4:
256 case GL_UNSIGNED_SHORT_5_5_5_1:
257 case GL_UNSIGNED_SHORT_5_6_5:
258 return sizeof(unsigned short);
259 default: UNREACHABLE();
260 }
261
262 return 0;
263}
264
265Texture2D::Texture2D()
266{
267 for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
268 {
269 mImageArray[level].pixels = NULL;
270 }
271
272 mTexture = NULL;
273}
274
275Texture2D::~Texture2D()
276{
277 for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
278 {
279 delete[] mImageArray[level].pixels;
280 }
281
282 if (mTexture)
283 {
284 mTexture->Release();
285 mTexture = NULL;
286 }
287}
288
289GLenum Texture2D::getTarget() const
290{
291 return GL_TEXTURE_2D;
292}
293
294void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
295{
296 if (level < 0 || level >= MAX_TEXTURE_LEVELS)
297 {
298 return;
299 }
300
301 mImageArray[level].internalFormat = internalFormat;
302 mImageArray[level].width = width;
303 mImageArray[level].height = height;
304 mImageArray[level].format = format;
305 mImageArray[level].type = type;
306
307 delete[] mImageArray[level].pixels;
308 mImageArray[level].pixels = NULL;
309
310 int imageSize = pixelSize(mImageArray[level]) * width * height;
311 mImageArray[level].pixels = new unsigned char[imageSize];
312
daniel@transgaming.come2b22122010-03-11 19:22:14 +0000313 if (pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000314 {
315 memcpy(mImageArray[level].pixels, pixels, imageSize);
316 }
317
318 if (level == 0)
319 {
320 mWidth = width;
321 mHeight = height;
322 }
323}
324
325// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
326bool Texture2D::isComplete()
327{
328 if (mWidth <= 0 || mHeight <= 0)
329 {
330 return false;
331 }
332
333 bool mipmapping;
334
335 switch (mMagFilter)
336 {
337 case GL_NEAREST:
338 case GL_LINEAR:
339 mipmapping = false;
340 break;
341 case GL_NEAREST_MIPMAP_NEAREST:
342 case GL_LINEAR_MIPMAP_NEAREST:
343 case GL_NEAREST_MIPMAP_LINEAR:
344 case GL_LINEAR_MIPMAP_LINEAR:
345 mipmapping = true;
346 break;
347 default: UNREACHABLE();
348 }
349
350 if (mipmapping)
351 {
daniel@transgaming.com16973022010-03-11 19:22:19 +0000352 int q = log2(std::max(mWidth, mHeight));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000353
354 for (int level = 1; level <= q; level++)
355 {
356 if (mImageArray[level].format != mImageArray[0].format)
357 {
358 return false;
359 }
360
361 if (mImageArray[level].internalFormat != mImageArray[0].internalFormat)
362 {
363 return false;
364 }
365
366 if (mImageArray[level].type != mImageArray[0].type)
367 {
368 return false;
369 }
370
371 if (mImageArray[level].width != (mImageArray[level - 1].width + 1) / 2)
372 {
373 return false;
374 }
375
376 if (mImageArray[level].height != (mImageArray[level - 1].height + 1) / 2)
377 {
378 return false;
379 }
380 }
381 }
382
383 return true;
384}
385
386// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
387IDirect3DBaseTexture9 *Texture2D::getTexture()
388{
389 if (!isComplete())
390 {
391 return NULL;
392 }
393
394 if (!mTexture) // FIXME: Recreate when changed (same for getRenderTarget)
395 {
396 IDirect3DDevice9 *device = getDevice();
397 D3DFORMAT format = selectFormat(mImageArray[0]);
398
399 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &mTexture, NULL);
400
401 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
402 {
403 error(GL_OUT_OF_MEMORY, 0);
404 }
405
406 ASSERT(SUCCEEDED(result));
407
408 IDirect3DTexture9 *lockableTexture;
409 result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_DYNAMIC, format, D3DPOOL_SYSTEMMEM, &lockableTexture, NULL);
410
411 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
412 {
413 error(GL_OUT_OF_MEMORY,(IDirect3DBaseTexture9*)NULL);
414 }
415
416 ASSERT(SUCCEEDED(result));
417
418 if (mTexture) // FIXME: Handle failure
419 {
420 int levelCount = mTexture->GetLevelCount();
421
422 for (int level = 0; level < levelCount; level++)
423 {
424 D3DLOCKED_RECT lock = {0};
425 lockableTexture->LockRect(level, &lock, NULL, 0);
426
427 copyImage(lock, format, mImageArray[level]);
428
429 lockableTexture->UnlockRect(level);
430 }
431
432 device->UpdateTexture(lockableTexture, mTexture);
433 lockableTexture->Release();
434 }
435 }
436
437 return mTexture;
438}
439
440// Returns the top-level texture surface as a render target
441IDirect3DSurface9 *Texture2D::getRenderTarget()
442{
443 if (!mRenderTarget && getTexture())
444 {
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +0000445 mTexture->GetSurfaceLevel(0, &mRenderTarget);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000446 }
447
448 return mRenderTarget;
449}
450
451TextureCubeMap::TextureCubeMap()
452{
453 for (int face = 0; face < 6; face++)
454 {
455 for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
456 {
457 mImageArray[face][level].pixels = NULL;
458 }
459 }
460
461 mTexture = NULL;
462}
463
464TextureCubeMap::~TextureCubeMap()
465{
466 for (int face = 0; face < 6; face++)
467 {
468 for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
469 {
470 delete[] mImageArray[face][level].pixels;
471 }
472 }
473
474 if (mTexture)
475 {
476 mTexture->Release();
477 mTexture = NULL;
478 }
479}
480
481GLenum TextureCubeMap::getTarget() const
482{
483 return GL_TEXTURE_CUBE_MAP;
484}
485
486void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
487{
488 setImage(0, level, internalFormat, width, height, format, type, pixels);
489}
490
491void TextureCubeMap::setImageNegX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
492{
493 setImage(1, level, internalFormat, width, height, format, type, pixels);
494}
495
496void TextureCubeMap::setImagePosY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
497{
498 setImage(2, level, internalFormat, width, height, format, type, pixels);
499}
500
501void TextureCubeMap::setImageNegY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
502{
503 setImage(3, level, internalFormat, width, height, format, type, pixels);
504}
505
506void TextureCubeMap::setImagePosZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
507{
508 setImage(4, level, internalFormat, width, height, format, type, pixels);
509}
510
511void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
512{
513 setImage(5, level, internalFormat, width, height, format, type, pixels);
514}
515
516// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
517bool TextureCubeMap::isComplete()
518{
519 if (mWidth <= 0 || mHeight <= 0 || mWidth != mHeight)
520 {
521 return false;
522 }
523
524 bool mipmapping;
525
526 switch (mMagFilter)
527 {
528 case GL_NEAREST:
529 case GL_LINEAR:
530 mipmapping = false;
531 break;
532 case GL_NEAREST_MIPMAP_NEAREST:
533 case GL_LINEAR_MIPMAP_NEAREST:
534 case GL_NEAREST_MIPMAP_LINEAR:
535 case GL_LINEAR_MIPMAP_LINEAR:
536 mipmapping = true;
537 break;
538 default: UNREACHABLE();
539 }
540
541 for (int face = 0; face < 6; face++)
542 {
543 if (mImageArray[face][0].width != mWidth || mImageArray[face][0].height != mHeight)
544 {
545 return false;
546 }
547 }
548
549 if (mipmapping)
550 {
551 int q = log2(mWidth);
552
553 for (int face = 0; face < 6; face++)
554 {
555 for (int level = 1; level <= q; level++)
556 {
557 if (mImageArray[face][level].format != mImageArray[0][0].format)
558 {
559 return false;
560 }
561
562 if (mImageArray[face][level].internalFormat != mImageArray[0][0].internalFormat)
563 {
564 return false;
565 }
566
567 if (mImageArray[face][level].type != mImageArray[0][0].type)
568 {
569 return false;
570 }
571
572 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
573 {
574 return false;
575 }
576
577 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
578 {
579 return false;
580 }
581 }
582 }
583 }
584
585 return true;
586}
587
588// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
589IDirect3DBaseTexture9 *TextureCubeMap::getTexture()
590{
591 if (!isComplete())
592 {
593 return NULL;
594 }
595
596 if (!mTexture) // FIXME: Recreate when changed (same for getRenderTarget)
597 {
598 IDirect3DDevice9 *device = getDevice();
599 D3DFORMAT format = selectFormat(mImageArray[0][0]);
600
601 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &mTexture, NULL);
602
603 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
604 {
605 error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
606 }
607
608 ASSERT(SUCCEEDED(result));
609
610 IDirect3DCubeTexture9 *lockableTexture;
611 result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_DYNAMIC, format, D3DPOOL_SYSTEMMEM, &lockableTexture, NULL);
612
613 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
614 {
615 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
616 }
617
618 ASSERT(SUCCEEDED(result));
619
620 if (mTexture)
621 {
622 for (int face = 0; face < 6; face++)
623 {
624 for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
625 {
626 D3DLOCKED_RECT lock = {0};
627 lockableTexture->LockRect((D3DCUBEMAP_FACES)face, level, &lock, NULL, 0);
628
629 copyImage(lock, format, mImageArray[face][level]);
630
631 lockableTexture->UnlockRect((D3DCUBEMAP_FACES)face, level);
632 }
633 }
634
635 device->UpdateTexture(lockableTexture, mTexture);
636 lockableTexture->Release();
637 }
638 }
639
640 return mTexture;
641}
642
643void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
644{
645 if (level < 0 || level >= MAX_TEXTURE_LEVELS)
646 {
647 return;
648 }
649
650 mImageArray[face][level].internalFormat = internalFormat;
651 mImageArray[face][level].width = width;
652 mImageArray[face][level].height = height;
653 mImageArray[face][level].format = format;
654 mImageArray[face][level].type = type;
655
656 delete[] mImageArray[face][level].pixels;
657 mImageArray[face][level].pixels = NULL;
658
659 int imageSize = pixelSize(mImageArray[face][level]) * width * height;
660 mImageArray[face][level].pixels = new unsigned char[imageSize];
661
662 if (pixels)
663 {
664 memcpy(mImageArray[face][level].pixels, pixels, imageSize);
665 }
666
667 if (face == 0 && level == 0)
668 {
669 mWidth = width;
670 mHeight = height;
671 }
672}
673}