KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > ImageModule


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.quercus.lib;
31
32 import com.caucho.quercus.QuercusException;
33 import com.caucho.quercus.QuercusModuleException;
34 import com.caucho.quercus.annotation.NotNull;
35 import com.caucho.quercus.annotation.Optional;
36 import com.caucho.quercus.annotation.ReturnNullAsFalse;
37 import com.caucho.quercus.env.*;
38 import com.caucho.quercus.module.AbstractQuercusModule;
39 import com.caucho.util.L10N;
40 import com.caucho.vfs.Path;
41 import com.caucho.vfs.ReadStream;
42 import com.caucho.vfs.WriteStream;
43
44 import javax.imageio.ImageIO JavaDoc;
45 import java.awt.*;
46 import java.awt.color.ColorSpace JavaDoc;
47 import java.awt.geom.Arc2D JavaDoc;
48 import java.awt.geom.Ellipse2D JavaDoc;
49 import java.awt.geom.FlatteningPathIterator JavaDoc;
50 import java.awt.geom.Line2D JavaDoc;
51 import java.awt.geom.Rectangle2D JavaDoc;
52 import java.awt.image.BufferedImage JavaDoc;
53 import java.awt.image.BufferedImageOp JavaDoc;
54 import java.awt.image.ColorConvertOp JavaDoc;
55 import java.awt.image.ConvolveOp JavaDoc;
56 import java.awt.image.Kernel JavaDoc;
57 import java.awt.image.RescaleOp JavaDoc;
58 import java.io.IOException JavaDoc;
59 import java.io.InputStream JavaDoc;
60 import java.util.LinkedList JavaDoc;
61 import java.util.logging.Level JavaDoc;
62 import java.util.logging.Logger JavaDoc;
63
64 /**
65  * PHP image
66  */

