1 /* 2 * Copyright 2007, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Pfeiffer 7 */ 8 9 #include <GraphicsDefs.h> 10 #include <InterfaceKit.h> 11 #include <String.h> 12 13 #include <stdio.h> 14 15 #include "PictureTest.h" 16 17 #define TEST_AND_RETURN(condition, message, result) \ 18 { \ 19 if (condition) { \ 20 SetErrorMessage(message); \ 21 return result; \ 22 } \ 23 } 24 25 template <class T> 26 class AutoDelete 27 { 28 public: 29 AutoDelete(T *object) : fObject(object) { } 30 ~AutoDelete() { delete fObject; fObject = NULL; } 31 32 T* Release() { T* object = fObject; fObject = NULL; return object; } 33 34 private: 35 T *fObject; 36 }; 37 38 class OffscreenBitmap { 39 public: 40 OffscreenBitmap(BRect frame, color_space colorSpace); 41 virtual ~OffscreenBitmap(); 42 status_t InitCheck() const { return fStatus; } 43 44 BView *View(); 45 BBitmap *Copy(); 46 47 private: 48 BRect fFrame; 49 color_space fColorSpace; 50 status_t fStatus; 51 52 BBitmap *fBitmap; 53 BView *fView; 54 }; 55 56 OffscreenBitmap::OffscreenBitmap(BRect frame, color_space colorSpace) 57 : fFrame(frame) 58 , fColorSpace(colorSpace) 59 , fStatus(B_ERROR) 60 , fBitmap(NULL) 61 , fView(NULL) 62 { 63 BBitmap *bitmap = new BBitmap(frame, fColorSpace, true); 64 AutoDelete<BBitmap> _bitmap(bitmap); 65 if (bitmap == NULL || bitmap->IsValid() == false || bitmap->InitCheck() != B_OK) 66 return; 67 68 BView *view = new BView(frame, "offscreen", B_FOLLOW_ALL, B_WILL_DRAW); 69 AutoDelete<BView> _view(view); 70 if (view == NULL) 71 return; 72 73 bitmap->Lock(); 74 bitmap->AddChild(view); 75 // bitmap is locked during the life time of this object 76 77 fBitmap = _bitmap.Release(); 78 fView = _view.Release(); 79 fStatus = B_OK; 80 } 81 82 OffscreenBitmap::~OffscreenBitmap() 83 { 84 if (fStatus != B_OK) 85 return; 86 87 fView->RemoveSelf(); 88 fBitmap->Unlock(); 89 delete fView; 90 fView = NULL; 91 92 delete fBitmap; 93 fBitmap = NULL; 94 95 fStatus = B_ERROR; 96 } 97 98 BView * 99 OffscreenBitmap::View() 100 { 101 return fView; 102 } 103 104 BBitmap* 105 OffscreenBitmap::Copy() 106 { 107 // the result bitmap that does not accept views 108 // to save resources in the application server 109 BBitmap *copy = new BBitmap(fFrame, fColorSpace, false); 110 AutoDelete<BBitmap> _copy(copy); 111 if (copy == NULL || copy->IsValid() == false || copy->InitCheck() != B_OK) 112 return NULL; 113 114 fView->Sync(); 115 fBitmap->Unlock(); 116 117 memcpy(copy->Bits(), fBitmap->Bits(), fBitmap->BitsLength()); 118 119 fBitmap->Lock(); 120 121 return _copy.Release(); 122 } 123 124 PictureTest::PictureTest() 125 : fColorSpace(B_RGBA32) 126 , fDirectBitmap(NULL) 127 , fBitmapFromPicture(NULL) 128 , fBitmapFromRestoredPicture(NULL) 129 { 130 } 131 132 133 BBitmap* 134 PictureTest::DirectBitmap(bool detach) 135 { 136 BBitmap* bitmap = fDirectBitmap; 137 if (detach) 138 fDirectBitmap = NULL; 139 return bitmap; 140 } 141 142 BBitmap* 143 PictureTest::BitmapFromPicture(bool detach) 144 { 145 BBitmap* bitmap = fBitmapFromPicture; 146 if (detach) 147 fBitmapFromPicture = NULL; 148 return bitmap; 149 } 150 151 152 BBitmap* 153 PictureTest::BitmapFromRestoredPicture(bool detach) 154 { 155 BBitmap* bitmap = fBitmapFromRestoredPicture; 156 if (detach) 157 fBitmapFromRestoredPicture = NULL; 158 return bitmap; 159 } 160 161 162 PictureTest::~PictureTest() 163 { 164 CleanUp(); 165 } 166 167 void 168 PictureTest::CleanUp() 169 { 170 delete fBitmapFromPicture; 171 fBitmapFromPicture = NULL; 172 delete fBitmapFromRestoredPicture; 173 fBitmapFromRestoredPicture = NULL; 174 fErrorMessage = ""; 175 } 176 177 void 178 PictureTest::SetErrorMessage(const char *message) 179 { 180 if (fErrorMessage.Length() == 0) 181 fErrorMessage = message; 182 } 183 184 bool 185 PictureTest::Test(draw_func* func, BRect frame) 186 { 187 CleanUp(); 188 189 fDirectBitmap = CreateBitmap(func, frame); 190 TEST_AND_RETURN(fDirectBitmap == NULL, "Could not create direct draw bitmap!", false); 191 192 BPicture *picture = RecordPicture(func, frame); 193 AutoDelete<BPicture> _picture(picture); 194 TEST_AND_RETURN(picture == NULL, "Picture could not be recorded!", false); 195 196 BPicture *archivedPicture = SaveAndRestore(picture); 197 AutoDelete<BPicture> _archivedPicture(archivedPicture); 198 TEST_AND_RETURN(picture == NULL, "Picture could not be flattened and unflattened!", false); 199 200 fBitmapFromPicture = CreateBitmap(picture, frame); 201 TEST_AND_RETURN(fBitmapFromPicture == NULL, "Could not create bitmap from original picture!", false); 202 203 fBitmapFromRestoredPicture = CreateBitmap(archivedPicture, frame); 204 TEST_AND_RETURN(fBitmapFromRestoredPicture == NULL, "Could not create bitmap from archived picture!", false); 205 206 BString reason; 207 if (!IsSame(fDirectBitmap, fBitmapFromPicture, reason)) { 208 BString message("Bitmap from picture differs from direct drawing bitmap: "); 209 message += reason; 210 SetErrorMessage(message.String()); 211 return false; 212 } 213 if (!IsSame(fDirectBitmap, fBitmapFromRestoredPicture, reason)) { 214 BString message("Bitmap from restored picture differs from direct drawing bitmap: "); 215 message += reason; 216 SetErrorMessage(message.String()); 217 return false; 218 } 219 return true; 220 } 221 222 223 BBitmap * 224 PictureTest::CreateBitmap(draw_func* func, BRect frame) 225 { 226 OffscreenBitmap bitmap(frame, fColorSpace); 227 TEST_AND_RETURN(bitmap.InitCheck() != B_OK, "Offscreen bitmap for direct drawing could not be created!" , NULL); 228 func(bitmap.View(), frame); 229 return bitmap.Copy(); 230 } 231 232 BPicture * 233 PictureTest::RecordPicture(draw_func* func, BRect frame) 234 { 235 OffscreenBitmap bitmap(frame, fColorSpace); 236 TEST_AND_RETURN(bitmap.InitCheck() != B_OK, "Offscreen bitmap for picture recording could not be created!" , NULL); 237 238 BView *view = bitmap.View(); 239 // record 240 BPicture *picture = new BPicture(); 241 view->BeginPicture(picture); 242 func(view, frame); 243 picture = view->EndPicture(); 244 245 return picture; 246 } 247 248 BBitmap * 249 PictureTest::CreateBitmap(BPicture *picture, BRect frame) 250 { 251 OffscreenBitmap bitmap(frame, fColorSpace); 252 TEST_AND_RETURN(bitmap.InitCheck() != B_OK, "Offscreen bitmap for picture drawing could not be created!" , NULL); 253 254 BView *view = bitmap.View(); 255 view->DrawPicture(picture); 256 view->Sync(); 257 258 return bitmap.Copy(); 259 } 260 261 262 static void setMismatchReason(int32 x, int32 y, uint8 *pixel1, uint8 *pixel2, 263 int32 bpp, BString &reason) 264 { 265 char buffer1[32]; 266 char buffer2[32]; 267 uint32 color1 = 0; 268 uint32 color2 = 0; 269 memcpy(&color1, pixel1, bpp); 270 memcpy(&color2, pixel2, bpp); 271 sprintf(buffer1, "0x%8.8x", (int)color1); 272 sprintf(buffer2, "0x%8.8x", (int)color2); 273 274 reason = "Pixel at "; 275 reason << x << ", " << y << " differs: " << buffer1 << " != " << buffer2; 276 } 277 278 279 bool 280 PictureTest::IsSame(BBitmap *bitmap1, BBitmap *bitmap2, BString &reason) 281 { 282 if (bitmap1->ColorSpace() != bitmap2->ColorSpace()) { 283 reason = "ColorSpace() differs"; 284 return false; 285 } 286 287 if (bitmap1->BitsLength() != bitmap2->BitsLength()) { 288 reason = "BitsLength() differs"; 289 return false; 290 } 291 292 size_t rowAlignment; 293 size_t pixelChunk; 294 size_t pixelsPerChunk; 295 if (get_pixel_size_for(bitmap1->ColorSpace(), &pixelChunk, &rowAlignment, 296 &pixelsPerChunk) != B_OK) { 297 reason = "get_pixel_size_for() not supported for this color space"; 298 return false; 299 } 300 if (pixelsPerChunk != 1) { 301 reason = "Unsupported color_space; IsSame(...) supports 1 pixels per chunk only"; 302 return false; 303 } 304 int32 bpp = (int32)pixelChunk; 305 uint8* row1 = (uint8*)bitmap1->Bits(); 306 uint8* row2 = (uint8*)bitmap2->Bits(); 307 int32 bpr = bitmap1->BytesPerRow(); 308 int32 width = bitmap1->Bounds().IntegerWidth() + 1; 309 int32 height = bitmap1->Bounds().IntegerHeight() + 1; 310 for (int y = 0; y < height; y ++, row1 += bpr, row2 += bpr) { 311 uint8* pixel1 = row1; 312 uint8* pixel2 = row2; 313 for (int x = 0; x < width; x ++, pixel1 += bpp, pixel2 += bpp) { 314 if (memcmp(pixel1, pixel2, bpp) != 0) { 315 setMismatchReason(x, y, pixel1, pixel2, bpp, reason); 316 return false; 317 } 318 } 319 } 320 321 reason = ""; 322 return true; 323 } 324 325 326 FlattenPictureTest::FlattenPictureTest() 327 { 328 } 329 330 BPicture * 331 FlattenPictureTest::SaveAndRestore(BPicture *picture) 332 { 333 BMallocIO *data = new BMallocIO(); 334 AutoDelete<BMallocIO> _data(data); 335 TEST_AND_RETURN(data == NULL, "BMallocIO could not be allocated for flattening the picture!" , NULL); 336 337 picture->Flatten(data); 338 339 data->Seek(0, SEEK_SET); 340 BPicture *archivedPicture = new BPicture(); 341 TEST_AND_RETURN(archivedPicture == NULL, "BPicture could not be allocated for unflattening the picture!" , NULL); 342 archivedPicture->Unflatten(data); 343 344 return archivedPicture; 345 } 346 347 ArchivePictureTest::ArchivePictureTest() 348 { 349 } 350 351 BPicture * 352 ArchivePictureTest::SaveAndRestore(BPicture *picture) 353 { 354 BMessage archive; 355 TEST_AND_RETURN(picture->Archive(&archive) != B_OK, "Picture could not be archived to BMessage", NULL); 356 357 BArchivable *archivable = BPicture::Instantiate(&archive); 358 AutoDelete<BArchivable> _archivable(archivable); 359 TEST_AND_RETURN(archivable == NULL, "Picture could not be instantiated from BMessage", NULL); 360 361 BPicture *archivedPicture = dynamic_cast<BPicture*>(archivable); 362 TEST_AND_RETURN(archivedPicture == NULL, "Picture could not be restored from BMessage", NULL); 363 364 _archivable.Release(); 365 return archivedPicture; 366 } 367 368