Skip to content
Shai Almog edited this page Oct 12, 2018 · 2 revisions

Graphics, Drawing, Images & Fonts

Warning
Drawing is considered a low level API that might introduce some platform fragmentation.

Basics - Where & How Do I Draw Manually?

The Graphics class is responsible for drawing basics, shapes, images and text, it is never instantiated by the developer and is always passed on by the Codename One API.

You can gain access to a Graphics using one of the following methods:

  • Derive Component or a subclass of Component - within Component there are several methods that allow developers to modify the drawing behavior. These can be overridden to change the way the component is drawn:

    • paint(Graphics) - invoked to draw the component, this can be overridden to draw the component from scratch.

    • paintBackground(Graphics) / paintBackgrounds(Graphics) - these allow overriding the way the component background is painted although you would probably be better off implementing a painter (see below).

    • paintBorder(Graphics) - allows overriding the process of drawing a border, notice that border drawing might differ based on the style of the component.

    • paintComponent(Graphics) - allows painting only the components contents while leaving the default paint behavior to the style.

    • paintScrollbars(Graphics), paintScrollbarX(Graphics), paintScrollbarY(Graphics) allows overriding the behavior of scrollbar painting.

  • Implement the painter interface, this interface can be used as a GlassPane or a background painter.

    The painter interface is a simple interface that includes 1 paint method, this is a useful way to allow developers to perform custom painting without subclassing Component. Painters can be chained together to create elaborate paint behavior by using the PainterChain class.

    • Glass pane - a glass pane allows developers to paint on top of the form painting. This allows an overlay effect on top of a form.

      For a novice it might seem that a glass pane is similar to overriding the Form’s paint method and drawing after super.paint(g) completed. This isn’t the case. When a component repaints (by invoking the repaint() method) only that component is drawn and Form’s paint() method wouldn’t be invoked. However, the glass pane painter is invoked for such cases and would work exactly as expected.

      Container has a glass pane method called paintGlass(Graphics), which can be overridden to provide a similar effect on a Container level. This is especially useful for complex containers such as Table which draws its lines using such a methodology.

    • Background painter - the background painter is installed via the style, by default Codename installs a custom background painter of its own. Installing a custom painter allows a developer to completely define how the background of the component is drawn.
      Notice that a lot of the background style behaviors can be achieved using styles alone.

Important
A common mistake developers make is overriding the paint(Graphics) method of Form. The problem with that is that form has child components which might request a repaint. To prevent that either place a paintable component in the center of the Form, override the glasspane or the background painter.

A paint method can be implemented as such:

// hide the title
Form hi = new Form("", new BorderLayout());
hi.add(BorderLayout.CENTER, new Component() {
    @Override
    public void paint(Graphics g) {
        // red color
        g.setColor(0xff0000);

        // paint the screen in red
        g.fillRect(getX(), getY(), getWidth(), getHeight());

        // draw hi world in white text at the top left corner of the screen
        g.setColor(0xffffff);
        g.drawString("Hi World", getX(), getY());
    }
});
hi.show();
Hi world demo code
Figure 1. Hi world demo code, notice that the blue bar on top is the iOS7+ status bar

Glass Pane

The GlassPane `in Codename One is inspired by the Swing `GlassPane & LayeredPane with quite a few twists. We tried to imagine how Swing developers would have implemented the glass pane knowing what they do now about painters and Swings learning curve. But first: what is the glass pane?

A typical Codename One application is essentially composed of 3 layers (this is a gross simplification though), the background painters are responsible for drawing the background of all components including the main form. The component draws its own content (which might overrule the painter) and the glass pane paints last…​

Form layout graphic
Figure 2. Form layout graphic

Essentially the glass pane is a painter that allows us to draw an overlay on top of the Codename One application.

Overriding the paint method of a form isn’t a substitute for glasspane as it would appear to work initially, when you enter a Form. However, when modifying an element within the form only that element gets repainted not the entire Form!

So if we have a form with a Button and text drawn on top using the Form’s paint method it would get erased whenever the button gets focus.

The glass pane is called whenever a component gets painted, it only paints within the clipping region of the component hence it won’t break the rest of the components on the Form which weren’t modified.

You can set a painter on a form using code like this:

hi.setGlassPane(new Painter() {
    @Override
    public void paint(Graphics g, Rectangle rect) {
    }
});

Or you can use Java 8 lambdas to tighten the code a bit:

hi.setGlassPane((g, rect) -> {
});

PainterChain allows us to chain several painters together to perform different logical tasks such as a validation painter coupled with a fade out painter. The sample below shows a crude validation panel that allows us to draw error icons next to components while exceeding their physical bounds as is common in many user interfaces

Form hi = new Form("Glass Pane", new BoxLayout(BoxLayout.Y_AXIS));
Style s = UIManager.getInstance().getComponentStyle("Label");
s.setFgColor(0xff0000);
s.setBgTransparency(0);
Image warningImage = FontImage.createMaterial(FontImage.MATERIAL_WARNING, s).toImage();
TextField tf1 = new TextField("My Field");
tf1.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS);
tf1.getAllStyles().setMargin(5, 5, 5, 5);
hi.add(tf1);
hi.setGlassPane((g, rect) -> {
    int x = tf1.getAbsoluteX() + tf1.getWidth();
    int y = tf1.getAbsoluteY();
    x -= warningImage.getWidth() / 2;
    y += (tf1.getHeight() / 2 - warningImage.getHeight() / 2);
    g.drawImage(warningImage, x, y);
});
hi.show();
The glass pane draws the warning sign on the border of the component partially peeking out
Figure 3. The glass pane draws the warning sign on the border of the component partially peeking out

Shapes & Transforms

The graphics API provides a high performance shape API that allows drawing arbitrary shapes by defining paths and curves and caching the shape drawn in the GPU.

Device Support

Shapes and transforms are available on most smartphone platforms with some caveats for the current Windows Phone port.

Notice that perspective transform is missing from the desktop/simulator port. Unfortunately there is no real equivalent to perspective transform in JavaSE that we could use.

A 2D Drawing App

We can demonstrate shape drawing with a simple example of a drawing app, that allows the user to tap the screen to draw a contour picture.

