Teensy 3.6 plays video from SD-Card

Status
Not open for further replies.
Yeah, I paid a total of about $15 for my first one. Later, I picked up a couple more reasonably by adding them to orders for other stuff.

best,
Michael
 
@frank B

i got some example code for slightly more advanced graphical features. (including some AA shapes and somealpha blending stuff)

the performance on my teensy 3.2 is rather meh (especially when doing alpha blending for a complete screen of 320*240 @96MHZ), i think because the t3.2 doesn't have an FPU (lot's of FPU calculations going on in my code)

currently only for my pix-watch project (of which the code will be open source), but since that's on hold right now, i can help with graphical features and make that part open source already.

i'm also working on a AA font system for it. :)

if interested, would you like to include it in the library?

only request if you'll end up using it: please put myname (Ramon Schepers) on the github page.

NOW, please be aware of a messy chunk of code that can be slow, but does it's job as described.
(it is currently configured for my ili9163c display with framebuffer, so mods are needed to make it work)


Code:
#include <TFT_ILI9163C.h>
/////////////////////////////font data///////////////////////////////
const char standard_font_data[720]=  {
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, // (start)
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, // (end)

0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //a (start)
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,5 ,10,10,10,10,10,3 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,3 ,10,0 ,0, //
0 ,6 ,10,10,10,10,10,10,0 ,0, //
0 ,10,3 ,0 ,0 ,0 ,3 ,10,0 ,0, //
0 ,10,3 ,0 ,0 ,0 ,3 ,10,0 ,0, //
0 ,5 ,10,10,10,10,10,6 ,8 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //a (end)

0 ,8 ,10,0 ,0 ,0 ,0 ,0 ,0 ,0, //b (start)
0 ,0 ,10,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,10,3 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,10,10,10,10,10,3 ,0 ,0, //
0 ,0 ,10,6 ,0 ,0 ,3 ,10,0 ,0, //
0 ,0 ,10,0 ,0 ,0 ,0 ,10,0 ,0, //
0 ,0 ,10,0 ,0 ,0 ,0 ,10,0 ,0, //
0 ,0 ,10,5 ,0 ,0 ,3 ,10,0 ,0, //
0 ,8 ,10,10,10,10,10,3 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //b (end)

0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //c (start)
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,6 ,10,10,10,10,5 ,0 ,0, //
0 ,0 ,10,3 ,0 ,0 ,3 ,10,0 ,0, //
0 ,0 ,10,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,10,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,10,3 ,0 ,0 ,3 ,10,0 ,0, //
0 ,0 ,6 ,10,10,10,10,5 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //c (end)

0 ,0 ,0 ,0 ,0 ,0 ,1 ,10,3 ,0, //d (start)
0 ,0 ,0 ,0 ,0 ,0 ,0 ,10,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,1 ,10,0 ,0, //
0 ,0 ,6 ,10,10,10,10,10,0 ,0, //
0 ,0 ,10,3 ,0 ,0 ,3 ,10,0 ,0, //
0 ,0 ,10,0 ,0 ,0 ,0 ,10,0 ,0, //
0 ,0 ,10,0 ,0 ,0 ,0 ,10,0 ,0, //
0 ,0 ,10,3 ,0 ,0 ,3 ,10,0 ,0, //
0 ,0 ,6 ,10,10,10,10,6 ,8 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //d (end)

0 ,0 ,0 ,1 ,6 ,6 ,1 ,0 ,0 ,0, // ! (start)
0 ,0 ,0 ,5 ,10,10,5 ,0 ,0 ,0, //
0 ,0 ,0 ,4 ,10,10,4 ,0 ,0 ,0, //
0 ,0 ,0 ,3 ,10,10,3 ,0 ,0 ,0, //
0 ,0 ,0 ,1 ,10,10,1 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,10,10,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,8 ,8 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,2 ,2 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,10,10,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,4 ,4 ,0 ,0 ,0 ,0, //
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0, // ! (end)

};



struct {
	const uint8_t font_id = 0; //default
	const uint8_t font_width = 10;
	const uint8_t font_height = 12;
	const char *data = standard_font_data;
} standard_font_AA;




//////////////////////////////GPU data/////////////////////






//*************************************************
//drawing functions
//*************************************************

class GPU : public Print {
	struct Font_Data{
		//font stuff
		uint8_t cursor_x = 0, cursor_y = 0, textsize = 1, wrap = 0;
		uint16_t textcolor = 0xffff, textbgcolor = 0xffff, fontKern = 1;
		const unsigned char * fontData = glcdFont;
		uint8_t fontWidth = pgm_read_byte(fontData+0);
		uint8_t	fontHeight = pgm_read_byte(fontData+1);
		uint8_t fontStart = pgm_read_byte(fontData+2);
		uint8_t fontLength = pgm_read_byte(fontData+3);
	} Font;


