Using frame buffering to solidify tft displayed objects

Status
Not open for further replies.
I will check that out.

I did a lot of playing last night. The first thing I did was re-build my character sprites to 16x16 which was easy enough, just had to a row or two of pixels on each side or whatever. This helped a bit and made the bitmap appear almost perfect. What it's doing is cutting off the last 2-4 rows on the right side of the image then placing those 2-4 rows on the left side of the image. This is using the color variable 0 in the palette.

Out of curiosity I decided to change the color 0 to purple so all pixels are assigned a proper color and guess what. Nope unfortunately it not help but it colored all my invisible pixel purple. Next I printed another bitmap, this time from the tiles. I picked my palm tree Sprite and it did the same thing.

Next I printed out the tilemap, the main rock looks fine but the others do the cutoff thing.

But here's the cool thing. When I was using multiple stacked bitmaps to create the tilemap, the camera comtrols caused the map to flash when a new row of pixels would come up to displayed using the camera controls. Now when scrolling there's no flash. The rows of pixels just show up like they should. Yay!!!!

All right so to clarify..... The 4bpp and Nbpp functions when used are cutting 2-4 rows off the right side then placing those two rows in the right order on the left side of the image. And changing the 0 color variable to an actual color has no effect except to color the invisible pixels the color I choose.
 
I've been playing with this problem all night and the closest I can get is adding 2 more rows of pixels to equal 18x16. What am I doing wrong to cause it to cut off the last two rows and set them to the left side.
 
If the code getting the location and data are working properly to display ... then either the drawing location and size are off - or the data format presented it improperly shaped or formatted for that space.
As noted before give yourself reference marks - I'd draw an off color rectangle filled or unfilled around exactly where you expect the image to appear [up an left one pixel and two pixels taller and wider than the bitmap] - or even the same size and location and it should be perfectly covered. It will either show the origin and dimensions are right or not if the drawing is perfectly within/over the box. if it is outside the box you can then examine how much and which way.
 
I agree with defragster, that is one way to find the issues. I did a bit of that when i was doing the test program for testing clipping, frame buffer...

Another thing I would do, is to add some debug code into the draw function(s), that would check the clipping ranges and if something you pass in causes it to be clipped, print out some data, like the bounding rectangle passed in, maybe hex address of object... Maybe in some cases do the code i mentioned in other posts for a logical breakpoint and then see exactly where things are drawn.

Also unclear from your description, you say it cuts off a couple of rows, but displays them on the left? I think of rows as horizontal (i.e. bottom of screen is being cut off?), versus cutting off columns, which would be not displaying something on the right hand side? The reason I mention the differences, is errors in the logic in the different directions will show up different. That is for example if you are outputting a rectangle and your code outputs too much data for the defined rectangle, the extra data will wrap to the top left of the rectangle and will continue along the top... Now if your code is outputting one horizontal line (ROW) at a time and it overflows, than yes it could wrap around to the right hand side of the bounding rectangle...

But again often break this down to simple test app. Maybe does something like:
FillScreen(Black), Draw Rectangle maybe red (1 or 2 pixels in on all all sides), Depending on your Tiles and the stuff, Try drawing a few tiles or objects... Like: if you have a large one, can you draw one that again is 1 or 2 pixels inside the rectangle you drew? Or if they are all something like 18x16, try drawing 4 of them, for each of the corners, where they should again be drawn 1 or 2 pixels inside the red rectangle... Did it draw correctly? If again doing smaller objects, maybe add drawing some more rectangles within the first one, the size of your objects plus 1/2 pixels on each side. And draw your primitives in these. Then verify that your objects drew properly and you could see that your images drew properly.

Again experiment: When I said Black and Red above, preferably use colors that are not used in your objects, such that you can visually inspect and see complete bounding rectangle plus a background color all the way around each of your objects...
 
Well let's chalk this up to still learning how the library works. SMH!!!

Ok got up a lil while ago read your posts. Ran a couple tests every thing was fine. So I placed Kurts lil 16x16 blue and red square from the FB and clip test in my bitmaps.h file then called it in my sketch. Worked fine. So I changed a few pixels. That worked as well. So I closed the lid and thought for a minute or two. The question I asked myself next was what is the actuall difference between Kurts bitmap and mine.

So I opened the beast back up looked. So freaking simple. Turns out when I removed the Progmem from the bitmaps I also needed to remove two more things. Can you guess what I forgot to remove?

