root/src/tests/kits/interface/flatten_picture/PictureTestCases.cpp
/*
 * Copyright 2007, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Michael Pfeiffer
 */

#include "PictureTestCases.h"

#include <GradientLinear.h>
#include <GradientRadial.h>
#include <GradientRadialFocus.h>
#include <GradientDiamond.h>
#include <GradientConic.h>

#include <stdio.h>

static const rgb_color kBlack = {0, 0, 0};
static const rgb_color kWhite = {255, 255, 255};
static const rgb_color kRed = {255, 0, 0};
static const rgb_color kGreen = {0, 255, 0};
static const rgb_color kBlue = {0, 0, 255};

static BPoint centerPoint(BRect rect)
{
        int x = (int)(rect.left + rect.IntegerWidth() / 2);
        int y = (int)(rect.top + rect.IntegerHeight() / 2);
        return BPoint(x, y);
}

static void testNoOp(BView *view, BRect frame)
{
        // no op
}

static void testDrawChar(BView *view, BRect frame)
{
        view->MovePenTo(frame.left, frame.bottom - 5);
        view->DrawChar('A');
        
        view->DrawChar('B', BPoint(frame.left + 20, frame.bottom - 5));
}

static void testDrawString(BView *view, BRect frame)
{
        BFont font;
        view->GetFont(&font);
        font_height height;
        font.GetHeight(&height);
        float baseline = frame.bottom - height.descent;
        // draw base line
        view->SetHighColor(kGreen);
        view->StrokeLine(BPoint(frame.left, baseline - 1), BPoint(frame.right, baseline -1));

        view->SetHighColor(kBlack);
        view->DrawString("Haiku [ÖÜÄöüä]", BPoint(frame.left, baseline));
}

static void testDrawStringWithLength(BView *view, BRect frame)
{
        BFont font;
        view->GetFont(&font);
        font_height height;
        font.GetHeight(&height);
        float baseline = frame.bottom - height.descent;
        // draw base line
        view->SetHighColor(kGreen);
        view->StrokeLine(BPoint(frame.left, baseline - 1), BPoint(frame.right, baseline -1));

        view->SetHighColor(kBlack);
        view->DrawString("Haiku [ÖÜÄöüä]", 13, BPoint(frame.left, baseline));
}


static void testDrawStringWithOffsets(BView* view, BRect frame)
{
        BFont font;
        view->GetFont(&font);
        font_height height;
        font.GetHeight(&height);
        float baseline = frame.bottom - height.descent;
        // draw base line
        view->SetHighColor(kGreen);
        view->StrokeLine(BPoint(frame.left, baseline - 1), BPoint(frame.right, baseline -1));

        view->SetHighColor(kBlack);
        BPoint point(frame.left, baseline);
        BPoint pointArray[] = {
                point,
                point,
                point,
                point,
                point
        };
        
        for (size_t i = 1; i < (sizeof(pointArray) / sizeof(pointArray[0])); i++)
                pointArray[i] = pointArray[i - 1] + BPoint(10, 0);

        view->DrawString("Haiku", pointArray, sizeof(pointArray) / sizeof(pointArray[0]));
}


static void testDrawStringWithoutPosition(BView* view, BRect frame)
{
        BFont font;
        view->GetFont(&font);
        font_height height;
        font.GetHeight(&height);
        float baseline = frame.bottom - height.descent;
        // draw base line
        view->SetHighColor(kGreen);
        view->StrokeLine(BPoint(frame.left, baseline - 1), BPoint(frame.right, baseline -1));

        view->SetHighColor(kBlack);
        view->MovePenTo(BPoint(frame.left, baseline));
        view->DrawString("H");
        view->DrawString("a");
        view->DrawString("i");
        view->DrawString("k");
        view->DrawString("u");
}


static void testFillArc(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->FillArc(frame, 45, 180);
}

static void testStrokeArc(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->StrokeArc(frame, 45, 180);
}

static void testFillBezier(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint points[4];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(frame.left, frame.top);
        points[1] = BPoint(frame.left, frame.top);
        points[3] = BPoint(frame.right, frame.top);
        view->FillBezier(points);
}

static void testStrokeBezier(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint points[4];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(frame.left, frame.top);
        points[1] = BPoint(frame.left, frame.top);
        points[3] = BPoint(frame.right, frame.top);
        view->StrokeBezier(points);
}

