java.net member

Rechercher dans ce site

A graphical counter on GAEJ (Google App Engine for Java) using Images API Service

>> 26 May 2010

Images Service on GAEJ provides the ability to manipulate images, thus you can composite multiple images into a single one. I'll use this possibility to display a graphical hit counter. This  tutorial is only a kind of how-to. I'm sure you can write real programs using the instructions given  in this post. For simplicity reasons error handling are reduced to the minimum.

The idea is pretty simple, persist a counter using Memcache or DataStore, have digits from 0 to 9 as  PNG images, read images as bytes, make images and composites of these images, put all composites in a List and finally use this List to get the composed image.


PNG or Gif Images?
Png images does not render transparency very well ! If you want to get clean, with transparent background counter, you can replace Png by .gif images, and setContentType accordingly.

Lets go !

First declare some variables

List<Composite> listComposites=new ArrayList<Composite>();
Image img=null;
Composite composite=null;

//n is the value of the counter
int n=0;

//offset between images
int offset=0;

Create a background image
This one will normally has the total of all images width as it's width. All images have the same height.
See readImage method below.

//create a background image, this image (a physical one) is read on disk as bytes
//com.google.appengine.api.images.Image;  
    img=ImagesServiceFactory.makeImage(readImage("images/counter/b.png"));

makeComposite
makeComposite is defined like this :
public static Composite makeComposite(Imageimage,
                                      intxOffset,
                                      intyOffset,
                                      floatopacity,
                                      Composite.Anchoranchor)
Parameters:
image - The image to be composited.
xOffset - Offset in the x axis from the anchor point.
yOffset - Offset in the y axis from the anchor point.
opacity - Opacity to be used for the image in range [0.0, 1.0].
anchor - Anchor position from the enum Composite.Anchor. The anchor position of the image is aligned with the anchor position of the canvas and then the offsets are applied.

Make a Composite of the background Image

//create a Composite for the background image
    composite= ImagesServiceFactory.makeComposite(img, 0, 0, 1, Anchor.TOP_LEFT);
  

Add the background Composite to the List

  
    listComposites.add(composite);

Get the value of the counter
See getNumber() method below.

//get the number you want to display
n=getNumber();

Get individual numbers of the counter
Using a String and it's characters (not very elegant, but it works)
String s=Integer.toString(n);
for(char c:s.toCharArray())

Make Images and Composites

  • Make an image for every number of the counter,
  • Make a Composite,
  • Add the Composite to the List of Composites
  • Add the width of an image to the offset

for(char c:s.toCharArray())
                {
              
                img=ImagesServiceFactory.makeImage(readImage("images/counter/"+c+".png"));
              
                composite=ImagesServiceFactory.makeComposite(img, offset, 0, 1, Anchor.TOP_LEFT);
              
                listComposites.add(composite);
                offset+=14;
                }

Define a color for the background
color - Background color of the canvas in ARGB format.

long color=0xffffff;



Get the final image
Use composite method of ImageService
This method return an Image, it's bytes can be get by using getImageData(), you can write these bytes to your servlet (for instance ) to display the image.

Image composite(java.util.Collection<Composite>composites,
                intwidth,
                intheight,
                longcolor)


Applies the provided Collection of Composites using a canvas with dimensions determined by width and height and background color color. Uses PNG as its output encoding.

Parameters:
composites - Compositing operations to be applied.
width - Width of the canvas in pixels.
height - Height of the canvas in pixels.
color - Background color of the canvas in ARGB format.
Returns:
A new image containing the result of composition.
Throws:
java.lang.IllegalArgumentException - If width or height is greater than 4000, color is outside the range [0, 0xffffffff], composites contains more than 16 elements or something is wrong with the contents of composites.
ImagesServiceFailureException - If there is a problem with the Images Service

TutorCounterServlet.java
package com.java_javafx;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.images.Composite;
import com.google.appengine.api.images.Image;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.Composite.Anchor;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceException;
import com.google.appengine.api.memcache.MemcacheServiceFactory;

/**
 * @author Kaesar ALNIJRES
 *
 */

public class TutorCounterServlet extends HttpServlet{