Well normally after the { you would add the width and height like 16,16, and still name the 16,16, in the call to the function. So I removed the 16,16, from the bitmaps and voila, they show up right now. Just need to go back and read what you guys said about making invisible pixels.

Edit::: just got done testing with the width and height markers removed from all the writeRectnbpp bitmaps and it looks great. There's a lil bit of flash when the tile,app scrolls but not nearly as bad. It stays solid. As for the player character he looks better as well and just a lil bit of flash.

With out the tilemap he is real solid but trails. Things just need a tweak or two.

Also I think I might finally get how to kill the trails using draw rect. I can have it call drawrect() after each move. So if move the player right, I call a black rectangle to fill the space left of the Sprite moving right which will kill the trail.
 
Last edited:
So far the these draw functions have no concept of drawing transparent.

It would not be hard to add to the frame buffer code. That is you simply need to make a version of the function that changes the line:
Code:
*pfbPixel++ = palette[((*pixels)>>pixel_shift) & pixel_bit_mask];

To detect some way that you want a transparent bit.

Example if you wish for 0 as the palette index to imply transparent... I would not put it into my version of the library as it would potentially break other users...

But the code could looks something like:
Code:
#define TRANSPARENT_INDEX 0
...   
    uint8_t palette_index = (*pixels)>>pixel_shift) & pixel_bit_mask];
    if (palette_index != TRANSPARENT_INDEX)
        *pfbPixel = palette[palette_index];
    *pfbPixel++;

Lots of other options as well. Above I defined a constant to check the index, could instead be a variable. Where maybe you set the transparent index by new member method, or maybe special value in palette tells you...

As for doing it in the non frame buffer code. Logically you could do it similar, but
Code:
			writedata16_cont(palette[((*pixels)>>pixel_shift) & pixel_bit_mask]);
changing the above to not output things would not work properly as we told the display we are downloading a rectangle of colors, so not outputting something would just screw up the alignment. Instead you would need to change the whole output code to instead output one pixel at a time, which would really slow it down. The code would look very similar to the drawChar function, that breaks up into two main parts. Transparent text (fgcolor == bgcolor), which runs reasonably slow and the opaque text which outputs the whole character as one output rectangle...


Well it definitely needs to be fast so the last paragraph is out of the question. wouldn't exactly know how to do the first option unless I saw it in a function. im trying to implement the second option. What did you mean by break the user. If the library is used as its made, having something in place to make transparent pixels would make things easier for any user that wants to use multicolored backgrounds behind their player.

ok so I added the define at the top of the libraries header and then added the uint part but its missing stuff. Or maybe Im implementiong it in the wrong place or the wrong way. Or do i add it to a function?

I havent even tried the frame buffer yet with the new functions and way of making bitmaps yet. It flashes a lil but looks so much better than it did with stacking which made constant flicker.
 
Last edited:
As I mentioned, the code works easily when using frame buffer as You are simply looping through the image and updating memory, so makes little difference if you skip some bits as long as you keep your position in the bitmap and your position in the frame buffer in sync.

The thing about breaking users code, is if I blindly assumed some index is now transparent, but the users code has used that index in their bitmaps for a color, their color would not display properly... There are a few options for that, which could be added, like:
a method something like: tft.setWriteRectTransparentIndex(uint16_t index);
Where it would save away that index into a member variable, which would default to something like 0xffff which would not match any index and change the code to not look for the #define index but instead use the member variable...

As for not using the frame buffer. The issue, is in that section of the code:
Code:
	beginSPITransaction();
	setAddr(x, y, x+w-1, y+h-1);
	writecommand_cont(ILI9341_RAMWR);
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask

		for (int i = 0 ;i < w; i++) {
			writedata16_cont(palette[((*pixels)>>pixel_shift) & pixel_bit_mask]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
		}
		pixels_row_start += count_of_bytes_per_row;
	}
	writecommand_last(ILI9341_NOP);
	endSPITransaction();
The setAddr call sets up the rectangle of bits to be output and assumes that that many words are output to the display. so you can not just skip any... If you really wanted
to output transparent, the code would have to do something more like:
Code:
	beginSPITransaction();
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask
                int16_t x_out = x; 
		for (int i = 0 ;i < w; i++) {
			uint8_t palette_index = (*pixels)>>pixel_shift) & pixel_bit_mask];
			if (palette_index != TRANSPARENT_INDEX)
				Pixel(x_out, y, palette[palette_index]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
			x_out++;
		}
		pixels_row_start += count_of_bytes_per_row;
		y++;
	}
	endSPITransaction();
Note: I made these changes on the fly here, so could likely be issues. But the nutshell about this, is instead of simply outputting a 16 color value for each pixel, this code calls Pixel, which itself does: setAddr, and the writecommand_cont, and the transfer of the 16 bit color. So of instead of just outputting one entry into fifo queue, which can properly queue up, the above code will go into command mode 1 byte, data mode 2 words, command mode 1 byte, data mode 2 words, command 1 byte, data 1 word.. Each of these transitions into and out of command word luckily with ili9341_t3n we can encode the DC pin on hardware CS pin, else would have to wait for data to output... But regardless it requires
8 outputs (13 bytes) to SPI... Which is why I mentioned it would be slower.
 
Ok so just to be sure I want to replace the stuff in the wroteRectNbpp() function with the above?
 
