views:

62

answers:

4

Hey folks, I am working on a j2ME game for java-capable cell phones. I am attempting to scale a transparent PNG with the following method:

// method derived from a Snippet from http://snippets.dzone.com/posts/show/3257
// scales an image according to the ratios given as parameters

private Image rescaleImage(Image image, double XRatio, double YRatio)
{
    // the old height and width
    int sourceWidth = image.getWidth();
    int sourceHeight = image.getHeight();

    // what the new height and width should be
    int newWidth = (int)(XRatio * sourceWidth);
    int newHeight = (int)(YRatio * sourceHeight);

    Image newImage = Image.createImage(newWidth, newHeight);
    Graphics g = newImage.getGraphics();

    for (int y = 0; y < newHeight; y++)
    {
        for (int x = 0; x < newWidth; x++)
        {
            g.setClip(x, y, 1, 1);
            int dx = (x * sourceWidth) / newWidth;
            int dy = (y * sourceHeight) / newHeight;
            g.drawImage(image, (x - dx), (y - dy), Graphics.LEFT | Graphics.TOP);
        }
    }
    return Image.createImage(newImage);
}

It scales the image correctly, unfortunately I seem to be losing the transparency with the image returned by the method. I am fairly new to these concepts, and any help would be greatly appreciated! Please note that in order to be properly displayed on any java-capable mobile device, the rescaling needs to be done in code, not in any sort of image editor.

Thanks in advance!

A: 

Maybe the Image class' method:

Image   getScaledInstance(int width, int height, int hints) 

is a good fit for what you need..?

From: http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/java/awt/Image.html

fd
I do not think this method is supported in J2ME, thank you for the reply, though!
jbabey
My mistake, will post another answer.
fd
+1  A: 

It is possibly because you don't distinguish alpha in the pixel values. What you can do is add an extra routine to handle the ones with alpha so they keep their possible alpha values.

Basically it's checking every pixel, see if it has alpha, if it has, check if it will be on the resized image, if so, apply it there with it's alpha, if not, discard it.

Yonathan Klijnsma
Thanks Yonathan. I am attempting to use Image.GetRGB() and Image.createRGBImage() along with a scaling algorithm to manipulate the ARGB array in between. Wish me luck :)
jbabey
Update on the ARGB approach: It works quite well, except for the fact that the scaling ratios are usually not integers, which makes pure pixel array manipulation a little more difficult.
jbabey
A: 

The javax.microedition.lcdui.Image class has the following method:

          Image createImage(Image image,
                            int x,
                            int y,
                            int width,
                            int height,
                            int transform)

The doc says it can create an immutable image from a source immutable image (seems like only immutable images are allowed transparency data) and will preserve transparency in this case. Setting the transform to NONE and using the desired width and height might give you what you want.

Documentation: http://java.sun.com/javame/reference/apis/jsr118/javax/microedition/lcdui/Image.html#createImage%28javax.microedition.lcdui.Image,%20int,%20int,%20int,%20int,%20int%29

fd
Thanks again for the reply, I will try this tonight =)
jbabey
Throws: IllegalArgumentException - if the region to be copied exceeds the bounds of the source image. I appreciate the effort :)
jbabey
Right, I misuderstood the meaning of the arguments for this method. It will not scale the image but rather make a copy of a part of the image and (optionally) apply a transformation; sadly scaling is not one of the available transforms.
fd
A: 

Thanks to everyone who has been looking for solutions to this seemingly wide-spread and unsolved problem. I managed to find a great solution at http://willperone.net/Code/codescaling.php

You just change the "false" in the createRGBImage parameter to a true. This flags the method to process the high-order bits of each pixel as alpha values. Here is my implementation, not much change from the original link above.

XRatio and YRatio are declared as constants when the canvas is initialized, where XRatio = this.getWidth() (the current phone's screen width) divided by your original background image width, and YRatio with getHeight() / original background image height.

// RESCALEIMAGE
// scales an image according to the ratios given as parameters
// derived from http://willperone.net/Code/codescaling.php

public static Image rescaleImage(Image original, double XRatio, double YRatio)
{
    // the original width and height
    int originalWidth = original.getWidth();
    int originalHeight = original.getHeight();

    // the target width and height
    int newWidth = (int)(XRatio * originalWidth);
    int newHeight = (int)(YRatio * originalHeight);

    // create and fill the pixel array from the original image
    int[] rawInput = new int[originalHeight * originalWidth];
    original.getRGB(rawInput, 0, originalWidth, 0, 0, originalWidth, originalHeight);

    // pixel array for the target image
    int[] rawOutput = new int[newWidth*newHeight];

    // YD compensates for the x loop by subtracting the width back out
    int YD = (originalHeight / newHeight) * originalWidth - originalWidth;
    int YR = originalHeight % newHeight;
    int XD = originalWidth / newWidth;
    int XR = originalWidth % newWidth;
    int outOffset= 0;
    int inOffset=  0;

    for (int y = newHeight, YE = 0; y > 0; y--)
    {
        for (int x = newWidth, XE = 0; x > 0; x--)
        {
            rawOutput[outOffset++] = rawInput[inOffset];

            inOffset += XD;
            XE += XR;

            if (XE >= newWidth)
            {
                XE -= newWidth;
                inOffset++;
            }
        }

        inOffset += YD;
        YE += YR;

        if (YE >= newHeight)
        {
            YE -= newHeight;
            inOffset += originalWidth;
        }
    }
    return Image.createRGBImage(rawOutput, newWidth, newHeight, true);
}
jbabey