	//for font-writing
	const unsigned char char_map[6] = {' ','a','b','c','d','!'};
struct {
	uint8_t font_id = standard_font_AA.font_id; //default
	uint8_t font_width = standard_font_AA.font_width;
	uint8_t font_height = standard_font_AA.font_height;
	const char *data = standard_font_AA.data;
} current_font;
		
	
public:
	size_t write(uint8_t c) {
		if (c == '\n') {
			Cursor(Font.textsize*8,0);
		} else if (c == '\r') {
			// skip em
		} else {
			drawChar_cont(Font.cursor_x,Font.cursor_y,c,Font.textcolor,Font.textbgcolor,Font.textsize);
			Font.cursor_x += 1*(current_font.font_width);
			if (Font.wrap && (Font.cursor_x > (128 - Font.textsize*Font.fontWidth))) {
				Font.cursor_y += Font.textsize*current_font.font_height;
				Font.cursor_x = 0;
			}
		}
		return 1;
	}
	
	
	void drawChar_cont(int16_t x, int16_t y, unsigned char c,uint16_t color, uint16_t bg, uint8_t size) {
		if((x >= 127)             || // Clip right
			(y >= 127)           || // Clip bottom
			((x + 8 * size - 1) < 0) || // Clip left
			((y + 12 * size - 1) < 0))   // Clip top
			return;

		uint16_t xx = x;
		uint16_t yy = y;
		uint8_t width = current_font.font_width;
		uint8_t height = current_font.font_height;
		uint8_t charindex = 0;
		bool found = true;
		for (uint8_t i =0; i < sizeof(char_map); i++) {
			if (char_map[i] == c) {
				found = true;
				charindex = i;
			}
		}
		if (found) {
			uint16_t offset =0;
			for (uint8_t i = 0; i < height-1; i++) {
				for (uint8_t j = 0; j < width-1; j++){
					float pix;
					pix = (float)(pgm_read_byte_near(current_font.data+(charindex*(width*height))+offset))/10;
					if (pix > 1) {
						pix = 1;
					}
					draw_pixel_AA(xx+j,yy+i,color,pix);
					offset++;
				}
				offset++;
			}
		}
	}
	/*
	void drawChar_cont(int16_t x, int16_t y, unsigned char c,uint16_t color, uint16_t bg, uint8_t size) {
		if((x >= 127)             || // Clip right
			(y >= 127)           || // Clip bottom
			((x + 8 * size - 1) < 0) || // Clip left
			((y + 12 * size - 1) < 0))   // Clip top
			return;
			
		if (c < Font.fontStart || c > Font.fontStart+Font.fontLength) {
			c = 0;
		} else {
			c -= Font.fontStart;
		}
		uint16_t bitCount = 0;
		uint16_t line = 0;
		uint16_t i,j;
		int fontIndex = (c*(Font.fontWidth*Font.fontHeight)/8)+4;
		for (i=0; i<Font.fontHeight; i++ ) {
			//uint8_t line;
			for (j = 0; j<Font.fontWidth; j++) {
				if (bitCount++%8 == 0) line = pgm_read_byte(Font.fontData+fontIndex++);
				if (line & 0x80) {
					draw_pixel(x+j, y+i, color);
				} else if (bg != color) {
					draw_pixel(x+j, y+i, bg);
				}
				line <<= 1;
			}
		}
	}
	*/

	uint8_t height() {
		return s_height;
	}
	uint8_t width() {
		return s_width;
	}

	
private:
	struct Ative_Area {
		uint16_t active_x1;
		uint16_t active_y1;
		uint16_t active_x2;
		uint16_t active_y2;
		double alpha;
	} Area;	
	uint8_t s_width, s_height;
	void swap(int16_t &a, int16_t &b) { int16_t t = a; a = b; b = t; }

	TFT_ILI9163C tft = TFT_ILI9163C(PIN::TFT_CS, PIN::TFT_DC);
	
	void blend_screen(double AA) {
		if (AA > 0.2) {
			AA = 0.2;
		}
		uint16_t bgbuffer[5];
		for (int16_t y = -2; y < 130; y++) {
			for (int16_t x = -2; x < 130; x++) {
				bgbuffer[0] = get_pixel(x,y);
				bgbuffer[1] = blendColor(get_pixel(x-1,y),bgbuffer[0], AA); //left pixel
				bgbuffer[2] = blendColor(get_pixel(x+1,y),bgbuffer[0], AA); //right pixel
				bgbuffer[3] = blendColor(get_pixel(x,y-1),bgbuffer[0], AA); //top pixel
				bgbuffer[4] = blendColor(get_pixel(x,y+1),bgbuffer[0], AA); //bottom pixel
				draw_pixel(x,y,bgbuffer[0]);
				draw_pixel(x-1,y,bgbuffer[1]);
				draw_pixel(x+1,y,bgbuffer[2]);
				draw_pixel(x,y-1,bgbuffer[3]);
				draw_pixel(x,y+1,bgbuffer[4]);
			}
		}
	}
	
	 uint16_t blendColor(uint16_t bgpix, uint16_t forepix, float alpha) {
		if ( alpha < 0 ) {
			return bgpix;
		}
		if ( alpha > 1) {
			return forepix;
		}
		uint8_t red, green, blue,forered,foregreen,foreblue,finalred,finalgreen,finalblue;
		red = (bgpix & 0xf800) >> 11 << 3;
		green = (bgpix & 0x7E0) >> 5 << 2;
		blue = (bgpix & 0x1F) << 3;
		
		forered = (forepix & 0xf800) >> 11 <<3;
		foregreen = (forepix & 0x7E0) >> 5 <<2;
		foreblue = (forepix & 0x1F) <<3	;

		finalred = (red * (1 - alpha) + (forered * alpha));
		finalgreen = (green * (1 - alpha) + (foregreen * alpha));
		finalblue = (blue * (1 - alpha) + (foreblue * alpha));
		//return ((finalred / 8) << 11) | ((finalgreen / 4) << 5) | (finalblue / 8);
		return (uint16_t)((finalred & 0xF8) << 8) | ((finalgreen & 0xFC) << 3) | (finalblue >> 3);
	}

