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
25template <class T>
26class AutoDelete
27{
28public:
29	AutoDelete(T *object) : fObject(object) { }
30	~AutoDelete() { delete fObject; fObject = NULL; }
31
32	T* Release() { T* object = fObject; fObject = NULL; return object; }
33
34private:
35	T *fObject;
36};
37
38class OffscreenBitmap {
39public:
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
47private:
48	BRect fFrame;
49	color_space fColorSpace;
50	status_t fStatus;
51
52	BBitmap *fBitmap;
53	BView *fView;
54};
55
56OffscreenBitmap::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
82OffscreenBitmap::~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
98BView *
99OffscreenBitmap::View()
100{
101	return fView;
102}
103
104BBitmap*
105OffscreenBitmap::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
124PictureTest::PictureTest()
125	: fColorSpace(B_RGBA32)
126	, fDirectBitmap(NULL)
127	, fBitmapFromPicture(NULL)
128	, fBitmapFromRestoredPicture(NULL)
129{
130}
131
132
133BBitmap*
134PictureTest::DirectBitmap(bool detach)
135{
136	BBitmap* bitmap = fDirectBitmap;
137	if (detach)
138		fDirectBitmap = NULL;
139	return bitmap;
140}
141
142BBitmap*
143PictureTest::BitmapFromPicture(bool detach)
144{
145	BBitmap* bitmap = fBitmapFromPicture;
146	if (detach)
147		fBitmapFromPicture = NULL;
148	return bitmap;
149}
150
151
152BBitmap*
153PictureTest::BitmapFromRestoredPicture(bool detach)
154{
155	BBitmap* bitmap = fBitmapFromRestoredPicture;
156	if (detach)
157		fBitmapFromRestoredPicture = NULL;
158	return bitmap;
159}
160
161
162PictureTest::~PictureTest()
163{
164	CleanUp();
165}
166
167void
168PictureTest::CleanUp()
169{
170	delete fBitmapFromPicture;
171	fBitmapFromPicture = NULL;
172	delete fBitmapFromRestoredPicture;
173	fBitmapFromRestoredPicture = NULL;
174	fErrorMessage = "";
175}
176
177void
178PictureTest::SetErrorMessage(const char *message)
179{
180	if (fErrorMessage.Length() == 0)
181		fErrorMessage = message;
182}
183
184bool
185PictureTest::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
223BBitmap *
224PictureTest::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
232BPicture *
233PictureTest::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
248BBitmap *
249PictureTest::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
262static 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
279bool
280PictureTest::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
326FlattenPictureTest::FlattenPictureTest()
327{
328}
329
330BPicture *
331FlattenPictureTest::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
347ArchivePictureTest::ArchivePictureTest()
348{
349}
350
351BPicture *
352ArchivePictureTest::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