1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3 // This software contains source code provided by NVIDIA Corporation.
4 // License: http://developer.download.nvidia.com/licenses/general_license.txt
5
6 // Modified DDS reader class from NVIDIA SDK
7 ///////////////////////////////////////////////////////////////////////////////
8 //
9 // Description:
10 //
11 // Loads DDS images (DXTC1, DXTC3, DXTC5, RGB (888, 888X), and RGBA (8888) are
12 // supported) for use in OpenGL. Image is flipped when its loaded as DX images
13 // are stored with different coordinate system. If file has mipmaps and/or
14 // cubemaps then these are loaded as well. Volume textures can be loaded as
15 // well but they must be uncompressed.
16 //
17 // When multiple textures are loaded (i.e a volume or cubemap texture),
18 // additional faces can be accessed using the array operator.
19 //
20 // The mipmaps for each face are also stored in a list and can be accessed like
21 // so: image.get_mipmap() (which accesses the first mipmap of the first
22 // image). To get the number of mipmaps call the get_num_mipmaps function for
23 // a given texture.
24 //
25 // Call the is_volume() or is_cubemap() function to check that a loaded image
26 // is a volume or cubemap texture respectively. If a volume texture is loaded
27 // then the get_depth() function should return a number greater than 1.
28 // Mipmapped volume textures and DXTC compressed volume textures are supported.
29 //
30 ///////////////////////////////////////////////////////////////////////////////
31 //
32 // Update: 9/15/2003
33 //
34 // Added functions to create new image from a buffer of pixels. Added function
35 // to save current image to disk.
36 //
37 // Update: 6/11/2002
38 //
39 // Added some convenience functions to handle uploading textures to OpenGL. The
40 // following functions have been added:
41 //
42 // bool upload_texture1D();
43 // bool upload_texture2D(unsigned int imageIndex = 0, GLenum target = GL_TEXTURE_2D);
44 // bool upload_textureRectangle();
45 // bool upload_texture3D();
46 // bool upload_textureCubemap();
47 //
48 // See function implementation below for instructions/comments on using each
49 // function.
50 //
51 // The open function has also been updated to take an optional second parameter
52 // specifying whether the image should be flipped on load. This defaults to
53 // true.
54 //
55 ///////////////////////////////////////////////////////////////////////////////
56 // Sample usage
57 ///////////////////////////////////////////////////////////////////////////////
58 //
59 // Loading a compressed texture:
60 //
61 // CDDSImage image;
62 // GLuint texobj;
63 //
64 // image.load("compressed.dds");
65 //
66 // glGenTextures(1, &texobj);
67 // glEnable(GL_TEXTURE_2D);
68 // glBindTexture(GL_TEXTURE_2D, texobj);
69 //
70 // glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, image.get_format(),
71 // image.get_width(), image.get_height(), 0, image.get_size(),
72 // image);
73 //
74 // for (int i = 0; i < image.get_num_mipmaps(); i++)
75 // {
76 // CSurface mipmap = image.get_mipmap(i);
77 //
78 // glCompressedTexImage2DARB(GL_TEXTURE_2D, i+1, image.get_format(),
79 // mipmap.get_width(), mipmap.get_height(), 0, mipmap.get_size(),
80 // mipmap);
81 // }
82 ///////////////////////////////////////////////////////////////////////////////
83 //
84 // Loading an uncompressed texture:
85 //
86 // CDDSImage image;
87 // GLuint texobj;
88 //
89 // image.load("uncompressed.dds");
90 //
91 // glGenTextures(1, &texobj);
92 // glEnable(GL_TEXTURE_2D);
93 // glBindTexture(GL_TEXTURE_2D, texobj);
94 //
95 // glTexImage2D(GL_TEXTURE_2D, 0, image.get_components(), image.get_width(),
96 // image.get_height(), 0, image.get_format(), GL_UNSIGNED_BYTE, image);
97 //
98 // for (int i = 0; i < image.get_num_mipmaps(); i++)
99 // {
100 // glTexImage2D(GL_TEXTURE_2D, i+1, image.get_components(),
101 // image.get_mipmap(i).get_width(), image.get_mipmap(i).get_height(),
102 // 0, image.get_format(), GL_UNSIGNED_BYTE, image.get_mipmap(i));
103 // }
104 //
105 ///////////////////////////////////////////////////////////////////////////////
106 //
107 // Loading an uncompressed cubemap texture:
108 //
109 // CDDSImage image;
110 // GLuint texobj;
111 // GLenum target;
112 //
113 // image.load("cubemap.dds");
114 //
115 // glGenTextures(1, &texobj);
116 // glEnable(GL_TEXTURE_CUBE_MAP_ARB);
117 // glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, texobj);
118 //
119 // for (int n = 0; n < 6; n++)
120 // {
121 // target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB+n;
122 //
123 // glTexImage2D(target, 0, image.get_components(), image[n].get_width(),
124 // image[n].get_height(), 0, image.get_format(), GL_UNSIGNED_BYTE,
125 // image[n]);
126 //
127 // for (int i = 0; i < image[n].get_num_mipmaps(); i++)
128 // {
129 // glTexImage2D(target, i+1, image.get_components(),
130 // image[n].get_mipmap(i).get_width(),
131 // image[n].get_mipmap(i).get_height(), 0,
132 // image.get_format(), GL_UNSIGNED_BYTE, image[n].get_mipmap(i));
133 // }
134 // }
135 //
136 ///////////////////////////////////////////////////////////////////////////////
137 //
138 // Loading a volume texture:
139 //
140 // CDDSImage image;
141 // GLuint texobj;
142 //
143 // image.load("volume.dds");
144 //
145 // glGenTextures(1, &texobj);
146 // glEnable(GL_TEXTURE_3D);
147 // glBindTexture(GL_TEXTURE_3D, texobj);
148 //
149 // PFNGLTEXIMAGE3DPROC glTexImage3D;
150 // glTexImage3D(GL_TEXTURE_3D, 0, image.get_components(), image.get_width(),
151 // image.get_height(), image.get_depth(), 0, image.get_format(),
152 // GL_UNSIGNED_BYTE, image);
153 //
154 // for (int i = 0; i < image.get_num_mipmaps(); i++)
155 // {
156 // glTexImage3D(GL_TEXTURE_3D, i+1, image.get_components(),
157 // image[0].get_mipmap(i).get_width(),
158 // image[0].get_mipmap(i).get_height(),
159 // image[0].get_mipmap(i).get_depth(), 0, image.get_format(),
160 // GL_UNSIGNED_BYTE, image[0].get_mipmap(i));
161 // }
162
163 #include <stdio.h>
164 #include <string.h>
165 #include <assert.h>
166
167 // spring related
168 #include "Rendering/GL/myGL.h"
169 #include "nv_dds.h"
170 #include "System/FileSystem/FileHandler.h"
171 #include "System/Platform/byteorder.h"
172 #include "System/Log/ILog.h"
173
174 using namespace std;
175 using namespace nv_dds;
176
177 ///////////////////////////////////////////////////////////////////////////////
178 // CDDSImage public functions
179
180 ///////////////////////////////////////////////////////////////////////////////
181 // default constructor
CDDSImage()182 CDDSImage::CDDSImage()
183 : m_format(0),
184 m_components(0),
185 m_type(TextureNone),
186 m_valid(false)
187 {
188 }
189
~CDDSImage()190 CDDSImage::~CDDSImage()
191 {
192 }
193
create_textureFlat(unsigned int format,unsigned int components,const CTexture & baseImage)194 void CDDSImage::create_textureFlat(unsigned int format, unsigned int components, const CTexture &baseImage)
195 {
196 assert(format != 0);
197 assert(components != 0);
198 assert(baseImage.get_depth() == 1);
199
200 // remove any existing images
201 clear();
202
203 m_format = format;
204 m_components = components;
205 m_type = TextureFlat;
206
207 m_images.push_back(baseImage);
208
209 m_valid = true;
210 }
211
create_texture3D(unsigned int format,unsigned int components,const CTexture & baseImage)212 void CDDSImage::create_texture3D(unsigned int format, unsigned int components, const CTexture &baseImage)
213 {
214 assert(format != 0);
215 assert(components != 0);
216 assert(baseImage.get_depth() > 1);
217
218 // remove any existing images
219 clear();
220
221 m_format = format;
222 m_components = components;
223 m_type = Texture3D;
224
225 m_images.push_back(baseImage);
226
227 m_valid = true;
228 }
229
same_size(const CTexture & a,const CTexture & b)230 inline bool same_size(const CTexture &a, const CTexture &b)
231 {
232 if (a.get_width() != b.get_width())
233 return false;
234 if (a.get_height() != b.get_height())
235 return false;
236 if (a.get_depth() != b.get_depth())
237 return false;
238
239 return true;
240 }
241
create_textureCubemap(unsigned int format,unsigned int components,const CTexture & positiveX,const CTexture & negativeX,const CTexture & positiveY,const CTexture & negativeY,const CTexture & positiveZ,const CTexture & negativeZ)242 void CDDSImage::create_textureCubemap(unsigned int format, unsigned int components,
243 const CTexture &positiveX, const CTexture &negativeX,
244 const CTexture &positiveY, const CTexture &negativeY,
245 const CTexture &positiveZ, const CTexture &negativeZ)
246 {
247 assert(format != 0);
248 assert(components != 0);
249 assert(positiveX.get_depth() == 1);
250
251 // verify that all dimensions are the same
252 assert(same_size(positiveX, negativeX));
253 assert(same_size(positiveX, positiveY));
254 assert(same_size(positiveX, negativeY));
255 assert(same_size(positiveX, positiveZ));
256 assert(same_size(positiveX, negativeZ));
257
258 // remove any existing images
259 clear();
260
261 m_format = format;
262 m_components = components;
263 m_type = TextureCubemap;
264
265 m_images.push_back(positiveX);
266 m_images.push_back(negativeX);
267 m_images.push_back(positiveY);
268 m_images.push_back(negativeY);
269 m_images.push_back(positiveZ);
270 m_images.push_back(negativeZ);
271
272 m_valid = true;
273 }
274
275 ///////////////////////////////////////////////////////////////////////////////
276 // loads DDS image
277 //
278 // filename - fully qualified name of DDS image
279 // flipImage - specifies whether image is flipped on load, default is true
load(string filename,bool flipImage)280 bool CDDSImage::load(string filename, bool flipImage)
281 {
282 assert(filename.length() != 0);
283
284 // clear any previously loaded images
285 clear();
286
287 // open file
288 // FILE *fp = fopen(filename.c_str(), "rb");
289 CFileHandler file(filename);
290
291 if (!file.FileExists())
292 return false;
293
294 // read in file marker, make sure its a DDS file
295 char filecode[4];
296 //fread(filecode, 1, 4, fp);
297 file.Read(filecode, 4);
298 if (strncmp(filecode, "DDS ", 4) != 0)
299 {
300 //fclose(fp);
301 return false;
302 }
303
304 // read in DDS header
305 DDS_HEADER ddsh;
306 //fread(&ddsh, sizeof(DDS_HEADER), 1, fp);
307 int tmp = sizeof(unsigned int);
308 file.Read(&ddsh.dwSize, tmp);
309 file.Read(&ddsh.dwFlags, tmp);
310 file.Read(&ddsh.dwHeight, tmp);
311 file.Read(&ddsh.dwWidth, tmp);
312 file.Read(&ddsh.dwPitchOrLinearSize, tmp);
313 file.Read(&ddsh.dwDepth, tmp);
314 file.Read(&ddsh.dwMipMapCount, tmp);
315 file.Read(&ddsh.dwReserved1, tmp*11);
316 file.Read(&ddsh.ddspf.dwSize, tmp);
317 file.Read(&ddsh.ddspf.dwFlags, tmp);
318 file.Read(&ddsh.ddspf.dwFourCC, tmp);
319 file.Read(&ddsh.ddspf.dwRGBBitCount, tmp);
320 file.Read(&ddsh.ddspf.dwRBitMask, tmp);
321 file.Read(&ddsh.ddspf.dwGBitMask, tmp);
322 file.Read(&ddsh.ddspf.dwBBitMask, tmp);
323 file.Read(&ddsh.ddspf.dwABitMask, tmp);
324 file.Read(&ddsh.dwCaps1, tmp);
325 file.Read(&ddsh.dwCaps2, tmp);
326 file.Read(&ddsh.dwReserved2, tmp*3);
327
328 ddsh.dwSize = swabdword(ddsh.dwSize);
329 ddsh.dwFlags = swabdword(ddsh.dwFlags);
330 ddsh.dwHeight = swabdword(ddsh.dwHeight);
331 ddsh.dwWidth = swabdword(ddsh.dwWidth);
332 ddsh.dwPitchOrLinearSize = swabdword(ddsh.dwPitchOrLinearSize);
333 ddsh.dwDepth = swabdword(ddsh.dwDepth);
334 ddsh.dwMipMapCount = swabdword(ddsh.dwMipMapCount);
335 ddsh.ddspf.dwSize = swabdword(ddsh.ddspf.dwSize);
336 ddsh.ddspf.dwFlags = swabdword(ddsh.ddspf.dwFlags);
337 ddsh.ddspf.dwFourCC = swabdword(ddsh.ddspf.dwFourCC);
338 ddsh.ddspf.dwRGBBitCount = swabdword(ddsh.ddspf.dwRGBBitCount);
339 ddsh.ddspf.dwRBitMask = swabdword(ddsh.ddspf.dwRBitMask);
340 ddsh.ddspf.dwGBitMask = swabdword(ddsh.ddspf.dwGBitMask);
341 ddsh.ddspf.dwBBitMask = swabdword(ddsh.ddspf.dwBBitMask);
342 ddsh.ddspf.dwABitMask = swabdword(ddsh.ddspf.dwABitMask);
343 ddsh.dwCaps1 = swabdword(ddsh.dwCaps1);
344 ddsh.dwCaps2 = swabdword(ddsh.dwCaps2);
345
346 // default to flat texture type (1D, 2D, or rectangle)
347 m_type = TextureFlat;
348
349 // check if image is a cubemap
350 if (ddsh.dwCaps2 & DDSF_CUBEMAP)
351 m_type = TextureCubemap;
352
353 // check if image is a volume texture
354 if ((ddsh.dwCaps2 & DDSF_VOLUME) && (ddsh.dwDepth > 0))
355 m_type = Texture3D;
356
357 // figure out what the image format is
358 if (ddsh.ddspf.dwFlags & DDSF_FOURCC)
359 {
360 switch(ddsh.ddspf.dwFourCC)
361 {
362 case FOURCC_DXT1:
363 m_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
364 m_components = 3;
365 break;
366 case FOURCC_DXT3:
367 m_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
368 m_components = 4;
369 break;
370 case FOURCC_DXT5:
371 m_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
372 m_components = 4;
373 break;
374 default:
375 //fclose(fp);
376 return false;
377 }
378 }
379 else if (ddsh.ddspf.dwFlags == DDSF_RGBA && ddsh.ddspf.dwRGBBitCount == 32)
380 {
381 m_format = GL_BGRA_EXT;
382 m_components = 4;
383 }
384 else if (ddsh.ddspf.dwFlags == DDSF_RGB && ddsh.ddspf.dwRGBBitCount == 32)
385 {
386 m_format = GL_BGRA_EXT;
387 m_components = 4;
388 }
389 else if (ddsh.ddspf.dwFlags == DDSF_RGB && ddsh.ddspf.dwRGBBitCount == 24)
390 {
391 m_format = GL_BGR_EXT;
392 m_components = 3;
393 }
394 else if (ddsh.ddspf.dwRGBBitCount == 8)
395 {
396 m_format = GL_LUMINANCE;
397 m_components = 1;
398 }
399 else
400 {
401 //fclose(fp);
402 return false;
403 }
404
405 // store primary surface width/height/depth
406 unsigned int width, height, depth;
407 width = ddsh.dwWidth;
408 height = ddsh.dwHeight;
409 depth = clamp_size(ddsh.dwDepth); // set to 1 if 0
410
411 // use correct size calculation function depending on whether image is
412 // compressed
413 unsigned int (CDDSImage::*sizefunc)(unsigned int, unsigned int);
414 sizefunc = (is_compressed() ? &CDDSImage::size_dxtc : &CDDSImage::size_rgb);
415
416 // load all surfaces for the image (6 surfaces for cubemaps)
417 for (unsigned int n = 0; n < (unsigned int)(m_type == TextureCubemap ? 6 : 1); n++)
418 {
419 // add empty texture object
420 m_images.push_back(CTexture());
421
422 // get reference to newly added texture object
423 CTexture &img = m_images[n];
424
425 // calculate surface size
426 unsigned int size = (this->*sizefunc)(width, height)*depth;
427
428 // load surface
429 unsigned char *pixels = new unsigned char[size];
430 //fread(pixels, 1, size, fp);
431 file.Read(pixels, size);
432
433 img.create(width, height, depth, size, pixels);
434
435 delete [] pixels;
436
437 if (flipImage) flip(img);
438
439 unsigned int w = clamp_size(width >> 1);
440 unsigned int h = clamp_size(height >> 1);
441 unsigned int d = clamp_size(depth >> 1);
442
443 // store number of mipmaps
444 unsigned int numMipmaps = ddsh.dwMipMapCount;
445
446 // number of mipmaps in file includes main surface so decrease count
447 // by one
448 if (numMipmaps != 0)
449 numMipmaps--;
450
451 // load all mipmaps for current surface
452 for (unsigned int i = 0; i < numMipmaps && (w || h); i++)
453 {
454 // add empty surface
455 img.add_mipmap(CSurface());
456
457 // get reference to newly added mipmap
458 CSurface &mipmap = img.get_mipmap(i);
459
460 // calculate mipmap size
461 size = (this->*sizefunc)(w, h)*d;
462
463 unsigned char *pixels = new unsigned char[size];
464 //fread(pixels, 1, size, fp);
465 file.Read(pixels, size);
466
467 mipmap.create(w, h, d, size, pixels);
468
469 delete [] pixels;
470
471 if (flipImage) flip(mipmap);
472
473 // shrink to next power of 2
474 w = clamp_size(w >> 1);
475 h = clamp_size(h >> 1);
476 d = clamp_size(d >> 1);
477 }
478 }
479
480 // swap cubemaps on y axis (since image is flipped in OGL)
481 if (m_type == TextureCubemap && flipImage)
482 {
483 CTexture tmp;
484 tmp = m_images[3];
485 m_images[3] = m_images[2];
486 m_images[2] = tmp;
487 }
488
489 //fclose(fp);
490
491 m_valid = true;
492
493 return true;
494 }
495
write_texture(const CTexture & texture,FILE * fp)496 bool CDDSImage::write_texture(const CTexture &texture, FILE *fp)
497 {
498 assert(get_num_mipmaps() == texture.get_num_mipmaps());
499
500 int res = fwrite(texture, 1, texture.get_size(), fp);
501 if (res<0) {
502 return false;
503 }
504 for (unsigned int i = 0; i < texture.get_num_mipmaps(); i++)
505 {
506 const CSurface &mipmap = texture.get_mipmap(i);
507 res = fwrite(mipmap, 1, mipmap.get_size(), fp);
508 if (res<0) {
509 return false;
510 }
511 }
512 return true;
513 }
514
save(std::string filename,bool flipImage)515 bool CDDSImage::save(std::string filename, bool flipImage)
516 {
517 assert(m_valid);
518 assert(m_type != TextureNone);
519
520 DDS_HEADER ddsh;
521 unsigned int headerSize = sizeof(DDS_HEADER);
522 memset(&ddsh, 0, headerSize);
523 ddsh.dwSize = headerSize;
524 ddsh.dwFlags = DDSF_CAPS | DDSF_WIDTH | DDSF_HEIGHT | DDSF_PIXELFORMAT;
525 ddsh.dwHeight = get_height();
526 ddsh.dwWidth = get_width();
527
528 if (is_compressed())
529 {
530 ddsh.dwFlags |= DDSF_LINEARSIZE;
531 ddsh.dwPitchOrLinearSize = get_size();
532 }
533 else
534 {
535 ddsh.dwFlags |= DDSF_PITCH;
536 ddsh.dwPitchOrLinearSize = get_dword_aligned_linesize(get_width(), m_components * 8);
537 }
538
539 if (m_type == Texture3D)
540 {
541 ddsh.dwFlags |= DDSF_DEPTH;
542 ddsh.dwDepth = get_depth();
543 }
544
545 if (get_num_mipmaps() > 0)
546 {
547 ddsh.dwFlags |= DDSF_MIPMAPCOUNT;
548 ddsh.dwMipMapCount = get_num_mipmaps() + 1;
549 }
550
551 ddsh.ddspf.dwSize = sizeof(DDS_PIXELFORMAT);
552
553 if (is_compressed())
554 {
555 ddsh.ddspf.dwFlags = DDSF_FOURCC;
556
557 if (m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
558 ddsh.ddspf.dwFourCC = FOURCC_DXT1;
559 if (m_format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
560 ddsh.ddspf.dwFourCC = FOURCC_DXT3;
561 if (m_format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
562 ddsh.ddspf.dwFourCC = FOURCC_DXT5;
563 }
564 else
565 {
566 ddsh.ddspf.dwFlags = (m_components == 4) ? DDSF_RGBA : DDSF_RGB;
567 ddsh.ddspf.dwRGBBitCount = m_components * 8;
568 ddsh.ddspf.dwRBitMask = 0x00ff0000;
569 ddsh.ddspf.dwGBitMask = 0x0000ff00;
570 ddsh.ddspf.dwBBitMask = 0x000000ff;
571
572 if (m_components == 4)
573 {
574 ddsh.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
575 ddsh.ddspf.dwABitMask = 0xff000000;
576 }
577 }
578
579 ddsh.dwCaps1 = DDSF_TEXTURE;
580
581 if (m_type == TextureCubemap)
582 {
583 ddsh.dwCaps1 |= DDSF_COMPLEX;
584 ddsh.dwCaps2 = DDSF_CUBEMAP | DDSF_CUBEMAP_ALL_FACES;
585 }
586
587 if (m_type == Texture3D)
588 {
589 ddsh.dwCaps1 |= DDSF_COMPLEX;
590 ddsh.dwCaps2 = DDSF_VOLUME;
591 }
592
593 if (get_num_mipmaps() > 0)
594 ddsh.dwCaps1 |= DDSF_COMPLEX | DDSF_MIPMAP;
595
596 // open file
597 FILE *fp = fopen(filename.c_str(), "wb");
598 if (fp == NULL) {
599 LOG_L(L_ERROR, "couldn't create texture %s", filename.c_str());
600 return false;
601 }
602
603 // write file header
604 int res = fwrite("DDS ", 1, 4, fp);
605 if (res<0) {
606 fclose(fp);
607 return false;
608 }
609
610 // write dds header
611 res = fwrite(&ddsh, 1, sizeof(DDS_HEADER), fp);
612 if (res<0) {
613 fclose(fp);
614 return false;
615 }
616
617 if (m_type != TextureCubemap)
618 {
619 CTexture tex = m_images[0];
620 if (flipImage) flip_texture(tex);
621 if (!write_texture(tex, fp)) {
622 LOG_L(L_ERROR, "couldn't write texture %s: %s",filename.c_str(), strerror(ferror(fp)));
623 fclose(fp);
624 return false;
625 }
626 }
627 else
628 {
629 assert(m_images.size() == 6);
630
631 for (unsigned int i = 0; i < m_images.size(); i++)
632 {
633 CTexture cubeFace;
634
635 if (i == 2)
636 cubeFace = m_images[3];
637 else if (i == 3)
638 cubeFace = m_images[2];
639 else
640 cubeFace = m_images[i];
641
642 if (flipImage) flip_texture(cubeFace);
643 write_texture(cubeFace, fp);
644 }
645 }
646
647 fclose(fp);
648 return true;
649 }
650
651 ///////////////////////////////////////////////////////////////////////////////
652 // free image memory
clear()653 void CDDSImage::clear()
654 {
655 m_components = 0;
656 m_format = 0;
657 m_type = TextureNone;
658 m_valid = false;
659
660 m_images.clear();
661 }
662
is_compressed() const663 bool CDDSImage::is_compressed() const
664 {
665 if ((m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ||
666 (m_format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) ||
667 (m_format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT))
668 return true;
669 else
670 return false;
671 }
672
673 #ifndef BITMAP_NO_OPENGL
674
675 ///////////////////////////////////////////////////////////////////////////////
676 // uploads a compressed/uncompressed 1D texture
upload_texture1D()677 bool CDDSImage::upload_texture1D()
678 {
679 assert(m_valid);
680 assert(!m_images.empty());
681
682 const CTexture &baseImage = m_images[0];
683
684 assert(baseImage.get_height() == 1);
685 assert(baseImage.get_width() > 0);
686
687 if (is_compressed())
688 {
689 glCompressedTexImage1DARB(GL_TEXTURE_1D, 0, m_format,
690 baseImage.get_width(), 0, baseImage.get_size(), baseImage);
691
692 // load all mipmaps
693 for (unsigned int i = 0; i < baseImage.get_num_mipmaps(); i++)
694 {
695 const CSurface &mipmap = baseImage.get_mipmap(i);
696 glCompressedTexImage1DARB(GL_TEXTURE_1D, i+1, m_format,
697 mipmap.get_width(), 0, mipmap.get_size(), mipmap);
698 }
699 }
700 else
701 {
702 GLint alignment = -1;
703 if (!is_dword_aligned())
704 {
705 glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
706 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
707 }
708
709 glTexImage1D(GL_TEXTURE_1D, 0, m_components, baseImage.get_width(), 0,
710 m_format, GL_UNSIGNED_BYTE, baseImage);
711
712 // load all mipmaps
713 for (unsigned int i = 0; i < baseImage.get_num_mipmaps(); i++)
714 {
715 const CSurface &mipmap = baseImage.get_mipmap(i);
716
717 glTexImage1D(GL_TEXTURE_1D, i+1, m_components,
718 mipmap.get_width(), 0, m_format, GL_UNSIGNED_BYTE, mipmap);
719 }
720
721 if (alignment != -1)
722 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
723 }
724
725 return true;
726 }
727
728 ///////////////////////////////////////////////////////////////////////////////
729 // uploads a compressed/uncompressed 2D texture
730 //
731 // imageIndex - allows you to optionally specify other loaded surfaces for 2D
732 // textures such as a face in a cubemap or a slice in a volume
733 //
734 // default: 0
735 //
736 // target - allows you to optionally specify a different texture target for
737 // the 2D texture such as a specific face of a cubemap
738 //
739 // default: GL_TEXTURE_2D
upload_texture2D(unsigned int imageIndex,int target)740 bool CDDSImage::upload_texture2D(unsigned int imageIndex, int target)
741 {
742 assert(m_valid);
743 assert(!m_images.empty());
744 assert(imageIndex >= 0);
745 assert(imageIndex < m_images.size());
746 assert(m_images[imageIndex]);
747
748 const CTexture &image = m_images[imageIndex];
749
750 assert(image.get_height() > 0);
751 assert(image.get_width() > 0);
752 assert(target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE_NV ||
753 (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
754 target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB));
755
756 if (is_compressed())
757 {
758 glCompressedTexImage2DARB(target, 0, m_format, image.get_width(),
759 image.get_height(), 0, image.get_size(), image);
760
761 // load all mipmaps
762 for (unsigned int i = 0; i < image.get_num_mipmaps(); i++)
763 {
764 const CSurface &mipmap = image.get_mipmap(i);
765 glCompressedTexImage2DARB(target, i+1, m_format,
766 mipmap.get_width(), mipmap.get_height(), 0,
767 mipmap.get_size(), mipmap);
768 }
769 }
770 else
771 {
772 GLint alignment = -1;
773 if (!is_dword_aligned())
774 {
775 glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
776 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
777 }
778
779 glTexImage2D(target, 0, m_components, image.get_width(),
780 image.get_height(), 0, m_format, GL_UNSIGNED_BYTE,
781 image);
782
783 // load all mipmaps
784 for (unsigned int i = 0; i < image.get_num_mipmaps(); i++)
785 {
786 const CSurface &mipmap = image.get_mipmap(i);
787
788 glTexImage2D(target, i+1, m_components, mipmap.get_width(),
789 mipmap.get_height(), 0, m_format, GL_UNSIGNED_BYTE, mipmap);
790 }
791
792 if (alignment != -1)
793 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
794 }
795
796 return true;
797 }
798
799 ///////////////////////////////////////////////////////////////////////////////
800 // uploads a compressed/uncompressed 3D texture
upload_texture3D()801 bool CDDSImage::upload_texture3D()
802 {
803 assert(m_valid);
804 assert(!m_images.empty());
805 assert(m_type == Texture3D);
806
807 const CTexture &baseImage = m_images[0];
808
809 assert(baseImage.get_depth() >= 1);
810
811 if (is_compressed())
812 {
813 glCompressedTexImage3DARB(GL_TEXTURE_3D, 0, m_format,
814 baseImage.get_width(), baseImage.get_height(), baseImage.get_depth(),
815 0, baseImage.get_size(), baseImage);
816
817 // load all mipmap volumes
818 for (unsigned int i = 0; i < baseImage.get_num_mipmaps(); i++)
819 {
820 const CSurface &mipmap = baseImage.get_mipmap(i);
821 glCompressedTexImage3DARB(GL_TEXTURE_3D, i+1, m_format,
822 mipmap.get_width(), mipmap.get_height(), mipmap.get_depth(),
823 0, mipmap.get_size(), mipmap);
824 }
825 }
826 else
827 {
828 GLint alignment = -1;
829 if (!is_dword_aligned())
830 {
831 glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
832 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
833 }
834
835 glTexImage3D(GL_TEXTURE_3D, 0, m_components, baseImage.get_width(),
836 baseImage.get_height(), baseImage.get_depth(), 0, m_format,
837 GL_UNSIGNED_BYTE, baseImage);
838
839 // load all mipmap volumes
840 for (unsigned int i = 0; i < baseImage.get_num_mipmaps(); i++)
841 {
842 const CSurface &mipmap = baseImage.get_mipmap(i);
843
844 glTexImage3D(GL_TEXTURE_3D, i+1, m_components,
845 mipmap.get_width(), mipmap.get_height(), mipmap.get_depth(), 0,
846 m_format, GL_UNSIGNED_BYTE, mipmap);
847 }
848
849 if (alignment != -1)
850 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
851 }
852
853 return true;
854 }
855
upload_textureRectangle()856 bool CDDSImage::upload_textureRectangle()
857 {
858 return upload_texture2D(0, GL_TEXTURE_RECTANGLE_NV);
859 }
860
861 ///////////////////////////////////////////////////////////////////////////////
862 // uploads a compressed/uncompressed cubemap texture
upload_textureCubemap()863 bool CDDSImage::upload_textureCubemap()
864 {
865 assert(m_valid);
866 assert(!m_images.empty());
867 assert(m_type == TextureCubemap);
868 assert(m_images.size() == 6);
869
870 GLenum target;
871
872 // loop through cubemap faces and load them as 2D textures
873 for (unsigned int n = 0; n < 6; n++)
874 {
875 // specify cubemap face
876 target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + n;
877 if (!upload_texture2D(n, target))
878 return false;
879 }
880
881 return true;
882 }
883
884 #endif // !BITMAP_NO_OPENGL
885
886 ///////////////////////////////////////////////////////////////////////////////
887 // clamps input size to [1-size]
clamp_size(unsigned int size)888 inline unsigned int CDDSImage::clamp_size(unsigned int size)
889 {
890 if (size <= 0)
891 size = 1;
892
893 return size;
894 }
895
896 ///////////////////////////////////////////////////////////////////////////////
897 // CDDSImage private functions
898 ///////////////////////////////////////////////////////////////////////////////
899
900 ///////////////////////////////////////////////////////////////////////////////
901 // calculates size of DXTC texture in bytes
size_dxtc(unsigned int width,unsigned int height)902 inline unsigned int CDDSImage::size_dxtc(unsigned int width, unsigned int height)
903 {
904 return ((width+3)/4)*((height+3)/4)*
905 (m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
906 }
907
908 ///////////////////////////////////////////////////////////////////////////////
909 // calculates size of uncompressed RGB texture in bytes
size_rgb(unsigned int width,unsigned int height)910 inline unsigned int CDDSImage::size_rgb(unsigned int width, unsigned int height)
911 {
912 return width*height*m_components;
913 }
914
915 ///////////////////////////////////////////////////////////////////////////////
916 // flip image around X axis
flip(CSurface & surface)917 void CDDSImage::flip(CSurface &surface)
918 {
919 unsigned int linesize;
920
921 if (!is_compressed())
922 {
923 assert(surface.get_depth() > 0);
924
925 unsigned int imagesize = surface.get_size()/surface.get_depth();
926 linesize = imagesize / surface.get_height();
927
928 for (unsigned int n = 0; n < surface.get_depth(); n++)
929 {
930 unsigned int offset = imagesize*n;
931 unsigned char *top = (unsigned char*)surface + offset;
932 unsigned char *bottom = top + (imagesize-linesize);
933
934 for (unsigned int i = 0; i < (surface.get_height() >> 1); i++)
935 {
936 swap(bottom, top, linesize);
937
938 top += linesize;
939 bottom -= linesize;
940 }
941 }
942 }
943 else
944 {
945 void (CDDSImage::*flipblocks)(DXTColBlock*, unsigned int);
946 unsigned int xblocks = surface.get_width() / 4;
947 unsigned int yblocks = surface.get_height() / 4;
948 unsigned int blocksize;
949
950 switch (m_format)
951 {
952 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
953 blocksize = 8;
954 flipblocks = &CDDSImage::flip_blocks_dxtc1;
955 break;
956 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
957 blocksize = 16;
958 flipblocks = &CDDSImage::flip_blocks_dxtc3;
959 break;
960 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
961 blocksize = 16;
962 flipblocks = &CDDSImage::flip_blocks_dxtc5;
963 break;
964 default:
965 return;
966 }
967
968 linesize = xblocks * blocksize;
969
970 DXTColBlock *top;
971 DXTColBlock *bottom;
972
973 for (unsigned int j = 0; j < (yblocks >> 1); j++)
974 {
975 top = (DXTColBlock*)((unsigned char*)surface+ j * linesize);
976 bottom = (DXTColBlock*)((unsigned char*)surface + (((yblocks-j)-1) * linesize));
977
978 (this->*flipblocks)(top, xblocks);
979 (this->*flipblocks)(bottom, xblocks);
980
981 swap(bottom, top, linesize);
982 }
983 }
984 }
985
flip_texture(CTexture & texture)986 void CDDSImage::flip_texture(CTexture &texture)
987 {
988 flip(texture);
989
990 for (unsigned int i = 0; i < texture.get_num_mipmaps(); i++)
991 {
992 flip(texture.get_mipmap(i));
993 }
994 }
995
996 ///////////////////////////////////////////////////////////////////////////////
997 // swap to sections of memory
swap(void * byte1,void * byte2,unsigned int size)998 void CDDSImage::swap(void *byte1, void *byte2, unsigned int size)
999 {
1000 unsigned char *tmp = new unsigned char[size];
1001
1002 memcpy(tmp, byte1, size);
1003 memcpy(byte1, byte2, size);
1004 memcpy(byte2, tmp, size);
1005
1006 delete [] tmp;
1007 }
1008
1009 ///////////////////////////////////////////////////////////////////////////////
1010 // flip a DXT1 color block
flip_blocks_dxtc1(DXTColBlock * line,unsigned int numBlocks)1011 void CDDSImage::flip_blocks_dxtc1(DXTColBlock *line, unsigned int numBlocks)
1012 {
1013 DXTColBlock *curblock = line;
1014
1015 for (unsigned int i = 0; i < numBlocks; i++)
1016 {
1017 swap(&curblock->row[0], &curblock->row[3], sizeof(unsigned char));
1018 swap(&curblock->row[1], &curblock->row[2], sizeof(unsigned char));
1019
1020 curblock++;
1021 }
1022 }
1023
1024 ///////////////////////////////////////////////////////////////////////////////
1025 // flip a DXT3 color block
flip_blocks_dxtc3(DXTColBlock * line,unsigned int numBlocks)1026 void CDDSImage::flip_blocks_dxtc3(DXTColBlock *line, unsigned int numBlocks)
1027 {
1028 DXTColBlock *curblock = line;
1029 DXT3AlphaBlock *alphablock;
1030
1031 for (unsigned int i = 0; i < numBlocks; i++)
1032 {
1033 alphablock = reinterpret_cast<DXT3AlphaBlock*>(curblock);
1034
1035 swap(&alphablock->row[0], &alphablock->row[3], sizeof(unsigned short));
1036 swap(&alphablock->row[1], &alphablock->row[2], sizeof(unsigned short));
1037
1038 curblock++;
1039
1040 swap(&curblock->row[0], &curblock->row[3], sizeof(unsigned char));
1041 swap(&curblock->row[1], &curblock->row[2], sizeof(unsigned char));
1042
1043 curblock++;
1044 }
1045 }
1046
1047 ///////////////////////////////////////////////////////////////////////////////
1048 // flip a DXT5 alpha block
flip_dxt5_alpha(DXT5AlphaBlock * block)1049 void CDDSImage::flip_dxt5_alpha(DXT5AlphaBlock *block)
1050 {
1051 unsigned char gBits[4][4];
1052
1053 const unsigned int mask = 0x00000007; // bits = 00 00 01 11
1054 unsigned int bits = 0;
1055 memcpy(&bits, &block->row[0], sizeof(unsigned char) * 3);
1056
1057 gBits[0][0] = (unsigned char)(bits & mask);
1058 bits >>= 3;
1059 gBits[0][1] = (unsigned char)(bits & mask);
1060 bits >>= 3;
1061 gBits[0][2] = (unsigned char)(bits & mask);
1062 bits >>= 3;
1063 gBits[0][3] = (unsigned char)(bits & mask);
1064 bits >>= 3;
1065 gBits[1][0] = (unsigned char)(bits & mask);
1066 bits >>= 3;
1067 gBits[1][1] = (unsigned char)(bits & mask);
1068 bits >>= 3;
1069 gBits[1][2] = (unsigned char)(bits & mask);
1070 bits >>= 3;
1071 gBits[1][3] = (unsigned char)(bits & mask);
1072
1073 bits = 0;
1074 memcpy(&bits, &block->row[3], sizeof(unsigned char) * 3);
1075
1076 gBits[2][0] = (unsigned char)(bits & mask);
1077 bits >>= 3;
1078 gBits[2][1] = (unsigned char)(bits & mask);
1079 bits >>= 3;
1080 gBits[2][2] = (unsigned char)(bits & mask);
1081 bits >>= 3;
1082 gBits[2][3] = (unsigned char)(bits & mask);
1083 bits >>= 3;
1084 gBits[3][0] = (unsigned char)(bits & mask);
1085 bits >>= 3;
1086 gBits[3][1] = (unsigned char)(bits & mask);
1087 bits >>= 3;
1088 gBits[3][2] = (unsigned char)(bits & mask);
1089 bits >>= 3;
1090 gBits[3][3] = (unsigned char)(bits & mask);
1091
1092 memset(block->row, 0, sizeof(unsigned char) * 6);
1093
1094 unsigned int *pBits = ((unsigned int*) &(block->row[0]));
1095
1096 *pBits = *pBits | (gBits[3][0] << 0);
1097 *pBits = *pBits | (gBits[3][1] << 3);
1098 *pBits = *pBits | (gBits[3][2] << 6);
1099 *pBits = *pBits | (gBits[3][3] << 9);
1100
1101 *pBits = *pBits | (gBits[2][0] << 12);
1102 *pBits = *pBits | (gBits[2][1] << 15);
1103 *pBits = *pBits | (gBits[2][2] << 18);
1104 *pBits = *pBits | (gBits[2][3] << 21);
1105
1106 pBits = ((unsigned int*) &(block->row[3]));
1107
1108 #ifdef MACOS
1109 *pBits &= 0x000000ff;
1110 #else
1111 *pBits &= 0xff000000;
1112 #endif
1113
1114 *pBits = *pBits | (gBits[1][0] << 0);
1115 *pBits = *pBits | (gBits[1][1] << 3);
1116 *pBits = *pBits | (gBits[1][2] << 6);
1117 *pBits = *pBits | (gBits[1][3] << 9);
1118
1119 *pBits = *pBits | (gBits[0][0] << 12);
1120 *pBits = *pBits | (gBits[0][1] << 15);
1121 *pBits = *pBits | (gBits[0][2] << 18);
1122 *pBits = *pBits | (gBits[0][3] << 21);
1123 }
1124
1125 ///////////////////////////////////////////////////////////////////////////////
1126 // flip a DXT5 color block
flip_blocks_dxtc5(DXTColBlock * line,unsigned int numBlocks)1127 void CDDSImage::flip_blocks_dxtc5(DXTColBlock *line, unsigned int numBlocks)
1128 {
1129 DXTColBlock *curblock = line;
1130 DXT5AlphaBlock *alphablock;
1131
1132 for (unsigned int i = 0; i < numBlocks; i++)
1133 {
1134 alphablock = reinterpret_cast<DXT5AlphaBlock*>(curblock);
1135
1136 flip_dxt5_alpha(alphablock);
1137
1138 curblock++;
1139
1140 swap(&curblock->row[0], &curblock->row[3], sizeof(unsigned char));
1141 swap(&curblock->row[1], &curblock->row[2], sizeof(unsigned char));
1142
1143 curblock++;
1144 }
1145 }
1146
1147 ///////////////////////////////////////////////////////////////////////////////
1148 // CTexture implementation
1149 ///////////////////////////////////////////////////////////////////////////////
1150
1151 ///////////////////////////////////////////////////////////////////////////////
1152 // default constructor
CTexture()1153 CTexture::CTexture()
1154 : CSurface() // initialize base class part
1155 {
1156 }
1157
1158 ///////////////////////////////////////////////////////////////////////////////
1159 // creates an empty texture
CTexture(unsigned int w,unsigned int h,unsigned int d,unsigned int imgsize,const unsigned char * pixels)1160 CTexture::CTexture(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels)
1161 : CSurface(w, h, d, imgsize, pixels) // initialize base class part
1162 {
1163 }
1164
~CTexture()1165 CTexture::~CTexture()
1166 {
1167 }
1168
1169 ///////////////////////////////////////////////////////////////////////////////
1170 // copy constructor
CTexture(const CTexture & copy)1171 CTexture::CTexture(const CTexture ©)
1172 : CSurface(copy)
1173 {
1174 for (unsigned int i = 0; i < copy.get_num_mipmaps(); i++)
1175 m_mipmaps.push_back(copy.get_mipmap(i));
1176 }
1177
1178 ///////////////////////////////////////////////////////////////////////////////
1179 // assignment operator
operator =(const CTexture & rhs)1180 CTexture &CTexture::operator= (const CTexture &rhs)
1181 {
1182 if (this != &rhs)
1183 {
1184 CSurface::operator = (rhs);
1185
1186 m_mipmaps.clear();
1187 for (unsigned int i = 0; i < rhs.get_num_mipmaps(); i++)
1188 m_mipmaps.push_back(rhs.get_mipmap(i));
1189 }
1190
1191 return *this;
1192 }
1193
create(unsigned int w,unsigned int h,unsigned int d,unsigned int imgsize,const unsigned char * pixels)1194 void CTexture::create(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels)
1195 {
1196 CSurface::create(w, h, d, imgsize, pixels);
1197
1198 m_mipmaps.clear();
1199 }
1200
clear()1201 void CTexture::clear()
1202 {
1203 CSurface::clear();
1204
1205 m_mipmaps.clear();
1206 }
1207
1208 ///////////////////////////////////////////////////////////////////////////////
1209 // CSurface implementation
1210 ///////////////////////////////////////////////////////////////////////////////
1211
1212 ///////////////////////////////////////////////////////////////////////////////
1213 // default constructor
CSurface()1214 CSurface::CSurface()
1215 : m_width(0),
1216 m_height(0),
1217 m_depth(0),
1218 m_size(0),
1219 m_pixels(NULL)
1220 {
1221 }
1222
1223 ///////////////////////////////////////////////////////////////////////////////
1224 // creates an empty image
CSurface(unsigned int w,unsigned int h,unsigned int d,unsigned int imgsize,const unsigned char * pixels)1225 CSurface::CSurface(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels)
1226 : m_width(0),
1227 m_height(0),
1228 m_depth(0),
1229 m_size(0),
1230 m_pixels(NULL)
1231 {
1232 create(w, h, d, imgsize, pixels);
1233 }
1234
1235 ///////////////////////////////////////////////////////////////////////////////
1236 // copy constructor
CSurface(const CSurface & copy)1237 CSurface::CSurface(const CSurface ©)
1238 : m_width(0),
1239 m_height(0),
1240 m_depth(0),
1241 m_size(0),
1242 m_pixels(NULL)
1243 {
1244 if (copy.get_size() != 0)
1245 {
1246 m_size = copy.get_size();
1247 m_width = copy.get_width();
1248 m_height = copy.get_height();
1249 m_depth = copy.get_depth();
1250
1251 m_pixels = new unsigned char[m_size];
1252 memcpy(m_pixels, copy, m_size);
1253 }
1254 }
1255
1256 ///////////////////////////////////////////////////////////////////////////////
1257 // assignment operator
operator =(const CSurface & rhs)1258 CSurface &CSurface::operator= (const CSurface &rhs)
1259 {
1260 if (this != &rhs)
1261 {
1262 clear();
1263
1264 if (rhs.get_size())
1265 {
1266 m_size = rhs.get_size();
1267 m_width = rhs.get_width();
1268 m_height = rhs.get_height();
1269 m_depth = rhs.get_depth();
1270
1271 m_pixels = new unsigned char[m_size];
1272 memcpy(m_pixels, rhs, m_size);
1273 }
1274 }
1275
1276 return *this;
1277 }
1278
1279 ///////////////////////////////////////////////////////////////////////////////
1280 // clean up image memory
~CSurface()1281 CSurface::~CSurface()
1282 {
1283 clear();
1284 }
1285
1286 ///////////////////////////////////////////////////////////////////////////////
1287 // returns a pointer to image
operator unsigned char*() const1288 CSurface::operator unsigned char*() const
1289 {
1290 return m_pixels;
1291 }
1292
1293 ///////////////////////////////////////////////////////////////////////////////
1294 // creates an empty image
create(unsigned int w,unsigned int h,unsigned int d,unsigned int imgsize,const unsigned char * pixels)1295 void CSurface::create(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels)
1296 {
1297 assert(w != 0);
1298 assert(h != 0);
1299 assert(d != 0);
1300 assert(imgsize != 0);
1301 assert(pixels);
1302
1303 clear();
1304
1305 m_width = w;
1306 m_height = h;
1307 m_depth = d;
1308 m_size = imgsize;
1309 m_pixels = new unsigned char[imgsize];
1310 memcpy(m_pixels, pixels, imgsize);
1311 }
1312
1313 ///////////////////////////////////////////////////////////////////////////////
1314 // free surface memory
clear()1315 void CSurface::clear()
1316 {
1317 if (m_pixels != NULL)
1318 {
1319 delete [] m_pixels;
1320 m_pixels = NULL;
1321 }
1322 }
1323