	void plot4points_FAT(int16_t cx, int16_t cy, uint16_t x, uint16_t y, uint16_t color,uint8_t fat)
	{
		fill_circle(cx + x,cy + y,fat,color);
		if (x != 0) fill_circle(cx - x,cy + y,fat,color);
		if (y != 0) fill_circle(cx + x,cy - y,fat,color);
		if (x != 0 && y != 0) fill_circle(cx - x,cy - y,fat,color);
	}
	void plot4points(int16_t cx, int16_t cy, uint16_t x, uint16_t y, uint16_t color)
	{
		draw_pixel(cx + x,cy + y,color);
		if (x != 0) draw_pixel(cx - x,cy + y,color);
		if (y != 0) draw_pixel(cx + x,cy - y,color);
		if (x != 0 && y != 0) draw_pixel(cx - x,cy - y,color);
	}
	void drawHline(int16_t x, int16_t y, int16_t w, uint16_t color) {
		do { draw_pixel(x+w-1,y,color); } while (--w > 0);
	}
	void drawVline(int16_t x, int16_t y, int16_t h, uint16_t color) {
		do { draw_pixel(x,y+h-1,color); } while (--h > 0);
	}
	void drawHline_fat(int16_t x, int16_t y, int16_t w, uint16_t color, uint8_t thickness) {
		do { fill_circle(x+w-1,y,thickness,color); } while (--w > 0);
	}
	void drawVline_fat(int16_t x, int16_t y, int16_t h, uint16_t color, uint8_t thickness) {
		do { fill_circle(x,y+h-1, thickness,color); } while (--h > 0);
	}


	void drawHline_AA(int16_t x, int16_t y, int16_t w, uint16_t color, double AA) {
		do { draw_pixel_AA(x+w-1,y,color,AA); } while (--w > 0);
	}
	void drawVline_AA(int16_t x, int16_t y, int16_t h, uint16_t color, double AA) {
		do { draw_pixel_AA(x,y+h-1,color,AA); } while (--h > 0);
	}
	void plot4points_AA( int cx, int cy, int x, int y, uint16_t color, double alpha) {
		draw_pixel(cx + x,cy + y,blendColor(get_pixel(cx + x,cy + y),color,alpha));
		if (y != 0) draw_pixel_AA(cx + x,cy - y,color,alpha);
		if (x != 0) draw_pixel_AA(cx - x,cy + y,color,alpha);
		if (x != 0 && y != 0) draw_pixel_AA(cx - x,cy - y,color,alpha);
	}
public:


	void begin() {
		delay(100);
		Area.active_x1 = 0;
		Area.active_y1 = 0;
		Area.active_x2 = 128;
		Area.active_y2 = 128;
		s_width = 128;
		s_height = 128;
		tft.begin();
		tft.clearScreen();
		tft.setTextColor(WHITE);  
		tft.setTextSize(1);
//		tft.setCursor(33, 0); //128/2=64 - 72/2=31 = 33
//		tft.println("Pix-Watch.");
	}
	void sleepMode(bool enable) {
		tft.sleepMode(enable);
	}
	void setPRINTColor(uint16_t col) {
		Font.textcolor = col;
		Font.textbgcolor = col;
	}
	void Cursor(uint8_t x, uint8_t y) {
		if (x > 120|| y > 120) {}
		else {
			Font.cursor_x = x;
			Font.cursor_y = y;
		}
	}