ok so I did a replacement of the stuff in the nbpp function like so.............
Code:
void GrafxT3::writeRectNBPP(int16_t x, int16_t y, int16_t w, int16_t h,  uint8_t bits_per_pixel, 
		const uint8_t *pixels, const uint16_t * palette )
{
	//Serial.printf("\nWR8: %d %d %d %d %x\n", x, y, w, h, (uint32_t)pixels);
	x+=_originx;
	y+=_originy;
	uint8_t pixels_per_byte = 8/ bits_per_pixel;
	uint16_t count_of_bytes_per_row = (w + pixels_per_byte -1)/pixels_per_byte;		// Round up to handle non multiples
	uint8_t row_shift_init = 8 - bits_per_pixel;				// We shift down 6 bits by default 
	uint8_t pixel_bit_mask = (1 << bits_per_pixel) - 1; 		// get mask to use below
	// Rectangular clipping 

	// See if the whole thing out of bounds...
	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
	if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;

	// In these cases you can not do simple clipping, as we need to synchronize the colors array with the
	// We can clip the height as when we get to the last visible we don't have to go any farther. 
	// also maybe starting y as we will advance the color array. 
	// Again assume multiple of 8 for width
 	if(y < _displayclipy1) {
 		int dy = (_displayclipy1 - y);
 		h -= dy; 
 		pixels += dy * count_of_bytes_per_row;
 		y = _displayclipy1; 	
 	}

	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;

	// For X see how many items in color array to skip at start of row and likewise end of row 
	if(x < _displayclipx1) {
		uint16_t x_clip_left = _displayclipx1-x; 
		w -= x_clip_left; 
		x = _displayclipx1; 
		// Now lets update pixels to the rigth offset and mask
		uint8_t x_clip_left_bytes_incr = x_clip_left / pixels_per_byte;
		pixels += x_clip_left_bytes_incr;
		row_shift_init = 8 - (x_clip_left - (x_clip_left_bytes_incr * pixels_per_byte) + 1) * bits_per_pixel; 	
	}

	if((x + w - 1) >= _displayclipx2) {
		w = _displayclipx2  - x;
	} 

	const uint8_t * pixels_row_start = pixels;  // remember our starting position offset into row

	#ifdef ENABLE_GrafxT3_FRAMEBUFFER
	if (_use_fbtft) {
		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
		for (;h>0; h--) {
			uint16_t * pfbPixel = pfbPixel_row;
			pixels = pixels_row_start;				// setup for this row
			uint8_t pixel_shift = row_shift_init;			// Setup mask

			for (int i = 0 ; i < w; i++) {
				*pfbPixel++ = palette[((*pixels)>>pixel_shift) & pixel_bit_mask];
				if (!pixel_shift) {
					pixel_shift = 8 - bits_per_pixel;	//setup next mask
					pixels++;
				} else {
					pixel_shift -= bits_per_pixel;
				}
			}
			pfbPixel_row += _width;
			pixels_row_start += count_of_bytes_per_row;
		}
		return;	

	}
	#endif

///////////////////////////////////////////////////////////////////
/*	beginSPITransaction();
	setAddr(x, y, x+w-1, y+h-1);
	writecommand_cont(GrafxT3_RAMWR);
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask

		for (int i = 0 ;i < w; i++) {
			writedata16_cont(palette[((*pixels)>>pixel_shift) & pixel_bit_mask]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
		}
		pixels_row_start += count_of_bytes_per_row;
	}
	writecommand_last(GrafxT3_NOP);
	endSPITransaction();*/
////////////////////////////////////////////////////////////////////
///for transparent pixels
	beginSPITransaction();
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask
                int16_t x_out = x; 
		for (int i = 0 ;i < w; i++) {
			uint8_t palette_index = (*pixels)>>pixel_shift) & pixel_bit_mask];
			if (palette_index != TRANSPARENT_INDEX)
				Pixel(x_out, y, palette[palette_index]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
			x_out++;
		}
		pixels_row_start += count_of_bytes_per_row;
		y++;
	}
	endSPITransaction();
}

I get these errors.....
Code:
Arduino: 1.8.2 (Windows 7), TD: 1.36, Board: "Teensy 3.6, Serial, 180 MHz, Faster, US English"

C:\Users\Duhjoker\Desktop\GameR-Iot_4bpp_splitfile\GameR-Iot_4bpp_splitfile.ino: In function 'void loop()':

C:\Users\Duhjoker\Desktop\GameR-Iot_4bpp_splitfile\GameR-Iot_4bpp_splitfile.ino:137:48: warning: large integer implicitly truncated to unsigned type [-Woverflow]

                                     palette[6] = PINK;

                                                ^

C:\Users\Duhjoker\Documents\Arduino\libraries\GameRIot_T3_only\GrafxT3.cpp: In member function 'void GrafxT3::writeRectNBPP(int16_t, int16_t, int16_t, int16_t, uint8_t, const uint8_t*, const uint16_t*)':

