For those using UTFT, there is a pretty annoying bug in the library!

I have emailed the author about it but no response.

In the file (HW_ATmega1280.h) the function:

void UTFT::_fast_fill_16(int ch, int cl, long pix)

Has a bug in it that it doesnt correctly write the correct number of bytes to the display.

This has the side effect of screwing with the fill circle function plus anything else that uses the fast_fill function, like fill screen, filled rectangles etc. The symptom is on the LEFT hand side of circles and some rectangles there are missing pixels.

To fix it:

replace

if ((pix % 16) != 0)
		for (int i=0; i<(pix % 16)+1; i++)
		{
			pulse_low(P_WR, B_WR);
		}

with this:

		for (byte i=0; i<=(byte)(pix-(blocks*16)); i++)
		{
			pulse_low(P_WR, B_WR);
		}

This piece of code is probably floating around a lot of the files, but in my case im using a mega 2560 and this piece of code in that H file is what caused my issue.

I have also been fiddling around with the UTFT library, putting some sanity checks in, and i also included the adafruit copy of a filltriangle into the lib that's a bit better and faster then the UTFT version in UTFT_Geom. This way you can get filltri in the standard UTFT library without having to include the Geom lib as well.

You could probably do a text search and replace to fix that fast fill issue.

I also modified the fill circle routine in UTFT.cpp to make the filled circle a tiny bit more accurate:

replace:
if(x1x1+y1y1 <= radius*radius)

with
if(x1x1+y1y1 <= radius*radius+radius)

This makes the fill circle outline much closer to the standard circle routine.

That does not look correct to me.

Let's say that you want to fastFill 25 pixels.
This would be one complete block of 16. Plus 9 pixels in the tidy-up loop.

The original does (25 % 16) + 1 = 9 + 1 = 10 loops.
Your solution does (25 - 16) + 1 = 10 loops.

I have never looked at the high level code. I can't believe that UTFT would have been filling the wrong number of pixels in all these years.

Of course any whole number of blocks will be written correctly.

With most libraries, the fill "window" would be set in hardware. So writing an extra pixel would never be noticed. However UTFT does everything in software. There is nothing to stop the extra pixel being displayed.

David.

Edit. Yes, UTFT does set the Window with setxy()

Its bizarre, but i can show you photo proof of it. I dont know WHY it does it, but i do know its on a byte/word border when it "skips" a pixel.

I have corrected my erroneous assertion in #1.

Perhaps you can write a sketch that demonstrates the wrong behaviour.

As far as I can see, drawHLine() and drawVLine() are both wrong. fillRect() is correct.

With drawHLine, drawVLine the window is set one too big. (in my UTFT v2.8.1)
So if the length is not a multiple of 16, an extra pixel is drawn into the extra-sized window.

This has intrigued me. I will have to write a test sketch for myself.

UTFT methods have never seemed very intuitive to me. e.g. defining a rectangle by its bounding coordinates instead of XY position and WH dimensions.
This makes programming rather difficult. You have to perform the maths instead of the library. Likewise, defining screen dimensions as 239x319 instead of the actual 240x320.

David.

#include <UTFT.h>


#define F_LARGE 2
#define F_SMALL 1
#define F_SEG 3

extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];

UTFT myGLCD(ITDB43, 38, 39, 40, 41);

/********************************************************************************************************************************************************************************************************/
void setup(void) {
  myGLCD.InitLCD();
  myGLCD.setFont(BigFont);
  myGLCD.clrScr();
}

/********************************************************************************************************************************************************************************************************/
void gText(char *v, int x, int y, byte onr, byte ong, byte onb, byte offr, byte offg, byte offb) {
  // gText( 10, 10 , "this is a test", onR, onG, onB, off R, offG, off B)

  myGLCD.setColor(onr, ong, onb);
  myGLCD.setBackColor(offr, offg, offb);
  myGLCD.print(v, x, y);
}
/********************************************************************************************************************************************************************************************************/
void gCircle(int x, int y, int r, byte onr, byte ong, byte onb ) {
  myGLCD.setColor(onr, ong, onb);
  myGLCD.drawCircle(x, y, r);
}
/********************************************************************************************************************************************************************************************************/
void gFCircle(int x0, int y0, int r, byte onr, byte ong, byte onb ) {
  myGLCD.setColor(onr, ong, onb);
  myGLCD.fillCircle(x0, y0, r);
  // myGLCD.drawCircle(x0, y0, r); // hack for broken filled circle in UTFT.
}