	void SetActiveArea(int x,int y, int x1, int y1) {
		if (!(x < 0 || x > s_width || y < 0 || y > s_height)) {
			Area.active_x1 = x;
			Area.active_x2 = x1;
			Area.active_y1 = y;
			Area.active_y2 = y1;
		}
	}
	int get_x_offset() {
		return Area.active_x1;
	}
	int get_y_offset() {
		return Area.active_y1;
	}
	void draw_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color_) {
		drawHline(x,y,w,color_);
	}
	 void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color_) {
		if (y0 == y1) {
			if (x1 > x0) {
				drawHline(x0, y0, x1 - x0 + 1, color_);
			} else if (x1 < x0) {
				drawHline(x0, y0, x0 - x1 + 1, color_);
			} else {
				draw_pixel(x0,y0,color_);
			}
			return;
		} else if (x0 == x1) {
			if (y1 > y0) {
				drawVline(x0, y0, y1 - y0 + 1, color_);
			} else {
				drawVline(x0, y1, y0 - y1 + 1, color_);
			}
			return;
		}

		bool steep = abs(y1 - y0) > abs(x1 - x0);
		if (steep) {swap(x0, y0); swap(x1, y1);}
		if (x0 > x1) {swap(x0, x1); swap(y0, y1);}

		int16_t dx, dy;
		dx = x1 - x0;
		dy = abs(y1 - y0);

		int16_t err = dx / 2;
		int16_t ystep;

		if (y0 < y1) {
			ystep = 1;
		} else {
			ystep = -1;
		}
		int16_t xbegin = x0;
			
		if (steep) {
			for (; x0<=x1; x0++) {
				err -= dy;
				if (err < 0) {
					int16_t len = x0 - xbegin;
					if (len) {
						drawVline(y0, xbegin, len + 1, color_);
					} else {
							draw_pixel(y0,x0,color_);
					}
					xbegin = x0 + 1;
					y0 += ystep;
					err += dx;
				}
			}
			if (x0 > xbegin + 1) { drawVline(y0, xbegin, x0 - xbegin, color_); }
		} else {
			for (; x0<=x1; x0++) {
				err -= dy;
				if (err < 0) {
					int16_t len = x0 - xbegin;
					if (len) {
						drawHline(xbegin, y0, len + 1, color_);
					} else {
						draw_pixel(x0,y0,color_);
					}
					xbegin = x0 + 1;
					y0 += ystep;
					err += dx;
				}
			}
			
			if (x0 > xbegin + 1) { drawHline(xbegin, y0, x0 - xbegin, color_); }
		}
	}
	
	
	void drawLine_fat(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color_, uint8_t thickness) {
		if (thickness < 1) {
			drawLine(x0,y0,x1,y1,color_);
		} else {
			if (y0 == y1) {
				if (x1 > x0) {
					drawHline_fat(x0, y0, x1 - x0 + 1, color_, thickness);
				} else if (x1 < x0) {
					drawHline_fat(x0, y0, x0 - x1 + 1, color_, thickness);
				} else {
					fill_circle(x0,y0,thickness, color_);
				}
				return;
			} else if (x0 == x1) {
				if (y1 > y0) {
					drawVline_fat(x0, y0, y1 - y0 + 1, color_, thickness);
				} else {
					drawVline_fat(x0, y1, y0 - y1 + 1, color_, thickness);
				}
				return;
			}

			bool steep = abs(y1 - y0) > abs(x1 - x0);
			if (steep) {swap(x0, y0); swap(x1, y1);}
			if (x0 > x1) {swap(x0, x1); swap(y0, y1);}

			int16_t dx, dy;
			dx = x1 - x0;
			dy = abs(y1 - y0);

			int16_t err = dx / 2;
			int16_t ystep;

			if (y0 < y1) {
				ystep = 1;
			} else {
				ystep = -1;
			}
			int16_t xbegin = x0;				
			if (steep) {
				for (; x0<=x1; x0++) {
					err -= dy;
					if (err < 0) {
						int16_t len = x0 - xbegin;
						if (len) {
							drawVline_fat(y0, xbegin, len + 1, color_, thickness);
						} else {
							fill_circle(y0,x0,thickness, color_);
						}
						xbegin = x0 + 1;
						y0 += ystep;
						err += dx;
					}
				}
				if (x0 > xbegin + 1) { drawVline_fat(y0, xbegin, x0 - xbegin, color_, thickness); }
			} else {
				for (; x0<=x1; x0++) {
					err -= dy;
					if (err < 0) {
						int16_t len = x0 - xbegin;
						if (len) {
							drawHline_fat(xbegin, y0, len + 1, color_, thickness);
						} else {
							fill_circle(x0,y0,thickness, color_);
						}
						xbegin = x0 + 1;
						y0 += ystep;
						err += dx;
					}
				}
				
				if (x0 > xbegin + 1) { drawHline_fat(xbegin, y0, x0 - xbegin, color_, thickness); }
			}
		}
	}
	
	void draw_circle(int cx, int  cy , uint8_t  i , uint16_t color) {
		int error = -i;
		int16_t x = i;
		int16_t y = 0;
		while (x >= y){
			plot4points(cx, cy, x, y, color);
			if (x != y) plot4points(cx, cy, y, x, color);
			error += y;
			++y;
			error += y;
			if (error >= 0){
				--x;
				error -= x;
				error -= x;
			}
		}
	}
	void fill_circle(int16_t x0, int16_t  y0 , uint8_t  r , uint16_t color_, uint8_t cornername = 3) {
		drawVline(x0, y0-r, 2*r+1, color_);
		// Used to do circles and roundrects
		int16_t f     = 1 - r;
		int16_t ddF_x = 1;
		int16_t ddF_y = -2 * r;
		int16_t x     = 0;
		int16_t y     = r;
		while (x<y) {
			if (f >= 0) {
				y--;
				ddF_y += 2;
				f     += ddF_y;
			}
			x++;
			ddF_x += 2;
			f     += ddF_x;
			if (cornername & 0x1) {
				drawVline(x0+x, y0-y, 2*y+1, color_);
				drawVline(x0+y, y0-x, 2*x+1, color_);
			}
			if (cornername & 0x2) {
				drawVline(x0-x, y0-y, 2*y+1, color_);
				drawVline(x0-y, y0-x, 2*x+1, color_);
			}
		}
	}
	void draw_circle_fat(int16_t cx, int16_t  cy , uint8_t  i , uint16_t color, uint8_t fat) {
		int error = -i;
		int16_t x = i;
		int16_t y = 0;
		while (x >= y){
			plot4points_FAT(cx, cy, x, y, color,fat);
			if (x != y) plot4points_FAT(cx, cy, y, x, color,fat);
			error += y;
			++y;
			error += y;
			if (error >= 0){
				--x;
				error -= x;
				error -= x;
			}
		}
	}
	void drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *bitmap) 
	{
		int16_t py =y;
		int16_t xx =x;
		for (int16_t px = 0;px < w*h; px++){//loop trough pixels
			if (xx %(x+w) == 0) {
				xx=x;
				py++;
			}
			draw_pixel(xx,py,bitmap[px]);//16bit
			xx++;
		}
	}
	void fill_rect(int16_t x,int16_t y,int16_t w,int16_t h, uint16_t col) {
		for (int yy=y; yy<h; yy++) {
			for (int xx = x; xx < w; xx++) {
				draw_pixel(xx,yy,col);
			}
		}
	}
	void draw_buffer() {
			//blend_screen(0.08); //the magic to blend all pixels
		tft.drawBuffer();
	}
	void set_buffer_bg(uint16_t color_) {
		//tft.setbufferbg(((((color_ >> 16) & 0xFF) / 8) << 11) | ((((color_ >> 8) & 0xFF) / 4) << 5) | (((color_) &  0xFF) / 8));
		tft.setbufferbg(color_);
	}
	void draw_pixel(int16_t x, int16_t  y, uint16_t color) {
		if (!(x < Area.active_x1 || x > Area.active_x2 || y < Area.active_y1 || y > Area.active_y2)) {
			tft.buffer[x][y] = color;
		}
	}
	uint16_t get_pixel(int16_t x, int16_t  y) {
		if (x < 0 || x > s_width) return 0x0000;
		if (y < 0 || y > s_height) return 0x0000;
		return tft.buffer[x][y];
	}
	 void draw_roundrect_AA(int16_t x, int16_t y, int16_t width, int16_t height, int16_t rx, int16_t ry, boolean bordermode,uint16_t col) {
		int16_t i;
		int32_t a2, b2, ds, dt, dxt, t, s, d;
		int16_t xp, yp, xs, ys, dyt, od, xx, yy, xc2, yc2;
		float cp;
		double sab;
		double weight, iweight;
		if ((rx < 0) || (ry < 0)) {
			return;
		}
		if (rx == 0) {
			drawVline(x, y - ry, y + ry,col);
			return;
		}
		if (ry == 0) {
			drawHline(x - rx, y, x + rx,col);
			return;
		}
		a2 = rx * rx;
		b2 = ry * ry;
		ds = a2 << 1;
		dt = b2 << 1;
		xc2 = x << 1;
		yc2 = y << 1;
		sab = sqrt((double)(a2 + b2));
		od = (int)round(sab*0.01) + 1;
		dxt = (int)round((double)a2 / sab) + od;
		t = 0;
		s = -2 * a2 * ry;
		d = 0;
		xp = x + rx;
		yp = y;
		drawHline(x + rx, y, width - rx-rx+1,col);
		drawHline(x + rx, y + height, width - rx-rx+1,col);
		drawVline(x + width, y + ry, height-ry-ry+1,col);
		drawVline(x, y + ry, height-ry-ry+1,col);

		for (i = 1; i <= dxt; i++) {
			xp--;
			d += t - b2;

			if (d >= 0) {
				ys = yp - 1;
			} else if ((d - s - a2) > 0) {
				if (((d << 1) - s - a2) >= 0) {
					ys = yp + 1;
				} else {
					ys = yp;
					yp++;
					d -= s + a2;
					s += ds;
				}
			} else {
				yp++;
				ys = yp + 1;
				d -= s + a2;
				s += ds;
			}

			t -= dt;

			if (s != 0) {
				cp = (float) abs(d) / (float) abs(s);
				if (cp > 1.0) {
					cp = 1.0f;
				}
			} else {
				cp = 1.0f;
			}

			weight = cp;
			iweight = 1 - weight;

			if( bordermode ) {
				iweight = yp > ys ? 1 : iweight;
				weight = ys > yp ? 1 : weight;
			}

			/* Upper half */
			xx = xc2 - xp;
			draw_pixel(xp,yp, blendColor(get_pixel(xp,yp),col,iweight));
			draw_pixel(xx+width, yp, blendColor(get_pixel(xx+width,yp),col,iweight));
			
			draw_pixel(xp,ys, blendColor(get_pixel(xp,ys),col,weight));
			draw_pixel(xx+width, ys, blendColor(get_pixel(xx+width,ys),col,weight));

			/* Lower half */
			yy = yc2 - yp;
			
			draw_pixel(xp, yy+height, blendColor(get_pixel(xp, yy+height),col,iweight));
			draw_pixel(xx+width,yy+height, blendColor(get_pixel(xx+width, yy+height),col,iweight));

			yy = yc2 - ys;
			draw_pixel(xp, yy+height, blendColor(get_pixel(xp, yy+height),col,weight));
			draw_pixel(xx+width, yy+height, blendColor(get_pixel(xx+width, yy+height),col,weight));
		}
		dyt = (int)round((double)b2 / sab ) + od;

		for (i = 1; i <= dyt; i++) {
			yp++;
			d -= s + a2;

			if (d <= 0) {
				xs = xp + 1;
			} else if ((d + t - b2) < 0) {
				if (((d << 1) + t - b2) <= 0) {
					xs = xp - 1;
				} else {
					xs = xp;
					xp--;
					d += t - b2;
					t -= dt;
				}
			} else {
				xp--;
				xs = xp - 1;
				d += t - b2;
				t -= dt;
			}

			s += ds;

			if (t != 0) {
				cp = (float) abs(d) / (float) abs(t);
				if (cp > 1.0) {
					cp = 1.0f;
				}
			} else {
				cp = 1.0f;
			}

			weight = cp;
			iweight = 1 - weight;
			/* Left half */
			xx = xc2 - xp;
			yy = yc2 - yp;
			
			draw_pixel(xp,yp, blendColor(get_pixel(xp,yp),col,iweight));
			draw_pixel(xx+width,yp, blendColor(get_pixel(xx+width,yp),col,iweight));
			
			draw_pixel(xp, yy+height, blendColor(get_pixel(xp,yy+height),col,iweight));
			draw_pixel(xx+width,yy+height, blendColor(get_pixel(xx+width,yy+height),col,iweight));

			/* Right half */
			xx = xc2 - xs;
			draw_pixel(xs,yp, blendColor(get_pixel(xs,yp),col,weight));
			draw_pixel(xx+width, yp, blendColor(get_pixel(xx+width,yp),col,weight));

			draw_pixel(xs, yy+height, blendColor(get_pixel(xs,yy+height),col,weight));
			draw_pixel(xx+width, yy+height, blendColor(get_pixel(xx+width,yy+height),col,weight));
		}
	}
	void fill_circle_blend(int16_t x0, int16_t  y0 , uint8_t  r , uint16_t color_, double alpha) {
		drawVline_AA(x0, y0-r, 2*r+1, color_, alpha);
		// Used to do circles and roundrects
		int16_t f     = 1 - r;
		int16_t ddF_x = 1;
		int16_t ddF_y = -2 * r;
		int16_t x     = 0;
		int16_t y     = r;
		int16_t oldy = r;
		for (int8_t offset = x0; offset < x0+r; offset++) {
			if (f >= 0) {
				y--;
				ddF_y += 2;
				f     += ddF_y;
			}
			x++;
			ddF_x += 2;
			f     += ddF_x;
			if (x<=y) {
				if (x != y) {
					drawVline_AA(x0+x, y0-y, 2*y+1, color_, alpha);
					drawVline_AA(x0-x, y0-y, 2*y+1, color_, alpha);
				}
				if (oldy != y){
					drawVline_AA(x0+y, y0-x, 2*x+1, color_, alpha);
					drawVline_AA(x0-y, y0-x, 2*x+1, color_, alpha);
					oldy = y;
				}
			}
		}
	}
	void fill_rect_AA(int x,int y,int w,int h, uint16_t col, double alpha, bool depth) {
		for (int yy=y; yy<h; yy++) {
			for (int xx = x; xx < w; xx++) {
				if (depth == 0) {
					draw_pixel(xx,yy,blendColor(get_pixel(xx,yy),col,alpha));
				} else {
					if ((xx == x) || (xx == w) || (yy == y) || (yy == h)) {
						draw_pixel(xx,yy,blendColor(get_pixel(xx,yy),col,alpha));
					} else {
						draw_pixel(xx,yy,col);
					}
				}
			}
		}
	}
	void draw_circle_AA(int16_t x, int16_t y, int16_t radius, boolean bordermode,uint16_t col) {
		draw_roundrect_AA(x-radius, y-radius, radius<<1, radius<<1, radius, radius, bordermode,col);
	}
	void draw_circle_blend(uint8_t cx, uint8_t  cy , uint8_t  i , uint32_t color, double alpha) {
		int error = -i;
		int16_t x = i;
		int16_t y = 0;
		while (x >= y){
			plot4points_AA(cx, cy, x, y, color, alpha);
			if (x != y) plot4points_AA(cx, cy, y, x, color, alpha);
			error += y;
			++y;
			error += y;
			if (error >= 0){
				--x;
				error -= x;
				error -= x;
			}
		}
	}
	void draw_pixel_AA(int x,int y, uint16_t color, double a) {
		if (x < 0 || x > s_width || y < 0 || y > s_height) {}
		else {
			tft.buffer[x][y] = blendColor(get_pixel(x,y),color,a);
			//put_aa_pixel(x,y,color,a);
		}
	}
	void drawPieEnd_AA(int16_t x, int16_t y, int16_t r, int16_t rs, int16_t re,uint16_t color,float a) {
	int16_t cx, cy, d;
	rs -= 90;
	re   -= 90;
	if (rs>re) rs -= 360;
		for (d=rs+1; d<re+1; d++){
			cx = x + cos((d*3.14)/180) * r;
			cy = y + sin((d*3.14)/180) * r;
			draw_pixel_AA(cx, cy,color,a);
		}
	}
	void drawPieEnd(int16_t x, int16_t y, int16_t r, int16_t rs, int16_t re,uint16_t color,uint8_t size) {
	int16_t cx, cy, d;
	rs -= 90;
	re   -= 90;
	if (rs>re) rs -= 360;
		for (d=rs+1; d<re+1; d++){
			cx = x + cos((d*3.14)/180) * r;
			cy = y + sin((d*3.14)/180) * r;
			fill_circle(cx, cy,size,color);
		}
	}
};
 
