Link to home
Start Free TrialLog in
Avatar of wagtail
wagtail

asked on

How do I write a colour-map rotation filter?

How do I write an ImageFilter that will rotate an Image Producer's colour map?

Ideally I'd like to be able to directly access the color map and shift the colour entries one to the right.

If this isn't possible, can I read the colour map into an array somehow?

Thanks.
Avatar of imladris
imladris
Flag of Canada image

An image filter looks like this:

class TransFilter extends RGBImageFilter
{      TransFilter()
      {      canFilterIndexColorModel=true;
            return;
      }

      public int filterRGB(int x,int y,int rgb)
      {      int a=rgb&0xff;
                                return(((rgb&0xffffff)>>8) | (a<<16));
      }
}

This one shifts the red, green and blue components one to the right. (Java uses a 24bit model internally). Since canFilterIndexColorModel is set to true, the runtime knows that the transformation is position independent, and will only call the filter for each different colour in the palette.

It can be  used like:

Image buf=tk.getImage(path+File.separator+f+".gif");
ImageFilter flt=new TransFilter();
ImageProducer p=new FilteredImageSource(buf.getSource(),flt);
pi=window.createImage(p);


Avatar of wagtail
wagtail

ASKER

imladris,

Thanks for the detailed answer, but I already know this.

I don't want to just shift the colours of an individual palette entry... I want to take palette entry say, 10, and copy it into palette entry 9.

Basically I want to preserve all the image's original colours, but rotate them...

Sorry, but I must reopen the question.
It seems to me that the only way to change the presentation,
without changing the data, is to twiddle the hardware (e.g. the
palette register, which is what your alluding to). However, Java
is agnostic about the hardware it runs on.

Furthermore what would be the advantage of introducing such a
hardware dependency in a case like this? It will not be noticably
faster (with canFilterIndexColorModel=true, it will only
manipulate the colormodel, not the whole image). The ColorModel
IS in a sense the palette. Nor does it give greater range of
options. Nor does it preserve the data any better. The filtered
image comes through the filter, the original is still there.

Avatar of wagtail

ASKER

I had hoped this might be possible without twiddling any hardware - I definitely want to avoid hardware dependency!

I'm trying to create a "colour-map rotating applet". The idea is to implement a kind of palette animation in software, for small images.

The applet would paint, then rotate the colours using the filter, then paint again in a loop... giving the illusion of a living image.

I'd be using fractal images which have strictly ordered colour maps - the colours flow into each other gradually. The applet should use these and only these colours to create the flowing effect that I'm after.

Is this beyond the capabilities of the java.awt.image package? Maybe I should be looking at creating a special purpose image class?
What is the problem with the ImageFilter approach? Is it not
rotating fast enough? Would it help to "precalculate"? How many
do you think you need? How big are they?

Avatar of wagtail

ASKER

The problem is that I can't read the colours in the image's original colour map, so I've no way of knowing what they are.

Speed isn't a problem (at the moment!), and no need to precalculate. We are talking about 196 colours or so.

I see two options. One, build my own image class which will read in gif colour map info. Two,  provide a set of default palettes to rotate. Option one is preferable as it will keep the applet small and handle the colours of any image it loads.
Still confused. With the extension to the RGBImageFilter, your filter is handed every colour in the image one by one. You can then return any colour you like for each one. Doesn't that implicitly tell you what colours there are? Is it that you are dealing with a 24bit colour space, and changing from 0x000200 to 0x000100, for example, has no visible effect since there are only 256 colours displayed on the screen?

P.S. Have you actually tried using the ImageFilter approach? What happened?

Avatar of wagtail

ASKER

OK... here's how I see it.

I'm using standard .gif files, 256 colours. I load in my image - it's a fractal, all shades of orange and yellow...
     image = getImage(getDocumentBase(), "images/snowman.gif");
     while (image.getHeight(null) < 0); //wait for image to load
      
then I create my filtered producer...
     ImageProducer producer = image.getSource();
     TestFilter filter = new TestFilter();
      filteredProducer = new FilteredImageSource(producer, filter);

...then I generate a new, filtered image, which replaces the old image...
     image = createImage(filteredProducer);

The filtering takes place in the TestFilter object's filterRGB() method...

     public int filterRGB(int x, int y, int rgb)
     {
          int a = rgb & 0xff;
          return( ((rgb & 0xffffff) >> 8) | (a << 16) );
     }

Every pixel in the original image is passed in here when I generate my new image.

Let's say there were only 3 different colours in the original .gif colour table - yellow, orange and red. Let's also say that the image itself is composed of three wide bands of each colour side by side - 20 yellow pixels, then 20 orange pixels, then 20 red pixels, repeating for 60 lines.

