﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Pudgy's World-随笔分类-Graphics</title><link>http://www.blogjava.net/pudgy/category/2880.html</link><description>&lt;SCRIPT&gt;
var dict_width   = 'auto'
var dict_height  = '72px'
var dict_background = 'transparent'
var dict_border  = '0px dotted #000000'
var dict_textColor = '#6FBC4C'
var dict_fontSize = '100%'
var dict_encoding = 'Utf-8'
&lt;/SCRIPT&gt;
&lt;SCRIPT src="http://livid.cn/services/fortune"&gt;&lt;/SCRIPT&gt;
&lt;script src="http://www.google-analytics.com/urchin.js" type="text/javascript"&gt;
&lt;/script&gt;</description><language>zh-cn</language><lastBuildDate>Thu, 01 Mar 2007 12:15:42 GMT</lastBuildDate><pubDate>Thu, 01 Mar 2007 12:15:42 GMT</pubDate><ttl>60</ttl><item><title>Thumbnail.java - Load an image, scale it to thumbnail size and save it as JPEG</title><link>http://www.blogjava.net/Pudgy/archive/2005/08/29/11457.html</link><dc:creator>Pudgy's World</dc:creator><author>Pudgy's World</author><pubDate>Mon, 29 Aug 2005 08:12:00 GMT</pubDate><guid>http://www.blogjava.net/Pudgy/archive/2005/08/29/11457.html</guid><wfw:comment>http://www.blogjava.net/Pudgy/comments/11457.html</wfw:comment><comments>http://www.blogjava.net/Pudgy/archive/2005/08/29/11457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Pudgy/comments/commentRss/11457.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Pudgy/services/trackbacks/11457.html</trackback:ping><description><![CDATA[<P class=topmenu><FONT face=Verdana size=2>From </FONT><A href="http://schmidt.devlib.org/java/save-jpeg-thumbnail.html"><FONT face=Verdana size=2>http://schmidt.devlib.org/java/save-jpeg-thumbnail.html</FONT></A></P>
<H1><FONT face=Verdana size=4>Thumbnail.java - Load an image, scale it to thumbnail size and save it as JPEG</FONT></H1>
<P><FONT face=Verdana>This programm loads an image via <CODE>java.awt.Toolkit</CODE>, scales it down to a user-defined resolution and saves it as a </FONT><A href="http://schmidt.devlib.org/file-formats/jpeg-image-file-format.html"><FONT face=Verdana>JPEG</FONT></A><FONT face=Verdana> file. The first part, the loading of the original image, is done the same way as in </FONT><A href="http://schmidt.devlib.org/java/load-image-toolkit.html"><FONT face=Verdana>Viewer</FONT></A><FONT face=Verdana>. So if you don't know yet how loading images with <CODE>Toolkit</CODE> works you might want to study that program first. </FONT></P>
<P><FONT face=Verdana>Different from <EM>Viewer</EM>, this program (Thumbnail) works on the command line. So you won't get any windows or other graphical user interface components. The only visual feedback is the word <CODE>Done.</CODE> after the program successfully terminated. </FONT></P>
<P><FONT face=Verdana>To use this program do the following: </FONT></P>
<UL>
<LI><FONT face=Verdana>Save the </FONT><A href="http://schmidt.devlib.org/java/save-jpeg-thumbnail.html#source"><FONT face=Verdana>program's source code</FONT></A><FONT face=Verdana> as <CODE>Thumbnail.java</CODE> (regard case). </FONT></LI>
<LI><FONT face=Verdana>Open a shell (prompt), go to the directory where <CODE>Thumbnail.java</CODE> is and compile it: </FONT><PRE><FONT face=Verdana>javac Thumbnail.java</FONT></PRE><FONT face=Verdana>You should now have a new class file <CODE>Thumbnail.class</CODE>.</FONT></LI>
<LI><FONT face=Verdana>Run the program with five parameters for image file, thumbnail file, thumbnail width and thumbnail height and quality (a value from 0 to 100, 100 being the best and 0 the worst quality), e.g.: </FONT><PRE><FONT face=Verdana>java Thumbnail c:\image.jpg c:\thumbnail.jpg 120 80 75</FONT></PRE><FONT face=Verdana>The file <CODE>image.jpg</CODE> must exist already, <CODE>thumbnail.jpg</CODE> will be created (and any existing file of that name overwritten).</FONT></LI></UL>
<P><FONT face=Verdana>You will need Java 1.2 or higher to successfully run this program. The <CODE>com.sun.image.codec.jpeg</CODE> package that will be used for saving the thumbnail is not available with all Java development kits, but as long as you are using a Sun JDK, it should be present. </FONT></P>
<P><FONT face=Verdana>With Java 1.4 a new way of writing JPEG files was introduced, the image I/O library in the package <CODE>javax.imageio</CODE>. See </FONT><A href="http://schmidt.devlib.org/java/save-screenshot.html"><FONT face=Verdana>the Screenshot.java example program</FONT></A><FONT face=Verdana>. It saves as PNG, but all you have to do is change the second argument of <CODE>ImageIO.write</CODE> from <CODE>png</CODE> to <CODE>jpg</CODE>. The advantage of <CODE>ImageIO</CODE>: It is available with each 1.4+ JDK and JRE, not only those coming from Sun. </FONT></P>
<H2><FONT face=Verdana size=3>Explanation</FONT></H2>
<P><FONT face=Verdana>Now let's see how this program works. First, it is checked that we have exactly five arguments. If this is not the case, an error message is printed to output and the program terminates. </FONT></P>
<P><FONT face=Verdana>Next, the input image is loaded via <CODE>Toolkit</CODE> and <CODE>MediaTracker</CODE> just as it was done in </FONT><A href="http://schmidt.devlib.org/java/load-image-toolkit.html"><FONT face=Verdana>Viewer</FONT></A><FONT face=Verdana>. </FONT></P>
<P><FONT face=Verdana>The third and fourth program argument contain the maximum size of the thumbnail to be created. The <EM>actual</EM> size of the thumbnail will be computed from that maximum size and the actual size of the image (all sizes are given as pixels). The code that does this is not really very readable, and also not essential to loading and saving image files. But it is necessary to create a thumbnail that is scaled correctly. </FONT></P>
<P><FONT face=Verdana>As an example, if the two arguments for the maximum thumbnail size are both <CODE>100</CODE> and the image that was loaded is <CODE>400</CODE> times <CODE>200</CODE> pixels large, we want the thumbnail to be <CODE>100</CODE> times <CODE>50</CODE> pixels large, not <CODE>100</CODE> times <CODE>100</CODE>, because the original image is twice as wide as it is high. A <CODE>100</CODE> times <CODE>100</CODE> pixel thumbnail would contain a very skewed version of the original image. </FONT></P>
<P><FONT face=Verdana>Now that we have determined the size of the thumbnail we create a <CODE>BufferedImage</CODE> of that size, named <CODE>thumbImage</CODE>. We ask for a <CODE>Graphics2D</CODE> object for that new thumbnail image and call its <CODE>drawImage</CODE> method to draw the original image on that new image. The call to <CODE>drawImage</CODE> does the actual scaling. The rendering hints for bilinear interpolation can be left out if quality is not that necessary and speed more important. For nicer results (at least in some cases) try <CODE>RenderingHints.VALUE_INTERPOLATION_BICUBIC</CODE> instead of <CODE>RenderingHints.VALUE_INTERPOLATION_BILINEAR</CODE>. </FONT></P>
<P><FONT face=Verdana>In order to save the scaled-down image to a JPEG file, we create a buffered <CODE>FileOutputStream</CODE> with the second argument as name and initialize the necessary objects from the <CODE>com.sun.image.codec.jpeg</CODE> package. The quality argument from the command line is converted from the interval <CODE>0</CODE> to <CODE>100</CODE> to the interval <CODE>0.0f</CODE> to <CODE>1.0f</CODE>, because that's what the codec expects (I mostly use <CODE>0.75f</CODE>). The higher that quality number is, the better the resulting thumbnail image quality, but also the larger the resulting file. </FONT></P>
<P><FONT face=Verdana>The call to <CODE>System.exit(0);</CODE> is unfortunately necessary for some Java runtime environments (because of a bug that keeps the AWT thread from terminating). </FONT></P>
<H2><A id=source><FONT face=Verdana size=3>Source code of Thumbnail.java</FONT></A></H2><PRE><FONT face=Verdana size=2>&nbsp;</FONT><DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid"><DIV><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">import&nbsp;com.sun.image.codec.jpeg.</SPAN><SPAN style="COLOR: #000000">*</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">;<BR>import&nbsp;java.awt.</SPAN><SPAN style="COLOR: #000000">*</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">;<BR>import&nbsp;java.awt.image.</SPAN><SPAN style="COLOR: #000000">*</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">;<BR>import&nbsp;java.io.</SPAN><SPAN style="COLOR: #000000">*</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">;<BR><BR></SPAN><SPAN style="COLOR: #008000">/*</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #008000">*<BR>&nbsp;*&nbsp;Thumbnail.java&nbsp;(requires&nbsp;Java&nbsp;1.2+)<BR>&nbsp;*&nbsp;Load&nbsp;an&nbsp;image,&nbsp;scale&nbsp;it&nbsp;down&nbsp;and&nbsp;save&nbsp;it&nbsp;as&nbsp;a&nbsp;JPEG&nbsp;file.<BR>&nbsp;*&nbsp;@author&nbsp;Marco&nbsp;Schmidt<BR>&nbsp;</SPAN><SPAN style="COLOR: #008000">*/</SPAN></FONT></FONT><SPAN style="COLOR: #000000"><BR></SPAN><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;Thumbnail&nbsp;{<BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">static</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;main(String[]&nbsp;args)&nbsp;throws&nbsp;Exception&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(args.length&nbsp;</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">5</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Usage:&nbsp;java&nbsp;Thumbnail&nbsp;INFILE&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN></FONT></FONT><SPAN style="COLOR: #000000"><BR><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT></SPAN><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">OUTFILE&nbsp;WIDTH&nbsp;HEIGHT&nbsp;QUALITY</SPAN><SPAN style="COLOR: #000000">"</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(</SPAN><SPAN style="COLOR: #000000">1</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;load&nbsp;image&nbsp;from&nbsp;INFILE</SPAN></FONT></FONT><SPAN style="COLOR: #008000"><BR></SPAN><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;Image&nbsp;image&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Toolkit.getDefaultToolkit().getImage(args[</SPAN><SPAN style="COLOR: #000000">0</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;MediaTracker&nbsp;mediaTracker&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;MediaTracker(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;Container());<BR>&nbsp;&nbsp;&nbsp;&nbsp;mediaTracker.addImage(image,&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;mediaTracker.waitForID(</SPAN><SPAN style="COLOR: #000000">0</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;determine&nbsp;thumbnail&nbsp;size&nbsp;from&nbsp;WIDTH&nbsp;and&nbsp;HEIGHT</SPAN></FONT></FONT><SPAN style="COLOR: #008000"><BR></SPAN><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;thumbWidth&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Integer.parseInt(args[</SPAN><SPAN style="COLOR: #000000">2</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;thumbHeight&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Integer.parseInt(args[</SPAN><SPAN style="COLOR: #000000">3</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;thumbRatio&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">)thumbWidth&nbsp;</SPAN><SPAN style="COLOR: #000000">/</SPAN><SPAN style="COLOR: #000000">&nbsp;(</SPAN><SPAN style="COLOR: #0000ff">double</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">)thumbHeight;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;imageWidth&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;image.getWidth(</SPAN><SPAN style="COLOR: #0000ff">null</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;imageHeight&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;image.getHeight(</SPAN><SPAN style="COLOR: #0000ff">null</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;imageRatio&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">)imageWidth&nbsp;</SPAN><SPAN style="COLOR: #000000">/</SPAN><SPAN style="COLOR: #000000">&nbsp;(</SPAN><SPAN style="COLOR: #0000ff">double</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">)imageHeight;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(thumbRatio&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;imageRatio)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thumbHeight&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">)(thumbWidth&nbsp;</SPAN><SPAN style="COLOR: #000000">/</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;imageRatio);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">else</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thumbWidth&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">)(thumbHeight&nbsp;</SPAN><SPAN style="COLOR: #000000">*</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;imageRatio);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #008000">&nbsp;draw&nbsp;original&nbsp;image&nbsp;to&nbsp;thumbnail&nbsp;image&nbsp;object&nbsp;and<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;scale&nbsp;it&nbsp;to&nbsp;the&nbsp;new&nbsp;size&nbsp;on-the-fly</SPAN></FONT></FONT><SPAN style="COLOR: #008000"><BR></SPAN><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;BufferedImage&nbsp;thumbImage&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;BufferedImage(thumbWidth,&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thumbHeight,&nbsp;BufferedImage.TYPE_INT_RGB);<BR>&nbsp;&nbsp;&nbsp;&nbsp;Graphics2D&nbsp;graphics2D&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;thumbImage.createGraphics();<BR>&nbsp;&nbsp;&nbsp;&nbsp;graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RenderingHints.VALUE_INTERPOLATION_BILINEAR);<BR>&nbsp;&nbsp;&nbsp;&nbsp;graphics2D.drawImage(image,&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,&nbsp;thumbWidth,&nbsp;thumbHeight,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">null</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;save&nbsp;thumbnail&nbsp;image&nbsp;to&nbsp;OUTFILE</SPAN></FONT></FONT><SPAN style="COLOR: #008000"><BR></SPAN><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;BufferedOutputStream&nbsp;</SPAN><SPAN style="COLOR: #0000ff">out</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;BufferedOutputStream(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN></FONT></FONT><SPAN style="COLOR: #000000"><BR><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileOutputStream(args[</FONT></SPAN><SPAN style="COLOR: #000000"><FONT face=Verdana size=2>1</FONT></SPAN><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">]));<BR>&nbsp;&nbsp;&nbsp;&nbsp;JPEGImageEncoder&nbsp;encoder&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;JPEGCodec.createJPEGEncoder(</SPAN><SPAN style="COLOR: #0000ff">out</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;JPEGEncodeParam&nbsp;param&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">&nbsp;encoder.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getDefaultJPEGEncodeParam(thumbImage);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;quality&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Integer.parseInt(args[</SPAN><SPAN style="COLOR: #000000">4</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;quality&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Math.max(</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,&nbsp;Math.min(quality,&nbsp;</SPAN><SPAN style="COLOR: #000000">100</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">));<BR>&nbsp;&nbsp;&nbsp;&nbsp;param.setQuality((</SPAN><SPAN style="COLOR: #0000ff">float</SPAN><SPAN style="COLOR: #000000">)quality&nbsp;</SPAN><SPAN style="COLOR: #000000">/</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">100.0f</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">false</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;encoder.setJPEGEncodeParam(param);<BR>&nbsp;&nbsp;&nbsp;&nbsp;encoder.encode(thumbImage);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">out</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">.close();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;System.</SPAN><SPAN style="COLOR: #0000ff">out</SPAN><SPAN style="COLOR: #000000">.println(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Done.</SPAN><SPAN style="COLOR: #000000">"</SPAN></FONT></FONT><FONT face=Verdana><FONT size=2><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;System.exit(</SPAN><SPAN style="COLOR: #000000">0</SPAN></FONT></FONT><SPAN style="COLOR: #000000"><FONT face=Verdana size=2>);<BR>&nbsp;&nbsp;}<BR>}<BR></FONT></SPAN></DIV></DIV></PRE>
<DIV class=footer>
<P><FONT face=Verdana size=2>Copyright © 2002, 2003, 2004, 2005 </FONT><A href="http://schmidt.devlib.org/contact.html"><FONT face=Verdana size=2>Marco Schmidt</FONT></A><FONT face=Verdana size=2>. </FONT></P></DIV>
<SCRIPT type=text/javascript>
<!--
var awdoc=document.location.href;
if (awdoc.match(/^http/i)!=null) {
document.write('<scr' + 'ipt language="javascript" src="http://jiu.sourceforge.net/cgi-bin/pslogger.pl?loc='+escape(document.location)+'&ref='+escape(document.referrer));
if(document.all) { document.write('&size='+document.fileSize); }
document.write('"></scr' + 'ipt>'); }
-->
</SCRIPT>

