xref: /haiku/src/tests/kits/interface/flatten_picture/PictureTest.cpp (revision b5c0078dc0013189eeb82ba7eef5a741230da3c6)
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