The filterRGB method above takes each pixel and shifts its rgb components one to the right. (In fact when I tried it it turned every pixel white). The filterRGB code example in the Java API documentation swaps the red and blue components, and would turn my hypothetical example into three shades of blue.

This isn't what I want to do though. I want to change my yellow band to an orange band, my orange band to a red band, and my red band to a yellow band. And I want to use the exact colours found in the original .gif file. It's no good applying an arbitrary formula to create the new colour - I need to get the next (or previous) colour in the .gif colour table, and return *that*.

I see that the RGBImageFilter has a "setColorModel" method, but there doesn't seem to be a corresponding "getColorModel", otherwise I could have maybe grabbed the filter's IndexedColorModel, rotated the r, g and b arrays inside it, constructed a new IndexedColorModel with the adjusted arrays and passed it back into the filter?
 
Strewth. This package... :o(

Avatar of wagtail

ASKER

OK... here's how I see it.

I'm using standard .gif files, 256 colours. I load in my image - it's a fractal, all shades of orange and yellow...
     image = getImage(getDocumentBase(), "images/snowman.gif");
     while (image.getHeight(null) < 0); //wait for image to load
      
then I create my filtered producer...
     ImageProducer producer = image.getSource();
     TestFilter filter = new TestFilter();
      filteredProducer = new FilteredImageSource(producer, filter);

...then I generate a new, filtered image, which replaces the old image...
     image = createImage(filteredProducer);

The filtering takes place in the TestFilter object's filterRGB() method...

     public int filterRGB(int x, int y, int rgb)
     {
          int a = rgb & 0xff;
          return( ((rgb & 0xffffff) >> 8) | (a << 16) );
     }

Every pixel in the original image is passed in here when I generate my new image.

Let's say there were only 3 different colours in the original .gif colour table - yellow, orange and red. Let's also say that the image itself is composed of three wide bands of each colour side by side - 20 yellow pixels, then 20 orange pixels, then 20 red pixels, repeating for 60 lines.

The filterRGB method above takes each pixel and shifts its rgb components one to the right. (In fact when I tried it it turned every pixel white). The filterRGB code example in the Java API documentation swaps the red and blue components, and would turn my hypothetical example into three shades of blue.

This isn't what I want to do though. I want to change my yellow band to an orange band, my orange band to a red band, and my red band to a yellow band. And I want to use the exact colours found in the original .gif file. It's no good applying an arbitrary formula to create the new colour - I need to get the next (or previous) colour in the .gif colour table, and return *that*.

I see that the RGBImageFilter has a "setColorModel" method, but there doesn't seem to be a corresponding "getColorModel", otherwise I could have maybe grabbed the filter's IndexedColorModel, rotated the r, g and b arrays inside it, constructed a new IndexedColorModel with the adjusted arrays and passed it back into the filter?
 
What d'you reckon?

ASKER CERTIFIED SOLUTION
Avatar of imladris
imladris
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of wagtail

ASKER

Thanks. This looks good, but there are some problems...

The "zero" filter will produce an array of pixel colours, the size of the original image.
 
For the applet to be fast enough, I need to group all the pixel colours into a 256 element array which will represent the image's colour map.

The image's pixels, however, know nothing of the *order* of the colours in the original colour map. Therefore, although it is possible to recreate the *colours* in the colour map by this method, it is not possible to arrange them in the right order.

Without the right order, the animated colours will not flow into each other - they'll jump about all over the place at random.

I'm going to experiment with writing an image class which will allow direct access to the image's original colour map. I'll then pass the map to a custom filter.

Thanks for your efforts. This has turned out to be a tricky question, and your input has been helpful.
Had another stray thought. If you set canFilterIndexColorModel to false for the "nothing" filter, then you WOULD get positional information with the colors. That might allow you to sort it (based on the y value for instance). Would that help?

Avatar of wagtail

ASKER

Yes it might - thanks for this.

However, I've just found a really great solution - I override RGBFilterIndex's filterIndexColorModel() method. It actually does the colour model filtering when canFilterIndexColorModel is false. Takes the original colour model as input,
puts the 256 colour entries through filterRGB with (-1, -1) for positional data,
and then outputs a new, altered colour model.

I make filterRGB just pass the rgb value straight through unchanged, but plug
my rotation code straight into filterIndexColorModel(). Works a treat!


Avatar of wagtail

ASKER

woops - that third sentence should read "when canFilterIndexColorModel is *true*".

Too many late nights staring at the screen... 8^)