<SCRIPT language=javascript src="http://jiu.sourceforge.net/cgi-bin/pslogger.pl?loc=http%3A//schmidt.devlib.org/java/save-jpeg-thumbnail.html&amp;ref=http%3A//schmidt.devlib.org/java/image-file-code-examples.html"></SCRIPT>

<DIV id=google-toolbar-tooltip style="BORDER-RIGHT: rgb(0,0,0) 1px outset; PADDING-RIGHT: 2px; BORDER-TOP: rgb(0,0,0) 1px outset; DISPLAY: none; PADDING-LEFT: 2px; Z-INDEX: 2147483647; BACKGROUND: rgb(255,255,221) 0% 50%; LEFT: 188px; PADDING-BOTTOM: 2px; FONT: medium serif; TEXT-TRANSFORM: none; BORDER-LEFT: rgb(0,0,0) 1px outset; COLOR: rgb(17,17,17); TEXT-INDENT: 0pt; PADDING-TOP: 2px; BORDER-BOTTOM: rgb(0,0,0) 1px outset; POSITION: absolute; TOP: 85px; TEXT-ALIGN: left; TEXT-DECORATION: none; font-size-adjust: none; font-stretch: normal; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
<CENTER><SMALL><FONT face=Verdana size=2>Thumbnail: 大拇指的指甲; 极小之物; 极小的; 极短的</FONT></SMALL></CENTER></DIV><img src ="http://www.blogjava.net/Pudgy/aggbug/11457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Pudgy/" target="_blank">Pudgy's World</a> 2005-08-29 16:12 <a href="http://www.blogjava.net/Pudgy/archive/2005/08/29/11457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于Batik的SVG应用: 关于几何变换</title><link>http://www.blogjava.net/Pudgy/archive/2005/08/20/10572.html</link><dc:creator>Pudgy's World</dc:creator><author>Pudgy's World</author><pubDate>Sat, 20 Aug 2005 00:55:00 GMT</pubDate><guid>http://www.blogjava.net/Pudgy/archive/2005/08/20/10572.html</guid><wfw:comment>http://www.blogjava.net/Pudgy/comments/10572.html</wfw:comment><comments>http://www.blogjava.net/Pudgy/archive/2005/08/20/10572.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Pudgy/comments/commentRss/10572.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Pudgy/services/trackbacks/10572.html</trackback:ping><description><![CDATA[<FONT face=Verdana size=2>
<P>摘自<A href="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/?ca=dwcn-newsletter-xml">http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/?ca=dwcn-newsletter-xml</A><A href="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/?ca=dwcn-newsletter-xml#author1"><NAME><BR>陈柯</NAME></A><BR>技术总监, 南京安元科技<BR>2005 年 1 月 </P>
<BLOCKQUOTE>本文是作者在 SVGGIS 系统的开发实践过程中关于 SVG 坐标转换的总结。在描述 SVG 坐标变换原理的同时，使用 Apache Batik 项目实现了相关例子。</BLOCKQUOTE>
<P>SVG 是一种用 xml 语言来描述二维图形对象的语言，SVG 允许三种图形对象：1．矢量图形，2．图片，3．文本对象。这三种图形对象都可以支持分组，使用样式渲染，进行几何变换。</P>
<P>SVG 还能够通过脚本来实现交互操作和动态显示。可以通过定义动画对象或使用script 脚本来实现动画。</P>
<P><A name=N1004D><SPAN class=atitle2>1. SVG 下几种常见的几何变换方式</SPAN></A><BR></P>
<P><A name=N10053><SPAN class=atitle3>1.1 一个 SVG 例子</SPAN></A><BR>我们首先来看一个 SVG 的例子，窗口右上角有四个色块，每个色块是一个50×50的矩形。</P>
<P><A name=N1005E><B>图 1. 一个 SVG 的样本</B></A><BR><IMG height=320 alt="图 1. 一个 SVG 的样本" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image002.jpg" width=311 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P><A name=N1006E><B>图 2. 样本文档</B></A><BR>
<TABLE style="WIDTH: 592px; HEIGHT: 191px" cellSpacing=0 cellPadding=5 width=592 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version="1.0"?&gt;
&lt;svg &gt;
    &lt;g&gt; 
	&lt;rect x="0" y="0" width="50" height="50" style="fill:red" /&gt;
	&lt;rect x="50" y="0" width="50" height="50" style="fill:yellow" /&gt;
	&lt;rect x="0" y="50" width="50" height="50" style="fill:green" /&gt;
	&lt;rect x="50" y="50" width="50" height="50" style="fill:black" /&gt;
    &lt;/g&gt;