The app works by simply keeping a GeneralPath in memory, and continually adding points as bezier curves. Whenever a point is added, the path is redrawn to the screen.

The center of the app is the DrawingCanvas class, which extends Component.

public class DrawingCanvas extends Component {
    GeneralPath p = new GeneralPath();
    int strokeColor = 0x0000ff;
    int strokeWidth = 10;

    public void addPoint(float x, float y){
        // To be written
    }

    @Override
    protected void paintBackground(Graphics g) {
        super.paintBackground(g);
            Stroke stroke = new Stroke(
                strokeWidth,
                Stroke.CAP_BUTT,
                Stroke.JOIN_ROUND, 1f
            );
            g.setColor(strokeColor);

            // Draw the shape
            g.drawShape(p, stroke);

    }

    @Override
    public void pointerPressed(int x, int y) {
        addPoint(x-getParent().getAbsoluteX(), y-getParent().getAbsoluteY());
    }
}

Conceptually this is very basic component. We will be overriding the paintBackground() method to draw the path. We keep a reference to a GeneralPath object (which is the concrete implementation of the Shape interface in Codename One) to store each successive point in the drawing. We also parametrize the stroke width and color.

The implementation of the paintBackground() method (shown above) should be fairly straight forward. It creates a stroke of the appropriate width, and sets the color on the graphics context. Then it calls drawShape() to render the path of points.

Implementing addPoint()

The addPoint method is designed to allow us to add points to the drawing. A simple implementation that uses straight lines rather than curves might look like this:

private float lastX = -1;
private float lastY = -1;

public void addPoint(float x, float y) {
    if (lastX == -1) {
        // this is the first point... don't draw a line yet
        p.moveTo(x, y);
    } else {
        p.lineTo(x, y);
    }
    lastX = x;
    lastY = y;

    repaint();
}

We introduced a couple house-keeping member vars (lastX and lastY) to store the last point that was added so that we know whether this is the first tap or a subsequent tap. The first tap triggers a moveTo() call, whereas subsequent taps trigger lineTo() calls, which draw lines from the last point to the current point.

A drawing might look like this:

lineTo example
Figure 4. lineTo example

Using Bezier Curves

Our previous implementation of addPoint() used lines for each segment of the drawing. Let’s make an adjustment to allow for smoother edges by using quadratic curves instead of lines.

Codename One’s GeneralPath class includes two methods for drawing curves:

  1. quadTo() : Appends a quadratic bezier curve. It takes 2 points: a control point, and an end point.

  2. curveTo() : Appends a cubic bezier curve, taking 3 points: 2 control points, and an end point.

See the General Path javadocs for the full API.

We will make use of the quadTo() method to append curves to the drawing as follows:

private boolean odd=true;
public void addPoint(float x, float y){
    if ( lastX == -1 ){
        p.moveTo(x, y);

    } else {
        float controlX = odd ? lastX : x;
        float controlY = odd ? y : lastY;
        p.quadTo(controlX, controlY, x, y);
    }
    odd = !odd;
    lastX = x;
    lastY = y;
    repaint();
}

This change should be fairly straight forward except, perhaps, the business with the odd variable. Since quadratic curves require two points (in addition to the implied starting point), we can’t simply take the last tap point and the current tap point. We need a point between them to act as a control point. This is where we get the curve from. The control point works by exerting a sort of "gravity" on the line segment, to pull the line towards it. This results in the line being curved. I use the odd marker to alternate the control point between positions above the line and below the line.

A drawing from the resulting app looks like:

Result of quadTo example
Figure 5. Result of quadTo example

Detecting Platform Support

The DrawingCanvas example is a bit naive in that it assumes that the device supports the shape API. If I were to run this code on a device that doesn’t support the Shape API, it would just draw a blank canvas where I expected my shape to be drawn. You can fall back gracefully if you make use of the Graphics.isShapeSupported() method. E.g.

@Override
protected void paintBackground(Graphics g) {
    super.paintBackground(g);
    if ( g.isShapeSupported() ){
       // do my shape drawing code here
    } else {
        // draw an alternate representation for device
        // that doesn't support shapes.
        // E.g. you could defer to the Pisces
        // library in this case
    }

}

Transforms

The Graphics class has included limited support for 2D transformations for some time now including scaling, rotation, and translation:

  • scale(x,y) : Scales drawing operations by a factor in each direction.

  • translate(x,y) : Translates drawing operations by an offset in each direction.

  • rotate(angle) : Rotates about the origin.

  • rotate(angle, px, py) : Rotates about a pivot point.

Note
scale() and rotate() methods are only available on platforms that support Affine transforms. See table X for a compatibility list.

Device Support

As of this writing, not all devices support transforms (i.e. scale() and rotate()). The following is a list of platforms and their respective levels of support.

Table 1. Transforms Device Support

Platform

Affine Supported

Simulator

Yes

iOS

Yes

Android

Yes

JavaScript

Yes

J2ME

No

BlackBerry (4.2 & 5)

No

Windows Phone

No (pending)

You can check if a particular Graphics context supports rotation and scaling using the isAffineSupported() method.

e.g.

public void paint(Graphics g){
    if ( g.isAffineSupported() ){
        // Do something that requires rotation and scaling

    } else {
        // Fallback behavior here
    }
}

Example: Drawing an Analog Clock

In the following sections, I will implement an analog clock component. This will demonstrate three key concepts in Codename One’s graphics:

  1. Using the GeneralPath class for drawing arbitrary shapes.

  2. Using Graphics.translate() to translate our drawing position by an offset.

  3. Using Graphics.rotate() to rotate our drawing position.

There are three separate things that need to be drawn in a clock:

  1. The tick marks. E.g. most clocks will have a tick mark for each second, larger tick marks for each hour, and sometimes even larger tick marks for each quarter hour.

  2. The numbers. We will draw the clock numbers (1 through 12) in the appropriate positions.

  3. The hands. We will draw the clock hands to point at the appropriate points to display the current time.

The AnalogClock Component

Our clock will extend the Component class, and override the paintBackground() method to draw the clock as follows:

public class AnalogClock extends Component {
    Date currentTime = new Date();

    @Override
    public void paintBackground(Graphics g) {
        // Draw the clock in this method
    }
}

