Package ch.sahits.game.graphic.image

Source Code of ch.sahits.game.graphic.image.ImageSFXs

package ch.sahits.game.graphic.image;

// ImageSFXs.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th



import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.BandCombineOp;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.Raster;
import java.awt.image.RescaleOp;
import java.awt.image.ShortLookupTable;
import java.awt.image.WritableRaster;
import java.util.Random;

import org.apache.log4j.Logger;


/** Various image operations used as special effects.<br/>
<br/>
These methods are called from ImagesTests, usually from methods
which implement a series of images changes (an animation effect)
spread over several 'ticks' of the update/redraw animation
cycle.<br/>
<br/>
The types of SFXs and their public methods:<br/>
<br/>
drawImage() based effects
<ul>
<li>draw a resized image: drawResizedImage()
</li><li>return a flipped image: getFlippedImage()
</li><li>draw a flipped image: drawVerticalFlip(), drawHorizFlip()</li>
</ul>
Alpha composite effect
<ul>
<li>draw a faded image: drawFadedImage();</li>
</ul>
Affine transform effect
<ul>
<li>return a rotated image: getRotatedImage()</li>
</ul>
Convolution effect
<ul>
<li>draw a blurred image: drawBlurredImage();
</li><li>draw a blurred image with a specified blur size: drawBlurredImage()</li>
</ul>

LookupOp effect
</ul>
<li>draw a redder image: drawRedderImage()
             -- there are LookupOp and RescaleOp versions</li>
</ul>           
<br/>
RescaleOp effects
<ul>
<li>draw a brighter image: drawBrighterImage()
</li><li>draw a negated image: drawNegatedImage()</li>
</ul>

BandCombineOp effect
<ul>
<li>draw the image with mixed up colours: drawMixedColouredImage()</li>
</ul>

Pixel effects
<ul>
<li>make some of the image's pixels transparent: eraseImageParts()
</li><li>change some of the image's pixels to red or yellow:
                                                  zapImageParts()</li>
</ul>                                                 
* @author Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th
*/
public class ImageSFXs
{
  private static final Logger logger = Logger.getLogger(ImageSFXs.class);
  // constants used to specify the type of image flipping required
  public static final int VERTICAL_FLIP = 0;
  public static final int HORIZONTAL_FLIP = 1;
  public static final int DOUBLE_FLIP = 2;   // flip horizontally and vertically

  private GraphicsConfiguration gc;

  // pre-defined image operations
  private RescaleOp negOp, negOpTrans;   // image negation
  private ConvolveOp blurOp;    // image blurring