&lt;/svg&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10078><SPAN class=atitle3>1.2 使用 Adobe SVG Viewer 展示在 SVG 文档中实现的几何变换</SPAN></A><BR>缩放&lt;g transform="scale(2)"&gt;</P>
<P><A name=N10083><B>图 3. 放大一倍</B></A><BR><IMG height=346 alt="图 3. 放大一倍" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image004.jpg" width=337 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>平移&lt;g transform="translate(200 ,200)"&gt;</P>
<P><A name=N10098><B>图 4. 平移200，200个像素</B></A><BR><IMG height=317 alt="图 4. 平移200，200个像素" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image006.jpg" width=308 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>旋转&lt;g transform="rotate(45)"&gt;</P>
<P><A name=N100AD><B>图 5. 顺时针旋转45度</B></A><BR><IMG height=333 alt="图 5. 顺时针旋转45度" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image008.jpg" width=325 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>横切&lt;g transform="skewX(45)"&gt;</P>
<P><A name=N100C2><B>图 6. 以 y 轴为基线在 X 方向横切45度</B></A><BR><IMG height=332 alt="图 6. 以 y 轴为基线在 X 方向横切45度" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image010.jpg" width=323 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N100D2><SPAN class=atitle2>2. 在 Batik 下实现 SVG 的几何变换</SPAN></A><BR></P>
<P><A name=N100D8><SPAN class=atitle3>2.1 Batik 的基础知识</SPAN></A><BR><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2.1.1 Batik 的用途</B> </P>
<P>Batik 是基于 java 语言实现的一个 SVG 应用的工具集，用于实现对 SVG 对象的显示、编辑以及将 SVG 图形对象转换成其他图片格式，如 jpg、gif 等。</P>
<P>这个项目的目标就是给开发人员一套用于处理或应用 SVG 对象的基础核心模型。作为Apache 项目成员之一，该项目也为开发人员提供了一个开发的可扩展的平台。同时 batik 也维护了一个可以查看 SVG 文件的浏览器。虽然 batik 还没有完全实现 SVG 的所有标准语法和标记，但通过比较不同版本的区别就会发现，他正在以很高的效率覆盖 SVG 所有的标准。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2.1.2 让我们实现一个简单的 Batik 程序</B> </P>
<P>首先让我们实现一个简单的基于 Batik 的 SVG 浏览器。Batik 封装了org.apache.batik.swing.JSVGCanvas 对象可以用于在 swing 中嵌入 SVG 显示容器，并可以通过 org.apache.batik.swing.JSVGCanvas 提供的方法对 SVG 文档和图像进行操作。这个浏览器可以支持大部分 SVG 的语法和标准包括脚本交互的功能，但暂时还没有引入动画。关于动画和脚本交互的内容我们会在以后的文章中讲述，今天先集中解决几何变换的问题。</P>
<P><A name=N100F5><B>图 7. 运行程序打开我们编写的 SVG 的例子</B></A><BR><IMG height=391 alt="图 7. 运行程序打开我们编写的 SVG 的例子" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image012.jpg" width=469 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>可以通过该页面引导运行该程序： <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">从附件中可以查看该程序的完整代码，也可通过 <A href="http://221.130.8.10:8080/discover/batik/index.htm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">网上下载地址</A> 运行该程序。 </P><A name=N1010E><B>图 8. 将一个 SVGCanvas 添加到界面</B></A><BR>
<TABLE style="WIDTH: 600px; HEIGHT: 95px" cellSpacing=0 cellPadding=5 width=600 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
private javax.swing.JPanel SVGPanel = new javax.swing.JPanel();
private JSVGCanvas svgCanvas = new org.apache.batik.swing.JSVGCanvas();
SVGPanel.add("Center", svgCanvas);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10118><SPAN class=atitle3>2.2 通过 Batik 的 GVT 模型实现 SVG 的几何变换</SPAN></A><BR><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2.2.1 为什么要使用 Batik 来实现 SVG 的几何变换</B> </P>
<P>当我们用 Batik 工具集作为 SVG 客户端的解决方案时，如缩放平移这样的操作就在所难免了。但 Batik 并没有直接支持如 Adobe SVG Viewer 那样的鼠标拖动几何变换的操作，这就要求我们对这些功能进行编程处理。</P>
<P>在分析 SVG 的几何变换的细节之前，先让我们了解一下基本的操作编程方式。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2.2.2 Batik 下通过 java.awt.geom.AffineTransform 来实现 SVG 的几何变换</B> </P>
<P>JSVGCanvas 提供方法可以获取 java.awt.geom.AffineTransform 对象。AffineTransform 是用于实现2D 几何图形坐标变换处理的对象，可以通过该对象进行二维几何空间中两个坐标系的相互映射和变换。</P>
<P>平移:</P><A name=N10136><B></B></A><BR>
<TABLE style="WIDTH: 589px; HEIGHT: 112px" cellSpacing=0 cellPadding=5 width=589 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//向左和向下平移50个像素
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.translate(50,50);
svgCanvas.setRenderingTransform(rat);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>缩放:</P><A name=N10142><B></B></A><BR>
<TABLE style="WIDTH: 586px; HEIGHT: 111px" cellSpacing=0 cellPadding=5 width=586 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//以屏幕左上角原点为固定点进行缩放操作
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.scale(0.5,0.5);
svgCanvas.setRenderingTransform(rat);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>旋转:</P><A name=N1014E><B></B></A><BR>
<TABLE style="WIDTH: 585px; HEIGHT: 113px" cellSpacing=0 cellPadding=5 width=585 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//以屏幕左上角原点为固定点进行旋转缩放
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.rotate(3.1415926/4);
svgCanvas.setRenderingTransform(rat);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>复合变换:</P><A name=N1015A><B></B></A><BR>
<TABLE style="WIDTH: 586px; HEIGHT: 144px" cellSpacing=0 cellPadding=5 width=586 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//一个综合平移、放大和旋转90度的复合变换
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.translate(50，50);
rat.scale(2，2);
rat.rotate(3.1415926/4);
svgCanvas.setRenderingTransform(rat);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10163><SPAN class=atitle2>3. 当我们需要进行复合几何变换的时候</SPAN></A><BR></P>
<P><A name=N10169><SPAN class=atitle3>3.1 先来让我们通过不同的变换代码组合实现复合几何变换</SPAN></A><BR>先让我们看第一个例子:</P>//放大一倍和平移50个像素的复合变换 <A name=N10172><B></B></A><BR>
<TABLE style="WIDTH: 467px; HEIGHT: 163px" cellSpacing=0 cellPadding=5 width=467 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
AffineTransform rat = svgCanvas.getRenderingTransform();

        <FONT color=#ff0000>rat.scale(2,2);