static void testFillEllipse(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->FillEllipse(frame);

        view->SetHighColor(kRed);
        float r = frame.Width() / 3;
        float s = frame.Height() / 4;
        view->FillEllipse(centerPoint(frame), r, s);
}

static void testStrokeEllipse(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->StrokeEllipse(frame);

        view->SetHighColor(kRed);
        float r = frame.Width() / 3;
        float s = frame.Height() / 4;
        view->StrokeEllipse(centerPoint(frame), r, s);
}

static void testFillPolygon(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);

        BPoint points[4];
        points[0] = BPoint(frame.left, frame.top);
        points[1] = BPoint(frame.right, frame.bottom);
        points[2] = BPoint(frame.right, frame.top);
        points[3] = BPoint(frame.left, frame.bottom);

        view->FillPolygon(points, 4);
}

static void testStrokePolygon(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);

        BPoint points[4];
        points[0] = BPoint(frame.left, frame.top);
        points[1] = BPoint(frame.right, frame.bottom);
        points[2] = BPoint(frame.right, frame.top);
        points[3] = BPoint(frame.left, frame.bottom);

        view->StrokePolygon(points, 4);
}

static void testFillRect(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->FillRect(frame);
}

static void testFillRectGradientLinear(BView* view, BRect frame)
{
        BGradientLinear gradient(0, 0, frame.right, frame.bottom);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRect(frame, gradient);
}