67 public class ImageModule extends AbstractQuercusModule {
68   private static final Logger JavaDoc log
69     = Logger.getLogger(ImageModule.class.getName());
70   private static final L10N L = new L10N(ImageModule.class);
71
72   public static final long IMG_GIF = 0x1;
73   public static final long IMG_JPG = 0x2;
74   public static final long IMG_JPEG = 0x2;
75   public static final long IMG_PNG = 0x4;
76   public static final long IMG_WBMP = 0x8;
77   public static final long IMG_XPM = 0x10;
78
79   public static final int IMAGETYPE_GIF = 1;
80   public static final int IMAGETYPE_JPG = 2;
81   public static final int IMAGETYPE_JPEG = 2;
82   public static final int IMAGETYPE_PNG = 3;
83   public static final int IMAGETYPE_SWF = 4;
84   public static final int IMAGETYPE_PSD = 5;
85   public static final int IMAGETYPE_BMP = 6;
86   public static final int IMAGETYPE_TIFF_II = 7;
87   public static final int IMAGETYPE_TIFF_MM = 8;
88   public static final int IMAGETYPE_JPC = 9;
89   public static final int IMAGETYPE_JP2 = 10;
90   public static final int IMAGETYPE_JPX = 11;
91   public static final int IMAGETYPE_JB2 = 12;
92   public static final int IMAGETYPE_SWC = 13;
93   public static final int IMAGETYPE_IFF = 14;
94   public static final int IMAGETYPE_WBMP = 15;
95   public static final int IMAGETYPE_XBM = 16;
96
97   public static final int IMG_COLOR_STYLED = -2;
98   public static final int IMG_COLOR_BRUSHED = -3;
99
100   private static final int PNG_IHDR = pngCode("IHDR");
101
102   public static final int IMG_ARC_PIE = 0;
103   public static final int IMG_ARC_CHORD = 1;
104   public static final int IMG_ARC_NOFILL = 2;
105   public static final int IMG_ARC_EDGED = 4;
106
107   public static final int IMG_FILTER_NEGATE = 0;
108   public static final int IMG_FILTER_GRAYSCALE = 1;
109   public static final int IMG_FILTER_BRIGHTNESS = 2;
110   public static final int IMG_FILTER_CONTRAST = 3;
111   public static final int IMG_FILTER_COLORIZE = 4;
112   public static final int IMG_FILTER_EDGEDETECT = 5;
113   public static final int IMG_FILTER_EMBOSS = 6;
114   public static final int IMG_FILTER_GAUSSIAN_BLUR = 7;
115   public static final int IMG_FILTER_SELECTIVE_BLUR = 8;
116   public static final int IMG_FILTER_MEAN_REMOVAL = 9;
117   public static final int IMG_FILTER_SMOOTH = 10;
118
119   public String JavaDoc []getLoadedExtensions()
120   {
121     return new String JavaDoc[] { "gd" };
122   }
123
124   /**
125    * Returns the environment value.
126    */

127   public Value getimagesize(Env env,
128                 Path file,
129                 @Optional ArrayValue imageArray)
130   {
131     if (! file.canRead())
132       return BooleanValue.FALSE;
133
134     ImageInfo info = new ImageInfo();
135
136     ReadStream is = null;
137
138     try {
139       is = file.openRead();
140
141       if (! parseImageSize(is, info))
142     return BooleanValue.FALSE;
143     } catch (Exception JavaDoc e) {
144       log.log(Level.FINE, e.toString(), e);
145
146       return BooleanValue.FALSE;
147     } finally {
148       is.close();
149     }
150
151     if (imageArray == null)
152       imageArray = new ArrayValueImpl();
153
154     imageArray.put(new LongValue(info._width));
155     imageArray.put(new LongValue(info._height));
156     imageArray.put(new LongValue(info._type));
157     imageArray.put(new StringValueImpl("width=\"" + info._width +
158                    "\" height=\"" + info._height + "\""));
159
160     if (info._bits >= 0)
161       imageArray.put(new StringValueImpl("bits"), new LongValue(info._bits));
162
163     if (info._type == IMAGETYPE_JPEG)
164       imageArray.put("channels", 3);
165
166     if (info._mime != null)
167       imageArray.put("mime", info._mime);
168
169     return imageArray;
170   }
171
172   /**
173    * Parses the image size from the file.
174    */

175   private static boolean parseImageSize(ReadStream is, ImageInfo info)
176     throws IOException JavaDoc
177   {
178     int ch;
179
180     ch = is.read();
181
182     if (ch == 137) {
183       // PNG - http://www.libpng.org/pub/png/spec/iso/index-object.html
184
if (is.read() != 'P' ||
185           is.read() != 'N' ||
186           is.read() != 'G' ||
187           is.read() != '\r' ||
188           is.read() != '\n' ||
189           is.read() != 26 ||
190           is.read() != '\n')
191         return false;
192
193       return parsePNGImageSize(is, info);
194     }
195     else if (ch == 'G') {
196       // GIF
197
if (is.read() != 'I' ||
198           is.read() != 'F' ||
199           is.read() != '8' ||
200           ((ch = is.read()) != '7' && ch != '9') ||
201           is.read() != 'a')
202         return false;
203
204       return parseGIFImageSize(is, info);
205     }
206     else if (ch == 0xff) {
207       // JPEG
208
if (is.read() != 0xd8)
209         return false;
210
211       return parseJPEGImageSize(is, info);
212     }
213     else
214       return false;
215   }
216
217   /**
218    * Parses the image size from the PNG file.
219    */

220   private static boolean parsePNGImageSize(ReadStream is, ImageInfo info)
221     throws IOException JavaDoc
222   {
223     int length;
224
225     while ((length = readInt(is)) > 0) {
226       int type = readInt(is);
227
228       if (type == PNG_IHDR) {
229     int width = readInt(is);
230     int height = readInt(is);
231     int depth = is.read() & 0xff;
232     int color = is.read() & 0xff;
233     int compression = is.read() & 0xff;
234     int filter = is.read() & 0xff;
235     int interlace = is.read() & 0xff;
236
237     info._width = width;
238     info._height = height;
239     info._type = IMAGETYPE_PNG;
240
241     info._bits = depth;
242
243     info._mime = "image/png";
244
245     return true;
246       }
247       else {
248     for (int i = 0; i < length; i++) {
249       if (is.read() < 0)
250         return false;
251     }
252       }
253
254       int crc = readInt(is);
255     }
256
257     return false;
258   }
259
260   /**
261    * Parses the image size from the PNG file.
262    */

263   private static boolean parseGIFImageSize(ReadStream is, ImageInfo info)
264     throws IOException JavaDoc
265   {
266     int length;
267
268     int width = (is.read() & 0xff) + 256 * (is.read() & 0xff);
269     int height = (is.read() & 0xff) + 256 * (is.read() & 0xff);
270
271     int flags = is.read() & 0xff;
272
273     info._width = width;
274     info._height = height;
275     info._type = IMAGETYPE_GIF;
276
277     info._bits = flags & 0x7;
278
279     info._mime = "image/gif";
280
281     return true;
282   }
283
284   /**
285    * Parses the image size from the PNG file.
286    */

287   private static boolean parseJPEGImageSize(ReadStream is, ImageInfo info)
288     throws IOException JavaDoc
289   {
290     int ch;
291
292     while ((ch = is.read()) == 0xff) {
293       ch = is.read();
294
295       if (ch == 0xff) {
296     is.unread();
297       }
298       else if (0xd0 <= ch && ch <= 0xd9) {
299     // rst
300
}
301       else if (0x01 == ch) {
302     // rst
303
}
304       else if (ch == 0xc0) {
305     int len = 256 * is.read() + is.read();
306
307     int bits = is.read();
308     int height = 256 * is.read() + is.read();
309     int width = 256 * is.read() + is.read();
310
311     info._width = width;
312     info._height = height;
313     info._type = IMAGETYPE_JPEG;
314
315     info._bits = bits;
316
317     info._mime = "image/jpeg";
318
319     return true;
320       }
321       else {
322     int len = 256 * is.read() + is.read();
323
324     is.skip(len - 2);
325       }
326     }
327
328
329     return false;
330   }
331
332   private static int pngCode(String JavaDoc code)
333   {
334     return ((code.charAt(0) << 24) |
335         (code.charAt(1) << 16) |
336         (code.charAt(2) << 8) |
337         (code.charAt(3)));
338   }
339
340   private static int readInt(ReadStream is)
341     throws IOException JavaDoc
342   {
343     return (((is.read() & 0xff) << 24) |
344         ((is.read() & 0xff) << 16) |
345         ((is.read() & 0xff) << 8) |
346         ((is.read() & 0xff)));
347   }
348
349   /**
350    * Retrieve information about the currently installed GD library
351    */

352   public static Value gd_info()
353   {
354     return (new ArrayValueImpl()
355         .append(StringValue.create("GD Version"), // ] => 2.0 or higher
356
StringValue.create("2.0 or higher"))
357         .append(StringValue.create("FreeType Support"), // ] => 1
358
BooleanValue.TRUE)
359         .append(StringValue.create("FreeType Linkage"), // ] => with freetype
360
StringValue.create("with freetype"))
361         .append(StringValue.create("T1Lib Support"), // ] => 1
362
BooleanValue.TRUE)
363         .append(StringValue.create("GIF Read Support"), // ] => 1
364
BooleanValue.TRUE)
365         .append(StringValue.create("GIF Create Support"), // ] => 1
366
BooleanValue.TRUE)
367         .append(StringValue.create("JPG Support"), // ] => 1
368
BooleanValue.TRUE)
369         .append(StringValue.create("PNG Support"), // ] => 1
370
BooleanValue.TRUE)
371         .append(StringValue.create("WBMP Support"), // ] => 1
372
BooleanValue.TRUE)
373         .append(StringValue.create("XPM Support"), // ] =>
374
BooleanValue.FALSE)
375         .append(StringValue.create("XBM Support"), // ] =>
376
BooleanValue.FALSE)
377         .append(StringValue.create("JIS-mapped Japanese Font Support"), // ] =>
378
BooleanValue.FALSE));
379   }
380
381   /**
382    * Returns the imagetypes.
383    */

384   public static long imagetypes()
385   {
386     return IMG_GIF | IMG_JPG | IMG_PNG;
387   }
388
389   /**
390    * Get file extension for image type
391    */

392   public static Value image_type_to_extension(int imageType, boolean dot)
393   {
394     switch(imageType) {
395       case IMAGETYPE_GIF: return StringValue.create(dot ? ".gif" : "gif");
396       case IMAGETYPE_JPG: return StringValue.create(dot ? ".jpg" : "jpg");
397       case IMAGETYPE_PNG: return StringValue.create(dot ? ".png" : "png");
398       case IMAGETYPE_SWF: return StringValue.create(dot ? ".swf" : "swf");
399       case IMAGETYPE_PSD: return StringValue.create(dot ? ".psd" : "psd");
400       case IMAGETYPE_BMP: return StringValue.create(dot ? ".bmp" : "bmp");
401       case IMAGETYPE_TIFF_II: return StringValue.create(dot ? ".tiff" : "tiff");
402       case IMAGETYPE_TIFF_MM: return StringValue.create(dot ? ".tiff" : "tiff");
403       case IMAGETYPE_JPC: return StringValue.create(dot ? ".jpc" : "jpc");
404       case IMAGETYPE_JP2: return StringValue.create(dot ? ".jp2" : "jp2");
405       case IMAGETYPE_JPX: return StringValue.create(dot ? ".jpf" : "jpf");
406       case IMAGETYPE_JB2: return StringValue.create(dot ? ".jb2" : "jb2");
407       case IMAGETYPE_SWC: return StringValue.create(dot ? ".swc" : "swc");
408       case IMAGETYPE_IFF: return StringValue.create(dot ? ".iff" : "iff");
409       case IMAGETYPE_WBMP: return StringValue.create(dot ? ".wbmp" : "wbmp");
410       case IMAGETYPE_XBM: return StringValue.create(dot ? ".xbm" : "xbm");
411     }
412     throw new QuercusException("unknown imagetype " + imageType);
413   }
414
415   /**
416    * Get Mime-Type for image-type returned by getimagesize, exif_read_data,
417    * exif_thumbnail, exif_imagetype
418    */

419   public static Value image_type_to_mime_type(int imageType)
420   {
421     switch(imageType) {
422       case IMAGETYPE_GIF:
423     return StringValue.create("image/gif");
424       case IMAGETYPE_JPG:
425     return StringValue.create("image/jpeg");
426       case IMAGETYPE_PNG:
427     return StringValue.create("image/png");
428       case IMAGETYPE_SWF:
429     return StringValue.create("application/x-shockwave-flash");
430       case IMAGETYPE_PSD:
431     return StringValue.create("image/psd");
432       case IMAGETYPE_BMP:
433     return StringValue.create("image/bmp");
434       case IMAGETYPE_TIFF_II:
435     return StringValue.create("image/tiff");
436       case IMAGETYPE_TIFF_MM:
437     return StringValue.create("image/tiff");
438       case IMAGETYPE_JPC:
439     return StringValue.create("application/octet-stream");
440       case IMAGETYPE_JP2:
441     return StringValue.create("image/jp2");
442       case IMAGETYPE_JPX:
443     return StringValue.create("application/octet-stream");
444       case IMAGETYPE_JB2:
445     return StringValue.create("application/octet-stream");
446       case IMAGETYPE_SWC:
447     return StringValue.create("application/x-shockwave-flash");
448       case IMAGETYPE_IFF:
449     return StringValue.create("image/iff");
450       case IMAGETYPE_WBMP:
451     return StringValue.create("image/vnd.wap.wbmp");
452       case IMAGETYPE_XBM:
453     return StringValue.create("image/xbm");
454     }
455     throw new QuercusException("unknown imageType " + imageType);
456   }
457
458
459   /**
460    * Output image to browser or file
461    */

462   public static boolean imagegif(Env env, QuercusImage image)
463   {
464     try {
465       ImageIO.write(image._bufferedImage, "gif", env.getOut());
466       return true;
467     }
468     catch (IOException JavaDoc e) {
469       throw new QuercusModuleException(e);
470     }
471   }
472
473   /**
474    * Output a PNG image to either the browser or a file
475    */

476   public static boolean imagepng(Env env, QuercusImage image)
477   {
478     try {
479       ImageIO.write(image._bufferedImage, "png", env.getOut());
480       return true;
481     }
482     catch (IOException JavaDoc e) {
483       throw new QuercusModuleException(e);
484     }
485   }
486
487   /**
488    * Output image to browser or file
489    */

490   public static boolean imagejpeg(Env env,
491                   QuercusImage image,
492                   @Optional Path path,
493                   @Optional int quality)
494   {
495     try {
496       if (path != null) {
497     WriteStream os = path.openWrite();
498     try {
499       ImageIO.write(image._bufferedImage, "jpeg", os);
500     } finally {
501       os.close();
502     }
503       }
504       else
505     ImageIO.write(image._bufferedImage, "jpeg", env.getOut());
506       return true;
507     }
508     catch (IOException JavaDoc e) {
509       log.log(Level.FINE, e.toString(), e);
510       
511       return false;
512     }
513   }
514
515   /**
516    * Set the blending mode for an image
517    */

518   public static boolean imagealphablending(QuercusImage image,
519                        boolean useAlphaBlending)
520   {
521     image.getGraphics().setComposite(useAlphaBlending
522                      ? AlphaComposite.SrcOver
523                      : AlphaComposite.Src);
524     return true;
525   }
526
527   /**
528    * Should antialias functions be used or not
529    */

530   public static boolean imageantialias(QuercusImage image,
531                        boolean useAntiAliasing)
532   {
533     image.getGraphics().setRenderingHint(RenderingHints.KEY_ANTIALIASING,
534                      useAntiAliasing
535                      ? RenderingHints.VALUE_ANTIALIAS_ON
536                      : RenderingHints.VALUE_ANTIALIAS_OFF);
537     return true;
538   }
539
540   /**
541    * Allocate a color for an image
542    */

543   public static long imagecolorallocate(QuercusImage image,
544                     int r, int g, int b)
545   {
546     return (( 0x7f << 24) |
547         ((r & 0xff) << 16) |
548         ((g & 0xff) << 8) |
549         ((b & 0xff) << 0));
550   }
551
552   /**
553    * Allocate a color for an image
554    */

555   public static long imagecolorallocatealpha(QuercusImage image,
556                          int r, int g, int b, int a)
557   {
558     // PHP's alpha values are inverted and only 7 bits.
559
int alpha = 0x7f - (a & 0xff);
560     return ((alpha << 24) |
561         ((r & 0xff) << 16) |
562         ((g & 0xff) << 8) |
563         ((b & 0xff) << 0) );
564 }
565
566   /**
567    * De-allocate a color for an image
568    */

569   public static boolean imagecolordeallocate(QuercusImage image, int rgb)
570   {
571     // no-op
572
return true;
573   }
574
575   /**
576    * Get the index of the color of a pixel
577    */

578   public static long imagecolorat(QuercusImage image, int x, int y)
579   {
580     return image.getPixel(x, y);
581   }
582
583   /**
584    * Get the index of the closest color to the specified color
585    */

586   public static long imagecolorclosest(QuercusImage image, int r, int g, int b)
587   {
588     return imagecolorallocate(image, r, g, b);
589   }
590
591   /**
592    * Get the index of the closest color to the specified color + alpha
593    */

594   public static long imagecolorclosestalpha(QuercusImage image,
595                          int r, int g, int b, int a)
596   {
597     return imagecolorallocatealpha(image, r, g, b, a);
598   }
599
600   /**
601    * Get the index of the specified color
602    */

603   public static long imagecolorexact(QuercusImage image, int r, int g, int b)
604   {
605     return imagecolorallocate(image, r, g, b);
606   }
607
608   /**
609    * Get the index of the specified color + alpha
610    */

611   public static long imagecolorexactalpha(QuercusImage image,
612                        int r, int g, int b, int a)
613   {
614     return imagecolorallocatealpha(image, r, g, b, a);
615   }
616
617   /**
618    * Makes the colors of the palette version of an image more closely
619    * match the true color version
620    */

621   public static boolean imagecolormatch(QuercusImage image1,
622                     QuercusImage image2)
623   {
624     // no-op
625
return true;
626   }
627
628   /**
629    * Get the index of the specified color or its closest possible alternative
630    */

631   public static long imagecolorresolve(QuercusImage image, int r, int g, int b)
632   {
633     return imagecolorallocate(image, r, g, b);
634   }
635
636   /**
637    * Get the index of the specified color + alpha or its closest possible
638    * alternative
639    */

640   public static long imagecolorresolvealpha(QuercusImage image,
641                          int r, int g, int b, int a)
642   {
643     return imagecolorallocatealpha(image, r, g, b, a);
644   }
645
646   /**
647    * Get the colors for an index
648    */

649   public static ArrayValue imagecolorsforindex(QuercusImage image, int argb)
650   {
651     ArrayValue arrayValue = new ArrayValueImpl();
652     arrayValue.put("red", (argb >> 16) & 0xff);
653     arrayValue.put("green", (argb >> 8) & 0xff);
654     arrayValue.put("blue", (argb >> 0) & 0xff);
655
656     // PHP's alpha is backwards from the rest of the world...
657
int alpha = 0x7f - ((argb >> 24) & 0xff);
658     arrayValue.put("alpha", alpha);
659     return arrayValue;
660   }
661
662   /**
663    * Find out the number of colors in an image's palette
664    */

665   public static Value imagecolorstotal()
666   {
667     return LongValue.create(0);
668   }
669
670   /**
671    * Create a new palette based image
672    */

673   public static Value imagecreate(int width, int height)
674   {
675     return new QuercusImage(width, height);
676   }
677
678   /**
679    * Create a new image from file or URL
680    */

681   public static QuercusImage imagecreatefromgif(Env env, Path filename)
682   {
683     return new QuercusImage(env, filename);
684   }
685
686   /**
687    * Create a new image from file or URL
688    */

689   @ReturnNullAsFalse
690   public static QuercusImage imagecreatefromjpeg(Env env, Path filename)
691   {
692     try {
693       return new QuercusImage(env, filename);
694     } catch (Exception JavaDoc e) {
695       env.warning(L.l("Can't open {0} as a jpeg image", filename));
696       log.log(Level.FINE, e.toString(), e);
697
698       return null;
699     }
700   }
701
702   /**
703    * Create a new image from file or URL
704    */

705   public static QuercusImage imagecreatefrompng(Env env, Path filename)
706   {
707     return new QuercusImage(env, filename);
708   }
709
710   /**
711    * Create a new image from file or URL
712    */

713   public static Value imagecreatefromxbm(Env env, Path filename)
714   {
715     return new QuercusImage(env, filename);
716   }
717
718   /**
719    * Create a new image from file or URL
720    */

721   public static QuercusImage imagecreatefromxpm(Env env, Path filename)
722   {
723     return new QuercusImage(env, filename);
724   }
725
726   /**
727    * Create a new image from file or URL
728    */

729   public static QuercusImage imagecreatefromwbmp(Env env, Path filename)
730   {
731     return new QuercusImage(env, filename);
732   }
733
734   /**
735    * Create a new image from the image stream in the string
736    */

737   public static QuercusImage imagecreatefromstring(Env env, InputStream JavaDoc data)
738   {
739     return new QuercusImage(data);
740   }
741
742   /**
743    * Create a new true color image
744    */

745   public static Value imagecreatetruecolor(int width, int height)
746   {
747     return new QuercusImage(width, height);
748   }
749
750   /**
751    * Destroy an image
752    */

753   public static boolean imagedestroy(QuercusImage image)
754   {
755     // no-op
756
return true;
757   }
758
759   /**
760    * Finds whether an image is a truecolor image
761    */

762   public static boolean imageistruecolor(QuercusImage image)
763   {
764     return true;
765   }
766
767   /**
768    * Enable or disable interlace
769    */

770   public static boolean imageinterlace(QuercusImage image, boolean enable)
771   {
772     // no-op, can safely ignore (just makes images that load top-down)
773
return true;
774   }
775
776
777   // Shapes ///////////////////////////////////////////////////////////
778

779   /**
780    * Set a single pixel
781    */

782   public static boolean imagesetpixel(QuercusImage image,
783                       int x, int y, int color)
784   {
785     image.setPixel(x, y, color);
786     return true;
787   }
788
789   /**
790    * Draw a line
791    */

792   public static boolean imageline(QuercusImage image,
793                   int x1, int y1, int x2, int y2, int color)
794   {
795     image.stroke(new Line2D.Float JavaDoc(x1, y1, x2, y2), color);
796     return true;
797   }
798
799   /**
800    * Draw a dashed line
801    */

802   public static boolean imagedashedline(QuercusImage image,
803                     int x1, int y1, int x2, int y2,
804                     int color)
805   {
806     Graphics2D g = image.getGraphics();
807     Stroke stroke = g.getStroke();
808     g.setColor(intToColor(color));
809     g.setStroke(new BasicStroke(1, BasicStroke.JOIN_ROUND,
810                 BasicStroke.CAP_ROUND, 1,
811                 new float[] { 5, 5 }, 0));
812     g.draw(new Line2D.Float JavaDoc(x1, y1, x2, y2));
813     g.setStroke(stroke);
814     return true;
815   }
816
817   /**
818    * Draw a partial ellipse
819    */

820   public static boolean imagearc(QuercusImage image,
821                  double cx, double cy,
822                  double width, double height,
823                  double start, double end,
824                  int color)
825   {
826     Arc2D JavaDoc arc = new Arc2D.Double JavaDoc(cx-width/2, cy-height/2,
827                  width, height, -1 * start, -1 *(end-start),
828                  Arc2D.OPEN);
829     image.stroke(arc, color);
830     return true;
831   }
832
833   /**
834    * Draw a partial ellipse and fill it
835    */

836   public static boolean imagefilledarc(QuercusImage image,
837                        double cx, double cy,
838                        double width, double height,
839                        double start, double end,
840                        int color,
841                        int style)
842   {
843     int type = Arc2D.PIE;
844     if ((style & IMG_ARC_CHORD)!=0) type = Arc2D.CHORD;
845     if ((style & IMG_ARC_PIE)!=0) type = Arc2D.PIE;
846     Arc2D JavaDoc arc =
847       new Arc2D.Double JavaDoc(cx-width/2, cy-height/2,
848                width, height, -1 * start,
849                -1 *(end-start), type);
850     if ((style & IMG_ARC_NOFILL)==0) image.fill(arc, color);
851     if ((style & IMG_ARC_EDGED)!=0) image.stroke(arc, color);
852     return true;
853   }
854
855   /**
856    * Draw an ellipse
857    */

858   public static boolean imageellipse(QuercusImage image,
859                      double cx, double cy,
860                      double width, double height,
861                      int color)
862   {
863     Shape shape = new Ellipse2D.Double JavaDoc(cx-width/2, cy-height/2, width, height);
864     image.stroke(shape, color);
865     return true;
866   }
867
868   /**
869    * Draw a filled ellipse
870    */

871   public static boolean imagefilledellipse(QuercusImage image,
872                        double cx, double cy,
873                        double width, double height,
874                        int color)
875   {
876     Ellipse2D JavaDoc ellipse =
877       new Ellipse2D.Double JavaDoc(cx-width/2, cy-height/2, width, height);
878     image.fill(ellipse, color);
879     return true;
880   }
881
882   private static Polygon arrayToPolygon(ArrayValue points, int numPoints)
883   {
884     Polygon polygon = new Polygon();
885     ArrayValue.Entry entry = points.getHead();
886     for(int i=0; i<numPoints; i++) {
887       int x = entry.getValue().toInt();
888       entry = entry.getNext();
889       int y = entry.getValue().toInt();
890       entry = entry.getNext();
891       polygon.addPoint(x, y);
892     }
893     return polygon;
894   }
895
896   /**
897    * Draw a polygon
898    */

899   public static boolean imagepolygon(QuercusImage image, ArrayValue points,
900                      int numPoints, int color)
901   {
902     image.stroke(arrayToPolygon(points, numPoints), color);
903     return true;
904   }
905
906   /**
907    * Draw a filled polygon
908    */

909   public static boolean imagefilledpolygon(QuercusImage image,
910                        ArrayValue points,
911                        int numPoints, int color)
912   {
913     image.fill(arrayToPolygon(points, numPoints), color);
914     return true;
915   }
916
917   /**
918    * Draw a rectangle
919    */

920   public static boolean imagerectangle(QuercusImage image, int x1, int y1,
921                        int x2, int y2, int color)
922   {
923     if (x2 < x1) { int tmp = x1; x1 = x2; x2 = tmp; }
924     if (y2 < y1) { int tmp = y1; y1 = y2; y2 = tmp; }
925     image.stroke(new Rectangle2D.Float JavaDoc(x1, y1, x2-x1, y2-y1), color);
926     return true;
927   }
928
929   /**
930    * Draw a filled rectangle
931    */

932   public static boolean imagefilledrectangle(QuercusImage image, int x1, int y1,
933                          int x2, int y2, int color)
934   {
935     image.fill(new Rectangle2D.Float JavaDoc(x1, y1, x2-x1+1, y2-y1+1), color);
936     return true;
937   }
938
939
940   // Text ///////////////////////////////////////////////////////////
941

942   /**
943    * Draw a character horizontally
944    */

945   public static boolean imagechar(QuercusImage image, int font,
946                   int x, int y, String JavaDoc c, int color)
947   {
948     Graphics2D g = image.getGraphics();
949     g.setColor(intToColor(color));
950     Font awtfont = image.getFont(font);
951     int height = image.getGraphics().getFontMetrics(awtfont).getAscent();
952     g.setFont(awtfont);
953     g.drawString(c.substring(0, 1), x, y+height);
954     return true;
955   }
956
957   /**
958    * Draw a string horizontally
959    */

960   public static boolean imagestring(QuercusImage image, int font,
961                   int x, int y, String JavaDoc s, int color)
962   {
963     Graphics2D g = image.getGraphics();
964     g.setColor(intToColor(color));
965     Font awtfont = image.getFont(font);
966     int height = image.getGraphics().getFontMetrics(awtfont).getAscent();
967     g.setFont(awtfont);
968     g.drawString(s, x, y+height);
969     
970     return true;
971   }
972
973   /**
974    * Draw a character vertically
975    */

976   public static boolean imagecharup(QuercusImage image, int font,
977                     int x, int y, String JavaDoc c, int color)
978   {
979     Graphics2D g = (Graphics2D)image.getGraphics().create();
980     g.rotate(-1 * Math.PI / 2);
981     g.setColor(intToColor(color));
982     Font awtfont = image.getFont(font);
983     int height = image.getGraphics().getFontMetrics(awtfont).getAscent();
984     g.setFont(awtfont);
985     g.drawString(c.substring(0, 1), -1 * y, x+height);
986     return true;
987   }
988
989   /**
990    * Returns the width of the image.
991    */

992   public static int imagesx(@NotNull QuercusImage image)
993   {
994     if (image == null)
995       return 0;
996     
997     Graphics2D g = (Graphics2D) image.getGraphics();
998
999     Rectangle bounds = g.getDeviceConfiguration().getBounds();
1000
1001    return (int) bounds.getWidth();
1002  }
1003
1004  /**
1005   * Returns the height of the image.
1006   */

1007  public static int imagesy(@NotNull QuercusImage image)
1008  {
1009    if (image == null)
1010      return 0;
1011    
1012    Graphics2D g = (Graphics2D) image.getGraphics();
1013
1014    Rectangle bounds = g.getDeviceConfiguration().getBounds();
1015
1016    return (int) bounds.getHeight();
1017
1018  }
1019
1020  /**
1021   * Get font height
1022   */

1023  public static int imagefontheight(QuercusImage image, int font)
1024  {
1025    return image.getGraphics().getFontMetrics(image.getFont(font)).getHeight();
1026  }
1027
1028  /**
1029   * Get font width
1030   */

1031  public static int imagefontwidth(QuercusImage image, int font)
1032  {
1033    return image.getGraphics().getFontMetrics(image.getFont(font))
1034      .getMaxAdvance();
1035  }
1036
1037
1038  // BitBlts //////////////////////////////////////////////////////////
1039

1040  /**
1041   * Copy part of an image
1042   */

1043  public static boolean imagecopy(QuercusImage dest, QuercusImage src,
1044                  int dx, int dy, int sx, int sy, int w, int h)
1045  {
1046    dest.getGraphics().drawImage(src._bufferedImage,
1047                 dx, dy, dx+w, dy+h,
1048                 sx, sy, sx+w, sy+h, null);
1049    return true;
1050  }
1051
1052  /**
1053   * Copy and merge part of an image
1054   */

1055  public static boolean imagecopymerge(QuercusImage dest, QuercusImage src,
1056                       int dx, int dy, int sx, int sy,
1057                       int w, int h, int pct)
1058  {
1059    BufferedImage JavaDoc rgba =
1060      new BufferedImage JavaDoc(dest.getWidth(), dest.getHeight(),
1061            BufferedImage.TYPE_INT_ARGB);
1062    rgba.getGraphics().drawImage(src._bufferedImage, 0, 0, null);
1063    BufferedImageOp JavaDoc rescaleOp =
1064      new RescaleOp JavaDoc(new float[] { 1, 1, 1, ((float)pct)/100 },
1065            new float[] { 0, 0, 0, 0 },
1066            null);
1067    BufferedImage JavaDoc rescaledImage =
1068      rescaleOp.filter(rgba, null);
1069    Graphics2D g = (Graphics2D)dest.getGraphics().create();
1070    g.setComposite(AlphaComposite.SrcOver);
1071    g.drawImage(rescaledImage,
1072        dx, dy, dx+w, dy+h,
1073        sx, sy, sx+w, sy+h, null);
1074    return true;
1075  }
1076
1077  /**
1078   * Copy and merge part of an image with gray scale
1079   */

1080  public static boolean imagecopymergegray(QuercusImage dest, QuercusImage src,
1081                       int dx, int dy, int sx, int sy,
1082                       int w, int h, int pct)
1083  {
1084    BufferedImage JavaDoc rgba =
1085      new BufferedImage JavaDoc(dest.getWidth(), dest.getHeight(),
1086            BufferedImage.TYPE_INT_ARGB);
1087    rgba.getGraphics().drawImage(src._bufferedImage, 0, 0, null);
1088    BufferedImageOp JavaDoc rescaleOp =
1089      new RescaleOp JavaDoc(new float[] { 1, 1, 1, ((float)pct)/100 },
1090            new float[] { 0, 0, 0, 0 },
1091            null);
1092    BufferedImage JavaDoc rescaledImage =
1093      rescaleOp.filter(rgba, null);
1094
1095    ColorConvertOp JavaDoc colorConvertOp =
1096      new ColorConvertOp JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
1097    colorConvertOp.filter(dest._bufferedImage, dest._bufferedImage);
1098
1099    Graphics2D g = (Graphics2D)dest.getGraphics().create();
1100    g.setComposite(AlphaComposite.SrcOver);
1101    g.drawImage(rescaledImage,
1102        dx, dy, dx+w, dy+h,
1103        sx, sy, sx+w, sy+h, null);
1104    return true;
1105  }
1106
1107  /**
1108   * Copy and resize part of an image with resampling
1109   */

1110  public static boolean imagecopyresampled(QuercusImage dest, QuercusImage src,
1111                       int dx, int dy, int sx, int sy,
1112                       int dw, int dh, int sw, int sh)
1113  {
1114    Graphics2D g = (Graphics2D)dest.getGraphics().create();
1115    g.setRenderingHint(RenderingHints.KEY_RENDERING,
1116               RenderingHints.VALUE_RENDER_QUALITY);
1117    g.drawImage(src._bufferedImage,
1118        dx, dy, dx+dw, dy+dh,
1119        sx, sy, sx+sw, sy+sh, null);
1120    g.setRenderingHint(RenderingHints.KEY_RENDERING,
1121               RenderingHints.VALUE_RENDER_DEFAULT);
1122    return true;
1123  }
1124
1125  /**
1126   * Copy and resize part of an image
1127   */

1128  public static boolean imagecopyresized(QuercusImage dest, QuercusImage src,
1129                     int dx, int dy, int sx, int sy,
1130                     int dw, int dh, int sw, int sh)
1131  {
1132    Graphics2D g = (Graphics2D)dest.getGraphics().create();
1133    g.drawImage(src._bufferedImage,
1134        dx, dy, dx+dw, dy+dh,
1135        sx, sy, sx+sw, sy+sh, null);
1136    return true;
1137  }
1138
1139
1140  // Drawing Styles ///////////////////////////////////////////////////////
1141

1142  /**
1143   * Set the brush image for line drawing
1144   */

1145  public static boolean imagesetbrush(QuercusImage image, QuercusImage brush)
1146  {
1147    image.setBrush(brush);
1148    return true;
1149  }
1150
1151  /**
1152   * Set the style for line drawing
1153   */

1154  public static boolean imagesetstyle(QuercusImage image, ArrayValue style)
1155  {
1156    image.setStyle(style);
1157    return true;
1158  }
1159
1160  /**
1161   * Set the thickness for line
1162   */

1163  public static boolean imagesetthickness(QuercusImage image, int thickness)
1164  {
1165    image.setThickness(thickness);
1166    return true;
1167  }
1168
1169
1170  // Palette Management //////////////////////////////////////////////////
1171

1172  /**
1173   * Copy the palette from one image to another
1174   */

1175  public static boolean imagepalettecopy(QuercusImage source,
1176                     QuercusImage dest)
1177  {
1178    return true;
1179  }
1180
1181
1182  // Non-TrueColor / Full-Channel-Alpha ////////////////////////////////////
1183

1184  /**
1185   * Set the flag to save full alpha channel information (as opposed to
1186   * single-color transparency) when saving PNG images
1187   */

1188  public static boolean imagesavealpha(QuercusImage image, boolean set)
1189  {
1190    // no-op since we currently only support true-color, full-alpha channel
1191
return true;
1192  }
1193
1194  /**
1195   * Set the color for the specified palette index
1196   */

1197  public static boolean imagecolorset(QuercusImage image, int index,
1198                      int r, int g, int b)
1199  {
1200    // no-op since we currently only support true-color, full-alpha channel
1201
return true;
1202  }
1203
1204  /**
1205   * Define a color as transparent
1206   */

1207  public static long imagecolortransparent(QuercusImage image,
1208                       @Optional int color)
1209  {
1210    // form that includes the optional argument is a no-op since we
1211
// currently only support true-color, full-alpha channel
1212
return 0xFF000000;
1213  }
1214
1215
1216  // Fills //////////////////////////////////////////////////////////
1217

1218  /**
1219   * Flood fill
1220   */

1221  public static boolean imagefill(QuercusImage image, int x, int y, int color)
1222  {
1223    image.flood(x, y, color);
1224    return true;
1225  }
1226
1227  /**
1228   * Flood fill to specific color
1229   */

1230  public static boolean imagefilltoborder(QuercusImage image, int x, int y,
1231                      int border, int color)
1232  {
1233    image.flood(x, y, color, border);
1234    return true;
1235  }
1236
1237
1238  // Filters /////////////////////////////////////////////////////////
1239

1240  /**
1241   * Apply a 3x3 convolution matrix, using coefficient div and offset
1242   */

1243  public static boolean imageconvolution(QuercusImage image, ArrayValue matrix,
1244                     double div, double offset)
1245  {
1246    // XXX: implement div and offset
1247
float[] kernelValues = new float[9];
1248    ArrayValue.Entry entry = matrix.getHead();
1249    for(int y=0; y<3; y++)
1250      for(int x=0; x<3; x++)
1251    {
1252      kernelValues[x+y*3] =
1253        (float)matrix
1254        .get(LongValue.create(y))
1255        .get(LongValue.create(x)).toDouble();
1256    }
1257    ConvolveOp JavaDoc convolveOp =
1258      new ConvolveOp JavaDoc(new Kernel JavaDoc(3, 3, kernelValues),
1259             ConvolveOp.EDGE_NO_OP, null);
1260    BufferedImage JavaDoc bufferedImage =
1261      convolveOp.filter(image._bufferedImage, null);
1262    image._bufferedImage.getGraphics().drawImage(bufferedImage, 1, 0, null);
1263    return true;
1264  }
1265
1266  /**
1267   * Applies a filter to an image
1268   */

1269  public static boolean imagefilter(Env env, QuercusImage image, int filterType,
1270                    @Optional int arg1, @Optional int arg2,
1271                    @Optional int arg3)
1272  {
1273    switch(filterType)
1274      {
1275    case IMG_FILTER_NEGATE:
1276      // Reverses all colors of the image.
1277
env.warning(L.l("imagefilter(IMG_FILTER_NEGATE) unimplemented"));
1278      return false;
1279
1280    case IMG_FILTER_GRAYSCALE:
1281      // Converts the image into grayscale.
1282
env.warning(L.l("imagefilter(IMG_FILTER_GRAYSCALE) unimplemented"));
1283      return false;
1284
1285    case IMG_FILTER_BRIGHTNESS:
1286      // Changes brightness of the image. Arg1 sets level of brightness.
1287
env.warning(L.l("imagefilter(IMG_FILTER_BRIGHTNESS) unimplementetd"));
1288      return false;
1289
1290    case IMG_FILTER_CONTRAST:
1291      // Changes contrast of the image. Use arg1 to set level of contrast.
1292
env.warning(L.l("imagefilter(IMG_FILTER_CONTRAST) unimplementetd"));
1293      return false;
1294
1295    case IMG_FILTER_COLORIZE:
1296      // Like IMG_FILTER_GRAYSCALE, except you can specify the color. Use
1297
// arg1, arg2 and arg3 in the form of red, blue, green. The range
1298
// for each color is 0 to 255.
1299
env.warning(L.l("imagefilter(IMG_FILTER_COLORIZE) unimplemented"));
1300      return false;
1301
1302    case IMG_FILTER_EDGEDETECT:
1303      // Uses edge detection to highlight the edges in the image.
1304
env.warning(L.l("imagefilter(IMG_FILTER_EDGEDETECT) unimplemented"));
1305      return false;
1306
1307    case IMG_FILTER_EMBOSS:
1308      // Embosses the image.
1309
env.warning(L.l("imagefilter(IMG_FILTER_EMBOSS) unimplemented"));
1310      return false;
1311
1312    case IMG_FILTER_GAUSSIAN_BLUR:
1313      // Blurs the image using the Gaussian method.
1314
env.warning(L.l("imagefilter(IMG_FILTER_GAUSSIAN_BLUR) "+
1315              "unimplemented"));
1316      return false;
1317
1318    case IMG_FILTER_SELECTIVE_BLUR:
1319      // Blurs the image.
1320
env.warning(L.l("imagefilter(IMG_FILTER_SELECTIVE_BLUR) "+
1321              "unimplemented"));
1322      return false;
1323
1324    case IMG_FILTER_MEAN_REMOVAL:
1325      // Uses mean removal to achieve a "sketchy" effect.
1326
env.warning(L.l("imagefilter(IMG_FILTER_MEAN_REMOVAL) "+
1327              "unimplemented"));
1328      return false;
1329
1330    case IMG_FILTER_SMOOTH:
1331      // Makes the image smoother. Use arg1 to set the level of smoothness.
1332
env.warning(L.l("imagefilter(IMG_FILTER_SMOOTH) unimplemented"));
1333      return false;
1334
1335    default:
1336      throw new QuercusException("unknown filterType in imagefilter()");
1337      }
1338  }
1339
1340
1341  // Other ///////////////////////////////////////////////////////////
1342

1343  /**
1344   * Embe into single tags.
1345   */

1346  public static boolean iptcembed(String JavaDoc iptcdata, String JavaDoc jpegFileName,
1347                  @Optional int spool)
1348  {
1349    throw new QuercusException("iptcembed is not [yet] supported");
1350  }
1351
1352  /**
1353   * Apply a gamma correction to a GD image
1354   */

1355  public static boolean imagegammacorrect(QuercusImage image,
1356                      float gammaBefore, float gammaAfter)
1357  {
1358    // this is a no-op in PHP; apparently the GD library dropped
1359
// support for gamma correction between v1.8 and v2.0
1360
return true;
1361  }
1362
1363  /**
1364   * Rotate an image with a given angle
1365   */

1366  public static boolean imagerotate(QuercusImage image, float angle,
1367                    int backgroundColor,
1368                    @Optional int ignoreTransparent)
1369  {
1370    // this function is broken on most PHP installs: "Note: This
1371
// function is only available if PHP is compiled with the bundled
1372
// version of the GD library."
1373
return false;
1374  }
1375
1376
1377  // PHP Documentation Lacking /////////////////////////////////////////
1378

1379  /**
1380   * Get the index of the color which has the hue, white and blackness
1381   * nearest to the given color
1382   */

1383  public static long imagecolorclosesthwb(QuercusImage image,
1384                      int r, int g, int b)
1385  {
1386    throw new QuercusException("imagecolorclosesthwb is not supported");
1387  }
1388
1389  /**
1390   * Set the alpha blending flag to use the bundled libgd layering effects
1391   */

1392  public static boolean imagelayereffect(QuercusImage image, int effect)
1393  {
1394    // XXX: there is no documentation for how this function ought to work
1395
// http://us3.php.net/manual/en/function.imagelayereffect.php
1396
return false;
1397  }
1398
1399
1400  // Postscript/Truetype Fonts ////////////////////////////////////////////
1401

1402  /**
1403   * Give the bounding box of a text using fonts via freetype2
1404   */

1405  public static ArrayValue imageftbbox(float size, float angle, Path fontFile,
1406                       String JavaDoc text,
1407                       @Optional ArrayValue extraInfo)
1408  {
1409    throw new QuercusException("imageftbbox() not implemented");
1410  }
1411
1412  /**
1413   * Write text to the image using fonts using FreeType 2
1414   */

1415  public static ArrayValue imagefttext(QuercusImage image, float size,
1416                       float angle, int x, int y, int col,
1417                       Path fontFile, String JavaDoc text,
1418                       ArrayValue extraInfo)
1419  {
1420    throw new QuercusException("imagefttext() not implemented");
1421  }
1422
1423  /**
1424   * Load a new font
1425   */

1426  public static long imageloadfont(Path file)
1427  {
1428    throw new QuercusException("imageloadfont() not implemented");
1429  }
1430
1431  /**
1432   * Give the bounding box of a text rectangle using PostScript Type1 fonts
1433   */

1434  public static ArrayValue imagepsbbox(String JavaDoc text, int font, int size,
1435                       @Optional int space,
1436                       @Optional int tightness,
1437                       @Optional float angle)
1438  {
1439    throw new QuercusException("imagepsbbox() not implemented");
1440  }
1441
1442  /**
1443   * Make a copy of an already loaded font for further modification
1444   */

1445  public static int imagepscopyfont(Value fontIndex)
1446  {
1447    throw new QuercusException("imagepscopyfont() not implemented");
1448  }
1449
1450  /**
1451   * Change the character encoding vector of a font
1452   */

1453  public static boolean imagepsencodefont(Value fontIndex, Path encodingFile)
1454  {
1455    throw new QuercusException("imagepsencodefont() not implemented");
1456  }
1457
1458  /**
1459   * Extend or condense a font
1460   */

1461  public static boolean imagepsextendfont(int fontIndex, float extend)
1462  {
1463    throw new QuercusException("imagepsextendfont() not implemented");
1464  }
1465
1466  /**
1467   * Free memory used by a PostScript Type 1 font
1468   */

1469  public static boolean imagepsfreefont(Value fontIndex)
1470  {
1471    throw new QuercusException("imagepsfreefont() not implemented");
1472  }
1473
1474  /**
1475   * Load a PostScript Type 1 font from file
1476   */

1477  public static Value imagepsloadfont(Path fontFile)
1478  {
1479    throw new QuercusException("imagepsloadfont() not implemented");
1480  }
1481
1482  /**
1483   * Slant a font
1484   */

1485  public static boolean imagepsslantfont(Value fontIndex, float slant)
1486  {
1487    throw new QuercusException("imagepsslantfont() not implemented");
1488  }
1489
1490  /**
1491   * To draw a text string over an image using PostScript Type1 fonts
1492   */

1493  public static ArrayValue imagepstext(QuercusImage image,
1494                       String JavaDoc text,
1495                       Value fontIndex,
1496                       int size, int fg, int bg, int x, int y,
1497                       @Optional int space,
1498                       @Optional int tightness,
1499                       @Optional float angle,
1500                       @Optional int antialias_steps)
1501  {
1502    throw new QuercusException("imagepstext() not implemented");
1503  }
1504
1505
1506  // WBMP Images ////////////////////////////////////////////////////////////
1507

1508  /**
1509   * Output image to browser or file
1510   */

1511  public static void image2wbmp(QuercusImage image,
1512                @Optional Path filename,
1513                @Optional int threshhold)
1514  {
1515    throw new QuercusException("not supported");
1516  }
1517
1518  /**
1519   * Convert JPEG image file to WBMP image file
1520   */

1521  public static void jpeg2wbmp(String JavaDoc jpegFilename,
1522                   String JavaDoc wbmpName,
1523                   int d_height,
1524                   int d_width,
1525                   int threshhold)
1526  {
1527    throw new QuercusException("not supported");
1528  }
1529
1530  /**
1531   * Convert PNG image file to WBM
1532   */

1533  public static void png2wbmp(String JavaDoc pngFilename,
1534                  String JavaDoc wbmpName,
1535                  int d_height,
1536                  int d_width,
1537                  int threshhold)
1538  {
1539    throw new QuercusException("not supported");
1540  }
1541
1542
1543
1544  // [Currently] Unsupported Image Formats //////////////////////////////////
1545

1546  /**
1547   * Create a new image from GD2 file or URL
1548   */

1549  public static void imagecreatefromgd2(Path file)
1550  {
1551    throw new QuercusException(".gd images are not supported");
1552  }
1553
1554  /**
1555   * Create a new image from a given part of GD2 file or URL
1556   */

1557  public static void imagecreatefromgd2part(Path file,
1558                        int srcX, int srcY,
1559                        int width, int height)
1560  {
1561    throw new QuercusException(".gd images are not supported");
1562  }
1563
1564  /**
1565   * Create a new image from GD file or URL
1566   */

1567  public static void imagecreatefromgd(Path file)
1568  {
1569    throw new QuercusException(".gd images are not supported");
1570  }
1571
1572  /**
1573   * Output GD2 image to browser or file
1574   */

1575  public static void imagegd2(QuercusImage image, @Optional Path file)
1576  {
1577    throw new QuercusException("imagegd2 is not implemented");
1578  }
1579
1580  /**
1581   * Output GD image to browser or file
1582   */

1583  public static void imagegd(QuercusImage image, @Optional Path file)
1584  {
1585    throw new QuercusException("imagegd is not implemented");
1586  }
1587
1588
1589  // Private Helpers ////////////////////////////////////////////////////////
1590

1591  private static Color intToColor(int argb)
1592  {
1593    // don't forget: PHP alpha channel is only 7 bits
1594
int alpha = argb >> 24;
1595    alpha <<= 1;
1596    alpha |= ((alpha & 0x2) >> 1); // copy bit #2 to LSB
1597

1598    return new Color((argb >> 16) & 0xff,
1599             (argb >> 8) & 0xff,
1600             (argb >> 0) & 0xff,
1601             alpha);
1602  }
1603
1604  // Inner Classes ////////////////////////////////////////////////////////
1605

1606  static class ImageInfo {
1607    int _width;
1608    int _height;
1609    int _type;
1610
1611    int _bits;
1612
1613    String JavaDoc _mime;
1614  }
1615
1616  public static class QuercusImage extends ResourceValue {
1617    private int _width;
1618    private int _height;
1619    BufferedImage JavaDoc _bufferedImage;
1620    private Graphics2D _graphics;
1621
1622    private BufferedImage JavaDoc _brush;
1623    private int[] _style;
1624    private int _thickness;
1625
1626    public QuercusImage(int width, int height)
1627    {
1628      _width = width;
1629      _height = height;
1630      _bufferedImage =
1631    new BufferedImage JavaDoc(width, height, BufferedImage.TYPE_INT_RGB);
1632      _graphics = (Graphics2D)_bufferedImage.getGraphics();
1633    }
1634
1635    public QuercusImage(InputStream JavaDoc inputStream)
1636    {
1637      try {
1638    _bufferedImage = ImageIO.read(inputStream);
1639    _width = _bufferedImage.getWidth(null);
1640    _height = _bufferedImage.getHeight(null);
1641    _graphics = (Graphics2D)_bufferedImage.getGraphics();
1642      }
1643      catch (IOException JavaDoc e) {
1644    throw new QuercusException(e);
1645      }
1646    }
1647
1648    public QuercusImage(Env env, Path filename)
1649    {
1650      try {
1651    _bufferedImage = ImageIO.read(filename.openRead());
1652    _width = _bufferedImage.getWidth(null);
1653    _height = _bufferedImage.getHeight(null);
1654    _graphics = (Graphics2D)_bufferedImage.getGraphics();
1655      }
1656      catch (IOException JavaDoc e) {
1657    throw new QuercusException(e);
1658      }
1659    }
1660
1661    public String JavaDoc toString()
1662    {
1663      return "resource(Image)";
1664    }
1665
1666    public int getPixel(int x, int y)
1667    {
1668      return _bufferedImage.getRGB(x, y);
1669    }
1670
1671    public void setPixel(int x, int y, int color)
1672    {
1673      _bufferedImage.setRGB(x, y, color);
1674    }
1675
1676    public Graphics2D getGraphics()
1677    {
1678      return _graphics;
1679    }
1680
1681    public Font getFont(int font)
1682    {
1683      if (font <= 1)
1684    return new Font("sansserif", 0, 8);
1685      switch(font) {
1686    case 2: return new Font("sansserif", 0, 10);
1687    case 3: return new Font("sansserif", 0, 11);
1688    case 4: return new Font("sansserif", 0, 12);
1689    default:
1690      return new Font("sansserif", 0, 14);
1691      }
1692    }
1693
1694    public int getWidth()
1695    {
1696      return _bufferedImage.getWidth(null);
1697    }
1698
1699    public int getHeight()
1700    {
1701      return _bufferedImage.getHeight(null);
1702    }
1703
1704    public void fill(Shape shape, int color)
1705    {
1706      _graphics.setColor(intToColor(color));
1707      _graphics.fill(shape);
1708    }
1709
1710    public void stroke(Shape shape, int color)
1711    {
1712      switch(color)
1713    {
1714      case IMG_COLOR_STYLED:
1715        strokeStyled(shape);
1716        break;
1717      case IMG_COLOR_BRUSHED:
1718        strokeBrushed(shape);
1719        break;
1720      default:
1721        _graphics.setColor(intToColor(color));
1722        _graphics.setStroke(new BasicStroke(_thickness));
1723        _graphics.draw(shape);
1724        break;
1725    }
1726    }
1727    
1728    private void strokeStyled(Shape shape)
1729    {
1730      for(int i=0; i<_style.length; i++)
1731    {
1732      _graphics.setColor(intToColor(_style[i]));
1733      Stroke stroke =
1734        new BasicStroke(_thickness,
1735                BasicStroke.JOIN_ROUND, BasicStroke.CAP_ROUND, 1,
1736                new float[] { 1, _style.length-1 },
1737                i);
1738      _graphics.setStroke(stroke);
1739      _graphics.draw(shape);
1740    }
1741    }
1742
1743    private void strokeBrushed(Shape shape)
1744    {
1745      // XXX: support "styled brushes" (see imagesetstyle() example on php.net)
1746
Graphics2D g = _graphics;
1747      FlatteningPathIterator JavaDoc fpi =
1748    new FlatteningPathIterator JavaDoc(shape.getPathIterator(g.getTransform()), 1);
1749      float[] floats = new float[6];
1750      fpi.currentSegment(floats);
1751      float last_x = floats[0];
1752      float last_y = floats[1];
1753      while(! fpi.isDone())
1754    {
1755      fpi.currentSegment(floats);
1756      int distance = (int)Math.sqrt((floats[0]-last_x)*(floats[0]-last_x)+
1757                    (floats[1]-last_y)*(floats[1]-last_y));
1758      if (distance <= 1) distance = 1;
1759      for(int i=1; i<=distance; i++)
1760        {
1761          int x = (int)(floats[0]*i+last_x*(distance-i))/distance;
1762          x -= _brush.getWidth() / 2;
1763          int y = (int)(floats[1]*i+last_y*(distance-i))/distance;
1764          y -= _brush.getHeight() / 2;
1765          g.drawImage(_brush, x, y, null);
1766        }
1767      last_x = floats[0];
1768      last_y = floats[1];
1769      fpi.next();
1770    }
1771    }
1772
1773    public void setThickness(int thickness)
1774    {
1775      _style = null;
1776      _thickness = thickness;
1777    }
1778
1779    public void setStyle(ArrayValue colors)
1780    {
1781      _style = new int[colors.getSize()];
1782      ArrayValue.Entry e = colors.getHead();
1783      for(int i=0; i<_style.length; i++)
1784    {
1785      _style[i] = e.getValue().toInt();
1786      e = e.getNext();
1787    }
1788    }
1789
1790    public void setBrush(QuercusImage image)
1791    {
1792      _brush = image._bufferedImage;
1793    }
1794
1795    public BufferedImage JavaDoc getBrush()
1796    {
1797      return _brush;
1798    }
1799
1800    public void flood(int x, int y, int color)
1801    {
1802      flood(x, y, color, 0, false);
1803    }
1804
1805    public void flood(int x, int y, int color, int border)
1806    {
1807      flood(x, y, color, border, true);
1808    }
1809
1810    private void flood(int startx, int starty, int color, int border, boolean useBorder)
1811    {
1812      java.util.Queue JavaDoc<Integer JavaDoc> xq = new LinkedList JavaDoc<Integer JavaDoc>();
1813      java.util.Queue JavaDoc<Integer JavaDoc> yq = new LinkedList JavaDoc<Integer JavaDoc>();
1814      xq.add(startx);
1815      yq.add(starty);
1816      color &= 0x00ffffff;
1817      border &= 0x00ffffff;
1818      while(xq.size() > 0)
1819    {
1820      int x = xq.poll();
1821      int y = yq.poll();
1822      int p = (getPixel(x, y) & 0x00ffffff);
1823      if (useBorder ? (p==border||p==color) : (p != 0)) continue;
1824      setPixel(x, y, color);
1825      for(int i=x-1; i>=0; i--)
1826        {
1827          p = (getPixel(i, y) & 0x00ffffff);
1828          if (useBorder ? (p==border||p==color) : (p!= 0)) break;
1829          setPixel(i, y, color);
1830          xq.add(i);
1831          yq.add(y+1);
1832          xq.add(i);
1833          yq.add(y-1);
1834        }
1835      for(int i=x+1; i<getWidth(); i++)
1836        {
1837          p = (getPixel(i, y) & 0x00ffffff);
1838          if (useBorder ? (p==border||p==color) : (p != 0)) break;
1839          setPixel(i, y, color);
1840          xq.add(i);
1841          yq.add(y+1);
1842          xq.add(i);
1843          yq.add(y-1);
1844        }
1845      xq.add(x);
1846      yq.add(y+1);
1847      xq.add(x);
1848      yq.add(y-1);
1849    }
1850    }
1851
1852  }
1853
1854
1855
1856}
1857
1858
Popular Tags