?? ch13.htm
字號:
classes for using filters: FilteredImageSource and ImageFilter.<H3><A NAME="FilteredImageSource">FilteredImageSource</A></H3><P>The FilteredImageSource class implements the ImageProducer interface,which allows the class to masquerade as a real producer. Whena consumer attaches to the FilteredImageSource, it's stored inan instance of the current filter. The filter class object isthen given to the actual ImageProducer. When the image is renderedthrough the filter's interface, the data is altered before beingforwarded to the actual ImageConsumer. Figure 13.3 illustratesthe filtering operation.<P><A HREF="f13-3.gif" ><B>Figure 13.3 : </B><I>Image filtering classes.</I></A><P>The following is the constructor for FilteredImageSource:<BLOCKQUOTE><TT>FilteredImageSource(ImageProducer orig,ImageFilter imgf);</TT></BLOCKQUOTE><P>The producer and filter are stored until a consumer attaches itselfto the FilterImageSource. The following lines set up the filterchain:<BLOCKQUOTE><TT>// Create the filter<BR>ImageFilter filter = new SomeFilter();<BR>// Use the filter to get a producer<BR>ImageProducer p = new FilteredImageSource(myImage.getSource(),filter);<BR>// Use the producer to create the image<BR>Image img = createImage(p);</TT></BLOCKQUOTE><H3><A NAME="WritingaFilter">Writing a Filter</A></H3><P>Filters always extend the ImageFilter class, which implementsall the methods for an ImageConsumer. In fact, the ImageFilterclass is itself a pass-through filter. It passes the data withoutalteration but otherwise acts as a normal image filter. The FilteredImageSourceclass works only with ImageFilter and its subclasses. Using ImageFilteras a base frees you from having to implement a method you haveno use for, such as <TT>setProperties()</TT>.ImageFilter also implements one additional method:<UL><LI><TT>public void resendTopDownLeftRight(ImageProducerip);</TT></UL><P>When a FilteredImageSource gets a request to resend through itsImageProducer interface, it will call the ImageFilter insteadof the actual producer. ImageFilter's default resend functionwill call the producer and request a repaint. There are timeswhen the filter does not want to have the image regenerated, soit can override this call and simply do nothing. One example ofthis type of filter is described in the section "DynamicImage Filter: FXFilter." A special-effects filter may simplyremove or obscure certain parts of an underlying image. To performthe effect, the filter merely needs to know the image dimensions,not the specific pixels it will be overwriting. <TT>SetPixel()</TT>calls are safely ignored, but the producer must be prevented fromrepainting. If your filter does not implement <TT>setPixels()</TT>calls, a subsequent resend request will destroy the filter's changesby writing directly to the consumer.<BR><P><CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%><TR VALIGN=TOP><TD><B>Note</B></TD></TR><TR VALIGN=TOP><TD><BLOCKQUOTE>If <TT>setPixels()</TT> is not overridden in your filter, you will probably want to override <TT>resendTopDownLeftRight()</TT>to prevent the image from being regenerated after your filter has altered the image.</BLOCKQUOTE></TD></TR></TABLE></CENTER><H3><A NAME="StaticImageFilterRotation">Static Image Filter: Rotation</A></H3><P>The SimpleRoll applet works by loading four distinct images; rememberthat an external paint application was used to rotate each image.Unfortunately, the paint program cannot maintain the transparencyof the original image. You can see this if you change the backgroundcolor of the applet. The bounding rectangle of the image showsup in gray. Instead of loading the four images, a Java rotationfilter can be substituted to allow any image to be rolled. Notonly would this minimize the download time, but it would alsomaintain the image's transparency information. A transparent foregroundimage also allows a background image to be added.<H4>Pixel Rotation</H4><P>To perform image rotation, you need to use some math. You canperform the rotation of points with the following formulas:<BLOCKQUOTE><TT>new_x = x * cos(angle) - y * sin(angle)<BR>new_y = y * cos(angle) + x * sin(angle)</TT></BLOCKQUOTE><P>Rotation is around the z-axis. Positive angles cause counterclockwiserotation, and negative angles cause clockwise rotation. Theseformulas are defined for Cartesian coordinates. The Java screenis actually inverted, so the positive y-axis runs down the screen,not up. To compensate for this, invert the sign of the sine coefficients:<BLOCKQUOTE><TT>new_x = x * cos(angle) + y * sin(angle)<BR>new_y = y * cos(angle) - x * sin(angle)</TT></BLOCKQUOTE><P>In addition, the sine and cosine functions compute the angle inradians. The following formula converts degrees to radians:<BLOCKQUOTE><TT>radians = degrees * PI/180;</TT></BLOCKQUOTE><P>This works because there are <TT>2*PI</TT>radians in a circle. That's all the math you'll need; now youcan set up the ImageConsumer routines.<H4>Handling <TT>setDimensions()</TT></H4><P>The <TT>setDimensions()</TT> calltells you the total size of the image. Record the size and allocatean array to hold all the pixels. Because this filter will rotatethe image, the size may change. In an extreme case, the size couldgrow much larger than the original image because images are rectangular.If you rotate a rectangle 45 degrees, a new rectangle must becomputed that contains all the pixels from the rotated image,as shown in Figure 13.4.<P><A HREF="f13-4.gif" ><B>Figure 13.4 : </B><I>New bounding rectangle after rotation.</I></A><P>To calculate the new bounding rectangle, each vertex of the originalimage must be rotated. After rotation, the new coordinate is checkedfor minimum and maximum x and y values. When all four points arerotated, then you'll know what the new bounding rectangle is.Record this information as rotation space, and inform the consumerof the size after rotation.<H4>Handling <TT>setPixels()</TT></H4><P>The <TT>setPixels()</TT> calls arevery straightforward. Simply translate the pixel color into anRGB value and store it in the original image array allocated in<TT>setDimensions()</TT>.<H4>Handling <TT>imageComplete()</TT></H4><P>The <TT>imageComplete()</TT> methodperforms all the work. After the image is final, populate a newrotation space array and return it to the consumer through theconsumer's <TT>setPixels()</TT> routine.Finally, invoke the consumer's <TT>imageComplete()</TT>method. Listing 13.2 contains the entire filter.<HR><BLOCKQUOTE><B>Listing 13.2. The SpinFilter class.<BR></B></BLOCKQUOTE><BLOCKQUOTE><TT>import java.awt.*;<BR>import java.awt.image.*;<BR><BR>public class SpinFilter extends ImageFilter<BR>{<BR> private double angle;<BR> private double cos, sin;<BR> private Rectangle rotatedSpace;<BR> private Rectangle originalSpace;<BR> private ColorModel defaultRGBModel;<BR> private int inPixels[], outPixels[];<BR><BR> SpinFilter(double angle)<BR> {<BR> this.angle = angle* (Math.PI / 180);<BR> cos = Math.cos(this.angle);<BR> sin = Math.sin(this.angle);<BR> defaultRGBModel= ColorModel.getRGBdefault();<BR> }<BR><BR> private void transform(int x, int y, doubleout[])<BR> {<BR> out[0] = (x *cos) + (y * sin);<BR> out[1] = (y *cos) - (x * sin);<BR> }<BR><BR> private void transformBack(int x, inty, double out[])<BR> {<BR> out[0] = (x *cos) - (y * sin);<BR> out[1] = (y *cos) + (x * sin);<BR> }<BR><BR> public void transformSpace(Rectangle rect)<BR> {<BR> double out[] =new double[2];<BR><BR> double minx =Double.MAX_VALUE;<BR> double miny =Double.MAX_VALUE;<BR> double maxx =Double.MIN_VALUE;<BR> double maxy =Double.MIN_VALUE;<BR> int w = rect.width;<BR> int h = rect.height;<BR> int x = rect.x;<BR> int y = rect.y;<BR><BR> for ( int i =0; i < 4; i++ )<BR> {<BR> switch(i)<BR> {<BR> case0: transform(x + 0, y + 0, out); break;<BR> case1: transform(x + w, y + 0, out); break;<BR> case2: transform(x + 0, y + h, out); break;<BR> case3: transform(x + w, y + h, out); break;<BR> }<BR> minx= Math.min(minx, out[0]);<BR> miny= Math.min(miny, out[1]);<BR> maxx= Math.max(maxx, out[0]);<BR> maxy= Math.max(maxy, out[1]);<BR> }<BR> rect.x = (int)Math.floor(minx);<BR> rect.y = (int)Math.floor(miny);<BR> rect.width = (int)Math.ceil(maxx) - rect.x;<BR> rect.height =(int) Math.ceil(maxy) - rect.y;<BR> }<BR><BR> /**<BR> * Tell the consumer the new dimensionsbased on our<BR> * rotation of coordinate space.<BR> * @see ImageConsumer#setDimensions<BR> */<BR> public void setDimensions(int width, intheight)<BR> {<BR> originalSpace= new Rectangle(0, 0, width, height);<BR> rotatedSpace =new Rectangle(0, 0, width, height);<BR> transformSpace(rotatedSpace);<BR> inPixels = newint[originalSpace.width * originalSpace.height];<BR> consumer.setDimensions(rotatedSpace.width,rotatedSpace.height);<BR> }<BR><BR> /**<BR> * Tell the consumer that we use the defaultRGBModelcolor model<BR> * NOTE: This overrides whatever colormodel is used underneath us.<BR> * @param model contains the color modelof the image or filter<BR> * beneathus (preceding us)<BR> * @see ImageConsumer#setColorModel<BR> */<BR> public void setColorModel(ColorModel model)<BR> {<BR> consumer.setColorModel(defaultRGBModel);<BR> }<BR><BR> /**<BR> * Set the pixels in our image array fromthe passed<BR> * array of bytes. Xlate thepixels into our default<BR> * color model (RGB).<BR>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -