2D Bitmap Drawing Changes Canvas Size

Hello,
Every time I’m drawing a 2D picture on the screen it causes the canvas size to change (gets smaller - see video attached).
Is there something I need to configure with the guiNode?

In this video - I change the initial canvas size to 3 different sizes and still every time a 2D picture is added to the scene it causes the canvas to change its size…

Adding a picture won’t change the canvas size. Your code must be doing more than just adding a picture when you “add a picture”.

Here is the entire code of adding a picture to the scene:

    public void channelDraw(ChannelDrawCommand cmd) {

        Picture pic = _drawChannels.get(cmd.channelName);
        String fromRes=cmd.resourceName;
        boolean clear = fromRes.equalsIgnoreCase("clear");
        ResourceSetup2D resource = null;
        if(!clear) {
            resource = assetsMapping.getSpriteSheetsIndex().get(fromRes.toLowerCase());
            if (resource == null) {
                return;
            }
        }

        if(pic==null) {
            if(clear) {
                return;
            }

            pic = new Picture(cmd.resourceName+"_"+_drawChannels.size());
            _drawChannels.put(cmd.channelName,pic);
            setTexture2D(cmd,resource,pic);
            guiNode.attachChild(pic);
        } else {
            if(clear) {
                pic.setUserData(CLEAR_FLAG,1);
                guiNode.detachChild(pic);
                return;
            } else {
                Object clearFlag = pic.getUserData(CLEAR_FLAG);
                if(clearFlag!=null && clearFlag.equals(1)) {
                    pic.setUserData(CLEAR_FLAG,null);
                    guiNode.attachChild(pic);
                }

            }
            setTexture2D(cmd,resource,pic);
        }

        pic.setPosition(cmd.posXVal, settings.getHeight() - pic.getLocalScale().getY() - cmd.posYVal);

    }

    private void setTexture2D(ChannelDrawCommand cmd, ResourceSetup2D resource, Picture pic) {

        BufferedImage img = null;

        try {
            img = ImageIO.read(assetManager.locateAsset(new AssetKey(resource.path)).openStream());
        } catch (IOException ex) {
            //LOGGER.log(Level.SEVERE, "Failed to load the backdrop image with " + image + "!", ex);
        }

        int width = img.getWidth()/resource.cols;
        int height = img.getHeight()/resource.rows;
        int targetWidth = width;
        int targetHeight = height;

        if(cmd.heightExpr!=null) {
            targetHeight = cmd.heightVal;
            targetWidth = cmd.widthVal;
        }

        if(cmd.frameNumVal<0) {
            cmd.frameNumVal = 0;
        } else if(cmd.frameNumVal>resource.cols*resource.rows-1) {
            cmd.frameNumVal = resource.cols*resource.rows-1;
        }

        int row = cmd.frameNumVal / resource.cols ;
        int col = cmd.frameNumVal % resource.cols ;

        int startX = col * width;
        int startY= row * height;

        BufferedImage newImage = new BufferedImage(targetWidth, targetHeight, img != null ? img.getType() : BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(img,0,0,targetWidth,targetHeight,startX,startY,startX+width,startY+height, null,null);

        AWTLoader loader = new AWTLoader();
        Texture2D tex = new Texture2D(loader.load(newImage, true));

        pic.setTexture(assetManager, tex, true);
        pic.setWidth(targetWidth);
        pic.setHeight(targetHeight);

    }

What is it doing that affects the canvas size?

As a test, after that line add:
if( true ) return;

…and see if your canvas still resizes. (Obviously your image won’t get added but nothing in that code would affect the canvas size that I can see… the early return will just prove it.)

1 Like

Now I’m worried - On my development computer (old HP with Intel Radeon 3000 card) it works just fine - no canvas size changes after drawing pictures.
On my test machine , Lenovo LEGION with NVIDIA GeForce GTX 1650 it happens like shown in my video…
Strange

Even with the code I suggested that you add?

With the code added nothing happens (no picture added and the canvas preserves its size)
One more thing I have noticed on the Lenovo machine is that when the picture is added to the scene sometimes part of the canvas is not rendered until I move the window which causes the entire screen to invalidate and render correctly.

Now you can keep moving that if line until the problem comes back.

I put the if (true) return; before all guiNode.attachChild and detachChild and the problem remains which means the problem is not caused by adding pictures to the guiNode so I guess it is happening when I’m creating the texture…
Keep checking…

OK, I have isolated the problem - The line which causes this voodoo is this one:

Graphics2D g = newImage.createGraphics();

I guess I need to give up using Graphics2D since its not a JME class and look for other ways to render sprites on the guiNode.
If it happened on my machine it will happen on other people’s computers so it’s not safe to use it in JME based games

I ended up changing the texture coordinates instead of using the Graphics2D. Here is the complete code for adding a Picture to the guiNode (PictureExt extends Picture):

    public void channelDraw(ChannelDrawCommand cmd) {

        PictureExt pic = _drawChannels.get(cmd.channelName);
        String fromRes=cmd.resourceName;
        boolean clear = fromRes.equalsIgnoreCase("clear");
        ResourceSetup2D resource = null;
        if(!clear) {
            resource = assetsMapping.getSpriteSheetsIndex().get(fromRes.toLowerCase());
            if (resource == null) {
                return;
            }
        }

        if(pic==null) {
            if(clear) {
                return;
            }

            pic = new PictureExt(cmd.resourceName+"_"+_drawChannels.size());
            _drawChannels.put(cmd.channelName,pic);
            setTexture2D(cmd,resource,pic);
            guiNode.attachChild(pic);

        } else {
            if(clear) {
                pic.clear=true;
                guiNode.detachChild(pic);
                return;
            } else {

                if(pic.clear) {
                    pic.clear=false;
                    guiNode.attachChild(pic);
                }

            }
            setTexture2D(cmd,resource,pic);
        }

        pic.setPosition(cmd.posXVal, settings.getHeight() - pic.getLocalScale().getY() - cmd.posYVal);

    }

    private void setTexture2D(ChannelDrawCommand cmd, ResourceSetup2D resource, PictureExt pic) {

        BufferedImage img = null;

        try {
            if(!resource.path.equals(pic.resPath)) {
                img = ImageIO.read(assetManager.locateAsset(new AssetKey(resource.path)).openStream());
                pic.width = img.getWidth()/resource.cols;
                pic.height = img.getHeight()/resource.rows;
            }
        } catch (IOException ex) {
            //LOGGER.log(Level.SEVERE, "Failed to load the backdrop image with " + image + "!", ex);
        }

        int targetWidth = pic.width;
        int targetHeight = pic.height;

        if(cmd.heightExpr!=null) {
            targetHeight = cmd.heightVal;
            targetWidth = cmd.widthVal;
        }

        if(cmd.frameNumVal<0) {
            cmd.frameNumVal = 0;
        } else if(cmd.frameNumVal>resource.cols*resource.rows-1) {
            cmd.frameNumVal = resource.cols*resource.rows-1;
        }

        int row = cmd.frameNumVal / resource.cols ;
        int col = cmd.frameNumVal % resource.cols ;

        pic.setImage(assetManager,resource.path,true);
        updateTextureCoords(pic,col,row,resource.cols,resource.rows);
        pic.setWidth(targetWidth);
        pic.setHeight(targetHeight);

    }

    public void updateTextureCoords(Picture pic, int colPosition, int rowPosition, int columns, int rows) {

        float uvSpacing = 0.001f;

        float colSize = 1f / (float) columns;
        float rowSize = 1f / (float) rows;

        // Texture coordinates
        Vector2f[] texCoord = new Vector2f[4];

        texCoord[0] = new Vector2f(colSize * colPosition + uvSpacing, rowSize * rowPosition + uvSpacing);
        texCoord[1] = new Vector2f(colSize * colPosition + colSize - uvSpacing, rowSize * rowPosition + uvSpacing);
        texCoord[2] = new Vector2f(colSize * colPosition + colSize - uvSpacing, rowSize * rowPosition + rowSize - uvSpacing);
        texCoord[3] = new Vector2f(colSize * colPosition + uvSpacing, rowSize * rowPosition + rowSize - uvSpacing);

        pic.getMesh().setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));

    }

To what end?

Your code for setting the texture coordinates is the super long way of doing something like:

pic.getMesh().setBuffer(Type.TexCoord, 2, new float[]{0, 1,
                                                    1, 1,
                                                    1, 0,
                                                    0, 0});

…with appropriate values.

Nice! Also more efficient?

Because I allow users to change textures, attach / detach and wanna save some calculations in fields instead of userdata

Yes. No wasted Vector2f objects that will just get thrown away. Just a float[] array that will get thrown away.

1 Like