WIP on new canvas system

This commit is contained in:
Michaël Lemaire 2014-06-10 15:13:16 +02:00
parent 43431aae87
commit 8099361cc9
16 changed files with 381 additions and 30 deletions

View file

@ -1,6 +1,7 @@
#include "WidgetPreviewCanvas.h" #include "WidgetPreviewCanvas.h"
#include "Canvas.h" #include "Canvas.h"
#include "CanvasPreview.h"
WidgetPreviewCanvas::WidgetPreviewCanvas(QWidget *parent) : WidgetPreviewCanvas::WidgetPreviewCanvas(QWidget *parent) :
QWidget(parent), canvas(NULL) QWidget(parent), canvas(NULL)
@ -31,10 +32,11 @@ void WidgetPreviewCanvas::canvasPainted(int x, int y, const Color &col)
void WidgetPreviewCanvas::timerEvent(QTimerEvent *) void WidgetPreviewCanvas::timerEvent(QTimerEvent *)
{ {
// Refresh the view // Refresh the view
CanvasPreview *preview = canvas->getPreview();
if (canvas) if (canvas)
{ {
int width = canvas->getPreviewWidth(); int width = preview->getWidth();
int height = canvas->getPreviewHeight(); int height = preview->getHeight();
if (QSize(width, height) != this->size()) if (QSize(width, height) != this->size())
{ {

View file

@ -1,17 +1,19 @@
#include "Canvas.h" #include "Canvas.h"
#include "CanvasPortion.h"
#include <cassert> #include <cassert>
#include "CanvasPortion.h"
#include "CanvasPreview.h"
Canvas::Canvas() Canvas::Canvas()
{ {
horizontal_portion_count = 1; horizontal_portion_count = 1;
vertical_portion_count = 1; vertical_portion_count = 1;
width = 1; width = 1;
height = 1; height = 1;
preview_width = 1;
preview_height = 1;
portions.push_back(new CanvasPortion()); portions.push_back(new CanvasPortion());
preview = new CanvasPreview();
} }
Canvas::~Canvas() Canvas::~Canvas()
@ -20,6 +22,7 @@ Canvas::~Canvas()
{ {
delete portion; delete portion;
} }
delete preview;
} }
void Canvas::setSize(int width, int height) void Canvas::setSize(int width, int height)
@ -63,8 +66,9 @@ void Canvas::setSize(int width, int height)
this->width = width; this->width = width;
this->height = height; this->height = height;
this->preview_width = width;
this->preview_height = height; // TODO Smaller preview
preview->setSize(width, height, width, height);
} }
CanvasPortion *Canvas::at(int x, int y) const CanvasPortion *Canvas::at(int x, int y) const

View file

@ -27,8 +27,7 @@ public:
inline int getWidth() const {return width;} inline int getWidth() const {return width;}
inline int getHeight() const {return height;} inline int getHeight() const {return height;}
inline int getPreviewWidth() const {return preview_width;} inline CanvasPreview *getPreview() const {return preview;}
inline int getPreviewHeight() const {return preview_height;}
private: private:
std::vector<CanvasPortion*> portions; std::vector<CanvasPortion*> portions;
@ -36,8 +35,8 @@ private:
int vertical_portion_count; int vertical_portion_count;
int width; int width;
int height; int height;
int preview_width;
int preview_height; CanvasPreview *preview;
}; };
} }

View file

@ -3,3 +3,9 @@
CanvasFragment::CanvasFragment() CanvasFragment::CanvasFragment()
{ {
} }
CanvasFragment::CanvasFragment(double z, const Vector3 &location, int client, bool opaque):
z(z), location(location), client(client), opaque(opaque)
{
color = COLOR_BLACK;
}

View file