rat.translate(50,50);</FONT>
svgCanvas.setRenderingTransform(rat);

      </CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10181><B>图 9. 复合变换一</B></A><BR><IMG height=363 alt="图 9. 复合变换一" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image014.jpg" width=440 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>可以看得出来，这个变换的最终效果是：图形的形状放大一倍，原图形的（0，0）原点坐标平移100个像素。</P>
<P>再来看第二个例子:</P><A name=N10197><B></B></A><BR>
<TABLE style="WIDTH: 465px; HEIGHT: 177px" cellSpacing=0 cellPadding=5 width=465 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//放大一倍和平移50个像素的复合变换
AffineTransform rat = svgCanvas.getRenderingTransform();

        <FONT color=#ff0000>rat.translate(50,50);
rat.scale(2,2);</FONT>
svgCanvas.setRenderingTransform(rat);

      </CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101A6><B>图 10. 复合变换二</B></A><BR><IMG height=372 alt="图 10. 复合变换二" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image016.jpg" width=452 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>这个变换的最终效果是：图形的形状放大一倍，原图形的（0，0）原点坐标平移50个像素。</P>
<P><A name=N101B9><SPAN class=atitle3>3.2 关键是顺序</SPAN></A><BR>比较一下这两种平移后的效果会发现,只是因为缩放和平移的顺序不同，变换后的结果就发生了区别。第一个例子实际平移的不是50个像素,而是100个像素。而第二个例子则是平移了50个像素。</P>
<P>有兴趣的读者可以添加其他几何变换方式，并测试不同的变换顺序，会发现复合变换的顺序与复合变换的最终效果是紧密相关的。那么如何分析和计算复合变换的变换结果呢？这里我们需要补充一点数学知识了。</P>
<P><A name=N101C5><SPAN class=atitle3>3.3 对单一的几何变换进行数学模型分析</SPAN></A><BR>对计算机图形学中图形变换相关理论很熟悉的人可以跳过这部分直接看 Batik 的实现方式。这节使用的齐次式图片引用自 <A href="http://www.w3.org/TR/2001/REC-SVG-20010904/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">SVG标准</A>中关于坐标变换的齐次式的例子插图。 </P>
<P>首先我们来了解一下图形变换的齐次式计算方法：</P>
<P><A name=N101D7><B>图 11. 基本几何 变换齐次式</B></A><BR><IMG height=83 alt="图 11. 基本几何 变换齐次式" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image017.gif" width=408 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>这是一个基本图形变换齐次式。等式的最右边是一个坐标点未变换前的坐标矩阵，最左边是该坐标点变换后所在位置的一个三行一列的坐标矩阵，中间那个三行三列的矩阵就是变换矩阵。不同的变换方式将对应不同的变换矩阵，图形平移效果就是通过这样一个变换齐次式来实现的。</P>
<P>平移:如果需要将图形平移（tx，ty）个坐标时，采用如下的变换矩阵带入变换方程。</P>
<P><A name=N101EF><B>图 12. 平移变换矩阵</B></A><BR><IMG height=83 alt="图 12. 平移变换矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image018.gif" width=104 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>缩放：如果需要在 x 轴方向实现 sx 倍缩放，在 y 轴方向实现 sy 轴缩放时，采用如下的变换矩阵带入变换方程。</P>
<P><A name=N10204><B>图 13. 缩放变换矩阵</B></A><BR><IMG height=83 alt="图 13. 缩放变换矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image019.gif" width=104 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>旋转:如果需要将图像旋转 a 度时，使用如下的变换矩阵带入变换方程。</P>
<P><A name=N10219><B>图 14. 旋转变换矩阵</B></A><BR><IMG height=83 alt="图 14. 旋转变换矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image020.gif" width=169 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N10229><SPAN class=atitle3>3.4 采用复合几何变换的数学模型分析</SPAN></A><BR>3.4.1 数学分析</P>
<P>当两组变换同时作用于同一个图像时，连续使用该等式，得出如下等式。由于做变换的时候是将变换矩阵放在矩阵积的左边，所以对于复合变换的式子，应该从右向左进行读。对于如下的式子：从右至左依次是变换前的坐标，第一次转换的转换矩阵，第二次转换的转换矩阵，转换后的坐标值。</P>
<P><A name=N10237><B>图 15. 进行两次变换的复合矩阵</B></A><BR><IMG height=83 alt="图 15. 进行两次变换的复合矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image021.gif" width=359 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>进一步推导 n 次几何变换的复合变换等式：</P>
<P><A name=N1024C><B>图 16. n次变换的复合矩阵</B></A><BR><IMG height=179 alt="图 16. n次变换的复合矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image022.gif" width=483 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.4.2 来实践一下</B> </P>
<P><A name=N10264><B>图 17. 变换前效果</B></A><BR><IMG height=252 alt="图 17. 变换前效果" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image024.jpg" width=428 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>使用这样一个复合变换方式：</P>
<P>&lt;g transform="translate(50,90) rotate(-45) translate(130,160)"&gt; </P>
<P>根据前面的分析结果带入变换方程，得出如下等式</P>
<P><A name=N1027F><B>图 18. 推导矩阵</B></A><BR><IMG height=296 alt="图 18. 推导矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image025.gif" width=512 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>根据计算后的变换矩阵，可得变换结果是图形整体平移到（255.03,111.21），同时图形自身沿顺时针方向旋转45度。查看实际变换结果，这里我们可以发现，对于 SVG 的变换来说，虽然我们习惯上从左往右写变换参数的，实际上图形做变换的时候是从右边的变换参数开始依次进行图形变换的。</P>
<P><A name=N10294><B>图 19. 变换后效果</B></A><BR><IMG height=281 alt="图 19. 变换后效果" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image027.jpg" width=553 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N102A4><SPAN class=atitle3>3.5 分析一下 Batik 是如何实现 SVG 的复合几何变换的</SPAN></A><BR><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.5.1 先看第一个例子：</B> </P>
<P>这个变换是先放大后平移的变换，其变换效果最终是，先将图形的形状放大一倍，然后将图形整个平移100个像素。这里我们可以看出，虽然我们是先进行的放大变换，后进行的平移操作，但当我们进行复合变换的时候由于实际运算时上是先进行了平移，后进行了缩放。或者简单的理解为从左向右先写缩放矩阵，再写平移矩阵，这样得出的变换矩阵就可以对变换后的效果进行计算了。</P><A name=N102B3><B></B></A><BR>
<TABLE style="WIDTH: 510px; HEIGHT: 193px" cellSpacing=0 cellPadding=5 width=510 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//放大一倍和平移50个像素的复合变换
//&lt;g transform=" scale(2 ) translate(50 50)"&gt;
AffineTransform rat = svgCanvas.getRenderingTransform();

        <FONT color=#ff0000>rat.scale(2,2);