C:\Users\Duhjoker\Documents\Arduino\libraries\GameRIot_T3_only\GrafxT3.cpp:2927:50: error: expected ',' or ';' before ')' token

    uint8_t palette_index = (*pixels)>>pixel_shift) & pixel_bit_mask];

                                                  ^

C:\Users\Duhjoker\Documents\Arduino\libraries\GameRIot_T3_only\GrafxT3.cpp:2928:25: error: 'TRANSPARENT_INDEX' was not declared in this scope

    if (palette_index != TRANSPARENT_INDEX)

                         ^

Error compiling for board Teensy 3.6.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

I tried adding a , or a ; in the spot asked but no go. So i tried using the original stuff as reference but it just keeps me going in a circle.
 
Ok I fixed your fix to the nbpp function like so.......

Code:
beginSPITransaction();
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask
                int16_t x_out = x; 
		for (int i = 0 ;i < w; i++) {
			uint8_t palette_index = (palette[((*pixels)>>pixel_shift) & pixel_bit_mask]);
			if (palette_index != TRANSPARENT_INDEX)
				Pixel(x_out, y, palette[palette_index]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
			x_out++;
		}
		pixels_row_start += count_of_bytes_per_row;
		y++;
	}
	endSPITransaction();

But what it does is kills the tilemap completely and flashes random pixels where the player was and when moved
 
Ok guys I'm stuck. I have tried the update with and with out using the frame buffer but all it does is show some pixels at the area where the character Sprite is supposed to appear. Of course I might still be using the frame buffer wrong.

I was thinking about the barely visible flash using the new functions. I believe what is going on is that I'm stacking a full bitmap onto another bitmap and they are competing to be displayed. I believe this to be true with or with out the frame buffer.

I'm wondering if there is a way we could mask the pixels directly under the visible pixels of the player Sprite somehow so the two bitmaps aren't fighting each other to be displayed.
 
I'm sure the frame buffer code works well - haven't ever looked at it. To start if I did I would find a simple sample and understand the parameter usage expand or alter the sample - incorporate the desired methods of change in some way - using the many aforementioned debug tips to show what I tried and see how it worked.

AFAIK - Unless you know what is on the screen (memory image) or can recreate a given area - it would have to be read from the screen in order to work over it as desired to AND or OR pixels in RAM and then that work area could be written out with desired changes.

Not sure how much underlying code is in use that was imported from elsewhere? Camera this or that? If there is imported code for doing updates/write/changes - it may have inherent clearing or update methods leading to flashing or unexpected behavior.
 
I just want to be able to have invisible pixels with the writeRectbpp function/s. That's the important thing. Every thing else after that is manipulating the images to do what you want like moving characters and such. Like the camera stuff, which is on the fly programming and has no parts in the library. Every thing else that I'm using which are the buttons and the display functions come from Kurts t3n library. Plus the tilemap works great, no flashing.

Before when I was using the multiple bitmap method for tilemap and with the camera scroll the screen would flash and reload the map when either the next horizontal or vertical row entered the viewing area of the screen. Now it scrolls nicely and solid letting the next row come into view with out resetting the screen and causing flash. That's just using the 4bpp function as is plus the tilemap routine built in. But this is a seperate function with its own name.

Is there a video somewhere of the FB and clip test running. I got it to compile at point but afterwards the teensy nor the screen would activate. But later, first we need to fix the invisible pixels.


I was looking at Kurts original t3n library and noticed a variable define(?), Boolean _invisible = false; what is that for? Can it be used for what I need?
 
Sorry, right now I am busy debugging my own SPI/display issues... So can't help much... Plus it is summer :D

Have you written a set of simple tests to see if the functions are working at all? Like, draw a rectangle (fillRect) in some obvious color that your bitmap does not use, maybe a couple of pixels wider and taller than your bitmap, and then call the updated function to draw within it? Do your "Invisible" or "transparent" portions of the bitmap show the color? Are all of your pixels shown in the right region...

Sorry I don't typically do videos of things like this. At times I have done some of robots walking or...

invisible - I think is just a debug thing now... Maybe was used in earlier Pull Request to main ILI9341_t3 library that I brought in to this version.... It is simply checking to see if the clip rectangle is set such that no pixels can be drawn.

As for other ways to do transparent bits... There are many ways... Obviously the easiest way is to use the frame buffer, and then carefully draw things in the right order... Code is there, although would still require the changes I posted earlier. Another way assuming enough memory would be to develop some concept like setting up a mask to draw through. Only those bits who are set (or clear depending on how YOU implement it) would allow to draw through. You would then need functions to manipulate the mask...

Or write your own bitmap merge functions, which combine multiple bitmaps into a new one that you draw... (Sort of sounds like frame buffer...)
 