Setting up the Parameters

Before we actually draw anything, let’s take a moment to figure out what values we need to know in order to draw an effective clock. Minimally, we need two values:

  1. The center point of the clock.

  2. The radius of the clock.

In addition, I am adding the following parameters to to help customize how the clock is rendered:

  1. The padding (i.e. the space between the edge of the component and the edge of the clock circle.

  2. The tick lengths. I will be using 3 different lengths of tick marks on this clock. The longest ticks will be displayed at quarter points (i.e. 12, 3, 6, and 9). Slightly shorter ticks will be displayed at the five-minute marks (i.e. where the numbers appear), and the remaining marks (corresponding with seconds) will be quite short.

// Hard code the padding at 10 pixels for now
double padding = 10;

// Clock radius
double r = Math.min(getWidth(), getHeight())/2-padding;

// Center point.
double cX = getX()+getWidth()/2;
double cY = getY()+getHeight()/2;

//Tick Styles
int tickLen = 10;  // short tick
int medTickLen = 30;  // at 5-minute intervals
int longTickLen = 50; // at the quarters
int tickColor = 0xCCCCCC;
Stroke tickStroke = new Stroke(2f, Stroke.CAP_BUTT, Stroke.JOIN_ROUND, 1f);

Drawing the Tick Marks

For the tick marks, we will use a single GeneralPath object, making use of the moveTo() and lineTo() methods to draw each individual tick.

// Draw a tick for each "second" (1 through 60)
for ( int i=1; i<= 60; i++){
    // default tick length is short
    int len = tickLen;
    if ( i % 15 == 0 ){
        // Longest tick on quarters (every 15 ticks)
        len = longTickLen;
    } else if ( i % 5 == 0 ){
        // Medium ticks on the '5's (every 5 ticks)
        len = medTickLen;
    }

    double di = (double)i; // tick num as double for easier math

    // Get the angle from 12 O'Clock to this tick (radians)
    double angleFrom12 = di/60.0*2.0*Math.PI;

    // Get the angle from 3 O'Clock to this tick
        // Note: 3 O'Clock corresponds with zero angle in unit circle
        // Makes it easier to do the math.
    double angleFrom3 = Math.PI/2.0-angleFrom12;

    // Move to the outer edge of the circle at correct position
    // for this tick.
    ticksPath.moveTo(
            (float)(cX+Math.cos(angleFrom3)*r),
            (float)(cY-Math.sin(angleFrom3)*r)
    );

    // Draw line inward along radius for length of tick mark
    ticksPath.lineTo(
            (float)(cX+Math.cos(angleFrom3)*(r-len)),
            (float)(cY-Math.sin(angleFrom3)*(r-len))
    );
}

// Draw the full shape onto the graphics context.
g.setColor(tickColor);
g.drawShape(ticksPath, tickStroke);
Tip
This example uses a little bit of trigonometry to calculate the (x,y) coordinates of the tick marks based on the angle and the radius. If math isn’t your thing, don’t worry. This example just makes use of the identities: x=r*cosθ and y=r*sinθ.

At this point our clock should include a series of tick marks orbiting a blank center as shown below:

Drawing tick marks on the watch face
Figure 6. Drawing tick marks on the watch face

Drawing the Numbers

The Graphics.drawString(str, x, y) method allows you to draw text at any point of a component. The tricky part here is calculating the correct x and y values for each string so that the number appears in the correct location.

For the purposes of this tutorial, we will use the following strategy. For each number (1 through 12):

  1. Use the Graphics.translate(x,y) method to apply a translation from the clock’s center point to the point where the number should appear.

  2. Draw number (using drawString()) at the clock’s center. It should be rendered at the correct point due to our translation.

  3. Invert the translation performed in step 1.

for ( int i=1; i<=12; i++){
    // Calculate the string width and height so we can center it properly
    String numStr = ""+i;
    int charWidth = g.getFont().stringWidth(numStr);
    int charHeight = g.getFont().getHeight();

    double di = (double)i;  // number as double for easier math

    // Calculate the position along the edge of the clock where the number should
    // be drawn
     // Get the angle from 12 O'Clock to this tick (radians)
    double angleFrom12 = di/12.0*2.0*Math.PI;

    // Get the angle from 3 O'Clock to this tick
        // Note: 3 O'Clock corresponds with zero angle in unit circle
        // Makes it easier to do the math.
    double angleFrom3 = Math.PI/2.0-angleFrom12;

    // Get diff between number position and clock center
    int tx = (int)(Math.cos(angleFrom3)*(r-longTickLen));
    int ty = (int)(-Math.sin(angleFrom3)*(r-longTickLen));

    // For 6 and 12 we will shift number slightly so they are more even
    if ( i == 6 ){
        ty -= charHeight/2;
    } else if ( i == 12 ){
        ty += charHeight/2;
    }

    // Translate the graphics context by delta between clock center and
    // number position
    g.translate(
            tx,
            ty
    );


    // Draw number at clock center.
    g.drawString(numStr, (int)cX-charWidth/2, (int)cY-charHeight/2);

    // Undo translation
    g.translate(-tx, -ty);

}
Note
This example is, admittedly, a little contrived to allow for a demonstration of the Graphics.translate() method. We could have just as easily passed the exact location of the number to drawString() rather than draw at the clock center and translate to the correct location.

Now, we should have a clock with tick marks and numbers as shown below:

Drawing the numbers on the watch face
Figure 7. Drawing the numbers on the watch face

Drawing the Hands

The clock will include three hands: Hour, Minute, and Second. We will use a separate GeneralPath object for each hand. For the positioning/angle of each, I will employ the following strategy:

  1. Draw the hand at the clock center pointing toward 12 (straight up).

  2. Translate the hand slightly down so that it overlaps the center.

  3. Rotate the hand at the appropriate angle for the current time, using the clock center as a pivot point.

Drawing the Second Hand:

For the "second" hand, we will just use a simple line from the clock center to the inside edge of the medium tick mark at the 12 o’clock position.

GeneralPath secondHand = new GeneralPath();
secondHand.moveTo((float)cX, (float)cY);
secondHand.lineTo((float)cX, (float)(cY-(r-medTickLen)));

And we will translate it down slightly so that it overlaps the center. This translation will be performed on the GeneralPath object directly rather than through the Graphics context:

Shape translatedSecondHand = secondHand.createTransformedShape(
    Transform.makeTranslation(0f, 5)
);

Rotating the Second Hand::

The rotation of the second hand will be performed in the Graphics context via the rotate(angle, px, py) method. This requires us to calculate the angle. The px and py arguments constitute the pivot point of the rotation, which, in our case will be the clock center.

Warning
The rotation pivot point is expected to be in absolute screen coordinates rather than relative coordinates of the component. Therefore we need to get the absolute clock center position in order to perform the rotation.
// Calculate the angle of the second hand
Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
double second = (double)(calendar.get(Calendar.SECOND));
double secondAngle = second/60.0*2.0*Math.PI;

// Get absolute center position of the clock
double absCX = getAbsoluteX()+cX-getX();
double absCY = getAbsoluteY()+cY-getY();

g.rotate((float)secondAngle, (int)absCX, (int)absCY);
g.setColor(0xff0000);
g.drawShape(
        translatedSecondHand,
        new Stroke(2f, Stroke.CAP_BUTT, Stroke.JOIN_BEVEL, 1f)
);
g.resetAffine();
Note
Remember to call resetAffine() after you’re done with the rotation, or you will see some unexpected results on your form.

Drawing the Minute And Hour Hands:

The mechanism for drawing the hour and minute hands is largely the same as for the minute hand. There are a couple of added complexities though:

  1. We’ll make these hands trapezoidal, and almost triangular rather than just using a simple line. Therefore the GeneralPath construction will be slightly more complex.

  2. Calculation of the angles will be slightly more complex because they need to take into account multiple parameters. E.g. The hour hand angle is informed by both the hour of the day and the minute of the hour.

The remaining drawing code is as follows:

// Draw the minute hand
GeneralPath minuteHand = new GeneralPath();
minuteHand.moveTo((float)cX, (float)cY);
minuteHand.lineTo((float)cX+6, (float)cY);
minuteHand.lineTo((float)cX+2, (float)(cY-(r-tickLen)));
minuteHand.lineTo((float)cX-2, (float)(cY-(r-tickLen)));
minuteHand.lineTo((float)cX-6, (float)cY);
minuteHand.closePath();

// Translate the minute hand slightly down so it overlaps the center
Shape translatedMinuteHand = minuteHand.createTransformedShape(
    Transform.makeTranslation(0f, 5)
);

double minute = (double)(calendar.get(Calendar.MINUTE)) +
        (double)(calendar.get(Calendar.SECOND))/60.0;

double minuteAngle = minute/60.0*2.0*Math.PI;

// Rotate and draw the minute hand
g.rotate((float)minuteAngle, (int)absCX, (int)absCY);
g.setColor(0x000000);
g.fillShape(translatedMinuteHand);
g.resetAffine();


// Draw the hour hand
GeneralPath hourHand = new GeneralPath();
hourHand.moveTo((float)cX, (float)cY);
hourHand.lineTo((float)cX+4, (float)cY);
hourHand.lineTo((float)cX+1, (float)(cY-(r-longTickLen)*0.75));
hourHand.lineTo((float)cX-1, (float)(cY-(r-longTickLen)*0.75));
hourHand.lineTo((float)cX-4, (float)cY);
hourHand.closePath();

Shape translatedHourHand = hourHand.createTransformedShape(
    Transform.makeTranslation(0f, 5)
);

//Calendar cal = Calendar.getInstance().get
double hour = (double)(calendar.get(Calendar.HOUR_OF_DAY)%12) +
        (double)(calendar.get(Calendar.MINUTE))/60.0;

double angle = hour/12.0*2.0*Math.PI;
g.rotate((float)angle, (int)absCX, (int)absCY);
g.setColor(0x000000);
g.fillShape(translatedHourHand);
g.resetAffine();

The Final Result

At this point, we have a complete clock as shown below:

The final result - fully rendered watch face
Figure 8. The final result - fully rendered watch face

Animating the Clock

The current clock component is cool, but it is static. It just displays the time at the point the clock was created. We discussed low level animations in the animation section of the guide, here we will show a somewhat more elaborate example.

In order to animate our clock so that it updates once per second, we only need to do two things:

  1. Implement the animate() method to indicate when the clock needs to be updated/re-drawn.

  2. Register the component with the form so that it will receive animation "pulses".

The animate() method in the AnalogClock class:

Date currentTime = new Date();
long lastRenderedTime = 0;

@Override
public boolean animate() {
    if ( System.currentTimeMillis()/1000 != lastRenderedTime/1000){
        currentTime.setTime(System.currentTimeMillis());
        return true;
    }
    return false;
}

This method will be invoked on each "pulse" of the EDT. It checks the last time the clock was rendered and returns true only if the clock hasn’t been rendered in the current "time second" interval. Otherwise it returns false. This ensures that the clock will only be redrawn when the time changes.

Starting and Stopping the Animation

Animations can be started and stopped via the Form.registerAnimated(component) and Form.deregisterAnimated(component) methods. We chose to encapsulate these calls in start() and stop() methods in the component as follows:

public void start(){
    getComponentForm().registerAnimated(this);
}

public void stop(){
    getComponentForm().deregisterAnimated(this);
}

So the code to instantiate the clock, and start the animation would be something like:

AnalogClock clock = new AnalogClock();
parent.addComponent(clock);
clock.start();

Shape Clipping

Clipping is one of the core tenants of graphics programming, you define the boundaries for drawing and when you exceed said boundaries things aren’t drawn. Shape clipping allows us to clip based on any arbitrary Shape and not just a rectangle, this allows some unique effects generated in runtime.

E.g. this code allows us to draw a rather complex image of duke:

Image duke = null;
try {
    // duke.png is just the default Codename One icon copied into place
    duke = Image.createImage("/duke.png");
} catch(IOException err) {
    Log.e(err);
}
final Image finalDuke = duke;

Form hi = new Form("Shape Clip");

// We create a 50 x 100 shape, this is arbitrary since we can scale it easily
GeneralPath path = new GeneralPath();
path.moveTo(20,0);
path.lineTo(30, 0);
path.lineTo(30, 100);
path.lineTo(20, 100);
path.lineTo(20, 15);
path.lineTo(5, 40);
path.lineTo(5, 25);
path.lineTo(20,0);

Stroke stroke = new Stroke(0.5f, Stroke.CAP_ROUND, Stroke.JOIN_ROUND, 4);
hi.getContentPane().getUnselectedStyle().setBgPainter((Graphics g, Rectangle rect) -> {
    g.setColor(0xff);
    float widthRatio = ((float)rect.getWidth()) / 50f;
    float heightRatio = ((float)rect.getHeight()) / 100f;
    g.scale(widthRatio, heightRatio);
    g.translate((int)(((float)rect.getX()) / widthRatio), (int)(((float)rect.getY()) / heightRatio));
    g.setClip(path);
    g.setAntiAliased(true);
    g.drawImage(finalDuke, 0, 0, 50, 100);
    g.setClip(path.getBounds());
    g.drawShape(path, stroke);
    g.translate(-(int)(((float)rect.getX()) / widthRatio), -(int)(((float)rect.getY()) / heightRatio));
    g.resetAffine();
});

hi.show();
Shape Clipping used to clip the image of duke within the given shape
Figure 9. Shape Clipping used to clip the image of duke within the given shape
Tip
Notice that this functionality isn’t available on all platforms so you normally need to test if shaped clipping is supported using isShapeClipSupported().

The Coordinate System

The Codename One coordinate system follows the example of Swing (and many other - but not all- graphics libraries) and places the origin in the upper left corner of the screen. X-values grow to the right, and Y-values grow downward as illustrated below:

The Codename One graphics coordinate space
Figure 10. The Codename One graphics coordinate space

Therefore the screen origin is at the top left corner of the screen. Given this information, consider the method call on the Graphics context g:

g.drawRect(10,10, 100, 100);

Where would this rectangle be drawn on the screen?

If you answered something something like "10 pixels from the top, and 10 pixels from the left of the screen", you might be right. It depends on whether the graphics has a translation or transform applied to it. If there is currently a translation of (20,20) (i.e. 20 pixels to the right, and 20 pixels down), then the rectangle would be rendered at (30, 30).

You can always find out the current translation of the graphics context using the Graphics.getTranslateX() and Graphics.getTranslateY() methods:

// Find out the current translation
int currX = g.getTranslateX();
int currY = g.getTranslateY();

// Reset the translation to zeroes
g.translate(-currX, -currY);

// Now we are working in absolute screen coordinates
g.drawRect(10, 10, 100, 100);

// This rectangle should now be drawn at the exact screen
// coordinates (10,10).

//Restore the translation
g.translate(currX, currY);
Note
This example glosses over issues such as clipping and transforms which may cause it to not work as you expect. E.g. When painting a component inside its paint() method, there is a clip applied to the context so that only the content you draw within the bounds of the component will be seen.

If, in addition, there is a transform applied that rotates the context 45 degrees clockwise, then the rectangle will be drawn at a 45 degree angle with its top left corner somewhere on the left edge of the screen.

Luckily you usually don’t have to worry about the exact screen coordinates for the things you paint. Most of the time, you will only be concerned with relative coordinates.

Relative Coordinates

Usually, when you are drawing onto a Graphics context, you are doing so within the context of a Component’s paint() method (or one of its variants). In this case, you generally don’t care what the exact screen coordinates are of your drawing. You are only concerned with their relative location within the coordinate. You can leave the positioning (and even sizing) of the coordinate up to Codename One. Thank you for reading.

To demonstrate this, let’s create a simple component called Rectangle component, that simply draws a rectangle on the screen. We will use the component’s position and size to dictate the size of the rectangle to be drawn. And we will keep a 5 pixel padding between the edge of the component and the edge of our rectangle.

class RectangleComponent extends Component {
    public void paint(Graphics g){
        g.setColor(0x0000ff);
        g.drawRect(getX()+5, getY()+5, getWidth()-10, getHeight()-10);
    }
}

The result is as follows:

The rectangle component
Figure 11. The rectangle component
Note
The x and y coordinates that are passed to the drawRect(x,y,w,h) method are relative to the component’s parent’s origin — not the component itself .. its parent. This is why we the x position is getX()+5 and not just 5.

Transforms and Rotations

Unlike the Graphics drawXXX primitives, methods for setting transformations, including scale(x,y) and rotate(angle), are always applied in terms of screen coordinates. This can be confusing at first, because you may be unsure whether to provide a relative coordinate or an absolute coordinate for a given method.

The general rule is:

  1. All coordinates passed to the drawXXX() and fillXXX() methods will be subject to the graphics context’s transform and translation settings.

  2. All coordinates passed to the context’s transformation settings are considered to be screen coordinates, and are not subject to current transform and translation settings.

Let’s take our RectangleComponent as an example. Suppose we want to rotate the rectangle by 45 degrees, our first attempt might look something like:

    class RectangleComponent extends Component {

        @Override
        protected Dimension calcPreferredSize() {
            return new Dimension(250,250);
        }

        public void paint(Graphics g) {
            g.setColor(0x0000ff);
            g.rotate((float) (Math.PI / 4.0));
            g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10);
            g.rotate(-(float) (Math.PI / 4.0));
        }
    }