static void testFillRectGradientRadial(BView* view, BRect frame)
{
        BGradientRadial gradient(10, 10, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRect(frame, gradient);
}

static void testFillRectGradientRadialFocus(BView* view, BRect frame)
{
        BGradientRadialFocus gradient(0, 0, 10, 10, 5);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRect(frame, gradient);
}

static void testFillRectGradientDiamond(BView* view, BRect frame)
{
        BGradientDiamond gradient(0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRect(frame, gradient);
}

static void testFillRectGradientConic(BView* view, BRect frame)
{
        BGradientConic gradient(0, 0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRect(frame, gradient);
}

static void testStrokeRect(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->StrokeRect(frame);
}

static void testFillRegion(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BRegion region(frame);
        frame.InsetBy(10, 10);
        region.Exclude(frame);
        view->FillRegion(&region);
}

static void testFillRegionGradientLinear(BView* view, BRect frame)
{
        BGradientLinear gradient(0, 0, frame.right, frame.bottom);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BRegion region(frame);
        frame.InsetBy(10, 10);
        region.Exclude(frame);
        view->FillRegion(&region, gradient);
}

static void testFillRegionGradientRadial(BView* view, BRect frame)
{
        BGradientRadial gradient(10, 10, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BRegion region(frame);
        frame.InsetBy(10, 10);
        region.Exclude(frame);
        view->FillRegion(&region, gradient);
}

static void testFillRegionGradientRadialFocus(BView* view, BRect frame)
{
        BGradientRadialFocus gradient(0, 0, 10, 10, 5);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BRegion region(frame);
        frame.InsetBy(10, 10);
        region.Exclude(frame);
        view->FillRegion(&region, gradient);
}

static void testFillRegionGradientDiamond(BView* view, BRect frame)
{
        BGradientDiamond gradient(0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BRegion region(frame);
        frame.InsetBy(10, 10);
        region.Exclude(frame);
        view->FillRegion(&region, gradient);
}

static void testFillRegionGradientConic(BView* view, BRect frame)
{
        BGradientConic gradient(0, 0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BRegion region(frame);
        frame.InsetBy(10, 10);
        region.Exclude(frame);
        view->FillRegion(&region, gradient);
}

static void testFillRoundRect(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->FillRoundRect(frame, 5, 3);
}

static void testFillRoundRectGradientLinear(BView* view, BRect frame)
{
        BGradientLinear gradient(0, 0, frame.right, frame.bottom);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRoundRect(frame, 5, 3, gradient);
}

static void testFillRoundRectGradientRadial(BView* view, BRect frame)
{
        BGradientRadial gradient(10, 10, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRoundRect(frame, 5, 3, gradient);
}

static void testFillRoundRectGradientRadialFocus(BView* view, BRect frame)
{
        BGradientRadialFocus gradient(0, 0, 10, 10, 5);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        view->FillRoundRect(frame, 5, 3, gradient);
}

static void testFillRoundRectGradientDiamond(BView* view, BRect frame)
{
        BGradientDiamond gradient(0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRoundRect(frame, 5, 3, gradient);
}

static void testFillRoundRectGradientConic(BView* view, BRect frame)
{
        BGradientConic gradient(0, 0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        view->FillRoundRect(frame, 5, 3, gradient);
}

static void testStrokeRoundRect(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->StrokeRoundRect(frame, 5, 3);
}

static void testFillTriangle(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint points[3];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(centerPoint(frame).x, frame.top);
        points[2] = BPoint(frame.right, frame.bottom);
        view->FillTriangle(points[0], points[1], points[2]); 
}

static void testFillTriangleGradientLinear(BView* view, BRect frame)
{
        BGradientLinear gradient(0, 0, frame.right, frame.bottom);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BPoint points[3];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(centerPoint(frame).x, frame.top);
        points[2] = BPoint(frame.right, frame.bottom);
        view->FillTriangle(points[0], points[1], points[2], gradient);
}

static void testFillTriangleGradientRadial(BView* view, BRect frame)
{
        BGradientRadial gradient(10, 10, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BPoint points[3];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(centerPoint(frame).x, frame.top);
        points[2] = BPoint(frame.right, frame.bottom);
        view->FillTriangle(points[0], points[1], points[2], gradient);
}

static void testFillTriangleGradientRadialFocus(BView* view, BRect frame)
{
        BGradientRadialFocus gradient(0, 0, 10, 10, 5);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BPoint points[3];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(centerPoint(frame).x, frame.top);
        points[2] = BPoint(frame.right, frame.bottom);
        view->FillTriangle(points[0], points[1], points[2], gradient);
}

static void testFillTriangleGradientDiamond(BView* view, BRect frame)
{
        BGradientDiamond gradient(0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BPoint points[3];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(centerPoint(frame).x, frame.top);
        points[2] = BPoint(frame.right, frame.bottom);
        view->FillTriangle(points[0], points[1], points[2], gradient);
}

static void testFillTriangleGradientConic(BView* view, BRect frame)
{
        BGradientConic gradient(0, 0, 10);
        gradient.AddColor(kRed, 0);
        gradient.AddColor(kBlue, 255);
        frame.InsetBy(2, 2);
        BPoint points[3];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(centerPoint(frame).x, frame.top);
        points[2] = BPoint(frame.right, frame.bottom);
        view->FillTriangle(points[0], points[1], points[2], gradient);
}

static void testStrokeTriangle(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint points[3];
        points[0] = BPoint(frame.left, frame.bottom);
        points[1] = BPoint(centerPoint(frame).x, frame.top);
        points[2] = BPoint(frame.right, frame.bottom);
        view->StrokeTriangle(points[0], points[1], points[2]); 
}

static void testStrokeLine(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(frame.right, frame.top));
        
        frame.top += 2;
        frame.bottom -= 2;
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(frame.right, frame.bottom));
        
        frame.bottom += 2;;
        frame.top = frame.bottom;
        view->StrokeLine(BPoint(frame.right, frame.top), BPoint(frame.left, frame.top));
}

static void testFillShape(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BShape shape;
        shape.MoveTo(BPoint(frame.left, frame.bottom));
        shape.LineTo(BPoint(frame.right, frame.top));
        shape.LineTo(BPoint(frame.left, frame.top));
        shape.LineTo(BPoint(frame.right, frame.bottom));
        view->FillShape(&shape);
}

static void testStrokeShape(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BShape shape;
        shape.MoveTo(BPoint(frame.left, frame.bottom));
        shape.LineTo(BPoint(frame.right, frame.top));
        shape.LineTo(BPoint(frame.left, frame.top));
        shape.LineTo(BPoint(frame.right, frame.bottom));
        view->StrokeShape(&shape);
}

static void testRecordPicture(BView *view, BRect frame)
{
        BPicture *picture = new BPicture();
        view->BeginPicture(picture);
        view->FillRect(frame);
        view->EndPicture();
        delete picture;
}

static void testRecordAndPlayPicture(BView *view, BRect frame)
{
        BPicture *picture = new BPicture();
        view->BeginPicture(picture);
        frame.InsetBy(2, 2);
        view->FillRect(frame);
        view->EndPicture();
        view->DrawPicture(picture);
        delete picture;
}

static void testRecordAndPlayPictureWithOffset(BView *view, BRect frame)
{
        BPicture *picture = new BPicture();
        view->BeginPicture(picture);
        frame.InsetBy(frame.Width() / 4, frame.Height() / 4);
        frame.OffsetTo(0, 0);
        view->FillRect(frame);
        view->EndPicture();
        
        view->DrawPicture(picture, BPoint(10, 10));
        // color of picture should not change
        view->SetLowColor(kGreen);
        view->SetLowColor(kRed);
        view->DrawPicture(picture, BPoint(0, 0));
        delete picture;
}

static void testAppendToPicture(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->BeginPicture(new BPicture());
        view->FillRect(frame);
        BPicture* picture = view->EndPicture();
        if (picture == NULL)
                return;
        
        frame.InsetBy(2, 2);
        view->AppendToPicture(picture);
        view->SetHighColor(kRed);
        view->FillRect(frame);
        if (view->EndPicture() != picture)
                return;
        
        view->DrawPicture(picture);
        delete picture;
}

static void testDrawScaledPicture(BView* view, BRect frame)
{
        view->BeginPicture(new BPicture());
        view->FillRect(BRect(0, 0, 15, 15));
        BPicture* picture = view->EndPicture();

        // first unscaled at left, top
        view->DrawPicture(picture, BPoint(2, 2));
        
        // draw scaled at middle top
        view->SetScale(0.5);
        // the drawing offset must be scaled too!
        view->DrawPicture(picture, BPoint(frame.Width(), 4));
        
        delete picture;
}

static void testLineArray(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->BeginLineArray(3);
        view->AddLine(BPoint(frame.left, frame.top), BPoint(frame.right, frame.top), kBlack);
        
        frame.top += 2;
        frame.bottom -= 2;
        view->AddLine(BPoint(frame.left, frame.top), BPoint(frame.right, frame.bottom), kRed);
        
        frame.bottom += 2;;
        frame.top = frame.bottom;
        view->AddLine(BPoint(frame.right, frame.top), BPoint(frame.left, frame.top), kGreen);   
        
        view->EndLineArray();
}

static void testInvertRect(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->InvertRect(frame);
}

static void testInvertRectSetDrawingMode(BView *view, BRect frame)
{
        view->SetDrawingMode(B_OP_ALPHA);
        view->SetHighColor(128, 128, 128, 128);
        frame.InsetBy(2, 2);
        view->InvertRect(frame);
        frame.InsetBy(10, 10);
        view->FillRect(frame, B_SOLID_HIGH);
}

static bool isBorder(int32 x, int32 y, int32 width, int32 height) {
        return x == 0 || y == 0 || x == width - 1 || y == height - 1;
}

static void fillBitmap(BBitmap &bitmap) {
        int32 height = bitmap.Bounds().IntegerHeight()+1;
        int32 width = bitmap.Bounds().IntegerWidth()+1;
        for (int32 y = 0; y < height; y ++) {
                for (int32 x = 0; x < width; x ++) {
                        char *pixel = (char*)bitmap.Bits();
                        pixel += bitmap.BytesPerRow() * y + 4 * x;
                        if (isBorder(x, y, width, height)) {
                                // fill with green
                                pixel[0] = 255;
                                pixel[1] = 0;
                                pixel[2] = 255;
                                pixel[3] = 0;
                        } else  {
                                // fill with blue
                                pixel[0] = 255;
                                pixel[1] = 0;
                                pixel[2] = 0;
                                pixel[3] = 255;
                        }       
                }
        }
}

static void testDrawBitmap(BView *view, BRect frame) {
        BBitmap bitmap(frame, B_RGBA32);
        fillBitmap(bitmap);
        view->DrawBitmap(&bitmap, BPoint(0, 0));
}

static void testDrawBitmapAtPoint(BView *view, BRect frame) {
        frame.InsetBy(2, 2);
        
        BRect bounds(frame);
        bounds.OffsetTo(0, 0);
        bounds.right /= 2;
        bounds.bottom /= 2;
        
        BBitmap bitmap(bounds, B_RGBA32);
        fillBitmap(bitmap);
        view->DrawBitmap(&bitmap, centerPoint(frame));
}

static void testDrawBitmapAtRect(BView *view, BRect frame) {
        BRect bounds(frame);
        BBitmap bitmap(bounds, B_RGBA32);
        fillBitmap(bitmap);
        frame.InsetBy(2, 2);
        view->DrawBitmap(&bitmap, frame);
}

static void testDrawLargeBitmap(BView *view, BRect frame) {
        BRect bounds(frame);
        bounds.OffsetTo(0, 0);
        bounds.right *= 4;
        bounds.bottom *= 4;
        BBitmap bitmap(bounds, B_RGBA32);
        fillBitmap(bitmap);
        frame.InsetBy(2, 2);
        view->DrawBitmap(&bitmap, frame);
}

static void testConstrainClippingRegion(BView *view, BRect frame) 
{
        frame.InsetBy(2, 2);
        // draw background
        view->SetHighColor(kRed);
        view->FillRect(frame);
        
        frame.InsetBy(1, 1);
        BRegion region(frame);
        BRect r(frame);
        r.InsetBy(r.IntegerWidth() / 4, r.IntegerHeight() / 4);
        region.Exclude(r);
        view->ConstrainClippingRegion(&region);
        
        frame.InsetBy(-1, -1);
        view->SetHighColor(kBlack);
        view->FillRect(frame);
        // a filled black rectangle with a red one pixel border
        // and inside a red rectangle should be drawn.
}

static void testClipToPicture(BView *view, BRect frame) 
{
        frame.InsetBy(2, 2);
        view->BeginPicture(new BPicture());
        view->FillEllipse(frame);
        BPicture *picture = view->EndPicture();
        if (picture == NULL)
                return;
        
        view->ClipToPicture(picture);
        delete picture;
        
        view->FillRect(frame);
        // black ellipse should be drawn
}

static void testClipToInversePicture(BView *view, BRect frame) 
{
        frame.InsetBy(2, 2);
        
        view->BeginPicture(new BPicture());
        view->FillEllipse(frame);
        BPicture *picture = view->EndPicture();
        if (picture == NULL)
                return;
        
        view->ClipToInversePicture(picture);
        delete picture;
        
        view->FillRect(frame);
        // white ellipse inside a black rectangle
}

static void testSetPenSize(BView *view, BRect frame)
{
        frame.InsetBy(8, 2);
        float x = centerPoint(frame).x;
        
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(frame.right, frame.top));
        
        frame.OffsetBy(0, 5);
        view->SetPenSize(1);
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(x, frame.top));
        view->SetPenSize(0);
        view->StrokeLine(BPoint(x+1, frame.top), BPoint(frame.right, frame.top));

        frame.OffsetBy(0, 5);
        view->SetPenSize(1);
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(x, frame.top));
        view->SetPenSize(2);
        view->StrokeLine(BPoint(x+1, frame.top), BPoint(frame.right, frame.top));

        frame.OffsetBy(0, 5);
        view->SetPenSize(1);
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(x, frame.top));
        view->SetPenSize(3);
        view->StrokeLine(BPoint(x+1, frame.top), BPoint(frame.right, frame.top));

        frame.OffsetBy(0, 5);
        view->SetPenSize(1);
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(x, frame.top));
        view->SetPenSize(4);
        view->StrokeLine(BPoint(x+1, frame.top), BPoint(frame.right, frame.top));
}

static void testSetPenSize2(BView *view, BRect frame)
{
        // test if pen size is scaled too
        frame.InsetBy(2, 2);
        frame.OffsetBy(0, 5);
        view->SetPenSize(4);
        view->StrokeLine(BPoint(frame.left, frame.top), BPoint(frame.right, frame.top));
        view->SetScale(0.5);
        view->StrokeLine(BPoint(frame.left + 2, frame.bottom), BPoint(frame.right + 2, frame.bottom));  
        
        // black line from left to right, 4 pixel size
        // below black line with half the length of the first one
        // and 2 pixel size
}

static void testPattern(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        int x = frame.IntegerWidth() / 3;
        frame.right = frame.left + x - 2;
                // -2 for an empty pixel row between 
                // filled rectangles

        view->SetLowColor(kGreen);
        view->SetHighColor(kRed);
        
        view->FillRect(frame, B_SOLID_HIGH);
        
        frame.OffsetBy(x, 0);
        view->FillRect(frame, B_MIXED_COLORS);
        
        frame.OffsetBy(x, 0);
        view->FillRect(frame, B_SOLID_LOW);
}

static void testSetOrigin(BView *view, BRect frame)
{
        BPoint origin = view->Origin();
        BPoint center = centerPoint(frame);
        view->SetOrigin(center);

        BRect r(0, 0, center.x, center.y);
        view->SetHighColor(kBlue);
        view->FillRect(r);
        
        view->SetOrigin(origin);
        view->SetHighColor(kRed);
        view->FillRect(r);
        
        // red rectangle in left, top corner
        // blue rectangle in right, bottom corner
        // the red rectangle overwrites the
        // top, left pixel of the blue rectangle
}

static void testSetOrigin2(BView *view, BRect frame)
{
        BPoint center = centerPoint(frame);
        BRect r(0, 0, center.x, center.y);
        view->SetOrigin(center);
        view->PushState();
                view->SetOrigin(BPoint(-center.x, 0));
                view->FillRect(r);
        view->PopState();       
        // black rectangle in left, bottom corner
}

static void testSetScale(BView *view, BRect frame)
{
        view->SetScale(0.5);
        view->FillRect(frame);
        // black rectangle in left, top corner
}

static void testSetScale2(BView *view, BRect frame)
{
        view->SetScale(0.5);
        view->PushState();
                view->SetScale(0.5);
                view->FillRect(frame);
        view->PopState();       
        // black rectangle in left, top corner
        // with half the size of the rectangle
        // from test testSetScaling
}

static void testSetScale3(BView *view, BRect frame)
{
        view->SetScale(0.5);
        view->PushState();
                // if the second scale value differs slightly
                // the bug under BeOS R5 in testSetScale2 
                // does not occur
                view->SetScale(0.5000001);
                view->FillRect(frame);
        view->PopState();       
        // black rectangle in left, top corner
        // with half the size of the rectangle
        // from test testSetScaling
}

static void testSetOriginAndScale(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint center = centerPoint(frame);
        
        BRect r(0, 0, frame.IntegerWidth() / 2, frame.IntegerHeight() / 2);
        view->SetOrigin(center);
        view->FillRect(r);
        
        view->SetScale(0.5);
        view->SetHighColor(kRed);
        view->FillRect(r);
}

static void testSetOriginAndScale2(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint center = centerPoint(frame);
        
        BRect r(0, 0, frame.IntegerWidth() / 2, frame.IntegerHeight() / 2);
        view->SetOrigin(center);
        view->FillRect(r);
        
        view->SetScale(0.5);
        view->SetHighColor(kRed);
        view->FillRect(r);
        
        view->SetOrigin(0, 0);
        view->SetHighColor(kGreen);
        view->FillRect(r);
}

static void testSetOriginAndScale3(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint center = centerPoint(frame);
        
        BRect r(0, 0, frame.IntegerWidth() / 2, frame.IntegerHeight() / 2);
        view->SetOrigin(center);
        view->FillRect(r);
        
        view->SetScale(0.5);
        view->SetHighColor(kRed);
        view->FillRect(r);
        
        view->SetScale(0.25);
        view->SetHighColor(kGreen);
        view->FillRect(r);
}

static void testSetOriginAndScale4(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint center = centerPoint(frame);
        
        BRect r(0, 0, frame.IntegerWidth() / 2, frame.IntegerHeight() / 2);
        view->SetOrigin(center);
        view->FillRect(r);
        
        view->SetScale(0.5);
        view->SetHighColor(kRed);
        view->FillRect(r);
        
        view->PushState();
                // 
                view->SetOrigin(center.x+1, center.y);
                        // +1 to work around BeOS bug
                        // where setting the origin has no
                        // effect if it is the same as
                        // the previous value althou
                        // it is from the "outer" coordinate
                        // system
                view->SetHighColor(kGreen);
                view->FillRect(r);
        view->PopState();
}

static void testSetOriginAndScale5(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        BPoint center = centerPoint(frame);
        
        BRect r(0, 0, frame.IntegerWidth() / 2, frame.IntegerHeight() / 2);
        view->SetOrigin(center);
        view->FillRect(r);
        
        view->SetScale(0.5);
        view->SetHighColor(kRed);
        view->FillRect(r);
        
        view->PushState();
        view->SetScale(0.75);
        view->SetHighColor(kGreen);
        view->FillRect(r);
        view->PopState();
}

static void testSetFontSize(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        int size = frame.IntegerHeight() / 3;
        
        frame.OffsetBy(0, size);
        view->MovePenTo(BPoint(frame.left, frame.top));
        view->SetFontSize(size);
        view->DrawString("Haiku");
        
        size *= 2;      
        frame.OffsetBy(0, size);
        view->MovePenTo(BPoint(frame.left, frame.top));
        view->SetFontSize(size);
        view->DrawString("Haiku");      
}

static void testSetFontFamilyAndStyle(BView *view, BRect frame)
{
        view->DrawString("This is a test", BPoint(2, 6));
        
        BFont font;
        view->GetFont(&font);
        
        int32 families = count_font_families();
        font_family familyName;
        get_font_family(families - 1, &familyName);
        
        int32 styles = count_font_styles(familyName);
        font_style styleName;
        get_font_style(familyName, styles - 1, &styleName);
        font.SetFamilyAndStyle(familyName, styleName);
        view->SetFont(&font);
        view->DrawString( "This is a test", BPoint(2, 19));
}

static void testSetDrawingMode(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->StrokeLine(frame.LeftTop(), frame.RightBottom());
        view->StrokeLine(frame.LeftBottom(), frame.RightTop());
        view->SetDrawingMode(B_OP_ALPHA);
        rgb_color color = kRed;
        color.alpha = 127;      
        view->SetHighColor(color);
        view->FillRect(frame, B_SOLID_HIGH);
}

static void testPushPopState(BView *view, BRect frame)
{
        frame.InsetBy(2, 2);
        view->SetHighColor(kGreen);
        view->PushState();
        view->SetHighColor(kRed);
        view->PopState();
        
        view->FillRect(frame, B_SOLID_HIGH);
}

static void testFontRotation(BView* view, BRect frame)
{
        BFont font;
        view->GetFont(&font);
        
        font.SetRotation(90);
        view->SetFont(&font, B_FONT_ROTATION);
        view->DrawString("This is a test!", BPoint(frame.Width() / 2, frame.bottom - 3));
        
        view->GetFont(&font);
        if (font.Rotation() != 90.0)
                fprintf(stderr, "Error: Rotation is %f but should be 90.0\n", font.Rotation());
}


static void testClipToRect(BView* view, BRect frame)
{
        BRect clipped = frame;
        clipped.InsetBy(5, 5);
        
        view->ClipToRect(clipped);
        
        view->FillRect(frame);
}


static void testClipToInverseRect(BView* view, BRect frame)
{
        BRect clipped = frame;
        clipped.InsetBy(5, 5);
        
        view->ClipToInverseRect(clipped);
        
        view->FillRect(frame);
}


static void testClipToShape(BView* view, BRect frame)
{
        frame.InsetBy(2, 2);
        BShape shape;
        shape.MoveTo(BPoint(frame.left, frame.bottom));
        shape.LineTo(BPoint(frame.right, frame.top));
        shape.LineTo(BPoint(frame.left, frame.top));
        shape.LineTo(BPoint(frame.right, frame.bottom));
        view->ClipToShape(&shape);
        
        view->FillRect(frame);
}


static void testClipToInverseShape(BView* view, BRect frame)
{
        frame.InsetBy(2, 2);
        BShape shape;
        shape.MoveTo(BPoint(frame.left, frame.bottom));
        shape.LineTo(BPoint(frame.right, frame.top));
        shape.LineTo(BPoint(frame.left, frame.top));
        shape.LineTo(BPoint(frame.right, frame.bottom));
        view->ClipToInverseShape(&shape);
        
        view->FillRect(frame);
}


// TODO
// - blending mode
// - line mode
// - push/pop state
// - move pen
// - set font


TestCase gTestCases[] = {
        { "Test No Operation", testNoOp },
        { "Test DrawChar", testDrawChar },
        { "Test Draw String", testDrawString },
        { "Test Draw String With Length", testDrawStringWithLength },
        { "Test Draw String With Offsets", testDrawStringWithOffsets },
        { "Test Draw String Without Position", testDrawStringWithoutPosition },
        { "Test FillArc", testFillArc },
        { "Test StrokeArc", testStrokeArc },
        // testFillBezier fails under BeOS because the
        // direct draw version is not correct
        { "Test FillBezier", testFillBezier },
        { "Test StrokeBezier", testStrokeBezier },
        { "Test FillEllipse", testFillEllipse },
        { "Test StrokeEllipse", testStrokeEllipse },
        { "Test FillPolygon", testFillPolygon },
        { "Test StrokePolygon", testStrokePolygon },
        { "Test FillRect", testFillRect },
        { "Test FillRectGradientLinear", testFillRectGradientLinear },
        { "Test FillRectGradientRadial", testFillRectGradientRadial },
        { "Test FillRectGradientRadialFocus", testFillRectGradientRadialFocus },
        { "Test FillRectGradientDiamond", testFillRectGradientDiamond },
        { "Test FillRectGradientConic", testFillRectGradientConic },
        { "Test StrokeRect", testStrokeRect },
        { "Test FillRegion", testFillRegion },
        { "Test FillRegionGradientLinear", testFillRegionGradientLinear },
        { "Test FillRegionGradientRadial", testFillRegionGradientRadial },
        { "Test FillRegionGradientRadialFocus", testFillRegionGradientRadialFocus },
        { "Test FillRegionGradientDiamond", testFillRegionGradientDiamond },
        { "Test FillRegionGradientConic", testFillRegionGradientConic },
        { "Test FillRoundRect", testFillRoundRect },
        { "Test FillRoundRectGradientLinear", testFillRoundRectGradientLinear },
        { "Test FillRoundRectGradientRadial", testFillRoundRectGradientRadial },
        { "Test FillRoundRectGradientRadialFocus", testFillRoundRectGradientRadialFocus },
        { "Test FillRoundRectGradientDiamond", testFillRoundRectGradientDiamond },
        { "Test FillRoundRectGradientConic", testFillRoundRectGradientConic },
        { "Test StrokeRoundRect", testStrokeRoundRect },
        { "Test FillTriangle", testFillTriangle },
        { "Test FillTriangleGradientLinear", testFillTriangleGradientLinear },
        { "Test FillTriangleGradientRadial", testFillTriangleGradientRadial },
        { "Test FillTriangleGradientRadialFocus", testFillTriangleGradientRadialFocus },
        { "Test FillTriangleGradientDiamond", testFillTriangleGradientDiamond },
        { "Test FillTriangleGradientConic", testFillTriangleGradientConic },
        { "Test StrokeTriangle", testStrokeTriangle },
        { "Test StrokeLine", testStrokeLine },
        { "Test FillShape", testFillShape },
        { "Test StrokeShape", testStrokeShape },
        { "Test Record Picture", testRecordPicture },
        { "Test Record And Play Picture", testRecordAndPlayPicture },
        { "Test Record And Play Picture With Offset", testRecordAndPlayPictureWithOffset },
        { "Test AppendToPicture", testAppendToPicture },
        { "Test Draw Scaled Picture", testDrawScaledPicture },
        { "Test LineArray", testLineArray },
        { "Test InvertRect", testInvertRect },
        { "Test InvertRectSetDrawingMode", testInvertRectSetDrawingMode },
        { "Test DrawBitmap", testDrawBitmap },
        { "Test DrawBitmapAtPoint", testDrawBitmapAtPoint },
        { "Test DrawBitmapAtRect", testDrawBitmapAtRect },
        { "Test DrawLargeBitmap", testDrawLargeBitmap },
        { "Test ConstrainClippingRegion", testConstrainClippingRegion }, 
        { "Test ClipToPicture", testClipToPicture },
        { "Test ClipToInversePicture", testClipToInversePicture },
        { "Test ClipToRect", testClipToRect },
        { "Test ClipToInverseRect", testClipToInverseRect },
        { "Test ClipToShape", testClipToShape },
        { "Test ClipToInverseShape", testClipToInverseShape },
        { "Test SetPenSize", testSetPenSize },
        { "Test SetPenSize2", testSetPenSize2 },
        { "Test Pattern", testPattern },
        { "Test SetOrigin", testSetOrigin },
        { "Test SetOrigin2", testSetOrigin2 },
        { "Test SetScale", testSetScale },
        // testSetScale2 fails under BeOS. The picture versions of the
        // rectangle are twice as large as the direct draw version
        { "Test SetScale2", testSetScale2 },
        { "Test SetScale3", testSetScale3 },
        { "Test SetOriginAndScale", testSetOriginAndScale },
        { "Test SetOriginAndScale2", testSetOriginAndScale2 },
        { "Test SetOriginAndScale3", testSetOriginAndScale3 },
        { "Test SetOriginAndScale4", testSetOriginAndScale4 },
        { "Test SetOriginAndScale5", testSetOriginAndScale5 },
        { "Test SetFontSize", testSetFontSize },
        { "Test SetFontFamilyAndStyle", testSetFontFamilyAndStyle },
        { "Test SetDrawingMode", testSetDrawingMode },
        { "Test PushPopState", testPushPopState },
        { "Test FontRotation", testFontRotation },
        { NULL, NULL }
};