/********************************************************************************************************************************************************************************************************/
void gFont(byte fs)
{
  if (fs == F_SMALL) { myGLCD.setFont(SmallFont); return; }
  if (fs == F_LARGE) { myGLCD.setFont(BigFont);  return; }
  if (fs == F_SEG) { myGLCD.setFont(SevenSegNumFont);  return;   }
}

/********************************************************************************************************************************************************************************************************/
void loop(void) {
  gFont(F_SMALL);

  gCircle(201, 100, 80, 0, 255, 0);
  gFCircle(201, 100, 80, 128, 128, 128);
  gText("UTFT Fill. Green pixels are pixels MISSED", 100, 200, 255, 255, 255, 0, 0, 0);

  // green pixels are the pixels missed by the UTFT fast_fill function.
  while (1);
}

/********************************************************************************************************************************************************************************************************/

It shows the missing pixels.

Replace the fastfill with my fix, and the green pixels dissapear, and the filled circle becomes identical to the bresenham midpoint version.

this fast_fill_16 affects nearly every function too, rectangles, lines, circles, filled rectangles and circles and even the triangle fill function.

And yea, some of the functions and conventions in UTFT seem a bit weird, which is why im going through the entire library and rewriting/optimising some functions.

I also plan on modifying it for mega only, as the UNO shield even though it works, is totally useless as it uses every single pin on the arduino uno and nearly fills the flash up as well. Its a total waste.

There are also issues with the fonts, especially rotated fonts which are so incredibly slow because it uses sin/cos directly in the inner loop.

This issue is not new. Many, including myself have reached out to Henning and asked if he could fix these bugs. His response was he doesn't feel the need to fix them as not enought people have complained about them. I'm not sure how many it will take but he is fully aware of the bugs.

My guess is, he just doesn't care anymore as there are many fake versions of his library going around, one of them is bound to have fixed these issues.

To all the people that wish to malign Henning, consider where you would be without him! :frowning:

No UTFT is not the best or fastest, but it works for a lot of people with many different display models!

I for one appreciate his efforts, and only recently UTFT has undergone an upgrade to include IL9341_16bit (V2.82) as a result of my own and CTE contributions, you are all welcome!!! You are just a bunch of moaning whatsists!!

G

rainwulf:

It shows the missing pixels.

Replace the fastfill with my fix, and the green pixels dissapear, and the filled circle becomes identical to the bresenham midpoint version.

this fast_fill_16 affects nearly every function too, rectangles, lines, circles, filled rectangles and circles and even the triangle fill function.

And yea, some of the functions and conventions in UTFT seem a bit weird, which is why im going through the entire library and rewriting/optimising some functions.

I also plan on modifying it for mega only, as the UNO shield even though it works, is totally useless as it uses every single pin on the arduino uno and nearly fills the flash up as well. Its a total waste.

There are also issues with the fonts, especially rotated fonts which are so incredibly slow because it uses sin/cos directly in the inner loop.

I for one won't be losing any sleep over it :stuck_out_tongue:
G

Well I'm happy to pass the fix onto him. I emailed him about it but be never responded. The only other version of UTFT i have seen is sainsmarts version and they seem to think it's theirs.

I could post the other fixes if anyone wants them. I don't want to take this as my own work, just fixing a bug. This fast fill bugfix probably fixes a lot of bugs in the entire lib. I know I don't have the expertise to make a an entirely new Sd1693 Lib and I will happily pass on the fixes for anyone who wants them.

My primary aim is to both reduce the memory usage and increase the stability of the library as sometimes things don't do what you expect. Like the text function if you write over the end of the screen!