    private PersistenceManager pm=null;
    private final static Logger logger=Logger.getLogger("CounterServlet");
  
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException,IOException {
      
            resp.setContentType("image/png");
          
            ServletOutputStream out = resp.getOutputStream();
          
    //First some variables
      
    List<Composite> listComposites=new ArrayList<Composite>();
    Image img=null;
    Composite composite=null;
  
    int n=0;//This one will be rendered by a composition of image
    int offset=0;//offset between images (normally width of one image)
 
    //create a background image, this image (a physical one) is read on disk as bytes
   //com.google.appengine.api.images.Image;  
    img=ImagesServiceFactory.makeImage(readImage("images/counter/b.png"));
  
  
  
    //create a Composite for the background image
    composite= ImagesServiceFactory.makeComposite(img, 0, 0, 1, Anchor.TOP_LEFT);
  
    //Add the background Composite to the list
    listComposites.add(composite);

    //get the number you want to display (your counter)
n=getNumber();
String s=Integer.toString(n);

    
          
//every character here is an individual number of the counter          
            for(char c:s.toCharArray())
                {
                //create an image from bytes for every number of the counter, make a composite for every image
                //and add this compoiste to the list of composite
                img=ImagesServiceFactory.makeImage(readImage("images/counter/"+c+".png"));
              
                composite=ImagesServiceFactory.makeComposite(img, offset, 0, 1, Anchor.TOP_LEFT);
              
                listComposites.add(composite);
                //Modify this to the width of one image
                offset+=14;
                }
          
      
          
    
      



long color=0xffffff;
int widthOfCounter=70;
int heightOfCounter=16;

byte[] imgComplet=ImagesServiceFactory.getImagesService().composite(listComposites, widthOfCounter, heightOfCounter, color).getImageData();


out.write(imgComplet);
out.flush();



}
    private byte[] readImage(String fileName)
    {
        byte[] buffer;
      
        try
        {
        FileInputStream in=new FileInputStream(fileName);
        buffer=new byte[in.available()];
         in.read(buffer);
         in.close();
        
         return buffer;
        }
        catch(Exception e)
        {
            logger.severe("Exception "+e.getMessage());
        return null;  
        }
    }
//==============================================================================
    //This method will read the counter value from memcache or DataStore. The counter is called "counter" in memcache
    //give it any name you want
    private int getNumber()
    {
int counter=0;
      
      
        try {
            pm=PMF.get().getPersistenceManager();
            //get memcache   
            MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();

            //increment counter
            Long lCounter=memcache.increment("counter", 1);
          
            //if the key is in memcache and increment is ok
            if(lCounter !=null)
                counter=lCounter.intValue();
            else
            {
              
                List<Counter> pCounters = (List<Counter>) pm.newQuery("select from "+Counter.class.getName()).execute();
                if (pCounters.isEmpty()) //if no record
                 {
                    counter=1;
                    memcache.put("counter",1);
                  
                 }
                else
                    {
                    counter=pCounters.size()+1;
                    memcache.put("counter",pCounters.size()+1);
                    }
            }
              
                Counter c1=new Counter();
                c1.setCounter(1);
                pm.makePersistent(c1);
          
            }
            catch (MemcacheServiceException e) {
                            // Ignore cache problems, nothing we can do. Put log a message
                logger.severe("memcache exception "+e.getMessage());
                            }
            catch(Exception e)
            {
                logger.severe("General exception "+e.getMessage());  
            }
            finally
            {
                if(pm != null)
                pm.close();
              
              
            }
            return counter;
    }
//==============================================================================  
}

Counter.java

package com.java_javafx;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;

/**
 * @author Kaesar ALNIJRES
 *
 */
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Counter {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
  
    @Persistent
    private int counter;

    /**
     * @return the key
     */
    public Key getKey() {
        return key;
    }

    /**
     * @param key the key to set
     */
    public void setKey(Key key) {
        this.key = key;
    }

    /**
     * @return the counter
     */
    public int getCounter() {
        return counter;
    }

    /**
     * @param counter the counter to set
     */
    public void setCounter(int counter) {
        this.counter = counter;
    }
}

PFM.java
package com.java_javafx;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
/**
 * @author Kaesar ALNIJRES
 *
 */
public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }
}

Thank you for reading. Hope you find this post helpful.

1 comments:

Vinny2020 January 12, 2011 at 4:38 PM  

Thank you! I have been searching for an example of Composite() on the appengine

Post a Comment

  © Blogger template Simple n' Sweet by Ourblogtemplates.com 2009

Back to TOP