Ok maybe I'm just being dense but here's the thing. When I use the the bits per pixel functions as is with out the update you wrote. Every thing works.

I didn't make any other changes but to add the define for transparent. No changes in my sketch either. The only change was to the writeNbpp() function. When I compile using the updated code, I get a few pixels at some point on the screen.

I mean that's not right. Shouldn't it still display my images?
 
It might help to see the whole function. Note: that was a reasonably major change to the function, in that it is no longer outputting one rectangle of data, but instead outputting a pixel at a time. So could be issues for example with the pixel function. Maybe it is setup differently.
 
Code:
uint8_t palette_index = [B](palette[[/B]((*pixels)>>pixel_shift) & pixel_bit_mask]);
			if (palette_index != TRANSPARENT_INDEX)
				Pixel(x_out, y,[B] palette[[/B]palette_index]);

You are indexing into palette twice and I am fairly sure you only want to do that once. I marked the two places in bold text.
 
Yep - That would screw things up!
Code:
int8_t palette_index = ((*pixels)>>pixel_shift) & pixel_bit_mask;
			if (palette_index != TRANSPARENT_INDEX)
				Pixel(x_out, y, palette[palette_index]);
 
Thank you, thank you, thank you so much for humoring me for a moment. I had hard time getting it to compile so I used what I could find as comparison. Thank you so much for helping me fix that. You guys Rock!!!!! :cool:

Ok so now it works. Again thank you for writing that and correcting my try. All right gonna go to the gaming site and see if some one can help me with my camera controls and other gaming only needs. I can't seem to be able to get the camera to scroll but so far either way when scrolling to the right or down. they were originally written for scrolling though the Nokia 5110 LCD and not far for a simple 8 bit version of Legend of Zelda NES.
 
So which part do i need to change to use the frame buffer with the transparent pixels. Lol. Got bored and tried to use it but it gave me the black pixels instead of the transparent. If its already listed in the thread please tell me which post.
 
ok Im looking at the nbpp function and I want to add the transparent pixels part to the frame buffer part here.......

Code:
	#ifdef ENABLE_GrafxT3_FRAMEBUFFER
	if (_use_fbtft) {
		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
		for (;h>0; h--) {
			uint16_t * pfbPixel = pfbPixel_row;
			pixels = pixels_row_start;				// setup for this row
			uint8_t pixel_shift = row_shift_init;			// Setup mask

			for (int i = 0 ; i < w; i++) {
				*pfbPixel++ = palette[((*pixels)>>pixel_shift) & pixel_bit_mask];   ///////change this?
				if (!pixel_shift) {
					pixel_shift = 8 - bits_per_pixel;	//setup next mask
					pixels++;
				} else {
					pixel_shift -= bits_per_pixel;
				}
			}
			pfbPixel_row += _width;
			pixels_row_start += count_of_bytes_per_row;
		}
		return;	

	}
	#endif

or do change this part....

Code:
beginSPITransaction();
	setAddr(x, y, x+w-1, y+h-1);
	writecommand_cont(GrafxT3_RAMWR);
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask

		for (int i = 0 ;i < w; i++) {
			writedata16_cont(palette[((*pixels)>>pixel_shift) & pixel_bit_mask]); ///////////change this?
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
		}
		pixels_row_start += count_of_bytes_per_row;
	}
	writecommand_last(GrafxT3_NOP);
	endSPITransaction();
 
ok so I changed the nbpp function to this........

