• Welcome to Andy's Workshop Forums. Please login or sign up.
 
December 14, 2019, 09:53:20 pm

News:

SMF - Just Installed!


Reading a pixel using the GraphicsLibrary

Started by Kyle D. Williams, May 03, 2015, 01:09:32 pm

Previous topic - Next topic

Kyle D. Williams

May 03, 2015, 01:09:32 pm Last Edit: May 03, 2015, 02:59:16 pm by Kyle D. Williams
Can I ask the proper way to read a pixel using the Graphics Library I have attempted with variable success to use the FSMC accessmode(Fsmc16BitAccessMode<FsmcBank1NorSram1>) to read a pixel directly from the screen for image processing. The idea is to apply so form of image effect / filter over the whole screen. It's difficult to alter jpeg images and since a full bitmap cannot be loaded into a buffer without exceeding memory the next best thing is to alter what is already on screen. A basic example would simply be to read a row from the screen and write it back to the screen. Writing pixels to the screen seems easy enough; but I have had variable success with reading the pixels from the screen correctly, if that is what I am evening doing at all.

EDIT: I'm assuming the 16 bit data returned from readData is in the form RGB, 565, It would be even greater if I had random access to the pixels already drawn on the screen though i'm aware that something like that is probably not possible


//Simple Grayscale
                uint16_t *buffer;
uint32_t widthSize;

//Move cursor to origin
_gl->moveTo(_gl->getFullScreenRectangle());
buffer = new uint16_t[_gl->getWidth() * 2];
widthSize = _gl->getWidth(); //Set proper width
_gl->beginWriting();

for (uint16_t vpos = 0; vpos < _gl->getHeight(); vpos++) {

for (uint16_t hpos = 0; hpos < widthSize; hpos++) {
//buffer[hpos] = _accessMode->readData() & 0xF800;//Red
//buffer[hpos] = _accessMode->readData() & 0x7E0;//Green
//buffer[hpos] = _accessMode->readData() & 0x1F;//Blue
UnpackedColour colour{_accessMode->readData()};
uint8_t Red   = (colour.packed565 & 0xF800)>>(5+6);
uint8_t Green = (colour.packed565 & 0x7E0)>>(5);
uint8_t Blue  = (colour.packed565 & 0x1F);

float temp = (0.2126*Red) + (0.7152*Green) + (0.0722*Blue);
//temp /= 3;
Red = Green = Blue = (uint8_t)temp;
_gl->unpackColour(Red,Green,Blue,colour);
buffer[hpos]=colour.packed565;
}

// draw it
_gl->rawTransfer(buffer, _gl->getWidth());
}

Andy Brown

Reading pixels from these types of controllers is possible but it's very much slower than writing. Firstly you'll need to look up the read-cycle timings in the controller's datasheet for the i80 bus. For example the popular ILI9325 has a minimum write cycle of 100ns and a minimum read cycle of 300ns.

Next you need to declare the LCD to have different FSMC timings in its constructor. For example you might change this:


      Fsmc8080LcdTiming fsmcTiming(0,2);

      // set up the FSMC with RS=A16 (PD11)

      _accessMode=new LcdAccessMode(fsmcTiming,16,pe[1]);


to this (fictitious timings):


      Fsmc8080LcdTiming fsmcReadTiming(0,4);
      Fsmc8080LcdTiming fsmcWriteTiming(0,2);

      // set up the FSMC with RS=A16 (PD11)

      _accessMode=new LcdAccessMode(fsmcReadTiming,fsmcWriteTiming,16,pe[1]);


You can then use any of the 'move' methods in the LCD's implementation class to set the display window and then use the readData method to sequentially read back pixels from the window.

Note that some controllers waste even more cycles by requiring a dummy read before you get back the real data - check the datasheet for details. If you get the timing wrong then the data will appear corrupted.

