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