Tip
When performing rotations and transformations inside a paint() method, always remember to revert your transformations at the end of the method so that it doesn’t pollute the rendering pipeline for subsequent components.

The behavior of this rotation will vary based on where the component is rendered on the screen. To demonstrate this, let’s try to place five of these components on a form inside a BorderLayout and see how it looks:

    class MyForm extends Form {

        public MyForm() {
            super("Rectangle Rotations");
            for ( int i=0; i< 10; i++ ){
                this.addComponent(new RectangleComponent());
            }
        }
    }

The result is as follows:

Rotating the rectangle
Figure 12. Rotating the rectangle

This may not be an intuitive outcome since we drew 10 rectangle components, be we only see a portion of one rectangle. The reason is that the rotate(angle) method uses the screen origin as the pivot point for the rotation. Components nearer to this pivot point will experience a less dramatic effect than components farther from it. In our case, the rotation has caused all rectangles except the first one to be rotated outside the bounds of their containing component - so they are being clipped. A more sensible solution for our component would be to place the rotation pivot point somewhere inside the component. That way all of the components would look the same. Some possibilities would be:

Top Left Corner:

        public void paint(Graphics g) {
            g.setColor(0x0000ff);
            g.rotate((float)(Math.PI/4.0), getAbsoluteX(), getAbsoluteY());
            g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10);
            g.rotate(-(float) (Math.PI / 4.0), getAbsoluteX(), getAbsoluteY());
        }
