1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                            DDDD   DDDD   SSSSS                              %
7%                            D   D  D   D  SS                                 %
8%                            D   D  D   D   SSS                               %
9%                            D   D  D   D     SS                              %
10%                            DDDD   DDDD   SSSSS                              %
11%                                                                             %
12%                                                                             %
13%           Read/Write Microsoft Direct Draw Surface Image Format             %
14%                                                                             %
15%                              Software Design                                %
16%                             Bianca van Schaik                               %
17%                                March 2008                                   %
18%                               Dirk Lemstra                                  %
19%                              September 2013                                 %
20%                                                                             %
21%                                                                             %
22%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
23%  dedicated to making software imaging solutions freely available.           %
24%                                                                             %
25%  You may not use this file except in compliance with the License.  You may  %
26%  obtain a copy of the License at                                            %
27%                                                                             %
28%    http://www.imagemagick.org/script/license.php                            %
29%                                                                             %
30%  Unless required by applicable law or agreed to in writing, software        %
31%  distributed under the License is distributed on an "AS IS" BASIS,          %
32%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
33%  See the License for the specific language governing permissions and        %
34%  limitations under the License.                                             %
35%                                                                             %
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37%
38%
39*/
40
41/*
42  Include declarations.
43*/
44#include "MagickCore/studio.h"
45#include "MagickCore/attribute.h"
46#include "MagickCore/blob.h"
47#include "MagickCore/blob-private.h"
48#include "MagickCore/cache.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/colorspace-private.h"
51#include "MagickCore/exception.h"
52#include "MagickCore/exception-private.h"
53#include "MagickCore/image.h"
54#include "MagickCore/image-private.h"
55#include "MagickCore/list.h"
56#include "MagickCore/log.h"
57#include "MagickCore/magick.h"
58#include "MagickCore/memory_.h"
59#include "MagickCore/monitor.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/option.h"
62#include "MagickCore/pixel-accessor.h"
63#include "MagickCore/profile.h"
64#include "MagickCore/quantum.h"
65#include "MagickCore/quantum-private.h"
66#include "MagickCore/resource_.h"
67#include "MagickCore/static.h"
68#include "MagickCore/string_.h"
69#include "MagickCore/string-private.h"
70#include "MagickCore/module.h"
71#include "MagickCore/transform.h"
72
73/*
74  Definitions
75*/
76#define DDSD_CAPS         0x00000001
77#define DDSD_HEIGHT       0x00000002
78#define DDSD_WIDTH        0x00000004
79#define DDSD_PITCH        0x00000008
80#define DDSD_PIXELFORMAT  0x00001000
81#define DDSD_MIPMAPCOUNT  0x00020000
82#define DDSD_LINEARSIZE   0x00080000
83#define DDSD_DEPTH        0x00800000
84
85#define DDPF_ALPHAPIXELS  0x00000001
86#define DDPF_FOURCC       0x00000004
87#define DDPF_RGB          0x00000040
88#define DDPF_LUMINANCE    0x00020000
89
90#define FOURCC_DXT1       0x31545844
91#define FOURCC_DXT3       0x33545844
92#define FOURCC_DXT5       0x35545844
93
94#define DDSCAPS_COMPLEX   0x00000008
95#define DDSCAPS_TEXTURE   0x00001000
96#define DDSCAPS_MIPMAP    0x00400000
97
98#define DDSCAPS2_CUBEMAP  0x00000200
99#define DDSCAPS2_CUBEMAP_POSITIVEX  0x00000400
100#define DDSCAPS2_CUBEMAP_NEGATIVEX  0x00000800
101#define DDSCAPS2_CUBEMAP_POSITIVEY  0x00001000
102#define DDSCAPS2_CUBEMAP_NEGATIVEY  0x00002000
103#define DDSCAPS2_CUBEMAP_POSITIVEZ  0x00004000
104#define DDSCAPS2_CUBEMAP_NEGATIVEZ  0x00008000
105#define DDSCAPS2_VOLUME   0x00200000
106
107#ifndef SIZE_MAX
108#define SIZE_MAX ((size_t) -1)
109#endif
110
111/*
112  Structure declarations.
113*/
114typedef struct _DDSPixelFormat
115{
116  size_t
117    flags,
118    fourcc,
119    rgb_bitcount,
120    r_bitmask,
121    g_bitmask,
122    b_bitmask,
123    alpha_bitmask;
124} DDSPixelFormat;
125
126typedef struct _DDSInfo
127{
128  size_t
129    flags,
130    height,
131    width,
132    pitchOrLinearSize,
133    depth,
134    mipmapcount,
135    ddscaps1,
136    ddscaps2;
137
138  DDSPixelFormat
139    pixelformat;
140} DDSInfo;
141
142typedef struct _DDSColors
143{
144  unsigned char
145    r[4],
146    g[4],
147    b[4],
148    a[4];
149} DDSColors;
150
151typedef struct _DDSVector4
152{
153  float
154    x,
155    y,
156    z,
157    w;
158} DDSVector4;
159
160typedef struct _DDSVector3
161{
162  float
163    x,
164    y,
165    z;
166} DDSVector3;
167
168typedef struct _DDSSourceBlock
169{
170  unsigned char
171    start,
172    end,
173    error;
174} DDSSourceBlock;
175
176typedef struct _DDSSingleColourLookup
177{
178  DDSSourceBlock sources[2];
179} DDSSingleColourLookup;
180
181typedef MagickBooleanType
182  DDSDecoder(Image *, DDSInfo *, ExceptionInfo *);
183
184static const DDSSingleColourLookup DDSLookup_5_4[] =
185{
186  { { { 0, 0, 0 }, { 0, 0, 0 } } },
187  { { { 0, 0, 1 }, { 0, 1, 1 } } },
188  { { { 0, 0, 2 }, { 0, 1, 0 } } },
189  { { { 0, 0, 3 }, { 0, 1, 1 } } },
190  { { { 0, 0, 4 }, { 0, 2, 1 } } },
191  { { { 1, 0, 3 }, { 0, 2, 0 } } },
192  { { { 1, 0, 2 }, { 0, 2, 1 } } },
193  { { { 1, 0, 1 }, { 0, 3, 1 } } },
194  { { { 1, 0, 0 }, { 0, 3, 0 } } },
195  { { { 1, 0, 1 }, { 1, 2, 1 } } },
196  { { { 1, 0, 2 }, { 1, 2, 0 } } },
197  { { { 1, 0, 3 }, { 0, 4, 0 } } },
198  { { { 1, 0, 4 }, { 0, 5, 1 } } },
199  { { { 2, 0, 3 }, { 0, 5, 0 } } },
200  { { { 2, 0, 2 }, { 0, 5, 1 } } },
201  { { { 2, 0, 1 }, { 0, 6, 1 } } },
202  { { { 2, 0, 0 }, { 0, 6, 0 } } },
203  { { { 2, 0, 1 }, { 2, 3, 1 } } },
204  { { { 2, 0, 2 }, { 2, 3, 0 } } },
205  { { { 2, 0, 3 }, { 0, 7, 0 } } },
206  { { { 2, 0, 4 }, { 1, 6, 1 } } },
207  { { { 3, 0, 3 }, { 1, 6, 0 } } },
208  { { { 3, 0, 2 }, { 0, 8, 0 } } },
209  { { { 3, 0, 1 }, { 0, 9, 1 } } },
210  { { { 3, 0, 0 }, { 0, 9, 0 } } },
211  { { { 3, 0, 1 }, { 0, 9, 1 } } },
212  { { { 3, 0, 2 }, { 0, 10, 1 } } },
213  { { { 3, 0, 3 }, { 0, 10, 0 } } },
214  { { { 3, 0, 4 }, { 2, 7, 1 } } },
215  { { { 4, 0, 4 }, { 2, 7, 0 } } },
216  { { { 4, 0, 3 }, { 0, 11, 0 } } },
217  { { { 4, 0, 2 }, { 1, 10, 1 } } },
218  { { { 4, 0, 1 }, { 1, 10, 0 } } },
219  { { { 4, 0, 0 }, { 0, 12, 0 } } },
220  { { { 4, 0, 1 }, { 0, 13, 1 } } },
221  { { { 4, 0, 2 }, { 0, 13, 0 } } },
222  { { { 4, 0, 3 }, { 0, 13, 1 } } },
223  { { { 4, 0, 4 }, { 0, 14, 1 } } },
224  { { { 5, 0, 3 }, { 0, 14, 0 } } },
225  { { { 5, 0, 2 }, { 2, 11, 1 } } },
226  { { { 5, 0, 1 }, { 2, 11, 0 } } },
227  { { { 5, 0, 0 }, { 0, 15, 0 } } },
228  { { { 5, 0, 1 }, { 1, 14, 1 } } },
229  { { { 5, 0, 2 }, { 1, 14, 0 } } },
230  { { { 5, 0, 3 }, { 0, 16, 0 } } },
231  { { { 5, 0, 4 }, { 0, 17, 1 } } },
232  { { { 6, 0, 3 }, { 0, 17, 0 } } },
233  { { { 6, 0, 2 }, { 0, 17, 1 } } },
234  { { { 6, 0, 1 }, { 0, 18, 1 } } },
235  { { { 6, 0, 0 }, { 0, 18, 0 } } },
236  { { { 6, 0, 1 }, { 2, 15, 1 } } },
237  { { { 6, 0, 2 }, { 2, 15, 0 } } },
238  { { { 6, 0, 3 }, { 0, 19, 0 } } },
239  { { { 6, 0, 4 }, { 1, 18, 1 } } },
240  { { { 7, 0, 3 }, { 1, 18, 0 } } },
241  { { { 7, 0, 2 }, { 0, 20, 0 } } },
242  { { { 7, 0, 1 }, { 0, 21, 1 } } },
243  { { { 7, 0, 0 }, { 0, 21, 0 } } },
244  { { { 7, 0, 1 }, { 0, 21, 1 } } },
245  { { { 7, 0, 2 }, { 0, 22, 1 } } },
246  { { { 7, 0, 3 }, { 0, 22, 0 } } },
247  { { { 7, 0, 4 }, { 2, 19, 1 } } },
248  { { { 8, 0, 4 }, { 2, 19, 0 } } },
249  { { { 8, 0, 3 }, { 0, 23, 0 } } },
250  { { { 8, 0, 2 }, { 1, 22, 1 } } },
251  { { { 8, 0, 1 }, { 1, 22, 0 } } },
252  { { { 8, 0, 0 }, { 0, 24, 0 } } },
253  { { { 8, 0, 1 }, { 0, 25, 1 } } },
254  { { { 8, 0, 2 }, { 0, 25, 0 } } },
255  { { { 8, 0, 3 }, { 0, 25, 1 } } },
256  { { { 8, 0, 4 }, { 0, 26, 1 } } },
257  { { { 9, 0, 3 }, { 0, 26, 0 } } },
258  { { { 9, 0, 2 }, { 2, 23, 1 } } },
259  { { { 9, 0, 1 }, { 2, 23, 0 } } },
260  { { { 9, 0, 0 }, { 0, 27, 0 } } },
261  { { { 9, 0, 1 }, { 1, 26, 1 } } },
262  { { { 9, 0, 2 }, { 1, 26, 0 } } },
263  { { { 9, 0, 3 }, { 0, 28, 0 } } },
264  { { { 9, 0, 4 }, { 0, 29, 1 } } },
265  { { { 10, 0, 3 }, { 0, 29, 0 } } },
266  { { { 10, 0, 2 }, { 0, 29, 1 } } },
267  { { { 10, 0, 1 }, { 0, 30, 1 } } },
268  { { { 10, 0, 0 }, { 0, 30, 0 } } },
269  { { { 10, 0, 1 }, { 2, 27, 1 } } },
270  { { { 10, 0, 2 }, { 2, 27, 0 } } },
271  { { { 10, 0, 3 }, { 0, 31, 0 } } },
272  { { { 10, 0, 4 }, { 1, 30, 1 } } },
273  { { { 11, 0, 3 }, { 1, 30, 0 } } },
274  { { { 11, 0, 2 }, { 4, 24, 0 } } },
275  { { { 11, 0, 1 }, { 1, 31, 1 } } },
276  { { { 11, 0, 0 }, { 1, 31, 0 } } },
277  { { { 11, 0, 1 }, { 1, 31, 1 } } },
278  { { { 11, 0, 2 }, { 2, 30, 1 } } },
279  { { { 11, 0, 3 }, { 2, 30, 0 } } },
280  { { { 11, 0, 4 }, { 2, 31, 1 } } },
281  { { { 12, 0, 4 }, { 2, 31, 0 } } },
282  { { { 12, 0, 3 }, { 4, 27, 0 } } },
283  { { { 12, 0, 2 }, { 3, 30, 1 } } },
284  { { { 12, 0, 1 }, { 3, 30, 0 } } },
285  { { { 12, 0, 0 }, { 4, 28, 0 } } },
286  { { { 12, 0, 1 }, { 3, 31, 1 } } },
287  { { { 12, 0, 2 }, { 3, 31, 0 } } },
288  { { { 12, 0, 3 }, { 3, 31, 1 } } },
289  { { { 12, 0, 4 }, { 4, 30, 1 } } },
290  { { { 13, 0, 3 }, { 4, 30, 0 } } },
291  { { { 13, 0, 2 }, { 6, 27, 1 } } },
292  { { { 13, 0, 1 }, { 6, 27, 0 } } },
293  { { { 13, 0, 0 }, { 4, 31, 0 } } },
294  { { { 13, 0, 1 }, { 5, 30, 1 } } },
295  { { { 13, 0, 2 }, { 5, 30, 0 } } },
296  { { { 13, 0, 3 }, { 8, 24, 0 } } },
297  { { { 13, 0, 4 }, { 5, 31, 1 } } },
298  { { { 14, 0, 3 }, { 5, 31, 0 } } },
299  { { { 14, 0, 2 }, { 5, 31, 1 } } },
300  { { { 14, 0, 1 }, { 6, 30, 1 } } },
301  { { { 14, 0, 0 }, { 6, 30, 0 } } },
302  { { { 14, 0, 1 }, { 6, 31, 1 } } },
303  { { { 14, 0, 2 }, { 6, 31, 0 } } },
304  { { { 14, 0, 3 }, { 8, 27, 0 } } },
305  { { { 14, 0, 4 }, { 7, 30, 1 } } },
306  { { { 15, 0, 3 }, { 7, 30, 0 } } },
307  { { { 15, 0, 2 }, { 8, 28, 0 } } },
308  { { { 15, 0, 1 }, { 7, 31, 1 } } },
309  { { { 15, 0, 0 }, { 7, 31, 0 } } },
310  { { { 15, 0, 1 }, { 7, 31, 1 } } },
311  { { { 15, 0, 2 }, { 8, 30, 1 } } },
312  { { { 15, 0, 3 }, { 8, 30, 0 } } },
313  { { { 15, 0, 4 }, { 10, 27, 1 } } },
314  { { { 16, 0, 4 }, { 10, 27, 0 } } },
315  { { { 16, 0, 3 }, { 8, 31, 0 } } },
316  { { { 16, 0, 2 }, { 9, 30, 1 } } },
317  { { { 16, 0, 1 }, { 9, 30, 0 } } },
318  { { { 16, 0, 0 }, { 12, 24, 0 } } },
319  { { { 16, 0, 1 }, { 9, 31, 1 } } },
320  { { { 16, 0, 2 }, { 9, 31, 0 } } },
321  { { { 16, 0, 3 }, { 9, 31, 1 } } },
322  { { { 16, 0, 4 }, { 10, 30, 1 } } },
323  { { { 17, 0, 3 }, { 10, 30, 0 } } },
324  { { { 17, 0, 2 }, { 10, 31, 1 } } },
325  { { { 17, 0, 1 }, { 10, 31, 0 } } },
326  { { { 17, 0, 0 }, { 12, 27, 0 } } },
327  { { { 17, 0, 1 }, { 11, 30, 1 } } },
328  { { { 17, 0, 2 }, { 11, 30, 0 } } },
329  { { { 17, 0, 3 }, { 12, 28, 0 } } },
330  { { { 17, 0, 4 }, { 11, 31, 1 } } },
331  { { { 18, 0, 3 }, { 11, 31, 0 } } },
332  { { { 18, 0, 2 }, { 11, 31, 1 } } },
333  { { { 18, 0, 1 }, { 12, 30, 1 } } },
334  { { { 18, 0, 0 }, { 12, 30, 0 } } },
335  { { { 18, 0, 1 }, { 14, 27, 1 } } },
336  { { { 18, 0, 2 }, { 14, 27, 0 } } },
337  { { { 18, 0, 3 }, { 12, 31, 0 } } },
338  { { { 18, 0, 4 }, { 13, 30, 1 } } },
339  { { { 19, 0, 3 }, { 13, 30, 0 } } },
340  { { { 19, 0, 2 }, { 16, 24, 0 } } },
341  { { { 19, 0, 1 }, { 13, 31, 1 } } },
342  { { { 19, 0, 0 }, { 13, 31, 0 } } },
343  { { { 19, 0, 1 }, { 13, 31, 1 } } },
344  { { { 19, 0, 2 }, { 14, 30, 1 } } },
345  { { { 19, 0, 3 }, { 14, 30, 0 } } },
346  { { { 19, 0, 4 }, { 14, 31, 1 } } },
347  { { { 20, 0, 4 }, { 14, 31, 0 } } },
348  { { { 20, 0, 3 }, { 16, 27, 0 } } },
349  { { { 20, 0, 2 }, { 15, 30, 1 } } },
350  { { { 20, 0, 1 }, { 15, 30, 0 } } },
351  { { { 20, 0, 0 }, { 16, 28, 0 } } },
352  { { { 20, 0, 1 }, { 15, 31, 1 } } },
353  { { { 20, 0, 2 }, { 15, 31, 0 } } },
354  { { { 20, 0, 3 }, { 15, 31, 1 } } },
355  { { { 20, 0, 4 }, { 16, 30, 1 } } },
356  { { { 21, 0, 3 }, { 16, 30, 0 } } },
357  { { { 21, 0, 2 }, { 18, 27, 1 } } },
358  { { { 21, 0, 1 }, { 18, 27, 0 } } },
359  { { { 21, 0, 0 }, { 16, 31, 0 } } },
360  { { { 21, 0, 1 }, { 17, 30, 1 } } },
361  { { { 21, 0, 2 }, { 17, 30, 0 } } },
362  { { { 21, 0, 3 }, { 20, 24, 0 } } },
363  { { { 21, 0, 4 }, { 17, 31, 1 } } },
364  { { { 22, 0, 3 }, { 17, 31, 0 } } },
365  { { { 22, 0, 2 }, { 17, 31, 1 } } },
366  { { { 22, 0, 1 }, { 18, 30, 1 } } },
367  { { { 22, 0, 0 }, { 18, 30, 0 } } },
368  { { { 22, 0, 1 }, { 18, 31, 1 } } },
369  { { { 22, 0, 2 }, { 18, 31, 0 } } },
370  { { { 22, 0, 3 }, { 20, 27, 0 } } },
371  { { { 22, 0, 4 }, { 19, 30, 1 } } },
372  { { { 23, 0, 3 }, { 19, 30, 0 } } },
373  { { { 23, 0, 2 }, { 20, 28, 0 } } },
374  { { { 23, 0, 1 }, { 19, 31, 1 } } },
375  { { { 23, 0, 0 }, { 19, 31, 0 } } },
376  { { { 23, 0, 1 }, { 19, 31, 1 } } },
377  { { { 23, 0, 2 }, { 20, 30, 1 } } },
378  { { { 23, 0, 3 }, { 20, 30, 0 } } },
379  { { { 23, 0, 4 }, { 22, 27, 1 } } },
380  { { { 24, 0, 4 }, { 22, 27, 0 } } },
381  { { { 24, 0, 3 }, { 20, 31, 0 } } },
382  { { { 24, 0, 2 }, { 21, 30, 1 } } },
383  { { { 24, 0, 1 }, { 21, 30, 0 } } },
384  { { { 24, 0, 0 }, { 24, 24, 0 } } },
385  { { { 24, 0, 1 }, { 21, 31, 1 } } },
386  { { { 24, 0, 2 }, { 21, 31, 0 } } },
387  { { { 24, 0, 3 }, { 21, 31, 1 } } },
388  { { { 24, 0, 4 }, { 22, 30, 1 } } },
389  { { { 25, 0, 3 }, { 22, 30, 0 } } },
390  { { { 25, 0, 2 }, { 22, 31, 1 } } },
391  { { { 25, 0, 1 }, { 22, 31, 0 } } },
392  { { { 25, 0, 0 }, { 24, 27, 0 } } },
393  { { { 25, 0, 1 }, { 23, 30, 1 } } },
394  { { { 25, 0, 2 }, { 23, 30, 0 } } },
395  { { { 25, 0, 3 }, { 24, 28, 0 } } },
396  { { { 25, 0, 4 }, { 23, 31, 1 } } },
397  { { { 26, 0, 3 }, { 23, 31, 0 } } },
398  { { { 26, 0, 2 }, { 23, 31, 1 } } },
399  { { { 26, 0, 1 }, { 24, 30, 1 } } },
400  { { { 26, 0, 0 }, { 24, 30, 0 } } },
401  { { { 26, 0, 1 }, { 26, 27, 1 } } },
402  { { { 26, 0, 2 }, { 26, 27, 0 } } },
403  { { { 26, 0, 3 }, { 24, 31, 0 } } },
404  { { { 26, 0, 4 }, { 25, 30, 1 } } },
405  { { { 27, 0, 3 }, { 25, 30, 0 } } },
406  { { { 27, 0, 2 }, { 28, 24, 0 } } },
407  { { { 27, 0, 1 }, { 25, 31, 1 } } },
408  { { { 27, 0, 0 }, { 25, 31, 0 } } },
409  { { { 27, 0, 1 }, { 25, 31, 1 } } },
410  { { { 27, 0, 2 }, { 26, 30, 1 } } },
411  { { { 27, 0, 3 }, { 26, 30, 0 } } },
412  { { { 27, 0, 4 }, { 26, 31, 1 } } },
413  { { { 28, 0, 4 }, { 26, 31, 0 } } },
414  { { { 28, 0, 3 }, { 28, 27, 0 } } },
415  { { { 28, 0, 2 }, { 27, 30, 1 } } },
416  { { { 28, 0, 1 }, { 27, 30, 0 } } },
417  { { { 28, 0, 0 }, { 28, 28, 0 } } },
418  { { { 28, 0, 1 }, { 27, 31, 1 } } },
419  { { { 28, 0, 2 }, { 27, 31, 0 } } },
420  { { { 28, 0, 3 }, { 27, 31, 1 } } },
421  { { { 28, 0, 4 }, { 28, 30, 1 } } },
422  { { { 29, 0, 3 }, { 28, 30, 0 } } },
423  { { { 29, 0, 2 }, { 30, 27, 1 } } },
424  { { { 29, 0, 1 }, { 30, 27, 0 } } },
425  { { { 29, 0, 0 }, { 28, 31, 0 } } },
426  { { { 29, 0, 1 }, { 29, 30, 1 } } },
427  { { { 29, 0, 2 }, { 29, 30, 0 } } },
428  { { { 29, 0, 3 }, { 29, 30, 1 } } },
429  { { { 29, 0, 4 }, { 29, 31, 1 } } },
430  { { { 30, 0, 3 }, { 29, 31, 0 } } },
431  { { { 30, 0, 2 }, { 29, 31, 1 } } },
432  { { { 30, 0, 1 }, { 30, 30, 1 } } },
433  { { { 30, 0, 0 }, { 30, 30, 0 } } },
434  { { { 30, 0, 1 }, { 30, 31, 1 } } },
435  { { { 30, 0, 2 }, { 30, 31, 0 } } },
436  { { { 30, 0, 3 }, { 30, 31, 1 } } },
437  { { { 30, 0, 4 }, { 31, 30, 1 } } },
438  { { { 31, 0, 3 }, { 31, 30, 0 } } },
439  { { { 31, 0, 2 }, { 31, 30, 1 } } },
440  { { { 31, 0, 1 }, { 31, 31, 1 } } },
441  { { { 31, 0, 0 }, { 31, 31, 0 } } }
442};
443
444static const DDSSingleColourLookup DDSLookup_6_4[] =
445{
446  { { { 0, 0, 0 }, { 0, 0, 0 } } },
447  { { { 0, 0, 1 }, { 0, 1, 0 } } },
448  { { { 0, 0, 2 }, { 0, 2, 0 } } },
449  { { { 1, 0, 1 }, { 0, 3, 1 } } },
450  { { { 1, 0, 0 }, { 0, 3, 0 } } },
451  { { { 1, 0, 1 }, { 0, 4, 0 } } },
452  { { { 1, 0, 2 }, { 0, 5, 0 } } },
453  { { { 2, 0, 1 }, { 0, 6, 1 } } },
454  { { { 2, 0, 0 }, { 0, 6, 0 } } },
455  { { { 2, 0, 1 }, { 0, 7, 0 } } },
456  { { { 2, 0, 2 }, { 0, 8, 0 } } },
457  { { { 3, 0, 1 }, { 0, 9, 1 } } },
458  { { { 3, 0, 0 }, { 0, 9, 0 } } },
459  { { { 3, 0, 1 }, { 0, 10, 0 } } },
460  { { { 3, 0, 2 }, { 0, 11, 0 } } },
461  { { { 4, 0, 1 }, { 0, 12, 1 } } },
462  { { { 4, 0, 0 }, { 0, 12, 0 } } },
463  { { { 4, 0, 1 }, { 0, 13, 0 } } },
464  { { { 4, 0, 2 }, { 0, 14, 0 } } },
465  { { { 5, 0, 1 }, { 0, 15, 1 } } },
466  { { { 5, 0, 0 }, { 0, 15, 0 } } },
467  { { { 5, 0, 1 }, { 0, 16, 0 } } },
468  { { { 5, 0, 2 }, { 1, 15, 0 } } },
469  { { { 6, 0, 1 }, { 0, 17, 0 } } },
470  { { { 6, 0, 0 }, { 0, 18, 0 } } },
471  { { { 6, 0, 1 }, { 0, 19, 0 } } },
472  { { { 6, 0, 2 }, { 3, 14, 0 } } },
473  { { { 7, 0, 1 }, { 0, 20, 0 } } },
474  { { { 7, 0, 0 }, { 0, 21, 0 } } },
475  { { { 7, 0, 1 }, { 0, 22, 0 } } },
476  { { { 7, 0, 2 }, { 4, 15, 0 } } },
477  { { { 8, 0, 1 }, { 0, 23, 0 } } },
478  { { { 8, 0, 0 }, { 0, 24, 0 } } },
479  { { { 8, 0, 1 }, { 0, 25, 0 } } },
480  { { { 8, 0, 2 }, { 6, 14, 0 } } },
481  { { { 9, 0, 1 }, { 0, 26, 0 } } },
482  { { { 9, 0, 0 }, { 0, 27, 0 } } },
483  { { { 9, 0, 1 }, { 0, 28, 0 } } },
484  { { { 9, 0, 2 }, { 7, 15, 0 } } },
485  { { { 10, 0, 1 }, { 0, 29, 0 } } },
486  { { { 10, 0, 0 }, { 0, 30, 0 } } },
487  { { { 10, 0, 1 }, { 0, 31, 0 } } },
488  { { { 10, 0, 2 }, { 9, 14, 0 } } },
489  { { { 11, 0, 1 }, { 0, 32, 0 } } },
490  { { { 11, 0, 0 }, { 0, 33, 0 } } },
491  { { { 11, 0, 1 }, { 2, 30, 0 } } },
492  { { { 11, 0, 2 }, { 0, 34, 0 } } },
493  { { { 12, 0, 1 }, { 0, 35, 0 } } },
494  { { { 12, 0, 0 }, { 0, 36, 0 } } },
495  { { { 12, 0, 1 }, { 3, 31, 0 } } },
496  { { { 12, 0, 2 }, { 0, 37, 0 } } },
497  { { { 13, 0, 1 }, { 0, 38, 0 } } },
498  { { { 13, 0, 0 }, { 0, 39, 0 } } },
499  { { { 13, 0, 1 }, { 5, 30, 0 } } },
500  { { { 13, 0, 2 }, { 0, 40, 0 } } },
501  { { { 14, 0, 1 }, { 0, 41, 0 } } },
502  { { { 14, 0, 0 }, { 0, 42, 0 } } },
503  { { { 14, 0, 1 }, { 6, 31, 0 } } },
504  { { { 14, 0, 2 }, { 0, 43, 0 } } },
505  { { { 15, 0, 1 }, { 0, 44, 0 } } },
506  { { { 15, 0, 0 }, { 0, 45, 0 } } },
507  { { { 15, 0, 1 }, { 8, 30, 0 } } },
508  { { { 15, 0, 2 }, { 0, 46, 0 } } },
509  { { { 16, 0, 2 }, { 0, 47, 0 } } },
510  { { { 16, 0, 1 }, { 1, 46, 0 } } },
511  { { { 16, 0, 0 }, { 0, 48, 0 } } },
512  { { { 16, 0, 1 }, { 0, 49, 0 } } },
513  { { { 16, 0, 2 }, { 0, 50, 0 } } },
514  { { { 17, 0, 1 }, { 2, 47, 0 } } },
515  { { { 17, 0, 0 }, { 0, 51, 0 } } },
516  { { { 17, 0, 1 }, { 0, 52, 0 } } },
517  { { { 17, 0, 2 }, { 0, 53, 0 } } },
518  { { { 18, 0, 1 }, { 4, 46, 0 } } },
519  { { { 18, 0, 0 }, { 0, 54, 0 } } },
520  { { { 18, 0, 1 }, { 0, 55, 0 } } },
521  { { { 18, 0, 2 }, { 0, 56, 0 } } },
522  { { { 19, 0, 1 }, { 5, 47, 0 } } },
523  { { { 19, 0, 0 }, { 0, 57, 0 } } },
524  { { { 19, 0, 1 }, { 0, 58, 0 } } },
525  { { { 19, 0, 2 }, { 0, 59, 0 } } },
526  { { { 20, 0, 1 }, { 7, 46, 0 } } },
527  { { { 20, 0, 0 }, { 0, 60, 0 } } },
528  { { { 20, 0, 1 }, { 0, 61, 0 } } },
529  { { { 20, 0, 2 }, { 0, 62, 0 } } },
530  { { { 21, 0, 1 }, { 8, 47, 0 } } },
531  { { { 21, 0, 0 }, { 0, 63, 0 } } },
532  { { { 21, 0, 1 }, { 1, 62, 0 } } },
533  { { { 21, 0, 2 }, { 1, 63, 0 } } },
534  { { { 22, 0, 1 }, { 10, 46, 0 } } },
535  { { { 22, 0, 0 }, { 2, 62, 0 } } },
536  { { { 22, 0, 1 }, { 2, 63, 0 } } },
537  { { { 22, 0, 2 }, { 3, 62, 0 } } },
538  { { { 23, 0, 1 }, { 11, 47, 0 } } },
539  { { { 23, 0, 0 }, { 3, 63, 0 } } },
540  { { { 23, 0, 1 }, { 4, 62, 0 } } },
541  { { { 23, 0, 2 }, { 4, 63, 0 } } },
542  { { { 24, 0, 1 }, { 13, 46, 0 } } },
543  { { { 24, 0, 0 }, { 5, 62, 0 } } },
544  { { { 24, 0, 1 }, { 5, 63, 0 } } },
545  { { { 24, 0, 2 }, { 6, 62, 0 } } },
546  { { { 25, 0, 1 }, { 14, 47, 0 } } },
547  { { { 25, 0, 0 }, { 6, 63, 0 } } },
548  { { { 25, 0, 1 }, { 7, 62, 0 } } },
549  { { { 25, 0, 2 }, { 7, 63, 0 } } },
550  { { { 26, 0, 1 }, { 16, 45, 0 } } },
551  { { { 26, 0, 0 }, { 8, 62, 0 } } },
552  { { { 26, 0, 1 }, { 8, 63, 0 } } },
553  { { { 26, 0, 2 }, { 9, 62, 0 } } },
554  { { { 27, 0, 1 }, { 16, 48, 0 } } },
555  { { { 27, 0, 0 }, { 9, 63, 0 } } },
556  { { { 27, 0, 1 }, { 10, 62, 0 } } },
557  { { { 27, 0, 2 }, { 10, 63, 0 } } },
558  { { { 28, 0, 1 }, { 16, 51, 0 } } },
559  { { { 28, 0, 0 }, { 11, 62, 0 } } },
560  { { { 28, 0, 1 }, { 11, 63, 0 } } },
561  { { { 28, 0, 2 }, { 12, 62, 0 } } },
562  { { { 29, 0, 1 }, { 16, 54, 0 } } },
563  { { { 29, 0, 0 }, { 12, 63, 0 } } },
564  { { { 29, 0, 1 }, { 13, 62, 0 } } },
565  { { { 29, 0, 2 }, { 13, 63, 0 } } },
566  { { { 30, 0, 1 }, { 16, 57, 0 } } },
567  { { { 30, 0, 0 }, { 14, 62, 0 } } },
568  { { { 30, 0, 1 }, { 14, 63, 0 } } },
569  { { { 30, 0, 2 }, { 15, 62, 0 } } },
570  { { { 31, 0, 1 }, { 16, 60, 0 } } },
571  { { { 31, 0, 0 }, { 15, 63, 0 } } },
572  { { { 31, 0, 1 }, { 24, 46, 0 } } },
573  { { { 31, 0, 2 }, { 16, 62, 0 } } },
574  { { { 32, 0, 2 }, { 16, 63, 0 } } },
575  { { { 32, 0, 1 }, { 17, 62, 0 } } },
576  { { { 32, 0, 0 }, { 25, 47, 0 } } },
577  { { { 32, 0, 1 }, { 17, 63, 0 } } },
578  { { { 32, 0, 2 }, { 18, 62, 0 } } },
579  { { { 33, 0, 1 }, { 18, 63, 0 } } },
580  { { { 33, 0, 0 }, { 27, 46, 0 } } },
581  { { { 33, 0, 1 }, { 19, 62, 0 } } },
582  { { { 33, 0, 2 }, { 19, 63, 0 } } },
583  { { { 34, 0, 1 }, { 20, 62, 0 } } },
584  { { { 34, 0, 0 }, { 28, 47, 0 } } },
585  { { { 34, 0, 1 }, { 20, 63, 0 } } },
586  { { { 34, 0, 2 }, { 21, 62, 0 } } },
587  { { { 35, 0, 1 }, { 21, 63, 0 } } },
588  { { { 35, 0, 0 }, { 30, 46, 0 } } },
589  { { { 35, 0, 1 }, { 22, 62, 0 } } },
590  { { { 35, 0, 2 }, { 22, 63, 0 } } },
591  { { { 36, 0, 1 }, { 23, 62, 0 } } },
592  { { { 36, 0, 0 }, { 31, 47, 0 } } },
593  { { { 36, 0, 1 }, { 23, 63, 0 } } },
594  { { { 36, 0, 2 }, { 24, 62, 0 } } },
595  { { { 37, 0, 1 }, { 24, 63, 0 } } },
596  { { { 37, 0, 0 }, { 32, 47, 0 } } },
597  { { { 37, 0, 1 }, { 25, 62, 0 } } },
598  { { { 37, 0, 2 }, { 25, 63, 0 } } },
599  { { { 38, 0, 1 }, { 26, 62, 0 } } },
600  { { { 38, 0, 0 }, { 32, 50, 0 } } },
601  { { { 38, 0, 1 }, { 26, 63, 0 } } },
602  { { { 38, 0, 2 }, { 27, 62, 0 } } },
603  { { { 39, 0, 1 }, { 27, 63, 0 } } },
604  { { { 39, 0, 0 }, { 32, 53, 0 } } },
605  { { { 39, 0, 1 }, { 28, 62, 0 } } },
606  { { { 39, 0, 2 }, { 28, 63, 0 } } },
607  { { { 40, 0, 1 }, { 29, 62, 0 } } },
608  { { { 40, 0, 0 }, { 32, 56, 0 } } },
609  { { { 40, 0, 1 }, { 29, 63, 0 } } },
610  { { { 40, 0, 2 }, { 30, 62, 0 } } },
611  { { { 41, 0, 1 }, { 30, 63, 0 } } },
612  { { { 41, 0, 0 }, { 32, 59, 0 } } },
613  { { { 41, 0, 1 }, { 31, 62, 0 } } },
614  { { { 41, 0, 2 }, { 31, 63, 0 } } },
615  { { { 42, 0, 1 }, { 32, 61, 0 } } },
616  { { { 42, 0, 0 }, { 32, 62, 0 } } },
617  { { { 42, 0, 1 }, { 32, 63, 0 } } },
618  { { { 42, 0, 2 }, { 41, 46, 0 } } },
619  { { { 43, 0, 1 }, { 33, 62, 0 } } },
620  { { { 43, 0, 0 }, { 33, 63, 0 } } },
621  { { { 43, 0, 1 }, { 34, 62, 0 } } },
622  { { { 43, 0, 2 }, { 42, 47, 0 } } },
623  { { { 44, 0, 1 }, { 34, 63, 0 } } },
624  { { { 44, 0, 0 }, { 35, 62, 0 } } },
625  { { { 44, 0, 1 }, { 35, 63, 0 } } },
626  { { { 44, 0, 2 }, { 44, 46, 0 } } },
627  { { { 45, 0, 1 }, { 36, 62, 0 } } },
628  { { { 45, 0, 0 }, { 36, 63, 0 } } },
629  { { { 45, 0, 1 }, { 37, 62, 0 } } },
630  { { { 45, 0, 2 }, { 45, 47, 0 } } },
631  { { { 46, 0, 1 }, { 37, 63, 0 } } },
632  { { { 46, 0, 0 }, { 38, 62, 0 } } },
633  { { { 46, 0, 1 }, { 38, 63, 0 } } },
634  { { { 46, 0, 2 }, { 47, 46, 0 } } },
635  { { { 47, 0, 1 }, { 39, 62, 0 } } },
636  { { { 47, 0, 0 }, { 39, 63, 0 } } },
637  { { { 47, 0, 1 }, { 40, 62, 0 } } },
638  { { { 47, 0, 2 }, { 48, 46, 0 } } },
639  { { { 48, 0, 2 }, { 40, 63, 0 } } },
640  { { { 48, 0, 1 }, { 41, 62, 0 } } },
641  { { { 48, 0, 0 }, { 41, 63, 0 } } },
642  { { { 48, 0, 1 }, { 48, 49, 0 } } },
643  { { { 48, 0, 2 }, { 42, 62, 0 } } },
644  { { { 49, 0, 1 }, { 42, 63, 0 } } },
645  { { { 49, 0, 0 }, { 43, 62, 0 } } },
646  { { { 49, 0, 1 }, { 48, 52, 0 } } },
647  { { { 49, 0, 2 }, { 43, 63, 0 } } },
648  { { { 50, 0, 1 }, { 44, 62, 0 } } },
649  { { { 50, 0, 0 }, { 44, 63, 0 } } },
650  { { { 50, 0, 1 }, { 48, 55, 0 } } },
651  { { { 50, 0, 2 }, { 45, 62, 0 } } },
652  { { { 51, 0, 1 }, { 45, 63, 0 } } },
653  { { { 51, 0, 0 }, { 46, 62, 0 } } },
654  { { { 51, 0, 1 }, { 48, 58, 0 } } },
655  { { { 51, 0, 2 }, { 46, 63, 0 } } },
656  { { { 52, 0, 1 }, { 47, 62, 0 } } },
657  { { { 52, 0, 0 }, { 47, 63, 0 } } },
658  { { { 52, 0, 1 }, { 48, 61, 0 } } },
659  { { { 52, 0, 2 }, { 48, 62, 0 } } },
660  { { { 53, 0, 1 }, { 56, 47, 0 } } },
661  { { { 53, 0, 0 }, { 48, 63, 0 } } },
662  { { { 53, 0, 1 }, { 49, 62, 0 } } },
663  { { { 53, 0, 2 }, { 49, 63, 0 } } },
664  { { { 54, 0, 1 }, { 58, 46, 0 } } },
665  { { { 54, 0, 0 }, { 50, 62, 0 } } },
666  { { { 54, 0, 1 }, { 50, 63, 0 } } },
667  { { { 54, 0, 2 }, { 51, 62, 0 } } },
668  { { { 55, 0, 1 }, { 59, 47, 0 } } },
669  { { { 55, 0, 0 }, { 51, 63, 0 } } },
670  { { { 55, 0, 1 }, { 52, 62, 0 } } },
671  { { { 55, 0, 2 }, { 52, 63, 0 } } },
672  { { { 56, 0, 1 }, { 61, 46, 0 } } },
673  { { { 56, 0, 0 }, { 53, 62, 0 } } },
674  { { { 56, 0, 1 }, { 53, 63, 0 } } },
675  { { { 56, 0, 2 }, { 54, 62, 0 } } },
676  { { { 57, 0, 1 }, { 62, 47, 0 } } },
677  { { { 57, 0, 0 }, { 54, 63, 0 } } },
678  { { { 57, 0, 1 }, { 55, 62, 0 } } },
679  { { { 57, 0, 2 }, { 55, 63, 0 } } },
680  { { { 58, 0, 1 }, { 56, 62, 1 } } },
681  { { { 58, 0, 0 }, { 56, 62, 0 } } },
682  { { { 58, 0, 1 }, { 56, 63, 0 } } },
683  { { { 58, 0, 2 }, { 57, 62, 0 } } },
684  { { { 59, 0, 1 }, { 57, 63, 1 } } },
685  { { { 59, 0, 0 }, { 57, 63, 0 } } },
686  { { { 59, 0, 1 }, { 58, 62, 0 } } },
687  { { { 59, 0, 2 }, { 58, 63, 0 } } },
688  { { { 60, 0, 1 }, { 59, 62, 1 } } },
689  { { { 60, 0, 0 }, { 59, 62, 0 } } },
690  { { { 60, 0, 1 }, { 59, 63, 0 } } },
691  { { { 60, 0, 2 }, { 60, 62, 0 } } },
692  { { { 61, 0, 1 }, { 60, 63, 1 } } },
693  { { { 61, 0, 0 }, { 60, 63, 0 } } },
694  { { { 61, 0, 1 }, { 61, 62, 0 } } },
695  { { { 61, 0, 2 }, { 61, 63, 0 } } },
696  { { { 62, 0, 1 }, { 62, 62, 1 } } },
697  { { { 62, 0, 0 }, { 62, 62, 0 } } },
698  { { { 62, 0, 1 }, { 62, 63, 0 } } },
699  { { { 62, 0, 2 }, { 63, 62, 0 } } },
700  { { { 63, 0, 1 }, { 63, 63, 1 } } },
701  { { { 63, 0, 0 }, { 63, 63, 0 } } }
702};
703
704static const DDSSingleColourLookup*
705  DDS_LOOKUP[] =
706{
707  DDSLookup_5_4,
708  DDSLookup_6_4,
709  DDSLookup_5_4
710};
711
712/*
713  Macros
714*/
715#define C565_r(x) (((x) & 0xF800) >> 11)
716#define C565_g(x) (((x) & 0x07E0) >> 5)
717#define C565_b(x)  ((x) & 0x001F)
718
719#define C565_red(x)   ( (C565_r(x) << 3 | C565_r(x) >> 2))
720#define C565_green(x) ( (C565_g(x) << 2 | C565_g(x) >> 4))
721#define C565_blue(x)  ( (C565_b(x) << 3 | C565_b(x) >> 2))
722
723#define DIV2(x)  ((x) > 1 ? ((x) >> 1) : 1)
724
725#define FixRange(min, max, steps) \
726if (min > max) \
727  min = max; \
728if (max - min < steps) \
729  max = MagickMin(min + steps, 255); \
730if (max - min < steps) \
731  min = MagickMax(0, max - steps)
732
733#define Dot(left, right) (left.x*right.x) + (left.y*right.y) + (left.z*right.z)
734
735#define VectorInit(vector, value) vector.x = vector.y = vector.z = vector.w \
736  = value
737#define VectorInit3(vector, value) vector.x = vector.y = vector.z = value
738
739#define IsBitMask(mask, r, g, b, a) (mask.r_bitmask == r && mask.g_bitmask == \
740  g && mask.b_bitmask == b && mask.alpha_bitmask == a)
741
742/*
743  Forward declarations
744*/
745/*
746  Forward declarations
747*/
748static MagickBooleanType
749  ConstructOrdering(const size_t,const DDSVector4 *,const DDSVector3,
750    DDSVector4 *, DDSVector4 *, unsigned char *, size_t),
751  ReadDDSInfo(Image *,DDSInfo *),
752  ReadDXT1(Image *,DDSInfo *,ExceptionInfo *),
753  ReadDXT3(Image *,DDSInfo *,ExceptionInfo *),
754  ReadDXT5(Image *,DDSInfo *,ExceptionInfo *),
755  ReadUncompressedRGB(Image *,DDSInfo *,ExceptionInfo *),
756  ReadUncompressedRGBA(Image *,DDSInfo *,ExceptionInfo *),
757  SkipDXTMipmaps(Image *,DDSInfo *,int,ExceptionInfo *),
758  SkipRGBMipmaps(Image *,DDSInfo *,int,ExceptionInfo *),
759  WriteDDSImage(const ImageInfo *,Image *,ExceptionInfo *),
760  WriteMipmaps(Image *,const size_t,const size_t,const size_t,
761    const MagickBooleanType,const MagickBooleanType,ExceptionInfo *);
762
763static void
764  RemapIndices(const ssize_t *,const unsigned char *,unsigned char *),
765  WriteDDSInfo(Image *,const size_t,const size_t,const size_t),
766  WriteFourCC(Image *,const size_t,const MagickBooleanType,
767    const MagickBooleanType,ExceptionInfo *),
768  WriteImageData(Image *,const size_t,const size_t,const MagickBooleanType,
769    const MagickBooleanType,ExceptionInfo *),
770  WriteIndices(Image *,const DDSVector3,const DDSVector3,unsigned char *),
771  WriteSingleColorFit(Image *,const DDSVector4 *,const ssize_t *),
772  WriteUncompressed(Image *,ExceptionInfo *);
773
774static inline void VectorAdd(const DDSVector4 left, const DDSVector4 right,
775  DDSVector4 *destination)
776{
777  destination->x = left.x + right.x;
778  destination->y = left.y + right.y;
779  destination->z = left.z + right.z;
780  destination->w = left.w + right.w;
781}
782
783static inline void VectorClamp(DDSVector4 *value)
784{
785  value->x = MagickMin(1.0f,MagickMax(0.0f,value->x));
786  value->y = MagickMin(1.0f,MagickMax(0.0f,value->y));
787  value->z = MagickMin(1.0f,MagickMax(0.0f,value->z));
788  value->w = MagickMin(1.0f,MagickMax(0.0f,value->w));
789}
790
791static inline void VectorClamp3(DDSVector3 *value)
792{
793  value->x = MagickMin(1.0f,MagickMax(0.0f,value->x));
794  value->y = MagickMin(1.0f,MagickMax(0.0f,value->y));
795  value->z = MagickMin(1.0f,MagickMax(0.0f,value->z));
796}
797
798static inline void VectorCopy43(const DDSVector4 source,
799  DDSVector3 *destination)
800{
801  destination->x = source.x;
802  destination->y = source.y;
803  destination->z = source.z;
804}
805
806static inline void VectorCopy44(const DDSVector4 source,
807  DDSVector4 *destination)
808{
809  destination->x = source.x;
810  destination->y = source.y;
811  destination->z = source.z;
812  destination->w = source.w;
813}
814
815static inline void VectorNegativeMultiplySubtract(const DDSVector4 a,
816  const DDSVector4 b, const DDSVector4 c, DDSVector4 *destination)
817{
818  destination->x = c.x - (a.x * b.x);
819  destination->y = c.y - (a.y * b.y);
820  destination->z = c.z - (a.z * b.z);
821  destination->w = c.w - (a.w * b.w);
822}
823
824static inline void VectorMultiply(const DDSVector4 left,
825  const DDSVector4 right, DDSVector4 *destination)
826{
827  destination->x = left.x * right.x;
828  destination->y = left.y * right.y;
829  destination->z = left.z * right.z;
830  destination->w = left.w * right.w;
831}
832
833static inline void VectorMultiply3(const DDSVector3 left,
834  const DDSVector3 right, DDSVector3 *destination)
835{
836  destination->x = left.x * right.x;
837  destination->y = left.y * right.y;
838  destination->z = left.z * right.z;
839}
840
841static inline void VectorMultiplyAdd(const DDSVector4 a, const DDSVector4 b,
842  const DDSVector4 c, DDSVector4 *destination)
843{
844  destination->x = (a.x * b.x) + c.x;
845  destination->y = (a.y * b.y) + c.y;
846  destination->z = (a.z * b.z) + c.z;
847  destination->w = (a.w * b.w) + c.w;
848}
849
850static inline void VectorMultiplyAdd3(const DDSVector3 a, const DDSVector3 b,
851  const DDSVector3 c, DDSVector3 *destination)
852{
853  destination->x = (a.x * b.x) + c.x;
854  destination->y = (a.y * b.y) + c.y;
855  destination->z = (a.z * b.z) + c.z;
856}
857
858static inline void VectorReciprocal(const DDSVector4 value,
859  DDSVector4 *destination)
860{
861  destination->x = 1.0f / value.x;
862  destination->y = 1.0f / value.y;
863  destination->z = 1.0f / value.z;
864  destination->w = 1.0f / value.w;
865}
866
867static inline void VectorSubtract(const DDSVector4 left,
868  const DDSVector4 right, DDSVector4 *destination)
869{
870  destination->x = left.x - right.x;
871  destination->y = left.y - right.y;
872  destination->z = left.z - right.z;
873  destination->w = left.w - right.w;
874}
875
876static inline void VectorSubtract3(const DDSVector3 left,
877  const DDSVector3 right, DDSVector3 *destination)
878{
879  destination->x = left.x - right.x;
880  destination->y = left.y - right.y;
881  destination->z = left.z - right.z;
882}
883
884static inline void VectorTruncate(DDSVector4 *value)
885{
886  value->x = value->x > 0.0f ? floor(value->x) : ceil(value->x);
887  value->y = value->y > 0.0f ? floor(value->y) : ceil(value->y);
888  value->z = value->z > 0.0f ? floor(value->z) : ceil(value->z);
889  value->w = value->w > 0.0f ? floor(value->w) : ceil(value->w);
890}
891
892static inline void VectorTruncate3(DDSVector3 *value)
893{
894  value->x = value->x > 0.0f ? floor(value->x) : ceil(value->x);
895  value->y = value->y > 0.0f ? floor(value->y) : ceil(value->y);
896  value->z = value->z > 0.0f ? floor(value->z) : ceil(value->z);
897}
898
899static void CalculateColors(unsigned short c0, unsigned short c1,
900  DDSColors *c, MagickBooleanType ignoreAlpha)
901{
902  c->a[0] = c->a[1] = c->a[2] = c->a[3] = 0;
903
904  c->r[0] = (unsigned char) C565_red(c0);
905  c->g[0] = (unsigned char) C565_green(c0);
906  c->b[0] = (unsigned char) C565_blue(c0);
907
908  c->r[1] = (unsigned char) C565_red(c1);
909  c->g[1] = (unsigned char) C565_green(c1);
910  c->b[1] = (unsigned char) C565_blue(c1);
911
912  if (ignoreAlpha != MagickFalse || c0 > c1)
913    {
914      c->r[2] = (unsigned char) ((2 * c->r[0] + c->r[1]) / 3);
915      c->g[2] = (unsigned char) ((2 * c->g[0] + c->g[1]) / 3);
916      c->b[2] = (unsigned char) ((2 * c->b[0] + c->b[1]) / 3);
917
918      c->r[3] = (unsigned char) ((c->r[0] + 2 * c->r[1]) / 3);
919      c->g[3] = (unsigned char) ((c->g[0] + 2 * c->g[1]) / 3);
920      c->b[3] = (unsigned char) ((c->b[0] + 2 * c->b[1]) / 3);
921    }
922  else
923    {
924      c->r[2] = (unsigned char) ((c->r[0] + c->r[1]) / 2);
925      c->g[2] = (unsigned char) ((c->g[0] + c->g[1]) / 2);
926      c->b[2] = (unsigned char) ((c->b[0] + c->b[1]) / 2);
927
928      c->r[3] = c->g[3] = c->b[3] = 0;
929      c->a[3] = 255;
930    }
931}
932
933static size_t CompressAlpha(const size_t min, const size_t max,
934  const size_t steps, const ssize_t *alphas, unsigned char* indices)
935{
936  unsigned char
937    codes[8];
938
939  register ssize_t
940    i;
941
942  size_t
943    error,
944    index,
945    j,
946    least,
947    value;
948
949  codes[0] = (unsigned char) min;
950  codes[1] = (unsigned char) max;
951  codes[6] = 0;
952  codes[7] = 255;
953
954  for (i=1; i <  (ssize_t) steps; i++)
955    codes[i+1] = (unsigned char) (((steps-i)*min + i*max) / steps);
956
957  error = 0;
958  for (i=0; i<16; i++)
959  {
960    if (alphas[i] == -1)
961      {
962        indices[i] = 0;
963        continue;
964      }
965
966    value = alphas[i];
967    least = SIZE_MAX;
968    index = 0;
969    for (j=0; j<8; j++)
970    {
971      size_t
972        dist;
973
974      dist = value - (size_t)codes[j];
975      dist *= dist;
976
977      if (dist < least)
978        {
979          least = dist;
980          index = j;
981        }
982    }
983
984    indices[i] = (unsigned char)index;
985    error += least;
986  }
987
988  return error;
989}
990
991static void CompressClusterFit(const size_t count,
992  const DDSVector4 *points, const ssize_t *map, const DDSVector3 principle,
993  const DDSVector4 metric, DDSVector3 *start, DDSVector3* end,
994  unsigned char *indices)
995{
996  DDSVector3
997    axis;
998
999  DDSVector4
1000    grid,
1001    gridrcp,
1002    half,
1003    onethird_onethird2,
1004    pointsWeights[16],
1005    two,
1006    twonineths,
1007    twothirds_twothirds2,
1008    xSumwSum;
1009
1010  float
1011    bestError = 1e+37f;
1012
1013  size_t
1014    bestIteration = 0,
1015    besti = 0,
1016    bestj = 0,
1017    bestk = 0,
1018    iterationIndex;
1019
1020  ssize_t
1021    i;
1022
1023  unsigned char
1024    *o,
1025    order[128],
1026    unordered[16];
1027
1028  VectorInit(half,0.5f);
1029  VectorInit(two,2.0f);
1030
1031  VectorInit(onethird_onethird2,1.0f/3.0f);
1032  onethird_onethird2.w = 1.0f/9.0f;
1033  VectorInit(twothirds_twothirds2,2.0f/3.0f);
1034  twothirds_twothirds2.w = 4.0f/9.0f;
1035  VectorInit(twonineths,2.0f/9.0f);
1036
1037  grid.x = 31.0f;
1038  grid.y = 63.0f;
1039  grid.z = 31.0f;
1040  grid.w = 0.0f;
1041
1042  gridrcp.x = 1.0f/31.0f;
1043  gridrcp.y = 1.0f/63.0f;
1044  gridrcp.z = 1.0f/31.0f;
1045  gridrcp.w = 0.0f;
1046
1047  xSumwSum.x = 0.0f;
1048  xSumwSum.y = 0.0f;
1049  xSumwSum.z = 0.0f;
1050  xSumwSum.w = 0.0f;
1051
1052  ConstructOrdering(count,points,principle,pointsWeights,&xSumwSum,order,0);
1053
1054  for (iterationIndex = 0;;)
1055  {
1056#if defined(MAGICKCORE_OPENMP_SUPPORT)
1057  #pragma omp parallel for schedule(dynamic,1) \
1058    num_threads(GetMagickResourceLimit(ThreadResource))
1059#endif
1060    for (i=0; i < (ssize_t) count; i++)
1061    {
1062      DDSVector4
1063        part0,
1064        part1,
1065        part2;
1066
1067      size_t
1068        ii,
1069        j,
1070        k,
1071        kmin;
1072
1073      VectorInit(part0,0.0f);
1074      for(ii=0; ii < (size_t) i; ii++)
1075        VectorAdd(pointsWeights[ii],part0,&part0);
1076
1077      VectorInit(part1,0.0f);
1078      for (j=(size_t) i;;)
1079      {
1080        if (j == 0)
1081          {
1082            VectorCopy44(pointsWeights[0],&part2);
1083            kmin = 1;
1084          }
1085          else
1086          {
1087            VectorInit(part2,0.0f);
1088            kmin = j;
1089          }
1090
1091        for (k=kmin;;)
1092        {
1093          DDSVector4
1094            a,
1095            alpha2_sum,
1096            alphax_sum,
1097            alphabeta_sum,
1098            b,
1099            beta2_sum,
1100            betax_sum,
1101            e1,
1102            e2,
1103            factor,
1104            part3;
1105
1106          float
1107            error;
1108
1109          VectorSubtract(xSumwSum,part2,&part3);
1110          VectorSubtract(part3,part1,&part3);
1111          VectorSubtract(part3,part0,&part3);
1112
1113          VectorMultiplyAdd(part1,twothirds_twothirds2,part0,&alphax_sum);
1114          VectorMultiplyAdd(part2,onethird_onethird2,alphax_sum,&alphax_sum);
1115          VectorInit(alpha2_sum,alphax_sum.w);
1116
1117          VectorMultiplyAdd(part2,twothirds_twothirds2,part3,&betax_sum);
1118          VectorMultiplyAdd(part1,onethird_onethird2,betax_sum,&betax_sum);
1119          VectorInit(beta2_sum,betax_sum.w);
1120
1121          VectorAdd(part1,part2,&alphabeta_sum);
1122          VectorInit(alphabeta_sum,alphabeta_sum.w);
1123          VectorMultiply(twonineths,alphabeta_sum,&alphabeta_sum);
1124
1125          VectorMultiply(alpha2_sum,beta2_sum,&factor);
1126          VectorNegativeMultiplySubtract(alphabeta_sum,alphabeta_sum,factor,
1127            &factor);
1128          VectorReciprocal(factor,&factor);
1129
1130          VectorMultiply(alphax_sum,beta2_sum,&a);
1131          VectorNegativeMultiplySubtract(betax_sum,alphabeta_sum,a,&a);
1132          VectorMultiply(a,factor,&a);
1133
1134          VectorMultiply(betax_sum,alpha2_sum,&b);
1135          VectorNegativeMultiplySubtract(alphax_sum,alphabeta_sum,b,&b);
1136          VectorMultiply(b,factor,&b);
1137
1138          VectorClamp(&a);
1139          VectorMultiplyAdd(grid,a,half,&a);
1140          VectorTruncate(&a);
1141          VectorMultiply(a,gridrcp,&a);
1142
1143          VectorClamp(&b);
1144          VectorMultiplyAdd(grid,b,half,&b);
1145          VectorTruncate(&b);
1146          VectorMultiply(b,gridrcp,&b);
1147
1148          VectorMultiply(b,b,&e1);
1149          VectorMultiply(e1,beta2_sum,&e1);
1150          VectorMultiply(a,a,&e2);
1151          VectorMultiplyAdd(e2,alpha2_sum,e1,&e1);
1152
1153          VectorMultiply(a,b,&e2);
1154          VectorMultiply(e2,alphabeta_sum,&e2);
1155          VectorNegativeMultiplySubtract(a,alphax_sum,e2,&e2);
1156          VectorNegativeMultiplySubtract(b,betax_sum,e2,&e2);
1157          VectorMultiplyAdd(two,e2,e1,&e2);
1158          VectorMultiply(e2,metric,&e2);
1159
1160          error = e2.x + e2.y + e2.z;
1161
1162          if (error < bestError)
1163            {
1164#if defined(MAGICKCORE_OPENMP_SUPPORT)
1165              #pragma omp critical (DDS_CompressClusterFit)
1166#endif
1167              {
1168                if (error < bestError)
1169                  {
1170                    VectorCopy43(a,start);
1171                    VectorCopy43(b,end);
1172                    bestError = error;
1173                    besti = i;
1174                    bestj = j;
1175                    bestk = k;
1176                    bestIteration = iterationIndex;
1177                  }
1178              }
1179            }
1180
1181          if (k == count)
1182            break;
1183
1184          VectorAdd(pointsWeights[k],part2,&part2);
1185          k++;
1186        }
1187
1188        if (j == count)
1189          break;
1190
1191        VectorAdd(pointsWeights[j],part1,&part1);
1192        j++;
1193      }
1194    }
1195
1196    if (bestIteration != iterationIndex)
1197      break;
1198
1199    iterationIndex++;
1200    if (iterationIndex == 8)
1201      break;
1202
1203    VectorSubtract3(*end,*start,&axis);
1204    if (ConstructOrdering(count,points,axis,pointsWeights,&xSumwSum,order,
1205      iterationIndex) == MagickFalse)
1206      break;
1207  }
1208
1209  o = order + (16*bestIteration);
1210
1211  for (i=0; i < (ssize_t) besti; i++)
1212    unordered[o[i]] = 0;
1213  for (i=besti; i < (ssize_t) bestj; i++)
1214    unordered[o[i]] = 2;
1215  for (i=bestj; i < (ssize_t) bestk; i++)
1216    unordered[o[i]] = 3;
1217  for (i=bestk; i < (ssize_t) count; i++)
1218    unordered[o[i]] = 1;
1219
1220  RemapIndices(map,unordered,indices);
1221}
1222
1223static void CompressRangeFit(const size_t count,
1224  const DDSVector4* points, const ssize_t *map, const DDSVector3 principle,
1225  const DDSVector4 metric, DDSVector3 *start, DDSVector3 *end,
1226  unsigned char *indices)
1227{
1228  float
1229    d,
1230    bestDist,
1231    max,
1232    min,
1233    val;
1234
1235  DDSVector3
1236    codes[4],
1237    grid,
1238    gridrcp,
1239    half,
1240    dist;
1241
1242  register ssize_t
1243    i;
1244
1245  size_t
1246    bestj,
1247    j;
1248
1249  unsigned char
1250    closest[16];
1251
1252  VectorInit3(half,0.5f);
1253
1254  grid.x = 31.0f;
1255  grid.y = 63.0f;
1256  grid.z = 31.0f;
1257
1258  gridrcp.x = 1.0f/31.0f;
1259  gridrcp.y = 1.0f/63.0f;
1260  gridrcp.z = 1.0f/31.0f;
1261
1262  if (count > 0)
1263    {
1264      VectorCopy43(points[0],start);
1265      VectorCopy43(points[0],end);
1266
1267      min = max = Dot(points[0],principle);
1268      for (i=1; i < (ssize_t) count; i++)
1269      {
1270        val = Dot(points[i],principle);
1271        if (val < min)
1272        {
1273          VectorCopy43(points[i],start);
1274          min = val;
1275        }
1276        else if (val > max)
1277        {
1278          VectorCopy43(points[i],end);
1279          max = val;
1280        }
1281      }
1282    }
1283
1284  VectorClamp3(start);
1285  VectorMultiplyAdd3(grid,*start,half,start);
1286  VectorTruncate3(start);
1287  VectorMultiply3(*start,gridrcp,start);
1288
1289  VectorClamp3(end);
1290  VectorMultiplyAdd3(grid,*end,half,end);
1291  VectorTruncate3(end);
1292  VectorMultiply3(*end,gridrcp,end);
1293
1294  codes[0] = *start;
1295  codes[1] = *end;
1296  codes[2].x = (start->x * (2.0f/3.0f)) + (end->x * (1.0f/3.0f));
1297  codes[2].y = (start->y * (2.0f/3.0f)) + (end->y * (1.0f/3.0f));
1298  codes[2].z = (start->z * (2.0f/3.0f)) + (end->z * (1.0f/3.0f));
1299  codes[3].x = (start->x * (1.0f/3.0f)) + (end->x * (2.0f/3.0f));
1300  codes[3].y = (start->y * (1.0f/3.0f)) + (end->y * (2.0f/3.0f));
1301  codes[3].z = (start->z * (1.0f/3.0f)) + (end->z * (2.0f/3.0f));
1302
1303  for (i=0; i < (ssize_t) count; i++)
1304  {
1305    bestDist = 1e+37f;
1306    bestj = 0;
1307    for (j=0; j < 4; j++)
1308    {
1309      dist.x = (points[i].x - codes[j].x) * metric.x;
1310      dist.y = (points[i].y - codes[j].y) * metric.y;
1311      dist.z = (points[i].z - codes[j].z) * metric.z;
1312
1313      d = Dot(dist,dist);
1314      if (d < bestDist)
1315        {
1316          bestDist = d;
1317          bestj = j;
1318        }
1319    }
1320
1321    closest[i] = (unsigned char) bestj;
1322  }
1323
1324  RemapIndices(map, closest, indices);
1325}
1326
1327static void ComputeEndPoints(const DDSSingleColourLookup *lookup[],
1328  const unsigned char *color, DDSVector3 *start, DDSVector3 *end,
1329  unsigned char *index)
1330{
1331  register ssize_t
1332    i;
1333
1334  size_t
1335    c,
1336    maxError = SIZE_MAX;
1337
1338  for (i=0; i < 2; i++)
1339  {
1340    const DDSSourceBlock*
1341      sources[3];
1342
1343      size_t
1344        error = 0;
1345
1346    for (c=0; c < 3; c++)
1347    {
1348      sources[c] = &lookup[c][color[c]].sources[i];
1349      error += ((size_t) sources[c]->error) * ((size_t) sources[c]->error);
1350    }
1351
1352    if (error > maxError)
1353      continue;
1354
1355    start->x = (float) sources[0]->start / 31.0f;
1356    start->y = (float) sources[1]->start / 63.0f;
1357    start->z = (float) sources[2]->start / 31.0f;
1358
1359    end->x = (float) sources[0]->end / 31.0f;
1360    end->y = (float) sources[1]->end / 63.0f;
1361    end->z = (float) sources[2]->end / 31.0f;
1362
1363    *index = (unsigned char) (2*i);
1364    maxError = error;
1365  }
1366}
1367
1368static void ComputePrincipleComponent(const float *covariance,
1369  DDSVector3 *principle)
1370{
1371  DDSVector4
1372    row0,
1373    row1,
1374    row2,
1375    v;
1376
1377  register ssize_t
1378    i;
1379
1380  row0.x = covariance[0];
1381  row0.y = covariance[1];
1382  row0.z = covariance[2];
1383  row0.w = 0.0f;
1384
1385  row1.x = covariance[1];
1386  row1.y = covariance[3];
1387  row1.z = covariance[4];
1388  row1.w = 0.0f;
1389
1390  row2.x = covariance[2];
1391  row2.y = covariance[4];
1392  row2.z = covariance[5];
1393  row2.w = 0.0f;
1394
1395  VectorInit(v,1.0f);
1396
1397  for (i=0; i < 8; i++)
1398  {
1399    DDSVector4
1400      w;
1401
1402    float
1403      a;
1404
1405    w.x = row0.x * v.x;
1406    w.y = row0.y * v.x;
1407    w.z = row0.z * v.x;
1408    w.w = row0.w * v.x;
1409
1410    w.x = (row1.x * v.y) + w.x;
1411    w.y = (row1.y * v.y) + w.y;
1412    w.z = (row1.z * v.y) + w.z;
1413    w.w = (row1.w * v.y) + w.w;
1414
1415    w.x = (row2.x * v.z) + w.x;
1416    w.y = (row2.y * v.z) + w.y;
1417    w.z = (row2.z * v.z) + w.z;
1418    w.w = (row2.w * v.z) + w.w;
1419
1420    a = 1.0f / MagickMax(w.x,MagickMax(w.y,w.z));
1421
1422    v.x = w.x * a;
1423    v.y = w.y * a;
1424    v.z = w.z * a;
1425    v.w = w.w * a;
1426  }
1427
1428  VectorCopy43(v,principle);
1429}
1430
1431static void ComputeWeightedCovariance(const size_t count,
1432  const DDSVector4 *points, float *covariance)
1433{
1434  DDSVector3
1435    centroid;
1436
1437  float
1438    total;
1439
1440  size_t
1441    i;
1442
1443  total = 0.0f;
1444  VectorInit3(centroid,0.0f);
1445
1446  for (i=0; i < count; i++)
1447  {
1448    total += points[i].w;
1449    centroid.x += (points[i].x * points[i].w);
1450    centroid.y += (points[i].y * points[i].w);
1451    centroid.z += (points[i].z * points[i].w);
1452  }
1453
1454  if( total > 1.192092896e-07F)
1455    {
1456      centroid.x /= total;
1457      centroid.y /= total;
1458      centroid.z /= total;
1459    }
1460
1461  for (i=0; i < 6; i++)
1462    covariance[i] = 0.0f;
1463
1464  for (i = 0; i < count; i++)
1465  {
1466    DDSVector3
1467      a,
1468      b;
1469
1470    a.x = points[i].x - centroid.x;
1471    a.y = points[i].y - centroid.y;
1472    a.z = points[i].z - centroid.z;
1473
1474    b.x = points[i].w * a.x;
1475    b.y = points[i].w * a.y;
1476    b.z = points[i].w * a.z;
1477
1478    covariance[0] += a.x*b.x;
1479    covariance[1] += a.x*b.y;
1480    covariance[2] += a.x*b.z;
1481    covariance[3] += a.y*b.y;
1482    covariance[4] += a.y*b.z;
1483    covariance[5] += a.z*b.z;
1484  }
1485}
1486
1487static MagickBooleanType ConstructOrdering(const size_t count,
1488  const DDSVector4 *points, const DDSVector3 axis, DDSVector4 *pointsWeights,
1489  DDSVector4 *xSumwSum, unsigned char *order, size_t iteration)
1490{
1491  float
1492     dps[16],
1493     f;
1494
1495  register ssize_t
1496    i;
1497
1498  size_t
1499    j;
1500
1501  unsigned char
1502    c,
1503    *o,
1504    *p;
1505
1506  o = order + (16*iteration);
1507
1508  for (i=0; i < (ssize_t) count; i++)
1509  {
1510    dps[i] = Dot(points[i],axis);
1511    o[i] = (unsigned char)i;
1512  }
1513
1514  for (i=0; i < (ssize_t) count; i++)
1515  {
1516    for (j=i; j > 0 && dps[j] < dps[j - 1]; j--)
1517    {
1518      f = dps[j];
1519      dps[j] = dps[j - 1];
1520      dps[j - 1] = f;
1521
1522      c = o[j];
1523      o[j] = o[j - 1];
1524      o[j - 1] = c;
1525    }
1526  }
1527
1528  for (i=0; i < (ssize_t) iteration; i++)
1529  {
1530    MagickBooleanType
1531      same;
1532
1533    p = order + (16*i);
1534    same = MagickTrue;
1535
1536    for (j=0; j < count; j++)
1537    {
1538      if (o[j] != p[j])
1539        {
1540          same = MagickFalse;
1541          break;
1542        }
1543    }
1544
1545    if (same != MagickFalse)
1546      return MagickFalse;
1547  }
1548
1549  xSumwSum->x = 0;
1550  xSumwSum->y = 0;
1551  xSumwSum->z = 0;
1552  xSumwSum->w = 0;
1553
1554  for (i=0; i < (ssize_t) count; i++)
1555  {
1556    DDSVector4
1557      v;
1558
1559    j = (size_t) o[i];
1560
1561    v.x = points[j].w * points[j].x;
1562    v.y = points[j].w * points[j].y;
1563    v.z = points[j].w * points[j].z;
1564    v.w = points[j].w * 1.0f;
1565
1566    VectorCopy44(v,&pointsWeights[i]);
1567    VectorAdd(*xSumwSum,v,xSumwSum);
1568  }
1569
1570  return MagickTrue;
1571}
1572
1573/*
1574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1575%                                                                             %
1576%                                                                             %
1577%                                                                             %
1578%   I s D D S                                                                 %
1579%                                                                             %
1580%                                                                             %
1581%                                                                             %
1582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1583%
1584%  IsDDS() returns MagickTrue if the image format type, identified by the
1585%  magick string, is DDS.
1586%
1587%  The format of the IsDDS method is:
1588%
1589%      MagickBooleanType IsDDS(const unsigned char *magick,const size_t length)
1590%
1591%  A description of each parameter follows:
1592%
1593%    o magick: compare image format pattern against these bytes.
1594%
1595%    o length: Specifies the length of the magick string.
1596%
1597*/
1598static MagickBooleanType IsDDS(const unsigned char *magick, const size_t length)
1599{
1600  if (length < 4)
1601    return(MagickFalse);
1602  if (LocaleNCompare((char *) magick,"DDS ", 4) == 0)
1603    return(MagickTrue);
1604  return(MagickFalse);
1605}
1606/*
1607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1608%                                                                             %
1609%                                                                             %
1610%                                                                             %
1611%   R e a d D D S I m a g e                                                   %
1612%                                                                             %
1613%                                                                             %
1614%                                                                             %
1615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616%
1617%  ReadDDSImage() reads a DirectDraw Surface image file and returns it.  It
1618%  allocates the memory necessary for the new Image structure and returns a
1619%  pointer to the new image.
1620%
1621%  The format of the ReadDDSImage method is:
1622%
1623%      Image *ReadDDSImage(const ImageInfo *image_info,ExceptionInfo *exception)
1624%
1625%  A description of each parameter follows:
1626%
1627%    o image_info: The image info.
1628%
1629%    o exception: return any errors or warnings in this structure.
1630%
1631*/
1632
1633static Image *ReadDDSImage(const ImageInfo *image_info,ExceptionInfo *exception)
1634{
1635  Image
1636    *image;
1637
1638  MagickBooleanType
1639    status,
1640    cubemap = MagickFalse,
1641    volume = MagickFalse;
1642
1643  CompressionType
1644    compression;
1645
1646  DDSInfo
1647    dds_info;
1648
1649  DDSDecoder
1650    *decoder;
1651
1652  PixelTrait
1653    alpha_trait;
1654
1655  size_t
1656    n,
1657    num_images;
1658
1659  /*
1660    Open image file.
1661  */
1662  assert(image_info != (const ImageInfo *) NULL);
1663  assert(image_info->signature == MagickCoreSignature);
1664  if (image_info->debug != MagickFalse)
1665    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1666      image_info->filename);
1667  assert(exception != (ExceptionInfo *) NULL);
1668  assert(exception->signature == MagickCoreSignature);
1669  image=AcquireImage(image_info,exception);
1670  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1671  if (status == MagickFalse)
1672    {
1673      image=DestroyImageList(image);
1674      return((Image *) NULL);
1675    }
1676
1677  /*
1678    Initialize image structure.
1679  */
1680  if (ReadDDSInfo(image, &dds_info) != MagickTrue) {
1681    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1682  }
1683
1684  if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP)
1685    cubemap = MagickTrue;
1686
1687  if (dds_info.ddscaps2 & DDSCAPS2_VOLUME && dds_info.depth > 0)
1688    volume = MagickTrue;
1689
1690  (void) SeekBlob(image, 128, SEEK_SET);
1691
1692  /*
1693    Determine pixel format
1694  */
1695  if (dds_info.pixelformat.flags & DDPF_RGB)
1696    {
1697      compression = NoCompression;
1698      if (dds_info.pixelformat.flags & DDPF_ALPHAPIXELS)
1699        {
1700          alpha_trait = BlendPixelTrait;
1701          decoder = ReadUncompressedRGBA;
1702        }
1703      else
1704        {
1705          alpha_trait = UndefinedPixelTrait;
1706          decoder = ReadUncompressedRGB;
1707        }
1708    }
1709  else if (dds_info.pixelformat.flags & DDPF_LUMINANCE)
1710   {
1711      compression = NoCompression;
1712      if (dds_info.pixelformat.flags & DDPF_ALPHAPIXELS)
1713        {
1714          /* Not sure how to handle this */
1715          ThrowReaderException(CorruptImageError, "ImageTypeNotSupported");
1716        }
1717      else
1718        {
1719          alpha_trait = UndefinedPixelTrait;
1720          decoder = ReadUncompressedRGB;
1721        }
1722    }
1723  else if (dds_info.pixelformat.flags & DDPF_FOURCC)
1724    {
1725      switch (dds_info.pixelformat.fourcc)
1726      {
1727        case FOURCC_DXT1:
1728        {
1729          alpha_trait = UndefinedPixelTrait;
1730          compression = DXT1Compression;
1731          decoder = ReadDXT1;
1732          break;
1733        }
1734        case FOURCC_DXT3:
1735        {
1736          alpha_trait = BlendPixelTrait;
1737          compression = DXT3Compression;
1738          decoder = ReadDXT3;
1739          break;
1740        }
1741        case FOURCC_DXT5:
1742        {
1743          alpha_trait = BlendPixelTrait;
1744          compression = DXT5Compression;
1745          decoder = ReadDXT5;
1746          break;
1747        }
1748        default:
1749        {
1750          /* Unknown FOURCC */
1751          ThrowReaderException(CorruptImageError, "ImageTypeNotSupported");
1752        }
1753      }
1754    }
1755  else
1756    {
1757      /* Neither compressed nor uncompressed... thus unsupported */
1758      ThrowReaderException(CorruptImageError, "ImageTypeNotSupported");
1759    }
1760
1761  num_images = 1;
1762  if (cubemap)
1763    {
1764      /*
1765        Determine number of faces defined in the cubemap
1766      */
1767      num_images = 0;
1768      if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_POSITIVEX) num_images++;
1769      if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) num_images++;
1770      if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_POSITIVEY) num_images++;
1771      if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) num_images++;
1772      if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) num_images++;
1773      if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) num_images++;
1774    }
1775
1776  if (volume)
1777    num_images = dds_info.depth;
1778
1779  for (n = 0; n < num_images; n++)
1780  {
1781    if (n != 0)
1782      {
1783        /* Start a new image */
1784        AcquireNextImage(image_info,image,exception);
1785        if (GetNextImageInList(image) == (Image *) NULL)
1786          return(DestroyImageList(image));
1787        image=SyncNextImageInList(image);
1788      }
1789
1790    image->alpha_trait=alpha_trait;
1791    image->compression = compression;
1792    image->columns = dds_info.width;
1793    image->rows = dds_info.height;
1794    image->storage_class = DirectClass;
1795    image->endian = LSBEndian;
1796    image->depth = 8;
1797    if (image_info->ping != MagickFalse)
1798      {
1799        (void) CloseBlob(image);
1800        return(GetFirstImageInList(image));
1801      }
1802    status=SetImageExtent(image,image->columns,image->rows,exception);
1803    if (status == MagickFalse)
1804      return(DestroyImageList(image));
1805    if ((decoder)(image, &dds_info, exception) != MagickTrue)
1806      {
1807        (void) CloseBlob(image);
1808        return(GetFirstImageInList(image));
1809      }
1810  }
1811  (void) CloseBlob(image);
1812  return(GetFirstImageInList(image));
1813}
1814
1815static MagickBooleanType ReadDDSInfo(Image *image, DDSInfo *dds_info)
1816{
1817  size_t
1818    hdr_size,
1819    required;
1820
1821  /* Seek to start of header */
1822  (void) SeekBlob(image, 4, SEEK_SET);
1823
1824  /* Check header field */
1825  hdr_size = ReadBlobLSBLong(image);
1826  if (hdr_size != 124)
1827    return MagickFalse;
1828
1829  /* Fill in DDS info struct */
1830  dds_info->flags = ReadBlobLSBLong(image);
1831
1832  /* Check required flags */
1833  required=(size_t) (DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT);
1834  if ((dds_info->flags & required) != required)
1835    return MagickFalse;
1836
1837  dds_info->height = ReadBlobLSBLong(image);
1838  dds_info->width = ReadBlobLSBLong(image);
1839  dds_info->pitchOrLinearSize = ReadBlobLSBLong(image);
1840  dds_info->depth = ReadBlobLSBLong(image);
1841  dds_info->mipmapcount = ReadBlobLSBLong(image);
1842
1843  (void) SeekBlob(image, 44, SEEK_CUR);   /* reserved region of 11 DWORDs */
1844
1845  /* Read pixel format structure */
1846  hdr_size = ReadBlobLSBLong(image);
1847  if (hdr_size != 32)
1848    return MagickFalse;
1849
1850  dds_info->pixelformat.flags = ReadBlobLSBLong(image);
1851  dds_info->pixelformat.fourcc = ReadBlobLSBLong(image);
1852  dds_info->pixelformat.rgb_bitcount = ReadBlobLSBLong(image);
1853  dds_info->pixelformat.r_bitmask = ReadBlobLSBLong(image);
1854  dds_info->pixelformat.g_bitmask = ReadBlobLSBLong(image);
1855  dds_info->pixelformat.b_bitmask = ReadBlobLSBLong(image);
1856  dds_info->pixelformat.alpha_bitmask = ReadBlobLSBLong(image);
1857
1858  dds_info->ddscaps1 = ReadBlobLSBLong(image);
1859  dds_info->ddscaps2 = ReadBlobLSBLong(image);
1860  (void) SeekBlob(image, 12, SEEK_CUR); /* 3 reserved DWORDs */
1861
1862  return MagickTrue;
1863}
1864
1865static MagickBooleanType ReadDXT1(Image *image, DDSInfo *dds_info,
1866  ExceptionInfo *exception)
1867{
1868  DDSColors
1869    colors;
1870
1871  register Quantum
1872    *q;
1873
1874  register ssize_t
1875    i,
1876    x;
1877
1878  size_t
1879    bits;
1880
1881  ssize_t
1882    j,
1883    y;
1884
1885  unsigned char
1886    code;
1887
1888  unsigned short
1889    c0,
1890    c1;
1891
1892  for (y = 0; y < (ssize_t) dds_info->height; y += 4)
1893  {
1894    for (x = 0; x < (ssize_t) dds_info->width; x += 4)
1895    {
1896      /* Get 4x4 patch of pixels to write on */
1897      q = QueueAuthenticPixels(image, x, y, MagickMin(4, dds_info->width - x),
1898        MagickMin(4, dds_info->height - y),exception);
1899
1900      if (q == (Quantum *) NULL)
1901        return MagickFalse;
1902
1903      /* Read 8 bytes of data from the image */
1904      c0 = ReadBlobLSBShort(image);
1905      c1 = ReadBlobLSBShort(image);
1906      bits = ReadBlobLSBLong(image);
1907
1908      CalculateColors(c0, c1, &colors, MagickFalse);
1909
1910      /* Write the pixels */
1911      for (j = 0; j < 4; j++)
1912      {
1913        for (i = 0; i < 4; i++)
1914        {
1915          if ((x + i) < (ssize_t) dds_info->width &&
1916              (y + j) < (ssize_t) dds_info->height)
1917            {
1918              code = (unsigned char) ((bits >> ((j*4+i)*2)) & 0x3);
1919              SetPixelRed(image,ScaleCharToQuantum(colors.r[code]),q);
1920              SetPixelGreen(image,ScaleCharToQuantum(colors.g[code]),q);
1921              SetPixelBlue(image,ScaleCharToQuantum(colors.b[code]),q);
1922              SetPixelAlpha(image,ScaleCharToQuantum(colors.a[code]),q);
1923              if (colors.a[code] && (image->alpha_trait == UndefinedPixelTrait))
1924                image->alpha_trait=BlendPixelTrait;  /* Correct matte */
1925              q+=GetPixelChannels(image);
1926            }
1927        }
1928      }
1929
1930      if (SyncAuthenticPixels(image,exception) == MagickFalse)
1931        return MagickFalse;
1932    }
1933  }
1934
1935  return(SkipDXTMipmaps(image,dds_info,8,exception));
1936}
1937
1938static MagickBooleanType ReadDXT3(Image *image, DDSInfo *dds_info,
1939  ExceptionInfo *exception)
1940{
1941  DDSColors
1942    colors;
1943
1944  register Quantum
1945    *q;
1946
1947  register ssize_t
1948    i,
1949    x;
1950
1951  unsigned char
1952    alpha;
1953
1954  size_t
1955    a0,
1956    a1,
1957    bits,
1958    code;
1959
1960  ssize_t
1961    j,
1962    y;
1963
1964  unsigned short
1965    c0,
1966    c1;
1967
1968  for (y = 0; y < (ssize_t) dds_info->height; y += 4)
1969  {
1970    for (x = 0; x < (ssize_t) dds_info->width; x += 4)
1971    {
1972      /* Get 4x4 patch of pixels to write on */
1973      q = QueueAuthenticPixels(image, x, y, MagickMin(4, dds_info->width - x),
1974                         MagickMin(4, dds_info->height - y),exception);
1975
1976      if (q == (Quantum *) NULL)
1977        return MagickFalse;
1978
1979      /* Read alpha values (8 bytes) */
1980      a0 = ReadBlobLSBLong(image);
1981      a1 = ReadBlobLSBLong(image);
1982
1983      /* Read 8 bytes of data from the image */
1984      c0 = ReadBlobLSBShort(image);
1985      c1 = ReadBlobLSBShort(image);
1986      bits = ReadBlobLSBLong(image);
1987
1988      CalculateColors(c0, c1, &colors, MagickTrue);
1989
1990      /* Write the pixels */
1991      for (j = 0; j < 4; j++)
1992      {
1993        for (i = 0; i < 4; i++)
1994        {
1995          if ((x + i) < (ssize_t) dds_info->width && (y + j) < (ssize_t) dds_info->height)
1996            {
1997              code = (bits >> ((4*j+i)*2)) & 0x3;
1998              SetPixelRed(image,ScaleCharToQuantum(colors.r[code]),q);
1999              SetPixelGreen(image,ScaleCharToQuantum(colors.g[code]),q);
2000              SetPixelBlue(image,ScaleCharToQuantum(colors.b[code]),q);
2001              /*
2002                Extract alpha value: multiply 0..15 by 17 to get range 0..255
2003              */
2004              if (j < 2)
2005                alpha = 17U * (unsigned char) ((a0 >> (4*(4*j+i))) & 0xf);
2006              else
2007                alpha = 17U * (unsigned char) ((a1 >> (4*(4*(j-2)+i))) & 0xf);
2008              SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) alpha),q);
2009              q+=GetPixelChannels(image);
2010            }
2011        }
2012      }
2013
2014      if (SyncAuthenticPixels(image,exception) == MagickFalse)
2015        return MagickFalse;
2016    }
2017  }
2018
2019  return(SkipDXTMipmaps(image,dds_info,16,exception));
2020}
2021
2022static MagickBooleanType ReadDXT5(Image *image, DDSInfo *dds_info,
2023  ExceptionInfo *exception)
2024{
2025  DDSColors
2026    colors;
2027
2028  MagickSizeType
2029    alpha_bits;
2030
2031  register Quantum
2032    *q;
2033
2034  register ssize_t
2035    i,
2036    x;
2037
2038  unsigned char
2039    a0,
2040    a1;
2041
2042  size_t
2043    alpha,
2044    bits,
2045    code,
2046    alpha_code;
2047
2048  ssize_t
2049    j,
2050    y;
2051
2052  unsigned short
2053    c0,
2054    c1;
2055
2056  for (y = 0; y < (ssize_t) dds_info->height; y += 4)
2057  {
2058    for (x = 0; x < (ssize_t) dds_info->width; x += 4)
2059    {
2060      /* Get 4x4 patch of pixels to write on */
2061      q = QueueAuthenticPixels(image, x, y, MagickMin(4, dds_info->width - x),
2062                         MagickMin(4, dds_info->height - y),exception);
2063
2064      if (q == (Quantum *) NULL)
2065        return MagickFalse;
2066
2067      /* Read alpha values (8 bytes) */
2068      a0 = (unsigned char) ReadBlobByte(image);
2069      a1 = (unsigned char) ReadBlobByte(image);
2070
2071      alpha_bits = (MagickSizeType)ReadBlobLSBLong(image);
2072      alpha_bits = alpha_bits | ((MagickSizeType)ReadBlobLSBShort(image) << 32);
2073
2074      /* Read 8 bytes of data from the image */
2075      c0 = ReadBlobLSBShort(image);
2076      c1 = ReadBlobLSBShort(image);
2077      bits = ReadBlobLSBLong(image);
2078
2079      CalculateColors(c0, c1, &colors, MagickTrue);
2080
2081      /* Write the pixels */
2082      for (j = 0; j < 4; j++)
2083      {
2084        for (i = 0; i < 4; i++)
2085        {
2086          if ((x + i) < (ssize_t) dds_info->width &&
2087              (y + j) < (ssize_t) dds_info->height)
2088            {
2089              code = (bits >> ((4*j+i)*2)) & 0x3;
2090              SetPixelRed(image,ScaleCharToQuantum(colors.r[code]),q);
2091              SetPixelGreen(image,ScaleCharToQuantum(colors.g[code]),q);
2092              SetPixelBlue(image,ScaleCharToQuantum(colors.b[code]),q);
2093              /* Extract alpha value */
2094              alpha_code = (size_t) (alpha_bits >> (3*(4*j+i))) & 0x7;
2095              if (alpha_code == 0)
2096                alpha = a0;
2097              else if (alpha_code == 1)
2098                alpha = a1;
2099              else if (a0 > a1)
2100                alpha = ((8-alpha_code) * a0 + (alpha_code-1) * a1) / 7;
2101              else if (alpha_code == 6)
2102                alpha = 0;
2103              else if (alpha_code == 7)
2104                alpha = 255;
2105              else
2106                alpha = (((6-alpha_code) * a0 + (alpha_code-1) * a1) / 5);
2107              SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) alpha),q);
2108              q+=GetPixelChannels(image);
2109            }
2110        }
2111      }
2112
2113      if (SyncAuthenticPixels(image,exception) == MagickFalse)
2114        return MagickFalse;
2115    }
2116  }
2117
2118  return(SkipDXTMipmaps(image,dds_info,16,exception));
2119}
2120
2121static MagickBooleanType ReadUncompressedRGB(Image *image, DDSInfo *dds_info,
2122  ExceptionInfo *exception)
2123{
2124  register Quantum
2125    *q;
2126
2127  ssize_t
2128    x, y;
2129
2130  unsigned short
2131    color;
2132
2133  if (dds_info->pixelformat.rgb_bitcount == 8)
2134    (void) SetImageType(image,GrayscaleType,exception);
2135  else if (dds_info->pixelformat.rgb_bitcount == 16 && !IsBitMask(
2136    dds_info->pixelformat,0xf800,0x07e0,0x001f,0x0000))
2137    ThrowBinaryException(CorruptImageError,"ImageTypeNotSupported",
2138      image->filename);
2139
2140  for (y = 0; y < (ssize_t) dds_info->height; y++)
2141  {
2142    q = QueueAuthenticPixels(image, 0, y, dds_info->width, 1,exception);
2143
2144    if (q == (Quantum *) NULL)
2145      return MagickFalse;
2146
2147    for (x = 0; x < (ssize_t) dds_info->width; x++)
2148    {
2149      if (dds_info->pixelformat.rgb_bitcount == 8)
2150        SetPixelGray(image,ScaleCharToQuantum(ReadBlobByte(image)),q);
2151      else if (dds_info->pixelformat.rgb_bitcount == 16)
2152        {
2153           color=ReadBlobShort(image);
2154           SetPixelRed(image,ScaleCharToQuantum((unsigned char)
2155             (((color >> 11)/31.0)*255)),q);
2156           SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
2157             ((((unsigned short)(color << 5) >> 10)/63.0)*255)),q);
2158           SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
2159             ((((unsigned short)(color << 11) >> 11)/31.0)*255)),q);
2160        }
2161      else
2162        {
2163          SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
2164            ReadBlobByte(image)),q);
2165          SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
2166            ReadBlobByte(image)),q);
2167          SetPixelRed(image,ScaleCharToQuantum((unsigned char)
2168            ReadBlobByte(image)),q);
2169          if (dds_info->pixelformat.rgb_bitcount == 32)
2170            (void) ReadBlobByte(image);
2171        }
2172      q+=GetPixelChannels(image);
2173    }
2174
2175    if (SyncAuthenticPixels(image,exception) == MagickFalse)
2176      return MagickFalse;
2177  }
2178
2179  return(SkipRGBMipmaps(image,dds_info,3,exception));
2180}
2181
2182static MagickBooleanType ReadUncompressedRGBA(Image *image, DDSInfo *dds_info,
2183  ExceptionInfo *exception)
2184{
2185  register Quantum
2186    *q;
2187
2188  ssize_t
2189    alphaBits,
2190    x,
2191    y;
2192
2193  unsigned short
2194    color;
2195
2196  alphaBits=0;
2197  if (dds_info->pixelformat.rgb_bitcount == 16)
2198    {
2199      if (IsBitMask(dds_info->pixelformat,0x7c00,0x03e0,0x001f,0x8000))
2200        alphaBits=1;
2201      else if (IsBitMask(dds_info->pixelformat,0x00ff,0x00ff,0x00ff,0xff00))
2202        {
2203          alphaBits=2;
2204          (void) SetImageType(image,GrayscaleAlphaType,exception);
2205        }
2206      else if (IsBitMask(dds_info->pixelformat,0x0f00,0x00f0,0x000f,0xf000))
2207        alphaBits=4;
2208      else
2209        ThrowBinaryException(CorruptImageError,"ImageTypeNotSupported",
2210          image->filename);
2211    }
2212
2213  for (y = 0; y < (ssize_t) dds_info->height; y++)
2214  {
2215    q = QueueAuthenticPixels(image, 0, y, dds_info->width, 1,exception);
2216
2217    if (q == (Quantum *) NULL)
2218      return MagickFalse;
2219
2220    for (x = 0; x < (ssize_t) dds_info->width; x++)
2221    {
2222      if (dds_info->pixelformat.rgb_bitcount == 16)
2223        {
2224           color=ReadBlobShort(image);
2225           if (alphaBits == 1)
2226             {
2227               SetPixelAlpha(image,(color & (1 << 15)) ? QuantumRange : 0,q);
2228               SetPixelRed(image,ScaleCharToQuantum((unsigned char)
2229                 ((((unsigned short)(color << 1) >> 11)/31.0)*255)),q);
2230               SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
2231                 ((((unsigned short)(color << 6) >> 11)/31.0)*255)),q);
2232               SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
2233                 ((((unsigned short)(color << 11) >> 11)/31.0)*255)),q);
2234             }
2235          else if (alphaBits == 2)
2236            {
2237               SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
2238                 (color >> 8)),q);
2239               SetPixelGray(image,ScaleCharToQuantum((unsigned char)color),q);
2240            }
2241          else
2242            {
2243               SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
2244                 (((color >> 12)/15.0)*255)),q);
2245               SetPixelRed(image,ScaleCharToQuantum((unsigned char)
2246                 ((((unsigned short)(color << 4) >> 12)/15.0)*255)),q);
2247               SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
2248                 ((((unsigned short)(color << 8) >> 12)/15.0)*255)),q);
2249               SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
2250                 ((((unsigned short)(color << 12) >> 12)/15.0)*255)),q);
2251            }
2252        }
2253      else
2254        {
2255          SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
2256            ReadBlobByte(image)),q);
2257          SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
2258            ReadBlobByte(image)),q);
2259          SetPixelRed(image,ScaleCharToQuantum((unsigned char)
2260            ReadBlobByte(image)),q);
2261          SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
2262            ReadBlobByte(image)),q);
2263        }
2264      q+=GetPixelChannels(image);
2265    }
2266
2267    if (SyncAuthenticPixels(image,exception) == MagickFalse)
2268      return MagickFalse;
2269  }
2270
2271  return(SkipRGBMipmaps(image,dds_info,4,exception));
2272}
2273
2274/*
2275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2276%                                                                             %
2277%                                                                             %
2278%                                                                             %
2279%   R e g i s t e r D D S I m a g e                                           %
2280%                                                                             %
2281%                                                                             %
2282%                                                                             %
2283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2284%
2285%  RegisterDDSImage() adds attributes for the DDS image format to
2286%  the list of supported formats.  The attributes include the image format
2287%  tag, a method to read and/or write the format, whether the format
2288%  supports the saving of more than one frame to the same file or blob,
2289%  whether the format supports native in-memory I/O, and a brief
2290%  description of the format.
2291%
2292%  The format of the RegisterDDSImage method is:
2293%
2294%      RegisterDDSImage(void)
2295%
2296*/
2297ModuleExport size_t RegisterDDSImage(void)
2298{
2299  MagickInfo
2300    *entry;
2301
2302  entry = AcquireMagickInfo("DDS","DDS","Microsoft DirectDraw Surface");
2303  entry->decoder = (DecodeImageHandler *) ReadDDSImage;
2304  entry->encoder = (EncodeImageHandler *) WriteDDSImage;
2305  entry->magick = (IsImageFormatHandler *) IsDDS;
2306  entry->flags|=CoderSeekableStreamFlag;
2307  (void) RegisterMagickInfo(entry);
2308  entry = AcquireMagickInfo("DDS","DXT1","Microsoft DirectDraw Surface");
2309  entry->decoder = (DecodeImageHandler *) ReadDDSImage;
2310  entry->encoder = (EncodeImageHandler *) WriteDDSImage;
2311  entry->magick = (IsImageFormatHandler *) IsDDS;
2312  entry->flags|=CoderSeekableStreamFlag;
2313  (void) RegisterMagickInfo(entry);
2314  entry = AcquireMagickInfo("DDS","DXT5","Microsoft DirectDraw Surface");
2315  entry->decoder = (DecodeImageHandler *) ReadDDSImage;
2316  entry->encoder = (EncodeImageHandler *) WriteDDSImage;
2317  entry->magick = (IsImageFormatHandler *) IsDDS;
2318  entry->flags|=CoderSeekableStreamFlag;
2319  (void) RegisterMagickInfo(entry);
2320  return(MagickImageCoderSignature);
2321}
2322
2323static void RemapIndices(const ssize_t *map, const unsigned char *source,
2324  unsigned char *target)
2325{
2326  register ssize_t
2327    i;
2328
2329  for (i = 0; i < 16; i++)
2330  {
2331    if (map[i] == -1)
2332      target[i] = 3;
2333    else
2334      target[i] = source[map[i]];
2335  }
2336}
2337
2338/*
2339  Skip the mipmap images for compressed (DXTn) dds files
2340*/
2341static MagickBooleanType SkipDXTMipmaps(Image *image,DDSInfo *dds_info,
2342  int texel_size,ExceptionInfo *exception)
2343{
2344  MagickOffsetType
2345    offset;
2346
2347  register ssize_t
2348    i;
2349
2350  size_t
2351    h,
2352    w;
2353
2354  /*
2355    Only skip mipmaps for textures and cube maps
2356  */
2357  if (EOFBlob(image) != MagickFalse)
2358    {
2359      ThrowFileException(exception,CorruptImageWarning,"UnexpectedEndOfFile",
2360        image->filename);
2361      return(MagickFalse);
2362    }
2363  if (dds_info->ddscaps1 & DDSCAPS_MIPMAP
2364      && (dds_info->ddscaps1 & DDSCAPS_TEXTURE
2365          || dds_info->ddscaps2 & DDSCAPS2_CUBEMAP))
2366    {
2367      w = DIV2(dds_info->width);
2368      h = DIV2(dds_info->height);
2369
2370      /*
2371        Mipmapcount includes the main image, so start from one
2372      */
2373      for (i = 1; (i < (ssize_t) dds_info->mipmapcount) && w && h; i++)
2374      {
2375        offset = (MagickOffsetType) ((w + 3) / 4) * ((h + 3) / 4) * texel_size;
2376        if (SeekBlob(image, offset, SEEK_CUR) < 0)
2377          break;
2378        w = DIV2(w);
2379        h = DIV2(h);
2380      }
2381    }
2382  return(MagickTrue);
2383}
2384
2385/*
2386  Skip the mipmap images for uncompressed (RGB or RGBA) dds files
2387*/
2388static MagickBooleanType SkipRGBMipmaps(Image *image,DDSInfo *dds_info,
2389  int pixel_size,ExceptionInfo *exception)
2390{
2391  MagickOffsetType
2392    offset;
2393
2394  register ssize_t
2395    i;
2396
2397  size_t
2398    h,
2399    w;
2400
2401  /*
2402    Only skip mipmaps for textures and cube maps
2403  */
2404  if (EOFBlob(image) != MagickFalse)
2405    {
2406      ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
2407        image->filename);
2408      return(MagickFalse);
2409    }
2410  if (dds_info->ddscaps1 & DDSCAPS_MIPMAP
2411      && (dds_info->ddscaps1 & DDSCAPS_TEXTURE
2412          || dds_info->ddscaps2 & DDSCAPS2_CUBEMAP))
2413    {
2414      w = DIV2(dds_info->width);
2415      h = DIV2(dds_info->height);
2416
2417      /*
2418        Mipmapcount includes the main image, so start from one
2419      */
2420      for (i=1; (i < (ssize_t) dds_info->mipmapcount) && w && h; i++)
2421      {
2422        offset = (MagickOffsetType) w * h * pixel_size;
2423        if (SeekBlob(image, offset, SEEK_CUR) < 0)
2424          break;
2425        w = DIV2(w);
2426        h = DIV2(h);
2427      }
2428    }
2429  return(MagickTrue);
2430}
2431
2432/*
2433%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2434%                                                                             %
2435%                                                                             %
2436%                                                                             %
2437%   U n r e g i s t e r D D S I m a g e                                       %
2438%                                                                             %
2439%                                                                             %
2440%                                                                             %
2441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2442%
2443%  UnregisterDDSImage() removes format registrations made by the
2444%  DDS module from the list of supported formats.
2445%
2446%  The format of the UnregisterDDSImage method is:
2447%
2448%      UnregisterDDSImage(void)
2449%
2450*/
2451ModuleExport void UnregisterDDSImage(void)
2452{
2453  (void) UnregisterMagickInfo("DDS");
2454  (void) UnregisterMagickInfo("DXT1");
2455  (void) UnregisterMagickInfo("DXT5");
2456}
2457
2458static void WriteAlphas(Image *image, const ssize_t *alphas, size_t min5,
2459  size_t max5, size_t min7, size_t max7)
2460{
2461  register ssize_t
2462    i;
2463
2464  size_t
2465    err5,
2466    err7,
2467    j;
2468
2469  unsigned char
2470    indices5[16],
2471    indices7[16];
2472
2473  FixRange(min5,max5,5);
2474  err5 = CompressAlpha(min5,max5,5,alphas,indices5);
2475
2476  FixRange(min7,max7,7);
2477  err7 = CompressAlpha(min7,max7,7,alphas,indices7);
2478
2479  if (err7 < err5)
2480  {
2481    for (i=0; i < 16; i++)
2482    {
2483      unsigned char
2484        index;
2485
2486      index = indices7[i];
2487      if( index == 0 )
2488        indices5[i] = 1;
2489      else if (index == 1)
2490        indices5[i] = 0;
2491      else
2492        indices5[i] = 9 - index;
2493    }
2494
2495    min5 = max7;
2496    max5 = min7;
2497  }
2498
2499  (void) WriteBlobByte(image,(unsigned char) min5);
2500  (void) WriteBlobByte(image,(unsigned char) max5);
2501
2502  for(i=0; i < 2; i++)
2503  {
2504    size_t
2505      value = 0;
2506
2507    for (j=0; j < 8; j++)
2508    {
2509      size_t index = (size_t) indices5[j + i*8];
2510      value |= ( index << 3*j );
2511    }
2512
2513    for (j=0; j < 3; j++)
2514    {
2515      size_t byte = (value >> 8*j) & 0xff;
2516      (void) WriteBlobByte(image,(unsigned char) byte);
2517    }
2518  }
2519}
2520
2521static void WriteCompressed(Image *image, const size_t count,
2522  DDSVector4 *points, const ssize_t *map, const MagickBooleanType clusterFit)
2523{
2524  float
2525    covariance[16];
2526
2527  DDSVector3
2528    end,
2529    principle,
2530    start;
2531
2532  DDSVector4
2533    metric;
2534
2535  unsigned char
2536    indices[16];
2537
2538  VectorInit(metric,1.0f);
2539  VectorInit3(start,0.0f);
2540  VectorInit3(end,0.0f);
2541
2542  ComputeWeightedCovariance(count,points,covariance);
2543  ComputePrincipleComponent(covariance,&principle);
2544
2545  if ((clusterFit == MagickFalse) || (count == 0))
2546    CompressRangeFit(count,points,map,principle,metric,&start,&end,indices);
2547  else
2548    CompressClusterFit(count,points,map,principle,metric,&start,&end,indices);
2549
2550  WriteIndices(image,start,end,indices);
2551}
2552
2553/*
2554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2555%                                                                             %
2556%                                                                             %
2557%                                                                             %
2558%   W r i t e D D S I m a g e                                                 %
2559%                                                                             %
2560%                                                                             %
2561%                                                                             %
2562%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2563%
2564%  WriteDDSImage() writes a DirectDraw Surface image file in the DXT5 format.
2565%
2566%  The format of the WriteBMPImage method is:
2567%
2568%     MagickBooleanType WriteDDSImage(const ImageInfo *image_info,Image *image)
2569%
2570%  A description of each parameter follows.
2571%
2572%    o image_info: the image info.
2573%
2574%    o image:  The image.
2575%
2576*/
2577static MagickBooleanType WriteDDSImage(const ImageInfo *image_info,
2578  Image *image, ExceptionInfo *exception)
2579{
2580  const char
2581    *option;
2582
2583  size_t
2584    compression,
2585    columns,
2586    maxMipmaps,
2587    mipmaps,
2588    pixelFormat,
2589    rows;
2590
2591  MagickBooleanType
2592    clusterFit,
2593    status,
2594    weightByAlpha;
2595
2596  assert(image_info != (const ImageInfo *) NULL);
2597  assert(image_info->signature == MagickCoreSignature);
2598  assert(image != (Image *) NULL);
2599  assert(image->signature == MagickCoreSignature);
2600  if (image->debug != MagickFalse)
2601    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2602  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2603  if (status == MagickFalse)
2604    return(status);
2605  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2606  pixelFormat=DDPF_FOURCC;
2607  compression=FOURCC_DXT5;
2608
2609  if (image->alpha_trait == UndefinedPixelTrait)
2610    compression=FOURCC_DXT1;
2611
2612  if (LocaleCompare(image_info->magick,"dxt1") == 0)
2613    compression=FOURCC_DXT1;
2614
2615  option=GetImageOption(image_info,"dds:compression");
2616  if (option != (char *) NULL)
2617    {
2618       if (LocaleCompare(option,"dxt1") == 0)
2619         compression=FOURCC_DXT1;
2620       if (LocaleCompare(option,"none") == 0)
2621         pixelFormat=DDPF_RGB;
2622    }
2623
2624  clusterFit=MagickFalse;
2625  weightByAlpha=MagickFalse;
2626
2627  if (pixelFormat == DDPF_FOURCC)
2628    {
2629      option=GetImageOption(image_info,"dds:cluster-fit");
2630      if (IsStringTrue(option) != MagickFalse)
2631        {
2632          clusterFit=MagickTrue;
2633          if (compression != FOURCC_DXT1)
2634            {
2635              option=GetImageOption(image_info,"dds:weight-by-alpha");
2636              if (IsStringTrue(option) != MagickFalse)
2637                weightByAlpha=MagickTrue;
2638            }
2639        }
2640    }
2641
2642  maxMipmaps=SIZE_MAX;
2643  mipmaps=0;
2644  if ((image->columns & (image->columns - 1)) == 0 &&
2645      (image->rows & (image->rows - 1)) == 0)
2646    {
2647      option=GetImageOption(image_info,"dds:mipmaps");
2648      if (option != (char *) NULL)
2649        maxMipmaps=StringToUnsignedLong(option);
2650
2651      if (maxMipmaps != 0)
2652        {
2653          columns=image->columns;
2654          rows=image->rows;
2655          while (columns != 1 && rows != 1 && mipmaps != maxMipmaps)
2656          {
2657            columns=DIV2(columns);
2658            rows=DIV2(rows);
2659            mipmaps++;
2660          }
2661        }
2662    }
2663
2664  WriteDDSInfo(image,pixelFormat,compression,mipmaps);
2665
2666  WriteImageData(image,pixelFormat,compression,clusterFit,weightByAlpha,
2667    exception);
2668
2669  if (mipmaps > 0 && WriteMipmaps(image,pixelFormat,compression,mipmaps,
2670        clusterFit,weightByAlpha,exception) == MagickFalse)
2671    return(MagickFalse);
2672
2673  (void) CloseBlob(image);
2674  return(MagickTrue);
2675}
2676
2677static void WriteDDSInfo(Image *image, const size_t pixelFormat,
2678  const size_t compression, const size_t mipmaps)
2679{
2680  char
2681    software[MagickPathExtent];
2682
2683  register ssize_t
2684    i;
2685
2686  unsigned int
2687    format,
2688    caps,
2689    flags;
2690
2691  flags=(unsigned int) (DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT |
2692    DDSD_PIXELFORMAT);
2693  caps=(unsigned int) DDSCAPS_TEXTURE;
2694  format=(unsigned int) pixelFormat;
2695
2696  if (format == DDPF_FOURCC)
2697      flags=flags | DDSD_LINEARSIZE;
2698  else
2699      flags=flags | DDSD_PITCH;
2700
2701  if (mipmaps > 0)
2702    {
2703      flags=flags | (unsigned int) DDSD_MIPMAPCOUNT;
2704      caps=caps | (unsigned int) (DDSCAPS_MIPMAP | DDSCAPS_COMPLEX);
2705    }
2706
2707  if (format != DDPF_FOURCC && image->alpha_trait != UndefinedPixelTrait)
2708    format=format | DDPF_ALPHAPIXELS;
2709
2710  (void) WriteBlob(image,4,(unsigned char *) "DDS ");
2711  (void) WriteBlobLSBLong(image,124);
2712  (void) WriteBlobLSBLong(image,flags);
2713  (void) WriteBlobLSBLong(image,(unsigned int) image->rows);
2714  (void) WriteBlobLSBLong(image,(unsigned int) image->columns);
2715
2716  if (pixelFormat == DDPF_FOURCC)
2717    {
2718      /* Compressed DDS requires linear compressed size of first image */
2719      if (compression == FOURCC_DXT1)
2720        (void) WriteBlobLSBLong(image,(unsigned int) (MagickMax(1,
2721          (image->columns+3)/4)*MagickMax(1,(image->rows+3)/4)*8));
2722      else /* DXT5 */
2723        (void) WriteBlobLSBLong(image,(unsigned int) (MagickMax(1,
2724          (image->columns+3)/4)*MagickMax(1,(image->rows+3)/4)*16));
2725    }
2726  else
2727    {
2728      /* Uncompressed DDS requires byte pitch of first image */
2729      if (image->alpha_trait != UndefinedPixelTrait)
2730        (void) WriteBlobLSBLong(image,(unsigned int) (image->columns * 4));
2731      else
2732        (void) WriteBlobLSBLong(image,(unsigned int) (image->columns * 3));
2733    }
2734
2735  (void) WriteBlobLSBLong(image,0x00);
2736  (void) WriteBlobLSBLong(image,(unsigned int) mipmaps+1);
2737  (void) ResetMagickMemory(software,0,sizeof(software));
2738  (void) CopyMagickString(software,"IMAGEMAGICK",MagickPathExtent);
2739  (void) WriteBlob(image,44,(unsigned char *) software);
2740
2741  (void) WriteBlobLSBLong(image,32);
2742  (void) WriteBlobLSBLong(image,format);
2743
2744  if (pixelFormat == DDPF_FOURCC)
2745    {
2746      (void) WriteBlobLSBLong(image,(unsigned int) compression);
2747      for(i=0;i < 5;i++) // bitcount / masks
2748        (void) WriteBlobLSBLong(image,0x00);
2749    }
2750  else
2751    {
2752      (void) WriteBlobLSBLong(image,0x00);
2753      if (image->alpha_trait != UndefinedPixelTrait)
2754        {
2755          (void) WriteBlobLSBLong(image,32);
2756          (void) WriteBlobLSBLong(image,0xff0000);
2757          (void) WriteBlobLSBLong(image,0xff00);
2758          (void) WriteBlobLSBLong(image,0xff);
2759          (void) WriteBlobLSBLong(image,0xff000000);
2760        }
2761      else
2762        {
2763          (void) WriteBlobLSBLong(image,24);
2764          (void) WriteBlobLSBLong(image,0xff0000);
2765          (void) WriteBlobLSBLong(image,0xff00);
2766          (void) WriteBlobLSBLong(image,0xff);
2767          (void) WriteBlobLSBLong(image,0x00);
2768        }
2769    }
2770
2771  (void) WriteBlobLSBLong(image,caps);
2772  for(i=0;i < 4;i++) // ddscaps2 + reserved region
2773    (void) WriteBlobLSBLong(image,0x00);
2774}
2775
2776static void WriteFourCC(Image *image, const size_t compression,
2777  const MagickBooleanType clusterFit, const MagickBooleanType weightByAlpha,
2778  ExceptionInfo *exception)
2779{
2780  register ssize_t
2781    x;
2782
2783  ssize_t
2784    i,
2785    y,
2786    bx,
2787    by;
2788
2789  register const Quantum
2790    *p;
2791
2792  for (y=0; y < (ssize_t) image->rows; y+=4)
2793  {
2794    for (x=0; x < (ssize_t) image->columns; x+=4)
2795    {
2796      MagickBooleanType
2797        match;
2798
2799      DDSVector4
2800        point,
2801        points[16];
2802
2803      size_t
2804        count = 0,
2805        max5 = 0,
2806        max7 = 0,
2807        min5 = 255,
2808        min7 = 255,
2809        columns = 4,
2810        rows = 4;
2811
2812      ssize_t
2813        alphas[16],
2814        map[16];
2815
2816      unsigned char
2817        alpha;
2818
2819      if (x + columns >= image->columns)
2820        columns = image->columns - x;
2821
2822      if (y + rows >= image->rows)
2823        rows = image->rows - y;
2824
2825      p=GetVirtualPixels(image,x,y,columns,rows,exception);
2826      if (p == (const Quantum *) NULL)
2827        break;
2828
2829      for (i=0; i<16; i++)
2830      {
2831        map[i] = -1;
2832        alphas[i] = -1;
2833      }
2834
2835      for (by=0; by < (ssize_t) rows; by++)
2836      {
2837        for (bx=0; bx < (ssize_t) columns; bx++)
2838        {
2839          if (compression == FOURCC_DXT5)
2840            alpha = ScaleQuantumToChar(GetPixelAlpha(image,p));
2841          else
2842            alpha = 255;
2843
2844          if (compression == FOURCC_DXT5)
2845            {
2846              if (alpha < min7)
2847                min7 = alpha;
2848              if (alpha > max7)
2849                max7 = alpha;
2850              if (alpha != 0 && alpha < min5)
2851                min5 = alpha;
2852              if (alpha != 255 && alpha > max5)
2853                max5 = alpha;
2854            }
2855
2856          alphas[4*by + bx] = (size_t)alpha;
2857
2858          point.x = (float)ScaleQuantumToChar(GetPixelRed(image,p)) / 255.0f;
2859          point.y = (float)ScaleQuantumToChar(GetPixelGreen(image,p)) / 255.0f;
2860          point.z = (float)ScaleQuantumToChar(GetPixelBlue(image,p)) / 255.0f;
2861          point.w = weightByAlpha ? (float)(alpha + 1) / 256.0f : 1.0f;
2862          p+=GetPixelChannels(image);
2863
2864          match = MagickFalse;
2865          for (i=0; i < (ssize_t) count; i++)
2866          {
2867            if ((points[i].x == point.x) &&
2868                (points[i].y == point.y) &&
2869                (points[i].z == point.z) &&
2870                (alpha       >= 128 || compression == FOURCC_DXT5))
2871              {
2872                points[i].w += point.w;
2873                map[4*by + bx] = i;
2874                match = MagickTrue;
2875                break;
2876              }
2877          }
2878
2879          if (match != MagickFalse)
2880            continue;
2881
2882          points[count].x = point.x;
2883          points[count].y = point.y;
2884          points[count].z = point.z;
2885          points[count].w = point.w;
2886          map[4*by + bx] = count;
2887          count++;
2888        }
2889      }
2890
2891      for (i=0; i <  (ssize_t) count; i++)
2892        points[i].w = sqrt(points[i].w);
2893
2894      if (compression == FOURCC_DXT5)
2895        WriteAlphas(image,alphas,min5,max5,min7,max7);
2896
2897      if (count == 1)
2898        WriteSingleColorFit(image,points,map);
2899      else
2900        WriteCompressed(image,count,points,map,clusterFit);
2901    }
2902  }
2903}
2904
2905static void WriteImageData(Image *image, const size_t pixelFormat,
2906  const size_t compression,const MagickBooleanType clusterFit,
2907  const MagickBooleanType weightByAlpha, ExceptionInfo *exception)
2908{
2909  if (pixelFormat == DDPF_FOURCC)
2910    WriteFourCC(image,compression,clusterFit,weightByAlpha,exception);
2911  else
2912    WriteUncompressed(image,exception);
2913}
2914
2915static inline size_t ClampToLimit(const float value, const size_t limit)
2916{
2917  size_t
2918    result = (int) (value + 0.5f);
2919
2920  if (result < 0.0f)
2921    return(0);
2922  if (result > limit)
2923    return(limit);
2924  return result;
2925}
2926
2927static inline size_t ColorTo565(const DDSVector3 point)
2928{
2929  size_t r = ClampToLimit(31.0f*point.x,31);
2930  size_t g = ClampToLimit(63.0f*point.y,63);
2931  size_t b = ClampToLimit(31.0f*point.z,31);
2932
2933  return (r << 11) | (g << 5) | b;
2934}
2935
2936static void WriteIndices(Image *image, const DDSVector3 start,
2937  const DDSVector3 end, unsigned char *indices)
2938{
2939  register ssize_t
2940    i;
2941
2942  size_t
2943    a,
2944    b;
2945
2946  unsigned char
2947    remapped[16];
2948
2949  const unsigned char
2950    *ind;
2951
2952  a = ColorTo565(start);
2953  b = ColorTo565(end);
2954
2955  for (i=0; i<16; i++)
2956  {
2957    if( a < b )
2958      remapped[i] = (indices[i] ^ 0x1) & 0x3;
2959    else if( a == b )
2960      remapped[i] = 0;
2961    else
2962      remapped[i] = indices[i];
2963  }
2964
2965  if( a < b )
2966    Swap(a,b);
2967
2968  (void) WriteBlobByte(image,(unsigned char) (a & 0xff));
2969  (void) WriteBlobByte(image,(unsigned char) (a >> 8));
2970  (void) WriteBlobByte(image,(unsigned char) (b & 0xff));
2971  (void) WriteBlobByte(image,(unsigned char) (b >> 8));
2972
2973  for (i=0; i<4; i++)
2974  {
2975     ind = remapped + 4*i;
2976     (void) WriteBlobByte(image,ind[0] | (ind[1] << 2) | (ind[2] << 4) |
2977       (ind[3] << 6));
2978  }
2979}
2980
2981static MagickBooleanType WriteMipmaps(Image *image, const size_t pixelFormat,
2982  const size_t compression, const size_t mipmaps,
2983  const MagickBooleanType clusterFit, const MagickBooleanType weightByAlpha,
2984  ExceptionInfo *exception)
2985{
2986  Image*
2987    resize_image;
2988
2989  register ssize_t
2990    i;
2991
2992  size_t
2993    columns,
2994    rows;
2995
2996  columns = image->columns;
2997  rows = image->rows;
2998
2999  for (i=0; i< (ssize_t) mipmaps; i++)
3000  {
3001    resize_image = ResizeImage(image,columns/2,rows/2,TriangleFilter,
3002      exception);
3003
3004    if (resize_image == (Image *) NULL)
3005      return(MagickFalse);
3006
3007    DestroyBlob(resize_image);
3008    resize_image->blob=ReferenceBlob(image->blob);
3009
3010    WriteImageData(resize_image,pixelFormat,compression,weightByAlpha,
3011      clusterFit,exception);
3012
3013    resize_image=DestroyImage(resize_image);
3014
3015    columns = DIV2(columns);
3016    rows = DIV2(rows);
3017  }
3018
3019  return(MagickTrue);
3020}
3021
3022static void WriteSingleColorFit(Image *image, const DDSVector4 *points,
3023  const ssize_t *map)
3024{
3025  DDSVector3
3026    start,
3027    end;
3028
3029  register ssize_t
3030    i;
3031
3032  unsigned char
3033    color[3],
3034    index,
3035    indexes[16],
3036    indices[16];
3037
3038  color[0] = (unsigned char) ClampToLimit(255.0f*points->x,255);
3039  color[1] = (unsigned char) ClampToLimit(255.0f*points->y,255);
3040  color[2] = (unsigned char) ClampToLimit(255.0f*points->z,255);
3041
3042  index=0;
3043  ComputeEndPoints(DDS_LOOKUP,color,&start,&end,&index);
3044
3045  for (i=0; i< 16; i++)
3046    indexes[i]=index;
3047  RemapIndices(map,indexes,indices);
3048  WriteIndices(image,start,end,indices);
3049}
3050
3051static void WriteUncompressed(Image *image, ExceptionInfo *exception)
3052{
3053  register const Quantum
3054    *p;
3055
3056  register ssize_t
3057    x;
3058
3059  ssize_t
3060    y;
3061
3062  for (y=0; y < (ssize_t) image->rows; y++)
3063  {
3064    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3065    if (p == (const Quantum *) NULL)
3066      break;
3067
3068    for (x=0; x < (ssize_t) image->columns; x++)
3069    {
3070      (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelBlue(image,p)));
3071      (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelGreen(image,p)));
3072      (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelRed(image,p)));
3073      if (image->alpha_trait != UndefinedPixelTrait)
3074        (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelAlpha(image,p)));
3075      p+=GetPixelChannels(image);
3076    }
3077  }
3078}
3079