rat.translate(50,50);</FONT>
svgCanvas.setRenderingTransform(rat);

      </CODE></PRE></TD></TR></TBODY></TABLE>
<P>我们可以将这个变换对应的计算矩阵写出来：</P>
<P><A name=N102C5><B>图 20. 复合变换例一的变换矩阵</B></A><BR><IMG height=291 alt="图 20. 复合变换例一的变换矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image029.jpg" width=470 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>根据上面的分析我们可以看的出，先进行缩放变换在进行平移变换的复合变换时，变换后原图元的坐标会映射到新的位置，其中：</P>
<P>X1=Sx(X+dx) <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Y1=Sy(Y+dy) </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.5.2 再来看第二个例子：</B> </P>
<P>这个变换是先放大后平移的变换，其变换效果最终是，先将图形整个平移50个像素，然后将图形的形状放大一倍。</P><A name=N102E6><B></B></A><BR>
<TABLE style="WIDTH: 493px; HEIGHT: 192px" cellSpacing=0 cellPadding=5 width=493 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//放大一倍和平移50个像素的复合变换
//&lt;g transform="translate(50，50) scale(2 )"&gt;
AffineTransform rat = svgCanvas.getRenderingTransform();

        <FONT color=#ff0000>rat.translate(50,50);
rat.scale(2,2);</FONT>
svgCanvas.setRenderingTransform(rat);

      </CODE></PRE></TD></TR></TBODY></TABLE>