Rotating the rectangle with wrong pivot point
Figure 13. Rotating the rectangle with wrong pivot point

Center:

public void paint(Graphics g) {
    g.setColor(0x0000ff);
    g.rotate(
        (float)(Math.PI/4.0),
        getAbsoluteX()+getWidth()/2,
        getAbsoluteY()+getHeight()/2
    );
    g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10);
    g.rotate(
        -(float)(Math.PI/4.0),
        getAbsoluteX()+getWidth()/2,
        getAbsoluteY()+getHeight()/2
    );
}
Rotating the rectangle with the center pivot point
Figure 14. Rotating the rectangle with the center pivot point

You could also use the Graphics.setTransform() class to apply rotations and other complex transformations (including 3D perspective transforms), but I’ll leave that for its own topic as it is a little bit more complex.

Event Coordinates

The coordinate system and event handling are closely tied. You can listen for touch events on a component by overriding the pointerPressed(x,y) method. The coordinates received in this method will be absolute screen coordinates, so you may need to do some conversions on these coordinates before using them in your drawXXX() methods.

E.g. a pointerPressed() callback method can look like this:

public void pointerPressed(int x, int y) {
    addPoint(x-getParent().getAbsoluteX(), y-getParent().getAbsoluteY());
}

In this case we translated these points so that they would be relative to the origin of the parent component. This is because the drawXXX() methods for this component take coordinates relative to the parent component.

