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

View file

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

View file

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

View file

@ -3,3 +3,9 @@
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 "Color.h"
#include "Vector3.h"
namespace paysages {
namespace software {
@ -13,6 +16,20 @@ class SOFTWARESHARED_EXPORT CanvasFragment
{
public:
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 <cstring>
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 "CanvasFragment.h"
const int MAX_FRAGMENT_COUNT = 7;
namespace paysages {
namespace software {
@ -15,6 +19,16 @@ class SOFTWARESHARED_EXPORT CanvasPixel
{
public:
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 <cassert>
#include "CanvasPixel.h"
#define CHECK_COORDINATES() assert(x >= 0); \
assert(x < width); \
assert(y >= 0); \
assert(y < height)
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)
{
this->width = width;
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 software {
typedef struct {
double red;
double green;
double blue;
} CanvasPreviewPixel;
/**
* @brief Rectangular portion of a Canvas.
*
@ -21,20 +15,27 @@ class SOFTWARESHARED_EXPORT CanvasPortion
{
public:
CanvasPortion();
~CanvasPortion();
inline int getWidth() const {return width;}
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);
/**
* @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:
int width;
int height;
std::vector<CanvasPixel> *pixels;
int preview_width;
int preview_height;
std::vector<CanvasPreviewPixel> *preview_pixels;
CanvasPixel *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 \
SoftwareCanvasRenderer.cpp \
Rasterizer.cpp \
CanvasLiveClient.cpp
CanvasLiveClient.cpp \
CanvasPreview.cpp
HEADERS += SoftwareRenderer.h\
software_global.h \
@ -78,7 +79,8 @@ HEADERS += SoftwareRenderer.h\
CanvasFragment.h \
SoftwareCanvasRenderer.h \
Rasterizer.h \
CanvasLiveClient.h
CanvasLiveClient.h \
CanvasPreview.h
unix:!symbian {
maemo5 {

View file

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

View file

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