Search code examples
drawingcodenameonedpi

Why drawString method does not seem to always start at the given coordinates?


In my code I cannot draw a String at precise coordinates. Its upper left corner does not start at the given coordinates but somewhere else. However if I draw a rectangle from the same given coordinates it is well placed. How on earth can this behaviour be possible ?

Here is my code I call in the beforeShow() method :

Image photoBase = fetchResourceFile().getImage("Voiture_4_3.jpg");
    Image watermark = fetchResourceFile().getImage("Watermark.png");


    f.setLayout(new LayeredLayout());
    final Label drawing = new Label();
    f.addComponent(drawing);


    // Image mutable dans laquelle on va dessiner (fond blancpar défaut)
    Image mutableImage = Image.createImage(photoBase.getWidth(), photoBase.getHeight());

    // Paint all the stuff
    paintAll(mutableImage.getGraphics(), photoBase, watermark, photoBase.getWidth(), photoBase.getHeight());


    drawing.getUnselectedStyle().setBgImage(mutableImage);
    drawing.getUnselectedStyle().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);


    // Save the graphics
    // Save the image with the ImageIO class
    long time = new Date().getTime();
    OutputStream os;
    try {
        os = Storage.getInstance().createOutputStream("screenshot_" + Long.toString(time) + ".png");
        ImageIO.getImageIO().save(mutableImage, os, ImageIO.FORMAT_PNG, 1.0f);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

And the paintAll method

public void paintAll(Graphics g, Image background, Image watermark, int width, int height) {

    // Full quality
    float saveQuality = 1.0f;

    // Create image as buffer
    Image imageBuffer = Image.createImage(width, height, 0xffffff);
    // Create graphics out of image object
    Graphics imageGraphics  = imageBuffer.getGraphics();

    // Do your drawing operations on the graphics from the image
    imageGraphics.drawImage(background, 0, 0);
    imageGraphics.drawImage(watermark, 0, 0);

    imageGraphics.setColor(0xFF0000);

    // Upper left corner
    imageGraphics.fillRect(0, 0, 10, 10);

    // Lower right corner
    imageGraphics.setColor(0x00FF00);
    imageGraphics.fillRect(width - 10, height - 10, 10, 10);

    imageGraphics.setColor(0xFF0000);
    Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(220, Font.STYLE_BOLD);
    imageGraphics.setFont(f);
    // Draw a string right below the M from Mercedes on the car windscreen (measured in Gimp)
    int w = 0, h = 0;

    imageGraphics.drawString("HelloWorld", w, h);


    // Coin haut droit de la string
    imageGraphics.setColor(0x0000FF);
    imageGraphics.fillRect(w, h, 20, 20);

    // Draw the complete image on your Graphics object g (the screen I guess) 
    g.drawImage(imageBuffer, 0, 0);


}

Result for w = 0, h = 0 (no apparent offset) : no offset

Result for w = 841 , h = 610 (offset appears on both axis : there is an offset between the blue point near Mercedes M on the windscreen and the Hello World String) with offset

EDIT1: I also read this SO question for Android where it is advised to convert the dpi into pixel. Does it also applies in Codename One ? If so how can I do that ? I tried

Display.getInstance().convertToPixel(measureInMillimeterFromGimp)

without success (I used mm because the javadoc tells that dpi is roughly 1 mm)

Any help would be appreciated,

Cheers


Solution

  • Inspired from Gabriel Hass' answer I finally made it work using another intermediate Image to only write the String at (0 ; 0) and then drawing this image on the the imageBuffer Image now at the right coordinates. It works but to my mind drawString(Image, Coordinates) should directly draw at the given coordinates, shouldn't it @Shai ?

    String eventually at the righ coordinates (blue point expected to be on the top left corner of String HelloWorld

    Here is the method paintAll I used to solve my problem (beforeShow code hasn't changed) :

        // Full quality
        float saveQuality = 1.0f;
    
        String mess = "HelloWorld";
    
        // Create image as buffer
        Image imageBuffer = Image.createImage(width, height, 0xffffff);
        // Create graphics out of image object
        Graphics imageGraphics  = imageBuffer.getGraphics();
    
    
        // Do your drawing operations on the graphics from the image
        imageGraphics.drawImage(background, 0, 0);
        imageGraphics.drawImage(watermark, 0, 0);
    
        imageGraphics.setColor(0xFF0000);
    
        // Upper left corner
        imageGraphics.fillRect(0, 0, 10, 10);
    
        // Lower right corner
        imageGraphics.setColor(0x00FF00);
        imageGraphics.fillRect(width - 10, height - 10, 10, 10);
    
    
        // Create an intermediate image just with the message string (will be moved to the right coordinates later)
        Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(150, Font.STYLE_BOLD);
        // Get the message dimensions 
        int messWidth = f.stringWidth(mess);
        int messHeight = f.getHeight();
    
        Image messageImageBuffer = Image.createImage(messWidth, messHeight, 0xffffff);
        Graphics messageImageGraphics  = messageImageBuffer.getGraphics();
    
        messageImageGraphics.setColor(0xFF0000);
        messageImageGraphics.setFont(f);
    
        // Write the string at (0; 0)
        messageImageGraphics.drawString(mess, 0, 0);
    
        // Move the string to its final location right below the M from Mercedes on the car windscreen (measured in Gimp) 
        int w = 841, h = 610;   
        imageGraphics.drawImage(messageImageBuffer, w, h);
    
    
        // This "point" is expected to be on the lower left corner of the M letter from Mercedes and on the upper left corner of the message string
        imageGraphics.setColor(0x0000FF);
        imageGraphics.fillRect(w, h, 20, 20);
    
        // Draw the complete image on your Graphics object g 
        g.drawImage(imageBuffer, 0, 0);