Fixed heap corruption when reading a corrupted RLE TGA image.
[wxWidgets.git] / tests / image / image.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/image/image.cpp
3 // Purpose: Test wxImage
4 // Author: Francesco Montorsi
5 // Created: 2009-05-31
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Francesco Montorsi
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ----------------------------------------------------------------------------
12 // headers
13 // ----------------------------------------------------------------------------
14
15 #include "testprec.h"
16
17 #if wxUSE_IMAGE
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #endif // WX_PRECOMP
25
26 #include "wx/anidecod.h" // wxImageArray
27 #include "wx/image.h"
28 #include "wx/palette.h"
29 #include "wx/url.h"
30 #include "wx/log.h"
31 #include "wx/mstream.h"
32 #include "wx/zstream.h"
33 #include "wx/wfstream.h"
34
35 struct testData {
36 const char* file;
37 wxBitmapType type;
38 unsigned bitDepth;
39 } g_testfiles[] =
40 {
41 { "horse.ico", wxBITMAP_TYPE_ICO, 4 },
42 { "horse.xpm", wxBITMAP_TYPE_XPM, 8 },
43 { "horse.png", wxBITMAP_TYPE_PNG, 24 },
44 { "horse.ani", wxBITMAP_TYPE_ANI, 24 },
45 { "horse.bmp", wxBITMAP_TYPE_BMP, 8 },
46 { "horse.cur", wxBITMAP_TYPE_CUR, 1 },
47 { "horse.gif", wxBITMAP_TYPE_GIF, 8 },
48 { "horse.jpg", wxBITMAP_TYPE_JPEG, 24 },
49 { "horse.pcx", wxBITMAP_TYPE_PCX, 8 },
50 { "horse.pnm", wxBITMAP_TYPE_PNM, 24 },
51 { "horse.tga", wxBITMAP_TYPE_TGA, 8 },
52 { "horse.tif", wxBITMAP_TYPE_TIF, 8 }
53 };
54
55
56 // ----------------------------------------------------------------------------
57 // test class
58 // ----------------------------------------------------------------------------
59
60 class ImageTestCase : public CppUnit::TestCase
61 {
62 public:
63 ImageTestCase();
64 ~ImageTestCase();
65
66 private:
67 CPPUNIT_TEST_SUITE( ImageTestCase );
68 CPPUNIT_TEST( LoadFromSocketStream );
69 CPPUNIT_TEST( LoadFromZipStream );
70 CPPUNIT_TEST( LoadFromFile );
71 CPPUNIT_TEST( SizeImage );
72 CPPUNIT_TEST( CompareLoadedImage );
73 CPPUNIT_TEST( CompareSavedImage );
74 CPPUNIT_TEST( SaveAnimatedGIF );
75 CPPUNIT_TEST( ReadCorruptedTGA );
76 CPPUNIT_TEST_SUITE_END();
77
78 void LoadFromSocketStream();
79 void LoadFromZipStream();
80 void LoadFromFile();
81 void SizeImage();
82 void CompareLoadedImage();
83 void CompareSavedImage();
84 void SaveAnimatedGIF();
85 void ReadCorruptedTGA();
86
87 DECLARE_NO_COPY_CLASS(ImageTestCase)
88 };
89
90 CPPUNIT_TEST_SUITE_REGISTRATION( ImageTestCase );
91 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ImageTestCase, "ImageTestCase" );
92
93 ImageTestCase::ImageTestCase()
94 {
95 wxSocketBase::Initialize();
96
97 // the formats we're going to test:
98 wxImage::AddHandler(new wxICOHandler);
99 wxImage::AddHandler(new wxXPMHandler);
100 wxImage::AddHandler(new wxPNGHandler);
101 wxImage::AddHandler(new wxANIHandler);
102 wxImage::AddHandler(new wxBMPHandler);
103 wxImage::AddHandler(new wxCURHandler);
104 wxImage::AddHandler(new wxGIFHandler);
105 wxImage::AddHandler(new wxJPEGHandler);
106 wxImage::AddHandler(new wxPCXHandler);
107 wxImage::AddHandler(new wxPNMHandler);
108 wxImage::AddHandler(new wxTGAHandler);
109 wxImage::AddHandler(new wxTIFFHandler);
110 }
111
112 ImageTestCase::~ImageTestCase()
113 {
114 wxSocketBase::Shutdown();
115 }
116
117 void ImageTestCase::LoadFromFile()
118 {
119 wxImage img;
120 for (unsigned int i=0; i<WXSIZEOF(g_testfiles); i++)
121 CPPUNIT_ASSERT(img.LoadFile(g_testfiles[i].file));
122 }
123
124 void ImageTestCase::LoadFromSocketStream()
125 {
126 if (!IsNetworkAvailable()) // implemented in test.cpp
127 {
128 wxLogWarning("No network connectivity; skipping the "
129 "ImageTestCase::LoadFromSocketStream test unit.");
130 return;
131 }
132
133 struct {
134 const char* url;
135 wxBitmapType type;
136 } testData[] =
137 {
138 { "http://www.wxwidgets.org/logo9.jpg", wxBITMAP_TYPE_JPEG },
139 { "http://www.wxwidgets.org/favicon.ico", wxBITMAP_TYPE_ICO }
140 };
141
142 for (unsigned int i=0; i<WXSIZEOF(testData); i++)
143 {
144 wxURL url(testData[i].url);
145 WX_ASSERT_EQUAL_MESSAGE
146 (
147 ("Constructing URL \"%s\" failed.", testData[i].url),
148 wxURL_NOERR,
149 url.GetError()
150 );
151
152 wxInputStream *in_stream = url.GetInputStream();
153 WX_ASSERT_MESSAGE
154 (
155 ("Opening URL \"%s\" failed.", testData[i].url),
156 in_stream && in_stream->IsOk()
157 );
158
159 wxImage img;
160
161 // NOTE: it's important to inform wxImage about the type of the image being
162 // loaded otherwise it will try to autodetect the format, but that
163 // requires a seekable stream!
164 WX_ASSERT_MESSAGE
165 (
166 ("Loading image from \"%s\" failed.", testData[i].url),
167 img.LoadFile(*in_stream, testData[i].type)
168 );
169
170 delete in_stream;
171 }
172 }
173
174 void ImageTestCase::LoadFromZipStream()
175 {
176 for (unsigned int i=0; i<WXSIZEOF(g_testfiles); i++)
177 {
178 switch (g_testfiles[i].type)
179 {
180 case wxBITMAP_TYPE_XPM:
181 case wxBITMAP_TYPE_GIF:
182 case wxBITMAP_TYPE_PCX:
183 case wxBITMAP_TYPE_TGA:
184 case wxBITMAP_TYPE_TIF:
185 continue; // skip testing those wxImageHandlers which cannot
186 // load data from non-seekable streams
187
188 default:
189 ; // proceed
190 }
191
192 // compress the test file on the fly:
193 wxMemoryOutputStream memOut;
194 {
195 wxFileInputStream file(g_testfiles[i].file);
196 CPPUNIT_ASSERT(file.IsOk());
197
198 wxZlibOutputStream compressFilter(memOut, 5, wxZLIB_GZIP);
199 CPPUNIT_ASSERT(compressFilter.IsOk());
200
201 file.Read(compressFilter);
202 CPPUNIT_ASSERT(file.GetLastError() == wxSTREAM_EOF);
203 }
204
205 // now fetch the compressed memory to wxImage, decompressing it on the fly; this
206 // allows us to test loading images from non-seekable streams other than socket streams
207 wxMemoryInputStream memIn(memOut);
208 CPPUNIT_ASSERT(memIn.IsOk());
209 wxZlibInputStream decompressFilter(memIn, wxZLIB_GZIP);
210 CPPUNIT_ASSERT(decompressFilter.IsOk());
211
212 wxImage img;
213
214 // NOTE: it's important to inform wxImage about the type of the image being
215 // loaded otherwise it will try to autodetect the format, but that
216 // requires a seekable stream!
217 WX_ASSERT_MESSAGE(("Could not load file type '%d' after it was zipped", g_testfiles[i].type),
218 img.LoadFile(decompressFilter, g_testfiles[i].type));
219 }
220 }
221
222 void ImageTestCase::SizeImage()
223 {
224 // Test the wxImage::Size() function which takes a rectangle from source and
225 // places it in a new image at a given position. This test checks, if the
226 // correct areas are chosen, and clipping is done correctly.
227
228 // our test image:
229 static const char * xpm_orig[] = {
230 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
231 " .....",
232 " ++++@@@@.",
233 " +... @.",
234 " +.@@++ @.",
235 " +.@ .+ @.",
236 ".@ +. @.+ ",
237 ".@ ++@@.+ ",
238 ".@ ...+ ",
239 ".@@@@++++ ",
240 "..... "
241 };
242 // the expected results for all tests:
243 static const char * xpm_l_t[] = {
244 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
245 "... @.BB",
246 ".@@++ @.BB",
247 ".@ .+ @.BB",
248 " +. @.+ BB",
249 " ++@@.+ BB",
250 " ...+ BB",
251 "@@@++++ BB",
252 "... BB",
253 "BBBBBBBBBB",
254 "BBBBBBBBBB"
255 };
256 static const char * xpm_t[] = {
257 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
258 " +... @.",
259 " +.@@++ @.",
260 " +.@ .+ @.",
261 ".@ +. @.+ ",
262 ".@ ++@@.+ ",
263 ".@ ...+ ",
264 ".@@@@++++ ",
265 "..... ",
266 "BBBBBBBBBB",
267 "BBBBBBBBBB"
268 };
269 static const char * xpm_r_t[] = {
270 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
271 "BB +... ",
272 "BB +.@@++ ",
273 "BB +.@ .+ ",
274 "BB.@ +. @.",
275 "BB.@ ++@@.",
276 "BB.@ ...",
277 "BB.@@@@+++",
278 "BB..... ",
279 "BBBBBBBBBB",
280 "BBBBBBBBBB"
281 };
282 static const char * xpm_l[] = {
283 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
284 " .....BB",
285 "+++@@@@.BB",
286 "... @.BB",
287 ".@@++ @.BB",
288 ".@ .+ @.BB",
289 " +. @.+ BB",
290 " ++@@.+ BB",
291 " ...+ BB",
292 "@@@++++ BB",
293 "... BB"
294 };
295 static const char * xpm_r[] = {
296 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
297 "BB ...",
298 "BB ++++@@@",
299 "BB +... ",
300 "BB +.@@++ ",
301 "BB +.@ .+ ",
302 "BB.@ +. @.",
303 "BB.@ ++@@.",
304 "BB.@ ...",
305 "BB.@@@@+++",
306 "BB..... "
307 };
308 static const char * xpm_l_b[] = {
309 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
310 "BBBBBBBBBB",
311 "BBBBBBBBBB",
312 " .....BB",
313 "+++@@@@.BB",
314 "... @.BB",
315 ".@@++ @.BB",
316 ".@ .+ @.BB",
317 " +. @.+ BB",
318 " ++@@.+ BB",
319 " ...+ BB"
320 };
321 static const char * xpm_b[] = {
322 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
323 "BBBBBBBBBB",
324 "BBBBBBBBBB",
325 " .....",
326 " ++++@@@@.",
327 " +... @.",
328 " +.@@++ @.",
329 " +.@ .+ @.",
330 ".@ +. @.+ ",
331 ".@ ++@@.+ ",
332 ".@ ...+ "
333 };
334 static const char * xpm_r_b[] = {
335 "10 10 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
336 "BBBBBBBBBB",
337 "BBBBBBBBBB",
338 "BB ...",
339 "BB ++++@@@",
340 "BB +... ",
341 "BB +.@@++ ",
342 "BB +.@ .+ ",
343 "BB.@ +. @.",
344 "BB.@ ++@@.",
345 "BB.@ ..."
346 };
347 static const char * xpm_sm[] = {
348 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
349 " .....",
350 " ++++@@@",
351 " +... ",
352 " +.@@++ ",
353 " +.@ .+ ",
354 ".@ +. @.",
355 ".@ ++@@.",
356 ".@ ..."
357 };
358 static const char * xpm_gt[] = {
359 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
360 " .....BB",
361 " ++++@@@@.BB",
362 " +... @.BB",
363 " +.@@++ @.BB",
364 " +.@ .+ @.BB",
365 ".@ +. @.+ BB",
366 ".@ ++@@.+ BB",
367 ".@ ...+ BB",
368 ".@@@@++++ BB",
369 "..... BB",
370 "BBBBBBBBBBBB",
371 "BBBBBBBBBBBB"
372 };
373 static const char * xpm_gt_l_t[] = {
374 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
375 "... @.BBBB",
376 ".@@++ @.BBBB",
377 ".@ .+ @.BBBB",
378 " +. @.+ BBBB",
379 " ++@@.+ BBBB",
380 " ...+ BBBB",
381 "@@@++++ BBBB",
382 "... BBBB",
383 "BBBBBBBBBBBB",
384 "BBBBBBBBBBBB",
385 "BBBBBBBBBBBB",
386 "BBBBBBBBBBBB"
387 };
388 static const char * xpm_gt_l[] = {
389 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
390 " .....BBBB",
391 "+++@@@@.BBBB",
392 "... @.BBBB",
393 ".@@++ @.BBBB",
394 ".@ .+ @.BBBB",
395 " +. @.+ BBBB",
396 " ++@@.+ BBBB",
397 " ...+ BBBB",
398 "@@@++++ BBBB",
399 "... BBBB",
400 "BBBBBBBBBBBB",
401 "BBBBBBBBBBBB"
402 };
403 static const char * xpm_gt_l_b[] = {
404 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
405 "BBBBBBBBBBBB",
406 "BBBBBBBBBBBB",
407 " .....BBBB",
408 "+++@@@@.BBBB",
409 "... @.BBBB",
410 ".@@++ @.BBBB",
411 ".@ .+ @.BBBB",
412 " +. @.+ BBBB",
413 " ++@@.+ BBBB",
414 " ...+ BBBB",
415 "@@@++++ BBBB",
416 "... BBBB"
417 };
418 static const char * xpm_gt_l_bb[] = {
419 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
420 "BBBBBBBBBBBB",
421 "BBBBBBBBBBBB",
422 "BBBBBBBBBBBB",
423 "BBBBBBBBBBBB",
424 " .....BBBB",
425 "+++@@@@.BBBB",
426 "... @.BBBB",
427 ".@@++ @.BBBB",
428 ".@ .+ @.BBBB",
429 " +. @.+ BBBB",
430 " ++@@.+ BBBB",
431 " ...+ BBBB"
432 };
433 static const char * xpm_gt_t[] = {
434 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
435 " +... @.BB",
436 " +.@@++ @.BB",
437 " +.@ .+ @.BB",
438 ".@ +. @.+ BB",
439 ".@ ++@@.+ BB",
440 ".@ ...+ BB",
441 ".@@@@++++ BB",
442 "..... BB",
443 "BBBBBBBBBBBB",
444 "BBBBBBBBBBBB",
445 "BBBBBBBBBBBB",
446 "BBBBBBBBBBBB"
447 };
448 static const char * xpm_gt_b[] = {
449 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
450 "BBBBBBBBBBBB",
451 "BBBBBBBBBBBB",
452 " .....BB",
453 " ++++@@@@.BB",
454 " +... @.BB",
455 " +.@@++ @.BB",
456 " +.@ .+ @.BB",
457 ".@ +. @.+ BB",
458 ".@ ++@@.+ BB",
459 ".@ ...+ BB",
460 ".@@@@++++ BB",
461 "..... BB"
462 };
463 static const char * xpm_gt_bb[] = {
464 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
465 "BBBBBBBBBBBB",
466 "BBBBBBBBBBBB",
467 "BBBBBBBBBBBB",
468 "BBBBBBBBBBBB",
469 " .....BB",
470 " ++++@@@@.BB",
471 " +... @.BB",
472 " +.@@++ @.BB",
473 " +.@ .+ @.BB",
474 ".@ +. @.+ BB",
475 ".@ ++@@.+ BB",
476 ".@ ...+ BB"
477 };
478 static const char * xpm_gt_r_t[] = {
479 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
480 "BB +... @.",
481 "BB +.@@++ @.",
482 "BB +.@ .+ @.",
483 "BB.@ +. @.+ ",
484 "BB.@ ++@@.+ ",
485 "BB.@ ...+ ",
486 "BB.@@@@++++ ",
487 "BB..... ",
488 "BBBBBBBBBBBB",
489 "BBBBBBBBBBBB",
490 "BBBBBBBBBBBB",
491 "BBBBBBBBBBBB"
492 };
493 static const char * xpm_gt_r[] = {
494 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
495 "BB .....",
496 "BB ++++@@@@.",
497 "BB +... @.",
498 "BB +.@@++ @.",
499 "BB +.@ .+ @.",
500 "BB.@ +. @.+ ",
501 "BB.@ ++@@.+ ",
502 "BB.@ ...+ ",
503 "BB.@@@@++++ ",
504 "BB..... ",
505 "BBBBBBBBBBBB",
506 "BBBBBBBBBBBB"
507 };
508 static const char * xpm_gt_r_b[] = {
509 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
510 "BBBBBBBBBBBB",
511 "BBBBBBBBBBBB",
512 "BB .....",
513 "BB ++++@@@@.",
514 "BB +... @.",
515 "BB +.@@++ @.",
516 "BB +.@ .+ @.",
517 "BB.@ +. @.+ ",
518 "BB.@ ++@@.+ ",
519 "BB.@ ...+ ",
520 "BB.@@@@++++ ",
521 "BB..... "
522 };
523 static const char * xpm_gt_r_bb[] = {
524 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
525 "BBBBBBBBBBBB",
526 "BBBBBBBBBBBB",
527 "BBBBBBBBBBBB",
528 "BBBBBBBBBBBB",
529 "BB .....",
530 "BB ++++@@@@.",
531 "BB +... @.",
532 "BB +.@@++ @.",
533 "BB +.@ .+ @.",
534 "BB.@ +. @.+ ",
535 "BB.@ ++@@.+ ",
536 "BB.@ ...+ "
537 };
538 static const char * xpm_gt_rr_t[] = {
539 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
540 "BBBB +... ",
541 "BBBB +.@@++ ",
542 "BBBB +.@ .+ ",
543 "BBBB.@ +. @.",
544 "BBBB.@ ++@@.",
545 "BBBB.@ ...",
546 "BBBB.@@@@+++",
547 "BBBB..... ",
548 "BBBBBBBBBBBB",
549 "BBBBBBBBBBBB",
550 "BBBBBBBBBBBB",
551 "BBBBBBBBBBBB"
552 };
553 static const char * xpm_gt_rr[] = {
554 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
555 "BBBB ...",
556 "BBBB ++++@@@",
557 "BBBB +... ",
558 "BBBB +.@@++ ",
559 "BBBB +.@ .+ ",
560 "BBBB.@ +. @.",
561 "BBBB.@ ++@@.",
562 "BBBB.@ ...",
563 "BBBB.@@@@+++",
564 "BBBB..... ",
565 "BBBBBBBBBBBB",
566 "BBBBBBBBBBBB"
567 };
568 static const char * xpm_gt_rr_b[] = {
569 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
570 "BBBBBBBBBBBB",
571 "BBBBBBBBBBBB",
572 "BBBB ...",
573 "BBBB ++++@@@",
574 "BBBB +... ",
575 "BBBB +.@@++ ",
576 "BBBB +.@ .+ ",
577 "BBBB.@ +. @.",
578 "BBBB.@ ++@@.",
579 "BBBB.@ ...",
580 "BBBB.@@@@+++",
581 "BBBB..... "
582 };
583 static const char * xpm_gt_rr_bb[] = {
584 "12 12 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
585 "BBBBBBBBBBBB",
586 "BBBBBBBBBBBB",
587 "BBBBBBBBBBBB",
588 "BBBBBBBBBBBB",
589 "BBBB ...",
590 "BBBB ++++@@@",
591 "BBBB +... ",
592 "BBBB +.@@++ ",
593 "BBBB +.@ .+ ",
594 "BBBB.@ +. @.",
595 "BBBB.@ ++@@.",
596 "BBBB.@ ..."
597 };
598 static const char * xpm_sm_ll_tt[] = {
599 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
600 " .+ @.BB",
601 ". @.+ BB",
602 "+@@.+ BB",
603 " ...+ BB",
604 "@++++ BB",
605 ". BB",
606 "BBBBBBBB",
607 "BBBBBBBB"
608 };
609 static const char * xpm_sm_ll_t[] = {
610 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
611 ". @.BB",
612 "@++ @.BB",
613 " .+ @.BB",
614 ". @.+ BB",
615 "+@@.+ BB",
616 " ...+ BB",
617 "@++++ BB",
618 ". BB"
619 };
620 static const char * xpm_sm_ll[] = {
621 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
622 " .....BB",
623 "+@@@@.BB",
624 ". @.BB",
625 "@++ @.BB",
626 " .+ @.BB",
627 ". @.+ BB",
628 "+@@.+ BB",
629 " ...+ BB"
630 };
631 static const char * xpm_sm_ll_b[] = {
632 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
633 "BBBBBBBB",
634 "BBBBBBBB",
635 " .....BB",
636 "+@@@@.BB",
637 ". @.BB",
638 "@++ @.BB",
639 " .+ @.BB",
640 ". @.+ BB"
641 };
642 static const char * xpm_sm_l_tt[] = {
643 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
644 ".@ .+ @.",
645 " +. @.+ ",
646 " ++@@.+ ",
647 " ...+ ",
648 "@@@++++ ",
649 "... ",
650 "BBBBBBBB",
651 "BBBBBBBB"
652 };
653 static const char * xpm_sm_l_t[] = {
654 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
655 "... @.",
656 ".@@++ @.",
657 ".@ .+ @.",
658 " +. @.+ ",
659 " ++@@.+ ",
660 " ...+ ",
661 "@@@++++ ",
662 "... "
663 };
664 static const char * xpm_sm_l[] = {
665 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
666 " .....",
667 "+++@@@@.",
668 "... @.",
669 ".@@++ @.",
670 ".@ .+ @.",
671 " +. @.+ ",
672 " ++@@.+ ",
673 " ...+ "
674 };
675 static const char * xpm_sm_l_b[] = {
676 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
677 "BBBBBBBB",
678 "BBBBBBBB",
679 " .....",
680 "+++@@@@.",
681 "... @.",
682 ".@@++ @.",
683 ".@ .+ @.",
684 " +. @.+ "
685 };
686 static const char * xpm_sm_tt[] = {
687 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
688 " +.@ .+ ",
689 ".@ +. @.",
690 ".@ ++@@.",
691 ".@ ...",
692 ".@@@@+++",
693 "..... ",
694 "BBBBBBBB",
695 "BBBBBBBB"
696 };
697 static const char * xpm_sm_t[] = {
698 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
699 " +... ",
700 " +.@@++ ",
701 " +.@ .+ ",
702 ".@ +. @.",
703 ".@ ++@@.",
704 ".@ ...",
705 ".@@@@+++",
706 "..... "
707 };
708 static const char * xpm_sm_b[] = {
709 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
710 "BBBBBBBB",
711 "BBBBBBBB",
712 " ...",
713 " ++++@@@",
714 " +... ",
715 " +.@@++ ",
716 " +.@ .+ ",
717 ".@ +. @."
718 };
719 static const char * xpm_sm_r_tt[] = {
720 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
721 "BB +.@ .",
722 "BB.@ +. ",
723 "BB.@ ++@",
724 "BB.@ .",
725 "BB.@@@@+",
726 "BB..... ",
727 "BBBBBBBB",
728 "BBBBBBBB"
729 };
730 static const char * xpm_sm_r_t[] = {
731 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
732 "BB +... ",
733 "BB +.@@+",
734 "BB +.@ .",
735 "BB.@ +. ",
736 "BB.@ ++@",
737 "BB.@ .",
738 "BB.@@@@+",
739 "BB..... "
740 };
741 static const char * xpm_sm_r[] = {
742 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
743 "BB .",
744 "BB ++++@",
745 "BB +... ",
746 "BB +.@@+",
747 "BB +.@ .",
748 "BB.@ +. ",
749 "BB.@ ++@",
750 "BB.@ ."
751 };
752 static const char * xpm_sm_r_b[] = {
753 "8 8 5 1", "B c Black", " c #00ff00", ". c #0000ff", "+ c #7f7f7f", "@ c #FF0000",
754 "BBBBBBBB",
755 "BBBBBBBB",
756 "BB .",
757 "BB ++++@",
758 "BB +... ",
759 "BB +.@@+",
760 "BB +.@ .",
761 "BB.@ +. "
762 };
763
764 // this table defines all tests
765 struct SizeTestData
766 {
767 int w, h, dx, dy; // first parameters for Size()
768 const char **ref_xpm; // expected result
769 } sizeTestData[] =
770 {
771 { 10, 10, 0, 0, xpm_orig}, // same size, same position
772 { 12, 12, 0, 0, xpm_gt}, // target larger, same position
773 { 8, 8, 0, 0, xpm_sm}, // target smaller, same position
774 { 10, 10, -2, -2, xpm_l_t}, // same size, move left up
775 { 10, 10, -2, 0, xpm_l}, // same size, move left
776 { 10, 10, -2, 2, xpm_l_b}, // same size, move left down
777 { 10, 10, 0, -2, xpm_t}, // same size, move up
778 { 10, 10, 0, 2, xpm_b}, // same size, move down
779 { 10, 10, 2, -2, xpm_r_t}, // same size, move right up
780 { 10, 10, 2, 0, xpm_r}, // same size, move right
781 { 10, 10, 2, 2, xpm_r_b}, // same size, move right down
782 { 12, 12, -2, -2, xpm_gt_l_t}, // target larger, move left up
783 { 12, 12, -2, 0, xpm_gt_l}, // target larger, move left
784 { 12, 12, -2, 2, xpm_gt_l_b}, // target larger, move left down
785 { 12, 12, -2, 4, xpm_gt_l_bb}, // target larger, move left down
786 { 12, 12, 0, -2, xpm_gt_t}, // target larger, move up
787 { 12, 12, 0, 2, xpm_gt_b}, // target larger, move down
788 { 12, 12, 0, 4, xpm_gt_bb}, // target larger, move down
789 { 12, 12, 2, -2, xpm_gt_r_t}, // target larger, move right up
790 { 12, 12, 2, 0, xpm_gt_r}, // target larger, move right
791 { 12, 12, 2, 2, xpm_gt_r_b}, // target larger, move right down
792 { 12, 12, 2, 4, xpm_gt_r_bb}, // target larger, move right down
793 { 12, 12, 4, -2, xpm_gt_rr_t}, // target larger, move right up
794 { 12, 12, 4, 0, xpm_gt_rr}, // target larger, move right
795 { 12, 12, 4, 2, xpm_gt_rr_b}, // target larger, move right down
796 { 12, 12, 4, 4, xpm_gt_rr_bb}, // target larger, move right down
797 { 8, 8, -4, -4, xpm_sm_ll_tt}, // target smaller, move left up
798 { 8, 8, -4, -2, xpm_sm_ll_t}, // target smaller, move left up
799 { 8, 8, -4, 0, xpm_sm_ll}, // target smaller, move left
800 { 8, 8, -4, 2, xpm_sm_ll_b}, // target smaller, move left down
801 { 8, 8, -2, -4, xpm_sm_l_tt}, // target smaller, move left up
802 { 8, 8, -2, -2, xpm_sm_l_t}, // target smaller, move left up
803 { 8, 8, -2, 0, xpm_sm_l}, // target smaller, move left
804 { 8, 8, -2, 2, xpm_sm_l_b}, // target smaller, move left down
805 { 8, 8, 0, -4, xpm_sm_tt}, // target smaller, move up
806 { 8, 8, 0, -2, xpm_sm_t}, // target smaller, move up
807 { 8, 8, 0, 2, xpm_sm_b}, // target smaller, move down
808 { 8, 8, 2, -4, xpm_sm_r_tt}, // target smaller, move right up
809 { 8, 8, 2, -2, xpm_sm_r_t}, // target smaller, move right up
810 { 8, 8, 2, 0, xpm_sm_r}, // target smaller, move right
811 { 8, 8, 2, 2, xpm_sm_r_b}, // target smaller, move right down
812 };
813
814 const wxImage src_img(xpm_orig);
815 for ( unsigned i = 0; i < WXSIZEOF(sizeTestData); i++ )
816 {
817 SizeTestData& st = sizeTestData[i];
818 wxImage
819 actual(src_img.Size(wxSize(st.w, st.h), wxPoint(st.dx, st.dy), 0, 0, 0)),
820 expected(st.ref_xpm);
821
822 // to check results with an image viewer uncomment this:
823 //actual.SaveFile(wxString::Format("imagetest-%02d-actual.png", i), wxBITMAP_TYPE_PNG);
824 //expected.SaveFile(wxString::Format("imagetest-%02d-exp.png", i), wxBITMAP_TYPE_PNG);
825
826 CPPUNIT_ASSERT_EQUAL( actual.GetSize().x, expected.GetSize().x );
827 CPPUNIT_ASSERT_EQUAL( actual.GetSize().y, expected.GetSize().y );
828
829 const unsigned data_len = 3 * expected.GetHeight() * expected.GetWidth();
830
831 WX_ASSERT_MESSAGE
832 (
833 ("Resize test #%u: (%d, %d), (%d, %d)", i, st.w, st.h, st.dx, st.dy),
834 memcmp(actual.GetData(), expected.GetData(), data_len) == 0
835 );
836 }
837 }
838
839 void ImageTestCase::CompareLoadedImage()
840 {
841 wxImage expected8("horse.xpm");
842 CPPUNIT_ASSERT( expected8.IsOk() );
843
844 wxImage expected24("horse.png");
845 CPPUNIT_ASSERT( expected24.IsOk() );
846
847 const size_t dataLen = expected8.GetWidth() * expected8.GetHeight() * 3;
848
849 for (size_t i=0; i<WXSIZEOF(g_testfiles); i++)
850 {
851 if ( !(g_testfiles[i].bitDepth == 8 || g_testfiles[i].bitDepth == 24)
852 || g_testfiles[i].type == wxBITMAP_TYPE_JPEG /*skip lossy JPEG*/)
853 {
854 continue;
855 }
856
857 wxImage actual(g_testfiles[i].file);
858
859 if ( actual.GetSize() != expected8.GetSize() )
860 {
861 continue;
862 }
863
864
865 WX_ASSERT_MESSAGE
866 (
867 ("Compare test '%s' for loading failed", g_testfiles[i].file),
868
869 memcmp(actual.GetData(),
870 (g_testfiles[i].bitDepth == 8)
871 ? expected8.GetData()
872 : expected24.GetData(),
873 dataLen) == 0
874 );
875 }
876
877 }
878
879 enum
880 {
881 wxIMAGE_HAVE_ALPHA = (1 << 0),
882 wxIMAGE_HAVE_PALETTE = (1 << 1)
883 };
884
885 static
886 void CompareImage(const wxImageHandler& handler, const wxImage& image,
887 int properties = 0, const wxImage *compareTo = NULL)
888 {
889 wxBitmapType type = handler.GetType();
890
891 const bool testPalette = (properties & wxIMAGE_HAVE_PALETTE) != 0;
892 /*
893 This is getting messy and should probably be transformed into a table
894 with image format features before it gets hairier.
895 */
896 if ( testPalette
897 && ( !(type == wxBITMAP_TYPE_BMP
898 || type == wxBITMAP_TYPE_GIF
899 || type == wxBITMAP_TYPE_PNG)
900 || type == wxBITMAP_TYPE_XPM) )
901 {
902 return;
903 }
904
905 const bool testAlpha = (properties & wxIMAGE_HAVE_ALPHA) != 0;
906 if (testAlpha
907 && !(type == wxBITMAP_TYPE_PNG || type == wxBITMAP_TYPE_TGA) )
908 {
909 // don't test images with alpha if this handler doesn't support alpha
910 return;
911 }
912
913 if (type == wxBITMAP_TYPE_JPEG /* skip lossy JPEG */
914 || type == wxBITMAP_TYPE_TIF)
915 {
916 /*
917 TIFF is skipped because the memory stream can't be loaded. Libtiff
918 looks for a TIFF directory at offset 120008 while the memory
919 stream size is only 120008 bytes (when saving as a file
920 the file size is 120280 bytes).
921 */
922 return;
923 }
924
925 wxMemoryOutputStream memOut;
926 if ( !image.SaveFile(memOut, type) )
927 {
928 // Unfortunately we can't know if the handler just doesn't support
929 // saving images, or if it failed to save.
930 return;
931 }
932
933 wxMemoryInputStream memIn(memOut);
934 CPPUNIT_ASSERT(memIn.IsOk());
935
936 wxImage actual(memIn);
937 CPPUNIT_ASSERT(actual.IsOk());
938
939 const wxImage *expected = compareTo ? compareTo : &image;
940 CPPUNIT_ASSERT( actual.GetSize() == expected->GetSize() );
941
942 unsigned bitsPerPixel = testPalette ? 8 : (testAlpha ? 32 : 24);
943 WX_ASSERT_MESSAGE
944 (
945 ("Compare test '%s (%d-bit)' for saving failed",
946 handler.GetExtension(), bitsPerPixel),
947
948 memcmp(actual.GetData(), expected->GetData(),
949 expected->GetWidth() * expected->GetHeight() * 3) == 0
950 );
951
952 #if wxUSE_PALETTE
953 CPPUNIT_ASSERT(actual.HasPalette()
954 == (testPalette || type == wxBITMAP_TYPE_XPM));
955 #endif
956
957 CPPUNIT_ASSERT( actual.HasAlpha() == testAlpha);
958
959 if (!testAlpha)
960 {
961 return;
962 }
963
964 WX_ASSERT_MESSAGE
965 (
966 ("Compare alpha test '%s' for saving failed", handler.GetExtension()),
967
968 memcmp(actual.GetAlpha(), expected->GetAlpha(),
969 expected->GetWidth() * expected->GetHeight()) == 0
970 );
971 }
972
973 void ImageTestCase::CompareSavedImage()
974 {
975 // FIXME-VC6: Pre-declare the loop variables for compatibility with
976 // pre-standard compilers such as MSVC6 that don't implement proper scope
977 // for the variables declared in the for loops.
978 int i, x, y;
979
980 wxImage expected24("horse.png");
981 CPPUNIT_ASSERT( expected24.IsOk() );
982 CPPUNIT_ASSERT( !expected24.HasAlpha() );
983
984 wxImage expected8 = expected24.ConvertToGreyscale();
985
986 #if wxUSE_PALETTE
987 unsigned char greys[256];
988 for (i = 0; i < 256; ++i)
989 {
990 greys[i] = i;
991 }
992 wxPalette palette(256, greys, greys, greys);
993 expected8.SetPalette(palette);
994 #endif // #if wxUSE_PALETTE
995
996 expected8.SetOption(wxIMAGE_OPTION_BMP_FORMAT, wxBMP_8BPP_PALETTE);
997
998 // Create an image with alpha based on the loaded image
999 wxImage expected32(expected24);
1000 expected32.SetAlpha();
1001
1002 int width = expected32.GetWidth();
1003 int height = expected32.GetHeight();
1004 for (y = 0; y < height; ++y)
1005 {
1006 for (x = 0; x < width; ++x)
1007 {
1008 expected32.SetAlpha(x, y, (x*y) & wxIMAGE_ALPHA_OPAQUE);
1009 }
1010 }
1011
1012 const wxList& list = wxImage::GetHandlers();
1013 for ( wxList::compatibility_iterator node = list.GetFirst();
1014 node; node = node->GetNext() )
1015 {
1016 wxImageHandler *handler = (wxImageHandler *) node->GetData();
1017
1018 #if wxUSE_PALETTE
1019 CompareImage(*handler, expected8, wxIMAGE_HAVE_PALETTE);
1020 #endif
1021 CompareImage(*handler, expected24);
1022 CompareImage(*handler, expected32, wxIMAGE_HAVE_ALPHA);
1023 }
1024
1025
1026 expected8.LoadFile("horse.gif");
1027 CPPUNIT_ASSERT( expected8.IsOk() );
1028 #if wxUSE_PALETTE
1029 CPPUNIT_ASSERT( expected8.HasPalette() );
1030 #endif // #if wxUSE_PALETTE
1031
1032 expected8.SetAlpha();
1033
1034 width = expected8.GetWidth();
1035 height = expected8.GetHeight();
1036 for (y = 0; y < height; ++y)
1037 {
1038 for (x = 0; x < width; ++x)
1039 {
1040 expected8.SetAlpha(x, y, (x*y) & wxIMAGE_ALPHA_OPAQUE);
1041 }
1042 }
1043
1044 /*
1045 Explicitly make known we want a palettised PNG. If we don't then this
1046 particular image gets saved as a true colour image because there's an
1047 alpha channel present and the PNG saver prefers to keep the alpha over
1048 saving as a palettised image that has alpha converted to a mask.
1049 */
1050 expected8.SetOption(wxIMAGE_OPTION_PNG_FORMAT, wxPNG_TYPE_PALETTE);
1051
1052 /*
1053 The image contains 256 indexed colours and needs another palette entry
1054 for storing the transparency index. This results in wanting 257 palette
1055 entries but that amount is not supported by PNG, as such this image
1056 should not contain a palette (but still have alpha) and be stored as a
1057 true colour image instead.
1058 */
1059 CompareImage(*wxImage::FindHandler(wxBITMAP_TYPE_PNG),
1060 expected8, wxIMAGE_HAVE_ALPHA);
1061
1062 #if wxUSE_PALETTE
1063 /*
1064 Now do the same test again but remove one (random) palette entry. This
1065 should result in saving the PNG with a palette.
1066 */
1067 unsigned char red[256], green[256], blue[256];
1068 const wxPalette& pal = expected8.GetPalette();
1069 const int paletteCount = pal.GetColoursCount();
1070 for (i = 0; i < paletteCount; ++i)
1071 {
1072 expected8.GetPalette().GetRGB(i, &red[i], &green[i], &blue[i]);
1073 }
1074 wxPalette newPal(paletteCount - 1, red, green, blue);
1075 expected8.Replace(
1076 red[paletteCount-1], green[paletteCount-1], blue[paletteCount-1],
1077 red[paletteCount-2], green[paletteCount-2], blue[paletteCount-2]);
1078
1079 expected8.SetPalette(newPal);
1080
1081 wxImage ref8 = expected8;
1082
1083 /*
1084 Convert the alpha channel to a mask like the PNG saver does. Also convert
1085 the colour used for transparency from 1,0,0 to 2,0,0. The latter gets
1086 done by the PNG loader in search of an unused colour to use for
1087 transparency (this should be fixed).
1088 */
1089 ref8.ConvertAlphaToMask();
1090 ref8.Replace(1, 0, 0, 2, 0, 0);
1091
1092 CompareImage(*wxImage::FindHandler(wxBITMAP_TYPE_PNG),
1093 expected8, wxIMAGE_HAVE_PALETTE, &ref8);
1094 #endif
1095 }
1096
1097 void ImageTestCase::SaveAnimatedGIF()
1098 {
1099 #if wxUSE_PALETTE
1100 wxImage image("horse.gif");
1101 CPPUNIT_ASSERT( image.IsOk() );
1102
1103 wxImageArray images;
1104 images.Add(image);
1105 int i;
1106 for (i = 0; i < 4-1; ++i)
1107 {
1108 images.Add( images[i].Rotate90() );
1109
1110 images[i+1].SetPalette(images[0].GetPalette());
1111 }
1112
1113 wxMemoryOutputStream memOut;
1114 CPPUNIT_ASSERT( wxGIFHandler().SaveAnimation(images, &memOut) );
1115
1116 wxGIFHandler handler;
1117 wxMemoryInputStream memIn(memOut);
1118 CPPUNIT_ASSERT(memIn.IsOk());
1119 const int imageCount = handler.GetImageCount(memIn);
1120 CPPUNIT_ASSERT_EQUAL(4, imageCount);
1121
1122 for (i = 0; i < imageCount; ++i)
1123 {
1124 wxFileOffset pos = memIn.TellI();
1125 CPPUNIT_ASSERT( handler.LoadFile(&image, memIn, true, i) );
1126 memIn.SeekI(pos);
1127
1128 WX_ASSERT_MESSAGE
1129 (
1130 ("Compare test for GIF frame number %d failed", i),
1131 memcmp(image.GetData(), images[i].GetData(),
1132 images[i].GetWidth() * images[i].GetHeight() * 3) == 0
1133 );
1134 }
1135 #endif // #if wxUSE_PALETTE
1136 }
1137
1138 void ImageTestCase::ReadCorruptedTGA()
1139 {
1140 static unsigned char corruptTGA[18+1+3] =
1141 {
1142 0,
1143 0,
1144 10, // RLE compressed image.
1145 0, 0,
1146 0, 0,
1147 0,
1148 0, 0,
1149 0, 0,
1150 1, 0, // Width is 1.
1151 1, 0, // Height is 1.
1152 24, // Bits per pixel.
1153 0,
1154
1155 0xff, // Run length (repeat next pixel 127+1 times).
1156 0xff, 0xff, 0xff // One 24-bit pixel.
1157 };
1158
1159 wxMemoryInputStream memIn(corruptTGA, WXSIZEOF(corruptTGA));
1160 CPPUNIT_ASSERT(memIn.IsOk());
1161
1162 wxImage tgaImage;
1163 CPPUNIT_ASSERT( !tgaImage.LoadFile(memIn) );
1164
1165
1166 /*
1167 Instead of repeating a pixel 127+1 times, now tell it there will
1168 follow 127+1 uncompressed pixels (while we only should have 1 in total).
1169 */
1170 corruptTGA[18] = 0x7f;
1171 CPPUNIT_ASSERT( !tgaImage.LoadFile(memIn) );
1172 }
1173
1174 #endif //wxUSE_IMAGE
1175
1176
1177 /*
1178 TODO: add lots of more tests to wxImage functions
1179 */