diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index 8f4203e..d702bd3 --- a/build.sh +++ b/build.sh @@ -168,22 +168,22 @@ package() { ls -tr $LIB/* 2>/dev/null | tail -1 | grep '\.a' > /dev/null A=$? - echo Buliding source for dynamic library for sake of size and compatibility + echo Building source for dynamic library for sake of size and compatibility DYNAMIC="-fPIC" [ $A = 0 ] && clean objects build - echo Packagiang lib$LIBNAME.so + echo Packaging lib$LIBNAME.so OBJECTS=$(find $OBJ -type f) ${CC} -shared -Wl,-soname,lib$LIBNAME.so -o $LIB/lib$LIBNAME.so.$VERSION ${OBJECTS} || die clean objects fi - echo Buliding source for static library for sake of runtime speed + echo Building source for static library for sake of runtime speed DYNAMIC="" build - echo Packagiang lib$LIBNAME.a + echo Packaging lib$LIBNAME.a OBJECTS=$(find $OBJ -type f) ar rcs $LIB/lib$LIBNAME.a $OBJECTS || die ranlib $LIB/lib$LIBNAME.a || die diff --git a/example/demo1.cpp b/example/demo1.cpp index bdda2ff..4e543bd 100644 --- a/example/demo1.cpp +++ b/example/demo1.cpp @@ -19,7 +19,7 @@ Wade Ryan using namespace udd; -DisplayConfigruation d1Config; +DisplayConfiguration d1Config; DisplayST7789R d1 = DisplayST7789R(); @@ -41,7 +41,7 @@ unsigned long long currentTimeMillis() { } -void drawSine(Image image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { +void drawSine(Image &image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { bool first = true;; int lx = -1, ly = -1; double vx = 0; @@ -62,7 +62,7 @@ void drawSine(Image image, float offset, float speed, int maxX, int maxY, float } -bool demoSineWave(int frameCount, long long start, Image image) { +bool demoSineWave(int frameCount, long long start, Image &image) { long long now = currentTimeMillis(); float refVoltage = 5; @@ -148,6 +148,29 @@ void rotationDemo() { } +void imageRotationDemo(Image &img) { + Image srcImg = img.scale(0.75f, 0.75f, BILINEAR); + for (int deg = 0; deg < 360; deg += 5) { + printf("Rotate: %d degrees CW\n", deg); fflush(stdout); + d1.showImage(srcImg.rotate(deg, DEGREES), DEGREE_270); + } + for (int deg = 0; deg > -360; deg -= 5) { + printf("Rotate: %d degrees CCW\n", deg); fflush(stdout); + d1.showImage(srcImg.rotate(deg, DEGREES), DEGREE_270); + } +} + +void imageScalingDemo(Image &img) { + for (int i = 100; i > 0; i -= 10) { + float scale = i / 100.0f; + d1.showImage(img.scale(scale, scale, BILINEAR), DEGREE_270); + } + for (int i = 20; i <= 200; i += 10) { + float scale = i / 100.0f; + d1.showImage(img.scale(scale, scale, BILINEAR), DEGREE_270); + } +} + void display1Demo() { printf("demo1\n"); fflush(stdout); @@ -170,8 +193,8 @@ void display1Demo() { delay(solidsDelay); d1.clearScreen(BLACK); - d1.showImage(bmp, DEGREE_270); - delay(2000); + imageRotationDemo(bmp); + imageScalingDemo(bmp); rotationDemo(); delay(4000); @@ -189,6 +212,7 @@ void configureDisplay1() { d1Config.width = 240; d1Config.height = 320; d1Config.spiSpeed = spiSpeed; + d1Config.spiMode = 0; d1Config.CS = 21; d1Config.DC = 22; @@ -215,14 +239,6 @@ int main(int argc, char **argv) digitalWrite(10, HIGH); digitalWrite(11, HIGH); - int demos = 3; - pthread_t threads[demos]; - char message[demos][256]; - - for (int i = 0; i < demos; ++i) { - sprintf(message[i], "demo thread %d", i); - } - configureDisplay1(); printf("press control-c to quit\n"); diff --git a/example/demo2.cpp b/example/demo2.cpp index 5a53314..d0cc924 100644 --- a/example/demo2.cpp +++ b/example/demo2.cpp @@ -20,8 +20,8 @@ Wade Ryan using namespace udd; -DisplayConfigruation d1Config; -DisplayConfigruation d2Config; +DisplayConfiguration d1Config; +DisplayConfiguration d2Config; DisplayST7789R d1 = DisplayST7789R(); DisplayST7735R d2 = DisplayST7735R(); @@ -42,7 +42,7 @@ unsigned long long currentTimeMillis() { } -void drawSine(Image image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { +void drawSine(Image &image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { bool first = true;; int lx = -1, ly = -1; double vx = 0; @@ -63,7 +63,7 @@ void drawSine(Image image, float offset, float speed, int maxX, int maxY, float } -bool demoSineWave(int frameCount, long long start, Image image) { +bool demoSineWave(int frameCount, long long start, Image &image) { long long now = currentTimeMillis(); float refVoltage = 5; diff --git a/example/neopixel.cpp b/example/neopixel.cpp index 3687162..4b2f2a3 100644 --- a/example/neopixel.cpp +++ b/example/neopixel.cpp @@ -23,7 +23,7 @@ I'm using an 8x8 matrix using namespace udd; -DisplayConfigruation d1Config; +DisplayConfiguration d1Config; DisplayNeoPixel d1 = DisplayNeoPixel(); diff --git a/example/neopixel2.cpp b/example/neopixel2.cpp index bfcb1e9..b317259 100644 --- a/example/neopixel2.cpp +++ b/example/neopixel2.cpp @@ -44,7 +44,7 @@ Here is the layout: using namespace udd; -DisplayConfigruation d1Config; +DisplayConfiguration d1Config; DisplayNeoPixel d1 = DisplayNeoPixel(); @@ -73,7 +73,7 @@ unsigned long long currentTimeMillis() { } -void drawSine(Image image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { +void drawSine(Image &image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { bool first = true; int lx = -1, ly = -1; double vx = 0; @@ -94,7 +94,7 @@ void drawSine(Image image, float offset, float speed, int maxX, int maxY, float } -bool demoSineWave(int frameCount, long long start, Image image) { +bool demoSineWave(int frameCount, long long start, Image &image) { long long now = currentTimeMillis(); float refVoltage = 5; @@ -394,7 +394,7 @@ void* setBrightness(void *) { int startTime=currentTimeMillis(); for (int i=0;i<4;++i) { - float v=readVoltage(handle, i, 0); + float v=readVoltage(handle); if (v>6) { v=0; } diff --git a/example/wsePaperV2.cpp b/example/wsePaperV2.cpp index 76fb6b9..ee3a60d 100644 --- a/example/wsePaperV2.cpp +++ b/example/wsePaperV2.cpp @@ -19,7 +19,7 @@ Wade Ryan using namespace udd; -DisplayConfigruation d1Config; +DisplayConfiguration d1Config; DisplayWS_ePaper_v2 d1 = DisplayWS_ePaper_v2(); @@ -39,7 +39,7 @@ unsigned long long currentTimeMillis() { } -void drawSine(Image image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { +void drawSine(Image &image, float offset, float speed, int maxX, int maxY, float waveHeight, Color color, int width) { bool first = true;; int lx = -1, ly = -1; double vx = 0; @@ -60,7 +60,7 @@ void drawSine(Image image, float offset, float speed, int maxX, int maxY, float } -bool demoSineWave(int frameCount, long long start, Image image) { +bool demoSineWave(int frameCount, long long start, Image &image) { long long now = currentTimeMillis(); float refVoltage = 5; diff --git a/src/controller/Color.cpp b/src/controller/Color.cpp index 520b513..292b232 100644 --- a/src/controller/Color.cpp +++ b/src/controller/Color.cpp @@ -9,22 +9,22 @@ Color::Color() { color.opacity = 255; } - Color::Color(ColorType color) { - this->color.red = color.red; - this->color.green = color.green; - this->color.blue = color.blue; - this->color.opacity = color.opacity; + memcpy(&this->color, &color, sizeof(ColorType)); } - Color::Color(_byte red, _byte green, _byte blue) { - color.red = red; - color.green = green; - color.blue = blue; - color.opacity = 255; + setColor(red, green, blue, 255); +} + +Color::Color(_byte red, _byte green, _byte blue, _byte opacity) { + setColor(red, green, blue, opacity); } +Color::Color(uint32_t colorRGB24) { + setRGB24(colorRGB24); +} + Color::Color(const char* hexbytes) { char buf[3]; int intensity; @@ -68,63 +68,37 @@ Color::Color(const char* hexbytes) { } } -bool Color::equals(Color otherColor) { - if (this->color.blue != otherColor.color.blue) { - return false; - } - if (this->color.red!= otherColor.color.red) { - return false; - } - if (this->color.green != otherColor.color.green) { - return false; - } - return true; +bool Color::equals(const Color &otherColor) const { + return equals(otherColor.color); } -bool Color::equals(ColorType otherColor) { - if (this->color.blue != otherColor.blue) { +bool Color::equals(const ColorType &otherColor) const { + if (color.blue != otherColor.blue) { return false; } - if (this->color.red != otherColor.red) { + if (color.red != otherColor.red) { return false; } - if (this->color.green != otherColor.green) { + if (color.green != otherColor.green) { return false; } return true; } -bool Color::equals(ColorType *otherColor) { - if (this->color.blue != otherColor->blue) { - return false; - } - if (this->color.red != otherColor->red) { - return false; - } - if (this->color.green != otherColor->green) { - return false; - } - return true; -} - - -Color::Color(_byte red, _byte green, _byte blue, _byte opacity) { - color.red = red; - color.green = green; - color.blue = blue; - color.opacity = opacity; -} - -ColorType Color::toType() { - return color; +inline void Color::setColor(_byte red, _byte green, _byte blue, _byte opacity) { + color.red = red; + color.green = green; + color.blue = blue; + color.opacity = opacity; } - -int32_t Color::rgb24() { - return (color.green<<16)+(color.red<<8)+(color.blue); +inline void Color::setRGB24(uint32_t colorRGB24) { + color.red = (_byte)(colorRGB24 & 0xFF); + color.green = (_byte)((colorRGB24 & 0xFF00) >> 8); + color.blue = (_byte)((colorRGB24 & 0xFF0000) >> 16); + color.opacity = (_byte)((colorRGB24 & 0xFF000000) >> 24); } - Color::Color(int clr) { color.opacity = 255; @@ -150,7 +124,7 @@ Color::Color(int clr) { } -void Color::print() { +void Color::print() const { printf("color::print: 0x%02x%02x%02x\n", this->color.red, this->color.blue,this->color.green); printf("color::print: 0x%02x%02x%02x\n", color.red, color.blue, color.green); @@ -164,26 +138,26 @@ void Color::print() { // Original 8 -Color BLACK = Color(0, 0, 0); -Color RED = Color(255, 0, 0); -Color GREEN = Color(0, 255, 0); -Color BLUE = Color(0, 0, 255); -Color YELLOW = Color(255, 255, 0); -Color MAGENTA = Color(255, 0, 255); -Color CYAN = Color(0, 255, 255); -Color WHITE = Color(255, 255, 255); +const Color BLACK = Color(0, 0, 0); +const Color RED = Color(255, 0, 0); +const Color GREEN = Color(0, 255, 0); +const Color BLUE = Color(0, 0, 255); +const Color YELLOW = Color(255, 255, 0); +const Color MAGENTA = Color(255, 0, 255); +const Color CYAN = Color(0, 255, 255); +const Color WHITE = Color(255, 255, 255); // extended colors -Color GRAY = Color("#808080"); -Color BROWN = Color(165, 42, 42); -Color ORANGE = Color(255, 128, 0); +const Color GRAY = Color("#808080"); +const Color BROWN = Color(165, 42, 42); +const Color ORANGE = Color(255, 128, 0); -Color DARK_RED = Color(128, 0, 0); -Color DARK_GREEN = Color(0, 128, 0); -Color DARK_BLUE = Color(0, 0, 128); +const Color DARK_RED = Color(128, 0, 0); +const Color DARK_GREEN = Color(0, 128, 0); +const Color DARK_BLUE = Color(0, 0, 128); -Color LIGHT_BLUE = Color(204, 228, 255); -Color LIGHT_GRAY = Color("#E0E0E0"); +const Color LIGHT_BLUE = Color(204, 228, 255); +const Color LIGHT_GRAY = Color("#E0E0E0"); -Color DARK_GRAY_BLUE = Color("003366"); \ No newline at end of file +const Color DARK_GRAY_BLUE = Color("003366"); \ No newline at end of file diff --git a/src/controller/Color.h b/src/controller/Color.h index b6e3f03..401301f 100644 --- a/src/controller/Color.h +++ b/src/controller/Color.h @@ -12,6 +12,9 @@ struct ColorStruct { typedef ColorStruct ColorType; +inline uint32_t getRGB24(const ColorType *color) { + return (color->opacity << 24 ) | (color->blue << 16) | (color->green << 8) | color->red; +} class Color { public: @@ -19,42 +22,44 @@ class Color { Color(); Color(ColorType color); - Color(_byte red, _byte blue, _byte green); + Color(_byte red, _byte green, _byte blue); + Color(_byte red, _byte green, _byte blue, _byte opacity); + Color(uint32_t colorRGB24); Color(const char *hexbytes); - Color(_byte red, _byte blue, _byte green, _byte opacity); - + Color(int pos); - ColorType toType(); - int32_t rgb24(); - void print(); - bool equals(Color otherColor); - bool equals(ColorType otherColor); - bool equals(ColorType *otherColor); + inline ColorType toType() const { return color; } + inline uint32_t rgb24() const { return getRGB24(&color); } + inline void setColor(_byte red, _byte green, _byte blue, _byte opacity); + inline void setRGB24(uint32_t colorRGB24); + void print() const; + bool equals(const Color &otherColor) const; + bool equals(const ColorType &otherColor) const; }; // original 8 -extern Color BLACK; -extern Color RED; -extern Color GREEN; -extern Color BLUE; -extern Color YELLOW; -extern Color MAGENTA; -extern Color CYAN; -extern Color WHITE; - -extern Color GRAY; -extern Color BROWN; -extern Color ORANGE; - -extern Color LIGHT_BLUE; -extern Color LIGHT_GRAY; - -extern Color DARK_BLUE; -extern Color DARK_RED; -extern Color DARK_GREEN; - -extern Color DARK_GRAY_BLUE; +extern const Color BLACK; +extern const Color RED; +extern const Color GREEN; +extern const Color BLUE; +extern const Color YELLOW; +extern const Color MAGENTA; +extern const Color CYAN; +extern const Color WHITE; + +extern const Color GRAY; +extern const Color BROWN; +extern const Color ORANGE; + +extern const Color LIGHT_BLUE; +extern const Color LIGHT_GRAY; + +extern const Color DARK_BLUE; +extern const Color DARK_RED; +extern const Color DARK_GREEN; + +extern const Color DARK_GRAY_BLUE; diff --git a/src/controller/Display.cpp b/src/controller/Display.cpp index b0b9148..724eb27 100644 --- a/src/controller/Display.cpp +++ b/src/controller/Display.cpp @@ -43,7 +43,7 @@ namespace udd { - void Display::openDisplay(DisplayConfigruation configuration) { + void Display::openDisplay(DisplayConfiguration configuration) { this->config = configuration; // this->vImage = Image(config.width, config.height, BLACK); @@ -138,7 +138,7 @@ namespace udd { } } - void Display::clearWindow(Color color, Point p1, Point p2, Rotation rotation) { + void Display::clearWindow(const Color &color, Point p1, Point p2, Rotation rotation) { screenLock.lock(); openSPI(); @@ -168,7 +168,7 @@ namespace udd { screenLock.unlock(); } - void Display::clearScreen(Color color) { + void Display::clearScreen(const Color &color) { screenLock.lock(); openSPI(); resume(); @@ -204,11 +204,11 @@ namespace udd { } - void Display::showImage(Image& image) { + void Display::showImage(const Image& image) { showImage(image, Point(0,0), Point(config.width-1, config.height-1), DEGREE_0); } - void Display::showImage(Image& image, Rotation rotation) { + void Display::showImage(const Image& image, Rotation rotation) { Point p1 = Point(0, 0); Point p2 = Point(config.width-1, config.height-1); @@ -218,7 +218,7 @@ namespace udd { showImage(image, p1, p2, rotation); } - void Display::showImage(Image& image, Point p1, Point p2, Rotation rotation) { + void Display::showImage(const Image& image, Point p1, Point p2, Rotation rotation) { int width, height; screenLock.lock(); openSPI(); @@ -232,8 +232,8 @@ namespace udd { if (image.getHeight() != height || image.getWidth() != width) { - fprintf(stderr, "Image size does not match window size. Height: [Image=%d, Window=%d] Width: [Image=%d, Window=%d]\n", - image.getHeight(), height, image.getWidth(), width); + fprintf(stderr, "Image size does not match window size. Image: (%dx%d) Window: (%dx%d)\n", + image.getWidth(), image.getHeight(), width, height); } diff --git a/src/controller/Display.h b/src/controller/Display.h index a229056..531298e 100644 --- a/src/controller/Display.h +++ b/src/controller/Display.h @@ -50,7 +50,7 @@ namespace udd { int brightness; }; - typedef DisplayConfigurationStruct DisplayConfigruation; + typedef DisplayConfigurationStruct DisplayConfiguration; Rotation validateRotation(char* rotation); @@ -67,20 +67,20 @@ namespace udd { virtual void setWindow(Point p1, Point p2, Rotation rotation); public: - DisplayConfigruation config; + DisplayConfiguration config; virtual void init(); - void openDisplay(DisplayConfigruation configuratrion); + void openDisplay(DisplayConfiguration configuratrion); - virtual void clearScreen(Color color); - virtual void clearWindow(Color color, Point p1, Point p2, Rotation rotation); + virtual void clearScreen(const Color &color); + virtual void clearWindow(const Color &color, Point p1, Point p2, Rotation rotation); - virtual void showImage(Image& image); - virtual void showImage(Image& image, Rotation rotation); - virtual void showImage(Image& image, Point p1, Point p2, Rotation rotation); + virtual void showImage(const Image& image); + virtual void showImage(const Image& image, Rotation rotation); + virtual void showImage(const Image& image, Point p1, Point p2, Rotation rotation); virtual void readBusy() { fprintf(stderr, "readBusy() is not implemented for this method\n"); diff --git a/src/controller/Image.cpp b/src/controller/Image.cpp index d5e5614..10c69b2 100644 --- a/src/controller/Image.cpp +++ b/src/controller/Image.cpp @@ -1,42 +1,108 @@ #include "Image.h" #include "fonts.h" #include +#include +#include #include "GUI_BMPfile.h" using namespace udd; -Image::Image() { - width=0; - height=0; - backgroundColor=BLACK; +#undef max // in case any sys header defines it +#undef min // in case any sys header defines it +inline float min(float a, float b) { return (a < b) ? a : b; }; +inline float max(float a, float b) { return (a < b) ? b : a; }; +inline float lerp(float s, float e, float t) {return s+(e-s)*t;} +inline float blerp(float c00, float c10, float c01, float c11, float tx, float ty) { + return lerp(lerp(c00, c10, tx), lerp(c01, c11, tx), ty); +} +inline bool floatsAreEqual(float f1, float f2, float epsilon) { + return fabsf(f1 - f2) < epsilon; +} +inline bool floatsAreEqual(float f1, float f2) { + return floatsAreEqual(f1, f2, std::numeric_limits::epsilon()); +} +const static float RADIANS_EPSILON = 1e-5; +inline bool radiansAreEqual(float f1, float f2) { + return floatsAreEqual(f1, f2, RADIANS_EPSILON); } +#define getByte(value, n) (value >> (n*8) & 0xFF) +// Default c'tor +Image::Image() : Image(0, 0, BLACK) { +} -Image::Image(int width, int height, Color backgroundColor) { - this->width = width; - this->height = height; +// Copy c'tor +Image::Image(const Image &img) { + copy(img); +} - this->backgroundColor = backgroundColor; +// Move c'tor +Image::Image(Image &&img) + : canvas(img.canvas) + , width(img.width) + , height(img.height) + , backgroundColor(std::move(img.backgroundColor)) { + img.canvas = NULL; // We are 'moving' canvas data from the rvalue-ref temp 'img' +} - uint32_t imageSize = width * height; - uint32_t memorySize = imageSize * sizeof(ColorType); +// (Copy) Assignment operator +Image& Image::operator=(const Image &img) { + close(); // free up any existing memory + copy(img); + return *this; +} - canvas = (ColorType*)malloc(memorySize); +// (Move) Assignment operator +Image& Image::operator=(Image &&img) { + canvas = img.canvas; + img.canvas = NULL; // We are 'moving' canvas data from the rvalue-ref temp 'img' + width = img.width; + height = img.height; + backgroundColor = std::move(img.backgroundColor); + return *this; +} +Image::Image(int width, int height, const Color &backgroundColor) + : width(width) + , height(height) + , backgroundColor(backgroundColor) { + init(); clear(backgroundColor); } -int udd::Image::getWidth() { +// Private initializer helper +void Image::init() { + uint32_t imageSize = width * height; + canvas = NULL; + if (imageSize > 0) { + canvas = new ColorType[imageSize]; + } +} + +void Image::copy(const Image &img) { + width = img.width; + height = img.height; + backgroundColor = img.backgroundColor; + init(); + memcpy(canvas, img.canvas, width * height * sizeof(ColorType)); +} + +// D'tor +Image::~Image() { + close(); +} + +int udd::Image::getWidth() const { return width; } -int udd::Image::getHeight() { +int udd::Image::getHeight() const { return height; } -void Image::clear(Color backgroundColor) { +void Image::clear(const Color &backgroundColor) { for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { ColorType* xp = getPixelColor(x, y); @@ -49,10 +115,17 @@ void Image::clear(Color backgroundColor) { } void Image::close() { - free(canvas); + if (canvas) { + delete[] canvas; + canvas = NULL; + } } -void Image::drawPixel(int x, int y, Color color) { +void Image::drawPixel(int x, int y, const Color &color) { + drawPixel(x, y, color.color); +} + +void Image::drawPixel(int x, int y, const ColorType &color) { if (x < 0) return; if (y < 0) return; if (x >= width) return; @@ -60,21 +133,12 @@ void Image::drawPixel(int x, int y, Color color) { ColorType* xp = getPixelColor(x, y); - xp->red = color.color.red; - xp->green = color.color.green; - xp->blue = color.color.blue; - xp->opacity = color.color.opacity; - - - if (color.color.red != 0 || - color.color.green != 0 || - color.color.blue != 0 - ) { - } + xp->red = color.red; + xp->green = color.green; + xp->blue = color.blue; + xp->opacity = color.opacity; } - - void Image::printPixel(int x, int y) { ColorType* xp = getPixelColor(x, y); @@ -86,20 +150,19 @@ void Image::printPixel(int x, int y) { } } -ColorType* Image::getPixelColor(int x, int y) { +ColorType* Image::getPixelColor(int x, int y) const { if (x<0 || x >= width || y<0 || y>=height) { return NULL; } long offset = (y * width) + x; - ColorType* xp = canvas + offset; - return xp; + return &canvas[offset]; } -ColorType* Image::getPixel(int x, int y, Rotation rotation) { +ColorType* Image::getPixel(int x, int y, Rotation rotation) const { switch (rotation) { case DEGREE_0: return getPixelColor(x, y); case DEGREE_90: return getPixelColor(y, height-x-1); @@ -117,7 +180,7 @@ _word Image::color2word(ColorType* xp) { return ((0x1f & (xp->blue)) << 11) | ((0x3f & (xp->red)) << 5) | ((0x1f & (xp->green))); } -void Image::drawPoint(int x, int y, Color color, int width) { +void Image::drawPoint(int x, int y, const Color &color, int width) { switch (width) { case 0: @@ -147,7 +210,7 @@ void Image::drawPoint(int x, int y, Color color, int width) { } -void Image::drawLine(int x1, int y1, int x2, int y2, Color color, LineStyle style, int width) { +void Image::drawLine(int x1, int y1, int x2, int y2, const Color &color, LineStyle style, int width) { float x, y, dx, dy; int i, longestLeg, rx, ry; @@ -187,7 +250,7 @@ void Image::drawLine(int x1, int y1, int x2, int y2, Color color, LineStyle styl void Image::drawText(int Xstart, int Ystart, const char* pString, - sFONT* Font, Color background, Color foreground) { + sFONT* Font, const Color &background, const Color &foreground) { int Xpoint = Xstart; @@ -217,7 +280,7 @@ void Image::drawText(int Xstart, int Ystart, const char* pString, } void Image::drawChar(int Xpoint, int Ypoint, const char Acsii_Char, - sFONT* Font, Color background, Color foreground) { + sFONT* Font, const Color &background, const Color &foreground) { int Page, Column; @@ -359,19 +422,19 @@ void Image::loadBMP(FILE *fp, int Xstart, int Ystart) { } -void Image::drawRectangle(int x1, int y1, int x2, int y2, Color Color, +void Image::drawRectangle(int x1, int y1, int x2, int y2, const Color &color, FillPattern pattern, LineStyle lineStyle, int width) { switch (pattern) { case NONE: - drawLine(x1, y1, x2, y1, Color, lineStyle, width); - drawLine(x1, y1, x1, y2, Color, lineStyle, width); - drawLine(x2, y2, x2, y1, Color, lineStyle, width); - drawLine(x2, y2, x1, y2, Color, lineStyle, width); + drawLine(x1, y1, x2, y1, color, lineStyle, width); + drawLine(x1, y1, x1, y2, color, lineStyle, width); + drawLine(x2, y2, x2, y1, color, lineStyle, width); + drawLine(x2, y2, x1, y2, color, lineStyle, width); break; case FILL: for (int y = y1; y < y2; y++) { - drawLine(x1, y, x2, y, Color, lineStyle, width); + drawLine(x1, y, x2, y, color, lineStyle, width); } break; case MASK: @@ -395,7 +458,7 @@ void Image::arcPoint(int x, int y, int radius, double degree, int* xPoint, int* *yPoint = iy; } -void Image::drawCircle(int x, int y, int radius, Color color, FillPattern pattern, LineStyle lineStyle, int width) { +void Image::drawCircle(int x, int y, int radius, const Color &color, FillPattern pattern, LineStyle lineStyle, int width) { double xPoint, yPoint; @@ -427,7 +490,7 @@ void Image::drawCircle(int x, int y, int radius, Color color, FillPattern patter } } -void Image::drawLineArc(int x, int y, int length, float degree, Color color, LineStyle style, int width) { +void Image::drawLineArc(int x, int y, int length, float degree, const Color &color, LineStyle style, int width) { double xPoint, yPoint; @@ -437,7 +500,7 @@ void Image::drawLineArc(int x, int y, int length, float degree, Color color, Lin drawLine(x, y, x + round(xPoint), y + round(yPoint), color, style, width); } -void Image::drawPieSlice(int x, int y, int radius, float degree1, float degree2, Color color, LineStyle style, int width) { +void Image::drawPieSlice(int x, int y, int radius, float degree1, float degree2, const Color &color, LineStyle style, int width) { double xPoint, yPoint; @@ -468,7 +531,179 @@ void Image::drawPieSlice(int x, int y, int radius, float degree1, float degree2, } } } +} + +Image Image::scale(float scaleX, float scaleY, ScaleMode mode) { + if (floatsAreEqual(scaleX, 1.0f) && floatsAreEqual(scaleY, 1.0f)) { + // 100% scale. Optimize by copy + return Image(*this); + } + int newWidth = ceilf(width * scaleX); + int newHeight = ceilf(height * scaleY); + Image newImage(newWidth, newHeight, backgroundColor); + switch (mode) { + case BILINEAR: + Image::scaleBilinear(*this, newImage); + break; + default: + fprintf(stderr, "not yet implemented (mode=%d)\n", mode); + } + return newImage; +} + +void Image::scaleBilinear(const Image &src, Image &dst) { + for (int y = 0; y < dst.height; y++) { + for (int x = 0; x < dst.width; x++) { + float gx = min(x / (float)(dst.width) * (src.width) - 0.5f, src.width - 1); + float gy = min(y / (float)(dst.height) * (src.height) - 0.5f, src.height - 1); + int gxi = (int)gx, gyi = (int)gy; + int gxi1 = min(gxi+1, src.width - 1); + int gyi1 = min(gyi+1, src.height - 1); + uint32_t result=0; + uint32_t c00 = getRGB24(src.getPixelColor(gxi, gyi)); + uint32_t c10 = getRGB24(src.getPixelColor(gxi1, gyi)); + uint32_t c01 = getRGB24(src.getPixelColor(gxi, gyi1)); + uint32_t c11 = getRGB24(src.getPixelColor(gxi1, gyi1)); + for(uint8_t i = 0; i < 3; i++){ + result |= (uint8_t)blerp(getByte(c00, i), getByte(c10, i), getByte(c01, i), getByte(c11, i), gx - gxi, gy -gyi) << (8*i); + } + dst.drawPixel(x,y, Color(result)); + } + } +} +Image Image::rotate(float angle, AngleUnit units) { + if (DEGREES == units) { + angle *= PI / 180.0f; + } + angle = -fmodf(angle, 2.0f*PI); // -2*PI <= angle <= 2*PI + if (angle < -PI) { + angle += 2.0f*PI; + } else if (angle > PI) { + angle -= 2.0f*PI; + } + // Now: -PI <= angle <= PI + // Some cases can be optimized when multiples of 90 degrees + // so that we don't need to do any trigonometry operations + udd::Rotation rotation = DEGREE_INVALID; + if (radiansAreEqual(angle, 0.0f)) { + rotation = DEGREE_0; + } else if (radiansAreEqual(angle, PI/2.0f)) { + rotation = DEGREE_90; + } else if (radiansAreEqual(fabsf(angle), PI)) { + rotation = DEGREE_180; + } else if (radiansAreEqual(angle, PI/-2.0f)) { + rotation = DEGREE_270; + } + if (DEGREE_INVALID != rotation) { + return simpleRotate(rotation); + } + // Three shear rotate is effective in the range -PI/2 < angle < PI/2 + // and gives poor results close to +/- PI + // For angles outside this range, we optimize using a two-stage rotate + // using a simpleRotate() as the first step. It will use more memory + // and be slightly slower but will give superior results. + if (angle < -PI/2.0f) { + return simpleRotate(DEGREE_270).threeShearRotate(angle + PI/2.0f); + } + if (angle > PI/2.0f) { + return simpleRotate(DEGREE_90).threeShearRotate(angle - PI/2.0f); + } + return threeShearRotate(angle); +} +Image Image::simpleRotate(udd::Rotation rotation) { + int newWidth = width; + int newHeight = height; + switch (rotation) { + case DEGREE_INVALID: + fprintf(stderr, "Not supported simpleRotate(DEGREE_INVALID)"); + // fallthrough + case DEGREE_0: + // No rotation, optimize by copy + return Image(*this); + case DEGREE_90: + case DEGREE_270: + newWidth = height; + newHeight = width; + break; + default: + break; + } + Image newImage(newWidth, newHeight, backgroundColor); + + // Find the centres of the original and new images + int centreX = width / 2; + int centreY = height / 2; + int newCentreX = newWidth / 2; + int newCentreY = newHeight / 2; + + ColorType *destPixel = NULL; + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + // map coords w.r.t centre + int relX = x - centreX; + int relY = height - y - centreY; + int newX = relX, newY = relY; + switch (rotation) { + default: + case DEGREE_0: break; + case DEGREE_90: newX = -relY; newY = relX; break; + case DEGREE_180: newX = -relX; newY = -relY; break; + case DEGREE_270: newX = relY; newY = -relX; break; + } + // Convert back from centre to new img origin coords + newX = max(0, min(newCentreX + newX, newWidth - 1)); + newY = max(0, min(newHeight - (newCentreY + newY), newHeight - 1)); + destPixel = newImage.getPixelColor(newX, newY); + if (destPixel) { + memcpy(destPixel, getPixelColor(x, y), sizeof(ColorType)); + } + } + } + return newImage; +} -} \ No newline at end of file +Image Image::threeShearRotate(float angleRads) { + // Pre-calc some trig ops we will need more than once + float sine = sinf(angleRads); + float cosine = cosf(angleRads); + float shearTangent = tanf(angleRads/2.0f); + + // Create a new image at the correct size to hold the rotated image + int newWidth = roundf(abs(width * cosine) + abs(height * sine)); + int newHeight = roundf(abs(height * cosine) + abs(width * sine)); + + // Find the centres of the original and new images + int centreX = width / 2; + int centreY = height / 2; + int newCentreX = newWidth / 2; + int newCentreY = newHeight / 2; + + // Perform the three-shear rotation using coords w.r.t. centres + // https://datagenetics.com/blog/august32013/index.html + Image img(newWidth, newHeight, backgroundColor); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // map coords w.r.t centre + int newX = x - centreX; + int newY = height - y - centreY; + // Shear 1 + newX = roundf(newX - newY * shearTangent); + // Shear 2 + newY = roundf(newX * sine + newY); + // Shear 3 + newX = roundf(newX - newY * shearTangent); + // Convert back from centre to img origin coords + newX = max(0, min(newCentreX + newX, newWidth -1)); + newY = max(0, min(newHeight - (newCentreY + newY), newHeight - 1)); + // Write pixel into new image + ColorType *srcPixel = getPixelColor(x, y); + ColorType *destPixel = &img.canvas[newY * newWidth + newX]; + if (destPixel) { + memcpy(destPixel, srcPixel, sizeof(ColorType)); + } + } + } + return img; +} diff --git a/src/controller/Image.h b/src/controller/Image.h index f4b384c..40d2a99 100644 --- a/src/controller/Image.h +++ b/src/controller/Image.h @@ -4,7 +4,7 @@ #include #include -#include +#include "Color.h" #include "inttypes.h" #include "fonts.h" @@ -17,43 +17,57 @@ namespace udd { class Image { public: Image(); + Image(const Image &img); + Image(Image &&img); + Image& operator=(const Image &img); + Image& operator=(Image &&img); + virtual ~Image(); - Image(int width, int height, Color backgroundColor); + Image(int width, int height, const Color &backgroundColor); - int getWidth(); - int getHeight(); + int getWidth() const; + int getHeight() const; - void clear(Color backgroundColor); + void clear(const Color &backgroundColor); void close(); - void drawPixel(int x, int y, Color color); + void drawPixel(int x, int y, const Color &color); + void drawPixel(int x, int y, const ColorType &color); - void drawLine(int x1, int y1, int x2, int y2, Color color, LineStyle style, int width); - void drawLineArc(int x, int y, int radius, float degree, Color color, LineStyle style, int width); + void drawLine(int x1, int y1, int x2, int y2, const Color &color, LineStyle style, int width); + void drawLineArc(int x, int y, int radius, float degree, const Color &color, LineStyle style, int width); - void drawPoint(int x, int y, Color color, int width); + void drawPoint(int x, int y, const Color &color, int width); void drawText(int Xstart, int Ystart, const char* pString, - sFONT* Font, Color background, Color forground); + sFONT* Font, const Color &background, const Color &forground); - void drawChar(int Xpoint, int Ypoint, const char Acsii_Char, sFONT* Font, Color Color_Background, Color Color_Foreground); + void drawChar(int Xpoint, int Ypoint, const char Acsii_Char, sFONT* Font, const Color &Color_Background, const Color &Color_Foreground); void loadBMP(FILE* file, int Xstart, int Ystart); void loadBMP(const char* filename, int Xstart, int Ystart); - void drawRectangle(int x1, int y1, int x2, int y2, Color Color, FillPattern pattern, LineStyle lineStyle, int width); + void drawRectangle(int x1, int y1, int x2, int y2, const Color &Color, FillPattern pattern, LineStyle lineStyle, int width); void arcPoint(int x, int y, int length, double degree, int* xPoint, int* yPoint); - void drawCircle(int x, int y, int radius, Color Color, FillPattern pattern, LineStyle lineStyle, int width); - void drawPieSlice(int x, int y, int radius, float degree1, float degree2, Color color, LineStyle style, int width); + void drawCircle(int x, int y, int radius, const Color &Color, FillPattern pattern, LineStyle lineStyle, int width); + void drawPieSlice(int x, int y, int radius, float degree1, float degree2, const Color &color, LineStyle style, int width); void printPixel(int x, int y); - ColorType* getPixel(int x, int y, udd::Rotation rotation); + ColorType* getPixel(int x, int y, udd::Rotation rotation) const; - ColorType* getPixelColor(int x, int y); + ColorType* getPixelColor(int x, int y) const; + + Image scale(float scaleX, float scaleY, ScaleMode mode); + Image rotate(float angle, AngleUnit units); + + private: + static void scaleBilinear(const Image &src, Image &dst); + Image simpleRotate(udd::Rotation rotation); + Image threeShearRotate(float angleRads); private: @@ -64,6 +78,7 @@ namespace udd { Color backgroundColor; _word color2word(ColorType* xp); - + void init(); + void copy(const Image& img); }; } diff --git a/src/controller/Metadata.h b/src/controller/Metadata.h index 95853dc..d15563c 100644 --- a/src/controller/Metadata.h +++ b/src/controller/Metadata.h @@ -36,6 +36,13 @@ namespace udd { MASK } FillPattern; + typedef enum { + BILINEAR + } ScaleMode; + typedef enum { + DEGREES, + RADIANS + } AngleUnit; } diff --git a/src/displays/DisplayNeoPixel.cpp b/src/displays/DisplayNeoPixel.cpp index 87fcc1a..1cee64c 100644 --- a/src/displays/DisplayNeoPixel.cpp +++ b/src/displays/DisplayNeoPixel.cpp @@ -5,7 +5,7 @@ namespace udd { DisplayNeoPixel::DisplayNeoPixel() : Display() {} - void DisplayNeoPixel::openDisplay(DisplayConfigruation configuration) { + void DisplayNeoPixel::openDisplay(DisplayConfiguration configuration) { this->config = configuration; this->vImage = Image(config.width, config.height, BLACK); @@ -46,7 +46,7 @@ namespace udd { this->vImage.drawPoint(pixel.point.x, pixel.point.y, pixel.color, 1); } - void DisplayNeoPixel::clearScreen(Color color) { + void DisplayNeoPixel::clearScreen(const Color &color) { for (int x=0;x points); - void clearScreen(Color color); + void clearScreen(const Color &color); - void showImage(Image image, Rotation rotation, ScreenMirror mirror); - void showImage(Image image, Rotation rotation); - void showImage(Image image); + void showImage(const Image &image, Rotation rotation, ScreenMirror mirror); + void showImage(const Image &image, Rotation rotation); + void showImage(const Image &image); void setPixel(Pixel pixel); private: diff --git a/src/displays/DisplayWS_ePaper_v2.cpp b/src/displays/DisplayWS_ePaper_v2.cpp index 2b9af48..4825429 100644 --- a/src/displays/DisplayWS_ePaper_v2.cpp +++ b/src/displays/DisplayWS_ePaper_v2.cpp @@ -96,7 +96,7 @@ namespace udd { } - void DisplayWS_ePaper_v2::clearScreen(Color color) { + void DisplayWS_ePaper_v2::clearScreen(const Color &color) { screenLock.lock(); openSPI(); @@ -167,16 +167,16 @@ namespace udd { } } - void DisplayWS_ePaper_v2::showImage(Image& image) { + void DisplayWS_ePaper_v2::showImage(const Image& image) { Display::showImage(image); } - void DisplayWS_ePaper_v2::showImage(Image& image, Rotation rotation) { + void DisplayWS_ePaper_v2::showImage(const Image& image, Rotation rotation) { Display::showImage(image, rotation); } - void DisplayWS_ePaper_v2::showImage(Image &image, Point p1, Point p2, Rotation rotation) { + void DisplayWS_ePaper_v2::showImage(const Image &image, Point p1, Point p2, Rotation rotation) { fprintf(stderr, "ePaper showImage(%d,%d)\n", config.width, config.height); @@ -198,13 +198,13 @@ namespace udd { ColorType* ct = image.getPixel(x - config.xOffset, y - config.yOffset, rotation); int val=1; if (ct != NULL) { - if (WHITE.equals(ct)) { + if (WHITE.equals(*ct)) { val=1; } - else if (BLACK.equals(ct)) { + else if (BLACK.equals(*ct)) { val=0; } - else if (RED.equals(ct)) { + else if (RED.equals(*ct)) { val=1; } else { fprintf(stderr, "invalid color found at (%d,%d)\n", x, y); @@ -232,13 +232,13 @@ namespace udd { ColorType* ct = image.getPixel(x - config.xOffset, y - config.yOffset, rotation); int val=1; if (ct != NULL) { - if (WHITE.equals(ct)) { + if (WHITE.equals(*ct)) { val=1; } - else if (BLACK.equals(ct)) { + else if (BLACK.equals(*ct)) { val=1; } - else if (RED.equals(ct)) { + else if (RED.equals(*ct)) { val=0; } else { fprintf(stderr, "invalid color found at (%d,%d)\n", x, y); diff --git a/src/displays/DisplayWS_ePaper_v2.h b/src/displays/DisplayWS_ePaper_v2.h index 2aa1b17..e6f690f 100644 --- a/src/displays/DisplayWS_ePaper_v2.h +++ b/src/displays/DisplayWS_ePaper_v2.h @@ -16,10 +16,10 @@ namespace udd { void initPartial(); void reset() override; void readBusy() override; - void clearScreen(Color color) override; - void showImage(Image& image) override; - void showImage(Image& image, Rotation rotation) override; - void showImage(Image& image, Point p1, Point p2, Rotation rotation) override; + void clearScreen(const Color &color) override; + void showImage(const Image& image) override; + void showImage(const Image& image, Rotation rotation) override; + void showImage(const Image& image, Point p1, Point p2, Rotation rotation) override; void EPD_SetFullReg(); void setPartReg(); void enableDisplay();