⛏️ index : haiku.git

/*
 * Copyright 2014 Stephan Aßmus <superstippi@gmx.de>
 * All rights reserved. Distributed under the terms of the MIT license.
 */


#include <algorithm>
#include <stdio.h>
#include <string.h>

#include <Application.h>
#include <Bitmap.h>
#include <GradientLinear.h>
#include <Picture.h>
#include <Region.h>
#include <Resources.h>
#include <Roster.h>
#include <String.h>
#include <StringView.h>

#include "harness.h"

static const char* kAppSignature = "application/x-vnd.Haiku-Transformation";


class BitmapTest : public Test {
public:
	BitmapTest(const char* name)
		:
		Test(name),
		fBitmap(_LoadBitmap(555))
	{
	}

private:
	status_t
	_GetAppResources(BResources& resources) const
	{
		app_info info;
		status_t status = be_app->GetAppInfo(&info);
		if (status != B_OK)
			return status;
	
		return resources.SetTo(&info.ref);
	}


	BBitmap* _LoadBitmap(int resourceID) const
	{
		BResources resources;
		status_t status = _GetAppResources(resources);
		if (status != B_OK)
			return NULL;
	
		size_t dataSize;
		const void* data = resources.LoadResource(B_MESSAGE_TYPE, resourceID,
			&dataSize);
		if (data == NULL)
			return NULL;

		BMemoryIO stream(data, dataSize);

		// Try to read as an archived bitmap.
		BMessage archive;
		status = archive.Unflatten(&stream);
		if (status != B_OK)
			return NULL;

		BBitmap* bitmap = new BBitmap(&archive);

		status = bitmap->InitCheck();
		if (status != B_OK) {
			delete bitmap;
			bitmap = NULL;
		}

		return bitmap;
	}

protected:
	BBitmap*	fBitmap;
};


// #pragma mark - Test1


class RectsTest : public Test {
public:
	RectsTest()
		:
		Test("Rects")
	{
	}
	
	virtual void Draw(BView* view, BRect updateRect)
	{
		view->DrawString("Rects", BPoint(20, 30));

		view->SetDrawingMode(B_OP_ALPHA);
		view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);

		BRect rect(view->Bounds());
		rect.OffsetTo(B_ORIGIN);

		rect.InsetBy(rect.Width() / 3, rect.Height() / 3);
		BPoint center(
			rect.left + rect.Width() / 2,
			rect.top + rect.Height() / 2);

		for (int32 i = 0; i < 360; i += 40) {
			BAffineTransform transform;
			transform.RotateBy(center, i * M_PI / 180.0);
			view->SetTransform(transform);

			view->SetHighColor(51, 151, 255, 20);
			view->FillRect(rect);

			view->SetHighColor(51, 255, 151, 180);
			view->DrawString("Rect", center);
		}
	}
};


// #pragma mark - AlphaMaskBitmapTest


class AlphaMaskBitmapTest : public BitmapTest {
public:
	AlphaMaskBitmapTest()
		:
		BitmapTest("Alpha Masked Bitmap")
	{
	}

	virtual void Draw(BView* view, BRect updateRect)
	{
		BRect rect(view->Bounds());

		if (fBitmap == NULL) {
			view->SetHighColor(255, 0, 0);
			view->FillRect(rect);
			view->SetHighColor(0, 0, 0);
			view->DrawString("Failed to load the bitmap.", BPoint(20, 20));
			return;
		}

		rect.left = (rect.Width() - fBitmap->Bounds().Width()) / 2;
		rect.top = (rect.Height() - fBitmap->Bounds().Height()) / 2;
		rect.right = rect.left + fBitmap->Bounds().Width();
		rect.bottom = rect.top + fBitmap->Bounds().Height();

		BPoint center(
			rect.left + rect.Width() / 2,
			rect.top + rect.Height() / 2);

		BPicture picture;
		view->BeginPicture(&picture);
		view->SetDrawingMode(B_OP_ALPHA);
		view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
		BFont font;
		view->GetFont(&font);
		font.SetSize(70);
		view->SetFont(&font);
		view->SetHighColor(0, 0, 0, 80);
		view->FillRect(view->Bounds());
		view->SetHighColor(0, 0, 0, 255);
		view->DrawString("CLIPPING", BPoint(0, center.y + 35));
		view->EndPicture();

		view->ClipToPicture(&picture);

		BAffineTransform transform;
			transform.RotateBy(center, 30 * M_PI / 180.0);
			view->SetTransform(transform);
	
		view->DrawBitmap(fBitmap, fBitmap->Bounds(), rect);
	}
};