  public ImageSFXs()
  {
    // get the GraphicsConfiguration so images can be copied easily and
    // efficiently
    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    gc = ge.getDefaultScreenDevice().getDefaultConfiguration();

    initEffects();
  // end of ImageSFXs()


  private void initEffects()
  // Create pre-defined image operations for image negation and blurring.
  {
    // image negative.  Multiply each colour value by -1.0 and add 255
    negOp = new RescaleOp(-1.0f, 255f, null);

    // image negative for images with transparency
    float[] negFactors = {-1.0f, -1.0f, -1.0f, 1.0f}// don't change the alpha
    float[] offsets = {255f, 255f, 255f, 0.0f};
    negOpTrans = new RescaleOp(negFactors, offsets, null);


    // blur by convolving the image with a matrix
    float ninth = 1.0f / 9.0f;

    float[] blurKernel = {      // the 'hello world' of Image Ops :)
        ninth, ninth, ninth,
        ninth, ninth, ninth,
        ninth, ninth, ninth
    };

    blurOp = new ConvolveOp(
       new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, null);

  // end of initEffects()



  // ------------------ drawImage() based effects -----------------
  // resizing, flipping


  public void drawResizedImage(Graphics2D g2d, BufferedImage im, int x, int y,
                double widthChange, double heightChange)
  // draw a resized image by multiplying by widthChange and heightChange
  {
    if (im == null) {
      logger.warn("drawResizedImage: input image is null");
      return;
    }

    if (widthChange <= 0) {
      logger.debug("width change cannot <= 0");
      widthChange = 1.0;
    }
    if (heightChange <= 0) {
      logger.debug("height change cannot <= 0");
      heightChange = 1.0;
    }

    int destWidth = (int) (im.getWidth() * widthChange);
    int destHeight = (int) (im.getHeight() * heightChange);

    // adjust top-left (x,y) coord of resized image so it remains centered
    int destX = x + im.getWidth()/2 - destWidth/2;
    int destY = y + im.getHeight()/2 - destHeight/2;

    g2d.drawImage(im, destX, destY, destWidth, destHeight, null);
  } // end of drawResizedImage()



  // ------------------ flipping -----------------------

  public BufferedImage getFlippedImage(BufferedImage im, int flipKind)
  /* Return a new image which has been flipped horizontally, vertically,
     or both. */
  {
    if (im == null) {
      logger.warn("getFlippedImage: input image is null");
      return null;
    }

    int imWidth = im.getWidth();
    int imHeight = im.getHeight();
    int transparency = im.getColorModel().getTransparency();

    BufferedImage copy =  gc.createCompatibleImage(imWidth, imHeight, transparency);
    Graphics2D g2d = copy.createGraphics();
    // g2d.setComposite(AlphaComposite.Src);

    // draw in the flipped image
    renderFlip(g2d, im, imWidth, imHeight, flipKind);
    g2d.dispose();

    return copy;
  } // end of getFlippedImage()


  private void renderFlip(Graphics2D g2d, BufferedImage im,
                     int imWidth, int imHeight, int flipKind)
  // the flipping is achieved by supplying suitable coords to drawImage()
  {
    if (flipKind == VERTICAL_FLIP)
      g2d.drawImage(im, imWidth, 00, imHeight,
                      0, 0,  imWidth, imHeight, null);
    else if (flipKind == HORIZONTAL_FLIP)
      g2d.drawImage(im, 0, imHeight,  imWidth, 0,
                      0, 0,  imWidth, imHeight, null);
    else    // assume DOUBLE_FLIP
      g2d.drawImage(im, imWidth, imHeight,  0, 0,
                      0, 0,  imWidth, imHeight, null);

  // end of renderFlip()


  public void drawVerticalFlip(Graphics2D g2d, BufferedImage im, int x, int y)
  // Draw a vertically flipped image without creating a new
  // BufferedImage object.
  {
    if (im == null) {
      logger.warn("drawVerticalFlip: input image is null");
      return;
    }
    int imWidth = im.getWidth();
    int imHeight = im.getHeight();
    g2d.drawImage(im, x+imWidth, y,  x, y+imHeight,
                      0, 0,  imWidth, imHeight, null);
  } // end of drawVerticalFlip()


  public void drawHorizFlip(Graphics2D g2d, BufferedImage im, int x, int y)
  // Draw a horizontally flipped image without creating a new
  // BufferedImage object.
  {
    if (im == null) {
      logger.warn("drawHorizFlip: input image is null");
      return;
    }
    int imWidth = im.getWidth();
    int imHeight = im.getHeight();
    g2d.drawImage(im, x, y+imHeight,  x+imWidth, y,
                      0, 0,  imWidth, imHeight, null);
  } // end of drawHorizFlip()



  // --------------- alpha composite effect: fading  -----------------


  public void drawFadedImage(Graphics2D g2d, BufferedImage im,
                                   int x, int y, float alpha)
  /* The degree of fading is specified with the alpha value.
     alpha == 1 means fully visible, 0 mean invisible. */
  {
    if (im == null) {
      logger.warn("drawFadedImage: input image is null");
      return;
    }

    if (alpha < 0.0f) {
      logger.debug("Alpha must be >= 0.0f; setting to 0.0f");
      alpha = 0.0f;
    }
    else if (alpha > 1.0f) {
      logger.debug("Alpha must be <= 1.0f; setting to 1.0f");
      alpha = 1.0f;
    }

    Composite c = g2d.getComposite()// backup the old composite

    g2d.setComposite( AlphaComposite.getInstance(
                           AlphaComposite.SRC_OVER, alpha));
    g2d.drawImage(im, x, y, null);

    g2d.setComposite(c);
      // restore the old composite so it doesn't mess up future rendering
  // end of drawFadedImage()



  // --------------- affine transform effect: rotation  -----------------


  public BufferedImage getRotatedImage(BufferedImage src, int angle)
  /* Create a new BufferedImage which is the input image, rotated
     angle degrees clockwise.

     An issue is edge clipping. The simplest solution is to design the
     image with plenty of (transparent) border.
  */
  {
    if (src == null) {
      logger.warn("getRotatedImage: input image is null");
      return null;
    }

    int transparency = src.getColorModel().getTransparency();
    BufferedImage dest =  gc.createCompatibleImage(
                              src.getWidth(), src.getHeight(), transparency );
    Graphics2D g2d = dest.createGraphics();

    AffineTransform origAT = g2d.getTransform(); // save original transform
 
    // rotate the coord. system of the dest. image around its center
    AffineTransform rot = new AffineTransform();
    rot.rotate( Math.toRadians(angle), src.getWidth()/2, src.getHeight()/2);
    g2d.transform(rot);

    g2d.drawImage(src, 0, 0, null);   // copy in the image

    g2d.setTransform(origAT);    // restore original transform
    g2d.dispose();

    return dest;
  } // end of getRotatedImage()



  // --------------- convolution effect: blurring  -----------------


  public void drawBlurredImage(Graphics2D g2d,
                          BufferedImage im, int x, int y)
  // blurring with a fixed convolution kernel
  { if (im == null) {
    logger.warn("getBlurredImage: input image is null");
      return;
    }
    g2d.drawImage(im, blurOp, x, y);   // use the predefined ConvolveOp
  // end of drawBlurredImage()


  public void drawBlurredImage(Graphics2D g2d,
                          BufferedImage im, int x, int y, int size)
  /*
     The size argument is used to specify a size*size blur kernel,
     filled with 1/(size*size) values.

     Size should be odd so that the center cell of the kernel
     corresponds to the coordinate being blurred.

     The larger the size value, the larger the kernel, and more blurry
     the resulting image.

     An issue is the edge effects, which will produce a nasty black
     border or a border with no bluriness, depending on which
     ConvolveOp EDGE constant is used.
  */
  {
    if (im == null) {
      logger.warn("getBlurredImage: input image is null");
      return;
    }
    int imWidth = im.getWidth();
    int imHeight = im.getHeight();
    int maxSize = (imWidth > imHeight) ? imWidth : imHeight;
    if ((maxSize%2) == 0// if even
      maxSize--;  // make it odd

    if ((size%2) == 0) {  // if even
      size++;   // make it odd
      logger.debug("Blur size must be odd; adding 1 to make size = " + size);
    }

    if (size < 3) {
      logger.debug("Minimum blur size is 3");
      size = 3;
    }
    else if (size > maxSize) {
      logger.debug("Maximum blur size is " + maxSize);
      size = maxSize;
    }

    // create the blur kernel
    int numCoords = size * size;
    float blurFactor = 1.0f / (float) numCoords;

    float[] blurKernel = new float[numCoords];
    for (int i=0; i < numCoords; i++)
      blurKernel[i] = blurFactor;

    ConvolveOp blurringOp = new ConvolveOp(
                 new Kernel(size, size, blurKernel),
                 ConvolveOp.EDGE_NO_OP, null);   // leaves edges unaffected
              // ConvolveOp.EDGE_ZERO_FILL, null);  
                                                // edges are filled with black

    g2d.drawImage(im, blurringOp, x, y);
  // end of drawBlurredImage() with size argument



  // --------------- LookupOp effect: redden  -----------------

  /* There are two versions of drawRedderImage() here: one using
     a LookupOp, the other a RescaleOp. There is functionally no
     difference between them.
  */


  public void drawRedderImage(Graphics2D g2d, BufferedImage im,
                                       int x, int y, float brightness)
  // using LookupOp
  /* Draw the image with its redness is increased, and its greenness
     and blueness decreased. Any alpha channel is left unchanged.
  */
  { if (im == null) {
    logger.warn("drawRedderImage: input image is null");
      return;
    }

    if (brightness < 0.0f) {
      logger.debug("Brightness must be >= 0.0f; setting to 0.0f");
      brightness = 0.0f;
    }
    // brightness may be less than 1.0 to make the image less red

    short[] brighten = new short[256];    // for red channel
    short[] lessen = new short[256];      // for green and blue channels
    short[] noChange = new short[256];    // for the alpha channel

    for(int i=0; i < 256; i++) {
      float brightVal = 64.0f + (brightness * i);
      if (brightVal > 255.0f)
        brightVal = 255.0f;
      brighten[i] = (short) brightVal;
      lessen[i] = (short) ((float)i / brightness);
      noChange[i] = (short) i;
   }

    short[][] brightenRed;
    if (hasAlpha(im)) {
      brightenRed = new short[4][];
      brightenRed[0] = brighten;
      brightenRed[1] = lessen;
      brightenRed[2] = lessen;
      brightenRed[3] = noChange;    // without this the LookupOp fails
                                    // which is a bug (?)
    }
    else // not transparent
      brightenRed = new short[3][];
      brightenRed[0] = brighten;
      brightenRed[1] = lessen;
      brightenRed[2] = lessen;
    }
    LookupTable table = new ShortLookupTable(0, brightenRed);
    LookupOp brightenRedOp = new LookupOp(table, null);

    g2d.drawImage(im, brightenRedOp, x, y);
  // end of drawRedderImage() using LookupOp


/*
  public void drawRedderImage(Graphics2D g2d, BufferedImage im,
                                    int x, int y, float brightness)
  // using RescaleOp
  // Draw the image with its redness is increased, and its greenness
  // and blueness decreased. Any alpha channel is left unchanged.
  {
    if (im == null) {
      System.out.println("drawRedderImage: input image is null");
      return;
    }

    if (brightness < 0.0f) {
      System.out.println("Brightness must be >= 0.0f; setting to 0.0f");
      brightness = 0.0f;
    }
    // brightness may be less than 1.0 to make the image less red

    RescaleOp brigherOp;
    if (hasAlpha(im)) {
      float[] scaleFactors = {brightness, 1.0f/brightness, 1.0f/brightness, 1.0f};
                // don't change alpha
                // without the 1.0f the RescaleOp fails, which is a bug (?)
      float[] offsets = {64.0f, 0.0f, 0.0f, 0.0f};
      brigherOp = new RescaleOp(scaleFactors, offsets, null);
    }
    else {  // not transparent
      float[] scaleFactors = {brightness, 1.0f/brightness, 1.0f/brightness};
      float[] offsets = {64.0f, 0.0f, 0.0f};
      brigherOp = new RescaleOp(scaleFactors, offsets, null);
    }
    g2d.drawImage(im, brigherOp, x, y);
  }  // end of drawRedderImage() using RescaleOp
*/



  // --------------- RescaleOp effects: brighten, negate  ---------------


  public void drawBrighterImage(Graphics2D g2d, BufferedImage im,
                                      int x, int y, float brightness)
  /* Draw the image with changed brightness, by using a RescaleOp.
     Any alpha channel is unaffected. */
  {
    if (im == null) {
      logger.warn("drawBrighterImage: input image is null");
      return;
    }

    if (brightness < 0.0f) {
      logger.debug("Brightness must be >= 0.0f; setting to 0.5f");
      brightness = 0.5f;
    }
    // brightness may be less than 1.0 to make the image dimmer

    RescaleOp brigherOp;
    if (hasAlpha(im)) {
       float[] scaleFactors = {brightness, brightness, brightness, 1.0f};
                // don't change alpha
                // without the 1.0f the RescaleOp fails, which is a bug (?)
       float[] offsets = {0.0f, 0.0f, 0.0f, 0.0f};
       brigherOp = new RescaleOp(scaleFactors, offsets, null);
    }
    else   // not transparent
      brigherOp = new RescaleOp(brightness, 0, null);

    g2d.drawImage(im, brigherOp, x, y);
  // end of drawBrighterImage()


  public void drawNegatedImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* Draw the image with 255-<colour value> applied to its RGB components.
     Any alpha channel is unaffected. */
  {
    if (im == null) {
      logger.warn("drawNegatedImage: input image is null");
      return;
    }

    if (hasAlpha(im))
      g2d.drawImage(im, negOpTrans, x, y)// use predefined RescaleOp
    else
      g2d.drawImage(im, negOp, x, y);
  // end of drawNegatedImage()



  // ------------------- BandCombineOp effect  -------------------


  public void drawMixedColouredImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* Mix up the colours in the green and blue bands of the image. */
  {
    if (im == null) {
      logger.warn("drawMixedColouredImage: input image is null");
      return;
    }

    BandCombineOp changeColoursOp;
    Random r = new Random();

    if (hasAlpha(im)) {
      float[][] colourMatrix = {
         { 1.0f, 0.0f, 0.0f, 0.0f },           // for new red band, unchanged
         { r.nextFloat(), r.nextFloat(), r.nextFloat(), 0.0f },    // new green band
         { r.nextFloat(), r.nextFloat(), r.nextFloat(), 0.0f },    // new blue band
         { 0.0f, 0.0f, 0.0f, 1.0f} };   // unchanged alpha
      changeColoursOp =  new BandCombineOp(colourMatrix, null);
    }
    else {    // not transparent
      float[][] colourMatrix = {
         { 1.0f, 0.0f, 0.0f },           // for new red band, unchanged
         { r.nextFloat(), r.nextFloat(), r.nextFloat() },    // new green band
         { r.nextFloat(), r.nextFloat(), r.nextFloat() }};   // new blue band

      changeColoursOp =  new BandCombineOp(colourMatrix, null);
    }

    // create the Raster used by the filter
    Raster sourceRaster = im.getRaster();

    WritableRaster destRaster = changeColoursOp.filter(sourceRaster, null);

    // convert the destination Raster into a BufferedImage
    BufferedImage newIm = new BufferedImage(im.getColorModel(),
                                          destRaster, false, null);

    g2d.drawImage(newIm, x, y, null);
  // end of drawMixedColouredImage()



  // ------------------- Pixel effects -------------------
  // erasing, zapping


  public void eraseImageParts(BufferedImage im, int spacing)
  /* Change some of the image's pixels to 0's, which will become
     transparent in an image with an alpha channel, black otherwise.
  */
  {
    if (im == null) {
      logger.warn("eraseImageParts: input image is null");
      return;
    }

    int imWidth = im.getWidth();
    int imHeight = im.getHeight();

    int [] pixels = new int[imWidth * imHeight];
    im.getRGB(0, 0, imWidth, imHeight, pixels, 0, imWidth);

    int i = 0;
    while (i < pixels.length) {
      pixels[i] = 0;    // make transparent (or black if no alpha)
      i = i + spacing;
    }
 
    im.setRGB(0, 0, imWidth, imHeight, pixels, 0, imWidth);
  // end of eraseImageParts()



  public void zapImageParts(BufferedImage im, double likelihood)
  // change some of the image's pixels to red or yellow
  {
    if (im == null) {
      logger.warn("zapImageParts: input image is null");
      return;
    }

    if ((likelihood < 0) || (likelihood > 1)) {
      logger.debug("likelihood must be in the range 0 to 1");
      likelihood = 0.5;
    }

    int redCol = 0xf90000;      // nearly full-on red
    int yellowCol = 0xf9fd00;   // a mix of red and green

    int imWidth = im.getWidth();
    int imHeight = im.getHeight();

    int [] pixels = new int[imWidth * imHeight];
    im.getRGB(0, 0, imWidth, imHeight, pixels, 0, imWidth);

    double rnd;
    for(int i=0; i < pixels.length; i++) {
      rnd = Math.random();
      if (rnd <= likelihood) {
         if (rnd <=  15*likelihood/16 )    // make red more likely then yellow
           pixels[i] = pixels[i] | redCol;
         else
            pixels[i] = pixels[i] | yellowCol;
      }
    }
 
    im.setRGB(0, 0, imWidth, imHeight, pixels, 0, imWidth);
  // end of eraseImageParts()



  // --------- useful support methods --------------

  public boolean hasAlpha(BufferedImage im)
  // does im have an alpha channel?
  {
    if (im == null)
      return false;

    int transparency = im.getColorModel().getTransparency();

    if ((transparency == Transparency.BITMASK) ||
        (transparency == Transparency.TRANSLUCENT))
      return true;
    else
      return false;
  } // end of hasAlpha()


  public BufferedImage copyImage(BufferedImage src)
  // make a copy of the BufferedImage src
  {
    if (src == null) {
      logger.warn("copyImage: input image is null");
      return null;
    }

    int transparency = src.getColorModel().getTransparency();
    BufferedImage copy =  gc.createCompatibleImage(
                              src.getWidth(), src.getHeight(), transparency );
    Graphics2D g2d = copy.createGraphics();

    // copy image
    g2d.drawImage(src, 0, 0, null);
    g2d.dispose();

    return copy;
  } // end of copyImage()



  public BufferedImage makeTransImage(BufferedImage src)
  /* Make a copy of src, but in a BufferedImage object
     with an alpha channel. */
  {
    if (src == null) {
      logger.debug("makeTransImage: input image is null");
      return null;
    }

    BufferedImage dest = new BufferedImage( src.getWidth(), src.getHeight(),
                                  BufferedImage.TYPE_INT_ARGB)// alpha channel
    Graphics2D g2d = dest.createGraphics();

    // copy image
    g2d.drawImage(src, 0, 0, null);
    g2d.dispose();

    return dest;
  } // end of makeTransImage()

} // end of ImageSFXs
TOP

Related Classes of ch.sahits.game.graphic.image.ImageSFXs

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.