Code:
void GrafxT3::writeRectNBPP(int16_t x, int16_t y, int16_t w, int16_t h,  uint8_t bits_per_pixel, 
		const uint8_t *pixels, const uint16_t * palette )
{
	//Serial.printf("\nWR8: %d %d %d %d %x\n", x, y, w, h, (uint32_t)pixels);
	x+=_originx;
	y+=_originy;
	uint8_t pixels_per_byte = 8/ bits_per_pixel;
	uint16_t count_of_bytes_per_row = (w + pixels_per_byte -1)/pixels_per_byte;		// Round up to handle non multiples
	uint8_t row_shift_init = 8 - bits_per_pixel;				// We shift down 6 bits by default 
	uint8_t pixel_bit_mask = (1 << bits_per_pixel) - 1; 		// get mask to use below
	// Rectangular clipping 

	// See if the whole thing out of bounds...
	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
	if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;

	// In these cases you can not do simple clipping, as we need to synchronize the colors array with the
	// We can clip the height as when we get to the last visible we don't have to go any farther. 
	// also maybe starting y as we will advance the color array. 
	// Again assume multiple of 8 for width
 	if(y < _displayclipy1) {
 		int dy = (_displayclipy1 - y);
 		h -= dy; 
 		pixels += dy * count_of_bytes_per_row;
 		y = _displayclipy1; 	
 	}

	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;

	// For X see how many items in color array to skip at start of row and likewise end of row 
	if(x < _displayclipx1) {
		uint16_t x_clip_left = _displayclipx1-x; 
		w -= x_clip_left; 
		x = _displayclipx1; 
		// Now lets update pixels to the rigth offset and mask
		uint8_t x_clip_left_bytes_incr = x_clip_left / pixels_per_byte;
		pixels += x_clip_left_bytes_incr;
		row_shift_init = 8 - (x_clip_left - (x_clip_left_bytes_incr * pixels_per_byte) + 1) * bits_per_pixel; 	
	}

	if((x + w - 1) >= _displayclipx2) {
		w = _displayclipx2  - x;
	} 

	const uint8_t * pixels_row_start = pixels;  // remember our starting position offset into row

	#ifdef ENABLE_GrafxT3_FRAMEBUFFER
	if (_use_fbtft) {
		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
		for (;h>0; h--) {
			uint16_t * pfbPixel = pfbPixel_row;
			pixels = pixels_row_start;				// setup for this row
			uint8_t pixel_shift = row_shift_init;			// Setup mask
			for (int i = 0 ; i < w; i++) {
	//			*pfbPixel++ = palette[((*pixels)>>pixel_shift) & pixel_bit_mask];
		   uint8_t palette_index = ((*pixels)>>pixel_shift) & pixel_bit_mask;      //////////////////////changed this        
			    if (palette_index != TRANSPARENT_INDEX)                               //////////////////////and this
                *pfbPixel = palette [palette_index];
//				    Pixel(x_out, y, palette[palette_index]);
                if (!pixel_shift) {
					pixel_shift = 8 - bits_per_pixel;	//setup next mask
					pixels++;
				} else {
					pixel_shift -= bits_per_pixel;
				}
			}
			pfbPixel_row += _width;
			pixels_row_start += count_of_bytes_per_row;
		}
		return;	

	}
	#endif

///////////////////////////////////////////////////////////////////
/*	beginSPITransaction();
	setAddr(x, y, x+w-1, y+h-1);
	writecommand_cont(GrafxT3_RAMWR);
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask

		for (int i = 0 ;i < w; i++) {
			writedata16_cont(palette[((*pixels)>>pixel_shift) & pixel_bit_mask]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
		}
		pixels_row_start += count_of_bytes_per_row;
	}
	writecommand_last(GrafxT3_NOP);
	endSPITransaction();*/
////////////////////////////////////////////////////////////////////
///for transparent pixels
	beginSPITransaction();
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask
                int16_t x_out = x; 
		for (int i = 0 ;i < w; i++) {
			uint8_t palette_index = ((*pixels)>>pixel_shift) & pixel_bit_mask;
			if (palette_index != TRANSPARENT_INDEX)
				Pixel(x_out, y, palette[palette_index]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
			x_out++;
		}
		pixels_row_start += count_of_bytes_per_row;
		y++;
	}
	endSPITransaction();
}

but I think I missed something because it causes the whole screen to flash when I use it with thew frame buffer on vs using it with buffer and with out the update

Edit:::::
Fixed it so it uses transparent pixels whether you use the frameBuffer or not. Had to go back a page and re-read some stuff.
 
Last edited:
I've got a problem that so far hasn't been addressed. Kind of a weird problem at that. When ever I use the tilemap in the sketch it causes the player Sprite to walk slowly. Unbearably slowly. Which is weird because I have the sprites movements set from Normal to light speed when the tilemap is off.
 
I wonder if there is a way to adjust the rate at which the tilemap is brought into view using the camera controls. Plus hey look new camera controls.....

Code:
#include <GrafxT3.h>
#include <SPIN.h>
#include "SPI.h"
#include <Bounce.h>
#include "bitmaps.h"
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
#define TFT_DC  9
#define TFT_CS 10
#define TFT_RST 7
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
uint8_t use_fb = 0;
uint8_t use_clip_rect = 0;
uint8_t use_set_origin = 0;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
GrafxT3 tft = GrafxT3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO, &SPIN);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
 int player_x = 240;
 int player_y = 320;
 int player_direction = 2;
 int x=-0,y=0;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Camera offset
int cameraX = -160;  /////starting position X 0f camera on tilemap
int cameraY = -120;  /////starting position Y of camera on tilemap

// Camera offset boundaries
const int cameraXMin = -2352;
const int cameraXMax = 0;
const int cameraYMin = -2352;
const int cameraYMax = 0;

// Player offset boundaries
const int playerXMin = 64;
const int playerXMax = 274;
const int playerYMin = 64;
const int playerYMax = 180;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////Pixel Color Includes////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
uint16_t palette[16];  // Should probably be 256, but I don't use many colors...
uint16_t pixel_data[2500];