Hi Ramon,

thanks :)

I'll work on the library the next days - there is still a (big) pending task for me (i want to remove the original ili9341 code). After that is done, i'll look at your code. Perhaps next weekend :)
OK ? :)

Frank.
 
Hi all...I am trying the video playback from SD card on a Teensy 3.6 with ILI9341 using Frank's libraries and demo video. I am using the SDFat libraries, and copied the myvideo.bin video file to a 32Gb Sandisk card. The card is I believe working ok ..at least I could play RAW audio files form it before without issue. Here is the code i am trying to run. I commented out the lines referring to audio playback .


//
//Demovideo see :
//
//https://drive.google.com/drive/folders/0Bx2Jw84lqebkelF3MHg2eWVzb0U
//
// This sketch works with std 180MHz and less, but better with 240MHZ and overclocked F_BUS, or 144MHz and overclocked F_BUS to the max.
//

// #include <SD.h>
#include <SdFat.h>
#include <SPI.h>
//#include <Audio.h>
#include <ILI9341_t3DMA.h>

#define SCREEN_WIDTH ILI9341_TFTWIDTH
#define SCREEN_HEIGHT ILI9341_TFTHEIGHT

#define TFT_DC 20
#define TFT_CS 21
#define TFT_RST 255 // 255 = unused, connect to 3.3V
#define TFT_MOSI 7
#define TFT_SCLK 14
#define TFT_MISO 12