Since this is a raw data read then if the LCD is in 16-bit mode you'll get back one complete pixel per read. If the panel is in 18 or 24 bit mode then more reads will be required to get back a complete pixel. The datasheet for the controller will tell you what the pixel packing format is.
It's worse than that, it's physics Jim!

Kyle D. Williams

May 04, 2015, 06:56:19 am #2 Last Edit: May 04, 2015, 06:57:54 am by Kyle D. Williams
Okay great so its slower but I can access the pixels in random which can be useful. I know for the Hx8352a the minimum write cycle is 100ns and the minimum read cycle when read from frame memory is 250ns. Reviewing previous code in the repository I realize that they are separated by board mainly the Stm32F4 and Stm32F1. Focusing on the Stm32F4 I realize the timings do not correlate with anything notable namely in the ili9327 example and the hx8352a example the Fsmc8080LcdTiming constructor has the values fsmcTiming(4,10) and fsmcTiming(3,6) respectably. I know the constructor asks for the addressSetup and the dataSetup, but i am not sure how this correlates with the timing.

Andy Brown

The calculations to derive the address/data setup timings are given in AN2790 but to be honest I usually just guess and then tweak the values up and down if I get it wrong. My logic analyser is invaluable when doing this.
It's worse than that, it's physics Jim!

Kyle D. Williams

May 05, 2015, 11:23:34 pm #4 Last Edit: May 06, 2015, 05:56:35 am by Kyle D. Williams
Lol I see, since I've been having a difficult timing with the timing for some odd reason my ADDSET is always larger than my DATAST and at times one of the values are negative. Its quite odd to calculate for these values on the board, for without the correct values the screen is again blank. I can however get it to work by using your default values and copying them for both read and right however that seems like the incorrect approach as well. Currently I recieve 14, and 5 for read and 11 and 9 for write in respect to ADDST and DATAST. Any suggestions?

Also

So far I understand that I must move the cursor and always call beginWriting before i read or right the data. Out of curiosity can I ask the reason you prioritize  the higher bits in unpack colour using a mask. its not a real issue I can just shift the bits up the appropriate amount; however, I am was curious of the the reason of this method of colour parsing

This is my implementation that should simply greyscale whatever is on the screen; however, it instead shows an odd saturation, do you have any suggestions?


//Move cursor to origin
_gl->moveTo(_gl->getFullScreenRectangle());

//Lets try a simply getting the pixels and drawing them back on screen
uint16_t *buffer;
// allocate space for even scan lines and odd scan lines
buffer = new uint16_t[_gl->getWidth() * 2];
for (uint16_t vpos = p.Y; vpos < _gl->getHeight(); vpos++) {
//Move Cursor into position for proper reading
_gl->moveTo(p.X+1,vpos,_gl->getWidth(),vpos);
_gl->beginWriting();
for (uint16_t hpos = p.X+1; hpos < _gl->getWidth(); hpos++) {
UnpackedColour colour{_accessMode->readData()};
uint8_t Red   = (colour.packed565>>(11))&0x1F;
uint8_t Green = (colour.packed565>>5)&0x3F;
uint8_t Blue  = colour.packed565 & 0x1F;
//float temp = ((0.2126*Red) + (0.7152*Green) + (0.0722*Blue))/3;
float temp = (Red+Green+Blue)/3;
Red = temp;Green = temp;Blue = (uint8_t)temp;

_gl->unpackColour(Red<<3,Green<<2,Blue<<3,colour);
buffer[hpos]=colour.packed565;
}
//Need to move cursor back to begining of row for writing
// draw it
_gl->moveTo(0,vpos,_gl->getWidth(),vpos);
_gl->beginWriting();
_gl->rawTransfer(buffer, _gl->getWidth());
}


EDIT:
I've actually realized it was an issue with me shifting the bits into place by 565 they have to all be shifted evenly, by either 2 or 3 both works, for the correct value. Can I ask why  unpackColour masks only the top bits and ignores the bottom ones I see it used in Jpeg Decoder but unsure of the reasoning.