Images

Codename One has quite a few image types: loaded, RGB (builtin), RGB (Codename One), Mutable, EncodedImage, SVG, MultiImage, FontImage & Timeline. There are also URLImage, FileEncodedImage, FileEncodedImageAsync, StorageEncodedImage/Async that will be covered in the IO section.

All image types are mostly seamless to use and will just work with drawImage and various image related image API’s for the most part with caveats on performance etc.

Tip
For animation images the code must invoke the animate() method on the image (this is done automatically by Codename One when placing the image as a background or as an icon!
You only need to do it if you invoke drawImage in code rather than use a builtin component).

Performance and memory wise you should read the section below carefully and be aware of the image types you use. The Codename One designer tries to conserve memory and be "clever" by using only EncodedImage. While these are great for low memory you need to understand the complexities of image locking and be aware that you might pay a penalty if you don’t.

Here are the pros/cons and logic behind every image type. This covers the logic of how it’s created:

Loaded Image

This is the basic image you get when loading an image from the jar or network using Image.createImage(String), Image.createImage(InputStream) & Image.createImage(byte array,int,int), …​

Tip
Some other API’s might return this image type but those API’s do so explicitly!

In some platforms calling getGraphics() on an image like this will throw an exception as it’s immutable). This is true for almost all other images as well.

This restriction might not apply for all platforms.

The image is stored in RAM based on device logic and should be reasonably efficient in terms of drawing speed. However, it usually takes up a lot of RAM.

To calculate the amount of RAM taken by a loaded image we use the following formula:

Image Width * Image Height * 4 = Size In RAM in Bytes

E.g. a 50x100 image will take up 20,000 bytes of RAM.

The logic behind this is simple, every pixel contains 3 color channels and an alpha component hence 3 bytes for color and one for alpha.

Note
This isn’t the case for all images but it’s very common and we prefer calculating for the worst case scenario. Even with JPEG’s that don’t include an alpha channel some OS’s might reuire that additional byte.

The RGB Image’s

There are two types of RGB constructed images that are very different from one another but since they are both technically "RGB image’s" we are bundling them under the same subsection.

Internal

This is a close cousin of the loaded image. This image is created using the method Image.createImage(int array, int, int) and receives the AARRGGBB data to form the image. It’s more efficient than the Codename One RGB image but can’t be modified, at least not on the pixel level.

The goal of this image type is to provide an easy way to render RGB data that isn’t modified efficiently at platform native speeds. It’s technically a standard "Loaded Image" internally.

RGBImage class

RGBImage is effectively an AARRGGBB array that can be drawn by Codename One.

On most platforms this is quite inefficient but for some pixel level manipulations there is just no other way.

An RGBImage is constructed with an int array (int[]) that includes width*height elements. You can then modify the colors and alpha channel directly within the array and draw the image to any source using standard image drawing API’s.

Tip
This is very inefficient in terms of rendering speed and memory overhead. Only use this technique if there is absolutely no other way!

EncodedImage

EncodedImage is the workhorse of Codename One. Images returned from resource files are EncodedImage and many API’s expect it.

The EncodedImage is effectively a loaded image that is "hidden" and extracted as needed to remove the memory overhead associated with loaded image. When creating an EncodedImage only the PNG (or JPEG etc.) is loaded to an array in RAM. Normally such images are very small (relatively) so they can be kept in memory without much overhead.

When image information is needed (pixels) the image is decoded into RAM and kept in a weak/sort reference. This allows the image to be cached for performance and allows the garbage collector to reclaim it when the memory becomes scarce.

Since the fully decoded image can be pretty big (width X height X 4) the ability to store just the encoded image can be pretty stark. E.g. taking our example above a 50x100 image will take up 20,000 bytes of RAM for a loaded image but an EncodedImage can reduce that to 1kb-2kb of RAM.

Tip
An EncodedImage might be more expensive than a loaded image as it will take up both the encoded size and the loaded size. So the cost might be slightly bigger in some cases. It’s main value is its ability to shrink.

When drawing an EncodedImage it checks the weak reference cache and if the image is cached then it is shown otherwise the image is loaded the encoded image cache it then drawn.

EncodedImage is not final and can be derived to produce complex image fetching strategies e.g. the URLImage class that can dynamically download its content from the web.