ILI9341_t3DMA tft = ILI9341_t3DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);

SdFatSdio sd;
File file;


// GUItool: begin automatically generated code
//AudioPlayQueue queue1; //xy=183,163
//AudioOutputAnalog dac1; //xy=376,154
//AudioConnection patchCord1(queue1, dac1);
// GUItool: end automatically generated code

const float fps = 23.98;
const int rate_us = 1e6 / fps;

uint8_t header[512];

void setup() {
//AudioMemory(30);

tft.begin();
tft.dfillScreen(ILI9341_BLUE);
tft.refresh();
delay(500);

if (!sd.begin()) {
sd.initErrorHalt();
}

if (!file.open("myvideo.bin", O_READ)) {
sd.errorHalt("open failed");
}

Serial.println("Begin playing");

//read header
file.read(header, 512);
}


//uint32_t audioPos = 0;
const uint32_t framesize = 5 * 32768;
const uint16_t * screen16e = ( uint16_t*) (screen32 + (320 + 240 * 2) / 4);

int16_t *lb;
int16_t *rb;

void loop() {
uint32_t m;
m = micros();
uint32_t * p = screen32;
uint32_t sdbufsize = 65536;
uint32_t rd = 0;
int bytesread;
do {
do {
if (sdbufsize & framesize) {
bytesread = file.read(p, sdbufsize);
if (!bytesread) {
file.seek(512);
break;
}
p += sdbufsize / 4;
rd += sdbufsize;
} else {
sdbufsize /= 2;
}
} while (rd < framesize);
} while (!bytesread);


//Audio begins at position 320*240*2 and is 1839 samples*2 channels
/* int16_t * ap = ( int16_t*) screen32;
ap += 320 * 240;
int i = 0;
int16_t l, r;
for (i = 0; i < 1839; i++) {
if (audioPos == 0) {
lb = queue1.getBuffer();
}
*lb++ = (*ap) * 2 ;
ap++;
ap++;
audioPos += 1;
if (audioPos >= 128) {
audioPos = 0;
queue1.playBuffer();
}

}
*/
while (micros() - m < rate_us);

}