UTFT is excellent for getting a controller up and running on a new microcontroller or a new display.

Its main selling point is the UNIVERSAL part of its name. It has never claimed to be fast or efficient.

If you are using an established controller you will find that the finished project is easier to implement with an efficient library. (with more intuitive methods)

Yes, there are many "features" in the Henning code. If you want to correct them, it is fairly easy to do. You can offer them to RinkyDink and they might be accepted or rejected. I suspect that he would want to maintain the original structure. And if you correct features that people have already worked around, you upset them.

I have a great sympathy for Henning. It is an excellent library but it has been hacked by everyone. The hacked versions make no explanation or distinction from the official Release.

There is a similar situation with the Adafruit_TFTLCD library.

David.

Are there other SD1639 libraries? All i have ever found is the UTFT ones.

I am happy to pass these changes to Henning, but i bet he probably rues the day he made UTFT heh

I support SSD1963 with 800x480. If you do a simple mod on the CTE Due Adapter Shield, you can read the GRAM memory too.

I would guess that there are several other libraries that support SSD1963.

I do not know the SD1639. If you provide a datasheet, I could support it.

David.

Sorry David, i meant SSD1963. I always get the numbers backwards.

You have a lib for it? When i googled for SSD1963 libs all i get are UTFT or its poor hacked up brethren cough sainsmart

MCUFRIEND_kbv supports the SSD1963.

Since your display is probably write-only, you have to configure for your Adapter shield.

#define USE_SPECIAL in mcufriend_shield.h
#define USE_MEGA_16BIT_SHIELD in mcufriend_special.h

tft.begin(0x1963) in your sketch.

David.

Thanks David! I will give that a go. I think my shield is read and write, its an elecfreak shield.
http://www.elecfreaks.com/estore/lcd-tft01-arduino-mega-shield-v2-0-shd10.html

I'm guessing that some of this is "off by one" pixel issues related to how the code views pixel line drawing and filling.
Believe it or not there are multiple ways of how to draw pixels that are incompatible with each other.

Different parts of the library code may not all be making the same assumptions as to how it works.

For example there can be different rendering concepts:

  • draw a line of N pixels starting here
  • draw a line from here N more pixels.
  • using a more complex coordinate system methodology which allows composting & aliasing but where the # of pixels draw can vary from N-1 to N+1 depending on base coordinate and the graphic operation.

Believe it or not that 3rd model was/is used by the java awt 2d graphics library:
https://docs.oracle.com/javase/8/docs/api/java/awt/Graphics2D.html

In its model, the coordinates do not map to actual pixels but rather define points in-between the actual pixels.
While this sounds a bit bizarre it does make some things work better/easier when using colored pixels and doing more complex composting, especially when using variable width "pens" for wider than single pixel line drawing.

However, that methodology comes with some very odd (and confusing) quirks in my opinion that can make it difficult to use and prone to errors.
You can and end up with things that don't seem correct like the number of pixels you specify to a fill routine will be different (off by one) than the number of pixels you specify in drawing a line or a shape.

Java AWT considers those to be "normal".

Much of the benefit of rendering this way is not needed in a monochrome pixel LCD environment.

Originally the glcd library (now openGLCD) modeled its API after Java awt API functions and followed its rendering model.
It makes doing higher complex rendering functions that involve fills difficult as they must be aware of these things and account for them.
It is also a total pain to deal with the application (sketch) code as it also has to these differences into account.

When I created openGLCD I got tired of the differences since it didn't really buy anything and often created issues and complaints about it - since it often looks like bugs.
So I changed the openGLCD API & code to always be consistent and use a model of

  • draw starting here, and the line length or fill size is EXACTLY the number of pixels you specify.

It makes things so much simpler and less error prone.

So now in openGLCD, lines, shapes, and shape fills all use the same pixel length parameter and can start on the same coordinate.
i.e. you can draw a shape or fill it using the same coordinate and length/size parameters.
Whereas previously if you wanted to draw a shape like a square and fill it not only was the length of the sides you had to specify different but the starting coordinate must be different as well.
This seemingly inconsistent way of specifying things is the way Java awt works!

