From 8099361cc918f67baa357e1b5da59a1925330005 Mon Sep 17 00:00:00 2001 From: Michael Lemaire Date: Tue, 10 Jun 2014 15:13:16 +0200 Subject: [PATCH] WIP on new canvas system --- src/interface/desktop/WidgetPreviewCanvas.cpp | 6 +- src/render/software/Canvas.cpp | 14 ++- src/render/software/Canvas.h | 7 +- src/render/software/CanvasFragment.cpp | 6 + src/render/software/CanvasFragment.h | 17 +++ src/render/software/CanvasPixel.cpp | 83 ++++++++++++++ src/render/software/CanvasPixel.h | 14 +++ src/render/software/CanvasPortion.cpp | 52 ++++++++- src/render/software/CanvasPortion.h | 23 ++-- src/render/software/CanvasPreview.cpp | 22 ++++ src/render/software/CanvasPreview.h | 44 ++++++++ src/render/software/software.pro | 6 +- src/render/software/software_global.h | 1 + src/tests/CanvasPortion_Test.cpp | 104 ++++++++++++++++++ src/tests/Canvas_Test.cpp | 9 +- src/tests/tests.pro | 3 +- 16 files changed, 381 insertions(+), 30 deletions(-) create mode 100644 src/render/software/CanvasPreview.cpp create mode 100644 src/render/software/CanvasPreview.h create mode 100644 src/tests/CanvasPortion_Test.cpp diff --git a/src/interface/desktop/WidgetPreviewCanvas.cpp b/src/interface/desktop/WidgetPreviewCanvas.cpp index 3c104e2..ade72a8 100644 --- a/src/interface/desktop/WidgetPreviewCanvas.cpp +++ b/src/interface/desktop/WidgetPreviewCanvas.cpp @@ -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()) { diff --git a/src/render/software/Canvas.cpp b/src/render/software/Canvas.cpp index 09a890e..ee82e3f 100644 --- a/src/render/software/Canvas.cpp +++ b/src/render/software/Canvas.cpp @@ -1,17 +1,19 @@ #include "Canvas.h" -#include "CanvasPortion.h" #include +#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 diff --git a/src/render/software/Canvas.h b/src/render/software/Canvas.h index 4934e06..edd8e80 100644 --- a/src/render/software/Canvas.h +++ b/src/render/software/Canvas.h @@ -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 portions; @@ -36,8 +35,8 @@ private: int vertical_portion_count; int width; int height; - int preview_width; - int preview_height; + + CanvasPreview *preview; }; } diff --git a/src/render/software/CanvasFragment.cpp b/src/render/software/CanvasFragment.cpp index 2d105dd..c5a5564 100644 --- a/src/render/software/CanvasFragment.cpp +++ b/src/render/software/CanvasFragment.cpp @@ -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; +} diff --git a/src/render/software/CanvasFragment.h b/src/render/software/CanvasFragment.h index f1b3b5a..7175add 100644 --- a/src/render/software/CanvasFragment.h +++ b/src/render/software/CanvasFragment.h @@ -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; }; } diff --git a/src/render/software/CanvasPixel.cpp b/src/render/software/CanvasPixel.cpp index c7c1b5c..7cc4cf6 100644 --- a/src/render/software/CanvasPixel.cpp +++ b/src/render/software/CanvasPixel.cpp @@ -1,5 +1,88 @@ #include "CanvasPixel.h" +#include + 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++; + } + } + } } diff --git a/src/render/software/CanvasPixel.h b/src/render/software/CanvasPixel.h index 4a00c62..da32e3f 100644 --- a/src/render/software/CanvasPixel.h +++ b/src/render/software/CanvasPixel.h @@ -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]; }; } diff --git a/src/render/software/CanvasPortion.cpp b/src/render/software/CanvasPortion.cpp index c44e15f..e017592 100644 --- a/src/render/software/CanvasPortion.cpp +++ b/src/render/software/CanvasPortion.cpp @@ -1,12 +1,62 @@ #include "CanvasPortion.h" +#include + +#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); } diff --git a/src/render/software/CanvasPortion.h b/src/render/software/CanvasPortion.h index 274d645..e735b7f 100644 --- a/src/render/software/CanvasPortion.h +++ b/src/render/software/CanvasPortion.h @@ -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 *pixels; - - int preview_width; - int preview_height; - std::vector *preview_pixels; + CanvasPixel *pixels; }; } diff --git a/src/render/software/CanvasPreview.cpp b/src/render/software/CanvasPreview.cpp new file mode 100644 index 0000000..3a7a427 --- /dev/null +++ b/src/render/software/CanvasPreview.cpp @@ -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; +} diff --git a/src/render/software/CanvasPreview.h b/src/render/software/CanvasPreview.h new file mode 100644 index 0000000..f10e2dc --- /dev/null +++ b/src/render/software/CanvasPreview.h @@ -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 diff --git a/src/render/software/software.pro b/src/render/software/software.pro index 8fbd61a..444a395 100644 --- a/src/render/software/software.pro +++ b/src/render/software/software.pro @@ -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 { diff --git a/src/render/software/software_global.h b/src/render/software/software_global.h index 432f108..419a378 100644 --- a/src/render/software/software_global.h +++ b/src/render/software/software_global.h @@ -52,6 +52,7 @@ namespace software { class CanvasPixel; class CanvasFragment; class CanvasLiveClient; + class CanvasPreview; } } diff --git a/src/tests/CanvasPortion_Test.cpp b/src/tests/CanvasPortion_Test.cpp new file mode 100644 index 0000000..643d11e --- /dev/null +++ b/src/tests/CanvasPortion_Test.cpp @@ -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)); +} diff --git a/src/tests/Canvas_Test.cpp b/src/tests/Canvas_Test.cpp index 7ae9551..39edf16 100644 --- a/src/tests/Canvas_Test.cpp +++ b/src/tests/Canvas_Test.cpp @@ -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); diff --git a/src/tests/tests.pro b/src/tests/tests.pro index 3dd95e6..2efe000 100644 --- a/src/tests/tests.pro +++ b/src/tests/tests.pro @@ -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