Code uploads and I see this:

Sketch uses 44956 bytes (4%) of program storage space. Maximum is 1048576 bytes.
Global variables use 160752 bytes (61%) of dynamic memory, leaving 101392 bytes for local variables. Maximum is 262144 bytes.
Invalid library found in /Applications/Arduino.app/Contents/Java/libraries/SdFat-beta-master: /Applications/Arduino.app/Contents/Java/libraries/SdFat-beta-master
Invalid library found in /Applications/Arduino.app/Contents/Java/libraries/SdFat-beta-master: /Applications/Arduino.app/Contents/Java/libraries/SdFat-beta-master



All I see is a blue screen with black flashing vertical bar :0(


I tried the ILI9341_DMALIB_graphicstet.ino that Frank created ..and that worked fine ...so the DMA library sees to be working ok ?

Any help greatly appreciated !
steve

ps: in the serial monitor it just says 'playing video'
using TD 1.3.5 and Arduino 1.8.0
 
Last edited:
Hi,
I remember that there were problems with the 3.5. Something with SPI/DMA/Scatter-Gather is not compatible with the 3.6. But there might be a way to bring it to work with the 3.5, too.
 
Hi,
I remember that there were problems with the 3.5. Something with SPI/DMA/Scatter-Gather is not compatible with the 3.6. But there might be a way to bring it to work with the 3.5, too.


hi frank .. thanks..i have a teensy 3.6 and using Teensyduino 1.35... sorry about my confusion

but can ask what video container is the myvideo.bin video using ? Can i just copy it to my sd card ?? That is what i have been trying...
maybe it is an sd card image file i need to burn ?
 
The bin-file was created with processing. The Processing-sketch basically converts it to a raw-565-format and appends the audio-data. See Post#12 for more details.
But i've not tried to run it, since then :) Maybe it's broken due to Teensyduino-changes/updates?!?
It is sufficiant to copy the bin-file to the card. The bin-file from google is know to work, so i'd try it with the "bunny" example, first.
 
ok thanks ! now that i know its raw-565 format i will see if i can play it in a raw video player to test if it plays there...

i really want to see the big bunny hopping over my tft screen :0) !!
 
Yup, but there's audio embedded.. so i guess, it's hard to find a player for it (i don't think there is any :)
 
ah..ok ...but if i have my own mp4 file ..(no audio) and just wish to create a video that will play back using your DMA library .. ...what processing steps can I use ...? convert to raw 565 ? ....
commenting out the lines in your sketch which refer to the audio doesn't work for me ...
 
You have to edit the processing-sketch. Did'nt i upload it somewhere ?
I guess yes, because i saw other working videos on youtube.. hm. I have to look :)
 
hi ..ok getting somewhere ! ..i can at least see video (its your original inclusing the audio embedded ) but i see multiple ghost frames and also rotation by 90 degrees.. (see link at bottom to a short video of it playing on my tft )


i used your code and tried commenting out audio sections..maybe i did something wrong ?

//
//Demovideo see :
//
//https://drive.google.com/drive/folders/0Bx2Jw84lqebkelF3MHg2eWVzb0U
//
// This sketch works with std 180MHz and less, but better with 240MHZ and overclocked F_BUS, or 144MHz and overclocked F_BUS to the max.
//

//#include <SD.h>
#include <SdFat.h>
#include <SPI.h>
//#include <Audio.h>
#include <ILI9341_t3DMA.h>

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

#define TFT_DC 20
#define TFT_CS 21
#define TFT_RST 255 // 255 = unused, connect to 3.3V
#define TFT_MOSI 7
#define TFT_SCLK 14
#define TFT_MISO 12

ILI9341_t3DMA tft = ILI9341_t3DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);

SdFatSdio sd;
File file;


// GUItool: begin automatically generated code
//AudioPlayQueue queue1; //xy=183,163
//AudioOutputAnalog dac1; //xy=376,154
//AudioConnection patchCord1(queue1, dac1);
// GUItool: end automatically generated code

const float fps = 23.98;
const int rate_us = 1e6 / fps;

uint8_t header[512];

void setup() {
//AudioMemory(30);

tft.begin();
tft.dfillScreen(ILI9341_BLUE);
//tft.setRotation(3);
tft.refresh();
delay(500);

if (!sd.begin()) {
sd.initErrorHalt();
}

if (!file.open("bunny.bin", O_READ)) {
sd.errorHalt("open failed");
}

Serial.println("Begin playing");

//read header
file.read(header, 512);
}