EncodedImage can be instantiated via the create methods in the EncodedImage class. Pretty much any image can be converted into an EncodedImage via the createFromImage(Image, boolean) method.

EncodedImage Locking

Naturally loading the image is more expensive so we want the images that are on the current form to remain in cache (otherwise GC will thrash a lot). That’s where lock() kicks in, when lock() is active we keep a hard reference to the actual native image so it won’t get GC’d. This significantly improves performance!

Internally this is invoked automatically for background images, icons etc. which results in a huge performance boost. This makes sense since these images are currently showing and they will be in RAM anyway. However, if you use a complex renderer or custom drawing UI you should lock() your images where possible!

To verify that locking might be a problem you can launch the performance monitor tool (accessible from the simulator menu), if you get log messages that indicate that an unlocked image was drawn you might have a problem.

MultiImage

Multi images don’t physically exist as a concept within the Codename One API so there is no way to actually create them and they are in no way distinguishable from EnclodedImage.

The only builtin support for multi images is in the resource file loading logic where a MultiImage is decoded and only the version that matches the current DPI is physically loaded. From that point on user code can treat it like any other EnclodedImage.

9-image borders use multi images by default to keep their appearance more refined on the different DPI’s.

FontImage & Material Design Icons

FontImage allows using an icon font as if it was an image. You can specify the character, color and size and then treat the FontImage as if its a regular image. The huge benefits are that the font image can adapt to platform conventions in terms of color and easily scale to adapt to DPI.

You can generate icon fonts using free tools on the internet such as this. Icon fonts are a remarkably simple and powerful technique to create a small, modern applications.

Icon fonts can be created in 2 basic ways the first is explicitly by defining all of the elements within the font.

Form hi = new Form("Icon Font");
Font materialFont = FontImage.getMaterialDesignFont();
int w = Display.getInstance().getDisplayWidth();
FontImage fntImage = FontImage.createFixed("\uE161", materialFont, 0xff0000, w, w);
hi.add(fntImage);
hi.show();
Icon font from material design icons created with the fixed size of display width
Figure 15. Icon font from material design icons created with the fixed size of display width
Note
The samples use the builtin material design icon font. This is for convenience so the sample will work out of the box, for everyone. However you should be able to do this with any arbitrary icon font off the internet as long as its a valid TTF file.

A more common and arguably "correct" way to construct such an icon would be thru the Style object. The Style object can provide the color, size and background information needed by FontImage.

There are two versions of this method, the first one expects the Style object to have the correct icon font set to its font attribute. The second accepts a Font object as an argument. The latter is useful for a case where you want to reuse the same Style object that you defined for a general UI element e.g. we can set an icon for a Button like this and it will take up the style of the Button:

Form hi = new Form("Icon Font");
Font materialFont = FontImage.getMaterialDesignFont();
int size = Display.getInstance().convertToPixels(6, true);
materialFont = materialFont.derive(size, Font.STYLE_PLAIN);
Button myButton = new Button("Save");
myButton.setIcon(FontImage.create("\uE161", myButton.getUnselectedStyle(), materialFont));
hi.add(myButton);
hi.show();
An image created from the Style object
Figure 16. An image created from the Style object
Warning
Notice that for this specific version of the method the size of the font is used to determine the icon size. In the other methods for FontImage creation the size of the font is ignored!
Material Design Icons

There are many icon fonts in the web, the field is rather volatile and constantly changing. However, we wanted to have builtin icons that would allow us to create better looking demos and builtin components.

That’s why we picked the material design icon font for inclusion in the Codename One distribution. It features a relatively stable core set of icons, that aren’t IP encumbered.

You can use the builtin font directly as demonstrated above but there are far better ways to create a material design icon. To find the icon you want you can check out the material design icon gallery. E.g. we used the save icon in the samples above.

To recreate the save icon from above we can do something like:

Form hi = new Form("Icon Font");
Button myButton = new Button("Save");
myButton.setIcon(FontImage.createMaterial(FontImage.MATERIAL_SAVE, myButton.getUnselectedStyle()));
hi.add(myButton);
Material save icon
Figure 17. Material save icon
Note
Notice that the icon is smaller now as it’s calculated based on the font size of the Button UIID.

We can even write the code in a more terse style using:

Form hi = new Form("Icon Font");
Button myButton = new Button("Save");
FontImage.setMaterialIcon(myButton, FontImage.MATERIAL_SAVE);
hi.add(myButton);

This will produce the same result for slightly shorter syntax.

Tip
FontImage can conflict with some complex API’s that expect a "real" image underneath. Some odd issues can often be resolved by using the toImage() or toEncodedImage() methods to convert the scaled FontImage to a loaded image.

Timeline

Timeline’s allow rudimentary animation and enable GIF importing using the Codename One Designer. Effectively a timeline is a set of images that can be moved rotated, scaled & blended to provide interesting animation effects. It can be created manually using the Timeline class.

Image Masking

Image masking allows us to manipulate images by changing the opacity of an image according to a mask image. The mask image can be hardcoded or generated dynamically, it is then converted to a Mask object that can be applied to any image. Notice that the masking process is computationally intensive, it should be done once and cached/saved.

The code below can convert an image to a rounded image:

Toolbar.setGlobalToolbar(true);
Form hi = new Form("Rounder", new BorderLayout());
Label picture = new Label("", "Container");
hi.add(BorderLayout.CENTER, picture);
hi.getUnselectedStyle().setBgColor(0xff0000);
hi.getUnselectedStyle().setBgTransparency(255);
Style s = UIManager.getInstance().getComponentStyle("TitleCommand");
Image camera = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s);
hi.getToolbar().addCommandToRightBar("", camera, (ev) -> {
    try {
        int width = Display.getInstance().getDisplayWidth();
        Image capturedImage = Image.createImage(Capture.capturePhoto(width, -1));
        Image roundMask = Image.createImage(width, capturedImage.getHeight(), 0xff000000);
        Graphics gr = roundMask.getGraphics();
        gr.setColor(0xffffff);
        gr.fillArc(0, 0, width, width, 0, 360);
        Object mask = roundMask.createMask();
        capturedImage = capturedImage.applyMask(mask);
        picture.setIcon(capturedImage);
        hi.revalidate();
    } catch(IOException err) {
        Log.e(err);
    }
});
Picture after the capture was complete and the resulted image was rounded. The background was set to red so the rounding effect will be more noticeable
Figure 18. Picture after the capture was complete and the resulted image was rounded. The background was set to red so the rounding effect will be more noticeable

