OSDN Git Service

テキストを日本語化。
[marathon/Aorta.git] / imagdds.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        imagdds.cpp
3 // Purpose:     wxImage DDS handler
4 // Author:      Gregory Smith
5 // Copyright:   (c) Gregory Smith
6 // Licence:     wxWindows license
7 /////////////////////////////////////////////////////////////////////////////
8
9 // currently this file has some Aleph One specializations; some formats are
10 // ignored or treated differently...caveat emptor
11
12 #include "imagdds.h"
13
14 #include <wx/wxprec.h>
15 #ifndef WX_PRECOMP
16 #include <wx/wx.h>
17 #endif
18
19 #include <squish.h>
20
21 #include "Filter.h"
22 #include "FloatImage.h"
23
24 #include "math.h"
25 #include <algorithm>
26 #include <memory>
27 #include <vector>
28 using namespace std;
29
30 enum InternalFormat {
31         Format_None,
32         Format_DXT1,
33         Format_DXT3,
34         Format_DXT5,
35         Format_RGB,
36         Format_RGBA
37 };
38
39 #define MAKE_FOURCC(a,b,c,d) (((wxUint32(d) << 24) | (wxUint32)(c) << 16) | ((wxUint32)(b) << 8) | (wxUint32)(a))
40
41 static inline int NextPowerOfTwo(int n)
42 {
43         int p = 1;
44         while(p < n) {p <<= 1;}
45         return p;
46 }
47
48 bool wxDDSHandler::ReadHeader(wxInputStream& stream, DDSURFACEDESC2 &ddsd)
49 {
50     // try to read the whole thing, then swap it
51     stream.Read(&ddsd, sizeof(ddsd));
52     if (stream.LastRead() != sizeof(ddsd)) return FALSE;
53     
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);
61     
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);
66     
67     ddsd.ddsCaps.dwCaps1 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps1);
68     ddsd.ddsCaps.dwCaps2 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps2);
69     
70     return TRUE;
71     
72 }
73
74 bool wxDDSHandler::WriteHeader(wxOutputStream &stream, DDSURFACEDESC2 &ddsd)
75 {
76     wxUint32 dwMagic = (wxINT32_SWAP_ON_BE(MAKE_FOURCC('D', 'D', 'S', ' ')));
77     stream.Write(&dwMagic, sizeof(dwMagic));
78
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);
86     
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);
91     
92     ddsd.ddsCaps.dwCaps1 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps1);
93     ddsd.ddsCaps.dwCaps2 = wxINT32_SWAP_ON_BE(ddsd.ddsCaps.dwCaps2);
94
95     stream.Write(&ddsd, sizeof(ddsd));
96     return TRUE;
97 }
98
99 bool wxDDSHandler::DoCanRead(wxInputStream& stream)
100 {
101     wxUint32 dwMagic;
102     stream.Read(&dwMagic, sizeof(dwMagic));
103     if (stream.LastRead() != sizeof(dwMagic)) return FALSE;
104     
105     if (wxINT32_SWAP_ON_BE(dwMagic) != MAKE_FOURCC('D', 'D', 'S', ' ')) return FALSE;
106
107     DDSURFACEDESC2 ddsd;
108     if (!ReadHeader(stream, ddsd)) return FALSE;
109
110     // validate the sizes
111     if (ddsd.dwSize != 124) return FALSE;
112     if (ddsd.ddpfPixelFormat.dwSize != 32) return FALSE;
113
114     if (ddsd.ddsCaps.dwCaps2 & DDSCAPS2_VOLUME) return FALSE;
115     if (ddsd.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP) return FALSE;
116     
117     if (ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) {
118         return (ddsd.ddpfPixelFormat.dwRGBBitCount == 24 || ddsd.ddpfPixelFormat.dwRGBBitCount == 32);
119     }
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')))
126         return TRUE;
127     else
128         return FALSE;
129     
130 }
131
132 static void UnpremultiplyAlpha(wxImage *image)
133 {
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);
140
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));
144
145             image->SetRGB(x, y, (unsigned char) red, (unsigned char) green, (unsigned char) blue);
146         }
147     }
148 }
149
150 bool wxDDSHandler::LoadFile(wxImage *image, wxInputStream& stream, bool verbose, int index)
151 {
152     wxUint32 dwMagic;
153     stream.Read(&dwMagic, sizeof(dwMagic));
154     if (stream.LastRead() != sizeof(dwMagic)) return FALSE;
155
156     if (wxINT32_SWAP_ON_BE(dwMagic) != MAKE_FOURCC('D', 'D', 'S', ' ')) return FALSE;
157     
158     DDSURFACEDESC2 ddsd;
159     if (!ReadHeader(stream, ddsd)) return FALSE;
160     
161     bool unpremultiplyAlpha = false;
162     
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;
171         }
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;
177         }
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;
185         }
186     }
187     if (internalFormat == Format_None) return FALSE;
188
189     int width = ddsd.dwWidth;
190     int height = ddsd.dwHeight;
191
192     if (internalFormat == Format_RGB || internalFormat == Format_RGBA) {
193         int pitch;
194         if (ddsd.dwFlags & DDSD_PITCH) {
195             pitch = ddsd.dwPitchOrLinearSize;
196         } else if (ddsd.dwFlags & DDSD_LINEARSIZE) {
197             pitch = ddsd.dwPitchOrLinearSize / ddsd.dwHeight;
198         } else {
199             pitch = ((internalFormat == Format_RGB) ? 3 : 4) * width;
200         }
201
202         if (pitch != ((internalFormat == Format_RGB) ? 3 : 4) * width)
203         {
204             fprintf(stderr, "we don't know how to do weird pitch\n");
205             return FALSE;
206         }
207
208         image->Create(width, height);
209         if (internalFormat == Format_RGBA) 
210             image->InitAlpha();
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) {
215                     return FALSE;
216                 }
217                 unsigned char g = stream.GetC();
218                 if (stream.LastRead() != 1) {
219                     return FALSE;
220                 }
221                 unsigned char r = stream.GetC();
222                 if (stream.LastRead() != 1) {
223                     return FALSE;
224                 }
225
226                 image->SetRGB(x, y, r, g, b);
227
228                 if (internalFormat == Format_RGBA)
229                 {
230                     image->SetAlpha(x, y, stream.GetC());
231                     if (stream.LastRead() != 1) {
232                         return FALSE;
233                     }
234                 }
235             }
236         }
237     
238         return TRUE;
239     }
240     
241     int bpp = (internalFormat == Format_DXT1) ? 4 : 8;
242     int potWidth = NextPowerOfTwo(width);
243     int potHeight = NextPowerOfTwo(height);
244
245     int compressedBufferSize = (potWidth * potHeight * bpp) / 8;
246
247     vector<unsigned char> compressedBuffer(width * height * bpp / 8);
248     stream.Read(&compressedBuffer.front(), compressedBuffer.size());
249     if (stream.LastRead() != compressedBuffer.size()) {
250         return FALSE;
251     }
252
253     image->Create(width, height);
254     vector<unsigned char> uncompressedBuffer(width * height * 4);
255     int flags;
256     if (internalFormat == Format_DXT1) {
257         flags = squish::kDxt1;
258     } else {
259         if (internalFormat == Format_DXT3) {
260             flags = squish::kDxt3;
261         } else {
262             flags = squish::kDxt5;
263         }
264         image->InitAlpha();
265     }
266           
267     squish::DecompressImage(&uncompressedBuffer.front(), width, height, &compressedBuffer.front(), flags);
268     
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];
276             }
277         }
278         
279     }
280
281     if (unpremultiplyAlpha)
282         UnpremultiplyAlpha(image);
283
284     return TRUE;
285 }
286
287 static void PremultiplyAlpha(wxImage *image)
288 {
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);
294             
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;
298             
299             image->SetRGB(x, y, (unsigned char) red, (unsigned char) green, (unsigned char) blue);
300         }    
301     }
302 }
303
304 bool wxDDSHandler::SaveFile(wxImage *image, wxOutputStream& stream, bool verbose)
305 {
306
307     wxBusyCursor getBusy;
308
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);
315
316     if (compress)  {
317         if ((image->GetHeight() & 3) || (image->GetWidth() & 3)) {
318             image->Rescale((image->GetWidth() + 3) & ~3, (image->GetHeight() + 3) & ~3);
319         }
320     }
321     
322     DDSURFACEDESC2 ddsd;
323     memset(&ddsd, 0, sizeof(ddsd));
324     ddsd.dwSize = 124;
325     ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
326     ddsd.dwHeight = image->GetHeight();
327     ddsd.dwWidth = image->GetWidth();
328     if (compress) {
329         ddsd.dwFlags |= DDSD_LINEARSIZE;
330         if (image->HasAlpha()) {
331             ddsd.dwPitchOrLinearSize = image->GetWidth() / 4 * image->GetHeight() / 4 * 16;
332             
333             ddsd.ddpfPixelFormat.dwFourCC = MAKE_FOURCC('D', 'X', 'T', '5');
334         } else {
335             ddsd.dwPitchOrLinearSize = image->GetWidth() / 4 * image->GetHeight() / 4 * 8;
336             ddsd.ddpfPixelFormat.dwFourCC = MAKE_FOURCC('D', 'X', 'T', '1');
337         }
338     } else {
339         ddsd.dwFlags |= DDSD_PITCH;
340         if (image->HasAlpha()) {
341             ddsd.dwPitchOrLinearSize = image->GetWidth() * 4;
342         } else {
343             ddsd.dwPitchOrLinearSize = image->GetWidth() * 3;
344         }
345     }
346     
347     int mipmap_count;
348     
349     if (mipmap) {
350             mipmap_count = ddsd.dwMipMapCount = NumMipmaps(*image);
351             ddsd.dwFlags |= DDSD_MIPMAPCOUNT;
352     }
353     
354     ddsd.ddpfPixelFormat.dwSize = 32;
355     if (compress) {
356         ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
357     } else {
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())
363         {
364             ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
365             ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = wxINT32_SWAP_ON_BE(0xff000000);
366             ddsd.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS;
367         } else {
368             ddsd.ddpfPixelFormat.dwRGBBitCount = 24;
369         }
370     }
371     
372     ddsd.ddsCaps.dwCaps1 = DDSCAPS_TEXTURE;
373     if (mipmap) ddsd.ddsCaps.dwCaps1 |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
374     
375     WriteHeader(stream, ddsd);
376     
377     if (premultiply) 
378         PremultiplyAlpha(image);
379
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);
383
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);
387
388     wxImage minImage = *image;
389     
390     for (int level = 0; level < ((mipmap) ? mipmap_count : 1); level++) {
391         if (level) minImage = Minify(minImage, filter, wrap_mode);
392         if (compress) {
393             if (image->HasAlpha()) {
394                 WriteDXT5(minImage, stream);
395             } else {
396                 WriteDXT1(minImage, stream);
397             }
398         } else {
399             WriteRGBA(minImage, stream);
400         }
401     }
402 }
403
404 int wxDDSHandler::NumMipmaps(const wxImage &image)
405 {
406     return (1 + (int) floor(log(std::max(image.GetWidth(), image.GetHeight())) / log(2)));
407 }
408
409 static vector<unsigned char> BuildRGBAImage(const wxImage& image)
410 {
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);
419             else
420                 buffer[(x + y * image.GetWidth()) * 4 + 3] = 0xff;
421         }
422     }
423
424     return buffer;
425 }
426
427
428 void wxDDSHandler::WriteDXT1(const wxImage& image, wxOutputStream& stream)
429 {
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);
433
434     // skip the alpha blocks, and just write the colors
435     unsigned int i = 0;
436     while (i <= compressedBuffer.size() - 16)
437     {
438         i += 8;
439         stream.Write(&compressedBuffer[i], 8);
440         i += 8;
441     }
442 }
443
444
445 void wxDDSHandler::WriteDXT5(const wxImage& image, wxOutputStream& stream)
446 {
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());
450 }
451
452 void wxDDSHandler::WriteRGBA(const wxImage& image, wxOutputStream& stream)
453 {
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())
460             {
461                 stream.PutC(image.GetAlpha(x, y));
462             }
463         }
464     }   
465 }
466
467 wxImage wxDDSHandler::Minify(wxImage &image, long filter, long wrap_mode)
468 {
469     if (image.GetWidth() == 1 && image.GetHeight() == 1) return image;
470
471     FloatImage f;
472
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);
481             } else {
482                 f.setPixel(1.0f, x, y, 3);
483             }
484         }
485     }
486
487     // convert channels 0 through 2 (R through B) to linear space
488     f.toLinear(0, 2);
489
490     wxImage minifiedImage;
491
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;
497
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));
504     } else {
505             minif.reset(f.fastDownSample());
506     }
507     
508     minif->toGamma(0, 2);
509     minifiedImage.Create(minif->width(), minif->height());
510     
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);
517         }
518     }
519
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));
525             }
526         }
527     }
528
529     return minifiedImage;
530 }
531
532
533