Saturday, January 30, 2010

How to implement custom Paint in 50 lines of Java



Java offers many ways to customize strokes and fills, including the use of gradient fills and image fills (see this excellent tutorial by Marty Hall), but we tend to forget that "procedural textures" are easily implemented as custom Paint.

The text shown above was painted using a custom Paint class, SinePaint.java, consisting of around 50 lines of code (not counting imports), as shown below. (Scroll the code sideways to see lines that didn't wrap.) The SinePaint class procedurally generates a sine-wave fill pattern in the red color channel, running in the 'y' direction (vertical sine wave).

/* SinePaint
* Kas Thomas
* 30 January 2010
* Public domain.
* http://asserttrue.blogspot.com/
*
* A quick example of how to implement java.awt.Paint
*/


import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

class SinePaint implements Paint {

public SinePaint() {}

public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds,
Rectangle2D userBounds,
AffineTransform xform,
RenderingHints hints) {
return new Context(cm, xform);
}

public int getTransparency() {
return java.awt.Transparency.OPAQUE;
}

class Context implements PaintContext {

public Context(ColorModel cm_, AffineTransform xform_) { }

public void dispose() {}

public ColorModel getColorModel() {
return ColorModel.getRGBdefault();
}

public Raster getRaster(int xOffset, int yOffset, int w, int h) {

WritableRaster raster =
getColorModel().createCompatibleWritableRaster(w, h);
float [] color = new float[4];

// Row major traversal.
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
color[3] = 255;
color[2] = color[1] = 0;
// Write a sine-wave pattern to the Red channel
color[ 0 ] =
(1 + (float) Math.sin( 6.28f*((double) j)/h )) * .5f * 255;;

raster.setPixel(i, j, color);
} // i
} // j
return raster;
} // getRaster()
} // Context
} // SinePaint

Implementing the Paint interface turns out not to be such a big deal. There's only one required method, createContext():
   public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds,
Rectangle2D userBounds,
AffineTransform xform,
RenderingHints hints)
Most of the formal parameters are hints and can safely be ignored. Note that this method returns a java.awt.PaintContext object. It turns out PaintContext is an interface as well, so you do end up having to implement it, and this is where the real action occurs. The methods of the PaintContext interface include:

public void dispose() {};
public ColorModel getColorModel();
public Raster getRaster(int x,
int y,
int w,
int h);

The dispose() method releases any resources that were allocated by the class. In our case, we allocated nothing and so our dispose method is empty. The getColorModel() method can, in most cases, be a one-liner that simply returns ColorModel.getRGBdefault(). The real action is in getRaster(). That's where you have the opportunity to set the pixel values for all the pixels in the raster based on their x-y values. If you're familiar with shaders and/or procedural textures, you know what this is about. This is your opportunity to shade an area in accordance with a pixel's x-y location onscreen (or rather, within the image).

If you've been using the ImageMunger app I wrote about a few days ago, you can run the following script with it to see SinePaint in operation. (This is the script that produced the colored text shown above.)

/* paintedText.js
* Kas Thomas
* 30 January 2010
* Public domain.
*
* Run this file using ImageMunger:
* http://asserttrue.blogspot.com/2010/01/simple-java-class-for-running-scripts.html
*/


g2d = Image.createGraphics();

rh = java.awt.RenderingHints;
hint = new rh( rh.KEY_TEXT_ANTIALIASING,rh.VALUE_TEXT_ANTIALIAS_ON );
g2d.setRenderingHints( hint );

sinePaint = new Packages.SinePaint( );

g2d.setPaint( sinePaint );
g2d.setFont( new java.awt.Font("Times New Roman",java.awt.Font.BOLD,130) );
g2d.drawString( "Shiny",50,100);
g2d.drawString( "Text",50,200);

Panel.updatePanel();
(Scroll sideways to see lines that didn't wrap.)

Future projects:
  • Make use of the AffineTransform argument to enable scaled and otherwise transformed textures.
  • Implement Perlin noise as a type of Paint.
  • Implement "bump map" 3D effects in Paint.

16 comments:

  1. Can this interface be used for something that is not a gui app - e.g. generating pngs on the server ?

    ReplyDelete
    Replies
    1. This was very nice and informative article. Thanks admin for the nice post.
      Funny Wi-Fi Names

      Delete
  2. Great sharing...
    Thanks.....

    ReplyDelete
  3. Great! Post. I really appreciate the idea behind this post. Really awesome, I wish I could be express my feeling on my blog like that Fortnite Skins Names, Clan Names, Fortnite Names.

    ReplyDelete
  4. ESP Hack Pubg Mobile Emulator Free Download
    esp hack pubg mobile

    ReplyDelete
  5. Check out How To Repost on Instagram on Android.

    ReplyDelete
  6. All the information in this post is awesome and very interesting so thank you so much for sharing this post.
    Website Development Company

    ReplyDelete
  7. Excellent and very informative post. Thanks for the nice post. I love so much this post.

    Wi-Fi Names

    ReplyDelete
  8. I love so much your nice post. Thanks for the post. If anyone need web 2.0 backlinks service please visit my profile.

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. I love so much your nice post. Thanks for the post. If anyone need super web 2.0 backlinks service then please contact me.

    ReplyDelete
  11. Google started using site speed as a ranking signal in their algorithm way back in 2010, and it continues to serve as one of the many factors that determine where your website shows up in the search results. We help you WordPress Speed Optimization are Page Caching, PHP latest version, Image optimization and resizing, jquery update, Cache Preloading, Sitemap Preloading, GZIP Compression, Browser Caching, Database Optimization, Google Fonts Optimization, Lazyload, Minifying JS CSS HTML files, Deferring Unused JS/CSS, CDN setup, Mobile Detection, Stop unused CSS and JS file and many more optimization your WordPress website. So, your website super-fast loading within 1-5 seconds. WordPress Speed Optimization

    ReplyDelete
  12. Trải qua hơn 40 năm xây dựng và phát triển, Công Ty Cổ Phần Tư Vấn Điện 3 đã không ngừng lớn mạnh, luôn hoàn thành xuất sắc mọi nhiệm vụ được giao và đã được Nhà Nước nhiều HC lao động và nhiều bằng khen.

    ReplyDelete

Add a comment. Registration required because trolls.