Generating images from a template

Started by Zyphuris55, June 08, 2012, 11:27:25 AM

Previous topic - Next topic

Zyphuris55

I tried looking through the forum and Javadoc for something which would do this, but I haven't came back with much luck. What I'm trying to do is generate new pictures from a template image, which would reduce the need to package the game with multiple images.

Each template image is a 256x256 nodpi png in an 8-bit format. Currently what I'm doing is converting the template image into int[] "done once per game start", create a new int[] of the same size, test each pixel if it matches a list of values, then change that pixel to another pixel color (in the new int[]). Then when the new int[] is finished, its made into a ARGB_8888 bitmap which it loaded into the TextureManager.

Example: change all the pixels which have a red channel of 20 into a ARGB of 200:128:0:128 (purple with a transparency of 200, change all the pixels with an red channel of 40 into an ARGB of 200:255:0:0. Every other pixel is copied as is.

The method I have now works, but it takes about 3-4 second per image (good thing they are all done in threads). The problem is that the whole bulk of them are created each time the game starts (and oddly when the phone resumes from standby). I was thinking about using the ITextureEffect, but I'm still not sure how to use it.

Is there another method of doing this or a tutorial of how ITextureEffect works? Majority of the new images won't change, but I would want to have some of the images animated eventually (using the same color scheme).


= Current code to generate the images =

private void MakeImage(Panel data, int[] pcolors)
{
if(tm.containsTexture(data.Name))
return;

if(!rawBmp.containsKey(data.ImageAttribute.Location))
return;// if the template image was not able to be added, then skip making this image

Bitmap nBit = Bitmap.createBitmap(adjust(data.ImageAttribute.Location, pcolors), 256, 256, Config.ARGB_8888);
tm.addTexture(data.Name, new Texture(nBit, true));
nBit.recycle();
       
data.TexHandle = tm.getTextureID(data.Name);
data.Colors = pcolors;
panels.put(data.Name, data);
}

private int[] adjust(String d, int[] pcolors)
{   
if(pcolors == null)
return rawBmp.get(d);

    int cID = -1;
    int toColor = 0;
    int nPixel = 0;
   
    int[] nPanel = new int[256*256];
   
    for(int p = 0; p < rawBmp.get(d).length; p++)
    {
        nPixel = rawBmp.get(d)[p];
        nPanel[p] = nPixel;
       
        cID = match(nPixel);
       
        if(cID > -1 && cID < pcolors.length)
        {
        toColor = Color.argb(Color.green(nPixel), Color.red(Colors[pcolors[cID]]), Color.green(Colors[pcolors[cID]]), Color.blue(Colors[pcolors[cID]]));
        nPanel[p] = toColor;
        }
       
        if(cID == -2)
        {
        toColor = Color.argb(Color.green(nPixel), 100, 100, 100);
        nPanel[p] = toColor;
        }
    }
   
    return nPanel;
}

private int match(int pixel)
{
switch(Color.red(pixel))
{
case 0:
return -2;
case 20:
return 0;
case 40:
return 1;
case 60:
return 2;
case 80:
return 3;
case 100:
return 4;
}

return -1;
}


EgonOlsen

Something like this should work:

import com.threed.jpct.ITextureEffect;
import com.threed.jpct.Texture;


public class TT
{
  public static void main(String[] args)
  {
    Texture t = new Texture(256, 256);
    int[] template = new int[256 * 256];
    MyEffect effect = new MyEffect(template);
    t.setEffect(effect);
    t.applyEffect();
    t.removeEffect();
  }


  private static class MyEffect
    implements ITextureEffect
  {
    private int[] template;

    public MyEffect(int[] template)
    {
      this.template = template;
    }

    @Override
    public void apply(int[] dest, int[] src)
    {
      System.arraycopy(template, 0, dest, 0, template.length);
    }

    @Override
    public boolean containsAlpha()
    {
      return false;
    }

    @Override
    public void init(Texture tex)
    {
      //
    }
  }

}

Zyphuris55

#2
The code you provided makes sense, but it's only applying the image, not changing it. Applying the int[] to a bitmap then the bitmap to the texture doesn't seem to take much time.
The code you mentioned would still be using majority of the code I provided except for 3 lines.

What I'm trying to do is use the data from the template to determine how to change the new picture.
In the attached images, the all green one would become the colored one (through the code I provided), then use that image in the game. So each template would produce about 20 different permutations of the colored image (using 6 different colors).

I thought about making an int[] for all the colors in the template which match each criteria (red 20 for example), mark each place with a 1, then when the part needs to be colored, multiply the array values by the ARGB int and add the results to a new array (along with the other sections which were previously added). But I suspect this would just add more memory overhead and not much performance increase.

EgonOlsen

I still don't get if this is supposed to happen at run time or at start-up time!?

Zyphuris55

Currently it's happening at start-up time, but after all of the images are loaded up, it would take about 15 MBs. (2-5 Kb per picture * 6-120 permutations per image) * 40 images.

If it would be possible to change the colors during the render (on the gpu?) without slowing down the fps too much, I'd be willing to switch to that method.

EgonOlsen

Could be done in some custom shader IMHO. Anyway, your code has some real heavy performance bottlenecks.

For a 256*256 template, you are doing:


  • 131072 HashMap-lookups (these rawBmp.get(d)-calls)
  • 196608 array index accesses to pcolors and colors where actually 65536 each would be enough
  • 327680+x calls to static methods in Color, of which none is actually needed

I felt free to change adjust() to something that i would do in this performance critical case (i haven't touched the match()-method though i might inline it). This is untested. Especially the byte-fiddling for creating the colors might contain some flaws...anyway, i think you'll get the idea.


private int[] adjust(String d, int[] pcolors)
{   
if(pcolors == null)
return rawBmp.get(d);

    int cID = -1;
    int toColor = 0;
    int nPixel = 0;
   
    int[] nPanel = new int[256*256];
   
    int[] rawBmpD=rawBmp.get(d);
    int end=rawBmpD.length;
    for(int p = 0; p < end; p++)
    {
        nPixel = rawBmpD[p];
        nPanel[p] = nPixel;
       
        cID = match(nPixel);
       
        if(cID > -1 && cID < pcolors.length)
        {
        int col=Colors[pcolors[cID]];
                nPanel[p] = (((nPixel>>8)&0xff)<<24) | (col&0x00ffffff);
        } else if(cID == -2)
        {
        nPanel[p] = (((nPixel>>8)&0xff)<<24) | 0x00646464;;
        }
    }
   
    return nPanel;
}

Zyphuris55

Thanks, I'll try the updated code now (and I'll see if I can reduce the image to 128x128 without degrading the quality too much)

I had the Color.x() calls because I thought they were just straight calls to the needed functions. The javadoc in the descriptions said "This is the same as saying (color >> 16) & 0xFF", but I guess removing the middleman code wouldn't hurt. :-)

Zyphuris55

I used the adjusted code you recommended and it cut the loading time for each picture in half (50 - 60% increase in speed). And I didn't even touch the image sizes.

...You've amazed me again with your skills and knowledge. When I get to a presentable release, I'll add the game to the projects part of this forum  ;D.