Continuing to very powerful Rendering Model Java2D. Summary about Java2D API from previous week That API is not considered a part of Swing; but it is closely related to Swing & effectively used to develop sophisticated Swing applications. Consists of a set of classes and interfaces for advanced 2D line art, text, and image rendering. Some classes and interfaces from package jawa.awt Graphicd2D Graphics subclass for rendering 2D shapes, text and images BasicStroke Defines a basic set of rendering attributes for outlines of graphics primitives GradientPaint provides a way to fill and outline 2D shapes with a linear color gradient TexturePaint provides a way to fill and outline shapes with texture images Paint define how color patterns can be generated for rendering operations Shape provides definitions for geometrical objects Stroke provides methods for obtaining the outline of a geometrical shape Some classes and interfaces from package java.awt.geom General Path represents a path constructed from straight lines, quadratic curves and cubic curves Line2D Represents a line in coordinate space RectangularShape Base class for geometrical shapes with rectangular frames. Subclasses include Arc2D,Ellipse2D, Rectangle2D and RoundRectangle2D BufferedImage Describes an Image with a buffer of colored pixel data composed of a ColorModel and a Raster ColorModel Defines methods for translating a numerical pixel value to color Some classes and interfaces from package java.awt.image Raster is part of a BufferedImage that describes sample valued in a rectangular array of pixels Kernel describes 2D array used for filtering BufferedImages BufferedImageOp defines methods that perform operations on BufferedImages RasterOp describes single-input/single-output processes performed on Rasters Defining BufferedImage object new BufferedImage (int width, int height, int type) Gives us an image of the width/height/type we require, synchronously. That is: After the method returns, that image does exist We can then get at the data directly getRGB, setRGB, Raster object allows more ways of getting at the pixel data, get the ColorModel, ……………………………………………… BufferedImage b= new BufferedImage (10,10,BufferedImage.TYPE_INT_RGB); //This BufferedImage is 10 pixels wide and 10 pixels height //From the third image is stored in RGB color sheme. Graphics2D graph = b.createGraphics(); //To create the pattern, first draw into BufferedImage //Created a Graphics2D object for drawing on the BufferedImage graph.setColor (Color.yellow); //draw in yellow graph.fillRect (0,0,10,10); //draw filled rectangle …………………………………………………………… /**graph2D is previously created to get Graphics2D by casting g to Graphics2D **/ // Graphics2D= graph2D = (Graphics2D) g; graph2D.setPaint (new TexturePaint (b, new Rectangle (10,10))); //set the Paint object to a new TexturePaint object graphics2D.fill (new RoundRectangle2D.Double (155,30,75,100,50,50)); //invoked the fill method of Graphics2D to draw a filled objectRoundRectangle2D.Double TexturePaint class TexturePaint ( BufferedImage txt, Rectangle2D anchor) txt the BufferedImage object with the texture used for painting anchor - the Rectangle2D in user space from the BufferedImage used to anchor and replicate the texture A TexturePaint object uses the image stored in its associated BufferedImage as the fill texture for a filled-in shape. The TexturePaint class provides a way to fill any shape with a texture that is specified as a BufferedImage. The size of the BufferedImage object should be small because the BufferedImage data is copied by the TexturePaint object. But, rectangle may be the same size of BufferedImage At construction time, the texture is anchored to the upper left corner of a Rectangle2D that is specified in user space. Texture is computed for locations in the device space by conceptually replicating the specified Rectangle2D infinitely in all directions in user space and mapping the BufferedImage to each replicated Rectangle2D. TYPE_INT_RGB public static final int TYPE_INT_RGB Represents an image with 8-bit RGB color components packed into integer pixels. The image has a DirectColorModel without alpha. TYPE_BYTE_BINARY public static final int TYPE_BYTE_BINARY Represents an opaque byte-packed 1, 2, or 4 bit image. The image has an IndexColorModel without alpha. When this type is used as the imageType argument to the BufferedImage constructor that takes an imageType argument but no ColorModel argument, 1-bit image is created with an IndexColorModel with two colors in the default RGB ColorSpace: {0, 0, 0} and {255, 255, 255}. Images with 2 or 4 bits per pixel may be constructed via the BufferedImage constructor that takes a ColorModel argument by supplying a ColorModel with an appropriate map size. Images with 8 bits per pixel should use the image types TYPE_BYTE_INDEXED or TYPE_BYTE_GRAY depending on their ColorModel. What are Alpha Textures? An alpha texture is a texture that contains only alpha channel, transparency, information. Typically this is used to "stencil" a piece of geometry to only leave pieces visible without chewing very large numbers of polygons. Effectively it is a cookie-cutter for geometry. Where the alpha texture is completely transparent, we see nothing. Where it is fully opaque, we see whatever underlying geometry is there - including the material color. If we go the multi-texture route, it would also include the other textures. A typical use for alpha textures is for creating 2D text in a 3D world. Creating an alpha texture Dynamically creating one in code or Loading one from a file. (When we load from a file, we get whatever the file format has). The main feature of an alpha texture is that it only contains one piece of information - the alpha channel. This could be expressed in two ways a full RGBA image with the color channels ignored, We use 4 bytes per pixel, and effectively throwing three of them away, a simple grey-scale image with a single channel, which is interpreted to mean alpha channel information We use one byte per pixel. Creating Source Image Firstly we must start by a BufferedImage we set the type to be a greyscale image (1 byte per channel) BufferedImage b = new BufferedImage(128, 128, BufferedImage.TYPE_BYTE_GRAY); We can also use the byte-packed format (TYPE_BYTE_BINARY) if we want to save even more memory, these are really only useful if all we want is transparent/nontransparent, rather than shades of transparency. We need to draw to the texture. We start with the usual fetch of the Graphics context to draw with Graphics2D g = b.createGraphics(); Creating Source Image (cont’d) We have to execute the drawing instructions. How we are going to draw to the image? Clear all the alpha bits or make the image all transparent and just draw the parts we want to see OR We need to decide on the colors to use, and this is the tricky bit. To create instances of Color that have the Alpha value set Color CLEAR_COLOR = new Color(0, 0, 0, 0); Color VISIBLE_COLOR = new Color(0, 0, 0, 1f); This is not correct We are drawing to an image that only has one byte of color representation. If we did the above, all we get is a completely clear image. The drawing routines will just ignore the alpha value completely and only work from the color value. Creating Source Image (cont’d) This is a greyscale image so we really need to think in terms of black and white. What black and white mean in the context of transparency? If something is transparent, we have a (normal) alpha value of zero. If this alpha value is sourced from a black and white image, we need to have the value that is zero in that image be the transparent bit In the color world, what has a value of zero? Black. what is the visible bit? White. color definitions really need to be this: Color CLEAR_COLOR = Color.black; Color VISIBLE_COLOR = Color.white Creating Source Image (cont’d) We are ready to draw away. We clear all the image and then draw a box with a hole in it: g.setColor (CLEAR_COLOR); g.fillRect (0, 0, 128, 128); g.setColor (VISIBLE_COLOR); g.fillRect (32, 32, 64, 64); g.setColor (CLEAR_COLOR); g.fillRect (48, 48, 32, 32); g.dispose(); Example: Tiled Images as Fill Patterns import javax.swing.*; import java.awt.*; import java.awt.geom.*; import java.awt.image.*; /** An example of using TexturePaint to fill objects with tiled images. Uses the getBufferedImage method of ImageUtilities to load an Image from a file and turn that into a BufferedImage **/ public class TiledImages extends JPanel { private String dir = System.getProperty ("user.dir"); private String imageFile1 = dir + "/images/yourpicture.jpg"; private TexturePaint imagePaint1; private Rectangle imageRect; private String imageFile2 = dir + "/images/yourshape.gif"; private TexturePaint imagePaint2; private int[] xPoints = { 30, 700, 400 }; private int[] yPoints = { 30, 30, 600 }; private Polygon imageTriangle = new Polygon (xPoints, yPoints, 3); public TiledImages() { BufferedImage image = ImageUtilities.getBufferedImage(imageFile1, this); imageRect = new Rectangle(235, 70, image.getWidth(), image.getHeight()); imagePaint1 = new TexturePaint (image, imageRect); image = ImageUtilities.getBufferedImage(imageFile2, this); imagePaint2 = new TexturePaint(image, new Rectangle(0, 0, 32, 32)); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; g2d.setPaint(imagePaint2); g2d.fill(imageTriangle); g2d.setPaint(Color.blue); g2d.setStroke(new BasicStroke(5)); g2d.draw(imageTriangle); g2d.setPaint(imagePaint1); g2d.fill(imageRect); g2d.setPaint(Color.black); g2d.draw(imageRect); } public static void main(String[] args) { WindowUtilities.openInJFrame(new TiledImages(), 750, 650); } } Stroke Interface abstract interface java.awt.Stroke. Defines only one method: createStrokedShape(Shape p), which generates a Shape that is the outline of the given Shape parameter. This outline can be of various size, shape. The only implementing class is BasicStroke We use Strokes to define line styles for drawing in the Java2D graphics context. To set the stroke attribute of a given Graphics2D we use its setStroke() method. BasicStroke: class java.awt.BasicStroke This class implements the Stroke interface and defines a set of rendering attributes specifying how to render the outline of a Shape. These attributes consist of line width, join style, end-cap style, and dash style: The line width (often called the pen width) is the thickness measured perpendicular to its trajectory. The end-cap style specifies whether round, butt, or square ends are used to render the ends of line segments: CAP_ROUND, CAP_BUTT, and CAP_SQUARE. The join style specifies how to render the joints between segments. This can be one of bevel, miter, or round: JOIN_BEVEL, JOIN_MITER, and JOIN_ROUND. The dash style defines a pattern of opaque and transparent regions rendered along a line segment. ImageUtilities.java A class that simplifies a few common image operations Creating a BufferedImage from an image file, using MediaTracker to wait until an image or several images are done loading. import java.awt.*; import java.awt.image.*; public class ImageUtilities { // Create Image from a file, then turn that into a BufferedImage. public static BufferedImage getBufferedImage(String imageFile, Component c) { Image image = c. getToolkit() .getImage (imageFile); waitForImage(image, c); BufferedImage bufferedImage = new BufferedImage(image.getWidth(c), image.getHeight(c), BufferedImage.TYPE_INT_RGB); Graphics2D g2d = bufferedImage.createGraphics(); g2d.drawImage(image, 0, 0, c); return(bufferedImage); } //Take an Image associated with a file, and wait until it is done loading. //Just a simple application of MediaTracker. //If we are loading multiple images, we don't use this consecutive times; // instead we use the version that takes an array of images. public static boolean waitForImage(Image image, Component c) { MediaTracker tracker = new MediaTracker(c); tracker.addImage(image, 0); try { tracker.waitForAll(); } catch(InterruptedException ie) {} return (!tracker.isErrorAny()); } //Take some Images associated with files //wait until they are done loading. //Just a simple application of MediaTracker. public static boolean waitForImages (Image[] images, Component c) { MediaTracker tracker = new MediaTracker(c); for (int i=0; i<images.length; i++) tracker.addImage(images[i], 0); try { tracker.waitForAll(); } catch(InterruptedException ie) {} return(!tracker.isErrorAny()); } } public class MediaTracker The MediaTracker class is a utility class to track the status of a number of media objects. Media objects could include audio clips as well as images, though currently only images are supported. To use a media tracker, create an instance of MediaTracker and call its addImage method for each image to be tracked. In addition, each image can be assigned a unique identifier. This identifier controls the priority order in which the images are fetched. It can also be used to identify unique subsets of the images that can be waited on independently. Images with a lower ID are loaded in preference to those with a higher ID number.