<P>我们可以将这个变换对应的 svg 文档实现写出来：</P>
<P>&lt;g transform="translate(50 50) scale(2 )"&gt;</P>
<P><A name=N102FB><B>图 21. 复合变换例二的变换矩阵</B></A><BR><IMG height=259 alt="图 21. 复合变换例二的变换矩阵" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image032.jpg" width=416 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>根据上面的分析我们可以看的出，先进行平移变换再进行缩放变换的复合变换时，变换后原图元的坐标会映射到新的位置，其中：</P>
<P>X1=Sx*X+dx <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Y1=Sy*Y+dy </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.5.3 实用这两个例子的成果</B> </P>
<P>假设我们需要将图形原点的位置移动到（150，300），同时形状放大到原来的3倍，该如何进行变换来实现这样的效果呢？</P>
<P><A name=N1031E><B>图 22. 变换效果</B></A><BR><IMG height=343 alt="图 22. 变换效果" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image034.jpg" width=476 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>使用第一个例子的分析结果，先进行缩放变换，再进行平移变换的方程变换：</P>
<P><A name=N10333><B>图 23. 方程推演</B></A><BR><IMG height=68 alt="图 23. 方程推演" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image036.jpg" width=460 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>根据推导可知需先进行3倍的缩放变换，再平移（50，100）个坐标就可以实现指定的变换效果。实现程序如下</P><A name=N10346><B></B></A><BR>
<TABLE style="WIDTH: 497px; HEIGHT: 177px" cellSpacing=0 cellPadding=5 width=497 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//&lt;g transform=" scale(3 ) translate(50，100)"&gt;
AffineTransform rat = svgCanvas.getRenderingTransform();

        <FONT color=#ff0000>rat.scale(3,3);