Also of note, is that circles can be particularly tough. And I have seem issues in code and libraries with respect to drawing and filling circles that create incorrect fills with "garbage pixels" like what was shown in the photos.
These are definitely bugs in the code.

There are also several ways to handle circles.
Most routines specifying a center coordinate then a radius but then what is the radius?
and how is it counted? Does it include the center coordinate or does it extend "radius" pixels from the
center.
i.e. if you specify a radius of 10, is the diameter 19, 20, or 21 pixels? (I've seen all three ways).
This all gets even further complicated if using the java awt coordinate and rendering methodology.
Ironically, in java awt, they did not follow their own coordinate and pixel rendering model for circles and circle fills.
This always bothered me as it seemed like they punted and didn't bother to go in and fix the circle code which made it inconsistent with everything else.

If a library is using the awt coordinate and rendering model, changing this something else to be fully consistent is not as easy as it might sound as it can involve going in and fixing all the higher level API functions and potentially even modifying or tweaking some fill code right in the middle of some Bresenham calculations.

And then lots of existing code that was created the existing methodology will suddenly have rendering issues.

i.e. there is lots of public domain rendering routines that draw shapes or does fills that often use some very clever integer calculations that make assumptions or take into consideration how the lower level line drawing filling works that often have to be tweaked depending on how it may actually work in the library.
i.e. they may assume a awt model or they may not.
The code must be adapted to the way library works and the only way to fix it is by modifying the code at the upper level (the high level function) not the lower levels.

I spent many weeks converting over all the openGLCD functions getting all code to not have rendering issues like what can be seen in the photos posted and updating all the documentation. It is not fun work. I spent lots of time with a magnifying glass looking at pixels during the process.

--- bill

Adafruit_GFX seems to have reasonable geometry. It seems intuitive to use XY and WH.. Or in the case of an ellipse, XY for the centre and HR, VR i.e. horizontal radius, vertical radius. How you round the individual coordinates of the circumference is open for debate. I would choose rounding rather than truncation. Hey-ho, get your gloves out.

UTFT seems to have unusual geometry. All the same, it has been around for several years. No one has made a big deal of the "features". You can fix them if you want. I suspect that Henning would be amenable if you are tactful.

The main idea behind UTFT is to be Universal. It is certainly easy to add a new hardware interface or new controller.

David.

I modified my own personal version of UTFT to be pixel perfect to Adafruit_GFX. That way i can predict whats going to appear on the screen when i code it.

Some of the graphics im drawing on my current project look way off even with a single pixel error.

In this case there is actually an error with fast_fill_16 in that its not predictable and throws single pixel errors on every single call to that function.

Simply replacing those 3 lines has draw circle, and draw FILLED circle now pixel perfect.

Hey guys

I'm doing it in that way:

    int32_t pix = dx * dy;         // total number of pixel
    int16_t i, j, k;               //
    j = pix >> 4;                  // number of blocks, division by 16,
    k = pix - ((int32_t)j << 4);   // remaining pixel
    Screen_SetTargetRect ( &rect );
    clrPin (_CS);
    setPin (_RS);
    REG_PIOC_ODSR = FastColor;
    for (i = 0; i < j; i++) {
       invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);
       invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);
       invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);
       invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);invertPulse(_WR);
    }
    if ( k > 0 )
       for (i = 0; i < k; i++)
          invertPulse(_WR);

"Screen_SetTargetRect" derived from "setXY"
"invertPulse(_WR)" is a macro replacing "pulse_low(P_WR, B_WR)" and looks like:

#define clrPin(pin)   *(pin.reg) &= ~pin.msk        // delete a digital output
#define setPin(pin)   *(pin.reg) |=  pin.msk        // set a digital output to 1

#define invertPulse(pin) clrPin(pin);setPin(pin)    // Pulsfolge 0/1 -> 0 -> 1

BTW the UTFT-code mentioned in the opening post is not changed up to now. I downloaded it 4 weeks ago from rinky dink...