// #pragma mark - Gradient


class GradientTest : public Test {
public:
	GradientTest()
		:
		Test("Gradient")
	{
	}
	
	virtual void Draw(BView* view, BRect updateRect)
	{
		BRect rect(view->Bounds());
		rect.InsetBy(rect.Width() / 3, rect.Height() / 3);
		BPoint center(
			rect.left + rect.Width() / 2,
			rect.top + rect.Height() / 2);

		BAffineTransform transform;
		transform.RotateBy(center, 30.0 * M_PI / 180.0);
		view->SetTransform(transform);

		rgb_color top = (rgb_color){ 255, 255, 0, 255 };
		rgb_color bottom = (rgb_color){ 0, 255, 255, 255 };

		BGradientLinear gradient;
		gradient.AddColor(top, 0.0f);
		gradient.AddColor(bottom, 255.0f);
		gradient.SetStart(rect.LeftTop());
		gradient.SetEnd(rect.LeftBottom());

		float radius = std::min(rect.Width() / 5, rect.Height() / 5);

		view->FillRoundRect(rect, radius, radius, gradient);
	}
};


// #pragma mark - NestedStates


class NestedStatesTest : public Test {
public:
	NestedStatesTest()
		:
		Test("Nested view states")
	{
	}
	
	virtual void Draw(BView* view, BRect updateRect)
	{
		BAffineTransform transform;
		transform.RotateBy(BPoint(100, 100), 30.0 * M_PI / 180.0);
		view->SetTransform(transform);

		rgb_color top = (rgb_color){ 255, 0, 0, 255 };
		rgb_color bottom = (rgb_color){ 255, 255, 0, 255 };

		BRect rect(20, 20, 120, 120);

		BGradientLinear gradient;
		gradient.AddColor(top, 0.0f);
		gradient.AddColor(bottom, 255.0f);
		gradient.SetStart(rect.LeftTop());
		gradient.SetEnd(rect.LeftBottom());

		view->FillRoundRect(rect, 20, 20, gradient);

		view->PushState();
		// Should be in the same place!
		view->StrokeRoundRect(rect, 20, 20);

		// Now rotated by another 30 degree
		view->SetTransform(transform);

		view->SetDrawingMode(B_OP_ALPHA);
		view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
		view->SetHighColor(0, 0, 255, 120);
		view->FillRoundRect(rect, 20, 20);

		view->PopState();
	}
};


// #pragma mark - Clipping


class ClippingTest : public Test {
public:
	ClippingTest()
		:
		Test("View bounds clipping")
	{
	}

	virtual void Draw(BView* view, BRect updateRect)
	{
		BRect r (20, 20, 50, 50);
		view->SetHighColor(ui_color(B_FAILURE_COLOR));
		view->FillRect(r);

		BAffineTransform transform;
		transform.TranslateBy(400, 400);
		view->SetTransform(transform);

		// Make sure this rectangle is drawn, even when the original one is out
		// of the view bounds (for example because of scrolling).
		view->SetHighColor(ui_color(B_SUCCESS_COLOR));
		view->FillRect(r);
	}
};


// #pragma mark - Clipping


class TextClippingTest : public Test {
public:
	TextClippingTest()
		:
		Test("Text clipping")
	{
	}