rat.translate(50,100);</FONT>
svgCanvas.setRenderingTransform(rat);

      </CODE></PRE></TD></TR></TBODY></TABLE>
<P>若先进行平移变换，再进行缩放变换的话，根据第二个例子的推导结果：</P>
<P><A name=N10358><B>图 24. 方程推演</B></A><BR><IMG height=67 alt="图 24. 方程推演" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image038.jpg" width=469 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>可知应先进行（150，300）的平移操作，再实现3倍的缩放操作。实现程序如下：</P><A name=N1036B><B></B></A><BR>
<TABLE style="WIDTH: 495px; HEIGHT: 177px" cellSpacing=0 cellPadding=5 width=495 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//&lt;g transform=" translate(150，300) scale(3 )"&gt;
AffineTransform rat = svgCanvas.getRenderingTransform();

        <FONT color=#ff0000>rat.translate(150,300);
rat.scale(3,3);</FONT>
svgCanvas.setRenderingTransform(rat);

      </CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10378><SPAN class=atitle2>4. 一个常用复合变换实例的实现--定点变换</SPAN></A><BR>上边我们分析了 Batik 实现 SVG 图形变换的原理和计算方法。下面我们用分析的结果实现一个常用的变换实例：定点变换。变换的种类包括缩放，旋转（一般不考虑定点平移这个概念）。</P>