//uint32_t audioPos = 0;
const uint32_t framesize = 5 * 32768;
const uint16_t * screen16e = ( uint16_t*) (screen32 + (320 + 240 * 2) / 4);

// int16_t *lb;
//int16_t *rb;

void loop() {
uint32_t m;
m = micros();
uint32_t * p = screen32;
uint32_t sdbufsize = 65536;
uint32_t rd = 0;
int bytesread;
do {
do {
if (sdbufsize & framesize) {
bytesread = file.read(p, sdbufsize);
if (!bytesread) {
file.seek(512);
break;
}
p += sdbufsize / 4;
rd += sdbufsize;
} else {
sdbufsize /= 2;
}
} while (rd < framesize);
} while (!bytesread);


//Audio begins at position 320*240*2 and is 1839 samples*2 channels
/*
int16_t * ap = ( int16_t*) screen32;
ap += 320 * 240;
int i = 0;
int16_t l, r;
for (i = 0; i < 1839; i++) {
if (audioPos == 0) {
lb = queue1.getBuffer();
}
*lb++ = (*ap) * 2 ;
ap++;
ap++;
audioPos += 1;
if (audioPos >= 128) {
audioPos = 0;
queue1.playBuffer();
}

}
*/
while (micros() - m < rate_us);

}




http://www.dailymotion.com/video/x5bgfz4
 
Last edited:
update: ok finally I have high quality video playback ! BUT to do this i had to flip the video width and length numbers in your processing code sketch 320<-> 240 ...
......

//int ledWidth = 320; // size of LED panel
//int ledHeight = 240;
int ledWidth = 240; // size of LED panel
int ledHeight = 320;

......
so video plays perfectly ...full screen but in portrait rather than landscape.... .so i am close...
 
http://www.dailymotion.com/video/x5bh8ip

you can see its playing great ..but squashed into portrait !

also in your processing code i noticed 2 image commands ... and i was getting 'double' overlay of the move when running processing so I commented out one :
.
.
.

image(myMovie, 0, 80);
//image(ledImage, 240 - ledWidth / 2, 10);

.
.
maybe this is my mistake ?
 
You could try to add this line
Code:
    16, ILI9341_GMCTRN1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07,
        0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma
    3, 0xb1, 0x00, 0x10, // FrameRate Control 119Hz
    
[COLOR=#00ff00][B]    2, ILI9341_MADCTL, MADCTL_MV | MADCTL_BGR, //rotate //ADD THIS LINE[/B][/COLOR]
    0
};
to the init_commands.
 
ok !! I also just found that by adding tft.setRotation(3) to the arduino sketch its working in landscape !! Thanks for your great help !!
 
If you want to write a C64 emulator I'd have a quick look at the MAME source code. I looked at it years ago and it was very well organized. I use to program Apple ][ 6502 assembler in 1983 when 14 so was interested in the 6502 emulation. Actually quite simple and ingenious the way it worked.

The Teensy should do it easy given it has heaps more RAM, Mhz and is 32 bit instead of 8 bit.

Yet to migrate my projects to the teensy as I have 3 Arduino Dues that do the job as far as testing goes.

Been looking for a way to convert video to a raw format and it seems ffmpeg could be what I'm looking for. Been doing a lot on a graphics library and ssd1351/dma library but needed a way to convert video into my internal format. Currently my only option is animated GIF files. Small demo of library with ssd1351 here
 
Thanks, i have it pretty much working - faster than the original C64, including sound+graphics (currently 2x the speed, but i still have to add some things). I hope i can publish it in march or april.

In the extras folder of the download is a processing sketch that does converts a video to raw 565 + sound.
Your video looks amazing !
 
Thanks, i have it pretty much working - faster than the original C64, including sound+graphics (currently 2x the speed, but i still have to add some things). I hope i can publish it in march or april.

In the extras folder of the download is a processing sketch that does converts a video to raw 565 + sound.
Your video looks amazing !

Hey, good to hear! Off memory the C64 was a 65c02 or a slight variation of the straight 6502? I found an Apple ][ emulator with virtual disks and was gob-smacked by how faithfully it played everything. Even got to play escape from Rungistan again and finish it as we got stumped in 1983 near the end.

Being able to read SD and output to the OLED displays with DMA is a game changer. Faster and better than 8 or even 16 bit parallel because of the loop management etc.

Ended up making my own SPI base class as needed all SPI handled consistently. Mainly, being able to set the SCK rate and honor it for each device.

A greate article on SPI is https://www.dorkbotpdx.org/blog/paul/better_spi_bus_design_in_3_steps that I'm sure Paul S. spent a lot of time on and put together.

Must admit, I have been a victim of the CS problem because multiple devices listening to startup code. I now know to have a pullup resistor on all CS lines and designed the library a bit differently to pull CS high first before any initializing is done. (ie. CS is passed when class is initialized and .begin(...) will do the rest.) Also, since all is encapsulated in a base class I could in theory catch where possible conflicts happen. In debug mode it will tell me what is being initialized in order.

My flash rom driver use to work fine by itself but died when on my test board with other SPI devices. Thought it was a DMA conflict or something but after reading the SPI article found it was listening to OLED startup code and lost the plot before it even started up. Did some AVI rendering in Direct X years ago so I'm hoping I can also add some audio to clips. Just a matter of keeping a circular buffer topped up.

I output sound with an MCP4921 that uses SPI but use software SPI as it gets called in an interrupt... sort of wanted to avoid the issue of SPI within an interrupt. Anyway I'll look at video first and see what sound looks like.
 
Status
Not open for further replies.
Back
Top