1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxImage DDS handler
4 // Author: Gregory Smith
5 // Copyright: (c) Gregory Smith
6 // Licence: wxWindows license
7 /////////////////////////////////////////////////////////////////////////////
9 // currently this file has some Aleph One specializations; some formats are
10 // ignored or treated differently...caveat emptor
14 #include <wx/wxprec.h>
22 #include "FloatImage.h"
39 #define MAKE_FOURCC(a,b,c,d) (((wxUint32(d) << 24) | (wxUint32)(c) << 16) | ((wxUint32)(b) << 8) | (wxUint32)(a))
41 static inline int NextPowerOfTwo(int n)
44 while(p < n) {p <<= 1;}
48 bool wxDDSHandler::ReadHeader(wxInputStream& stream, DDSURFACEDESC2 &ddsd)
50 // try to read the whole thing, then swap it
51 stream.Read(&ddsd, sizeof(ddsd));
52 if (stream.LastRead() != sizeof(ddsd)) return FALSE;
54 ddsd.dwSize = wxINT32_SWAP_ON_BE(ddsd.dwSize);
55 ddsd.dwFlags = wxINT32_SWAP_ON_BE(ddsd.dwFlags);
56 ddsd.dwHeight = wxINT32_SWAP_ON_BE(ddsd.dwHeight);
57 ddsd.dwWidth = wxINT32_SWAP_ON_BE(ddsd.dwWidth);
58 ddsd.dwPitchOrLinearSize = wxINT32_SWAP_ON_BE(ddsd.dwPitchOrLinearSize);
59 ddsd.dwDepth = wxINT32_SWAP_ON_BE(ddsd.dwDepth);
60 ddsd.dwMipMapCount = wxINT32_SWAP_ON_BE(ddsd.dwMipMapCount);
62 ddsd.ddpfPixelFormat.dwSize = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwSize);
63 ddsd.ddpfPixelFormat.dwFlags = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwFlags);
64 ddsd.ddpfPixelFormat.dwFourCC = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwFourCC);
65 ddsd.ddpfPixelFormat.dwRGBBitCount = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwRGBBitCount);
67 ddsd.ddsCaps.dwCaps1 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps1);
68 ddsd.ddsCaps.dwCaps2 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps2);
74 bool wxDDSHandler::WriteHeader(wxOutputStream &stream, DDSURFACEDESC2 &ddsd)
76 wxUint32 dwMagic = (wxINT32_SWAP_ON_BE(MAKE_FOURCC('D', 'D', 'S', ' ')));
77 stream.Write(&dwMagic, sizeof(dwMagic));
79 ddsd.dwSize = wxINT32_SWAP_ON_BE(ddsd.dwSize);
80 ddsd.dwFlags = wxINT32_SWAP_ON_BE(ddsd.dwFlags);
81 ddsd.dwHeight = wxINT32_SWAP_ON_BE(ddsd.dwHeight);
82 ddsd.dwWidth = wxINT32_SWAP_ON_BE(ddsd.dwWidth);
83 ddsd.dwPitchOrLinearSize = wxINT32_SWAP_ON_BE(ddsd.dwPitchOrLinearSize);
84 ddsd.dwDepth = wxINT32_SWAP_ON_BE(ddsd.dwDepth);
85 ddsd.dwMipMapCount = wxINT32_SWAP_ON_BE(ddsd.dwMipMapCount);
87 ddsd.ddpfPixelFormat.dwSize = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwSize);
88 ddsd.ddpfPixelFormat.dwFlags = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwFlags);
89 ddsd.ddpfPixelFormat.dwFourCC = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwFourCC);
90 ddsd.ddpfPixelFormat.dwRGBBitCount = wxINT32_SWAP_ON_BE(ddsd.ddpfPixelFormat.dwRGBBitCount);
92 ddsd.ddsCaps.dwCaps1 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps1);
93 ddsd.ddsCaps.dwCaps2 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps2);
95 stream.Write(&ddsd, sizeof(ddsd));
99 bool wxDDSHandler::DoCanRead(wxInputStream& stream)
102 stream.Read(&dwMagic, sizeof(dwMagic));
103 if (stream.LastRead() != sizeof(dwMagic)) return FALSE;
105 if (wxINT32_SWAP_ON_BE(dwMagic) != MAKE_FOURCC('D', 'D', 'S', ' ')) return FALSE;
108 if (!ReadHeader(stream, ddsd)) return FALSE;
110 // validate the sizes
111 if (ddsd.dwSize != 124) return FALSE;
112 if (ddsd.ddpfPixelFormat.dwSize != 32) return FALSE;
114 if (ddsd.ddsCaps.dwCaps2 & DDSCAPS2_VOLUME) return FALSE;
115 if (ddsd.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP) return FALSE;
117 if (ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) {
118 return (ddsd.ddpfPixelFormat.dwRGBBitCount == 24 || ddsd.ddpfPixelFormat.dwRGBBitCount == 32);
120 else if ((ddsd.ddpfPixelFormat.dwFlags & DDPF_FOURCC) &&
121 (ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '1') ||
122 ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '2') ||
123 ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '3') ||
124 ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '4') ||
125 ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '5')))
132 static void UnpremultiplyAlpha(wxImage *image)
134 for (int x = 0; x < image->GetWidth(); x++) {
135 for (int y = 0; y < image->GetHeight(); y++) {
136 if (image->GetAlpha(x, y) == 0) continue;
137 short red = image->GetRed(x, y);
138 short green = image->GetGreen(x, y);
139 short blue = image->GetBlue(x, y);
141 red = std::min(255, 255 * red / image->GetAlpha(x, y));
142 green = std::min(255, 255 * green / image->GetAlpha(x, y));
143 blue = std::min(255, 255 * blue / image->GetAlpha(x, y));
145 image->SetRGB(x, y, (unsigned char) red, (unsigned char) green, (unsigned char) blue);
150 bool wxDDSHandler::LoadFile(wxImage *image, wxInputStream& stream, bool verbose, int index)
153 stream.Read(&dwMagic, sizeof(dwMagic));
154 if (stream.LastRead() != sizeof(dwMagic)) return FALSE;
156 if (wxINT32_SWAP_ON_BE(dwMagic) != MAKE_FOURCC('D', 'D', 'S', ' ')) return FALSE;
159 if (!ReadHeader(stream, ddsd)) return FALSE;
161 bool unpremultiplyAlpha = false;
163 // just read the first mipmap
164 InternalFormat internalFormat = Format_None;
165 if (ddsd.ddpfPixelFormat.dwFlags & DDPF_FOURCC) {
166 if (ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '1'))
167 internalFormat = Format_DXT1;
168 else if (ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '2')) {
169 unpremultiplyAlpha = true;
170 internalFormat = Format_DXT3;
172 else if (ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '3'))
173 internalFormat = Format_DXT3;
174 else if (ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '4')) {
175 unpremultiplyAlpha = true;
176 internalFormat = Format_DXT5;
178 else if (ddsd.ddpfPixelFormat.dwFourCC == MAKE_FOURCC('D', 'X', 'T', '5'))
179 internalFormat = Format_DXT5;
180 } else if (ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) {
181 if (ddsd.ddpfPixelFormat.dwRGBBitCount == 24) {
182 internalFormat = Format_RGB;
183 } else if (ddsd.ddpfPixelFormat.dwRGBBitCount == 32) {
184 internalFormat = Format_RGBA;
187 if (internalFormat == Format_None) return FALSE;
189 int width = ddsd.dwWidth;
190 int height = ddsd.dwHeight;
192 if (internalFormat == Format_RGB || internalFormat == Format_RGBA) {
194 if (ddsd.dwFlags & DDSD_PITCH) {
195 pitch = ddsd.dwPitchOrLinearSize;
196 } else if (ddsd.dwFlags & DDSD_LINEARSIZE) {
197 pitch = ddsd.dwPitchOrLinearSize / ddsd.dwHeight;
199 pitch = ((internalFormat == Format_RGB) ? 3 : 4) * width;
202 if (pitch != ((internalFormat == Format_RGB) ? 3 : 4) * width)
204 fprintf(stderr, "we don't know how to do weird pitch\n");
208 image->Create(width, height);
209 if (internalFormat == Format_RGBA)
211 for (int y = 0; y < height; y++) {
212 for (int x = 0; x < width; x++) {
213 unsigned char b = stream.GetC();
214 if (stream.LastRead() != 1) {
217 unsigned char g = stream.GetC();
218 if (stream.LastRead() != 1) {
221 unsigned char r = stream.GetC();
222 if (stream.LastRead() != 1) {
226 image->SetRGB(x, y, r, g, b);
228 if (internalFormat == Format_RGBA)
230 image->SetAlpha(x, y, stream.GetC());
231 if (stream.LastRead() != 1) {
241 int bpp = (internalFormat == Format_DXT1) ? 4 : 8;
242 int potWidth = NextPowerOfTwo(width);
243 int potHeight = NextPowerOfTwo(height);
245 int compressedBufferSize = (potWidth * potHeight * bpp) / 8;
247 vector<unsigned char> compressedBuffer(width * height * bpp / 8);
248 stream.Read(&compressedBuffer.front(), compressedBuffer.size());
249 if (stream.LastRead() != compressedBuffer.size()) {
253 image->Create(width, height);
254 vector<unsigned char> uncompressedBuffer(width * height * 4);
256 if (internalFormat == Format_DXT1) {
257 flags = squish::kDxt1;
259 if (internalFormat == Format_DXT3) {
260 flags = squish::kDxt3;
262 flags = squish::kDxt5;
267 squish::DecompressImage(&uncompressedBuffer.front(), width, height, &compressedBuffer.front(), flags);
269 for (int x = 0; x < width; x++) {
270 for (int y = 0; y < height; y++) {
271 image->GetData()[(x + y * width) * 3 + 0] = uncompressedBuffer[(x + y * width) * 4 + 0];
272 image->GetData()[(x + y * width) * 3 + 1] = uncompressedBuffer[(x + y * width) * 4 + 1];
273 image->GetData()[(x + y * width) * 3 + 2] = uncompressedBuffer[(x + y * width) * 4 + 2];
274 if (image->HasAlpha()) {
275 image->GetAlpha()[(x + y * width)] = uncompressedBuffer[(x + y * width) * 4 + 3];
281 if (unpremultiplyAlpha)
282 UnpremultiplyAlpha(image);
287 static void PremultiplyAlpha(wxImage *image)
289 for (int x = 0; x < image->GetWidth(); x++) {
290 for (int y = 0; y < image->GetHeight(); y++) {
291 short red = image->GetRed(x, y);
292 short green = image->GetGreen(x, y);
293 short blue = image->GetBlue(x, y);
295 red = (image->GetAlpha(x, y) * red + 127) / 255;
296 green = (image->GetAlpha(x, y) * green + 127) / 255;
297 blue = (image->GetAlpha(x, y) * blue + 127) / 255;
299 image->SetRGB(x, y, (unsigned char) red, (unsigned char) green, (unsigned char) blue);
304 bool wxDDSHandler::SaveFile(wxImage *image, wxOutputStream& stream, bool verbose)
307 wxBusyCursor getBusy;
309 bool mipmap = image->HasOption(wxIMAGE_OPTION_DDS_USE_MIPMAPS) &&
310 image->GetOptionInt(wxIMAGE_OPTION_DDS_USE_MIPMAPS);
311 bool compress = image->HasOption(wxIMAGE_OPTION_DDS_COMPRESS) &&
312 image->GetOptionInt(wxIMAGE_OPTION_DDS_COMPRESS);
313 bool premultiply = image->HasOption(wxIMAGE_OPTION_DDS_PREMULTIPLY_ALPHA) &&
314 image->GetOptionInt(wxIMAGE_OPTION_DDS_PREMULTIPLY_ALPHA);
317 if ((image->GetHeight() & 3) || (image->GetWidth() & 3)) {
318 image->Rescale((image->GetWidth() + 3) & ~3, (image->GetHeight() + 3) & ~3);
323 memset(&ddsd, 0, sizeof(ddsd));
325 ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
326 ddsd.dwHeight = image->GetHeight();
327 ddsd.dwWidth = image->GetWidth();
329 ddsd.dwFlags |= DDSD_LINEARSIZE;
330 if (image->HasAlpha()) {
331 ddsd.dwPitchOrLinearSize = image->GetWidth() / 4 * image->GetHeight() / 4 * 16;
333 ddsd.ddpfPixelFormat.dwFourCC = MAKE_FOURCC('D', 'X', 'T', '5');
335 ddsd.dwPitchOrLinearSize = image->GetWidth() / 4 * image->GetHeight() / 4 * 8;
336 ddsd.ddpfPixelFormat.dwFourCC = MAKE_FOURCC('D', 'X', 'T', '1');
339 ddsd.dwFlags |= DDSD_PITCH;
340 if (image->HasAlpha()) {
341 ddsd.dwPitchOrLinearSize = image->GetWidth() * 4;
343 ddsd.dwPitchOrLinearSize = image->GetWidth() * 3;
350 mipmap_count = ddsd.dwMipMapCount = NumMipmaps(*image);
351 ddsd.dwFlags |= DDSD_MIPMAPCOUNT;
354 ddsd.ddpfPixelFormat.dwSize = 32;
356 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
358 ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
359 ddsd.ddpfPixelFormat.dwRBitMask = wxINT32_SWAP_ON_BE(0x00ff0000);
360 ddsd.ddpfPixelFormat.dwGBitMask = wxINT32_SWAP_ON_BE(0x0000ff00);
361 ddsd.ddpfPixelFormat.dwBBitMask = wxINT32_SWAP_ON_BE(0x000000ff);
362 if (image->HasAlpha())
364 ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
365 ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = wxINT32_SWAP_ON_BE(0xff000000);
366 ddsd.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS;
368 ddsd.ddpfPixelFormat.dwRGBBitCount = 24;
372 ddsd.ddsCaps.dwCaps1 = DDSCAPS_TEXTURE;
373 if (mipmap) ddsd.ddsCaps.dwCaps1 |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
375 WriteHeader(stream, ddsd);
378 PremultiplyAlpha(image);
380 long filter = wxIMAGE_OPTION_DDS_FILTER_BOX;
381 if (image->HasOption(wxIMAGE_OPTION_DDS_MIPMAP_FILTER))
382 filter = image->GetOptionInt(wxIMAGE_OPTION_DDS_MIPMAP_FILTER);
384 long wrap_mode = wxIMAGE_OPTION_DDS_WRAP_CLAMP;
385 if (image->HasOption(wxIMAGE_OPTION_DDS_MIPMAP_WRAP_MODE))
386 wrap_mode = image->GetOptionInt(wxIMAGE_OPTION_DDS_MIPMAP_WRAP_MODE);
388 wxImage minImage = *image;
390 for (int level = 0; level < ((mipmap) ? mipmap_count : 1); level++) {
391 if (level) minImage = Minify(minImage, filter, wrap_mode);
393 if (image->HasAlpha()) {
394 WriteDXT5(minImage, stream);
396 WriteDXT1(minImage, stream);
399 WriteRGBA(minImage, stream);
404 int wxDDSHandler::NumMipmaps(const wxImage &image)
406 return (1 + (int) floor(log(std::max(image.GetWidth(), image.GetHeight())) / log(2)));
409 static vector<unsigned char> BuildRGBAImage(const wxImage& image)
411 vector<unsigned char> buffer(image.GetWidth() * image.GetHeight() * 4);
412 for (int x = 0; x < image.GetWidth(); x++) {
413 for (int y = 0; y < image.GetHeight(); y++) {
414 buffer[(x + y * image.GetWidth()) * 4 + 0] = image.GetRed(x, y);
415 buffer[(x + y * image.GetWidth()) * 4 + 1] = image.GetGreen(x, y);
416 buffer[(x + y * image.GetWidth()) * 4 + 2] = image.GetBlue(x, y);
417 if (image.HasAlpha())
418 buffer[(x + y * image.GetWidth()) * 4 + 3] = image.GetAlpha(x, y);
420 buffer[(x + y * image.GetWidth()) * 4 + 3] = 0xff;
428 void wxDDSHandler::WriteDXT1(const wxImage& image, wxOutputStream& stream)
430 // use DXT3 to force 4 colors
431 vector<unsigned char> compressedBuffer(squish::GetStorageRequirements(image.GetWidth(), image.GetHeight(), squish::kDxt3));
432 squish::CompressImage(&BuildRGBAImage(image).front(), image.GetWidth(), image.GetHeight(), &compressedBuffer.front(), squish::kDxt3 | squish::kColourIterativeClusterFit);
434 // skip the alpha blocks, and just write the colors
436 while (i <= compressedBuffer.size() - 16)
439 stream.Write(&compressedBuffer[i], 8);
445 void wxDDSHandler::WriteDXT5(const wxImage& image, wxOutputStream& stream)
447 vector<unsigned char> compressedBuffer(squish::GetStorageRequirements(image.GetWidth(), image.GetHeight(), squish::kDxt5));
448 squish::CompressImage(&BuildRGBAImage(image).front(), image.GetWidth(), image.GetHeight(), &compressedBuffer.front(), squish::kDxt5);
449 stream.Write(&compressedBuffer.front(), compressedBuffer.size());
452 void wxDDSHandler::WriteRGBA(const wxImage& image, wxOutputStream& stream)
454 for (int y = 0; y < image.GetHeight(); y++) {
455 for (int x = 0; x < image.GetWidth(); x++) {
456 stream.PutC(image.GetBlue(x, y));
457 stream.PutC(image.GetGreen(x, y));
458 stream.PutC(image.GetRed(x, y));
459 if (image.HasAlpha())
461 stream.PutC(image.GetAlpha(x, y));
467 wxImage wxDDSHandler::Minify(wxImage &image, long filter, long wrap_mode)
469 if (image.GetWidth() == 1 && image.GetHeight() == 1) return image;
473 f.allocate(4, image.GetWidth(), image.GetHeight());
474 for (int x = 0; x < image.GetWidth(); ++x) {
475 for (int y = 0; y < image.GetHeight(); ++y) {
476 f.setPixel(image.GetRed(x, y) / 255.0f, x, y, 0);
477 f.setPixel(image.GetGreen(x, y) / 255.0f, x, y, 1);
478 f.setPixel(image.GetBlue(x, y) / 255.0f, x, y, 2);
479 if (image.HasAlpha()) {
480 f.setPixel(image.GetAlpha(x, y) / 255.0f, x, y, 3);
482 f.setPixel(1.0f, x, y, 3);
487 // convert channels 0 through 2 (R through B) to linear space
490 wxImage minifiedImage;
492 FloatImage::WrapMode wm = FloatImage::WrapMode_Clamp;
493 if (wrap_mode == wxIMAGE_OPTION_DDS_WRAP_REPEAT)
494 wm = FloatImage::WrapMode_Repeat;
495 else if (wrap_mode == wxIMAGE_OPTION_DDS_WRAP_MIRROR)
496 wm = FloatImage::WrapMode_Mirror;
498 std::auto_ptr<FloatImage> minif;
499 if (filter == wxIMAGE_OPTION_DDS_FILTER_TRIANGLE) {
500 TriangleFilter filter;
501 minif.reset(f.downSample(TriangleFilter(), wm));
502 } else if (filter == wxIMAGE_OPTION_DDS_FILTER_KAISER) {
503 minif.reset(f.downSample(KaiserFilter(3), wm));
505 minif.reset(f.fastDownSample());
508 minif->toGamma(0, 2);
509 minifiedImage.Create(minif->width(), minif->height());
511 for (int x = 0; x < minif->width(); ++x) {
512 for (int y = 0; y < minif->height(); ++y) {
513 unsigned int r = clamp((int) (minif->pixel(x, y, 0) * 255.0f), 0, 255);
514 unsigned int g = clamp((int) (minif->pixel(x, y, 1) * 255.0f), 0, 255);
515 unsigned int b = clamp((int) (minif->pixel(x, y, 2) * 255.0f), 0, 255);
516 minifiedImage.SetRGB(x, y, r, g, b);
520 if (image.HasAlpha()) {
521 minifiedImage.InitAlpha();
522 for (int x = 0; x < minif->width(); ++x) {
523 for (int y = 0; y < minif->height(); ++y) {
524 minifiedImage.SetAlpha(x, y, clamp((int) (minif->pixel(x, y, 3) * 255.0f), 0, 255));
529 return minifiedImage;