<P>所谓定点变换就是在图形变换中指定一个固定的点，在变换结束后，该点的位置不发生变化。定点变换在 GIS 的实际运用中很常见，比如将地图放大到指定倍数而保持地图的某个位置（如：鼠标点击的位置）不发生变化。我们以定点缩放为例描述定点变换的使用方法。</P>
<P>假设我们用(x1,y1)点来做定点变换的基准点进行几何变换，要求实现变换后（x1,y1）的位置不变。</P>
<P>定点变换的基本思想是基于这样一个特性：当图像进行缩放，或旋转变换的时候，坐标原点的位置并不发生变化。定点变换的实现方法就是，先将基准点（x1,y1）平移到原点即做一次[-x1,-y1]变换，进行变换后再将变换后的原点平移到（x1,y1）即再进行一次[x1,y1]变换。</P>
<P><A name=N1038C><B>图 25. 基于（x1,y1）定点变换</B></A><BR><IMG height=262 alt="图 25. 基于（x1,y1）定点变换" src="http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/images/image040.jpg" width=540 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>实际编程的时候，我们无需对定点变换做如此复杂的运算。例如可以采用如下的方式实现针对（50,50）的定点变换：</P><A name=N1039F><B></B></A><BR>
<TABLE style="WIDTH: 587px; HEIGHT: 253px" cellSpacing=0 cellPadding=5 width=587 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//以（50，50）点为基准点进行几何变换
//&lt;g transform="translate(50，50) …………… translate(-50，-50)"&gt;
AffineTransform rat = svgCanvas.getRenderingTransform();

        <FONT color=#ff0000>rat.translate(50，50);
//其他变换方式
………………
………………
………………
rat.translate(-50,-50);
svgCanvas.setRenderingTransform(rat);</FONT>

      </CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>有关 SVG 的背景知识，请阅读 developerWorks 上的教程，" <A href="http://www-900.ibm.com/developerworks/cn/cnedu.nsf/xml-onlinecourse-bytitle/409E5759CF4CDA8E48256BBF0035382A?OpenDocument" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可伸缩向量图形介绍</A>" <BR><BR>
<LI><A href="http://www.w3.org/TR/2001/REC-SVG-20010904/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Scalable Vector Graphics (SVG) 1.0 Specification </A><BR><BR>
<LI>Batik项目介绍 <A href="http://xml.apache.org/batik/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://xml.apache.org/batik/</A> <BR></LI></UL>
<P></P></FONT><img src ="http://www.blogjava.net/Pudgy/aggbug/10572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Pudgy/" target="_blank">Pudgy's World</a> 2005-08-20 08:55 <a href="http://www.blogjava.net/Pudgy/archive/2005/08/20/10572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>