From 5de28e01db49117e563ab70f181a5984836f5322 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 7 Jul 2016 18:20:08 +0200 Subject: [PATCH] Migrate Kandinsky to C++ Change-Id: I2752a8db84ad0bb817119cf6c2993c1622621150 --- Makefile | 4 +- apps/graph/graph/cursor_view.cpp | 4 +- apps/graph/graph/cursor_view.h | 2 +- apps/graph/graph/graph_view.cpp | 86 +++--- apps/graph/graph/graph_view.h | 14 +- apps/graph/list/function_cell.cpp | 8 +- apps/graph/list/function_cell.h | 2 +- escher/include/escher/scroll_view_indicator.h | 2 +- escher/include/escher/solid_color_view.h | 2 +- escher/include/escher/tab_view.h | 2 +- escher/include/escher/tab_view_cell.h | 2 +- escher/include/escher/text_field.h | 2 +- escher/include/escher/text_view.h | 2 +- escher/include/escher/tiled_view.h | 4 +- escher/include/escher/view.h | 4 +- escher/src/app.cpp | 2 +- escher/src/scroll_view.cpp | 22 +- escher/src/scroll_view_indicator.cpp | 24 +- escher/src/solid_color_view.cpp | 4 +- escher/src/tab_view.cpp | 17 +- escher/src/tab_view_cell.cpp | 4 +- escher/src/tab_view_controller.cpp | 18 +- escher/src/table_view.cpp | 24 +- escher/src/text_field.cpp | 4 +- escher/src/text_view.cpp | 10 +- escher/src/tiled_view.cpp | 59 ++-- escher/src/view.cpp | 40 ++- ion/include/ion/screen.h | 10 +- ion/src/device/display/display.c | 14 +- ion/src/device/display/st7789.c | 15 +- ion/src/device/display/st7789.h | 5 +- ion/src/simulator/display/fltklcd.cpp | 8 +- ion/src/simulator/init.cpp | 38 ++- kandinsky/Makefile | 19 +- kandinsky/include/kandinsky.h | 9 +- kandinsky/include/kandinsky/color.h | 42 ++- kandinsky/include/kandinsky/context.h | 45 ++- kandinsky/include/kandinsky/coordinate.h | 8 + kandinsky/include/kandinsky/framebuffer.h | 21 +- .../include/kandinsky/framebuffer_context.h | 18 ++ kandinsky/include/kandinsky/ion_context.h | 16 ++ kandinsky/include/kandinsky/line.h | 11 - kandinsky/include/kandinsky/pixel.h | 10 - kandinsky/include/kandinsky/point.h | 20 ++ kandinsky/include/kandinsky/rect.h | 63 ++-- kandinsky/include/kandinsky/size.h | 18 ++ kandinsky/include/kandinsky/text.h | 10 - kandinsky/include/kandinsky/types.h | 34 --- kandinsky/src/color.c | 11 - kandinsky/src/color.cpp | 20 ++ kandinsky/src/context.c | 22 -- kandinsky/src/context.cpp | 16 ++ kandinsky/src/context_line.cpp | 59 ++++ kandinsky/src/context_pixel.cpp | 18 ++ kandinsky/src/context_rect.cpp | 84 ++++++ kandinsky/src/context_text.cpp | 29 ++ kandinsky/src/framebuffer.c | 34 --- kandinsky/src/framebuffer.cpp | 46 +++ kandinsky/src/framebuffer_context.cpp | 19 ++ kandinsky/src/ion_context.cpp | 27 ++ kandinsky/src/{line.c => line.cpp} | 0 kandinsky/src/pixel.c | 35 --- kandinsky/src/point.cpp | 12 + kandinsky/src/rect.c | 271 ------------------ kandinsky/src/rect.cpp | 124 ++++++++ kandinsky/src/size.cpp | 4 + kandinsky/src/types.c | 3 - kandinsky/test/{color.c => color.cpp} | 10 +- kandinsky/test/rect.c | 82 ------ kandinsky/test/rect.cpp | 60 ++++ kandinsky/test/set_pixel.c | 5 - quiz/src/{runner.c => runner.cpp} | 9 +- 72 files changed, 931 insertions(+), 871 deletions(-) create mode 100644 kandinsky/include/kandinsky/coordinate.h create mode 100644 kandinsky/include/kandinsky/framebuffer_context.h create mode 100644 kandinsky/include/kandinsky/ion_context.h delete mode 100644 kandinsky/include/kandinsky/line.h delete mode 100644 kandinsky/include/kandinsky/pixel.h create mode 100644 kandinsky/include/kandinsky/point.h create mode 100644 kandinsky/include/kandinsky/size.h delete mode 100644 kandinsky/include/kandinsky/text.h delete mode 100644 kandinsky/include/kandinsky/types.h delete mode 100644 kandinsky/src/color.c create mode 100644 kandinsky/src/color.cpp delete mode 100644 kandinsky/src/context.c create mode 100644 kandinsky/src/context.cpp create mode 100644 kandinsky/src/context_line.cpp create mode 100644 kandinsky/src/context_pixel.cpp create mode 100644 kandinsky/src/context_rect.cpp create mode 100644 kandinsky/src/context_text.cpp delete mode 100644 kandinsky/src/framebuffer.c create mode 100644 kandinsky/src/framebuffer.cpp create mode 100644 kandinsky/src/framebuffer_context.cpp create mode 100644 kandinsky/src/ion_context.cpp rename kandinsky/src/{line.c => line.cpp} (100%) delete mode 100644 kandinsky/src/pixel.c create mode 100644 kandinsky/src/point.cpp delete mode 100644 kandinsky/src/rect.c create mode 100644 kandinsky/src/rect.cpp create mode 100644 kandinsky/src/size.cpp delete mode 100644 kandinsky/src/types.c rename kandinsky/test/{color.c => color.cpp} (63%) delete mode 100644 kandinsky/test/rect.c create mode 100644 kandinsky/test/rect.cpp delete mode 100644 kandinsky/test/set_pixel.c rename quiz/src/{runner.c => runner.cpp} (68%) diff --git a/Makefile b/Makefile index 72fcadf17..d40c7419a 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ endif # Language-specific flags CFLAGS = -std=c99 -CXXFLAGS = -std=c++11 -fno-exceptions -fno-rtti +CXXFLAGS = -std=c++11 -fno-exceptions -fno-rtti -fno-threadsafe-statics products := boot.elf boot.hex boot.bin test.elf test.hex test.bin @@ -58,7 +58,7 @@ include libaxx/Makefile endif include ion/Makefile include kandinsky/Makefile -include poincare/Makefile +#include poincare/Makefile include escher/Makefile include apps/Makefile include quiz/Makefile # Quiz should be included at the end diff --git a/apps/graph/graph/cursor_view.cpp b/apps/graph/graph/cursor_view.cpp index f635c3185..69398d34c 100644 --- a/apps/graph/graph/cursor_view.cpp +++ b/apps/graph/graph/cursor_view.cpp @@ -1,5 +1,5 @@ #include "cursor_view.h" -void CursorView::drawRect(KDRect rect) const { - KDFillRect(rect, KDColorRed); +void CursorView::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(rect, KDColorRed); } diff --git a/apps/graph/graph/cursor_view.h b/apps/graph/graph/cursor_view.h index 2acd60934..f27ee5f9c 100644 --- a/apps/graph/graph/cursor_view.h +++ b/apps/graph/graph/cursor_view.h @@ -6,7 +6,7 @@ class CursorView : public ChildlessView { public: using ChildlessView::ChildlessView; - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; }; #endif diff --git a/apps/graph/graph/graph_view.cpp b/apps/graph/graph/graph_view.cpp index 6a39f5464..ba23a592c 100644 --- a/apps/graph/graph/graph_view.cpp +++ b/apps/graph/graph/graph_view.cpp @@ -1,9 +1,9 @@ #include "graph_view.h" #include -constexpr KDColor kAxisColor = KDColorGray(0x0); -constexpr KDColor kMainGridColor = KDColorGray(0xCC); -constexpr KDColor kSecondaryGridColor = KDColorGray(0xEE); +constexpr KDColor kAxisColor = KDColor(0x000000); +constexpr KDColor kMainGridColor = KDColor(0xCCCCCC); +constexpr KDColor kSecondaryGridColor = KDColor(0xEEEEEE); constexpr int kNumberOfMainGridLines = 5; constexpr int kNumberOfSecondaryGridLines = 4; @@ -31,15 +31,13 @@ View * GraphView::subviewAtIndex(int index) { } void GraphView::moveCursorRight() { - m_cursorPosition.x = m_cursorPosition.x + 2; + KDPoint offset = KDPoint(2,0); + m_cursorPosition = m_cursorPosition.translatedBy(offset); layoutSubviews(); } void GraphView::layoutSubviews() { - KDRect cursorFrame; - cursorFrame.origin = m_cursorPosition; - cursorFrame.width = 10; - cursorFrame.height = 10; + KDRect cursorFrame(m_cursorPosition, 10, 10); m_cursorView.setFrame(cursorFrame); } @@ -52,14 +50,14 @@ KDSize GraphView::tileSize() const { return {kTileWidth, kTileHeight}; } -void GraphView::drawTile(KDRect rect) const { +void GraphView::drawTile(KDContext * ctx, KDRect rect) const { #else -void GraphView::drawRect(KDRect rect) const { +void GraphView::drawRect(KDContext * ctx, KDRect rect) const { #endif - KDFillRect(rect, KDColorWhite); - drawGrid(rect); - drawAxes(rect); - drawFunction(rect); + ctx->fillRect(rect, KDColorWhite); + drawGrid(ctx, rect); + drawAxes(ctx, rect); + drawFunction(ctx, rect); /* constexpr int maskLength = 3; @@ -80,46 +78,46 @@ void GraphView::drawRect(KDRect rect) const { */ } -void GraphView::drawLine(KDRect rect, Axis axis, float coordinate, KDColor color, KDCoordinate thickness) const { - KDRect lineRect; +void GraphView::drawLine(KDContext * ctx, KDRect rect, Axis axis, float coordinate, KDColor color, KDCoordinate thickness) const { + KDRect lineRect = KDRectZero; switch(axis) { case Axis::Horizontal: - lineRect.x = rect.x; - lineRect.y = floatToPixel(Axis::Vertical, coordinate); - lineRect.width = rect.width; - lineRect.height = thickness; + lineRect = KDRect( + rect.x(), floatToPixel(Axis::Vertical, coordinate), + rect.width(), thickness + ); break; case Axis::Vertical: - lineRect.x = floatToPixel(Axis::Horizontal, coordinate); - lineRect.y = rect.y; - lineRect.width = thickness; - lineRect.height = rect.height; + lineRect = KDRect( + floatToPixel(Axis::Horizontal, coordinate), rect.y(), + thickness, rect.height() + ); break; } - KDFillRect(lineRect, color); + ctx->fillRect(lineRect, color); } -void GraphView::drawAxes(KDRect rect) const { - drawLine(rect, Axis::Horizontal, 0.0f, kAxisColor, 2); - drawLine(rect, Axis::Vertical, 0.0f, kAxisColor, 2); +void GraphView::drawAxes(KDContext * ctx, KDRect rect) const { + drawLine(ctx, rect, Axis::Horizontal, 0.0f, kAxisColor, 2); + drawLine(ctx, rect, Axis::Vertical, 0.0f, kAxisColor, 2); } -void GraphView::drawGridLines(KDRect rect, Axis axis, int count, KDColor color) const { +void GraphView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, int count, KDColor color) const { float range = max(axis)-min(axis); float step = range/count; float start = step*((int)(min(axis)/step)); for (int i=0; ifillRectWithMask(stampRect, KDColorRed, mask, workingBuffer); //KDBlitRect(stampRect, &red, {1,1}, mask, {stampSize,stampSize}); } } diff --git a/apps/graph/graph/graph_view.h b/apps/graph/graph/graph_view.h index cf251f237..6b2c647bf 100644 --- a/apps/graph/graph/graph_view.h +++ b/apps/graph/graph/graph_view.h @@ -19,9 +19,9 @@ public: #if GRAPH_VIEW_IS_TILED KDColor * tile() const override; KDSize tileSize() const override; - void drawTile(KDRect rect) const override; + void drawTile(KDContext * ctx, KDRect rect) const override; #else - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; #endif // void drawRect(KDRect rect) const override; @@ -43,13 +43,13 @@ private: float pixelToFloat(Axis axis, KDCoordinate p) const; KDCoordinate floatToPixel(Axis axis, float f) const; - void drawLine(KDRect rect, Axis axis, + void drawLine(KDContext * ctx, KDRect rect, Axis axis, float coordinate, KDColor color, KDCoordinate thickness = 1) const; - void drawAxes(KDRect rect) const; - void drawGrid(KDRect rect) const; - void drawGridLines(KDRect rect, Axis axis, int count, KDColor color) const; - void drawFunction(KDRect rect) const; + void drawAxes(KDContext * ctx, KDRect rect) const; + void drawGrid(KDContext * ctx, KDRect rect) const; + void drawGridLines(KDContext * ctx, KDRect rect, Axis axis, int count, KDColor color) const; + void drawFunction(KDContext * ctx, KDRect rect) const; static constexpr KDCoordinate kTileWidth = 32; static constexpr KDCoordinate kTileHeight = 32; diff --git a/apps/graph/list/function_cell.cpp b/apps/graph/list/function_cell.cpp index a602ba137..96fce3d65 100644 --- a/apps/graph/list/function_cell.cpp +++ b/apps/graph/list/function_cell.cpp @@ -8,10 +8,10 @@ FunctionCell::FunctionCell() : m_message = "NULL"; } -void FunctionCell::drawRect(KDRect rect) const { - KDColor background = m_even ? KDColorRGB(0xEE, 0xEE, 0xEE) : KDColorRGB(0x77,0x77,0x77); - KDFillRect(rect, background); - KDDrawString(m_message, KDPointZero, m_focused); +void FunctionCell::drawRect(KDContext * ctx, KDRect rect) const { + KDColor background = m_even ? KDColor(0xEEEEEE) : KDColor(0x777777); + ctx->fillRect(rect, background); + ctx->drawString(m_message, KDPointZero, m_focused); } void FunctionCell::setMessage(const char * message) { diff --git a/apps/graph/list/function_cell.h b/apps/graph/list/function_cell.h index 51251ad5d..8615874f0 100644 --- a/apps/graph/list/function_cell.h +++ b/apps/graph/list/function_cell.h @@ -9,7 +9,7 @@ public: void setMessage(const char * message); void setEven(bool even); - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; void setFocused(bool focused) override; private: const char * m_message; diff --git a/escher/include/escher/scroll_view_indicator.h b/escher/include/escher/scroll_view_indicator.h index 0e4787067..784c45749 100644 --- a/escher/include/escher/scroll_view_indicator.h +++ b/escher/include/escher/scroll_view_indicator.h @@ -10,7 +10,7 @@ public: Vertical }; ScrollViewIndicator(Direction direction); - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; void setStart(float start); void setEnd(float end); diff --git a/escher/include/escher/solid_color_view.h b/escher/include/escher/solid_color_view.h index de9c9f9e7..1c5a6bd3a 100644 --- a/escher/include/escher/solid_color_view.h +++ b/escher/include/escher/solid_color_view.h @@ -6,7 +6,7 @@ class SolidColorView : public ChildlessView { public: SolidColorView(KDColor color); - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; protected: #if ESCHER_VIEW_LOGGING const char * className() const override; diff --git a/escher/include/escher/tab_view.h b/escher/include/escher/tab_view.h index 791f10752..a510d1f46 100644 --- a/escher/include/escher/tab_view.h +++ b/escher/include/escher/tab_view.h @@ -10,7 +10,7 @@ class TabView : public View { public: TabView(); - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; void addTabNamed(const char * name); //TODO: void removeLastTab(); diff --git a/escher/include/escher/tab_view_cell.h b/escher/include/escher/tab_view_cell.h index 758828820..6880ff07f 100644 --- a/escher/include/escher/tab_view_cell.h +++ b/escher/include/escher/tab_view_cell.h @@ -7,7 +7,7 @@ class TabViewCell : public ChildlessView { public: TabViewCell(); - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; void setName(const char * name); void setActive(bool active); protected: diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index dcb138f97..f2ebd0632 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -9,7 +9,7 @@ class TextField : public ChildlessView, public Responder { public: TextField(char * textBuffer, size_t textBufferSize); // View - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; // Responder bool handleEvent(ion_event_t event) override; protected: diff --git a/escher/include/escher/text_view.h b/escher/include/escher/text_view.h index 7483b259f..243b681f8 100644 --- a/escher/include/escher/text_view.h +++ b/escher/include/escher/text_view.h @@ -12,7 +12,7 @@ public: TextView(const char * text, float horizontalAlignment, float verticalAlignment); - void drawRect(KDRect rect) const override; + void drawRect(KDContext * ctx, KDRect rect) const override; void setText(const char * text); protected: #if ESCHER_VIEW_LOGGING diff --git a/escher/include/escher/tiled_view.h b/escher/include/escher/tiled_view.h index 046e5cad0..f270ab9d8 100644 --- a/escher/include/escher/tiled_view.h +++ b/escher/include/escher/tiled_view.h @@ -6,8 +6,8 @@ class TiledView : public View { using View::View; protected: - void drawRect(KDRect rect) const override; - virtual void drawTile(KDRect rect) const = 0; + void drawRect(KDContext * ctx, KDRect rect) const override; + virtual void drawTile(KDContext * ctx, KDRect rect) const = 0; virtual KDColor * tile() const = 0; virtual KDSize tileSize() const = 0; diff --git a/escher/include/escher/view.h b/escher/include/escher/view.h index 91886632c..ada2f98c5 100644 --- a/escher/include/escher/view.h +++ b/escher/include/escher/view.h @@ -28,8 +28,8 @@ public: /* The drawRect method should be implemented by each View subclass. In a * typical drawRect implementation, a subclass will make drawing calls to the - * Kandinsky library. */ - virtual void drawRect(KDRect rect) const; + * Kandinsky library using the provided context. */ + virtual void drawRect(KDContext * ctx, KDRect rect) const; void setFrame(KDRect frame); diff --git a/escher/src/app.cpp b/escher/src/app.cpp index f0432f72d..679aaf8d1 100644 --- a/escher/src/app.cpp +++ b/escher/src/app.cpp @@ -15,7 +15,7 @@ void App::run() { sRunningApp = this; Window window; - window.setFrame({{{0,0}}, {{ION_SCREEN_WIDTH, ION_SCREEN_HEIGHT}}}); + window.setFrame(KDRect(0, 0, ION_SCREEN_WIDTH, ION_SCREEN_HEIGHT)); View * rootView = rootViewController()->view(); focus(rootViewController()); diff --git a/escher/src/scroll_view.cpp b/escher/src/scroll_view.cpp index b1b19c1bc..d9c7ed791 100644 --- a/escher/src/scroll_view.cpp +++ b/escher/src/scroll_view.cpp @@ -32,28 +32,24 @@ View * ScrollView::subviewAtIndex(int index) { void ScrollView::layoutSubviews() { // Layout indicators - KDRect verticalIndicatorFrame; - verticalIndicatorFrame.x = m_frame.width - k_indicatorThickness; - verticalIndicatorFrame.y = 0; - verticalIndicatorFrame.width = k_indicatorThickness; - verticalIndicatorFrame.height = m_frame.height; + KDRect verticalIndicatorFrame = KDRect( + m_frame.width() - k_indicatorThickness, 0, + k_indicatorThickness, m_frame.height() + ); m_verticalScrollIndicator.setFrame(verticalIndicatorFrame); // Layout contentView // We're only re-positionning the contentView, not modifying its size. - KDRect contentFrame; - contentFrame.x = -m_offset.x; - contentFrame.y = -m_offset.y; - contentFrame.size = m_contentView->bounds().size; + KDRect contentFrame = KDRect(m_offset.opposite(), m_contentView->bounds().size()); m_contentView->setFrame(contentFrame); } void ScrollView::setContentOffset(KDPoint offset) { m_offset = offset; - float contentHeight = m_contentView->bounds().height; - float start = offset.y; - float end = offset.y + m_frame.height; + float contentHeight = m_contentView->bounds().height(); + float start = offset.y(); + float end = offset.y() + m_frame.height(); m_verticalScrollIndicator.setStart(start/contentHeight); m_verticalScrollIndicator.setEnd(end/contentHeight); @@ -62,7 +58,7 @@ void ScrollView::setContentOffset(KDPoint offset) { } KDCoordinate ScrollView::maxContentWidth() { - return m_frame.width - k_indicatorThickness; + return m_frame.width() - k_indicatorThickness; } #if ESCHER_VIEW_LOGGING diff --git a/escher/src/scroll_view_indicator.cpp b/escher/src/scroll_view_indicator.cpp index 0b693288f..ec8b7d884 100644 --- a/escher/src/scroll_view_indicator.cpp +++ b/escher/src/scroll_view_indicator.cpp @@ -14,22 +14,22 @@ ScrollViewIndicator::ScrollViewIndicator(ScrollViewIndicator::Direction directio { } -void ScrollViewIndicator::drawRect(KDRect rect) const { - KDFillRect(bounds(), k_backgroundColor); - KDRect indicatorFrame; +void ScrollViewIndicator::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(bounds(), k_backgroundColor); + KDRect indicatorFrame = KDRectZero; if (m_direction == Direction::Horizontal) { - indicatorFrame.x = m_start*m_frame.width; - indicatorFrame.y = 0; - indicatorFrame.width = (m_end-m_start)*m_frame.width; - indicatorFrame.height = m_frame.height; + indicatorFrame = KDRect( + m_start*m_frame.width(), 0, + (m_end-m_start)*m_frame.width(), m_frame.height() + ); } else { assert(m_direction == Direction::Vertical); - indicatorFrame.x = 0; - indicatorFrame.y = m_start*m_frame.height; - indicatorFrame.width = m_frame.width; - indicatorFrame.height = (m_end-m_start)*m_frame.height; + indicatorFrame = KDRect( + 0, m_start*m_frame.height(), + m_frame.width(), (m_end-m_start)*m_frame.height() + ); } - KDFillRect(indicatorFrame, k_indicatorColor); + ctx->fillRect(indicatorFrame, k_indicatorColor); } void ScrollViewIndicator::setStart(float start) { diff --git a/escher/src/solid_color_view.cpp b/escher/src/solid_color_view.cpp index 9c0506c9a..9c2178bcf 100644 --- a/escher/src/solid_color_view.cpp +++ b/escher/src/solid_color_view.cpp @@ -6,8 +6,8 @@ SolidColorView::SolidColorView(KDColor color) : { } -void SolidColorView::drawRect(KDRect rect) const { - KDFillRect(rect, m_color); +void SolidColorView::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(rect, m_color); } #if ESCHER_VIEW_LOGGING diff --git a/escher/src/tab_view.cpp b/escher/src/tab_view.cpp index 3b4502358..338fab627 100644 --- a/escher/src/tab_view.cpp +++ b/escher/src/tab_view.cpp @@ -11,9 +11,9 @@ TabView::TabView() : { } -void TabView::drawRect(KDRect rect) const { - KDColor backgroundColor = KDColorRGB(0xb5, 0x1d, 0xab); - KDFillRect(rect, backgroundColor); +void TabView::drawRect(KDContext * ctx, KDRect rect) const { + KDColor backgroundColor = KDColor(0xB51DAB); + ctx->fillRect(rect, backgroundColor); } void TabView::addTabNamed(const char * name) { @@ -46,13 +46,12 @@ View * TabView::subviewAtIndex(int index) { void TabView::layoutSubviews() { // Simple layout: all tabs have the same length - KDCoordinate tabLength = m_frame.width/m_numberOfTabs; + KDCoordinate tabLength = m_frame.width()/m_numberOfTabs; for (int i=0; idrawString(m_name, {0,0}, m_active); } #if ESCHER_VIEW_LOGGING diff --git a/escher/src/tab_view_controller.cpp b/escher/src/tab_view_controller.cpp index 119cee8c4..2da2b1af8 100644 --- a/escher/src/tab_view_controller.cpp +++ b/escher/src/tab_view_controller.cpp @@ -19,17 +19,17 @@ void TabViewController::ContentView::setActiveView(View * view) { void TabViewController::ContentView::layoutSubviews() { KDCoordinate tabHeight = 20; - KDRect tabViewFrame; - tabViewFrame.origin = KDPointZero; - tabViewFrame.width = m_frame.width, - tabViewFrame.height = tabHeight; + KDRect tabViewFrame = KDRect( + 0, 0, + m_frame.width(), tabHeight + ); m_tabView.setFrame(tabViewFrame); if (m_activeView) { - KDRect activeViewFrame; - activeViewFrame.x = 0; - activeViewFrame.y = tabHeight; - activeViewFrame.width = m_frame.width; - activeViewFrame.height = m_frame.height - tabHeight; + KDRect activeViewFrame = KDRect( + 0, tabHeight, + m_frame.width(), + m_frame.height() - tabHeight + ); m_activeView->setFrame(activeViewFrame); } } diff --git a/escher/src/table_view.cpp b/escher/src/table_view.cpp index d0a9b2a62..6c5ec24eb 100644 --- a/escher/src/table_view.cpp +++ b/escher/src/table_view.cpp @@ -31,10 +31,7 @@ void TableView::layoutSubviews() { // We only have to layout our contentView. // We will size it here, and ScrollView::layoutSubviews will position it. - KDRect contentViewFrame; - contentViewFrame.origin = KDPointZero; // Will be overriden by ScrollView::layoutSubviews - contentViewFrame.width = maxContentWidth(); - contentViewFrame.height = m_contentView.height(); + KDRect contentViewFrame(0, 0, maxContentWidth(), m_contentView.height()); m_contentView.setFrame(contentViewFrame); ScrollView::layoutSubviews(); @@ -56,17 +53,14 @@ KDCoordinate TableView::ContentView::height() const { void TableView::ContentView::scrollToRow(int index) const { if (cellAtIndexIsBeforeFullyVisibleRange(index)) { // Let's scroll the tableView to put that cell on the top - KDPoint contentOffset; - contentOffset.x = 0; - contentOffset.y = index*m_dataSource->cellHeight(); + KDPoint contentOffset(0, index*m_dataSource->cellHeight()); m_tableView->setContentOffset(contentOffset); return; } if (cellAtIndexIsAfterFullyVisibleRange(index)) { // Let's scroll the tableView to put that cell on the bottom - KDPoint contentOffset; - contentOffset.x = 0; - contentOffset.y = (index+1)*m_dataSource->cellHeight() - m_tableView->bounds().height; + KDPoint contentOffset(0, + (index+1)*m_dataSource->cellHeight() - m_tableView->bounds().height()); m_tableView->setContentOffset(contentOffset); return; } @@ -99,12 +93,8 @@ void TableView::ContentView::layoutSubviews() { for (int i=0; icellHeight(); - cellFrame.x = 0; - cellFrame.y = (cellOffset+i)*cellHeight; - cellFrame.width = m_frame.width; - cellFrame.height = cellHeight; + KDRect cellFrame(0, (cellOffset+i)*cellHeight, m_frame.width(), cellHeight); cell->setFrame(cellFrame); @@ -113,7 +103,7 @@ void TableView::ContentView::layoutSubviews() { } int TableView::ContentView::numberOfDisplayableCells() const { - int result = m_tableView->bounds().height / m_dataSource->cellHeight() + 1; + int result = m_tableView->bounds().height() / m_dataSource->cellHeight() + 1; assert(result <= m_dataSource->reusableCellCount()); return result; } @@ -121,7 +111,7 @@ int TableView::ContentView::numberOfDisplayableCells() const { int TableView::ContentView::cellScrollingOffset() const { /* Here, we want to translate the offset at which our tableView is displaying * us into an integer offset we can use to ask cells to our data source. */ - KDCoordinate pixelScrollingOffset = -m_frame.y; + KDCoordinate pixelScrollingOffset = -m_frame.y(); return pixelScrollingOffset / m_dataSource->cellHeight(); } diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index c42a6a21f..d703983df 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -10,9 +10,9 @@ TextField::TextField(char * textBuffer, size_t textBufferSize) : /* View */ -void TextField::drawRect(KDRect rect) const { +void TextField::drawRect(KDContext * ctx, KDRect rect) const { m_textBuffer[m_currentTextLength] = 0; - KDDrawString(m_textBuffer, KDPointZero, 0); + ctx->drawString(m_textBuffer, KDPointZero, 0); } #if ESCHER_VIEW_LOGGING diff --git a/escher/src/text_view.cpp b/escher/src/text_view.cpp index 2e70357a3..209825e83 100644 --- a/escher/src/text_view.cpp +++ b/escher/src/text_view.cpp @@ -16,13 +16,13 @@ void TextView::setText(const char * text) { m_text = text; } -void TextView::drawRect(KDRect rect) const { - KDSize textSize = KDStringSize(m_text); +void TextView::drawRect(KDContext * ctx, KDRect rect) const { + KDSize textSize = ctx->stringSize(m_text); KDPoint origin = { - (KDCoordinate)(m_horizontalAlignment*(m_frame.width - textSize.width)), - (KDCoordinate)(m_verticalAlignment*(m_frame.height - textSize.height)) + (KDCoordinate)(m_horizontalAlignment*(m_frame.width() - textSize.width())), + (KDCoordinate)(m_verticalAlignment*(m_frame.height() - textSize.height())) }; - KDDrawString(m_text, origin, 0); + ctx->drawString(m_text, origin, 0); } #if ESCHER_VIEW_LOGGING diff --git a/escher/src/tiled_view.cpp b/escher/src/tiled_view.cpp index b2b341810..9d90c9911 100644 --- a/escher/src/tiled_view.cpp +++ b/escher/src/tiled_view.cpp @@ -1,53 +1,26 @@ #include #include -static KDFrameBuffer sCurrentTile; +void TiledView::drawRect(KDContext * ctx, KDRect rect) const { + KDColor * pixels = tile(); + KDSize size = tileSize(); + KDFrameBuffer tileBuffer(pixels, size); -void tilePushRect(KDRect rect, const KDColor * pixels) { - KDFramePushRect(&sCurrentTile, rect, pixels); -} -void tilePushRectUniform(KDRect rect, KDColor color) { - KDFramePushRectUniform(&sCurrentTile, rect, color); -} - -void tilePullRect(KDRect rect, KDColor * pixels) { - KDFramePullRect(&sCurrentTile, rect, pixels); -} - -static KDContext sTileContext = { - { - &tilePushRect, - &tilePushRectUniform, - &tilePullRect - }, - {0, 0}, - {0, 0, 0, 0} -}; - -void TiledView::drawRect(KDRect rect) const { - sCurrentTile.pixels = tile(); - sCurrentTile.size = tileSize(); - - sTileContext.clippingRect.origin = KDPointZero; // Useless - sTileContext.clippingRect.size = sCurrentTile.size; - - KDRect tileRect; - for (int i=0; i<(rect.width/sCurrentTile.size.width+1); i++) { - for (int j=0; j<(rect.height/sCurrentTile.size.height+1); j++) { - tileRect.x = rect.x + i*sCurrentTile.size.width; - tileRect.y = rect.y + j*sCurrentTile.size.height; - tileRect.size = sCurrentTile.size; + for (int i=0; i<(rect.width()/size.width()+1); i++) { + for (int j=0; j<(rect.height()/size.height()+1); j++) { + KDRect tileRect( + rect.x() + i*size.width(), + rect.y() + j*size.height(), + size.width(), size.height() + ); //tileRect = KDRectIntersection(tileRect, rect); // Optional - KDContext * previousContext = KDCurrentContext; - sTileContext.origin.x = -tileRect.x; - sTileContext.origin.y = -tileRect.y; + KDFrameBufferContext tileContext = KDFrameBufferContext(&tileBuffer); + KDPoint origin = tileRect.origin().opposite(); + tileContext.setOrigin(origin); - KDCurrentContext = &sTileContext; - drawTile(tileRect); - KDCurrentContext = previousContext; - KDSize zero = {0,0}; + drawTile(&tileContext, tileRect); - KDFillRectWithPixels(tileRect, sCurrentTile.pixels, sCurrentTile.pixels); + ctx->fillRectWithPixels(tileRect, pixels, pixels); } } } diff --git a/escher/src/view.cpp b/escher/src/view.cpp index 964a5773d..6f2f8aede 100644 --- a/escher/src/view.cpp +++ b/escher/src/view.cpp @@ -10,7 +10,7 @@ View::View() : { } -void View::drawRect(KDRect rect) const { +void View::drawRect(KDContext * ctx, KDRect rect) const { // By default, a view doesn't do anything // It's transparent! } @@ -24,7 +24,7 @@ const Window * View::window() const { } void View::markRectAsDirty(KDRect rect) { - m_dirtyRect = KDRectUnion(m_dirtyRect, rect); + m_dirtyRect = m_dirtyRect.unionedWith(rect); } void View::redraw(KDRect rect) { @@ -35,14 +35,15 @@ void View::redraw(KDRect rect) { } // First, let's draw our own content by calling drawRect - KDRect rectNeedingRedraw = KDRectIntersection(rect, m_dirtyRect); - if (rectNeedingRedraw.width > 0 && rectNeedingRedraw.height > 0) { + KDRect rectNeedingRedraw = rect.intersectedWith(m_dirtyRect); + if (!rectNeedingRedraw.isEmpty()) { KDPoint absOrigin = absoluteOrigin(); - KDRect absRect = KDRectTranslate(rectNeedingRedraw, absOrigin); - KDRect absClippingRect = KDRectIntersection(absoluteVisibleFrame(), absRect); - KDCurrentContext->origin = absOrigin; - KDCurrentContext->clippingRect = absClippingRect; - this->drawRect(rectNeedingRedraw); + KDRect absRect = rectNeedingRedraw.translatedBy(absOrigin); + KDRect absClippingRect = absoluteVisibleFrame().intersectedWith(absRect); + KDContext * ctx = KDIonContext::sharedContext(); + ctx->setOrigin(absOrigin); + ctx->setClippingRect(absClippingRect); + this->drawRect(ctx, rectNeedingRedraw); } // Then, let's recursively draw our children over ourself @@ -53,11 +54,10 @@ void View::redraw(KDRect rect) { } assert(subview->m_superview == this); - if (KDRectIntersect(rect, subview->m_frame)) { - KDRect intersection = KDRectIntersection(rect, subview->m_frame); + if (rect.intersects(subview->m_frame)) { + KDRect intersection = rect.intersectedWith(subview->m_frame); // Let's express intersection in subview's coordinates - intersection.x -= subview->m_frame.x; - intersection.y -= subview->m_frame.y; + intersection = intersection.translatedBy(subview->m_frame.origin().opposite()); subview->redraw(intersection); } } @@ -100,18 +100,15 @@ void View::setFrame(KDRect frame) { } KDRect View::bounds() const { - KDRect bounds = m_frame; - bounds.x = 0; - bounds.y = 0; - return bounds; + return m_frame.movedTo(KDPointZero); } KDPoint View::absoluteOrigin() const { if (m_superview == nullptr) { assert(this == (View *)window()); - return m_frame.origin; + return m_frame.origin(); } else { - return KDPointTranslate(m_frame.origin, m_superview->absoluteOrigin()); + return m_frame.origin().translatedBy(m_superview->absoluteOrigin()); } } @@ -122,10 +119,9 @@ KDRect View::absoluteVisibleFrame() const { } else { KDRect parentDrawingArea = m_superview->absoluteVisibleFrame(); - KDRect absoluteFrame = m_frame; - absoluteFrame.origin = absoluteOrigin(); + KDRect absoluteFrame = m_frame.movedTo(absoluteOrigin()); - return KDRectIntersection(absoluteFrame, parentDrawingArea); + return absoluteFrame.intersectedWith(parentDrawingArea); } } diff --git a/ion/include/ion/screen.h b/ion/include/ion/screen.h index 6ad92ba2d..17dc5878d 100644 --- a/ion/include/ion/screen.h +++ b/ion/include/ion/screen.h @@ -11,11 +11,13 @@ * pixel coordinates every time. We're therefore leveraging this capability * which results in a very consequent speedup (up to ~10x faster). */ -#include +#include -void ion_screen_push_rect(KDRect rect, const KDColor * pixels); -void ion_screen_push_rect_uniform(KDRect rect, KDColor color); -void ion_screen_pull_rect(KDRect rect, KDColor * pixels); +typedef uint16_t ion_color_t; + +void ion_screen_push_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const ion_color_t * pixels); +void ion_screen_push_rect_uniform(uint16_t x, uint16_t y, uint16_t width, uint16_t height, ion_color_t color); +void ion_screen_pull_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, ion_color_t * pixels); /* void ion_screen_set_working_area(KDRect area); void ion_screen_push_pixels(const KDColor * pixels, size_t count); diff --git a/ion/src/device/display/display.c b/ion/src/device/display/display.c index d59bf0206..4d9a97fc5 100644 --- a/ion/src/device/display/display.c +++ b/ion/src/device/display/display.c @@ -40,19 +40,19 @@ void ion_display_off() { // Turn off panel } -void ion_screen_push_rect(KDRect rect, const KDColor * pixels) { - st7789_set_drawing_area(&sDisplayController, rect); - st7789_push_pixels(&sDisplayController, pixels, rect.width*rect.height); +void ion_screen_push_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const ion_color_t * pixels) { + st7789_set_drawing_area(&sDisplayController, x, y, width, height); + st7789_push_pixels(&sDisplayController, pixels, width*height); } -void ion_screen_push_rect_uniform(KDRect rect, KDColor color) { - st7789_set_drawing_area(&sDisplayController, rect); - for (size_t i=0; i> 8)); perform_instruction(controller, DATA(pixels[i] & 0xFF)); diff --git a/ion/src/device/display/st7789.h b/ion/src/device/display/st7789.h index a35ff75ef..553f5d46a 100644 --- a/ion/src/device/display/st7789.h +++ b/ion/src/device/display/st7789.h @@ -5,7 +5,6 @@ #include #include #include -#include #define ST7789_USE_9BIT_SPI 1 @@ -23,8 +22,8 @@ typedef struct { void st7789_initialize(st7789_t * controller); -void st7789_set_drawing_area(st7789_t * controller, KDRect area); +void st7789_set_drawing_area(st7789_t * controller, uint16_t x, uint16_t y, uint16_t width, uint16_t height); void st7789_push_pixels(st7789_t * controller, - const KDColor * pixels, size_t numberOfPixels); + const ion_color_t * pixels, size_t numberOfPixels); #endif diff --git a/ion/src/simulator/display/fltklcd.cpp b/ion/src/simulator/display/fltklcd.cpp index 834f60475..506435b93 100644 --- a/ion/src/simulator/display/fltklcd.cpp +++ b/ion/src/simulator/display/fltklcd.cpp @@ -2,7 +2,7 @@ #include #include -FltkLCD::FltkLCD(int x, int y, int w, int h, uint16_t * rgb565FrameBuffer) : +FltkLCD::FltkLCD(int x, int y, int w, int h, KDColor * rgb565FrameBuffer) : Fl_Widget(x, y, w, h, nullptr), m_rgb565frameBufferStart(rgb565FrameBuffer), m_rgb565frameBufferEnd(rgb565FrameBuffer+w*h) @@ -21,9 +21,9 @@ void FltkLCD::draw() { while(rgb565Pixel < m_rgb565frameBufferEnd) { KDColor color = *rgb565Pixel++; - *rgb888Component++ = KDColorRedComponent(color); - *rgb888Component++ = KDColorGreenComponent(color); - *rgb888Component++ = KDColorBlueComponent(color); + *rgb888Component++ = color.red(); + *rgb888Component++ = color.green(); + *rgb888Component++ = color.blue(); } // 2/ Draw the 888 framebuffer diff --git a/ion/src/simulator/init.cpp b/ion/src/simulator/init.cpp index 38831f836..65b2e9f0e 100644 --- a/ion/src/simulator/init.cpp +++ b/ion/src/simulator/init.cpp @@ -15,7 +15,7 @@ extern "C" { static FltkLCD * sDisplay; static FltkKbd * sKeyboard; -static KDFrameBuffer sFrameBuffer; +static KDFrameBuffer * sFrameBuffer; #define FRAMEBUFFER_ADDRESS (sDisplay->m_framebuffer) @@ -29,16 +29,15 @@ void init_platform() { Fl_Window * window = new Fl_Window(screen_width+2*margin, margin+screen_height+margin+keyboard_height+margin); - sFrameBuffer.pixels = (KDColor *)malloc(ION_SCREEN_WIDTH*ION_SCREEN_HEIGHT*2); - sFrameBuffer.size.width = ION_SCREEN_WIDTH; - sFrameBuffer.size.height = ION_SCREEN_HEIGHT; + KDColor * pixels = (KDColor *)malloc(ION_SCREEN_WIDTH*ION_SCREEN_HEIGHT*sizeof(KDColor)); + sFrameBuffer = new KDFrameBuffer(pixels, KDSize(ION_SCREEN_WIDTH, ION_SCREEN_HEIGHT)); /* sFrameBuffer.drawingArea.origin = KDPointZero; sFrameBuffer.drawingArea.size = sFrameBuffer.size; sFrameBuffer.drawingCursor = KDPointZero; */ - sDisplay = new FltkLCD(margin, margin, screen_width, screen_height, sFrameBuffer.pixels); + sDisplay = new FltkLCD(margin, margin, screen_width, screen_height, pixels); sKeyboard = new FltkKbd(margin, margin+screen_height+margin, screen_width, keyboard_height); window->end(); @@ -47,16 +46,33 @@ void init_platform() { //KDCurrentContext->fillRect = NULL; } -void ion_screen_push_rect(KDRect rect, const KDColor * pixels) { - KDFramePushRect(&sFrameBuffer, rect, pixels); +void ion_screen_push_rect(uint16_t x, uint16_t y, + uint16_t width, uint16_t height, + const ion_color_t * pixels) +{ + // FIXME: Boy those casts are fugly + const void * foo = static_cast(pixels); + const KDColor * pouet = static_cast(foo); + sFrameBuffer->pushRect(KDRect(x,y,width,height), pouet); } -void ion_screen_push_rect_uniform(KDRect rect, KDColor color) { - KDFramePushRectUniform(&sFrameBuffer, rect, color); +void ion_screen_push_rect_uniform(uint16_t x, uint16_t y, + uint16_t width, uint16_t height, + ion_color_t color) +{ + ion_color_t * foo = &color; + const void * bar = static_cast(foo); + const KDColor * baz = static_cast(bar); + sFrameBuffer->pushRectUniform(KDRect(x,y,width,height), *baz); } -void ion_screen_pull_rect(KDRect rect, KDColor * pixels) { - KDFramePullRect(&sFrameBuffer, rect, pixels); +void ion_screen_pull_rect(uint16_t x, uint16_t y, + uint16_t width, uint16_t height, + ion_color_t * pixels) +{ + void * foo = static_cast(pixels); + KDColor * pouet = static_cast(foo); + sFrameBuffer->pullRect(KDRect(x,y,width,height), pouet); } bool ion_key_down(ion_key_t key) { diff --git a/kandinsky/Makefile b/kandinsky/Makefile index a9cfd3593..5dca6370e 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -2,18 +2,21 @@ SFLAGS += -Ikandinsky/include objs += $(addprefix kandinsky/src/,\ color.o\ context.o\ + context_line.o\ + context_pixel.o\ + context_rect.o\ + context_text.o\ font.o\ framebuffer.o\ - line.o\ - pixel.o\ + framebuffer_context.o\ + ion_context.o\ + point.o\ rect.o\ - text.o\ - types.o\ + size.o\ ) tests += $(addprefix kandinsky/test/,\ - color.c\ - rect.c\ - set_pixel.c\ + color.cpp\ + rect.cpp\ ) FREETYPE_PATH := /usr/local/Cellar/freetype/2.6.3 @@ -21,7 +24,7 @@ FREETYPE_PATH := /usr/local/Cellar/freetype/2.6.3 # built w/o PNG support and simply won't output an image of the rasterization #LIBPNG_PATH := /usr/local/Cellar/libpng/1.6.21 -kandinsky/src/text.c: kandinsky/src/font.h +kandinsky/src/context_text.cpp: kandinsky/src/font.h font_files = $(addprefix kandinsky/src/, font.h font.c) diff --git a/kandinsky/include/kandinsky.h b/kandinsky/include/kandinsky.h index e99ceeb2e..11386d20f 100644 --- a/kandinsky/include/kandinsky.h +++ b/kandinsky/include/kandinsky.h @@ -2,12 +2,13 @@ #define KANDINSKY_KANDINSKY_H #include +#include #include #include -#include -#include +#include +#include +#include #include -#include -#include +#include #endif diff --git a/kandinsky/include/kandinsky/color.h b/kandinsky/include/kandinsky/color.h index 9b8c1e6b4..9d78a2a96 100644 --- a/kandinsky/include/kandinsky/color.h +++ b/kandinsky/include/kandinsky/color.h @@ -3,32 +3,24 @@ #include -// Kandinsky uses RGB565 +class KDColor { +public: + constexpr KDColor() : m_value(0) {} + // FIXME: This should not be needed, and is probably wasting CPU cycles -typedef uint16_t KDColor; + constexpr KDColor(uint32_t rgb) + : m_value(((rgb&0xF80000)>>8)|((rgb&0x00FC00)>>5)|((rgb&0x0000F8)>>3)) {} + uint8_t red(); + uint8_t green(); + uint8_t blue(); + KDColor blend(KDColor other, uint8_t alpha); + operator uint16_t() { return m_value; } +private: + uint16_t m_value; +}; -/* KDColorRGB takes a 32-bits RGB color and builds a KDColor from it. - * Given KDColor is RGB565 and that we take 8-bit values for R, G, and B, - * some shifting has to happen. */ - -// FIXME: KDColorRGB(rgb), as a single 32-bits value -#define KDColorRGB(r,g,b) ((KDColor)((((((uint16_t)(r))>>3)&0x1F)<<11)|(((((uint16_t)(g))>>2)&0x3F)<<5)|((((uint16_t)(b))>>3)&0x1F))) - -#define KDColorRedComponent(color) ((uint8_t)(((color)>>11)<<3)) -#define KDColorGreenComponent(color) ((uint8_t)((((color)>>5)&0x3F)<<2)) -#define KDColorBlueComponent(color) ((uint8_t)(((color)&0x1F)<<3)) - -#define KDColorGray(i) KDColorRGB(i,i,i) - -#define KDColorBlack KDColorRGB(0x00, 0x00, 0x00) -#define KDColorWhite KDColorRGB(0xFF, 0xFF, 0xFF) - -#define KDColorRed KDColorRGB(0xFF, 0x00, 0x00) -#define KDColorGreen KDColorRGB(0x00, 0xFF, 0x00) -#define KDColorBlue KDColorRGB(0x00, 0x00, 0xFF) - -// alpha = 0 -> only a -// alpha = 0xFF -> only b -KDColor KDColorBlend(KDColor background, KDColor foreground, uint8_t alpha); +constexpr KDColor KDColorBlack = KDColor(0x000000); +constexpr KDColor KDColorWhite = KDColor(0xFFFFFF); +constexpr KDColor KDColorRed = KDColor(0xFF0000); #endif diff --git a/kandinsky/include/kandinsky/context.h b/kandinsky/include/kandinsky/context.h index 4d3716c85..d9f7a2dd1 100644 --- a/kandinsky/include/kandinsky/context.h +++ b/kandinsky/include/kandinsky/context.h @@ -1,19 +1,40 @@ #ifndef KANDINSKY_CONTEXT_H #define KANDINSKY_CONTEXT_H -#include "types.h" -#include "rect.h" +#include +#include -typedef struct { - struct { - void (*pushRect)(KDRect rect, const KDColor * pixels); - void (*pushRectUniform)(KDRect rect, KDColor color); - void (*pullRect)(KDRect rect, KDColor * pixels); - } io; - KDPoint origin; - KDRect clippingRect; -} KDContext; +class KDContext { +public: + void setOrigin(KDPoint origin); + void setClippingRect(KDRect clippingRect); -extern KDContext * KDCurrentContext; + // Pixel manipulation + void setPixel(KDPoint p, KDColor c); + KDColor getPixel(KDPoint p); + + // Text + void drawChar(char character, KDPoint p, uint8_t inverse); + void drawString(const char * text, KDPoint p, uint8_t inverse); + KDSize stringSize(const char * text); + + // Line. Not anti-aliased. + void drawLine(KDPoint p1, KDPoint p2, KDColor c); + + // Rect + void fillRect(KDRect rect, KDColor color); + void fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer); + void fillRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer); + void drawRect(KDRect rect, KDColor color); +protected: + KDContext(KDPoint origin, KDRect clippingRect); + virtual void pushRect(KDRect, const KDColor * pixels) = 0; + virtual void pushRectUniform(KDRect rect, KDColor color) = 0; + virtual void pullRect(KDRect rect, KDColor * pixels) = 0; +private: + KDRect absoluteFillRect(KDRect rect); + KDPoint m_origin; + KDRect m_clippingRect; +}; #endif diff --git a/kandinsky/include/kandinsky/coordinate.h b/kandinsky/include/kandinsky/coordinate.h new file mode 100644 index 000000000..cb583a0f8 --- /dev/null +++ b/kandinsky/include/kandinsky/coordinate.h @@ -0,0 +1,8 @@ +#ifndef KANDINSKY_COORDINATE_H +#define KANDINSKY_COORDINATE_H + +#include + +typedef int16_t KDCoordinate; + +#endif diff --git a/kandinsky/include/kandinsky/framebuffer.h b/kandinsky/include/kandinsky/framebuffer.h index 04335ff2c..02b81dfe0 100644 --- a/kandinsky/include/kandinsky/framebuffer.h +++ b/kandinsky/include/kandinsky/framebuffer.h @@ -2,16 +2,19 @@ #define KANDINSKY_FRAMEBUFFER_H #include -#include #include -typedef struct { - KDColor * pixels; - KDSize size; -} KDFrameBuffer; - -void KDFramePushRect(KDFrameBuffer * frameBuffer, KDRect rect, const KDColor * pixels); -void KDFramePushRectUniform(KDFrameBuffer * frameBuffer, KDRect rect, KDColor color); -void KDFramePullRect(KDFrameBuffer * frameBuffer, KDRect rect, KDColor * pixels); +class KDFrameBuffer { +public: + KDFrameBuffer(KDColor * pixels, KDSize size); + void pushRect(KDRect rect, const KDColor * pixels); + void pushRectUniform(KDRect rect, KDColor color); + void pullRect(KDRect rect, KDColor * pixels); + KDRect bounds(); +private: + KDColor * pixelAddress(KDPoint p) const; + KDColor * m_pixels; + KDSize m_size; +}; #endif diff --git a/kandinsky/include/kandinsky/framebuffer_context.h b/kandinsky/include/kandinsky/framebuffer_context.h new file mode 100644 index 000000000..d07c57fd3 --- /dev/null +++ b/kandinsky/include/kandinsky/framebuffer_context.h @@ -0,0 +1,18 @@ +#ifndef KANDINSKY_FRAMEBUFFER_CONTEXT_H +#define KANDINSKY_FRAMEBUFFER_CONTEXT_H + +#include +#include + +class KDFrameBufferContext : public KDContext { +public: + KDFrameBufferContext(KDFrameBuffer * frameBuffer); +protected: + void pushRect(KDRect, const KDColor * pixels) override; + void pushRectUniform(KDRect rect, KDColor color) override; + void pullRect(KDRect rect, KDColor * pixels) override; +private: + KDFrameBuffer * m_frameBuffer; +}; + +#endif diff --git a/kandinsky/include/kandinsky/ion_context.h b/kandinsky/include/kandinsky/ion_context.h new file mode 100644 index 000000000..42eb738ad --- /dev/null +++ b/kandinsky/include/kandinsky/ion_context.h @@ -0,0 +1,16 @@ +#ifndef KANDINSKY_ION_CONTEXT_H +#define KANDINSKY_ION_CONTEXT_H + +#include + +class KDIonContext : public KDContext { +public: + static KDIonContext * sharedContext(); +private: + KDIonContext(); + void pushRect(KDRect rect, const KDColor * pixels) override; + void pushRectUniform(KDRect rect, KDColor color) override; + void pullRect(KDRect rect, KDColor * pixels) override; +}; + +#endif diff --git a/kandinsky/include/kandinsky/line.h b/kandinsky/include/kandinsky/line.h deleted file mode 100644 index 7affd543e..000000000 --- a/kandinsky/include/kandinsky/line.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef KANDINSKY_LINE_H -#define KANDINSKY_LINE_H - -#include -#include - -void KDDrawLine(KDPoint p1, KDPoint p2, KDColor c); - -void KDDrawAntiAliasedLine(KDPoint p1, KDPoint p2, KDCoordinate width, KDColor frontColor, KDColor backColor); - -#endif diff --git a/kandinsky/include/kandinsky/pixel.h b/kandinsky/include/kandinsky/pixel.h deleted file mode 100644 index f12762880..000000000 --- a/kandinsky/include/kandinsky/pixel.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef KANDINSKY_REFERENTIAL_H -#define KANDINSKY_REFERENTIAL_H - -#include -#include - -void KDSetPixel(KDPoint p, KDColor c); -KDColor KDGetPixel(KDPoint p); - -#endif diff --git a/kandinsky/include/kandinsky/point.h b/kandinsky/include/kandinsky/point.h new file mode 100644 index 000000000..7b78bd0e6 --- /dev/null +++ b/kandinsky/include/kandinsky/point.h @@ -0,0 +1,20 @@ +#ifndef KANDINSKY_POINT_H +#define KANDINSKY_POINT_H + +#include + +struct KDPoint { + constexpr KDPoint(KDCoordinate x, KDCoordinate y) : + m_x(x), m_y(y) {} + KDCoordinate x() const; + KDCoordinate y() const; + KDPoint translatedBy(KDPoint other) const; + KDPoint opposite() const; +private: + KDCoordinate m_x; + KDCoordinate m_y; +}; + +constexpr KDPoint KDPointZero = KDPoint(0,0); + +#endif diff --git a/kandinsky/include/kandinsky/rect.h b/kandinsky/include/kandinsky/rect.h index 922e9f170..cc381c0df 100644 --- a/kandinsky/include/kandinsky/rect.h +++ b/kandinsky/include/kandinsky/rect.h @@ -1,42 +1,43 @@ #ifndef KANDINSKY_RECT_H #define KANDINSKY_RECT_H -#include -#include -#include -#include +#include +#include +#include -typedef struct { - union { - struct { - KDCoordinate x; - KDCoordinate y; - }; - KDPoint origin; - }; - union { - struct { - KDCoordinate width; - KDCoordinate height; - }; - KDSize size; - }; -} KDRect; +class KDRect { +public: + constexpr KDRect(KDCoordinate x, KDCoordinate y, + KDCoordinate width, KDCoordinate height) : + m_x(x), m_y(y), m_width(width), m_height(height) {} -extern KDRect KDRectZero; + KDRect(KDPoint p, KDSize s); + KDRect(KDCoordinate x, KDCoordinate y, KDSize s); + KDRect(KDPoint p, KDCoordinate width, KDCoordinate height); -bool KDRectIntersect(KDRect r1, KDRect r2); -KDRect KDRectIntersection(KDRect r1, KDRect r2); + KDCoordinate x() const; + KDCoordinate y() const; + KDPoint origin() const; + KDCoordinate width() const; + KDCoordinate height() const; + KDSize size() const; + KDCoordinate left() const; + KDCoordinate right() const; + KDCoordinate top() const; + KDCoordinate bottom() const; -// Returns the smallest rectangle containing r1 and r2 -KDRect KDRectUnion(KDRect r1, KDRect r2); + KDRect translatedBy(KDPoint p) const; + KDRect movedTo(KDPoint p) const; + bool intersects(const KDRect & other) const; + KDRect intersectedWith(const KDRect & other) const; + KDRect unionedWith(const KDRect & other) const; // Returns the smallest rectangle containing r1 and r2 + bool contains(KDPoint p) const; + bool isEmpty() const; -bool KDRectContains(KDRect r, KDPoint p); -KDRect KDRectTranslate(KDRect r, KDPoint p); +private: + KDCoordinate m_x, m_y, m_width, m_height; +}; -void KDFillRect(KDRect rect, KDColor color); -void KDFillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer); -void KDFillRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer); -void KDDrawRect(KDRect rect, KDColor color); +constexpr KDRect KDRectZero = KDRect(0,0,0,0); #endif diff --git a/kandinsky/include/kandinsky/size.h b/kandinsky/include/kandinsky/size.h new file mode 100644 index 000000000..f921c99b1 --- /dev/null +++ b/kandinsky/include/kandinsky/size.h @@ -0,0 +1,18 @@ +#ifndef KANDINSKY_SIZE_H +#define KANDINSKY_SIZE_H + +#include + +struct KDSize { + constexpr KDSize(KDCoordinate width, KDCoordinate height) : + m_width(width), m_height(height) {} + KDCoordinate width() const; + KDCoordinate height() const; +private: + KDCoordinate m_width; + KDCoordinate m_height; +}; + + + +#endif diff --git a/kandinsky/include/kandinsky/text.h b/kandinsky/include/kandinsky/text.h deleted file mode 100644 index 6284ca35b..000000000 --- a/kandinsky/include/kandinsky/text.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef KANDINSKY_TEXT_H -#define KANDINSKY_TEXT_H - -#include - -void KDDrawChar(char character, KDPoint p, uint8_t inverse); -void KDDrawString(const char * text, KDPoint p, uint8_t inverse); -KDSize KDStringSize(const char * text); - -#endif diff --git a/kandinsky/include/kandinsky/types.h b/kandinsky/include/kandinsky/types.h deleted file mode 100644 index 40aa3cafb..000000000 --- a/kandinsky/include/kandinsky/types.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef KANDINSKY_TYPES_H -#define KANDINSKY_TYPES_H - -#include - -typedef int16_t KDCoordinate; - -typedef struct { - KDCoordinate x; - KDCoordinate y; -} KDPoint; - -extern KDPoint KDPointZero; - -static inline KDPoint KDPointMake(KDCoordinate x, KDCoordinate y) { - KDPoint p; - p.x = x; - p.y = y; - return p; -} - -static inline KDPoint KDPointTranslate(KDPoint p1, KDPoint p2) { - KDPoint p; - p.x = p1.x+p2.x; - p.y = p1.y+p2.y; - return p; -} - -typedef struct { - KDCoordinate width; - KDCoordinate height; -} KDSize; - -#endif diff --git a/kandinsky/src/color.c b/kandinsky/src/color.c deleted file mode 100644 index 890bc89cb..000000000 --- a/kandinsky/src/color.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -#define KDAlphaBlend(c1,c2,a) ((uint8_t)((((uint16_t)c1*(0xFF-a))+((uint16_t)c2*a))/0xFF)) - -KDColor KDColorBlend(KDColor a, KDColor b, uint8_t alpha) { - return KDColorRGB( - KDAlphaBlend(KDColorRedComponent(a), KDColorRedComponent(b), alpha), - KDAlphaBlend(KDColorGreenComponent(a), KDColorGreenComponent(b), alpha), - KDAlphaBlend(KDColorBlueComponent(a), KDColorBlueComponent(b), alpha) - ); -} diff --git a/kandinsky/src/color.cpp b/kandinsky/src/color.cpp new file mode 100644 index 000000000..b5868cc6b --- /dev/null +++ b/kandinsky/src/color.cpp @@ -0,0 +1,20 @@ +#include + +KDColor KDColor::blend(KDColor other, uint8_t alpha) { + return other; // FIXME +} + +uint8_t KDColor::red() { + uint8_t r5 = (m_value>>11)&0x1F; + return r5 << 3; +} + +uint8_t KDColor::green() { + uint8_t g6 = (m_value>>5)&0x3F; + return g6 << 2; +} + +uint8_t KDColor::blue() { + uint8_t b5 = m_value&0x1F; + return b5 << 3; +} diff --git a/kandinsky/src/context.c b/kandinsky/src/context.c deleted file mode 100644 index 9e42c2b1f..000000000 --- a/kandinsky/src/context.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - -KDContext KDIonContext = { - .io = { - .pushRect = &ion_screen_push_rect, - .pushRectUniform = &ion_screen_push_rect_uniform, - .pullRect = &ion_screen_pull_rect - }, - .origin = { - .x = 0, - .y = 0 - }, - .clippingRect = { - .x = 0, - .y = 0, - .width = ION_SCREEN_WIDTH, - .height = ION_SCREEN_HEIGHT - } -}; - -KDContext * KDCurrentContext = &KDIonContext; diff --git a/kandinsky/src/context.cpp b/kandinsky/src/context.cpp new file mode 100644 index 000000000..f29b84e6c --- /dev/null +++ b/kandinsky/src/context.cpp @@ -0,0 +1,16 @@ +#include +#include + +KDContext::KDContext(KDPoint origin, KDRect clippingRect) : + m_origin(origin), + m_clippingRect(clippingRect) +{ +} + +void KDContext::setOrigin(KDPoint origin) { + m_origin = origin; +} + +void KDContext::setClippingRect(KDRect clippingRect) { + m_clippingRect = clippingRect; +} diff --git a/kandinsky/src/context_line.cpp b/kandinsky/src/context_line.cpp new file mode 100644 index 000000000..8c360e7ed --- /dev/null +++ b/kandinsky/src/context_line.cpp @@ -0,0 +1,59 @@ +#include +#include + +void KDContext::drawLine(KDPoint p1, KDPoint p2, KDColor c) { + // Find the largest gap + KDPoint left = KDPointZero, right = KDPointZero; + if (p2.x() > p1.x()) { + left = p1; + right = p2; + } else { + left = p2; + right = p1; + } + KDPoint top = KDPointZero, bottom = KDPointZero; + if (p2.y() > p1.y()) { + top = p1; + bottom = p2; + } else { + top = p2; + bottom = p1; + } + assert(right.x() >= left.x()); + assert(bottom.y() >= top.y()); + + KDCoordinate deltaX = 2*(right.x() - left.x()); + KDCoordinate deltaY = 2*(bottom.y() - top.y()); + + KDPoint p = KDPointZero, alwaysTranslate = KDPointZero, conditionalTranslate = KDPointZero; + KDCoordinate scanLength, error, minusError, plusError; + + if (deltaX >= deltaY) { + p = left; + scanLength = right.x() - left.x(); + error = right.x() - left.x(); + minusError = deltaY; + plusError = deltaX; + alwaysTranslate = KDPoint(1,0); + conditionalTranslate = KDPoint(0, (right.y() >= left.y() ? 1 : -1)); + } else { + p = top; + scanLength = bottom.y() - top.y(); + error = bottom.y() - top.y(); + minusError = deltaX; + plusError = deltaY; + alwaysTranslate = KDPoint(0,1); + conditionalTranslate = KDPoint((bottom.x() >= top.x() ? 1 : -1), 0); + } + + KDCoordinate scanCounter = 0; + while (scanCounter++ < scanLength) { + setPixel(p, c); + p = p.translatedBy(alwaysTranslate); + error = error - minusError; + if (error <= 0) { + p = p.translatedBy(conditionalTranslate); + error = error + plusError; + } + } +} diff --git a/kandinsky/src/context_pixel.cpp b/kandinsky/src/context_pixel.cpp new file mode 100644 index 000000000..3eb02a287 --- /dev/null +++ b/kandinsky/src/context_pixel.cpp @@ -0,0 +1,18 @@ +#include + +void KDContext::setPixel(KDPoint p, KDColor c) { + KDPoint absolutePoint = p.translatedBy(m_origin); + if (m_clippingRect.contains(absolutePoint)) { + pushRect(KDRect(absolutePoint, 1, 1), &c); + } +} + +KDColor KDContext::getPixel(KDPoint p) { + KDPoint absolutePoint = p.translatedBy(m_origin); + if (m_clippingRect.contains(absolutePoint)) { + KDColor result = KDColorBlack; + pullRect(KDRect(absolutePoint, 1, 1), &result); + return result; + } + return KDColorBlack; +} diff --git a/kandinsky/src/context_rect.cpp b/kandinsky/src/context_rect.cpp new file mode 100644 index 000000000..1559f17ea --- /dev/null +++ b/kandinsky/src/context_rect.cpp @@ -0,0 +1,84 @@ +#include +#include + +KDRect KDContext::absoluteFillRect(KDRect rect) { + return rect.translatedBy(m_origin).intersectedWith(m_clippingRect); +} + +void KDContext::fillRect(KDRect rect, KDColor color) { + KDRect absoluteRect = absoluteFillRect(rect); + if (absoluteRect.isEmpty()) { + return; + } + pushRectUniform(absoluteRect, color); +} + +/* Note: we support the case where workingBuffer IS equal to pixels */ +void KDContext::fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer) { + KDRect absoluteRect = absoluteFillRect(rect); + + if (absoluteRect.isEmpty()) { + return; + } + + /* Caution: + * The absoluteRect may have a SMALLER size than the original rect because it + * has been clipped. Therefore we cannot assume that the mask can be read as a + * continuous area. */ + + if (absoluteRect.width() == rect.width() && absoluteRect.height() == rect.height()) { + pushRect(absoluteRect, pixels); + return; + } + + /* At this point we need the working buffer */ + assert(workingBuffer != nullptr); + for (KDCoordinate j=0; jblend(color, *currentMaskAddress); + //*currentPixelAdress = KDColorBlend(*currentPixelAdress, color, *currentMaskAddress); + } + } + pushRect(absoluteRect, workingBuffer); +} + +void KDContext::drawRect(KDRect rect, KDColor color) { + KDPoint p1 = rect.origin(); + KDPoint p2 = KDPoint(rect.x(), rect.bottom()); + for (int i = 0; i +#include +#include "font.h" + +void KDContext::drawChar(char character, KDPoint p, uint8_t inverse) { + for (int j=0; j -#include - -static inline KDColor * frameBufferPixelAddress(KDFrameBuffer * frameBuffer, KDCoordinate x, KDCoordinate y) { - return frameBuffer->pixels + x + y*frameBuffer->size.width; -} - -void KDFramePushRect(KDFrameBuffer * frameBuffer, KDRect rect, const KDColor * pixels) { - const KDColor * line = pixels; - for (int j=0; j +#include + +KDFrameBuffer::KDFrameBuffer(KDColor * pixels, KDSize size) : + m_pixels(pixels), + m_size(size) +{ +} + +KDRect KDFrameBuffer::bounds() { + return KDRect(KDPointZero, m_size); +} + +KDColor * KDFrameBuffer::pixelAddress(KDPoint p) const { + return m_pixels + p.x() + p.y()*m_size.width(); +} + +void KDFrameBuffer::pushRect(KDRect rect, const KDColor * pixels) { + const KDColor * line = pixels; + for (KDCoordinate j=0; j + +KDFrameBufferContext::KDFrameBufferContext(KDFrameBuffer * frameBuffer) : + KDContext(KDPointZero, frameBuffer->bounds()), + m_frameBuffer(frameBuffer) +{ +} + +void KDFrameBufferContext::pushRect(KDRect rect, const KDColor * pixels) { + m_frameBuffer->pushRect(rect, pixels); +} + +void KDFrameBufferContext::pushRectUniform(KDRect rect, KDColor color) { + m_frameBuffer->pushRectUniform(rect, color); +} + +void KDFrameBufferContext::pullRect(KDRect rect, KDColor * pixels) { + m_frameBuffer->pullRect(rect, pixels); +} diff --git a/kandinsky/src/ion_context.cpp b/kandinsky/src/ion_context.cpp new file mode 100644 index 000000000..d66889479 --- /dev/null +++ b/kandinsky/src/ion_context.cpp @@ -0,0 +1,27 @@ +#include +extern "C" { +#include +} + +KDIonContext * KDIonContext::sharedContext() { + static KDIonContext context; + return &context; +} + +KDIonContext::KDIonContext() : +KDContext(KDPointZero, + KDRect(0, 0, ION_SCREEN_WIDTH, ION_SCREEN_HEIGHT)) +{ +} + +void KDIonContext::pushRect(KDRect rect, const KDColor * pixels) { + ion_screen_push_rect(rect.x(), rect.y(), rect.width(), rect.height(), (const ion_color_t *)pixels); +} + +void KDIonContext::pushRectUniform(KDRect rect, KDColor color) { + ion_screen_push_rect_uniform(rect.x(), rect.y(), rect.width(), rect.height(), color); +} + +void KDIonContext::pullRect(KDRect rect, KDColor * pixels) { + ion_screen_pull_rect(rect.x(), rect.y(), rect.width(), rect.height(), (ion_color_t *)pixels); +} diff --git a/kandinsky/src/line.c b/kandinsky/src/line.cpp similarity index 100% rename from kandinsky/src/line.c rename to kandinsky/src/line.cpp diff --git a/kandinsky/src/pixel.c b/kandinsky/src/pixel.c deleted file mode 100644 index d162ce0e9..000000000 --- a/kandinsky/src/pixel.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include -#include -#include - -void KDSetPixel(KDPoint p, KDColor c) { - KDPoint absolutePoint = KDPointTranslate(p, KDCurrentContext->origin); - if (KDRectContains(KDCurrentContext->clippingRect, absolutePoint)) { - KDCurrentContext->io.pushRect((KDRect){.origin = absolutePoint, .width = 1, .height = 1}, &c); - } -} - -KDColor KDGetPixel(KDPoint p) { - KDPoint absolutePoint = KDPointTranslate(p, KDCurrentContext->origin); - if (KDRectContains(KDCurrentContext->clippingRect, absolutePoint)) { - KDColor result; - KDCurrentContext->io.pullRect((KDRect){.origin = absolutePoint, .width = 1, .height = 1}, &result); - return result; - } - return KDColorBlack; -} -/* -void KDSetAbsolutePixelDirect(KDPoint p, KDColor c) { - KDCurrentContext->io.setWorkingArea((KDRect){.origin = p, .width = 1, .height = 1}); - KDCurrentContext->io.pushPixels(&c, 1); -} - -KDColor KDGetAbsolutePixelDirect(KDPoint p) { - KDColor result; - KDCurrentContext->io.setWorkingArea((KDRect){.origin = p, .width = 1, .height = 1}); - KDCurrentContext->io.pullPixels(&result, 1); - return result; -} -*/ diff --git a/kandinsky/src/point.cpp b/kandinsky/src/point.cpp new file mode 100644 index 000000000..a7f85e6ab --- /dev/null +++ b/kandinsky/src/point.cpp @@ -0,0 +1,12 @@ +#include + +KDCoordinate KDPoint::x() const { return m_x; } +KDCoordinate KDPoint::y() const { return m_y; } + +KDPoint KDPoint::translatedBy(KDPoint other) const { + return KDPoint(m_x+other.x(), m_y+other.y()); +} + +KDPoint KDPoint::opposite() const { + return KDPoint(-m_x, -m_y); +} diff --git a/kandinsky/src/rect.c b/kandinsky/src/rect.c deleted file mode 100644 index ef4b64e6a..000000000 --- a/kandinsky/src/rect.c +++ /dev/null @@ -1,271 +0,0 @@ -#include -#include -#include -#include -#include - -KDRect KDRectZero = {.x = 0, .y = 0, .width = 0, .height = 0}; - -static inline KDCoordinate left(KDRect r) { return r.x; } -static inline KDCoordinate right(KDRect r) { return r.x+r.width; } -static inline KDCoordinate top(KDRect r) { return r.y; } -static inline KDCoordinate bottom(KDRect r) { return r.y+r.height; } -static inline KDCoordinate min(KDCoordinate x, KDCoordinate y) { return (xy ? x : y); } - -bool KDRectIntersect(KDRect r1, KDRect r2) { - return !(right(r2) < left(r1) - || - left(r2) > right(r1) - || - top(r2) > bottom(r1) - || - bottom(r2) < top(r1) - ); -} - -KDRect KDRectIntersection(KDRect r1, KDRect r2) { - if (!KDRectIntersect(r1, r2)) { - return KDRectZero; - } - - KDCoordinate intersectionLeft = max(left(r1), left(r2)); - KDCoordinate intersectionRight = min(right(r1), right(r2)); - KDCoordinate intersectionTop = max(top(r1), top(r2)); - KDCoordinate intersectionBottom = min(bottom(r1), bottom(r2)); - - KDRect intersection; - intersection.x = intersectionLeft; - intersection.width = intersectionRight-intersectionLeft; - intersection.y = intersectionTop; - intersection.height = intersectionBottom-intersectionTop; - return intersection; -} - -static void KDRectComputeUnionBound(KDCoordinate size1, KDCoordinate size2, - KDCoordinate * outputMin, KDCoordinate * outputMax, - KDCoordinate min1, KDCoordinate min2, - KDCoordinate max1, KDCoordinate max2) -{ - if (size1 != 0) { - if (size2 != 0) { - *outputMin = min(min1, min2); - *outputMax = max(max1, max2); - } else { - *outputMin = min1; - *outputMax = max1; - } - } else { - if (size2 != 0) { - *outputMin = min2; - *outputMax = max2; - } - } -} - -KDRect KDRectUnion(KDRect r1, KDRect r2) { - /* We should ignore coordinate whose size is zero - * For example, if r1.height is zero, just ignore r1.y and r1.height. */ - - KDCoordinate resultLeft = 0; - KDCoordinate resultTop = 0; - KDCoordinate resultRight = 0; - KDCoordinate resultBottom = 0; - - KDRectComputeUnionBound(r1.width, r2.width, - &resultLeft, &resultRight, - left(r1), left(r2), - right(r1), right(r2)); - - KDRectComputeUnionBound(r1.height, r2.height, - &resultTop, &resultBottom, - top(r1), top(r2), - bottom(r1), bottom(r2)); - - return (KDRect){ - .x = resultLeft, - .y = resultTop, - .width = resultRight-resultLeft, - .height = resultBottom-resultTop - }; -} - -bool KDRectContains(KDRect r, KDPoint p) { - return (p.x >= r.x && p.x < (r.x+r.width) && p.y >= r.y && p.y < (r.y+r.height)); -} - -KDRect KDRectTranslate(KDRect r, KDPoint p) { - return (KDRect){ - .origin = KDPointTranslate(r.origin, p), - .size = r.size - }; -} - -KDRect absoluteFillRect(KDRect rect) { - KDRect absolutRect = rect; - absolutRect.origin = KDPointTranslate(absolutRect.origin, KDCurrentContext->origin); - - KDRect rectToBeFilled = KDRectIntersection(absolutRect, KDCurrentContext->clippingRect); - return rectToBeFilled; -} - -void KDFillRect(KDRect rect, KDColor color) { - KDRect absoluteRect = absoluteFillRect(rect); - if (absoluteRect.width > 0 && absoluteRect.height > 0) { - KDCurrentContext->io.pushRectUniform(absoluteRect, color); - } -} - -/* Note: we support the case where workingBuffer IS equal to pixels */ -void KDFillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer) { - KDRect absoluteRect = absoluteFillRect(rect); - - if (absoluteRect.width == 0 || absoluteRect.height == 0) { - return; - } - - /* Caution: - * The absoluteRect may have a SMALLER size than the original rect because it - * has been clipped. Therefore we cannot assume that the mask can be read as a - * continuous area. */ - - if (absoluteRect.width == rect.width && absoluteRect.height == rect.height) { - KDCurrentContext->io.pushRect(absoluteRect, pixels); - return; - } - - /* At this point we need the working buffer */ - assert(workingBuffer != NULL); - for (KDCoordinate j=0; jio.pushRect(absoluteRect, workingBuffer); -} - -// Mask's size must be rect.size -// WorkingBuffer, same deal -void KDFillRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer) { - KDRect absoluteRect = absoluteFillRect(rect); - - /* Caution: - * The absoluteRect may have a SMALLER size than the original rect because it - * has been clipped. Therefore we cannot assume that the mask can be read as a - * continuous area. */ - - KDCurrentContext->io.pullRect(absoluteRect, workingBuffer); - for (KDCoordinate j=0; jio.pushRect(absoluteRect, workingBuffer); -} - -#if 0 - -/* Takes an absolute rect and pushes the pixels */ -/* Does the pattern-to-continuous-memory conversion */ -void pushRect(KDRect rect, const KDColor * pattern, KDSize patternSize) { - KDCurrentContext->io.setWorkingArea(rect); -//#define ION_DEVICE_FILL_RECT_FAST_PATH 1 -#if ION_DEVICE_FILL_RECT_FAST_PATH - /* If the pattern width matches the target rect width, we can easily push - * mutliple lines at once since those will be contiguous in memory. */ - if (patternSize.width == rect.width) { - size_t remainingPixelCount = rect.width*rect.height; - size_t patternPixelCount = patternSize.width*patternSize.height; - while (remainingPixelCount > 0) { - int32_t blockSize = remainingPixelCount < patternPixelCount ? remainingPixelCount : patternPixelCount; - KDCurrentContext->io.pushPixels(pattern, blockSize); - remainingPixelCount -= blockSize; - } - return; - } -#endif - uint16_t remainingHeight = rect.height; - uint16_t patternLine = 0; - while (remainingHeight-- > 0) { - uint16_t remainingWidth = rect.width; - while (remainingWidth > 0) { - int32_t blockSize = remainingWidth < patternSize.width ? remainingWidth : patternSize.width; - KDCurrentContext->io.pushPixels(pattern + patternLine*patternSize.width, blockSize); - remainingWidth -= blockSize; - } - if (++patternLine >= patternSize.height) { - patternLine = 0; - } - } -} - -void KDBlitRect(KDRect rect, const KDColor * pattern, KDSize patternSize, const uint8_t * mask, KDSize maskSize) { - assert(pattern != NULL && patternSize.width > 0 && patternSize.height > 0); - KDRect absolutRect = rect; - absolutRect.origin = KDPointTranslate(absolutRect.origin, KDCurrentContext->origin); - - KDRect rectToBeFilled = KDRectIntersection(absolutRect, KDCurrentContext->clippingRect); - - bool useBlending = (mask != NULL && maskSize.width > 0 && maskSize.height > 0); - - if (!useBlending) { - pushRect(rectToBeFilled, pattern, patternSize); - return; - } - - assert(KDCurrentContext->io.pullPixels != NULL); - - KDCoordinate patternX = 0, patternY = 0, maskX = 0, maskY = 0; - for (KDCoordinate j=0; j= patternSize.width) { - patternX = 0; - } - if (++maskX >= maskSize.width) { - maskX = 0; - } - } - if (++patternY >= patternSize.height) { - patternY = 0; - } - if (++maskY >= maskSize.height) { - maskY = 0; - } - } -} -#endif - -void KDDrawRect(KDRect rect, KDColor color) { - KDPoint p1, p2; - p1.x = rect.x; - p1.y = rect.y; - p2.x = rect.x; - p2.y = rect.y + rect.height; - for (int i = 0; i + +KDRect::KDRect(KDPoint p, KDSize s) : + m_x(p.x()), m_y(p.y()), + m_width(s.width()), m_height(s.height()) +{ +} + +KDRect::KDRect(KDCoordinate x, KDCoordinate y, KDSize s) : + m_x(x), m_y(y), + m_width(s.width()), m_height(s.height()) +{ +} + +KDRect::KDRect(KDPoint p, KDCoordinate width, KDCoordinate height) : + m_x(p.x()), m_y(p.y()), + m_width(width), m_height(height) +{ +} + +KDCoordinate KDRect::x() const { return m_x; } +KDCoordinate KDRect::y() const { return m_y; } +KDPoint KDRect::origin() const { return KDPoint(m_x, m_y); } +KDCoordinate KDRect::width() const { return m_width; } +KDCoordinate KDRect::height() const { return m_height; } +KDSize KDRect::size() const { return KDSize(m_width, m_height); } + +KDCoordinate KDRect::top() const { return m_y; } +KDCoordinate KDRect::right() const { return m_x+m_width; } +KDCoordinate KDRect::bottom() const { return m_y+m_height; } +KDCoordinate KDRect::left() const { return m_x; } + +static inline KDCoordinate min(KDCoordinate x, KDCoordinate y) { return (xy ? x : y); } + +bool KDRect::intersects(const KDRect & other) const { + return ( + other.right() >= left() && + other.left() <= right() && + other.top() <= bottom() && + other.bottom() >= top() + ); +} + +KDRect KDRect::intersectedWith(const KDRect & other) const { + if (!intersects(other)) { + return KDRectZero; + } + + KDCoordinate intersectionLeft = max(left(), other.left()); + KDCoordinate intersectionRight = min(right(), other.right()); + KDCoordinate intersectionTop = max(top(), other.top()); + KDCoordinate intersectionBottom = min(bottom(), other.bottom()); + + return KDRect( + intersectionLeft, + intersectionTop, + intersectionRight-intersectionLeft, + intersectionBottom-intersectionTop); +} + +void computeUnionBound(KDCoordinate size1, KDCoordinate size2, + KDCoordinate * outputMin, KDCoordinate * outputMax, + KDCoordinate min1, KDCoordinate min2, + KDCoordinate max1, KDCoordinate max2) +{ + if (size1 != 0) { + if (size2 != 0) { + *outputMin = min(min1, min2); + *outputMax = max(max1, max2); + } else { + *outputMin = min1; + *outputMax = max1; + } + } else { + if (size2 != 0) { + *outputMin = min2; + *outputMax = max2; + } + } +} + +KDRect KDRect::unionedWith(const KDRect & other) const { + /* We should ignore coordinate whose size is zero + * For example, if r1.height is zero, just ignore r1.y and r1.height. */ + + KDCoordinate resultLeft = 0; + KDCoordinate resultTop = 0; + KDCoordinate resultRight = 0; + KDCoordinate resultBottom = 0; + + computeUnionBound(width(), other.width(), + &resultLeft, &resultRight, + left(), other.left(), + right(), other.right()); + + computeUnionBound(height(), other.height(), + &resultTop, &resultBottom, + top(), other.top(), + bottom(), other.bottom()); + + return KDRect( + resultLeft, + resultTop, + resultRight-resultLeft, + resultBottom-resultTop + ); +} + +bool KDRect::contains(KDPoint p) const { + return (p.x() >= x() && p.x() < right() && p.y() >= y() && p.y() < bottom()); +} + +KDRect KDRect::translatedBy(KDPoint p) const { + return KDRect(x() + p.x(), y() + p.y(), width(), height()); +} + +KDRect KDRect::movedTo(KDPoint p) const { + return KDRect(p.x(), p.y(), width(), height()); +} + +bool KDRect::isEmpty() const { + return (width() == 0 || height() == 0); +} diff --git a/kandinsky/src/size.cpp b/kandinsky/src/size.cpp new file mode 100644 index 000000000..9b681f88b --- /dev/null +++ b/kandinsky/src/size.cpp @@ -0,0 +1,4 @@ +#include + +KDCoordinate KDSize::width() const { return m_width; } +KDCoordinate KDSize::height() const { return m_height; } diff --git a/kandinsky/src/types.c b/kandinsky/src/types.c deleted file mode 100644 index 5518ca27b..000000000 --- a/kandinsky/src/types.c +++ /dev/null @@ -1,3 +0,0 @@ -#include - -KDPoint KDPointZero = {.x = 0, .y = 0}; diff --git a/kandinsky/test/color.c b/kandinsky/test/color.cpp similarity index 63% rename from kandinsky/test/color.c rename to kandinsky/test/color.cpp index e325116c1..9fbc9081e 100644 --- a/kandinsky/test/color.c +++ b/kandinsky/test/color.cpp @@ -3,15 +3,17 @@ #include QUIZ_CASE(kandinsky_color_rgb) { - assert(KDColorRGB(0xFF, 0, 0) == 0xF800); - assert(KDColorRGB(0, 0xFF, 0) == 0x07E0); - assert(KDColorRGB(0, 0, 0xFF) == 0x1F); + assert(sizeof(KDColor) == 2); // We really want KDColor to be packed + + assert(KDColor(0xFF0000) == 0xF800); + assert(KDColor(0x00FF00) == 0x07E0); + assert(KDColor(0x0000FF) == 0x1F); /* R = 0x12 = 0b 0001 0010. 5 most important bits are 00010. * G = 0x34 = 0b 0011 0100. 6 most important bits are 001101. * B = 0x56 = 0b 0101 0110. 5 most important bits are 01010. * KDColor = 0b 00010 001101 01010 * = 0b 0001 0001 1010 1010 * = 0x 1 1 A A */ - assert(KDColorRGB(0x12, 0x34, 0x56) == 0x11AA); + assert(KDColor(0x123456) == 0x11AA); } diff --git a/kandinsky/test/rect.c b/kandinsky/test/rect.c deleted file mode 100644 index c46f69a0e..000000000 --- a/kandinsky/test/rect.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include - -QUIZ_CASE(kandinsky_rect_intersect) { - KDRect a = { - .x = -5, .y = -5, - .width = 10, .height = 10 - }; - KDRect b = { - .x = 0, .y = 0, - .width = 10, .height = 10 - }; - assert(KDRectIntersect(a,b)); - assert(KDRectIntersect(b,a)); - KDRect c = KDRectIntersection(a,b); - assert(c.x == 0); - assert(c.y == 0); - assert(c.width == 5); - assert(c.height == 5); - c = KDRectIntersection(b,a); - assert(c.x == 0); - assert(c.y == 0); - assert(c.width == 5); - assert(c.height == 5); -} - -QUIZ_CASE(kandinsky_rect_union) { - KDRect a = { - .x = -5, .y = -5, - .width = 10, .height = 10 - }; - KDRect b = { - .x = 0, .y = 0, - .width = 10, .height = 10 - }; - - KDRect c = KDRectUnion(a,b); - assert(c.x == -5); - assert(c.y == -5); - assert(c.width == 15); - assert(c.height == 15); - - c = KDRectUnion(a,b); - assert(c.x == -5); - assert(c.y == -5); - assert(c.width == 15); - assert(c.height == 15); -} - -QUIZ_CASE(kandinsky_rect_empty_union) { - KDRect a = { - .x = 1, .y = 2, - .width = 3, .height = 4 - }; - KDRect b = { - .x = 5, .y = 6, - .width = 0, .height = 0 - }; - KDRect c = { - .x = -2, .y = -1, - .width = 0, .height = 1 - }; - - KDRect t = KDRectUnion(a,b); - assert(t.x == a.x); - assert(t.y == a.y); - assert(t.width == a.width); - assert(t.height == a.height); - - t = KDRectUnion(b,a); - assert(t.x == a.x); - assert(t.y == a.y); - assert(t.width == a.width); - assert(t.height == a.height); - - t = KDRectUnion(a,c); - assert(t.x == a.x); - assert(t.y == c.y); - assert(t.width == a.width); - assert(t.height == 7); -} diff --git a/kandinsky/test/rect.cpp b/kandinsky/test/rect.cpp new file mode 100644 index 000000000..5a374860a --- /dev/null +++ b/kandinsky/test/rect.cpp @@ -0,0 +1,60 @@ +#include +#include +#include + +QUIZ_CASE(kandinsky_rect_intersect) { + KDRect a(-5,-5, 10, 10); + KDRect b(0, 0, 10, 10); + assert(a.intersects(b)); + assert(b.intersects(a)); + KDRect c = a.intersectedWith(b); + assert(c.x() == 0); + assert(c.y() == 0); + assert(c.width() == 5); + assert(c.height() == 5); + c = b.intersectedWith(a); + assert(c.x() == 0); + assert(c.y() == 0); + assert(c.width() == 5); + assert(c.height() == 5); +} + +QUIZ_CASE(kandinsky_rect_union) { + KDRect a(-5, -5, 10, 10); + KDRect b(0, 0, 10, 10); + KDRect c = a.unionedWith(b); + assert(c.x() == -5); + assert(c.y() == -5); + assert(c.width() == 15); + assert(c.height() == 15); + + c = a.unionedWith(b); + assert(c.x() == -5); + assert(c.y() == -5); + assert(c.width() == 15); + assert(c.height() == 15); +} + +QUIZ_CASE(kandinsky_rect_empty_union) { + KDRect a(1, 2, 3, 4); + KDRect b(5, 6, 0, 0); + KDRect c(-2, -1, 0, 1); + + KDRect t = a.unionedWith(b); + assert(t.x() == a.x()); + assert(t.y() == a.y()); + assert(t.width() == a.width()); + assert(t.height() == a.height()); + + t = b.unionedWith(a); + assert(t.x() == a.x()); + assert(t.y() == a.y()); + assert(t.width() == a.width()); + assert(t.height() == a.height()); + + t = a.unionedWith(c); + assert(t.x() == a.x()); + assert(t.y() == c.y()); + assert(t.width() == a.width()); + assert(t.height() == 7); +} diff --git a/kandinsky/test/set_pixel.c b/kandinsky/test/set_pixel.c deleted file mode 100644 index 09a436674..000000000 --- a/kandinsky/test/set_pixel.c +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include - -QUIZ_CASE(setpixel) { -} diff --git a/quiz/src/runner.c b/quiz/src/runner.cpp similarity index 68% rename from quiz/src/runner.c rename to quiz/src/runner.cpp index 643a7a0b5..ae0f97ba5 100644 --- a/quiz/src/runner.c +++ b/quiz/src/runner.cpp @@ -1,12 +1,15 @@ #include "symbols.h" #include #include +extern "C" { #include +} -void print(char * message) { +void print(const char * message) { static int line_y = 0; - int line_height = KDStringSize("M").height; - KDDrawString(message, (KDPoint){.x = 0, .y = line_y}, 0); + KDContext * ctx = KDIonContext::sharedContext(); + int line_height = ctx->stringSize("M").height(); + ctx->drawString(message, KDPoint(0, line_y), 0); line_y += line_height; if (line_y > ION_SCREEN_HEIGHT) { line_y = 0;