//Extra integers for color palette
int a = 0xa; int b = 0xb; int c = 0xc; 
int d = 0xd; int e = 0xe; int f = 0xf;

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////Button assignments//////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////dpad + select buttons
const int buttonUp = 33; //up button
Bounce ButtonUp = Bounce(buttonUp, 10);  // 10 ms debounce
const int buttonDown = 38; //down_button
Bounce ButtonDown = Bounce(buttonDown, 10);  // 10 ms debounce
const int buttonLeft = 35; //left button
Bounce ButtonLeft = Bounce(buttonLeft, 10);  // 10 ms debounce
const int buttonRight = 17; //right button
Bounce ButtonRight = Bounce(buttonRight, 10);  // 10 ms debounce
const int buttonS = 21; //select button
Bounce ButtonS = Bounce(buttonS, 10);  // 10 ms debounce

//////action + start buttons
const int buttonX = 32; // X button up
Bounce ButtonX = Bounce(buttonX, 10);  // 10 ms debounce
const int buttonY = 26; // Y button left
Bounce ButtonY = Bounce(buttonY, 10);  // 10 ms debounce
const int buttonA = 21; // A button right
Bounce ButtonA = Bounce(buttonA, 10);  // 10 ms debounce
const int buttonB = 28; // B buttun down
Bounce ButtonB = Bounce(buttonB, 10);  // 10 ms debounce
const int buttonT = 4; // Start button
Bounce ButtonT = Bounce(buttonT, 10);  // 10 ms debounce

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////Set-up//////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void setup() {
  while (!Serial && (millis() < 4000)) ;
  Serial.begin(115200);
  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(BLACK);
  //tft.setFrameRate(60);
  tft.persistence = false;
   pinMode(buttonUp, INPUT_PULLUP);
   pinMode(buttonDown, INPUT_PULLUP);
   pinMode(buttonLeft, INPUT_PULLUP);
   pinMode(buttonRight, INPUT_PULLUP);
   pinMode(buttonS, INPUT_PULLUP);
   pinMode(buttonX, INPUT_PULLUP);
   pinMode(buttonY, INPUT_PULLUP);
   pinMode(buttonA, INPUT_PULLUP);
   pinMode(buttonB, INPUT_PULLUP);
   pinMode(buttonT, INPUT_PULLUP); 
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////Loop////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void loop(void) {
   //updates the GameRIot (the display, the sound, the buttons, everyyhing)
  //returns true when it's time to render a new frame (20 times/second)
//   if(tft.updateAll()){

///////////////////////////////////////////////////////////////////////////////
////////////////////////////////camera controls////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Clamp cameraX
if(cameraX < cameraXMin)
{
  cameraX = cameraXMin;
}
else if(cameraX > cameraXMax)
{
   cameraX = cameraXMax;
}

// Clamp cameraY
if(cameraY < cameraYMin)
{
  cameraY = cameraYMin;
}
else if(cameraY > cameraYMax)
{
   cameraY = cameraYMax;  
}

// Check if player is beyond X boundary
if(player_x < playerXMin)
{
  ++cameraX;
  if(cameraX > cameraXMin && cameraX < cameraXMax)
  {
    player_x = playerXMin;
  }
}
else if(player_x > playerXMax)
{
  --cameraX;
  if(cameraX > cameraXMin && cameraX < cameraXMax)
  {
    player_x = playerXMax;
  }
}

// Check if player is beyond Y boundary
if(player_y < playerYMin)
{
  ++cameraY;
  if(cameraY > cameraYMin && cameraY < cameraYMax)
  {
    player_y = playerYMin;
  }
}
else if(player_y > playerYMax)
{
  --cameraY;
  if(cameraY > cameraYMin && cameraY < cameraYMax)
  {
    player_y = playerYMax;
  }
}
//////////////////////////////////////////////////////////////////////////////
///////////////////////////////Palette////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 palette[0] = 0;
       palette[1] = BLACK;
             palette[2] = BLUE;
                   palette[3] = BROWN;
                         palette[4] = DARKGREEN;
                              palette[5] = GREY;
                                    palette[6] = PINK;
                                          palette[7] = RED;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////                                                
                                           palette[8] = LIGHTBROWN;
                                     palette[9] = GREEN;
                               palette[a]= DARKGREY;
                         palette[b] = LIGHTGREY;
                   palette[c] = YELLOW; 
             palette[d] = PURPLE; 
       palette[e] = WHITE;
 palette[f] = NAVY;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////Tilemap/////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

tft.drawTilemap(cameraX, cameraY, dune_demo, spritesheet, palette);
 
///////////////////////////////////////////////////////////////////////////////
///////////////////////////Buttons/////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////  
       if (ButtonUp.update());
               if (ButtonDown.update());
                       if (ButtonLeft.update());
                             if (ButtonRight.update());
                                       if (ButtonA.update());
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
                                       ButtonUp.rebounce(10);
                               ButtonDown.rebounce(10);
                       ButtonLeft.rebounce(10);
            ButtonRight.rebounce(10);
     ButtonA.rebounce(10);
