blob: c886745acbeb453e06c58112a651b465074283eb [file] [log] [blame]
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +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 "libGLESv2/Texture.h"
12
13#include <d3dx9tex.h>
14
15#include <algorithm>
jbauman@chromium.orgf1f28c82011-05-12 20:53:34 +000016#include <intrin.h>
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000017
18#include "common/debug.h"
19
jbauman@chromium.orgae345802011-03-30 22:04:25 +000020#include "libEGL/Display.h"
21
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000022#include "libGLESv2/main.h"
23#include "libGLESv2/mathutil.h"
24#include "libGLESv2/utilities.h"
25#include "libGLESv2/Blit.h"
26#include "libGLESv2/Framebuffer.h"
27
28namespace gl
29{
daniel@transgaming.coma9eb5da2011-03-21 16:39:16 +000030unsigned int Texture::mCurrentSerial = 1;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000031
daniel@transgaming.comde631782011-11-09 17:45:04 +000032Image::Image()
daniel@transgaming.comdff362f2011-11-09 17:45:08 +000033 : mWidth(0), mHeight(0), mDirty(false), mSurface(NULL), mFormat(GL_NONE), mType(GL_UNSIGNED_BYTE)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000034{
35}
36
daniel@transgaming.comde631782011-11-09 17:45:04 +000037Image::~Image()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000038{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +000039 if (mSurface)
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +000040 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +000041 mSurface->Release();
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +000042 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000043}
44
daniel@transgaming.com41634052011-11-09 17:46:24 +000045bool Image::redefine(GLenum format, GLsizei width, GLsizei height, GLenum type)
daniel@transgaming.comdff362f2011-11-09 17:45:08 +000046{
daniel@transgaming.com4c0a7712011-11-09 17:45:19 +000047 if (mWidth != width ||
48 mHeight != height ||
49 mFormat != format ||
50 mType != type)
51 {
daniel@transgaming.com41634052011-11-09 17:46:24 +000052 mWidth = width;
53 mHeight = height;
54 mFormat = format;
55 mType = type;
56
daniel@transgaming.com4c0a7712011-11-09 17:45:19 +000057 if (mSurface)
58 {
59 mSurface->Release();
60 mSurface = NULL;
61 }
daniel@transgaming.com41634052011-11-09 17:46:24 +000062
63 return true;
daniel@transgaming.com4c0a7712011-11-09 17:45:19 +000064 }
65
daniel@transgaming.com41634052011-11-09 17:46:24 +000066 return false;
daniel@transgaming.comdff362f2011-11-09 17:45:08 +000067}
68
69void Image::createSurface()
70{
71 if(mSurface)
72 {
73 return;
74 }
75
76 IDirect3DTexture9 *newTexture = NULL;
77 IDirect3DSurface9 *newSurface = NULL;
78
79 if (mWidth != 0 && mHeight != 0)
80 {
81 int levelToFetch = 0;
82 GLsizei requestWidth = mWidth;
83 GLsizei requestHeight = mHeight;
84 if (IsCompressed(mFormat) && (mWidth % 4 != 0 || mHeight % 4 != 0))
85 {
86 bool isMult4 = false;
87 int upsampleCount = 0;
88 while (!isMult4)
89 {
90 requestWidth <<= 1;
91 requestHeight <<= 1;
92 upsampleCount++;
93 if (requestWidth % 4 == 0 && requestHeight % 4 == 0)
94 {
95 isMult4 = true;
96 }
97 }
98 levelToFetch = upsampleCount;
99 }
100
101 HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, getD3DFormat(),
102 D3DPOOL_SYSTEMMEM, &newTexture, NULL);
103
104 if (FAILED(result))
105 {
106 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com73de05a2011-11-09 17:45:24 +0000107 ERR("Creating image surface failed.");
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000108 return error(GL_OUT_OF_MEMORY);
109 }
110
111 newTexture->GetSurfaceLevel(levelToFetch, &newSurface);
112 newTexture->Release();
113 }
114
115 mSurface = newSurface;
daniel@transgaming.com839fb9b2011-11-09 17:45:43 +0000116 mDirty = false;
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000117}
118
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +0000119HRESULT Image::lock(D3DLOCKED_RECT *lockedRect, const RECT *rect)
120{
121 createSurface();
122
123 HRESULT result = D3DERR_INVALIDCALL;
124
125 if (mSurface)
126 {
127 result = mSurface->LockRect(lockedRect, rect, 0);
128 ASSERT(SUCCEEDED(result));
129
130 mDirty = true;
131 }
132
133 return result;
134}
135
136void Image::unlock()
137{
138 if (mSurface)
139 {
140 HRESULT result = mSurface->UnlockRect();
141 ASSERT(SUCCEEDED(result));
142 }
143}
144
daniel@transgaming.comde631782011-11-09 17:45:04 +0000145bool Image::isRenderable() const
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000146{
147 switch(getD3DFormat())
148 {
149 case D3DFMT_L8:
150 case D3DFMT_A8L8:
151 case D3DFMT_DXT1:
gman@chromium.org50c526d2011-08-10 05:19:44 +0000152 case D3DFMT_DXT3:
153 case D3DFMT_DXT5:
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000154 return false;
155 case D3DFMT_A8R8G8B8:
156 case D3DFMT_X8R8G8B8:
157 case D3DFMT_A16B16G16R16F:
158 case D3DFMT_A32B32G32R32F:
159 return true;
160 default:
161 UNREACHABLE();
162 }
163
164 return false;
165}
166
daniel@transgaming.comde631782011-11-09 17:45:04 +0000167D3DFORMAT Image::getD3DFormat() const
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000168{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000169 if (mFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
170 mFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000171 {
172 return D3DFMT_DXT1;
173 }
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000174 else if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE)
gman@chromium.org50c526d2011-08-10 05:19:44 +0000175 {
176 return D3DFMT_DXT3;
177 }
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000178 else if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE)
gman@chromium.org50c526d2011-08-10 05:19:44 +0000179 {
180 return D3DFMT_DXT5;
181 }
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000182 else if (mType == GL_FLOAT)
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000183 {
184 return D3DFMT_A32B32G32R32F;
185 }
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000186 else if (mType == GL_HALF_FLOAT_OES)
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000187 {
188 return D3DFMT_A16B16G16R16F;
189 }
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000190 else if (mType == GL_UNSIGNED_BYTE)
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000191 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000192 if (mFormat == GL_LUMINANCE && getContext()->supportsLuminanceTextures())
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000193 {
194 return D3DFMT_L8;
195 }
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000196 else if (mFormat == GL_LUMINANCE_ALPHA && getContext()->supportsLuminanceAlphaTextures())
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000197 {
198 return D3DFMT_A8L8;
199 }
daniel@transgaming.comdff362f2011-11-09 17:45:08 +0000200 else if (mFormat == GL_RGB)
daniel@transgaming.com549bdef2011-03-29 00:57:01 +0000201 {
202 return D3DFMT_X8R8G8B8;
203 }
204
205 return D3DFMT_A8R8G8B8;
206 }
207
208 return D3DFMT_A8R8G8B8;
209}
210
daniel@transgaming.com73de05a2011-11-09 17:45:24 +0000211IDirect3DSurface9 *Image::getSurface()
212{
213 createSurface();
214
215 return mSurface;
216}
217
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000218// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
219// into the target pixel rectangle at output with outputPitch bytes in between each line.
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000220void Image::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum type,
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000221 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
222{
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000223 GLsizei inputPitch = -ComputePitch(width, mFormat, type, unpackAlignment);
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000224 input = ((char*)input) - inputPitch * (height - 1);
225
226 switch (type)
227 {
228 case GL_UNSIGNED_BYTE:
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000229 switch (mFormat)
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000230 {
231 case GL_ALPHA:
232 loadAlphaData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
233 break;
234 case GL_LUMINANCE:
235 loadLuminanceData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, getD3DFormat() == D3DFMT_L8);
236 break;
237 case GL_LUMINANCE_ALPHA:
238 loadLuminanceAlphaData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, getD3DFormat() == D3DFMT_A8L8);
239 break;
240 case GL_RGB:
241 loadRGBUByteData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
242 break;
243 case GL_RGBA:
244 if (supportsSSE2())
245 {
246 loadRGBAUByteDataSSE2(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
247 }
248 else
249 {
250 loadRGBAUByteData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
251 }
252 break;
253 case GL_BGRA_EXT:
254 loadBGRAData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
255 break;
256 default: UNREACHABLE();
257 }
258 break;
259 case GL_UNSIGNED_SHORT_5_6_5:
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000260 switch (mFormat)
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000261 {
262 case GL_RGB:
263 loadRGB565Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
264 break;
265 default: UNREACHABLE();
266 }
267 break;
268 case GL_UNSIGNED_SHORT_4_4_4_4:
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000269 switch (mFormat)
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000270 {
271 case GL_RGBA:
272 loadRGBA4444Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
273 break;
274 default: UNREACHABLE();
275 }
276 break;
277 case GL_UNSIGNED_SHORT_5_5_5_1:
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000278 switch (mFormat)
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000279 {
280 case GL_RGBA:
281 loadRGBA5551Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
282 break;
283 default: UNREACHABLE();
284 }
285 break;
286 case GL_FLOAT:
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000287 switch (mFormat)
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000288 {
289 // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
290 case GL_ALPHA:
291 loadAlphaFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
292 break;
293 case GL_LUMINANCE:
294 loadLuminanceFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
295 break;
296 case GL_LUMINANCE_ALPHA:
297 loadLuminanceAlphaFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
298 break;
299 case GL_RGB:
300 loadRGBFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
301 break;
302 case GL_RGBA:
303 loadRGBAFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
304 break;
305 default: UNREACHABLE();
306 }
307 break;
308 case GL_HALF_FLOAT_OES:
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +0000309 switch (mFormat)
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +0000310 {
311 // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
312 case GL_ALPHA:
313 loadAlphaHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
314 break;
315 case GL_LUMINANCE:
316 loadLuminanceHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
317 break;
318 case GL_LUMINANCE_ALPHA:
319 loadLuminanceAlphaHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
320 break;
321 case GL_RGB:
322 loadRGBHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
323 break;
324 case GL_RGBA:
325 loadRGBAHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
326 break;
327 default: UNREACHABLE();
328 }
329 break;
330 default: UNREACHABLE();
331 }
332}
333
334void Image::loadAlphaData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
335 int inputPitch, const void *input, size_t outputPitch, void *output) const
336{
337 const unsigned char *source = NULL;
338 unsigned char *dest = NULL;
339
340 for (int y = 0; y < height; y++)
341 {
342 source = static_cast<const unsigned char*>(input) + y * inputPitch;
343 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
344 for (int x = 0; x < width; x++)
345 {
346 dest[4 * x + 0] = 0;
347 dest[4 * x + 1] = 0;
348 dest[4 * x + 2] = 0;
349 dest[4 * x + 3] = source[x];
350 }
351 }
352}
353
354void Image::loadAlphaFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
355 int inputPitch, const void *input, size_t outputPitch, void *output) const
356{
357 const float *source = NULL;
358 float *dest = NULL;
359
360 for (int y = 0; y < height; y++)
361 {
362 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
363 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
364 for (int x = 0; x < width; x++)
365 {
366 dest[4 * x + 0] = 0;
367 dest[4 * x + 1] = 0;
368 dest[4 * x + 2] = 0;
369 dest[4 * x + 3] = source[x];
370 }
371 }
372}
373
374void Image::loadAlphaHalfFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
375 int inputPitch, const void *input, size_t outputPitch, void *output) const
376{
377 const unsigned short *source = NULL;
378 unsigned short *dest = NULL;
379
380 for (int y = 0; y < height; y++)
381 {
382 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
383 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
384 for (int x = 0; x < width; x++)
385 {
386 dest[4 * x + 0] = 0;
387 dest[4 * x + 1] = 0;
388 dest[4 * x + 2] = 0;
389 dest[4 * x + 3] = source[x];
390 }
391 }
392}
393
394void Image::loadLuminanceData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
395 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
396{
397 const int destBytesPerPixel = native? 1: 4;
398 const unsigned char *source = NULL;
399 unsigned char *dest = NULL;
400
401 for (int y = 0; y < height; y++)
402 {
403 source = static_cast<const unsigned char*>(input) + y * inputPitch;
404 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * destBytesPerPixel;
405
406 if (!native) // BGRA8 destination format
407 {
408 for (int x = 0; x < width; x++)
409 {
410 dest[4 * x + 0] = source[x];
411 dest[4 * x + 1] = source[x];
412 dest[4 * x + 2] = source[x];
413 dest[4 * x + 3] = 0xFF;
414 }
415 }
416 else // L8 destination format
417 {
418 memcpy(dest, source, width);
419 }
420 }
421}
422
423void Image::loadLuminanceFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
424 int inputPitch, const void *input, size_t outputPitch, void *output) const
425{
426 const float *source = NULL;
427 float *dest = NULL;
428
429 for (int y = 0; y < height; y++)
430 {
431 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
432 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
433 for (int x = 0; x < width; x++)
434 {
435 dest[4 * x + 0] = source[x];
436 dest[4 * x + 1] = source[x];
437 dest[4 * x + 2] = source[x];
438 dest[4 * x + 3] = 1.0f;
439 }
440 }
441}
442
443void Image::loadLuminanceHalfFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
444 int inputPitch, const void *input, size_t outputPitch, void *output) const
445{
446 const unsigned short *source = NULL;
447 unsigned short *dest = NULL;
448
449 for (int y = 0; y < height; y++)
450 {
451 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
452 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
453 for (int x = 0; x < width; x++)
454 {
455 dest[4 * x + 0] = source[x];
456 dest[4 * x + 1] = source[x];
457 dest[4 * x + 2] = source[x];
458 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
459 }
460 }
461}
462
463void Image::loadLuminanceAlphaData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
464 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
465{
466 const int destBytesPerPixel = native? 2: 4;
467 const unsigned char *source = NULL;
468 unsigned char *dest = NULL;
469
470 for (int y = 0; y < height; y++)
471 {
472 source = static_cast<const unsigned char*>(input) + y * inputPitch;
473 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * destBytesPerPixel;
474
475 if (!native) // BGRA8 destination format
476 {
477 for (int x = 0; x < width; x++)
478 {
479 dest[4 * x + 0] = source[2*x+0];
480 dest[4 * x + 1] = source[2*x+0];
481 dest[4 * x + 2] = source[2*x+0];
482 dest[4 * x + 3] = source[2*x+1];
483 }
484 }
485 else
486 {
487 memcpy(dest, source, width * 2);
488 }
489 }
490}
491
492void Image::loadLuminanceAlphaFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
493 int inputPitch, const void *input, size_t outputPitch, void *output) const
494{
495 const float *source = NULL;
496 float *dest = NULL;
497
498 for (int y = 0; y < height; y++)
499 {
500 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
501 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
502 for (int x = 0; x < width; x++)
503 {
504 dest[4 * x + 0] = source[2*x+0];
505 dest[4 * x + 1] = source[2*x+0];
506 dest[4 * x + 2] = source[2*x+0];
507 dest[4 * x + 3] = source[2*x+1];
508 }
509 }
510}
511
512void Image::loadLuminanceAlphaHalfFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
513 int inputPitch, const void *input, size_t outputPitch, void *output) const
514{
515 const unsigned short *source = NULL;
516 unsigned short *dest = NULL;
517
518 for (int y = 0; y < height; y++)
519 {
520 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
521 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
522 for (int x = 0; x < width; x++)
523 {
524 dest[4 * x + 0] = source[2*x+0];
525 dest[4 * x + 1] = source[2*x+0];
526 dest[4 * x + 2] = source[2*x+0];
527 dest[4 * x + 3] = source[2*x+1];
528 }
529 }
530}
531
532void Image::loadRGBUByteData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
533 int inputPitch, const void *input, size_t outputPitch, void *output) const
534{
535 const unsigned char *source = NULL;
536 unsigned char *dest = NULL;
537
538 for (int y = 0; y < height; y++)
539 {
540 source = static_cast<const unsigned char*>(input) + y * inputPitch;
541 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
542 for (int x = 0; x < width; x++)
543 {
544 dest[4 * x + 0] = source[x * 3 + 2];
545 dest[4 * x + 1] = source[x * 3 + 1];
546 dest[4 * x + 2] = source[x * 3 + 0];
547 dest[4 * x + 3] = 0xFF;
548 }
549 }
550}
551
552void Image::loadRGB565Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
553 int inputPitch, const void *input, size_t outputPitch, void *output) const
554{
555 const unsigned short *source = NULL;
556 unsigned char *dest = NULL;
557
558 for (int y = 0; y < height; y++)
559 {
560 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
561 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
562 for (int x = 0; x < width; x++)
563 {
564 unsigned short rgba = source[x];
565 dest[4 * x + 0] = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
566 dest[4 * x + 1] = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
567 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
568 dest[4 * x + 3] = 0xFF;
569 }
570 }
571}
572
573void Image::loadRGBFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
574 int inputPitch, const void *input, size_t outputPitch, void *output) const
575{
576 const float *source = NULL;
577 float *dest = NULL;
578
579 for (int y = 0; y < height; y++)
580 {
581 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
582 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
583 for (int x = 0; x < width; x++)
584 {
585 dest[4 * x + 0] = source[x * 3 + 0];
586 dest[4 * x + 1] = source[x * 3 + 1];
587 dest[4 * x + 2] = source[x * 3 + 2];
588 dest[4 * x + 3] = 1.0f;
589 }
590 }
591}
592
593void Image::loadRGBHalfFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
594 int inputPitch, const void *input, size_t outputPitch, void *output) const
595{
596 const unsigned short *source = NULL;
597 unsigned short *dest = NULL;
598
599 for (int y = 0; y < height; y++)
600 {
601 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
602 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
603 for (int x = 0; x < width; x++)
604 {
605 dest[4 * x + 0] = source[x * 3 + 0];
606 dest[4 * x + 1] = source[x * 3 + 1];
607 dest[4 * x + 2] = source[x * 3 + 2];
608 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
609 }
610 }
611}
612
613void Image::loadRGBAUByteDataSSE2(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
614 int inputPitch, const void *input, size_t outputPitch, void *output) const
615{
616 const unsigned int *source = NULL;
617 unsigned int *dest = NULL;
618 __m128i brMask = _mm_set1_epi32(0x00ff00ff);
619
620 for (int y = 0; y < height; y++)
621 {
622 source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
623 dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4);
624 int x = 0;
625
626 // Make output writes aligned
627 for (x = 0; ((reinterpret_cast<intptr_t>(&dest[x]) & 15) != 0) && x < width; x++)
628 {
629 unsigned int rgba = source[x];
630 dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
631 }
632
633 for (; x + 3 < width; x += 4)
634 {
635 __m128i sourceData = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&source[x]));
636 // Mask out g and a, which don't change
637 __m128i gaComponents = _mm_andnot_si128(brMask, sourceData);
638 // Mask out b and r
639 __m128i brComponents = _mm_and_si128(sourceData, brMask);
640 // Swap b and r
641 __m128i brSwapped = _mm_shufflehi_epi16(_mm_shufflelo_epi16(brComponents, _MM_SHUFFLE(2, 3, 0, 1)), _MM_SHUFFLE(2, 3, 0, 1));
642 __m128i result = _mm_or_si128(gaComponents, brSwapped);
643 _mm_store_si128(reinterpret_cast<__m128i*>(&dest[x]), result);
644 }
645
646 // Perform leftover writes
647 for (; x < width; x++)
648 {
649 unsigned int rgba = source[x];
650 dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
651 }
652 }
653}
654
655void Image::loadRGBAUByteData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
656 int inputPitch, const void *input, size_t outputPitch, void *output) const
657{
658 const unsigned int *source = NULL;
659 unsigned int *dest = NULL;
660 for (int y = 0; y < height; y++)
661 {
662 source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
663 dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4);
664
665 for (int x = 0; x < width; x++)
666 {
667 unsigned int rgba = source[x];
668 dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
669 }
670 }
671}
672
673void Image::loadRGBA4444Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
674 int inputPitch, const void *input, size_t outputPitch, void *output) const
675{
676 const unsigned short *source = NULL;
677 unsigned char *dest = NULL;
678
679 for (int y = 0; y < height; y++)
680 {
681 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
682 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
683 for (int x = 0; x < width; x++)
684 {
685 unsigned short rgba = source[x];
686 dest[4 * x + 0] = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
687 dest[4 * x + 1] = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
688 dest[4 * x + 2] = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
689 dest[4 * x + 3] = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
690 }
691 }
692}
693
694void Image::loadRGBA5551Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
695 int inputPitch, const void *input, size_t outputPitch, void *output) const
696{
697 const unsigned short *source = NULL;
698 unsigned char *dest = NULL;
699
700 for (int y = 0; y < height; y++)
701 {
702 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
703 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
704 for (int x = 0; x < width; x++)
705 {
706 unsigned short rgba = source[x];
707 dest[4 * x + 0] = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
708 dest[4 * x + 1] = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
709 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
710 dest[4 * x + 3] = (rgba & 0x0001) ? 0xFF : 0;
711 }
712 }
713}
714
715void Image::loadRGBAFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
716 int inputPitch, const void *input, size_t outputPitch, void *output) const
717{
718 const float *source = NULL;
719 float *dest = NULL;
720
721 for (int y = 0; y < height; y++)
722 {
723 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
724 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
725 memcpy(dest, source, width * 16);
726 }
727}
728
729void Image::loadRGBAHalfFloatData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
730 int inputPitch, const void *input, size_t outputPitch, void *output) const
731{
732 const unsigned char *source = NULL;
733 unsigned char *dest = NULL;
734
735 for (int y = 0; y < height; y++)
736 {
737 source = static_cast<const unsigned char*>(input) + y * inputPitch;
738 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8;
739 memcpy(dest, source, width * 8);
740 }
741}
742
743void Image::loadBGRAData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
744 int inputPitch, const void *input, size_t outputPitch, void *output) const
745{
746 const unsigned char *source = NULL;
747 unsigned char *dest = NULL;
748
749 for (int y = 0; y < height; y++)
750 {
751 source = static_cast<const unsigned char*>(input) + y * inputPitch;
752 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
753 memcpy(dest, source, width*4);
754 }
755}
756
757void Image::loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
758 int inputPitch, const void *input, size_t outputPitch, void *output) const {
759 switch (getD3DFormat())
760 {
761 case D3DFMT_DXT1:
762 loadDXT1Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
763 break;
764 case D3DFMT_DXT3:
765 loadDXT3Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
766 break;
767 case D3DFMT_DXT5:
768 loadDXT5Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
769 break;
770 }
771}
772
773static void FlipCopyDXT1BlockFull(const unsigned int* source, unsigned int* dest) {
774 // A DXT1 block layout is:
775 // [0-1] color0.
776 // [2-3] color1.
777 // [4-7] color bitmap, 2 bits per pixel.
778 // So each of the 4-7 bytes represents one line, flipping a block is just
779 // flipping those bytes.
780
781 // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
782 dest[0] = source[0];
783
784 // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors. All rows should be flipped.
785 dest[1] = (source[1] >> 24) |
786 ((source[1] << 8) & 0x00FF0000) |
787 ((source[1] >> 8) & 0x0000FF00) |
788 (source[1] << 24);
789}
790
791// Flips the first 2 lines of a DXT1 block in the y direction.
792static void FlipCopyDXT1BlockHalf(const unsigned int* source, unsigned int* dest) {
793 // See layout above.
794 dest[0] = source[0];
795 dest[1] = ((source[1] << 8) & 0x0000FF00) |
796 ((source[1] >> 8) & 0x000000FF);
797}
798
799// Flips a full DXT3 block in the y direction.
800static void FlipCopyDXT3BlockFull(const unsigned int* source, unsigned int* dest) {
801 // A DXT3 block layout is:
802 // [0-7] alpha bitmap, 4 bits per pixel.
803 // [8-15] a DXT1 block.
804
805 // First and Second 32 bits are 4bit per pixel alpha and need to be flipped.
806 dest[0] = (source[1] >> 16) | (source[1] << 16);
807 dest[1] = (source[0] >> 16) | (source[0] << 16);
808
809 // And flip the DXT1 block using the above function.
810 FlipCopyDXT1BlockFull(source + 2, dest + 2);
811}
812
813// Flips the first 2 lines of a DXT3 block in the y direction.
814static void FlipCopyDXT3BlockHalf(const unsigned int* source, unsigned int* dest) {
815 // See layout above.
816 dest[0] = (source[1] >> 16) | (source[1] << 16);
817 FlipCopyDXT1BlockHalf(source + 2, dest + 2);
818}
819
820// Flips a full DXT5 block in the y direction.
821static void FlipCopyDXT5BlockFull(const unsigned int* source, unsigned int* dest) {
822 // A DXT5 block layout is:
823 // [0] alpha0.
824 // [1] alpha1.
825 // [2-7] alpha bitmap, 3 bits per pixel.
826 // [8-15] a DXT1 block.
827
828 // The alpha bitmap doesn't easily map lines to bytes, so we have to
829 // interpret it correctly. Extracted from
830 // http://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt :
831 //
832 // The 6 "bits" bytes of the block are decoded into one 48-bit integer:
833 //
834 // bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 +
835 // 256 * (bits_4 + 256 * bits_5))))
836 //
837 // bits is a 48-bit unsigned integer, from which a three-bit control code
838 // is extracted for a texel at location (x,y) in the block using:
839 //
840 // code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0]
841 //
842 // where bit 47 is the most significant and bit 0 is the least
843 // significant bit.
844 const unsigned char* sourceBytes = static_cast<const unsigned char*>(static_cast<const void*>(source));
845 unsigned char* destBytes = static_cast<unsigned char*>(static_cast<void*>(dest));
846 unsigned int line_0_1 = sourceBytes[2] + 256 * (sourceBytes[3] + 256 * sourceBytes[4]);
847 unsigned int line_2_3 = sourceBytes[5] + 256 * (sourceBytes[6] + 256 * sourceBytes[7]);
848 // swap lines 0 and 1 in line_0_1.
849 unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) |
850 ((line_0_1 & 0xfff000) >> 12);
851 // swap lines 2 and 3 in line_2_3.
852 unsigned int line_3_2 = ((line_2_3 & 0x000fff) << 12) |
853 ((line_2_3 & 0xfff000) >> 12);
854 destBytes[0] = sourceBytes[0];
855 destBytes[1] = sourceBytes[1];
856 destBytes[2] = line_3_2 & 0xff;
857 destBytes[3] = (line_3_2 & 0xff00) >> 8;
858 destBytes[4] = (line_3_2 & 0xff0000) >> 16;
859 destBytes[5] = line_1_0 & 0xff;
860 destBytes[6] = (line_1_0 & 0xff00) >> 8;
861 destBytes[7] = (line_1_0 & 0xff0000) >> 16;
862
863 // And flip the DXT1 block using the above function.
864 FlipCopyDXT1BlockFull(source + 2, dest + 2);
865}
866
867// Flips the first 2 lines of a DXT5 block in the y direction.
868static void FlipCopyDXT5BlockHalf(const unsigned int* source, unsigned int* dest) {
869 // See layout above.
870 const unsigned char* sourceBytes = static_cast<const unsigned char*>(static_cast<const void*>(source));
871 unsigned char* destBytes = static_cast<unsigned char*>(static_cast<void*>(dest));
872 unsigned int line_0_1 = sourceBytes[2] + 256 * (sourceBytes[3] + 256 * sourceBytes[4]);
873 unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) |
874 ((line_0_1 & 0xfff000) >> 12);
875 destBytes[0] = sourceBytes[0];
876 destBytes[1] = sourceBytes[1];
877 destBytes[2] = line_1_0 & 0xff;
878 destBytes[3] = (line_1_0 & 0xff00) >> 8;
879 destBytes[4] = (line_1_0 & 0xff0000) >> 16;
880 FlipCopyDXT1BlockHalf(source + 2, dest + 2);
881}
882
883void Image::loadDXT1Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
884 int inputPitch, const void *input, size_t outputPitch, void *output) const
885{
886 ASSERT(xoffset % 4 == 0);
887 ASSERT(yoffset % 4 == 0);
888 ASSERT(width % 4 == 0 || width == 2 || width == 1);
889 ASSERT(inputPitch % 8 == 0);
890 ASSERT(outputPitch % 8 == 0);
891
892 const unsigned int *source = reinterpret_cast<const unsigned int*>(input);
893 unsigned int *dest = reinterpret_cast<unsigned int*>(output);
894
895 // Round width up in case it is less than 4.
896 int blocksAcross = (width + 3) / 4;
897 int intsAcross = blocksAcross * 2;
898
899 switch (height)
900 {
901 case 1:
902 for (int x = 0; x < intsAcross; x += 2)
903 {
904 // just copy the block
905 dest[x] = source[x];
906 dest[x + 1] = source[x + 1];
907 }
908 break;
909 case 2:
910 for (int x = 0; x < intsAcross; x += 2)
911 {
912 FlipCopyDXT1BlockHalf(source + x, dest + x);
913 }
914 break;
915 default:
916 ASSERT(height % 4 == 0);
917 for (int y = 0; y < height / 4; ++y)
918 {
919 const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
920 unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
921
922 for (int x = 0; x < intsAcross; x += 2)
923 {
924 FlipCopyDXT1BlockFull(source + x, dest + x);
925 }
926 }
927 break;
928 }
929}
930
931void Image::loadDXT3Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
932 int inputPitch, const void *input, size_t outputPitch, void *output) const
933{
934 ASSERT(xoffset % 4 == 0);
935 ASSERT(yoffset % 4 == 0);
936 ASSERT(width % 4 == 0 || width == 2 || width == 1);
937 ASSERT(inputPitch % 16 == 0);
938 ASSERT(outputPitch % 16 == 0);
939
940 const unsigned int *source = reinterpret_cast<const unsigned int*>(input);
941 unsigned int *dest = reinterpret_cast<unsigned int*>(output);
942
943 // Round width up in case it is less than 4.
944 int blocksAcross = (width + 3) / 4;
945 int intsAcross = blocksAcross * 4;
946
947 switch (height)
948 {
949 case 1:
950 for (int x = 0; x < intsAcross; x += 4)
951 {
952 // just copy the block
953 dest[x] = source[x];
954 dest[x + 1] = source[x + 1];
955 dest[x + 2] = source[x + 2];
956 dest[x + 3] = source[x + 3];
957 }
958 break;
959 case 2:
960 for (int x = 0; x < intsAcross; x += 4)
961 {
962 FlipCopyDXT3BlockHalf(source + x, dest + x);
963 }
964 break;
965 default:
966 ASSERT(height % 4 == 0);
967 for (int y = 0; y < height / 4; ++y)
968 {
969 const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
970 unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
971
972 for (int x = 0; x < intsAcross; x += 4)
973 {
974 FlipCopyDXT3BlockFull(source + x, dest + x);
975 }
976 }
977 break;
978 }
979}
980
981void Image::loadDXT5Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
982 int inputPitch, const void *input, size_t outputPitch, void *output) const
983{
984 ASSERT(xoffset % 4 == 0);
985 ASSERT(yoffset % 4 == 0);
986 ASSERT(width % 4 == 0 || width == 2 || width == 1);
987 ASSERT(inputPitch % 16 == 0);
988 ASSERT(outputPitch % 16 == 0);
989
990 const unsigned int *source = reinterpret_cast<const unsigned int*>(input);
991 unsigned int *dest = reinterpret_cast<unsigned int*>(output);
992
993 // Round width up in case it is less than 4.
994 int blocksAcross = (width + 3) / 4;
995 int intsAcross = blocksAcross * 4;
996
997 switch (height)
998 {
999 case 1:
1000 for (int x = 0; x < intsAcross; x += 4)
1001 {
1002 // just copy the block
1003 dest[x] = source[x];
1004 dest[x + 1] = source[x + 1];
1005 dest[x + 2] = source[x + 2];
1006 dest[x + 3] = source[x + 3];
1007 }
1008 break;
1009 case 2:
1010 for (int x = 0; x < intsAcross; x += 4)
1011 {
1012 FlipCopyDXT5BlockHalf(source + x, dest + x);
1013 }
1014 break;
1015 default:
1016 ASSERT(height % 4 == 0);
1017 for (int y = 0; y < height / 4; ++y)
1018 {
1019 const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
1020 unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
1021
1022 for (int x = 0; x < intsAcross; x += 4)
1023 {
1024 FlipCopyDXT5BlockFull(source + x, dest + x);
1025 }
1026 }
1027 break;
1028 }
1029}
1030
daniel@transgaming.com90cfcc92011-11-09 17:45:48 +00001031// This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats and incomplete textures
1032void Image::copy(GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget)
1033{
1034 IDirect3DDevice9 *device = getDevice();
1035 IDirect3DSurface9 *renderTargetData = NULL;
1036 D3DSURFACE_DESC description;
1037 renderTarget->GetDesc(&description);
1038
1039 HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &renderTargetData, NULL);
1040
1041 if (FAILED(result))
1042 {
1043 ERR("Could not create matching destination surface.");
1044 return error(GL_OUT_OF_MEMORY);
1045 }
1046
1047 result = device->GetRenderTargetData(renderTarget, renderTargetData);
1048
1049 if (FAILED(result))
1050 {
1051 ERR("GetRenderTargetData unexpectedly failed.");
1052 renderTargetData->Release();
1053 return error(GL_OUT_OF_MEMORY);
1054 }
1055
1056 RECT sourceRect = transformPixelRect(x, y, width, height, description.Height);
1057 int destYOffset = transformPixelYOffset(yoffset, height, mHeight);
1058 RECT destRect = {xoffset, destYOffset, xoffset + width, destYOffset + height};
1059
1060 if (isRenderable())
1061 {
1062 result = D3DXLoadSurfaceFromSurface(getSurface(), NULL, &destRect, renderTargetData, NULL, &sourceRect, D3DX_FILTER_BOX, 0);
1063
1064 if (FAILED(result))
1065 {
1066 ERR("Copying surfaces unexpectedly failed.");
1067 renderTargetData->Release();
1068 return error(GL_OUT_OF_MEMORY);
1069 }
1070 }
1071 else
1072 {
1073 D3DLOCKED_RECT sourceLock = {0};
1074 result = renderTargetData->LockRect(&sourceLock, &sourceRect, 0);
1075
1076 if (FAILED(result))
1077 {
1078 ERR("Failed to lock the source surface (rectangle might be invalid).");
1079 renderTargetData->Release();
1080 return error(GL_OUT_OF_MEMORY);
1081 }
1082
1083 D3DLOCKED_RECT destLock = {0};
1084 result = lock(&destLock, &destRect);
1085
1086 if (FAILED(result))
1087 {
1088 ERR("Failed to lock the destination surface (rectangle might be invalid).");
1089 renderTargetData->UnlockRect();
1090 renderTargetData->Release();
1091 return error(GL_OUT_OF_MEMORY);
1092 }
1093
1094 if (destLock.pBits && sourceLock.pBits)
1095 {
1096 unsigned char *source = (unsigned char*)sourceLock.pBits;
1097 unsigned char *dest = (unsigned char*)destLock.pBits;
1098
1099 switch (description.Format)
1100 {
1101 case D3DFMT_X8R8G8B8:
1102 case D3DFMT_A8R8G8B8:
1103 switch(getD3DFormat())
1104 {
1105 case D3DFMT_L8:
1106 for(int y = 0; y < height; y++)
1107 {
1108 for(int x = 0; x < width; x++)
1109 {
1110 dest[x] = source[x * 4 + 2];
1111 }
1112
1113 source += sourceLock.Pitch;
1114 dest += destLock.Pitch;
1115 }
1116 break;
1117 case D3DFMT_A8L8:
1118 for(int y = 0; y < height; y++)
1119 {
1120 for(int x = 0; x < width; x++)
1121 {
1122 dest[x * 2 + 0] = source[x * 4 + 2];
1123 dest[x * 2 + 1] = source[x * 4 + 3];
1124 }
1125
1126 source += sourceLock.Pitch;
1127 dest += destLock.Pitch;
1128 }
1129 break;
1130 default:
1131 UNREACHABLE();
1132 }
1133 break;
1134 case D3DFMT_R5G6B5:
1135 switch(getD3DFormat())
1136 {
1137 case D3DFMT_L8:
1138 for(int y = 0; y < height; y++)
1139 {
1140 for(int x = 0; x < width; x++)
1141 {
1142 unsigned char red = source[x * 2 + 1] & 0xF8;
1143 dest[x] = red | (red >> 5);
1144 }
1145
1146 source += sourceLock.Pitch;
1147 dest += destLock.Pitch;
1148 }
1149 break;
1150 default:
1151 UNREACHABLE();
1152 }
1153 break;
1154 case D3DFMT_A1R5G5B5:
1155 switch(getD3DFormat())
1156 {
1157 case D3DFMT_L8:
1158 for(int y = 0; y < height; y++)
1159 {
1160 for(int x = 0; x < width; x++)
1161 {
1162 unsigned char red = source[x * 2 + 1] & 0x7C;
1163 dest[x] = (red << 1) | (red >> 4);
1164 }
1165
1166 source += sourceLock.Pitch;
1167 dest += destLock.Pitch;
1168 }
1169 break;
1170 case D3DFMT_A8L8:
1171 for(int y = 0; y < height; y++)
1172 {
1173 for(int x = 0; x < width; x++)
1174 {
1175 unsigned char red = source[x * 2 + 1] & 0x7C;
1176 dest[x * 2 + 0] = (red << 1) | (red >> 4);
1177 dest[x * 2 + 1] = (signed char)source[x * 2 + 1] >> 7;
1178 }
1179
1180 source += sourceLock.Pitch;
1181 dest += destLock.Pitch;
1182 }
1183 break;
1184 default:
1185 UNREACHABLE();
1186 }
1187 break;
1188 default:
1189 UNREACHABLE();
1190 }
1191 }
1192
1193 unlock();
1194 renderTargetData->UnlockRect();
1195 }
1196
1197 renderTargetData->Release();
1198
1199 mDirty = true;
1200}
1201
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001202TextureStorage::TextureStorage(bool renderable) : mIsRenderable(renderable)
1203{
1204}
1205
1206TextureStorage::~TextureStorage()
1207{
1208}
1209
1210bool TextureStorage::isRenderable()
1211{
1212 return mIsRenderable;
1213}
1214
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00001215Texture::Texture(GLuint id) : RefCountObject(id)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001216{
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00001217 mSerial = 0;
1218
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001219 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
1220 mMagFilter = GL_LINEAR;
1221 mWrapS = GL_REPEAT;
1222 mWrapT = GL_REPEAT;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001223 mDirtyParameters = true;
daniel@transgaming.coma06aa872011-03-21 17:22:21 +00001224
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001225 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001226}
1227
1228Texture::~Texture()
1229{
1230}
1231
1232Blit *Texture::getBlitter()
1233{
1234 Context *context = getContext();
1235 return context->getBlitter();
1236}
1237
1238// Returns true on successful filter state update (valid enum parameter)
1239bool Texture::setMinFilter(GLenum filter)
1240{
1241 switch (filter)
1242 {
1243 case GL_NEAREST:
1244 case GL_LINEAR:
1245 case GL_NEAREST_MIPMAP_NEAREST:
1246 case GL_LINEAR_MIPMAP_NEAREST:
1247 case GL_NEAREST_MIPMAP_LINEAR:
1248 case GL_LINEAR_MIPMAP_LINEAR:
1249 {
1250 if (mMinFilter != filter)
1251 {
1252 mMinFilter = filter;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001253 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001254 }
1255 return true;
1256 }
1257 default:
1258 return false;
1259 }
1260}
1261
1262// Returns true on successful filter state update (valid enum parameter)
1263bool Texture::setMagFilter(GLenum filter)
1264{
1265 switch (filter)
1266 {
1267 case GL_NEAREST:
1268 case GL_LINEAR:
1269 {
1270 if (mMagFilter != filter)
1271 {
1272 mMagFilter = filter;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001273 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001274 }
1275 return true;
1276 }
1277 default:
1278 return false;
1279 }
1280}
1281
1282// Returns true on successful wrap state update (valid enum parameter)
1283bool Texture::setWrapS(GLenum wrap)
1284{
1285 switch (wrap)
1286 {
1287 case GL_REPEAT:
1288 case GL_CLAMP_TO_EDGE:
1289 case GL_MIRRORED_REPEAT:
1290 {
1291 if (mWrapS != wrap)
1292 {
1293 mWrapS = wrap;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001294 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001295 }
1296 return true;
1297 }
1298 default:
1299 return false;
1300 }
1301}
1302
1303// Returns true on successful wrap state update (valid enum parameter)
1304bool Texture::setWrapT(GLenum wrap)
1305{
1306 switch (wrap)
1307 {
1308 case GL_REPEAT:
1309 case GL_CLAMP_TO_EDGE:
1310 case GL_MIRRORED_REPEAT:
1311 {
1312 if (mWrapT != wrap)
1313 {
1314 mWrapT = wrap;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001315 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001316 }
1317 return true;
1318 }
1319 default:
1320 return false;
1321 }
1322}
1323
1324GLenum Texture::getMinFilter() const
1325{
1326 return mMinFilter;
1327}
1328
1329GLenum Texture::getMagFilter() const
1330{
1331 return mMagFilter;
1332}
1333
1334GLenum Texture::getWrapS() const
1335{
1336 return mWrapS;
1337}
1338
1339GLenum Texture::getWrapT() const
1340{
1341 return mWrapT;
1342}
1343
daniel@transgaming.com61208202011-03-21 16:38:50 +00001344void Texture::setImage(GLint unpackAlignment, const void *pixels, Image *image)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001345{
daniel@transgaming.com73de05a2011-11-09 17:45:24 +00001346 if (pixels != NULL)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001347 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001348 D3DLOCKED_RECT locked;
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001349 HRESULT result = image->lock(&locked, NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001350
1351 if (SUCCEEDED(result))
1352 {
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +00001353 image->loadData(0, 0, image->getWidth(), image->getHeight(), image->getType(), unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001354 image->unlock();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001355 }
1356
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001357 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001358 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001359}
1360
daniel@transgaming.com61208202011-03-21 16:38:50 +00001361void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001362{
daniel@transgaming.com73de05a2011-11-09 17:45:24 +00001363 if (pixels != NULL)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001364 {
1365 D3DLOCKED_RECT locked;
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001366 HRESULT result = image->lock(&locked, NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001367
1368 if (SUCCEEDED(result))
1369 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001370 int inputPitch = ComputeCompressedPitch(image->getWidth(), image->getFormat());
1371 int inputSize = ComputeCompressedSize(image->getWidth(), image->getHeight(), image->getFormat());
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +00001372 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), -inputPitch, static_cast<const char*>(pixels) + inputSize - inputPitch, locked.Pitch, locked.pBits);
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001373 image->unlock();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001374 }
1375
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001376 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001377 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001378}
1379
daniel@transgaming.comb5a3a6b2011-03-21 16:38:46 +00001380bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001381{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001382 if (width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001383 {
1384 error(GL_INVALID_VALUE);
1385 return false;
1386 }
1387
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001388 if (IsCompressed(image->getFormat()))
jbauman@chromium.orge2f954c2011-05-03 20:45:27 +00001389 {
1390 error(GL_INVALID_OPERATION);
1391 return false;
1392 }
1393
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001394 if (format != image->getFormat())
jbauman@chromium.orge2f954c2011-05-03 20:45:27 +00001395 {
1396 error(GL_INVALID_OPERATION);
1397 return false;
1398 }
1399
daniel@transgaming.com73de05a2011-11-09 17:45:24 +00001400 if (pixels != NULL)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001401 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001402 D3DLOCKED_RECT locked;
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001403 HRESULT result = image->lock(&locked, NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001404
1405 if (SUCCEEDED(result))
1406 {
daniel@transgaming.com0c67f3c2011-11-09 17:45:38 +00001407 image->loadData(xoffset, transformPixelYOffset(yoffset, height, image->getHeight()), width, height, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001408 image->unlock();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001409 }
1410
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001411 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001412 }
1413
1414 return true;
1415}
1416
daniel@transgaming.comb5a3a6b2011-03-21 16:38:46 +00001417bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001418{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001419 if (width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001420 {
1421 error(GL_INVALID_VALUE);
1422 return false;
1423 }
1424
1425 if (format != getInternalFormat())
1426 {
1427 error(GL_INVALID_OPERATION);
1428 return false;
1429 }
1430
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001431 if (pixels != NULL)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001432 {
1433 RECT updateRegion;
1434 updateRegion.left = xoffset;
1435 updateRegion.right = xoffset + width;
1436 updateRegion.bottom = yoffset + height;
1437 updateRegion.top = yoffset;
1438
1439 D3DLOCKED_RECT locked;
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001440 HRESULT result = image->lock(&locked, &updateRegion);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001441
1442 if (SUCCEEDED(result))
1443 {
1444 int inputPitch = ComputeCompressedPitch(width, format);
1445 int inputSize = ComputeCompressedSize(width, height, format);
daniel@transgaming.comf749f0e2011-11-09 17:45:34 +00001446 image->loadCompressedData(xoffset, transformPixelYOffset(yoffset, height, image->getHeight()), width, height, -inputPitch, static_cast<const char*>(pixels) + inputSize - inputPitch, locked.Pitch, locked.pBits);
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001447 image->unlock();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001448 }
1449
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001450 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001451 }
1452
1453 return true;
1454}
1455
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001456IDirect3DBaseTexture9 *Texture::getTexture()
1457{
1458 if (!isComplete())
1459 {
1460 return NULL;
1461 }
1462
daniel@transgaming.comaed18322011-03-21 16:38:13 +00001463 if (!getBaseTexture())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001464 {
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001465 createTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001466 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001467
daniel@transgaming.comc50edcb2011-03-21 16:38:40 +00001468 updateTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001469
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001470 return getBaseTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001471}
1472
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001473bool Texture::hasDirtyParameters() const
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001474{
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001475 return mDirtyParameters;
daniel@transgaming.coma06aa872011-03-21 17:22:21 +00001476}
1477
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001478bool Texture::hasDirtyImages() const
daniel@transgaming.coma06aa872011-03-21 17:22:21 +00001479{
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001480 return mDirtyImages;
daniel@transgaming.com38e76e52011-03-21 16:39:10 +00001481}
1482
1483void Texture::resetDirty()
1484{
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001485 mDirtyParameters = false;
1486 mDirtyImages = false;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001487}
1488
daniel@transgaming.coma9eb5da2011-03-21 16:39:16 +00001489unsigned int Texture::getSerial() const
1490{
1491 return mSerial;
1492}
1493
daniel@transgaming.comf81103a2011-11-09 17:46:28 +00001494GLint Texture::creationLevels(GLsizei width, GLsizei height) const
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001495{
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00001496 if ((isPow2(width) && isPow2(height)) || getContext()->supportsNonPower2Texture())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001497 {
daniel@transgaming.comf81103a2011-11-09 17:46:28 +00001498 return 0; // Maximum number of levels
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001499 }
1500 else
1501 {
1502 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
1503 return 1;
1504 }
1505}
1506
daniel@transgaming.comf81103a2011-11-09 17:46:28 +00001507GLint Texture::creationLevels(GLsizei size) const
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001508{
daniel@transgaming.comf81103a2011-11-09 17:46:28 +00001509 return creationLevels(size, size);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001510}
1511
1512int Texture::levelCount() const
1513{
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001514 return getBaseTexture() ? getBaseTexture()->GetLevelCount() : 0;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001515}
1516
daniel@transgaming.coma9eb5da2011-03-21 16:39:16 +00001517unsigned int Texture::issueSerial()
1518{
1519 return mCurrentSerial++;
1520}
1521
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001522TextureStorage2D::TextureStorage2D(IDirect3DTexture9 *texture, bool renderable) : TextureStorage(renderable)
1523{
1524 mTexture = texture;
1525}
1526
1527TextureStorage2D::~TextureStorage2D()
1528{
1529 mTexture->Release();
1530}
1531
1532IDirect3DSurface9 *TextureStorage2D::getSurfaceLevel(int level)
1533{
1534 IDirect3DSurface9 *surface = NULL;
1535
1536 if (mTexture)
1537 {
1538 HRESULT result = mTexture->GetSurfaceLevel(level, &surface);
1539 ASSERT(SUCCEEDED(result));
1540 }
1541
1542 return surface;
1543}
1544
1545IDirect3DBaseTexture9 *TextureStorage2D::getBaseTexture() const
1546{
1547 return mTexture;
1548}
1549
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001550Texture2D::Texture2D(GLuint id) : Texture(id)
1551{
1552 mTexture = NULL;
jbauman@chromium.orgae345802011-03-30 22:04:25 +00001553 mSurface = NULL;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001554}
1555
1556Texture2D::~Texture2D()
1557{
1558 mColorbufferProxy.set(NULL);
1559
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001560 delete mTexture;
1561 mTexture = NULL;
1562
jbauman@chromium.orgae345802011-03-30 22:04:25 +00001563 if (mSurface)
1564 {
1565 mSurface->setBoundTexture(NULL);
1566 mSurface = NULL;
1567 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001568}
1569
1570GLenum Texture2D::getTarget() const
1571{
1572 return GL_TEXTURE_2D;
1573}
1574
daniel@transgaming.com61208202011-03-21 16:38:50 +00001575GLsizei Texture2D::getWidth() const
1576{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001577 return mImageArray[0].getWidth();
daniel@transgaming.com61208202011-03-21 16:38:50 +00001578}
1579
1580GLsizei Texture2D::getHeight() const
1581{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001582 return mImageArray[0].getHeight();
daniel@transgaming.com61208202011-03-21 16:38:50 +00001583}
1584
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001585GLenum Texture2D::getInternalFormat() const
1586{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001587 return mImageArray[0].getFormat();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001588}
1589
daniel@transgaming.com61208202011-03-21 16:38:50 +00001590GLenum Texture2D::getType() const
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001591{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001592 return mImageArray[0].getType();
daniel@transgaming.com61208202011-03-21 16:38:50 +00001593}
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001594
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00001595D3DFORMAT Texture2D::getD3DFormat() const
1596{
1597 return mImageArray[0].getD3DFormat();
1598}
1599
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001600void Texture2D::redefineImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLenum type)
daniel@transgaming.com61208202011-03-21 16:38:50 +00001601{
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001602 releaseTexImage();
1603
daniel@transgaming.com41634052011-11-09 17:46:24 +00001604 bool redefined = mImageArray[level].redefine(format, width, height, type);
daniel@transgaming.comc9ba4ad2011-11-09 17:44:35 +00001605
daniel@transgaming.com41634052011-11-09 17:46:24 +00001606 if (mTexture && redefined)
daniel@transgaming.com61208202011-03-21 16:38:50 +00001607 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001608 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1609 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001610 mImageArray[i].markDirty();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001611 }
1612
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001613 delete mTexture;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001614 mTexture = NULL;
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00001615 mSerial = 0;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001616 mDirtyImages = true;
apatrick@chromium.org57a2cd62011-06-08 00:04:07 +00001617 mColorbufferProxy.set(NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001618 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001619}
1620
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00001621void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001622{
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001623 redefineImage(level, format, width, height, type);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001624
daniel@transgaming.com61208202011-03-21 16:38:50 +00001625 Texture::setImage(unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001626}
1627
jbauman@chromium.orgae345802011-03-30 22:04:25 +00001628void Texture2D::bindTexImage(egl::Surface *surface)
1629{
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001630 releaseTexImage();
1631
jbauman@chromium.orgae345802011-03-30 22:04:25 +00001632 GLenum format;
1633
1634 switch(surface->getFormat())
1635 {
1636 case D3DFMT_A8R8G8B8:
1637 format = GL_RGBA;
1638 break;
1639 case D3DFMT_X8R8G8B8:
1640 format = GL_RGB;
1641 break;
1642 default:
1643 UNIMPLEMENTED();
1644 return;
1645 }
1646
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001647 mImageArray[0].redefine(format, surface->getWidth(), surface->getHeight(), GL_UNSIGNED_BYTE);
jbauman@chromium.orgae345802011-03-30 22:04:25 +00001648
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001649 delete mTexture;
1650 mTexture = new TextureStorage2D(surface->getOffscreenTexture(), true);
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00001651 mSerial = issueSerial();
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00001652 mColorbufferProxy.set(NULL);
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001653 mDirtyImages = true;
jbauman@chromium.orgae345802011-03-30 22:04:25 +00001654 mSurface = surface;
1655 mSurface->setBoundTexture(this);
1656}
1657
1658void Texture2D::releaseTexImage()
1659{
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001660 if (mSurface)
1661 {
1662 mSurface->setBoundTexture(NULL);
1663 mSurface = NULL;
1664
1665 if (mTexture)
1666 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001667 delete mTexture;
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001668 mTexture = NULL;
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00001669 mSerial = 0;
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00001670 mColorbufferProxy.set(NULL);
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001671 }
1672
1673 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1674 {
1675 mImageArray[i].redefine(GL_RGBA, 0, 0, GL_UNSIGNED_BYTE);
1676 }
1677 }
jbauman@chromium.orgae345802011-03-30 22:04:25 +00001678}
1679
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00001680void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001681{
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001682 redefineImage(level, format, width, height, GL_UNSIGNED_BYTE);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001683
daniel@transgaming.com61208202011-03-21 16:38:50 +00001684 Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001685}
1686
1687void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1688{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001689 ASSERT(mImageArray[level].getSurface() != NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001690
1691 if (level < levelCount())
1692 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001693 IDirect3DSurface9 *destLevel = mTexture->getSurfaceLevel(level);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001694
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001695 if (destLevel)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001696 {
daniel@transgaming.comb5a3a6b2011-03-21 16:38:46 +00001697 Image *image = &mImageArray[level];
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001698
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001699 RECT sourceRect = transformPixelRect(xoffset, yoffset, width, height, image->getHeight());
daniel@transgaming.comb612f882011-11-09 17:44:31 +00001700 POINT destPoint = {sourceRect.left, sourceRect.top};
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001701
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001702 HRESULT result = getDevice()->UpdateSurface(image->getSurface(), &sourceRect, destLevel, &destPoint);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001703 ASSERT(SUCCEEDED(result));
1704
1705 destLevel->Release();
1706
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001707 image->markClean();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001708 }
1709 }
1710}
1711
1712void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1713{
1714 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
1715 {
1716 commitRect(level, xoffset, yoffset, width, height);
1717 }
1718}
1719
1720void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1721{
1722 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
1723 {
1724 commitRect(level, xoffset, yoffset, width, height);
1725 }
1726}
1727
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00001728void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001729{
1730 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1731
1732 if (!renderTarget)
1733 {
1734 ERR("Failed to retrieve the render target.");
1735 return error(GL_OUT_OF_MEMORY);
1736 }
1737
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00001738 redefineImage(level, format, width, height, GL_UNSIGNED_BYTE);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001739
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00001740 if (!mImageArray[level].isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001741 {
daniel@transgaming.com90cfcc92011-11-09 17:45:48 +00001742 mImageArray[level].copy(0, 0, x, y, width, height, renderTarget);
1743 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001744 }
1745 else
1746 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001747 if (!mTexture || !mTexture->isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001748 {
1749 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001750 }
daniel@transgaming.com3b3c1d42011-06-08 20:38:09 +00001751
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001752 mImageArray[level].markClean();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001753
1754 if (width != 0 && height != 0 && level < levelCount())
1755 {
1756 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
1757 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
1758 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
1759 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
1760 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
daniel@transgaming.comeef864a2011-04-22 11:33:27 +00001761
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001762 GLint destYOffset = transformPixelYOffset(0, height, mImageArray[level].getHeight());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001763
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001764 IDirect3DSurface9 *dest = mTexture->getSurfaceLevel(level);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001765
daniel@transgaming.comeef864a2011-04-22 11:33:27 +00001766 getBlitter()->copy(source->getRenderTarget(), sourceRect, format, 0, destYOffset, dest);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001767 dest->Release();
1768 }
1769 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001770}
1771
1772void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1773{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001774 if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001775 {
1776 return error(GL_INVALID_VALUE);
1777 }
1778
1779 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1780
1781 if (!renderTarget)
1782 {
1783 ERR("Failed to retrieve the render target.");
1784 return error(GL_OUT_OF_MEMORY);
1785 }
1786
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00001787 if (!mImageArray[level].isRenderable() || (!mTexture && !isComplete()))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001788 {
daniel@transgaming.com90cfcc92011-11-09 17:45:48 +00001789 mImageArray[level].copy(xoffset, yoffset, x, y, width, height, renderTarget);
1790 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001791 }
1792 else
1793 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001794 if (!mTexture || !mTexture->isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001795 {
1796 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001797 }
daniel@transgaming.com61208202011-03-21 16:38:50 +00001798
1799 updateTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001800
1801 if (level < levelCount())
1802 {
1803 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
1804 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
1805 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
1806 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
1807 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
1808
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001809 GLint destYOffset = transformPixelYOffset(yoffset, height, mImageArray[level].getHeight());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001810
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001811 IDirect3DSurface9 *dest = mTexture->getSurfaceLevel(level);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001812
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001813 getBlitter()->copy(source->getRenderTarget(), sourceRect, mImageArray[0].getFormat(), xoffset, destYOffset, dest);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001814 dest->Release();
1815 }
1816 }
1817}
1818
1819// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1820bool Texture2D::isComplete() const
1821{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001822 GLsizei width = mImageArray[0].getWidth();
1823 GLsizei height = mImageArray[0].getHeight();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001824
1825 if (width <= 0 || height <= 0)
1826 {
1827 return false;
1828 }
1829
1830 bool mipmapping = false;
1831
1832 switch (mMinFilter)
1833 {
1834 case GL_NEAREST:
1835 case GL_LINEAR:
1836 mipmapping = false;
1837 break;
1838 case GL_NEAREST_MIPMAP_NEAREST:
1839 case GL_LINEAR_MIPMAP_NEAREST:
1840 case GL_NEAREST_MIPMAP_LINEAR:
1841 case GL_LINEAR_MIPMAP_LINEAR:
1842 mipmapping = true;
1843 break;
1844 default: UNREACHABLE();
1845 }
1846
daniel@transgaming.combbeffbb2011-11-09 17:46:11 +00001847 if ((getInternalFormat() == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
1848 (getInternalFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001849 {
1850 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
1851 {
1852 return false;
1853 }
1854 }
1855
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00001856 bool npot = getContext()->supportsNonPower2Texture();
1857
1858 if (!npot)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001859 {
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00001860 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
1861 (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
1862 {
1863 return false;
1864 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001865 }
1866
1867 if (mipmapping)
1868 {
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00001869 if (!npot)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001870 {
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00001871 if (!isPow2(width) || !isPow2(height))
1872 {
1873 return false;
1874 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001875 }
1876
1877 int q = log2(std::max(width, height));
1878
1879 for (int level = 1; level <= q; level++)
1880 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001881 if (mImageArray[level].getFormat() != mImageArray[0].getFormat())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001882 {
1883 return false;
1884 }
1885
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001886 if (mImageArray[level].getType() != mImageArray[0].getType())
daniel@transgaming.com0bd22bc2011-03-21 16:38:26 +00001887 {
1888 return false;
1889 }
1890
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001891 if (mImageArray[level].getWidth() != std::max(1, width >> level))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001892 {
1893 return false;
1894 }
1895
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001896 if (mImageArray[level].getHeight() != std::max(1, height >> level))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001897 {
1898 return false;
1899 }
1900 }
1901 }
1902
1903 return true;
1904}
1905
1906bool Texture2D::isCompressed() const
1907{
1908 return IsCompressed(getInternalFormat());
1909}
1910
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001911IDirect3DBaseTexture9 *Texture2D::getBaseTexture() const
1912{
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001913 return mTexture ? mTexture->getBaseTexture() : NULL;
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001914}
1915
1916// Constructs a Direct3D 9 texture resource from the texture images
1917void Texture2D::createTexture()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001918{
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001919 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00001920 D3DFORMAT format = mImageArray[0].getD3DFormat();
daniel@transgaming.comf81103a2011-11-09 17:46:28 +00001921 GLint levels = creationLevels(mImageArray[0].getWidth(), mImageArray[0].getHeight());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001922
daniel@transgaming.com61208202011-03-21 16:38:50 +00001923 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001924 HRESULT result = device->CreateTexture(mImageArray[0].getWidth(), mImageArray[0].getHeight(), levels, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001925
1926 if (FAILED(result))
1927 {
1928 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001929 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001930 }
1931
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001932 delete mTexture;
1933 mTexture = new TextureStorage2D(texture, false);
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00001934 mSerial = issueSerial();
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00001935 mColorbufferProxy.set(NULL);
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00001936 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001937}
1938
1939void Texture2D::updateTexture()
1940{
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001941 int levels = levelCount();
1942
1943 for (int level = 0; level < levels; level++)
1944 {
daniel@transgaming.comb612f882011-11-09 17:44:31 +00001945 Image *image = &mImageArray[level];
1946
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00001947 if (image->isDirty())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001948 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001949 commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001950 }
1951 }
1952}
1953
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001954void Texture2D::convertToRenderTarget()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001955{
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00001956 TextureStorage2D *newTexture = NULL;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001957
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00001958 if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001959 {
1960 egl::Display *display = getDisplay();
1961 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00001962 D3DFORMAT format = mImageArray[0].getD3DFormat();
daniel@transgaming.comf81103a2011-11-09 17:46:28 +00001963 GLint levels = creationLevels(mImageArray[0].getWidth(), mImageArray[0].getHeight());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001964
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00001965 IDirect3DTexture9 *texture2D = NULL;
1966 HRESULT result = device->CreateTexture(mImageArray[0].getWidth(), mImageArray[0].getHeight(), levels, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture2D, NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001967
1968 if (FAILED(result))
1969 {
1970 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001971 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001972 }
1973
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00001974 newTexture = new TextureStorage2D(texture2D, true);
1975
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001976 if (mTexture != NULL)
1977 {
1978 int levels = levelCount();
1979 for (int i = 0; i < levels; i++)
1980 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00001981 IDirect3DSurface9 *source = mTexture->getSurfaceLevel(i);
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00001982 IDirect3DSurface9 *dest = newTexture->getSurfaceLevel(i);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001983
1984 display->endScene();
1985 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1986
1987 if (FAILED(result))
1988 {
1989 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1990
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00001991 delete newTexture;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001992 source->Release();
1993 dest->Release();
1994
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001995 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001996 }
1997
1998 source->Release();
1999 dest->Release();
2000 }
2001 }
2002 }
2003
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002004 delete mTexture;
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002005 mTexture = newTexture;
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00002006 mSerial = issueSerial();
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00002007 mColorbufferProxy.set(NULL);
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00002008 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002009}
2010
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002011void Texture2D::generateMipmaps()
2012{
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002013 if (!getContext()->supportsNonPower2Texture())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002014 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002015 if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight()))
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002016 {
2017 return error(GL_INVALID_OPERATION);
2018 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002019 }
2020
2021 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002022 unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight()));
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002023 for (unsigned int i = 1; i <= q; i++)
2024 {
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00002025 redefineImage(i, mImageArray[0].getFormat(),
2026 std::max(mImageArray[0].getWidth() >> i, 1),
2027 std::max(mImageArray[0].getHeight() >> i, 1),
2028 mImageArray[0].getType());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002029 }
2030
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002031 if (mTexture && mTexture->isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002032 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002033 for (unsigned int i = 1; i <= q; i++)
2034 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002035 IDirect3DSurface9 *upper = mTexture->getSurfaceLevel(i - 1);
2036 IDirect3DSurface9 *lower = mTexture->getSurfaceLevel(i);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002037
2038 if (upper != NULL && lower != NULL)
2039 {
2040 getBlitter()->boxFilter(upper, lower);
2041 }
2042
2043 if (upper != NULL) upper->Release();
2044 if (lower != NULL) lower->Release();
daniel@transgaming.com61208202011-03-21 16:38:50 +00002045
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002046 mImageArray[i].markClean();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002047 }
2048 }
2049 else
2050 {
2051 for (unsigned int i = 1; i <= q; i++)
2052 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002053 if (mImageArray[i].getSurface() == NULL)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002054 {
2055 return error(GL_OUT_OF_MEMORY);
2056 }
2057
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002058 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].getSurface(), NULL, NULL, mImageArray[i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002059 {
2060 ERR(" failed to load filter %d to %d.", i - 1, i);
2061 }
2062
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002063 mImageArray[i].markDirty();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002064 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002065 }
2066}
2067
2068Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
2069{
2070 if (target != GL_TEXTURE_2D)
2071 {
2072 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2073 }
2074
2075 if (mColorbufferProxy.get() == NULL)
2076 {
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00002077 mColorbufferProxy.set(new Renderbuffer(id(), new RenderbufferTexture(this, target)));
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002078 }
2079
2080 return mColorbufferProxy.get();
2081}
2082
2083IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
2084{
2085 ASSERT(target == GL_TEXTURE_2D);
2086
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002087 if (!mTexture || !mTexture->isRenderable())
daniel@transgaming.com61208202011-03-21 16:38:50 +00002088 {
2089 convertToRenderTarget();
2090 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002091
2092 if (mTexture == NULL)
2093 {
2094 return NULL;
2095 }
daniel@transgaming.com61208202011-03-21 16:38:50 +00002096
2097 updateTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002098
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002099 IDirect3DSurface9 *renderTarget = mTexture->getSurfaceLevel(0);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002100
2101 return renderTarget;
2102}
2103
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002104TextureStorageCubeMap::TextureStorageCubeMap(IDirect3DCubeTexture9 *texture, bool renderable) : TextureStorage(renderable)
2105{
2106 mTexture = texture;
2107}
2108
2109TextureStorageCubeMap::~TextureStorageCubeMap()
2110{
2111 mTexture->Release();
2112}
2113
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002114IDirect3DSurface9 *TextureStorageCubeMap::getCubeMapSurface(GLenum faceTarget, int level)
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002115{
2116 IDirect3DSurface9 *surface = NULL;
2117
2118 if (mTexture)
2119 {
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002120 HRESULT result = mTexture->GetCubeMapSurface(es2dx::ConvertCubeFace(faceTarget), level, &surface);
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002121 ASSERT(SUCCEEDED(result));
2122 }
2123
2124 return surface;
2125}
2126
2127IDirect3DBaseTexture9 *TextureStorageCubeMap::getBaseTexture() const
2128{
2129 return mTexture;
2130}
2131
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002132TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
2133{
2134 mTexture = NULL;
2135}
2136
2137TextureCubeMap::~TextureCubeMap()
2138{
2139 for (int i = 0; i < 6; i++)
2140 {
2141 mFaceProxies[i].set(NULL);
2142 }
2143
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002144 delete mTexture;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002145}
2146
2147GLenum TextureCubeMap::getTarget() const
2148{
2149 return GL_TEXTURE_CUBE_MAP;
2150}
2151
daniel@transgaming.com61208202011-03-21 16:38:50 +00002152GLsizei TextureCubeMap::getWidth() const
2153{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002154 return mImageArray[0][0].getWidth();
daniel@transgaming.com61208202011-03-21 16:38:50 +00002155}
2156
2157GLsizei TextureCubeMap::getHeight() const
2158{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002159 return mImageArray[0][0].getHeight();
daniel@transgaming.com61208202011-03-21 16:38:50 +00002160}
2161
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002162GLenum TextureCubeMap::getInternalFormat() const
2163{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002164 return mImageArray[0][0].getFormat();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002165}
2166
daniel@transgaming.com61208202011-03-21 16:38:50 +00002167GLenum TextureCubeMap::getType() const
2168{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002169 return mImageArray[0][0].getType();
daniel@transgaming.com61208202011-03-21 16:38:50 +00002170}
2171
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00002172D3DFORMAT TextureCubeMap::getD3DFormat() const
2173{
2174 return mImageArray[0][0].getD3DFormat();
2175}
2176
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002177void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002178{
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002179 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002180}
2181
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002182void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002183{
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002184 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002185}
2186
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002187void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002188{
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002189 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002190}
2191
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002192void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002193{
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002194 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002195}
2196
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002197void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002198{
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002199 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002200}
2201
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002202void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002203{
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002204 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002205}
2206
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002207void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002208{
daniel@transgaming.com68ae2992011-11-09 17:44:49 +00002209 redefineImage(faceIndex(face), level, format, width, height, GL_UNSIGNED_BYTE);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002210
daniel@transgaming.com61208202011-03-21 16:38:50 +00002211 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002212}
2213
daniel@transgaming.comb612f882011-11-09 17:44:31 +00002214void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002215{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002216 ASSERT(mImageArray[face][level].getSurface() != NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002217
2218 if (level < levelCount())
2219 {
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002220 IDirect3DSurface9 *destLevel = mTexture->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002221 ASSERT(destLevel != NULL);
2222
2223 if (destLevel != NULL)
2224 {
daniel@transgaming.comb5a3a6b2011-03-21 16:38:46 +00002225 Image *image = &mImageArray[face][level];
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002226
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002227 RECT sourceRect = transformPixelRect(xoffset, yoffset, width, height, image->getHeight());
daniel@transgaming.comb612f882011-11-09 17:44:31 +00002228 POINT destPoint = {sourceRect.left, sourceRect.top};
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002229
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002230 HRESULT result = getDevice()->UpdateSurface(image->getSurface(), &sourceRect, destLevel, &destPoint);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002231 ASSERT(SUCCEEDED(result));
2232
2233 destLevel->Release();
2234
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002235 image->markClean();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002236 }
2237 }
2238}
2239
2240void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2241{
2242 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
2243 {
daniel@transgaming.comb612f882011-11-09 17:44:31 +00002244 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002245 }
2246}
2247
2248void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
2249{
2250 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
2251 {
daniel@transgaming.comb612f882011-11-09 17:44:31 +00002252 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002253 }
2254}
2255
2256// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
2257bool TextureCubeMap::isComplete() const
2258{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002259 int size = mImageArray[0][0].getWidth();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002260
2261 if (size <= 0)
2262 {
2263 return false;
2264 }
2265
2266 bool mipmapping;
2267
2268 switch (mMinFilter)
2269 {
2270 case GL_NEAREST:
2271 case GL_LINEAR:
2272 mipmapping = false;
2273 break;
2274 case GL_NEAREST_MIPMAP_NEAREST:
2275 case GL_LINEAR_MIPMAP_NEAREST:
2276 case GL_NEAREST_MIPMAP_LINEAR:
2277 case GL_LINEAR_MIPMAP_LINEAR:
2278 mipmapping = true;
2279 break;
2280 default: UNREACHABLE();
2281 }
2282
2283 for (int face = 0; face < 6; face++)
2284 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002285 if (mImageArray[face][0].getWidth() != size || mImageArray[face][0].getHeight() != size)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002286 {
2287 return false;
2288 }
2289 }
2290
daniel@transgaming.combbeffbb2011-11-09 17:46:11 +00002291 if ((getInternalFormat() == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
2292 (getInternalFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002293 {
2294 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
2295 {
2296 return false;
2297 }
2298 }
2299
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002300 bool npot = getContext()->supportsNonPower2Texture();
2301
2302 if (!npot)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002303 {
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002304 if ((getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE) && !isPow2(size))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002305 {
2306 return false;
2307 }
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002308 }
2309
2310 if (mipmapping)
2311 {
2312 if (!npot)
2313 {
2314 if (!isPow2(size))
2315 {
2316 return false;
2317 }
2318 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002319
2320 int q = log2(size);
2321
2322 for (int face = 0; face < 6; face++)
2323 {
2324 for (int level = 1; level <= q; level++)
2325 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002326 if (mImageArray[face][level].getFormat() != mImageArray[0][0].getFormat())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002327 {
2328 return false;
2329 }
2330
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002331 if (mImageArray[face][level].getType() != mImageArray[0][0].getType())
daniel@transgaming.com0bd22bc2011-03-21 16:38:26 +00002332 {
2333 return false;
2334 }
2335
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002336 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002337 {
2338 return false;
2339 }
2340
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002341 ASSERT(mImageArray[face][level].getHeight() == mImageArray[face][level].getWidth());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002342 }
2343 }
2344 }
2345
2346 return true;
2347}
2348
2349bool TextureCubeMap::isCompressed() const
2350{
2351 return IsCompressed(getInternalFormat());
2352}
2353
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002354IDirect3DBaseTexture9 *TextureCubeMap::getBaseTexture() const
2355{
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002356 return mTexture ? mTexture->getBaseTexture() : NULL;
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002357}
2358
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002359// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002360void TextureCubeMap::createTexture()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002361{
2362 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00002363 D3DFORMAT format = mImageArray[0][0].getD3DFormat();
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002364 GLint levels = creationLevels(mImageArray[0][0].getWidth(), 0);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002365
daniel@transgaming.com0bd22bc2011-03-21 16:38:26 +00002366 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002367 HRESULT result = device->CreateCubeTexture(mImageArray[0][0].getWidth(), levels, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002368
2369 if (FAILED(result))
2370 {
2371 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002372 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002373 }
2374
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002375 delete mTexture;
2376 mTexture = new TextureStorageCubeMap(texture, false);
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00002377 mSerial = issueSerial();
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00002378 for(int face = 0; face < 6; face++) mFaceProxies[face].set(NULL);
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00002379 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002380}
2381
2382void TextureCubeMap::updateTexture()
2383{
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002384 for (int face = 0; face < 6; face++)
2385 {
2386 int levels = levelCount();
2387 for (int level = 0; level < levels; level++)
2388 {
daniel@transgaming.comb5a3a6b2011-03-21 16:38:46 +00002389 Image *image = &mImageArray[face][level];
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002390
daniel@transgaming.com5cce3ff2011-11-09 17:45:14 +00002391 if (image->isDirty())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002392 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002393 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002394 }
2395 }
2396 }
2397}
2398
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002399void TextureCubeMap::convertToRenderTarget()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002400{
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002401 TextureStorageCubeMap *newTexture = NULL;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002402
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002403 if (mImageArray[0][0].getWidth() != 0)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002404 {
2405 egl::Display *display = getDisplay();
2406 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00002407 D3DFORMAT format = mImageArray[0][0].getD3DFormat();
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002408 GLint levels = creationLevels(mImageArray[0][0].getWidth(), 0);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002409
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002410 IDirect3DCubeTexture9 *cubeTexture = NULL;
2411 HRESULT result = device->CreateCubeTexture(mImageArray[0][0].getWidth(), levels, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &cubeTexture, NULL);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002412
2413 if (FAILED(result))
2414 {
2415 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002416 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002417 }
2418
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002419 newTexture = new TextureStorageCubeMap(cubeTexture, true);
2420
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002421 if (mTexture != NULL)
2422 {
2423 int levels = levelCount();
2424 for (int f = 0; f < 6; f++)
2425 {
2426 for (int i = 0; i < levels; i++)
2427 {
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002428 IDirect3DSurface9 *source = mTexture->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i);
2429 IDirect3DSurface9 *dest = newTexture->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002430
2431 display->endScene();
2432 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
2433
2434 if (FAILED(result))
2435 {
2436 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2437
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002438 delete newTexture;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002439 source->Release();
2440 dest->Release();
2441
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002442 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002443 }
daniel@transgaming.coma1a86202011-08-09 13:41:08 +00002444
2445 source->Release();
2446 dest->Release();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002447 }
2448 }
2449 }
2450 }
2451
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002452 delete mTexture;
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002453 mTexture = newTexture;
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00002454 mSerial = issueSerial();
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00002455 for(int face = 0; face < 6; face++) mFaceProxies[face].set(NULL);
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00002456 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002457}
2458
daniel@transgaming.com61208202011-03-21 16:38:50 +00002459void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002460{
daniel@transgaming.com68ae2992011-11-09 17:44:49 +00002461 redefineImage(faceIndex, level, format, width, height, type);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002462
daniel@transgaming.com61208202011-03-21 16:38:50 +00002463 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002464}
2465
2466unsigned int TextureCubeMap::faceIndex(GLenum face)
2467{
2468 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
2469 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
2470 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
2471 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
2472 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
2473
2474 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
2475}
2476
daniel@transgaming.com68ae2992011-11-09 17:44:49 +00002477void TextureCubeMap::redefineImage(int face, GLint level, GLenum format, GLsizei width, GLsizei height, GLenum type)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002478{
daniel@transgaming.com41634052011-11-09 17:46:24 +00002479 bool redefined = mImageArray[face][level].redefine(format, width, height, type);
daniel@transgaming.com94a4f032011-03-21 16:38:55 +00002480
daniel@transgaming.com41634052011-11-09 17:46:24 +00002481 if (mTexture && redefined)
daniel@transgaming.com61208202011-03-21 16:38:50 +00002482 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002483 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
2484 {
2485 for (int f = 0; f < 6; f++)
2486 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002487 mImageArray[f][i].markDirty();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002488 }
2489 }
2490
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002491 delete mTexture;
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00002492 mTexture = NULL;
daniel@transgaming.comc1fde762011-11-09 17:46:07 +00002493 mSerial = 0;
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00002494 for(int face = 0; face < 6; face++) mFaceProxies[face].set(NULL);
daniel@transgaming.com0da803b2011-11-09 17:44:58 +00002495 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002496 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002497}
2498
daniel@transgaming.com8a0a2db2011-03-21 16:38:20 +00002499void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002500{
2501 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2502
2503 if (!renderTarget)
2504 {
2505 ERR("Failed to retrieve the render target.");
2506 return error(GL_OUT_OF_MEMORY);
2507 }
2508
2509 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com68ae2992011-11-09 17:44:49 +00002510 redefineImage(faceindex, level, format, width, height, GL_UNSIGNED_BYTE);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002511
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00002512 if (!mImageArray[faceindex][level].isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002513 {
daniel@transgaming.com90cfcc92011-11-09 17:45:48 +00002514 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2515 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002516 }
2517 else
2518 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002519 if (!mTexture || !mTexture->isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002520 {
2521 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002522 }
daniel@transgaming.com3b3c1d42011-06-08 20:38:09 +00002523
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002524 mImageArray[faceindex][level].markClean();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002525
2526 ASSERT(width == height);
2527
2528 if (width > 0 && level < levelCount())
2529 {
2530 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
2531 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
2532 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
2533 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
2534 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
2535
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002536 GLint destYOffset = transformPixelYOffset(0, height, mImageArray[faceindex][level].getWidth());
daniel@transgaming.comeef864a2011-04-22 11:33:27 +00002537
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002538 IDirect3DSurface9 *dest = mTexture->getCubeMapSurface(target, level);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002539
daniel@transgaming.comeef864a2011-04-22 11:33:27 +00002540 getBlitter()->copy(source->getRenderTarget(), sourceRect, format, 0, destYOffset, dest);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002541 dest->Release();
2542 }
2543 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002544}
2545
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002546void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2547{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002548 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002549
2550 if (xoffset + width > size || yoffset + height > size)
2551 {
2552 return error(GL_INVALID_VALUE);
2553 }
2554
2555 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2556
2557 if (!renderTarget)
2558 {
2559 ERR("Failed to retrieve the render target.");
2560 return error(GL_OUT_OF_MEMORY);
2561 }
2562
2563 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com01dae852011-11-09 17:44:53 +00002564
daniel@transgaming.com549bdef2011-03-29 00:57:01 +00002565 if (!mImageArray[faceindex][level].isRenderable() || (!mTexture && !isComplete()))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002566 {
daniel@transgaming.com90cfcc92011-11-09 17:45:48 +00002567 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2568 mDirtyImages = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002569 }
2570 else
2571 {
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002572 if (!mTexture || !mTexture->isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002573 {
2574 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002575 }
daniel@transgaming.com61208202011-03-21 16:38:50 +00002576
2577 updateTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002578
2579 if (level < levelCount())
2580 {
2581 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
2582 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
2583 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
2584 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
2585 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
2586
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002587 GLint destYOffset = transformPixelYOffset(yoffset, height, mImageArray[faceindex][level].getWidth());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002588
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002589 IDirect3DSurface9 *dest = mTexture->getCubeMapSurface(target, level);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002590
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002591 getBlitter()->copy(source->getRenderTarget(), sourceRect, mImageArray[0][0].getFormat(), xoffset, destYOffset, dest);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002592 dest->Release();
2593 }
2594 }
2595}
2596
2597bool TextureCubeMap::isCubeComplete() const
2598{
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002599 if (mImageArray[0][0].getWidth() == 0)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002600 {
2601 return false;
2602 }
2603
2604 for (unsigned int f = 1; f < 6; f++)
2605 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002606 if (mImageArray[f][0].getWidth() != mImageArray[0][0].getWidth()
2607 || mImageArray[f][0].getFormat() != mImageArray[0][0].getFormat())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002608 {
2609 return false;
2610 }
2611 }
2612
2613 return true;
2614}
2615
2616void TextureCubeMap::generateMipmaps()
2617{
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002618 if (!isCubeComplete())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002619 {
2620 return error(GL_INVALID_OPERATION);
2621 }
2622
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002623 if (!getContext()->supportsNonPower2Texture())
2624 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002625 if (!isPow2(mImageArray[0][0].getWidth()))
daniel@transgaming.com4f9ef0d2011-05-30 23:51:19 +00002626 {
2627 return error(GL_INVALID_OPERATION);
2628 }
2629 }
2630
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002631 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002632 unsigned int q = log2(mImageArray[0][0].getWidth());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002633 for (unsigned int f = 0; f < 6; f++)
2634 {
2635 for (unsigned int i = 1; i <= q; i++)
2636 {
daniel@transgaming.com1a01e832011-11-09 17:45:57 +00002637 redefineImage(f, i, mImageArray[f][0].getFormat(),
2638 std::max(mImageArray[f][0].getWidth() >> i, 1),
2639 std::max(mImageArray[f][0].getWidth() >> i, 1),
2640 mImageArray[f][0].getType());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002641 }
2642 }
2643
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002644 if (mTexture && mTexture->isRenderable())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002645 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002646 for (unsigned int f = 0; f < 6; f++)
2647 {
2648 for (unsigned int i = 1; i <= q; i++)
2649 {
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002650 IDirect3DSurface9 *upper = mTexture->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i-1);
2651 IDirect3DSurface9 *lower = mTexture->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002652
2653 if (upper != NULL && lower != NULL)
2654 {
2655 getBlitter()->boxFilter(upper, lower);
2656 }
2657
2658 if (upper != NULL) upper->Release();
2659 if (lower != NULL) lower->Release();
daniel@transgaming.com61208202011-03-21 16:38:50 +00002660
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002661 mImageArray[f][i].markClean();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002662 }
2663 }
2664 }
2665 else
2666 {
2667 for (unsigned int f = 0; f < 6; f++)
2668 {
2669 for (unsigned int i = 1; i <= q; i++)
2670 {
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002671 if (mImageArray[f][i].getSurface() == NULL)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002672 {
2673 return error(GL_OUT_OF_MEMORY);
2674 }
2675
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002676 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].getSurface(), NULL, NULL, mImageArray[f][i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002677 {
2678 ERR(" failed to load filter %d to %d.", i - 1, i);
2679 }
2680
daniel@transgaming.comdff362f2011-11-09 17:45:08 +00002681 mImageArray[f][i].markDirty();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002682 }
2683 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002684 }
2685}
2686
2687Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
2688{
2689 if (!IsCubemapTextureTarget(target))
2690 {
2691 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2692 }
2693
2694 unsigned int face = faceIndex(target);
2695
2696 if (mFaceProxies[face].get() == NULL)
2697 {
daniel@transgaming.comd14558a2011-11-09 17:46:18 +00002698 mFaceProxies[face].set(new Renderbuffer(id(), new RenderbufferTexture(this, target)));
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002699 }
2700
2701 return mFaceProxies[face].get();
2702}
2703
2704IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
2705{
2706 ASSERT(IsCubemapTextureTarget(target));
2707
daniel@transgaming.comdc82bf92011-11-11 04:10:08 +00002708 if (!mTexture || !mTexture->isRenderable())
daniel@transgaming.com61208202011-03-21 16:38:50 +00002709 {
2710 convertToRenderTarget();
2711 }
2712
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002713 if (mTexture == NULL)
2714 {
2715 return NULL;
2716 }
daniel@transgaming.com61208202011-03-21 16:38:50 +00002717
2718 updateTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002719
daniel@transgaming.comafa8ef32011-11-11 04:10:13 +00002720 return mTexture->getCubeMapSurface(target, 0);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002721}
2722
daniel@transgaming.comb612f882011-11-09 17:44:31 +00002723}