mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
Escher: Introduce the TiledView
Change-Id: I97d612cf89bd9cf45f8b440881918b9626cd65f6
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
#include "cursor_view.h"
|
||||
|
||||
void CursorView::drawRect(KDRect rect) const {
|
||||
KDColor cursorColor = KDColorRed;
|
||||
KDFillRect(rect, &cursorColor, 1);
|
||||
KDFillRect(rect, KDColorRed);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,11 @@ constexpr int kNumberOfMainGridLines = 5;
|
||||
constexpr int kNumberOfSecondaryGridLines = 4;
|
||||
|
||||
GraphView::GraphView() :
|
||||
#if GRAPH_VIEW_IS_TILED
|
||||
TiledView(),
|
||||
#else
|
||||
View(),
|
||||
#endif
|
||||
m_cursorView(CursorView()),
|
||||
m_cursorPosition(KDPointZero),
|
||||
m_xMin(-2.0f),
|
||||
@@ -39,36 +43,65 @@ void GraphView::layoutSubviews() {
|
||||
m_cursorView.setFrame(cursorFrame);
|
||||
}
|
||||
|
||||
#if GRAPH_VIEW_IS_TILED
|
||||
KDColor * GraphView::tile() const {
|
||||
return (KDColor *)m_tile;
|
||||
}
|
||||
|
||||
KDSize GraphView::tileSize() const {
|
||||
return {kTileWidth, kTileHeight};
|
||||
}
|
||||
|
||||
void GraphView::drawTile(KDRect rect) const {
|
||||
#else
|
||||
void GraphView::drawRect(KDRect rect) const {
|
||||
KDColor backgroundColor = KDColorWhite;
|
||||
KDFillRect(rect, &backgroundColor, 1);
|
||||
#endif
|
||||
KDFillRect(rect, KDColorWhite);
|
||||
drawGrid(rect);
|
||||
drawAxes(rect);
|
||||
drawFunction(rect);
|
||||
/*
|
||||
|
||||
constexpr int maskLength = 3;
|
||||
uint8_t mask[maskLength] = {
|
||||
#if 1
|
||||
0x10, 0x70, 0xE0,
|
||||
#else
|
||||
0x00, 0x30, 0x73, 0x30, 0x00,
|
||||
0x30, 0xfb, 0xff, 0xfb, 0x30,
|
||||
0x73, 0xff, 0xff, 0xff, 0x73,
|
||||
0x30, 0xfb, 0xff, 0xfb, 0x30,
|
||||
0x00, 0x30, 0x73, 0x30, 0x00
|
||||
#endif
|
||||
};
|
||||
|
||||
KDColor red = KDColorRed;
|
||||
KDBlitRect(rect, &red, 1, mask, maskLength);
|
||||
*/
|
||||
}
|
||||
|
||||
void GraphView::drawLine(KDRect rect, Axis axis, float coordinate, KDColor color) const {
|
||||
void GraphView::drawLine(KDRect rect, Axis axis, float coordinate, KDColor color, KDCoordinate thickness) const {
|
||||
KDRect lineRect;
|
||||
switch(axis) {
|
||||
case Axis::Horizontal:
|
||||
lineRect.x = rect.x;
|
||||
lineRect.y = floatToPixel(Axis::Vertical, coordinate);
|
||||
lineRect.width = rect.width;
|
||||
lineRect.height = 1;
|
||||
lineRect.height = thickness;
|
||||
break;
|
||||
case Axis::Vertical:
|
||||
lineRect.x = floatToPixel(Axis::Horizontal, coordinate);
|
||||
lineRect.y = rect.y;
|
||||
lineRect.width = 1;
|
||||
lineRect.width = thickness;
|
||||
lineRect.height = rect.height;
|
||||
break;
|
||||
}
|
||||
KDFillRect(lineRect, &color, 1);
|
||||
KDFillRect(lineRect, color);
|
||||
}
|
||||
|
||||
void GraphView::drawAxes(KDRect rect) const {
|
||||
drawLine(rect, Axis::Horizontal, 0.0f, kAxisColor);
|
||||
drawLine(rect, Axis::Vertical, 0.0f, kAxisColor);
|
||||
drawLine(rect, Axis::Horizontal, 0.0f, kAxisColor, 2);
|
||||
drawLine(rect, Axis::Vertical, 0.0f, kAxisColor, 2);
|
||||
}
|
||||
|
||||
void GraphView::drawGridLines(KDRect rect, Axis axis, int count, KDColor color) const {
|
||||
@@ -113,11 +146,80 @@ KDCoordinate GraphView::floatToPixel(Axis axis, float f) const {
|
||||
}
|
||||
|
||||
void GraphView::drawFunction(KDRect rect) const {
|
||||
KDPoint p = KDPointZero;
|
||||
for (p.x=rect.x; p.x<(rect.x+rect.width); p.x++) {
|
||||
KDPoint p;
|
||||
|
||||
constexpr KDCoordinate stampSize = 5;
|
||||
|
||||
uint8_t mask[stampSize*stampSize] = {
|
||||
0x00, 0x30, 0x73, 0x30, 0x00,
|
||||
0x30, 0xfb, 0xff, 0xfb, 0x30,
|
||||
0x73, 0xff, 0xff, 0xff, 0x73,
|
||||
0x30, 0xfb, 0xff, 0xfb, 0x30,
|
||||
0x00, 0x30, 0x73, 0x30, 0x00
|
||||
};
|
||||
|
||||
KDColor workingBuffer[stampSize*stampSize];
|
||||
|
||||
for (p.x=rect.x-stampSize; p.x<(rect.x+rect.width); p.x++) {
|
||||
float x = pixelToFloat(Axis::Horizontal, p.x);
|
||||
float y = (x-1)*(x+1)*x;
|
||||
p.y = floatToPixel(Axis::Vertical, y);
|
||||
KDSetPixel(p, KDColorRGB(0xCF, 0, 0));
|
||||
KDRect stampRect;
|
||||
stampRect.origin = p;
|
||||
stampRect.width = stampSize;
|
||||
stampRect.height = stampSize;
|
||||
//KDColor red = KDColorRed;
|
||||
KDFillRectWithMask(stampRect, KDColorRed, mask, workingBuffer);
|
||||
//KDBlitRect(stampRect, &red, {1,1}, mask, {stampSize,stampSize});
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
void GraphView::drawFunction(KDRect rect) const {
|
||||
/* Naive algorithm: iterate on pixels horizontally
|
||||
* Actually, this is kinda optimal. If two consecutive pixels don't touch
|
||||
* (i.e. they have a y-difference greater than 1), just draw a vertical line.
|
||||
* This may seem stupid but is kinda good actually. */
|
||||
KDCoordinate previousPixelY;
|
||||
KDCoordinate pixelX;
|
||||
|
||||
KDColor curveColor = KDColorRGB(0xFF, 0, 0);
|
||||
#define ANTI_ALIASING
|
||||
#ifdef ANTI_ALIASING
|
||||
KDColor antiAliasingColor = KDColorRGB(0, 0xFF, 0);
|
||||
#endif
|
||||
|
||||
for (pixelX=rect.x; pixelX<(rect.x+rect.width); pixelX++) {
|
||||
float x = pixelToFloat(Axis::Horizontal, pixelX);
|
||||
float y = (x-1)*(x+1)*x;
|
||||
KDCoordinate pixelY = floatToPixel(Axis::Vertical, y);
|
||||
KDRect r;
|
||||
r.x = pixelX;
|
||||
if (pixelY < previousPixelY) {
|
||||
r.y = pixelY;
|
||||
r.height = previousPixelY-pixelY;
|
||||
KDPoint p;
|
||||
p.x = pixelX-1;
|
||||
p.y = previousPixelY+3;
|
||||
KDSetPixel(p, KDColorRGB(0x00, 0xFF, 0x00));
|
||||
p.x = pixelX;
|
||||
p.y = pixelY-1;
|
||||
KDSetPixel(p, KDColorRGB(0x00, 0xFF, 0x00));
|
||||
} else {
|
||||
r.y = previousPixelY;
|
||||
r.height = pixelY - previousPixelY;
|
||||
}
|
||||
if (r.height == 0) {
|
||||
r.height = 1;
|
||||
}
|
||||
r.width = 1;
|
||||
|
||||
r.width += 2;
|
||||
r.height += 2;
|
||||
//KDFillRectWithPattern(r, antialiasedDot);
|
||||
KDFillRect(r, KDColorRGB(0xFF, 0, 0));
|
||||
previousPixelY = pixelY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,10 +4,27 @@
|
||||
#include <escher.h>
|
||||
#include "cursor_view.h"
|
||||
|
||||
class GraphView : public View {
|
||||
#define GRAPH_VIEW_IS_TILED 1
|
||||
|
||||
class GraphView : public
|
||||
#if GRAPH_VIEW_IS_TILED
|
||||
TiledView
|
||||
#else
|
||||
View
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
GraphView();
|
||||
|
||||
#if GRAPH_VIEW_IS_TILED
|
||||
KDColor * tile() const override;
|
||||
KDSize tileSize() const override;
|
||||
void drawTile(KDRect rect) const override;
|
||||
#else
|
||||
void drawRect(KDRect rect) const override;
|
||||
#endif
|
||||
|
||||
// void drawRect(KDRect rect) const override;
|
||||
void moveCursorRight();
|
||||
private:
|
||||
int numberOfSubviews() const override;
|
||||
@@ -27,13 +44,17 @@ private:
|
||||
KDCoordinate floatToPixel(Axis axis, float f) const;
|
||||
|
||||
void drawLine(KDRect rect, Axis axis,
|
||||
float coordinate, KDColor color) const;
|
||||
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;
|
||||
|
||||
static constexpr KDCoordinate kTileWidth = 32;
|
||||
static constexpr KDCoordinate kTileHeight = 32;
|
||||
KDColor m_tile[kTileWidth*kTileHeight];
|
||||
|
||||
CursorView m_cursorView;
|
||||
KDPoint m_cursorPosition;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ FunctionCell::FunctionCell() :
|
||||
|
||||
void FunctionCell::drawRect(KDRect rect) const {
|
||||
KDColor background = m_even ? KDColorRGB(0xEE, 0xEE, 0xEE) : KDColorRGB(0x77,0x77,0x77);
|
||||
KDFillRect(rect, &background, 1);
|
||||
KDFillRect(rect, background);
|
||||
KDDrawString(m_message, KDPointZero, m_focused);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ objs += $(addprefix escher/src/,\
|
||||
table_view.o\
|
||||
text_field.o\
|
||||
text_view.o\
|
||||
tiled_view.o\
|
||||
view.o\
|
||||
view_controller.o\
|
||||
window.o\
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <escher/text_view.h>
|
||||
#include <escher/tab_view_controller.h>
|
||||
#include <escher/table_view.h>
|
||||
#include <escher/tiled_view.h>
|
||||
#include <escher/view.h>
|
||||
#include <escher/view_controller.h>
|
||||
#include <escher/window.h>
|
||||
|
||||
16
escher/include/escher/tiled_view.h
Normal file
16
escher/include/escher/tiled_view.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef ESCHER_TILED_VIEW_H
|
||||
#define ESCHER_TILED_VIEW_H
|
||||
|
||||
#include <escher/view.h>
|
||||
|
||||
class TiledView : public View {
|
||||
using View::View;
|
||||
protected:
|
||||
void drawRect(KDRect rect) const override;
|
||||
virtual void drawTile(KDRect rect) const = 0;
|
||||
|
||||
virtual KDColor * tile() const = 0;
|
||||
virtual KDSize tileSize() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -15,7 +15,7 @@ ScrollViewIndicator::ScrollViewIndicator(ScrollViewIndicator::Direction directio
|
||||
}
|
||||
|
||||
void ScrollViewIndicator::drawRect(KDRect rect) const {
|
||||
KDFillRect(bounds(), &k_backgroundColor, 1);
|
||||
KDFillRect(bounds(), k_backgroundColor);
|
||||
KDRect indicatorFrame;
|
||||
if (m_direction == Direction::Horizontal) {
|
||||
indicatorFrame.x = m_start*m_frame.width;
|
||||
@@ -29,7 +29,7 @@ void ScrollViewIndicator::drawRect(KDRect rect) const {
|
||||
indicatorFrame.width = m_frame.width;
|
||||
indicatorFrame.height = (m_end-m_start)*m_frame.height;
|
||||
}
|
||||
KDFillRect(indicatorFrame, &k_indicatorColor, 1);
|
||||
KDFillRect(indicatorFrame, k_indicatorColor);
|
||||
}
|
||||
|
||||
void ScrollViewIndicator::setStart(float start) {
|
||||
|
||||
@@ -7,7 +7,7 @@ SolidColorView::SolidColorView(KDColor color) :
|
||||
}
|
||||
|
||||
void SolidColorView::drawRect(KDRect rect) const {
|
||||
KDFillRect(rect, &m_color, 1);
|
||||
KDFillRect(rect, m_color);
|
||||
}
|
||||
|
||||
#if ESCHER_VIEW_LOGGING
|
||||
|
||||
@@ -13,7 +13,7 @@ TabView::TabView() :
|
||||
|
||||
void TabView::drawRect(KDRect rect) const {
|
||||
KDColor backgroundColor = KDColorRGB(0xb5, 0x1d, 0xab);
|
||||
KDFillRect(rect, &backgroundColor, 1);
|
||||
KDFillRect(rect, backgroundColor);
|
||||
}
|
||||
|
||||
void TabView::addTabNamed(const char * name) {
|
||||
|
||||
53
escher/src/tiled_view.cpp
Normal file
53
escher/src/tiled_view.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <escher/tiled_view.h>
|
||||
#include <assert.h>
|
||||
|
||||
static KDFrameBuffer sCurrentTile;
|
||||
|
||||
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;
|
||||
//tileRect = KDRectIntersection(tileRect, rect); // Optional
|
||||
KDContext * previousContext = KDCurrentContext;
|
||||
sTileContext.origin.x = -tileRect.x;
|
||||
sTileContext.origin.y = -tileRect.y;
|
||||
|
||||
KDCurrentContext = &sTileContext;
|
||||
drawTile(tileRect);
|
||||
KDCurrentContext = previousContext;
|
||||
KDSize zero = {0,0};
|
||||
|
||||
KDFillRectWithPixels(tileRect, sCurrentTile.pixels, sCurrentTile.pixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,21 +11,16 @@
|
||||
* pixel coordinates every time. We're therefore leveraging this capability
|
||||
* which results in a very consequent speedup (up to ~10x faster). */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <kandinsky.h>
|
||||
|
||||
/* ION manipulates RGB565 colors */
|
||||
typedef uint16_t ion_color_t;
|
||||
|
||||
/* Set the color of a single pixel. */
|
||||
void ion_set_pixel(uint16_t x, uint16_t y, ion_color_t color);
|
||||
|
||||
/* Fill a rect with a single color */
|
||||
void ion_fill_rect(
|
||||
uint16_t x, uint16_t y,
|
||||
uint16_t width, uint16_t height,
|
||||
ion_color_t * pattern, size_t patternSize
|
||||
);
|
||||
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);
|
||||
/*
|
||||
void ion_screen_set_working_area(KDRect area);
|
||||
void ion_screen_push_pixels(const KDColor * pixels, size_t count);
|
||||
void ion_screen_pull_pixels(KDColor * pixels, size_t count);
|
||||
*/
|
||||
|
||||
#define ION_SCREEN_WIDTH 320
|
||||
#define ION_SCREEN_HEIGHT 240
|
||||
|
||||
@@ -40,25 +40,59 @@ void ion_display_off() {
|
||||
// Turn off panel
|
||||
}
|
||||
|
||||
void ion_set_pixel(uint16_t x, uint16_t y, ion_color_t color) {
|
||||
st7789_set_drawing_area(&sDisplayController, x, y, 1, 1);
|
||||
st7789_push_pixels(&sDisplayController, &color, 1);
|
||||
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_fill_rect(
|
||||
uint16_t x, uint16_t y,
|
||||
uint16_t width, uint16_t height,
|
||||
ion_color_t * pattern, size_t patternSize)
|
||||
{
|
||||
st7789_set_drawing_area(&sDisplayController, x, y, width, height);
|
||||
size_t remainingSize = width*height;
|
||||
while (remainingSize > 0) {
|
||||
int32_t blockSize = remainingSize > patternSize ? patternSize : remainingSize;
|
||||
st7789_push_pixels(&sDisplayController, pattern, blockSize);
|
||||
remainingSize -= blockSize;
|
||||
void ion_screen_push_rect_uniform(KDRect rect, KDColor color) {
|
||||
st7789_set_drawing_area(&sDisplayController, rect);
|
||||
for (size_t i=0; i<rect.width*rect.height; i++) {
|
||||
st7789_push_pixels(&sDisplayController, &color, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ion_screen_pull_rect(KDRect rect, KDColor * pixels) {
|
||||
assert(0); // Unimplemented
|
||||
}
|
||||
|
||||
#if 0
|
||||
void ion_screen_push_rect(
|
||||
uint16_t x, uint16_t y,
|
||||
uint16_t width, uint16_t height,
|
||||
ion_color_t * pattern, uint16_t patternWidth, uint16_t patternHeight)
|
||||
{
|
||||
st7789_set_drawing_area(&sDisplayController, x, y, width, height);
|
||||
#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 (patternWidth == width) {
|
||||
size_t remainingSize = width*height;
|
||||
size_t patternSize = patternWidth*patternHeight;
|
||||
while (remainingSize > 0) {
|
||||
int32_t blockSize = remainingSize > patternSize ? patternSize : remainingSize;
|
||||
st7789_push_pixels(&sDisplayController, pattern, blockSize);
|
||||
remainingSize -= blockSize;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint16_t remainingHeight = height;
|
||||
uint16_t patternLine = 0;
|
||||
while (remainingHeight-- > 0) {
|
||||
uint16_t remainingWidth = width;
|
||||
while (remainingWidth > 0) {
|
||||
int32_t blockSize = remainingWidth > patternWidth ? patternWidth : remainingWidth;
|
||||
st7789_push_pixels(&sDisplayController, pattern+patternLine*patternWidth, blockSize);
|
||||
remainingWidth -= blockSize;
|
||||
}
|
||||
if (++patternLine >= patternHeight) {
|
||||
patternLine = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_display() {
|
||||
//assert(FRAMEBUFFER_LENGTH == (FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*FRAMEBUFFER_BITS_PER_PIXEL)/8);
|
||||
|
||||
|
||||
@@ -155,11 +155,12 @@ void st7789_initialize(st7789_t * c) {
|
||||
perform_instructions(c, init_sequence, sizeof(init_sequence)/sizeof(init_sequence[0]));
|
||||
}
|
||||
|
||||
void st7789_set_drawing_area(st7789_t * controller, int16_t x, int16_t y, int16_t width, int16_t height) {
|
||||
uint16_t x_start = x;
|
||||
uint16_t x_end = x + width - 1;
|
||||
uint16_t y_start = y;
|
||||
uint16_t y_end = y + height - 1;
|
||||
void st7789_set_drawing_area(st7789_t * controller, KDRect area) {
|
||||
assert(sizeof(KDCoordinate) == 2); // We expect 16-bit values
|
||||
uint16_t x_start = area.x;
|
||||
uint16_t x_end = area.x + area.width - 1;
|
||||
uint16_t y_start = area.y;
|
||||
uint16_t y_end = area.y + area.height - 1;
|
||||
|
||||
const instruction_t sequence[] = {
|
||||
COMMAND(CASET),
|
||||
@@ -180,8 +181,11 @@ void st7789_set_drawing_area(st7789_t * controller, int16_t x, int16_t y, int16_
|
||||
perform_instructions(controller, sequence, sizeof(sequence)/sizeof(sequence[0]));
|
||||
}
|
||||
|
||||
void st7789_push_pixels(st7789_t * controller, ion_color_t * pixels, int32_t numberOfPixels) {
|
||||
for (int32_t i=0; i<numberOfPixels; i++) {
|
||||
|
||||
void st7789_push_pixels(st7789_t * controller,
|
||||
const KDColor * pixels, size_t numberOfPixels) {
|
||||
assert(sizeof(KDColor) == 2); // We expect KDColor to be RGB565
|
||||
for (size_t i=0; i<numberOfPixels; i++) {
|
||||
perform_instruction(controller, DATA(pixels[i] >> 8));
|
||||
perform_instruction(controller, DATA(pixels[i] & 0xFF));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <ion/screen.h>
|
||||
#include <kandinsky.h>
|
||||
|
||||
#define ST7789_USE_9BIT_SPI 1
|
||||
|
||||
@@ -22,10 +23,8 @@ typedef struct {
|
||||
|
||||
void st7789_initialize(st7789_t * controller);
|
||||
|
||||
void st7789_set_drawing_area(st7789_t * controller,
|
||||
int16_t x, int16_t y,
|
||||
int16_t width, int16_t height);
|
||||
void st7789_set_drawing_area(st7789_t * controller, KDRect area);
|
||||
|
||||
void st7789_push_pixels(st7789_t * controller,
|
||||
ion_color_t * pixels, int32_t numberOfPixels);
|
||||
const KDColor * pixels, size_t numberOfPixels);
|
||||
#endif
|
||||
|
||||
@@ -2,14 +2,32 @@
|
||||
#include <stdlib.h>
|
||||
#include <FL/fl_draw.H>
|
||||
|
||||
FltkLCD::FltkLCD(int x, int y, int w, int h, const char * label) :
|
||||
Fl_Widget(x, y, w, h, label) {
|
||||
m_framebuffer = malloc(w*h*3);
|
||||
// FIXME: Delete the framebuffer!
|
||||
FltkLCD::FltkLCD(int x, int y, int w, int h, uint16_t * rgb565FrameBuffer) :
|
||||
Fl_Widget(x, y, w, h, nullptr),
|
||||
m_rgb565frameBufferStart(rgb565FrameBuffer),
|
||||
m_rgb565frameBufferEnd(rgb565FrameBuffer+w*h)
|
||||
{
|
||||
m_rgb888frameBufferStart = malloc(w*h*3);
|
||||
}
|
||||
|
||||
FltkLCD::~FltkLCD() {
|
||||
free(m_rgb888frameBufferStart);
|
||||
}
|
||||
|
||||
void FltkLCD::draw() {
|
||||
fl_draw_image((const uchar *)m_framebuffer,
|
||||
// 1/ Convert the framebuffer from 565 to 888
|
||||
KDColor * rgb565Pixel = m_rgb565frameBufferStart;
|
||||
uint8_t * rgb888Component = (uint8_t *)m_rgb888frameBufferStart;
|
||||
|
||||
while(rgb565Pixel < m_rgb565frameBufferEnd) {
|
||||
KDColor color = *rgb565Pixel++;
|
||||
*rgb888Component++ = KDColorRedComponent(color);
|
||||
*rgb888Component++ = KDColorGreenComponent(color);
|
||||
*rgb888Component++ = KDColorBlueComponent(color);
|
||||
}
|
||||
|
||||
// 2/ Draw the 888 framebuffer
|
||||
fl_draw_image((const uchar *)m_rgb888frameBufferStart,
|
||||
x(), // x
|
||||
y(), // y
|
||||
w(), // width
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
#define ION_FLTK_LCD
|
||||
|
||||
#include <FL/Fl_Widget.H>
|
||||
#include <kandinsky.h>
|
||||
|
||||
class FltkLCD : public Fl_Widget {
|
||||
public:
|
||||
FltkLCD(int x, int y, int w, int h, const char * label = 0);
|
||||
void * m_framebuffer;
|
||||
FltkLCD(int x, int y, int w, int h, KDColor * rgb565FrameBuffer);
|
||||
~FltkLCD();
|
||||
protected:
|
||||
void draw();
|
||||
private:
|
||||
KDColor * m_rgb565frameBufferStart;
|
||||
KDColor * m_rgb565frameBufferEnd;
|
||||
void * m_rgb888frameBufferStart;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@ extern "C" {
|
||||
|
||||
static FltkLCD * sDisplay;
|
||||
static FltkKbd * sKeyboard;
|
||||
static KDFrameBuffer sFrameBuffer;
|
||||
|
||||
#define FRAMEBUFFER_ADDRESS (sDisplay->m_framebuffer)
|
||||
|
||||
@@ -28,34 +29,34 @@ void init_platform() {
|
||||
|
||||
Fl_Window * window = new Fl_Window(screen_width+2*margin, margin+screen_height+margin+keyboard_height+margin);
|
||||
|
||||
sDisplay = new FltkLCD(margin, margin, screen_width, screen_height);
|
||||
sFrameBuffer.pixels = (KDColor *)malloc(ION_SCREEN_WIDTH*ION_SCREEN_HEIGHT*2);
|
||||
sFrameBuffer.size.width = ION_SCREEN_WIDTH;
|
||||
sFrameBuffer.size.height = 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);
|
||||
sKeyboard = new FltkKbd(margin, margin+screen_height+margin, screen_width, keyboard_height);
|
||||
|
||||
window->end();
|
||||
window->show();
|
||||
|
||||
KDCurrentContext->fillRect = NULL;
|
||||
//KDCurrentContext->fillRect = NULL;
|
||||
}
|
||||
|
||||
void ion_set_pixel(uint16_t x, uint16_t y, ion_color_t color) {
|
||||
assert(x < ION_SCREEN_WIDTH);
|
||||
assert(y < ION_SCREEN_HEIGHT);
|
||||
uint8_t * pixel = (uint8_t *)(FRAMEBUFFER_ADDRESS) + 3*((y*ION_SCREEN_WIDTH)+x);
|
||||
uint8_t red = (color >> 11) << 3;
|
||||
uint8_t green = ((color >> 5) & 0x3F) << 2;
|
||||
uint8_t blue = (color & 0x1F) << 3;
|
||||
*pixel++ = red;
|
||||
*pixel++ = green;
|
||||
*pixel++ = blue;
|
||||
void ion_screen_push_rect(KDRect rect, const KDColor * pixels) {
|
||||
KDFramePushRect(&sFrameBuffer, rect, pixels);
|
||||
}
|
||||
|
||||
void ion_fill_rect(
|
||||
uint16_t x, uint16_t y,
|
||||
uint16_t width, uint16_t height,
|
||||
ion_color_t * pattern, size_t patternSize)
|
||||
{
|
||||
assert(false);
|
||||
void ion_screen_push_rect_uniform(KDRect rect, KDColor color) {
|
||||
KDFramePushRectUniform(&sFrameBuffer, rect, color);
|
||||
}
|
||||
|
||||
void ion_screen_pull_rect(KDRect rect, KDColor * pixels) {
|
||||
KDFramePullRect(&sFrameBuffer, rect, pixels);
|
||||
}
|
||||
|
||||
bool ion_key_down(ion_key_t key) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
SFLAGS += -Ikandinsky/include
|
||||
objs += $(addprefix kandinsky/src/,\
|
||||
color.o\
|
||||
context.o\
|
||||
font.o\
|
||||
framebuffer.o\
|
||||
line.o\
|
||||
pixel.o\
|
||||
rect.o\
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <kandinsky/color.h>
|
||||
#include <kandinsky/context.h>
|
||||
#include <kandinsky/framebuffer.h>
|
||||
#include <kandinsky/line.h>
|
||||
#include <kandinsky/pixel.h>
|
||||
#include <kandinsky/rect.h>
|
||||
|
||||
@@ -11,8 +11,13 @@ typedef uint16_t KDColor;
|
||||
* 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)
|
||||
@@ -22,4 +27,8 @@ typedef uint16_t KDColor;
|
||||
#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);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
#include "rect.h"
|
||||
|
||||
typedef struct {
|
||||
void (*setPixel)(KDCoordinate x, KDCoordinate y, KDColor color);
|
||||
// fillRect can be left NULL.
|
||||
// In that case, Kandinsky will fall back to using setPixel only
|
||||
void (*fillRect)(KDCoordinate x, KDCoordinate y,
|
||||
KDCoordinate width, KDCoordinate height,
|
||||
KDColor * pattern, size_t patternSize);
|
||||
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;
|
||||
|
||||
17
kandinsky/include/kandinsky/framebuffer.h
Normal file
17
kandinsky/include/kandinsky/framebuffer.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef KANDINSKY_FRAMEBUFFER_H
|
||||
#define KANDINSKY_FRAMEBUFFER_H
|
||||
|
||||
#include <kandinsky/color.h>
|
||||
#include <kandinsky/types.h>
|
||||
#include <kandinsky/rect.h>
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
@@ -6,4 +6,6 @@
|
||||
|
||||
void KDDrawLine(KDPoint p1, KDPoint p2, KDColor c);
|
||||
|
||||
void KDDrawAntiAliasedLine(KDPoint p1, KDPoint p2, KDCoordinate width, KDColor frontColor, KDColor backColor);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
#include <kandinsky/color.h>
|
||||
|
||||
void KDSetPixel(KDPoint p, KDColor c);
|
||||
KDColor KDGetPixel(KDPoint p);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,7 +34,9 @@ KDRect KDRectUnion(KDRect r1, KDRect r2);
|
||||
bool KDRectContains(KDRect r, KDPoint p);
|
||||
KDRect KDRectTranslate(KDRect r, KDPoint p);
|
||||
|
||||
void KDFillRect(KDRect rect, const KDColor * pattern, size_t patternSize);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
||||
11
kandinsky/src/color.c
Normal file
11
kandinsky/src/color.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <kandinsky/color.h>
|
||||
|
||||
#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)
|
||||
);
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
#include <ion.h>
|
||||
|
||||
KDContext KDIonContext = {
|
||||
.setPixel = &ion_set_pixel,
|
||||
.fillRect = &ion_fill_rect,
|
||||
.io = {
|
||||
.pushRect = &ion_screen_push_rect,
|
||||
.pushRectUniform = &ion_screen_push_rect_uniform,
|
||||
.pullRect = &ion_screen_pull_rect
|
||||
},
|
||||
.origin = {
|
||||
.x = 0,
|
||||
.y = 0
|
||||
|
||||
34
kandinsky/src/framebuffer.c
Normal file
34
kandinsky/src/framebuffer.c
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <kandinsky/framebuffer.h>
|
||||
#include <string.h>
|
||||
|
||||
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<rect.height; j++) {
|
||||
memcpy(frameBufferPixelAddress(frameBuffer, rect.x, rect.y+j),
|
||||
line,
|
||||
rect.width*sizeof(KDColor));
|
||||
line += rect.width;
|
||||
}
|
||||
}
|
||||
|
||||
void KDFramePushRectUniform(KDFrameBuffer * frameBuffer, KDRect rect, KDColor color) {
|
||||
for (int j=0; j<rect.height; j++) {
|
||||
for (int i=0; i<rect.width; i++) {
|
||||
*frameBufferPixelAddress(frameBuffer, rect.x+i, rect.y+j) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KDFramePullRect(KDFrameBuffer * frameBuffer, KDRect rect, KDColor * pixels) {
|
||||
KDColor * line = pixels;
|
||||
for (int j=0; j<rect.height; j++) {
|
||||
memcpy(line,
|
||||
frameBufferPixelAddress(frameBuffer, rect.x, rect.y+j),
|
||||
rect.width*sizeof(KDColor));
|
||||
line += rect.width;
|
||||
}
|
||||
}
|
||||
@@ -58,3 +58,43 @@ void KDDrawLine(KDPoint p1, KDPoint p2, KDColor c) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void KDDrawAntiAliasedLine(KDPoint p1, KDPoint p2, KDCoordinate width, KDColor frontColor, KDColor backColor) {
|
||||
int x0 = p1.x;
|
||||
int y0 = p1.y;
|
||||
int x1 = p2.x;
|
||||
int y1 = p2.y;
|
||||
float wd = width;
|
||||
|
||||
int dx = abs(x1-x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = abs(y1-y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx-dy, e2, x2, y2; // error value e_xy
|
||||
float ed = dx+dy == 0 ? 1 : sqrt((float)dx*dx+(float)dy*dy);
|
||||
|
||||
for (wd = (wd+1)/2; ; ) { // pixel loop
|
||||
KDPoint p = {.x = x0, .y = y0};
|
||||
KDColor color = KDColorMix(KDColorRed, KDColorWhite, (abs(err-dx+dy)/ed-wd+1));
|
||||
KDSetPixel(p, color);
|
||||
e2 = err; x2 = x0;
|
||||
if (2*e2 >= -dx) { // x step
|
||||
for (e2 += dy, y2 = y0; e2 < ed*wd && (y1 != y2 || dx > dy); e2 += dx) {
|
||||
y2 += sy;
|
||||
p = {.x = x0, .y = y2};
|
||||
color = KDColorMix(KDColorRed, KDColorWhite, (abs(err-dx+dy)/ed-wd+1));
|
||||
setPixelColor(x0, y2 += sy, max(0,255*(abs(e2)/ed-wd+1)));
|
||||
}
|
||||
if (x0 == x1) break;
|
||||
e2 = err; err -= dy; x0 += sx;
|
||||
}
|
||||
if (2*e2 <= dy) { // y step
|
||||
for (e2 = dx-e2; e2 < ed*wd && (x1 != x2 || dx < dy); e2 += dy)
|
||||
setPixelColor(x2 += sx, y0, max(0,255*(abs(e2)/ed-wd+1)));
|
||||
if (y0 == y1) break;
|
||||
err += dx; y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,29 @@
|
||||
void KDSetPixel(KDPoint p, KDColor c) {
|
||||
KDPoint absolutePoint = KDPointTranslate(p, KDCurrentContext->origin);
|
||||
if (KDRectContains(KDCurrentContext->clippingRect, absolutePoint)) {
|
||||
KDCurrentContext->setPixel(absolutePoint.x, absolutePoint.y, c);
|
||||
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;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -101,38 +101,150 @@ KDRect KDRectTranslate(KDRect r, KDPoint p) {
|
||||
};
|
||||
}
|
||||
|
||||
void KDPerPixelFillRect(KDCoordinate x, KDCoordinate y,
|
||||
KDCoordinate width, KDCoordinate height,
|
||||
KDColor * pattern, size_t patternSize) {
|
||||
size_t offset = 0;
|
||||
for (KDCoordinate j=0; j<height; j++) {
|
||||
for (KDCoordinate i=0; i<width; i++) {
|
||||
KDCurrentContext->setPixel(x+i, y+j, pattern[offset++]);
|
||||
if (offset >= patternSize) {
|
||||
offset = 0;
|
||||
}
|
||||
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; j<absoluteRect.height; j++) {
|
||||
for (KDCoordinate i=0; i<absoluteRect.width; i++) {
|
||||
workingBuffer[i+absoluteRect.width*j] = pixels[i+rect.width*j];
|
||||
}
|
||||
}
|
||||
KDCurrentContext->io.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; j<absoluteRect.height; j++) {
|
||||
for (KDCoordinate i=0; i<absoluteRect.width; i++) {
|
||||
KDColor * currentPixelAdress = workingBuffer + i + absoluteRect.width*j;
|
||||
const uint8_t * currentMaskAddress = mask + i + rect.width*j;
|
||||
*currentPixelAdress = KDColorBlend(*currentPixelAdress, color, *currentMaskAddress);
|
||||
}
|
||||
}
|
||||
KDCurrentContext->io.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 KDFillRect(KDRect rect, const KDColor * pattern, size_t patternSize) {
|
||||
assert(patternSize >= 1);
|
||||
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);
|
||||
|
||||
void (*fillRectFunction)(KDCoordinate x, KDCoordinate y,
|
||||
KDCoordinate width, KDCoordinate height,
|
||||
KDColor * pattern, size_t patternSize) = KDCurrentContext->fillRect;
|
||||
if (fillRectFunction == NULL) {
|
||||
fillRectFunction = KDPerPixelFillRect;
|
||||
bool useBlending = (mask != NULL && maskSize.width > 0 && maskSize.height > 0);
|
||||
|
||||
if (!useBlending) {
|
||||
pushRect(rectToBeFilled, pattern, patternSize);
|
||||
return;
|
||||
}
|
||||
|
||||
fillRectFunction(rectToBeFilled.x, rectToBeFilled.y,
|
||||
rectToBeFilled.width, rectToBeFilled.height,
|
||||
pattern, patternSize);
|
||||
assert(KDCurrentContext->io.pullPixels != NULL);
|
||||
|
||||
KDCoordinate patternX = 0, patternY = 0, maskX = 0, maskY = 0;
|
||||
for (KDCoordinate j=0; j<rectToBeFilled.height; j++) {
|
||||
for (KDCoordinate i=0; i<rectToBeFilled.width; i++) {
|
||||
KDColor foregroundColor = pattern[patternX+patternSize.width*patternY];
|
||||
KDPoint p = {
|
||||
.x = rectToBeFilled.x + i,
|
||||
.y = rectToBeFilled.y + j
|
||||
};
|
||||
KDColor backgroundColor = KDGetAbsolutePixelDirect(p);
|
||||
uint8_t alpha = mask[maskX+maskSize.width*maskY];
|
||||
KDColor newColor = KDColorBlend(backgroundColor, foregroundColor, alpha);
|
||||
KDSetAbsolutePixelDirect(p, newColor);
|
||||
if (++patternX >= 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;
|
||||
|
||||
@@ -4,3 +4,7 @@ liba/src/external/sqlite/mem5.o: CFLAGS += -w
|
||||
|
||||
objs += $(addprefix liba/src/, assert.o errno.o malloc.o memcpy.o memset.o powf.o strcmp.o strlen.o external/sqlite/mem5.o)
|
||||
tests += $(addprefix liba/test/, stdint.c)
|
||||
|
||||
# The use of aeabi-rt could be made conditional to an AEABI target.
|
||||
# In practice we're always using liba on such a target.
|
||||
objs += $(addprefix liba/src/aeabi-rt/, memcpy.o)
|
||||
|
||||
16
liba/src/aeabi-rt/README.txt
Normal file
16
liba/src/aeabi-rt/README.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
We're using the ARM Embedded ABI (AEABI) to build our code for the Cortex-M.
|
||||
That ABI specifies a lot of things: how procedures are called, how ELF files
|
||||
should be structured, and last but not least, a run-time.
|
||||
|
||||
That run-time consists in a list of helper functions that the compiler is free
|
||||
to call anywhere : unaligned memory accesses, integer division functions,
|
||||
memory copying/clearing/setting, etc...
|
||||
|
||||
Since we're telling our compiler to build an AEABI binary, it may decide to use
|
||||
those symbols, and so we have to provide an implementation for them. Refer to
|
||||
the "Run-time ABI for the ARM Architecture" for a full list of functions.
|
||||
|
||||
Note that this is not formally the job of a libc implementation. Similar code is
|
||||
often shipped alongside a compiler (LLVM calls it compiler-rt, GCC libgcc). But
|
||||
since we're using so few symbols in practice, it makes sense to glue it in. Also
|
||||
some libc do also implement this (newlib, for example).
|
||||
8
liba/src/aeabi-rt/memcpy.c
Normal file
8
liba/src/aeabi-rt/memcpy.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/* See the "Run-time ABI for the ARM Architecture", Section 4.3.3 */
|
||||
|
||||
void __aeabi_memcpy(void * dest, const void * src, size_t n) {
|
||||
memcpy(dest, src, n);
|
||||
}
|
||||
Reference in New Issue
Block a user