	virtual void Draw(BView* view, BRect updateRect)
	{
		BFont font;
		view->GetFont(&font);
		font.SetSize(70);
		view->SetFont(&font);

		float width = view->Bounds().Width();

		// The translation make the text, which has negative coordinates, be
		// visible inside the viewport.
		BAffineTransform transform;
		transform.TranslateBy(width, 0);
		view->SetTransform(transform);

		const char* str = "CLIPPING";

		// Test the standard DrawString method

		// Draw the text bounds
		float size = view->StringWidth(str);
		BRect r(-width, 0, size - width, 70);
		view->SetHighColor(ui_color(B_SUCCESS_COLOR));
		view->FillRect(r);

		// Draw the text (which should fit inside the bounds rectangle)
		view->SetHighColor(0, 0, 0, 255);
		view->DrawString(str, BPoint(-width, 70));

		// Test with offset-based DrawString
		BPoint offsets[strlen(str)];
		for(unsigned int i = 0; i < strlen(str); i++)
		{
			offsets[i].x = i * 35 - width;
			offsets[i].y = 145;
		}

		// Draw the text bounds
		view->SetHighColor(ui_color(B_SUCCESS_COLOR));
		r = BRect(offsets[0], offsets[strlen(str) - 1]);
		r.top = 75;
		view->FillRect(r);

		// Draw the text (which should fit inside the bounds rectangle)
		view->SetHighColor(0, 0, 0, 255);
		view->DrawString(str, offsets, strlen(str));

	}
};


// #pragma mark - BitmapClipTest


class BitmapClipTest : public BitmapTest {
public:
	BitmapClipTest()
		:
		BitmapTest("Bitmap clipping")
	{
	}

	virtual void Draw(BView* view, BRect updateRect)
	{
		BRect rect(view->Bounds());

		if (fBitmap == NULL) {
			view->SetHighColor(255, 0, 0);
			view->FillRect(rect);
			view->SetHighColor(0, 0, 0);
			view->DrawString("Failed to load the bitmap.", BPoint(20, 20));
			return;
		}

		rect = fBitmap->Bounds();

		view->SetHighColor(ui_color(B_FAILURE_COLOR));
		view->FillRect(rect);

		// The rect offset should compensate the transform translation, so the
		// bitmap should be drawn at the view origin. It will then exactly
		// cover the red rectangle, which should not be visible anymore.
		rect.OffsetBy(0, 40);

		BAffineTransform transform;
			transform.TranslateBy(0, -40);
		view->SetTransform(transform);

		view->DrawBitmap(fBitmap, fBitmap->Bounds(), rect);
	}
};


// #pragma mark - PixelAlignTest


class PixelAlignTest : public Test {
public:
	PixelAlignTest()
		:
		Test("Pixel alignment")
	{
	}

	virtual void Draw(BView* view, BRect updateRect)
	{
		BRect rect(20, 20, 120, 120);
		view->SetHighColor(ui_color(B_SUCCESS_COLOR));
		view->StrokeRect(rect);

		BAffineTransform transform;
			transform.TranslateBy(140, 0);
		view->SetTransform(transform);

		// Translating a pixel-aligned rectangle by an integer number of
		// pixels should result in a pixel-aligned rectangle.
		view->SetHighColor(ui_color(B_FAILURE_COLOR));
		view->StrokeRect(rect);
	}
};


// #pragma mark -


int
main(int argc, char** argv)
{
	BApplication app(kAppSignature);

	TestWindow* window = new TestWindow("Transformation tests");

	window->AddTest(new RectsTest());
	window->AddTest(new BitmapClipTest());
	window->AddTest(new TextClippingTest());
	window->AddTest(new AlphaMaskBitmapTest());
	window->AddTest(new GradientTest());
	window->AddTest(new NestedStatesTest());
	window->AddTest(new ClippingTest());
	window->AddTest(new PixelAlignTest());

	window->SetToTest(2);
	window->Show();

	app.Run();
	return 0;
}