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:
AutoDelete(T * object)29 AutoDelete(T *object) : fObject(object) { }
~AutoDelete()30 ~AutoDelete() { delete fObject; fObject = NULL; }
31
Release()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();
InitCheck() const42 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
OffscreenBitmap(BRect frame,color_space colorSpace)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
~OffscreenBitmap()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 *
View()99 OffscreenBitmap::View()
100 {
101 return fView;
102 }
103
104 BBitmap*
Copy()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
PictureTest()124 PictureTest::PictureTest()
125 : fColorSpace(B_RGBA32)
126 , fDirectBitmap(NULL)
127 , fBitmapFromPicture(NULL)
128 , fBitmapFromRestoredPicture(NULL)
129 {
130 }
131
132
133 BBitmap*
DirectBitmap(bool detach)134 PictureTest::DirectBitmap(bool detach)
135 {
136 BBitmap* bitmap = fDirectBitmap;
137 if (detach)
138 fDirectBitmap = NULL;
139 return bitmap;
140 }
141
142 BBitmap*
BitmapFromPicture(bool detach)143 PictureTest::BitmapFromPicture(bool detach)
144 {
145 BBitmap* bitmap = fBitmapFromPicture;
146 if (detach)
147 fBitmapFromPicture = NULL;
148 return bitmap;
149 }
150
151
152 BBitmap*
BitmapFromRestoredPicture(bool detach)153 PictureTest::BitmapFromRestoredPicture(bool detach)
154 {
155 BBitmap* bitmap = fBitmapFromRestoredPicture;
156 if (detach)
157 fBitmapFromRestoredPicture = NULL;
158 return bitmap;
159 }
160
161
~PictureTest()162 PictureTest::~PictureTest()
163 {
164 CleanUp();
165 }
166
167 void
CleanUp()168 PictureTest::CleanUp()
169 {
170 delete fBitmapFromPicture;
171 fBitmapFromPicture = NULL;
172 delete fBitmapFromRestoredPicture;
173 fBitmapFromRestoredPicture = NULL;
174 fErrorMessage = "";
175 }
176
177 void
SetErrorMessage(const char * message)178 PictureTest::SetErrorMessage(const char *message)
179 {
180 if (fErrorMessage.Length() == 0)
181 fErrorMessage = message;
182 }
183
184 bool
Test(draw_func * func,BRect frame)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 *
CreateBitmap(draw_func * func,BRect frame)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 *
RecordPicture(draw_func * func,BRect frame)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 *
CreateBitmap(BPicture * picture,BRect frame)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
setMismatchReason(int32 x,int32 y,uint8 * pixel1,uint8 * pixel2,int32 bpp,BString & reason)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
IsSame(BBitmap * bitmap1,BBitmap * bitmap2,BString & reason)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
FlattenPictureTest()326 FlattenPictureTest::FlattenPictureTest()
327 {
328 }
329
330 BPicture *
SaveAndRestore(BPicture * picture)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
ArchivePictureTest()347 ArchivePictureTest::ArchivePictureTest()
348 {
349 }
350
351 BPicture *
SaveAndRestore(BPicture * picture)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