///////////////////////////////////////////////////////////////////////////////
//////////////////////////Down/////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
 if (ButtonUp.fallingEdge()){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rearwalk,palette);

                   player_direction = 1;
             player_y = player_y - 5;
             if(checkcolision())player_y--;} 
           if(player_y <= 16){
              player_y = 16;}
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////Up///////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 if (ButtonDown.fallingEdge()){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_frontwalk,palette);

               player_direction = 2;
            player_y = player_y + 5;
            if(checkcolision())player_y++;}
            if(player_y >= 216){
              player_y = 216;}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////Left////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 if (ButtonLeft.fallingEdge()){
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_leftwalk,palette);

                player_direction = 3;
             player_x = player_x - 5;
             if(checkcolision())player_x--;}  
            if(player_x >= 288){
              player_x = 288;}
//////////////////////////////////////////////////////////////////////////////
////////////////////////////Right////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
if (ButtonRight.fallingEdge()){
  tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rightwalk,palette);
  
               player_direction = 4;
            player_x = player_x + 5;
           if(checkcolision())player_x++;}
            if(player_x <= 16){
              player_x = 16;}
///////////////////////////////////////////////////////////////////////////////     
//////////////////////////////PLAYER DIRECTION/////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
if (player_direction == 1){
  tft.writeRectNBPP(player_x, player_y,16,16,4,paul_rear,palette);
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
else if (player_direction == 2){
   tft.useFrameBuffer(use_fb);
   uint32_t start_time = millis();
   tft.writeRectNBPP(player_x, player_y,16,16,4,paul_front,palette);
    tft.updateScreen();
    use_fb = !use_fb;
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
else if (player_direction == 3){
    tft.writeRectNBPP(player_x, player_y,16,16,4,paul_left,palette);
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
else if (player_direction == 4){
     tft.writeRectNBPP(player_x, player_y,16,16,4,paul_right,palette);
        }
     }
//}
/////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////collision/////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
   bool checkcolision() { // Transformed it into a function 
    uint16_t i;  
     for(i=0; i < tft.numcolision + 1; i++)
   {
    if(tft.collideRectRect(player_x, player_y,16,16,tft.solid[i].x,tft.solid[i].y,16,16))
{
     if(tft.solid[i].spritecol == blank_tile)return false; //Do nothing because it's floor - this line not needed
else if(tft.solid[i].spritecol == cave)return true;
else if(tft.solid[i].spritecol == grass)return false;
else if(tft.solid[i].spritecol == grassbl)return false;
else if(tft.solid[i].spritecol == grassbr)return false;
else if(tft.solid[i].spritecol == grasstl)return false;
else if(tft.solid[i].spritecol == grasstr)return false;
else if(tft.solid[i].spritecol == rock){tft.popup(F(" ""Rock"" "),1); return true;} //Return True if character have touched the wall
else if(tft.solid[i].spritecol == rockbl)return true;
else if(tft.solid[i].spritecol == rockbr)return true;
else if(tft.solid[i].spritecol == rocktl)return true;
else if(tft.solid[i].spritecol == rocktr)return true;
else if(tft.solid[i].spritecol == sand)return false;
else if(tft.solid[i].spritecol == seitch)return true;
else if(tft.solid[i].spritecol == stairsl)return true;
else if(tft.solid[i].spritecol == stairsr)return true;
else if(tft.solid[i].spritecol == tree)return false;
   }
}
return false; // Return false if don't touch anything
}
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////

also you can see where I am using the frame buffer for player direction one. The frame buffer is turned on and being used like in the fb and clip test, and it is doing something obviously since the update to the frame buffer added the transparent pixels. But its still having trouble making the player sprite solid. he just flashes but slow.

https://youtu.be/wUg7WPhM8KA
 
Last edited:
To me the video shows you are writing out the complete background image. This erases your sprite character from the screen.
It looks like this is a very slow process and you could check that with an elapsedMillis object and some prints to the serial.

What I think you should be aiming at is to only print out the complete background when your camera controls scroll the screen. Now you will have the problem of the Tron trails back. What to do about those?

I don't know if KurtE's framebuffer can have a user set size. But lets pretend it can. Last I remember your sprite was 16 by 16 and you are moving the sprite by 5 pixels each update. So picture a 25 by 25 memory buffer where your sprite is in the middle. You calculate the location of your sprite on the background. You write just that portion of the background to the 25 by 25 memory buffer. You then write your sprite on top of that using your transparent pixels. You then write that 25 by 25 buffer to the screen.

That is 25 x 25 writes to the screen which is 625 writes compared to 240 by 320 which is 76,800 writes. That should be a whole lot faster and flicker free.

Basically this is simplified dirty rectangles and you can do a google search and read about it.

Edit. You should have at least 26 by 26 instead of 25 by 25 but the general idea is the same.
 
Status
Not open for further replies.
Back
Top