@ -3,6 +3,9 @@
#include "software_global.h" #include "software_global.h"
#include "Color.h"
#include "Vector3.h"
namespace paysages { namespace paysages {
namespace software { namespace software {
@ -13,6 +16,20 @@ class SOFTWARESHARED_EXPORT CanvasFragment
{ {
public: public:
CanvasFragment(); CanvasFragment();
CanvasFragment(double z, const Vector3 &location, int client=0, bool opaque=true);
inline bool getOpaque() const {return opaque;}
inline double getZ() const {return z;}
inline const Vector3 &getLocation() const {return location;}
inline int getClient() const {return client;}
inline const Color &getColor() const {return color;}
private:
bool opaque;
double z;
Vector3 location;
int client;
Color color;
}; };
} }

View file

@ -1,5 +1,88 @@
#include "CanvasPixel.h" #include "CanvasPixel.h"
#include <cstring>
CanvasPixel::CanvasPixel() CanvasPixel::CanvasPixel()
{ {
count = 0;
}
const CanvasFragment *CanvasPixel::getFrontFragment() const
{
if (count == 0)
{
return NULL;
}
else
{
return fragments + (count - 1);
}
}
void CanvasPixel::reset()
{
count = 0;
}
void CanvasPixel::pushFragment(const CanvasFragment &fragment)
{
if (count == 0)
{
fragments[0] = fragment;
count = 1;
}
else
{
if (fragments[0].getOpaque() and fragment.getZ() > fragments[0].getZ())
{
// behind opaque fragment, don't bother
return;
}
// find expected position
int i = 0;
while (i < count and fragment.getZ() < fragments[i].getZ())
{
i++;
}
if (fragment.getOpaque())
{
// Discard fragments masked by the incoming opaque one
if (i < count)
{
memmove(fragments + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
count -= i;
}
else
{
count = 1;
}
fragments[0] = fragment;
}
else if (i < count)
{
// Need to make room for the incoming fragment
if (count < MAX_FRAGMENT_COUNT)
{
memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
fragments[i] = fragment;
count++;
}
}
else
{
if (count == MAX_FRAGMENT_COUNT)
{
// Replace nearest fragment
fragments[count - 1] = fragment;
}
else
{
// Append
fragments[count] = fragment;
count++;
}
}
}
} }

View file

@ -3,6 +3,10 @@
#include "software_global.h" #include "software_global.h"
#include "CanvasFragment.h"
const int MAX_FRAGMENT_COUNT = 7;
namespace paysages { namespace paysages {
namespace software { namespace software {
@ -15,6 +19,16 @@ class SOFTWARESHARED_EXPORT CanvasPixel
{ {
public: public:
CanvasPixel(); CanvasPixel();
inline int getFragmentCount() const {return count;}
const CanvasFragment *getFrontFragment() const;
void reset();
void pushFragment(const CanvasFragment &fragment);
private:
int count;
CanvasFragment fragments[MAX_FRAGMENT_COUNT];
}; };
} }

View file

@ -1,12 +1,62 @@
#include "CanvasPortion.h" #include "CanvasPortion.h"
#include <cassert>
#include "CanvasPixel.h"
#define CHECK_COORDINATES() assert(x >= 0); \
assert(x < width); \
assert(y >= 0); \
assert(y < height)
CanvasPortion::CanvasPortion() CanvasPortion::CanvasPortion()
{ {
width = 1;
height = 1;
pixels = new CanvasPixel[1];
}
CanvasPortion::~CanvasPortion()
{
delete[] pixels;
}
int CanvasPortion::getFragmentCount(int x, int y) const
{
CHECK_COORDINATES();
return pixels[y * width + x].getFragmentCount();
}
const CanvasFragment *CanvasPortion::getFrontFragment(int x, int y) const
{
CHECK_COORDINATES();
return pixels[y * width + x].getFrontFragment();
}
void CanvasPortion::clear()
{
int n = width * height;
for (int i = 0; i < n; i++)
{
pixels[i].reset();
}
} }
void CanvasPortion::setSize(int width, int height) void CanvasPortion::setSize(int width, int height)
{ {
this->width = width; this->width = width;
this->height = height; this->height = height;
// TODO Resize and clear pixels
delete[] pixels;
pixels = new CanvasPixel[width * height];
}
void CanvasPortion::pushFragment(int x, int y, const CanvasFragment &fragment)
{
CHECK_COORDINATES();
CanvasPixel &pixel = pixels[y * width + x];
pixel.pushFragment(fragment);
} }

View file

@ -6,12 +6,6 @@
namespace paysages { namespace paysages {
namespace software { namespace software {
typedef struct {
double red;
double green;
double blue;
} CanvasPreviewPixel;
/** /**
* @brief Rectangular portion of a Canvas. * @brief Rectangular portion of a Canvas.
* *
@ -21,20 +15,27 @@ class SOFTWARESHARED_EXPORT CanvasPortion
{ {
public: public:
CanvasPortion(); CanvasPortion();
~CanvasPortion();
inline int getWidth() const {return width;} inline int getWidth() const {return width;}
inline int getHeight() const {return height;} inline int getHeight() const {return height;}
int getFragmentCount(int x, int y) const;
const CanvasFragment *getFrontFragment(int x, int y) const;
void clear();
void setSize(int width, int height); void setSize(int width, int height);
/**
* @brief Add a fragment to the pixel located at (x, y).
*
* Checking x and y coordinates to be in the canvas portion should be done before this call.
*/
void pushFragment(int x, int y, const CanvasFragment &fragment);
private: private:
int width; int width;
int height; int height;
std::vector<CanvasPixel> *pixels; CanvasPixel *pixels;
int preview_width;
int preview_height;
std::vector<CanvasPreviewPixel> *preview_pixels;
}; };
} }

View file

@ -0,0 +1,22 @@
#include "CanvasPreview.h"
CanvasPreview::CanvasPreview()
{
width = 1;
height = 1;
pixels = new CanvasPreviewPixel[1];
}
CanvasPreview::~CanvasPreview()
{
delete [] pixels;
}
void CanvasPreview::setSize(int real_width, int real_height, int preview_width, int preview_height)
{
delete [] pixels;
pixels = new CanvasPreviewPixel[preview_width * preview_height];
width = preview_width;
height = preview_height;
}

View file

@ -0,0 +1,44 @@
#ifndef CANVASPREVIEW_H
#define CANVASPREVIEW_H
#include "software_global.h"
namespace paysages {
namespace software {
typedef struct {
double red;
double green;
double blue;
} CanvasPreviewPixel;
/**
* @brief Smaller preview of a Canvas rendering, that can be watched live.
*/
class SOFTWARESHARED_EXPORT CanvasPreview
{
public:
CanvasPreview();
~CanvasPreview();
inline int getWidth() const {return width;}
inline int getHeight() const {return height;}
void setSize(int real_width, int real_height, int preview_width, int preview_height);
void reset();
void initLive(CanvasLiveClient &client);
void updateLive(CanvasLiveClient &client);
void pushPixel(int real_x, int real_y, Color old_color, Color new_color);
private:
CanvasPreviewPixel *pixels;
int width;
int height;
};
}
}
#endif // CANVASPREVIEW_H

View file

@ -44,7 +44,8 @@ SOURCES += SoftwareRenderer.cpp \
CanvasFragment.cpp \ CanvasFragment.cpp \
SoftwareCanvasRenderer.cpp \ SoftwareCanvasRenderer.cpp \
Rasterizer.cpp \ Rasterizer.cpp \
CanvasLiveClient.cpp CanvasLiveClient.cpp \
CanvasPreview.cpp
HEADERS += SoftwareRenderer.h\ HEADERS += SoftwareRenderer.h\
software_global.h \ software_global.h \
@ -78,7 +79,8 @@ HEADERS += SoftwareRenderer.h\
CanvasFragment.h \ CanvasFragment.h \
SoftwareCanvasRenderer.h \ SoftwareCanvasRenderer.h \
Rasterizer.h \ Rasterizer.h \
CanvasLiveClient.h CanvasLiveClient.h \
CanvasPreview.h
unix:!symbian { unix:!symbian {
maemo5 { maemo5 {

View file

@ -52,6 +52,7 @@ namespace software {
class CanvasPixel; class CanvasPixel;
class CanvasFragment; class CanvasFragment;
class CanvasLiveClient; class CanvasLiveClient;
class CanvasPreview;
} }
} }

View file

@ -0,0 +1,104 @@
#include "BaseTestCase.h"
#include "CanvasPortion.h"
#include "CanvasFragment.h"
TEST(CanvasPortion, setSize)
{
CanvasPortion portion;
portion.setSize(150, 30);
EXPECT_EQ(150, portion.getWidth());
EXPECT_EQ(30, portion.getHeight());
}
TEST(CanvasPortion, pushFragment)
{
CanvasPortion portion;
CanvasFragment pushed;
const CanvasFragment *got;
int count;
portion.setSize(50, 50);
portion.pushFragment(10, 15, pushed);
count = portion.getFragmentCount(10, 14);
ASSERT_EQ(0, count);
got = portion.getFrontFragment(10, 14);
ASSERT_FALSE(got);
count = portion.getFragmentCount(10, 15);
ASSERT_EQ(1, count);
got = portion.getFrontFragment(10, 15);
ASSERT_TRUE(got);
}
TEST(CanvasPortion, pushFragment_opaque)
{
CanvasPortion portion;
CanvasFragment pushed;
portion.setSize(10, 10);
pushed = CanvasFragment(2.0, VECTOR_ZERO, 0);
portion.pushFragment(2, 2, pushed);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
pushed = CanvasFragment(4.0, VECTOR_ZERO, 0);
portion.pushFragment(2, 2, pushed);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
pushed = CanvasFragment(1.0, VECTOR_ZERO, 0);
portion.pushFragment(2, 2, pushed);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
EXPECT_DOUBLE_EQ(1.0, portion.getFrontFragment(2, 2)->getZ());
}
TEST(CanvasPortion, pushFragment_transparent)
{
CanvasPortion portion;
CanvasFragment pushed;
portion.setSize(10, 10);
pushed = CanvasFragment(4.0, VECTOR_ZERO, 0, false);
portion.pushFragment(2, 2, pushed);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
EXPECT_DOUBLE_EQ(4.0, portion.getFrontFragment(2, 2)->getZ());
pushed = CanvasFragment(3.0, VECTOR_ZERO, 0, true);
portion.pushFragment(2, 2, pushed);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
EXPECT_DOUBLE_EQ(3.0, portion.getFrontFragment(2, 2)->getZ());
pushed = CanvasFragment(2.0, VECTOR_ZERO, 0, false);
portion.pushFragment(2, 2, pushed);
ASSERT_EQ(2, portion.getFragmentCount(2, 2));
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
}
TEST(CanvasPortion, clear)
{
CanvasPortion portion;
CanvasFragment fragment;
portion.setSize(10, 10);
portion.pushFragment(5, 5, fragment);
EXPECT_EQ(0, portion.getFragmentCount(4, 5));
EXPECT_EQ(1, portion.getFragmentCount(5, 5));
portion.clear();
EXPECT_EQ(0, portion.getFragmentCount(4, 5));
EXPECT_EQ(0, portion.getFragmentCount(5, 5));
}

View file

@ -2,6 +2,7 @@
#include "Canvas.h" #include "Canvas.h"
#include "CanvasPortion.h" #include "CanvasPortion.h"
#include "CanvasPreview.h"
static void checkPortion(Canvas &canvas, int x, int y, int width, int height) static void checkPortion(Canvas &canvas, int x, int y, int width, int height)
{ {
@ -21,8 +22,8 @@ TEST(Canvas, SizingAndCutting)
canvas.setSize(200, 100); canvas.setSize(200, 100);
EXPECT_EQ(200, canvas.getWidth()); EXPECT_EQ(200, canvas.getWidth());
EXPECT_EQ(100, canvas.getHeight()); EXPECT_EQ(100, canvas.getHeight());
EXPECT_EQ(200, canvas.getPreviewWidth()); EXPECT_EQ(200, canvas.getPreview()->getWidth());
EXPECT_EQ(100, canvas.getPreviewHeight()); EXPECT_EQ(100, canvas.getPreview()->getHeight());
ASSERT_EQ(1, canvas.getHorizontalPortionCount()); ASSERT_EQ(1, canvas.getHorizontalPortionCount());
ASSERT_EQ(1, canvas.getVerticalPortionCount()); ASSERT_EQ(1, canvas.getVerticalPortionCount());
checkPortion(canvas, 0, 0, 200, 100); checkPortion(canvas, 0, 0, 200, 100);
@ -30,8 +31,8 @@ TEST(Canvas, SizingAndCutting)
canvas.setSize(600, 501); canvas.setSize(600, 501);
EXPECT_EQ(600, canvas.getWidth()); EXPECT_EQ(600, canvas.getWidth());
EXPECT_EQ(501, canvas.getHeight()); EXPECT_EQ(501, canvas.getHeight());
EXPECT_EQ(600, canvas.getPreviewWidth()); EXPECT_EQ(600, canvas.getPreview()->getWidth());
EXPECT_EQ(501, canvas.getPreviewHeight()); EXPECT_EQ(501, canvas.getPreview()->getHeight());
ASSERT_EQ(2, canvas.getHorizontalPortionCount()); ASSERT_EQ(2, canvas.getHorizontalPortionCount());
ASSERT_EQ(2, canvas.getVerticalPortionCount()); ASSERT_EQ(2, canvas.getVerticalPortionCount());
checkPortion(canvas, 0, 0, 300, 250); checkPortion(canvas, 0, 0, 300, 250);

View file

@ -20,7 +20,8 @@ SOURCES += main.cpp \
FluidMediumManager_Test.cpp \ FluidMediumManager_Test.cpp \
VertexArray_Test.cpp \ VertexArray_Test.cpp \
FractalNoise_Test.cpp \ FractalNoise_Test.cpp \
Canvas_Test.cpp Canvas_Test.cpp \
CanvasPortion_Test.cpp
HEADERS += \ HEADERS += \
BaseTestCase.h BaseTestCase.h