Notice that this example is simplistic in order to be self contained. We often recommend that developers ship "ready made" mask images with their application which can allow very complex effects on the images.

URLImage

URLImage is an image created with a URL, it implicitly downloads and adapts the image in the given URL while caching it locally. The typical adapt process scales the image or crops it to fit into the same size which is a hard restriction because of the way URLImage is implemented.

How Does URLImage Work?

The reason for the size restriction lies in the implementation of URLImage. URLImage is physically an animated image and so the UI thread tries to invoke its animate() method to refresh. The URLImage uses that call to check if the image was fetched and if not fetches it asynchronously.

Once the image was fetched the animate() method returns true to refresh the UI. During the loading process the placeholder is shown, the reason for the restriction in size is that image animations can’t "grow" the image. They are assumed to be fixed so the placeholder must match the dimensions of the resulting image.

The simple use case is pretty trivial:

Image i = URLImage.createToStorage(placeholder, "fileNameInStorage", "http://xxx/myurl.jpg", URLImage.RESIZE_SCALE);

Alternatively you can use the similar URLImage.createToFileSystem method instead of the Storage version.

This image can now be used anywhere a regular image will appear, it will initially show the placeholder image and then seamlessly replace it with the file after it was downloaded and stored. Notice that if you make changes to the image itself (e.g. the scaled method) it will generate a new image which won’t be able to fetch the actual image.

Tip
Since ImageIO is used to perform the operations of the adapter interface its required that ImageIO will work. It is currently working in JavaSE, Android, iOS & Windows Phone. It doesn’t work on J2ME/Blackberry devices so if you pass an adapter instance on those platforms it will probably fail to perform its task.

If the file in the URL contains an image that is too big it will scale it to match the size of the placeholder precisely!
There is also an option to fail if the sizes don’t match. Notice that the image that will be saved is the scaled image, this means you will have very little overhead in downloading images that are the wrong size although you will get some artifacts.

The last argument is really quite powerful, its an interface called URLImage.ImageAdapter and you can implement it to adapt the downloaded image in any way you like. E.g. you can use an image mask to automatically create a rounded version of the downloaded image.

To do this you can just override:

public EncodedImage adaptImage(EncodedImage downloadedImage, Image placeholderImage)

In the adapter interface and just return the processed encoded image. If you do heavy processing (e.g. rounded edge images) you would need to convert the processed image back to an encoded image so it can be saved. You would then also want to indicate that this operation should run asynchronously via the appropriate method in the class.

If you need to download the file instantly and not wait for the image to appear before download initiates you can explicitly invoke the fetch() method which will asynchronously fetch the image from the network. Notice that the downloading will still take time so the placeholder is still required.

Mask Adapter

A URLImage can be created with a mask adapter to apply an effect to an image. This allows us to round downloaded images or apply any sort of masking e.g. we can adapt the round mask code above as such:

Image roundMask = Image.createImage(placeholder.getWidth(), placeholder.getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, placeholder.getWidth(), placeholder.getHeight(), 0, 360);

URLImage.ImageAdapter ada = URLImage.createMaskAdapter(roundMask);
Image i = URLImage.createToStorage(placeholder, "fileNameInStorage", "http://xxx/myurl.jpg", ada);
URLImage In Lists

The biggest problem with image download service is with lists. We decided to attack this issue at the core by integrating URLImage support directly into GenericListCellRenderer which means it will work with MultiList, List & ContainerList. To use this support just define the name of the component (name not UIID) to end with _URLImage and give it an icon to use as the placeholder. This is easy to do in the multilist by changing the name of icon to icon_URLImage then using this in the data:

map.put("icon_URLImage", urlToActualImage);

Make sure you also set a "real" icon to the entry in the GUI builder or in handcoded applications. This is important since the icon will be implicitly extracted and used as the placeholder value. Everything else should be handled automatically. You can use setDefaultAdapter & setAdapter on the generic list cell renderer to install adapters for the images. The default is a scale adapter although we might change that to scale fill in the future.

Style s = UIManager.getInstance().getComponentStyle("Button");
FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder = EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight() * 4), false);

Form hi = new Form("MultiList", new BorderLayout());

ArrayList<Map<String, Object>> data = new ArrayList<>();

data.add(createListEntry("A Game of Thrones", "1996", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/GOTMTI2.jpg"));
data.add(createListEntry("A Clash Of Kings", "1998", "http://www.georgerrmartin.com/wp-content/uploads/2012/08/clashofkings.jpg"));
data.add(createListEntry("A Storm Of Swords", "2000", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/stormswordsMTI.jpg"));
data.add(createListEntry("A Feast For Crows", "2005", "http://www.georgerrmartin.com/wp-content/uploads/2012/08/feastforcrows.jpg"));
data.add(createListEntry("A Dance With Dragons", "2011", "http://georgerrmartin.com/gallery/art/dragons05.jpg"));
data.add(createListEntry("The Winds of Winter", "2016 (please, please, please)", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/GOTMTI2.jpg"));
data.add(createListEntry("A Dream of Spring", "Ugh", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/GOTMTI2.jpg"));

DefaultListModel<Map<String, Object>> model = new DefaultListModel<>(data);
MultiList ml = new MultiList(model);
ml.getUnselectedButton().setIconName("icon_URLImage");
ml.getSelectedButton().setIconName("icon_URLImage");
ml.getUnselectedButton().setIcon(placeholder);
ml.getSelectedButton().setIcon(placeholder);
hi.add(BorderLayout.CENTER, ml);

The createListEntry method then looks like this:

private Map<String, Object> createListEntry(String name, String date, String coverURL) {
    Map<String, Object> entry = new HashMap<>();
    entry.put("Line1", name);
    entry.put("Line2", date);
    entry.put("icon_URLImage", coverURL);
    entry.put("icon_URLImageName", name);
    return entry;
}
A URL image fetched dynamically into the list model
Figure 19. A URL image fetched dynamically into the list model
Clone this wiki locally