KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > loader > CmsImageScaler


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/loader/CmsImageScaler.java,v $
3  * Date : $Date: 2006/09/22 15:17:03 $
4  * Version: $Revision: 1.4 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (C) 2002 - 2005 Alkacon Software (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.loader;
33
34 import com.alkacon.simapi.RenderSettings;
35 import com.alkacon.simapi.Simapi;
36 import com.alkacon.simapi.filter.GrayscaleFilter;
37 import com.alkacon.simapi.filter.ShadowFilter;
38
39 import org.opencms.file.CmsFile;
40 import org.opencms.file.CmsObject;
41 import org.opencms.file.CmsProperty;
42 import org.opencms.file.CmsPropertyDefinition;
43 import org.opencms.file.CmsResource;
44 import org.opencms.main.CmsLog;
45 import org.opencms.main.OpenCms;
46 import org.opencms.util.CmsStringUtil;
47
48 import java.awt.Color JavaDoc;
49 import java.awt.Rectangle JavaDoc;
50 import java.awt.image.BufferedImage JavaDoc;
51 import java.util.ArrayList JavaDoc;
52 import java.util.Arrays JavaDoc;
53 import java.util.Iterator JavaDoc;
54 import java.util.List JavaDoc;
55
56 import javax.servlet.http.HttpServletRequest JavaDoc;
57
58 import org.apache.commons.logging.Log;
59
60 /**
61  * Creates scaled images, acting as it's own parameter container.<p>
62  *
63  * @author Alexander Kandzior
64  *
65  * @version $Revision: 1.4 $
66  *
67  * @since 6.2.0
68  */

69 public class CmsImageScaler {
70
71     /** The name of the transparent color (for the backgound image). */
72     public static final String JavaDoc COLOR_TRANSPARENT = "transparent";
73
74     /** The name of the grayscale image filter. */
75     public static final String JavaDoc FILTER_GRAYSCALE = "grayscale";
76
77     /** The name of the shadow image filter. */
78     public static final String JavaDoc FILTER_SHADOW = "shadow";
79
80     /** The supported image filter names. */
81     public static final List JavaDoc FILTERS = Arrays.asList(new String JavaDoc[] {FILTER_GRAYSCALE, FILTER_SHADOW});
82
83     /** The (optional) parameter used for sending the scale information of an image in the http request. */
84     public static final String JavaDoc PARAM_SCALE = "__scale";
85
86     /** The default maximum image size (width * height) to apply image blurring when downscaling (setting this to high may case "out of memory" errors). */
87     public static final int SCALE_DEFAULT_MAX_BLUR_SIZE = 2500 * 2500;
88
89     /** The default maximum image size (width or height) to allow when updowscaling an image using request parameters. */
90     public static final int SCALE_DEFAULT_MAX_SIZE = 1500;
91
92     /** The scaler parameter to indicate the requested image background color (if required). */
93     public static final String JavaDoc SCALE_PARAM_COLOR = "c";
94
95     /** The scaler parameter to indicate the requested image filter. */
96     public static final String JavaDoc SCALE_PARAM_FILTER = "f";
97
98     /** The scaler parameter to indicate the requested image height. */
99     public static final String JavaDoc SCALE_PARAM_HEIGHT = "h";
100
101     /** The scaler parameter to indicate the requested image position (if required). */
102     public static final String JavaDoc SCALE_PARAM_POS = "p";
103
104     /** The scaler parameter to indicate to requested image save quality in percent (if applicable, for example used with JPEG images). */
105     public static final String JavaDoc SCALE_PARAM_QUALITY = "q";
106
107     /** The scaler parameter to indicate to requested <code>{@link java.awt.RenderingHints}</code> settings. */
108     public static final String JavaDoc SCALE_PARAM_RENDERMODE = "r";
109
110     /** The scaler parameter to indicate the requested scale type. */
111     public static final String JavaDoc SCALE_PARAM_TYPE = "t";
112
113     /** The scaler parameter to indicate the requested image width. */
114     public static final String JavaDoc SCALE_PARAM_WIDTH = "w";
115
116     /** The log object for this class. */
117     protected static final Log LOG = CmsLog.getLog(CmsImageScaler.class);
118
119     /** The target background color (optional). */
120     private Color JavaDoc m_color;
121
122     /** The list of image filter names (Strings) to apply. */
123     private List JavaDoc m_filters;
124
125     /** The target height (required). */
126     private int m_height;
127
128     /** The maximum image size (width * height) to apply image blurring when downscaling (setting this to high may case "out of memory" errors). */
129     private int m_maxBlurSize;
130
131     /** The target position (optional). */
132     private int m_position;
133
134     /** The target image save quality (if applicable, for example used with JPEG images) (optional). */
135     private int m_quality;
136
137     /** The image processing renderings hints constant mode indicator (optional). */
138     private int m_renderMode;
139
140     /** The final (parsed and corrected) scale parameters. */
141     private String JavaDoc m_scaleParameters;
142
143     /** The target scale type (optional). */
144     private int m_type;
145
146     /** The target width (required). */
147     private int m_width;
148
149     /**
150      * Creates a new, empty image scaler object.<p>
151      */

152     public CmsImageScaler() {
153
154         init();
155     }
156
157     /**
158      * Creates a new image scaler for the given image contained in the byte array.<p>
159      *
160      * <b>Please note:</b>The image itself is not stored in the scaler, only the width and
161      * height dimensions of the image. To actually scale an image, you need to use
162      * <code>{@link #scaleImage(CmsFile)}</code>. This constructor is commonly used only
163      * to extract the image dimensions, for example when creating a String value for
164      * the <code>{@link CmsPropertyDefinition#PROPERTY_IMAGE_SIZE}</code> property.<p>
165      *
166      * In case the byte array can not be decoded to an image, or in case of other errors,
167      * <code>{@link #isValid()}</code> will return <code>false</code>.<p>
168      *
169      * @param content the image to calculate the dimensions for
170      * @param rootPath the root path of the resource (for error logging)
171      */

172     public CmsImageScaler(byte[] content, String JavaDoc rootPath) {
173
174         init();
175         try {
176             // read the scaled image
177
BufferedImage JavaDoc image = Simapi.read(content);
178             m_height = image.getHeight();
179             m_width = image.getWidth();
180         } catch (Exception JavaDoc e) {
181             // nothing we can do about this, keep the original properties
182
if (LOG.isDebugEnabled()) {
183                 LOG.debug(Messages.get().getBundle().key(Messages.ERR_UNABLE_TO_EXTRACT_IMAGE_SIZE_1, rootPath), e);
184             }
185             // set height / width to default of -1
186
init();
187         }
188     }
189
190     /**
191      * Creates a new image scaler that is a recale from the original size to the given scaler.<p>
192      *
193      * @param original the scaler that holds the original image dimensions
194      * @param target the image scaler to be used for rescaling this image scaler
195      *
196      * @deprecated use {@link #getReScaler(CmsImageScaler)} on the <code>original</code> scaler instead
197      */

198     public CmsImageScaler(CmsImageScaler original, CmsImageScaler target) {
199
200         CmsImageScaler scaler = original.getReScaler(target);
201         initValuesFrom(scaler);
202     }
203
204     /**
205      * Creates a new image scaler by reading the property <code>{@link CmsPropertyDefinition#PROPERTY_IMAGE_SIZE}</code>
206      * from the given resource.<p>
207      *
208      * In case of any errors reading or parsing the property,
209      * <code>{@link #isValid()}</code> will return <code>false</code>.<p>
210      *
211      * @param cms the OpenCms user context to use when reading the property
212      * @param res the resource to read the property from
213      */

214     public CmsImageScaler(CmsObject cms, CmsResource res) {
215
216         init();
217         String JavaDoc sizeValue = null;
218         if ((cms != null) && (res != null)) {
219             try {
220                 CmsProperty sizeProp = cms.readPropertyObject(res, CmsPropertyDefinition.PROPERTY_IMAGE_SIZE, false);
221                 if (!sizeProp.isNullProperty()) {
222                     // parse property value using standard procedures
223
sizeValue = sizeProp.getValue();
224                 }
225             } catch (Exception JavaDoc e) {
226                 // ignore
227
}
228         }
229         if (CmsStringUtil.isNotEmpty(sizeValue)) {
230             parseParameters(sizeValue);
231         }
232     }
233
234     /**
235      * Creates a new image scaler based on the given http request.<p>
236      *
237      * @param request the http request to read the parameters from
238      * @param maxScaleSize the maximum scale size (width or height) for the image
239      * @param maxBlurSize the maximum size of the image (width * height) to apply blur (may cause "out of memory" for large images)
240      */

241     public CmsImageScaler(HttpServletRequest JavaDoc request, int maxScaleSize, int maxBlurSize) {
242
243         init();
244         m_maxBlurSize = maxBlurSize;
245         String JavaDoc parameters = request.getParameter(CmsImageScaler.PARAM_SCALE);
246         if (CmsStringUtil.isNotEmpty(parameters)) {
247             parseParameters(parameters);
248             if (isValid()) {
249                 // valid parameters, check if scale size is not too big
250
if ((getWidth() > maxScaleSize) || (getHeight() > maxScaleSize)) {
251                     // scale size is too big, reset scaler
252
init();
253                 }
254             }
255         }
256     }
257
258     /**
259      * Creates a new image scaler based on the given parameter String.<p>
260      *
261      * @param parameters the scale parameters to use
262      */

263     public CmsImageScaler(String JavaDoc parameters) {
264
265         init();
266         if (CmsStringUtil.isNotEmpty(parameters)) {
267             parseParameters(parameters);
268         }
269     }
270
271     /**
272      * Creates a new image scaler based on the given base scaler and the given width and height.<p>
273      *
274      * @param base the base scaler to initialize the values with
275      * @param width the width to set for this scaler
276      * @param height the height to set for this scaler
277      */

278     protected CmsImageScaler(CmsImageScaler base, int width, int height) {
279
280         initValuesFrom(base);
281         setWidth(width);
282         setHeight(height);
283     }
284
285     /**
286      * Adds a filter name to the list of filters that should be applied to the image.<p>
287      *
288      * @param filter the filter name to add
289      */

290     public void addFilter(String JavaDoc filter) {
291
292         if (CmsStringUtil.isNotEmpty(filter)) {
293             filter = filter.trim().toLowerCase();
294             if (FILTERS.contains(filter)) {
295                 m_filters.add(filter);
296             }
297         }
298     }
299
300     /**
301      * @see java.lang.Object#clone()
302      */

303     public Object JavaDoc clone() {
304
305         CmsImageScaler clone = new CmsImageScaler();
306         clone.initValuesFrom(this);
307         return clone;
308     }
309
310     /**
311      * Returns the color.<p>
312      *
313      * @return the color
314      */

315     public Color JavaDoc getColor() {
316
317         return m_color;
318     }
319
320     /**
321      * Returns the color as a String.<p>
322      *
323      * @return the color as a String
324      */

325     public String JavaDoc getColorString() {
326
327         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
328         if (m_color == Simapi.COLOR_TRANSPARENT) {
329             result.append(COLOR_TRANSPARENT);
330         } else {
331             if (m_color.getRed() < 16) {
332                 result.append('0');
333             }
334             result.append(Integer.toString(m_color.getRed(), 16));
335             if (m_color.getGreen() < 16) {
336                 result.append('0');
337             }
338             result.append(Integer.toString(m_color.getGreen(), 16));
339             if (m_color.getBlue() < 16) {
340                 result.append('0');
341             }
342             result.append(Integer.toString(m_color.getBlue(), 16));
343         }
344         return result.toString();
345     }
346
347     /**
348      * Returns a new image scaler that is a downscale from the size of <code>this</code> scaler
349      * to the given scaler size.<p>
350      *
351      * If no downscale from this to the given scaler is required according to
352      * {@link #isDownScaleRequired(CmsImageScaler)}, then <code>null</code> is returned.<p>
353      *
354      * @param downScaler the image scaler that holds the downscaled target image dimensions
355      *
356      * @return a new image scaler that is a downscale from the size of <code>this</code> scaler
357      * to the given target scaler size, or <code>null</code>
358      */

359     public CmsImageScaler getDownScaler(CmsImageScaler downScaler) {
360
361         if (!isDownScaleRequired(downScaler)) {
362             // no downscaling is required
363
return null;
364         }
365
366         int downHeight = downScaler.getHeight();
367         int downWidth = downScaler.getWidth();
368
369         int height = getHeight();
370         int width = getWidth();
371
372         if (((height > width) && (downHeight < downWidth)) || ((width > height) && (downWidth < downHeight))) {
373             // adjust orientation
374
downHeight = downWidth;
375             downWidth = downScaler.getHeight();
376         }
377
378         if (width > downWidth) {
379             // width is too large, re-calculate height
380
float scale = (float)downWidth / (float)width;
381             downHeight = Math.round(height * scale);
382         } else if (height > downHeight) {
383             // height is too large, re-calculate width
384
float scale = (float)downHeight / (float)height;
385             downWidth = Math.round(width * scale);
386         } else {
387             // something is wrong, don't downscale
388
return null;
389         }
390
391         // now create and initialize the result scaler
392
return new CmsImageScaler(downScaler, downWidth, downHeight);
393     }
394
395     /**
396      * Returns the list of image filter names (Strings) to be applied to the image.<p>
397      *
398      * @return the list of image filter names (Strings) to be applied to the image
399      */

400     public List JavaDoc getFilters() {
401
402         return m_filters;
403     }
404
405     /**
406      * Returns the list of image filter names (Strings) to be applied to the image as a String.<p>
407      *
408      * @return the list of image filter names (Strings) to be applied to the image as a String
409      */

410     public String JavaDoc getFiltersString() {
411
412         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
413         Iterator JavaDoc i = m_filters.iterator();
414         while (i.hasNext()) {
415             String JavaDoc filter = (String JavaDoc)i.next();
416             result.append(filter);
417             if (i.hasNext()) {
418                 result.append(':');
419             }
420         }
421         return result.toString();
422     }
423
424     /**
425      * Returns the height.<p>
426      *
427      * @return the height
428      */

429     public int getHeight() {
430
431         return m_height;
432     }
433
434     /**
435      * Returns the image type from the given file name based on the file suffix (extension)
436      * and the available image writers.<p>
437      *
438      * For example, for the file name "opencms.gif" the type is GIF, for
439      * "opencms.jpg" is is "JPEG" etc.<p>
440      *
441      * In case the input filename has no suffix, or there is no known image writer for the format defined
442      * by the suffix, <code>null</code> is returned.<p>
443      *
444      * Any non-null result can be used if an image type input value is required.<p>
445      *
446      * @param filename the file name to get the type for
447      *
448      * @return the image type from the given file name based on the suffix and the available image writers,
449      * or null if no image writer is available for the format
450      */

451     public String JavaDoc getImageType(String JavaDoc filename) {
452
453         return Simapi.getImageType(filename);
454     }
455
456     /**
457      * Returns the maximum image size (width * height) to apply image blurring when downscaling images.<p>
458      *
459      * Image blurring is required to achive the best results for downscale operatios when the target image size
460      * is 2 times or more smaller then the original image size. This parameter controls the maximum size (width * height) of an
461      * image that is blurred before it is downscaled. If the image is larger, no blurring is done.
462      * However, image blurring is an expensive operation in both CPU usage and memory consumption.
463      * Setting the blur size to large may case "out of memory" errors.<p>
464      *
465      * @return the maximum image size (width * height) to apply image blurring when downscaling images
466      */

467     public int getMaxBlurSize() {
468
469         return m_maxBlurSize;
470     }
471
472     /**
473      * Returns the image pixel count, that is the image with multiplied by the image height.<p>
474      *
475      * If this scalier is not valid (see {@link #isValid()}) the result is undefined.<p>
476      *
477      * @return the image pixel count, that is the image with multiplied by the image height
478      */

479     public int getPixelCount() {
480
481         return m_width * m_height;
482     }
483
484     /**
485      * Returns the position.<p>
486      *
487      * @return the position
488      */

489     public int getPosition() {
490
491         return m_position;
492     }
493
494     /**
495      * Returns the image saving quality in percent (0 - 100).<p>
496      *
497      * This is used oly if applicable, for example when saving JPEG images.<p>
498      *
499      * @return the image saving quality in percent
500      */

501     public int getQuality() {
502
503         return m_quality;
504     }
505
506     /**
507      * Returns the image rendering mode constant.<p>
508      *
509      * Possible values are:<dl>
510      * <dt>{@link Simapi#RENDER_QUALITY} (default)</dt>
511      * <dd>Use best possible image processing - this may be slow sometimes.</dd>
512      *
513      * <dt>{@link Simapi#RENDER_SPEED}</dt>
514      * <dd>Fastest image processing but worse results - use this for thumbnails or where speed is more important then quality.</dd>
515      *
516      * <dt>{@link Simapi#RENDER_MEDIUM}</dt>
517      * <dd>Use default rendering hints from JVM - not recommended since it's almost as slow as the {@link Simapi#RENDER_QUALITY} mode.</dd></dl>
518      *
519      * @return the image rendering mode constant
520      */

521     public int getRenderMode() {
522
523         return m_renderMode;
524     }
525
526     /**
527      * Returns a new image scaler that is a rescaler from the <code>this</code> scaler
528      * size to the given target scaler size.<p>
529      *
530      * The height of the target image is calculated in proportion
531      * to the original image width. If the width of the the original image is not known,
532      * the target image width is calculated in proportion to the original image height.<p>
533      *
534      * @param target the image scaler that holds the target image dimensions
535      *
536      * @return a new image scaler that is a rescale from the <code>this</code> scaler
537      * size to the given target scaler size.<p>
538      */

539     public CmsImageScaler getReScaler(CmsImageScaler target) {
540
541         int height = target.getHeight();
542         int width = target.getWidth();
543
544         if ((width > 0) && (getWidth() > 0)) {
545             // width is known, calculate height
546
float scale = (float)width / (float)getWidth();
547             height = Math.round(getHeight() * scale);
548         } else if ((height > 0) && (getHeight() > 0)) {
549             // height is known, calculate width
550
float scale = (float)height / (float)getHeight();
551             width = Math.round(getWidth() * scale);
552         } else if (isValid() && !target.isValid()) {
553             // scaler is not valid but original is, so use original size of image
554
width = getWidth();
555             height = getHeight();
556         }
557
558         if ((target.getType() == 1) && (!target.isValid())) {
559             // "no upscale" has been requested, only one target dimension was given
560
if ((target.getWidth() > 0) && (getWidth() < width)) {
561                 // target width was given, target image should have this width
562
height = getHeight();
563             } else if ((target.getHeight() > 0) && (getHeight() < height)) {
564                 // target height was given, target image should have this height
565
width = getWidth();
566             }
567         }
568
569         // now create and initialize the result scaler
570
return new CmsImageScaler(target, width, height);
571     }
572
573     /**
574      * Returns the type.<p>
575      *
576      * Possible values are:<dl>
577      *
578      * <dt>0 (default): Scale to exact target size with background padding</dt><dd><ul>
579      * <li>enlarge image to fit in target size (if required)
580      * <li>reduce image to fit in target size (if required)
581      * <li>keep image aspect ratio / propotions intact
582      * <li>fill up with bgcolor to reach exact target size
583      * <li>fit full image inside target size (only applies if reduced)</ul></dd>
584      *
585      * <dt>1: Thumbnail generation mode (like 0 but no image enlargement)</dt><dd><ul>
586      * <li>dont't enlarge image
587      * <li>reduce image to fit in target size (if required)
588      * <li>keep image aspect ratio / propotions intact
589      * <li>fill up with bgcolor to reach exact target size
590      * <li>fit full image inside target size (only applies if reduced)</ul></dd>
591      *
592      * <dt>2: Scale to exact target size, crop what does not fit</dt><dd><ul>
593      * <li>enlarge image to fit in target size (if required)
594      * <li>reduce image to fit in target size (if required)
595      * <li>keep image aspect ratio / propotions intact
596      * <li>fit full image inside target size (crop what does not fit)</ul></dd>
597      *
598      * <dt>3: Scale and keep image propotions, target size variable</dt><dd><ul>
599      * <li>enlarge image to fit in target size (if required)
600      * <li>reduce image to fit in target size (if required)
601      * <li>keep image aspect ratio / propotions intact
602      * <li>scaled image will not be padded or cropped, so target size is likley not the exact requested size</ul></dd>
603      *
604      * <dt>4: Don't keep image propotions, use exact target size</dt><dd><ul>
605      * <li>enlarge image to fit in target size (if required)
606      * <li>reduce image to fit in target size (if required)
607      * <li>don't keep image aspect ratio / propotions intact
608      * <li>the image will be scaled exactly to the given target size and likley will be loose proportions</ul></dd>
609      * </dl>
610      *
611      * @return the type
612      */

613     public int getType() {
614
615         return m_type;
616     }
617
618     /**
619      * Returns the width.<p>
620      *
621      * @return the width
622      */

623     public int getWidth() {
624
625         return m_width;
626     }
627
628     /**
629      * Returns a new image scaler that is a width based downscale from the size of <code>this</code> scaler
630      * to the given scaler size.<p>
631      *
632      * If no downscale from this to the given scaler is required because the width of <code>this</code>
633      * scaler is not larger than the target width, then the image dimensions of <code>this</code> scaler
634      * are unchanged in the result scaler. No upscaling is done!<p>
635      *
636      * @param downScaler the image scaler that holds the downscaled target image dimensions
637      *
638      * @return a new image scaler that is a downscale from the size of <code>this</code> scaler
639      * to the given target scaler size
640      */

641     public CmsImageScaler getWidthScaler(CmsImageScaler downScaler) {
642
643         int width = downScaler.getWidth();
644         int height;
645
646         if (getWidth() > width) {
647             // width is too large, re-calculate height
648
float scale = (float)width / (float)getWidth();
649             height = Math.round(getHeight() * scale);
650         } else {
651             // width is ok
652
width = getWidth();
653             height = getHeight();
654         }
655
656         // now create and initialize the result scaler
657
return new CmsImageScaler(downScaler, width, height);
658     }
659
660     /**
661      * @see java.lang.Object#hashCode()
662      */

663     public int hashCode() {
664
665         return toString().hashCode();
666     }
667
668     /**
669      * Returns <code>true</code> if this image scaler must be downscaled when compared to the
670      * given "downscale" image scaler.<p>
671      *
672      * If either <code>this</code> scaler or the given <code>downScaler</code> is invalid according to
673      * {@link #isValid()}, then <code>false</code> is returned.<p>
674      *
675      * The use case: <code>this</code> scaler represents an image (that is contains width and height of
676      * an image). The <code>downScaler</code> represents the maximum wanted image. The scalers
677      * are compared and if the image represented by <code>this</code> scaler is too large,
678      * <code>true</code> is returned. Image orientation is ignored, so for example an image with 600x800 pixel
679      * will NOT be downscaled if the target size is 800x600 but kept unchanged.<p>
680      *
681      * @param downScaler the downscaler to compare this image scaler with
682      *
683      * @return <code>true</code> if this image scaler must be downscaled when compared to the
684      * given "downscale" image scaler
685      */

686     public boolean isDownScaleRequired(CmsImageScaler downScaler) {
687
688         if ((downScaler == null) || !isValid() || !downScaler.isValid()) {
689             // one of the scalers is invalid
690
return false;
691         }
692
693         if (getPixelCount() < (downScaler.getPixelCount() / 2)) {
694             // the image has much less pixels then the target, so don't downscale
695
return false;
696         }
697
698         int downWidth = downScaler.getWidth();
699         int downHeight = downScaler.getHeight();
700         if (downHeight > downWidth) {
701             // normalize image orientation - the width should always be the large side
702
downWidth = downHeight;
703             downHeight = downScaler.getWidth();
704         }
705         int height = getHeight();
706         int width = getWidth();
707         if (height > width) {
708             // normalize image orientation - the width should always be the large side
709
width = height;
710             height = getWidth();
711         }
712
713         return (width > downWidth) || (height > downHeight);
714     }
715
716     /**
717      * Returns <code>true</code> if all required parameters are available.<p>
718      *
719      * Required parameters are "h" (height), and "w" (width).<p>
720      *
721      * @return <code>true</code> if all required parameters are available
722      */

723     public boolean isValid() {
724
725         return (m_width > 0) && (m_height > 0);
726     }
727
728     /**
729      * Returns a scaled version of the given image byte content according this image scalers parameters.<p>
730      *
731      * @param content the image byte content to scale
732      * @param rootPath the root path of the image file in the VFS
733      *
734      * @return a scaled version of the given image byte content according to the provided scaler parameters
735      */

736     public byte[] scaleImage(byte[] content, String JavaDoc rootPath) {
737
738         byte[] result = content;
739
740         RenderSettings renderSettings;
741         if ((m_renderMode == 0) && (m_quality == 0)) {
742             // use default render mode and quality
743
renderSettings = new RenderSettings(Simapi.RENDER_QUALITY);
744         } else {
745             // use special render mode and/or quality
746
renderSettings = new RenderSettings(m_renderMode);
747             if (m_quality != 0) {
748                 renderSettings.setCompressionQuality(m_quality / 100f);
749             }
750         }
751         // set max blur siuze
752
renderSettings.setMaximumBlurSize(m_maxBlurSize);
753         // new create the scaler
754
Simapi scaler = new Simapi(renderSettings);
755         // calculate a valid image type supported by the imaging libary (e.g. "JPEG", "GIF")
756
String JavaDoc imageType = Simapi.getImageType(rootPath);
757         if (imageType == null) {
758             // no type given, maybe the name got mixed up
759
String JavaDoc mimeType = OpenCms.getResourceManager().getMimeType(rootPath, null, null);
760             // check if this is another known mime type, if so DONT use it (images should not be named *.pdf)
761
if (mimeType == null) {
762                 // no mime type found, use JPEG format to write images to the cache
763
imageType = Simapi.TYPE_JPEG;
764             }
765         }
766         if (imageType == null) {
767             // unknown type, unable to scale the image
768
if (LOG.isDebugEnabled()) {
769                 LOG.debug(Messages.get().getBundle().key(Messages.ERR_UNABLE_TO_SCALE_IMAGE_2, rootPath, toString()));
770             }
771             return result;
772         }
773         try {
774             BufferedImage JavaDoc image = Simapi.read(content);
775
776             Color JavaDoc color = getColor();
777
778             if (!m_filters.isEmpty()) {
779                 Iterator JavaDoc i = m_filters.iterator();
780                 while (i.hasNext()) {
781                     String JavaDoc filter = (String JavaDoc)i.next();
782                     if (FILTER_GRAYSCALE.equals(filter)) {
783                         // add a grayscale filter
784
GrayscaleFilter grayscaleFilter = new GrayscaleFilter();
785                         renderSettings.addImageFilter(grayscaleFilter);
786                     } else if (FILTER_SHADOW.equals(filter)) {
787                         // add a drop shadow filter
788
ShadowFilter shadowFilter = new ShadowFilter();
789                         shadowFilter.setXOffset(5);
790                         shadowFilter.setYOffset(5);
791                         shadowFilter.setOpacity(192);
792                         shadowFilter.setBackgroundColor(color.getRGB());
793                         color = Simapi.COLOR_TRANSPARENT;
794                         renderSettings.setTransparentReplaceColor(Simapi.COLOR_TRANSPARENT);
795                         renderSettings.addImageFilter(shadowFilter);
796                     }
797                 }
798             }
799
800             switch (getType()) {
801                 // select the "right" method of scaling according to the "t" parameter
802
case 1:
803                     // thumbnail generation mode (like 0 but no image enlargement)
804
image = scaler.resize(image, getWidth(), getHeight(), color, getPosition(), false);
805                     break;
806                 case 2:
807                     // scale to exact target size, crop what does not fit
808
image = scaler.resize(image, getWidth(), getHeight(), getPosition());
809                     break;
810                 case 3:
811                     // scale and keep image propotions, target size variable
812
image = scaler.resize(image, getWidth(), getHeight(), true);
813                     break;
814                 case 4:
815                     // don't keep image propotions, use exact target size
816
image = scaler.resize(image, getWidth(), getHeight(), false);
817                     break;
818                 default:
819                     // scale to exact target size with background padding
820
image = scaler.resize(image, getWidth(), getHeight(), color, getPosition(), true);
821             }
822
823             if (!m_filters.isEmpty()) {
824                 Rectangle JavaDoc targetSize = scaler.applyFilterDimensions(getWidth(), getHeight());
825                 image = scaler.resize(
826                     image,
827                     (int)targetSize.getWidth(),
828                     (int)targetSize.getHeight(),
829                     Simapi.COLOR_TRANSPARENT,
830                     Simapi.POS_CENTER);
831                 image = scaler.applyFilters(image);
832             }
833
834             // get the byte result for the scaled image
835
result = scaler.getBytes(image, imageType);
836         } catch (Exception JavaDoc e) {
837             if (LOG.isDebugEnabled()) {
838                 LOG.debug(Messages.get().getBundle().key(Messages.ERR_UNABLE_TO_SCALE_IMAGE_2, rootPath, toString()), e);
839             }
840         }
841         return result;
842     }
843
844     /**
845      * Returns a scaled version of the given image file according this image scalers parameters.<p>
846      *
847      * @param file the image file to scale
848      *
849      * @return a scaled version of the given image file according to the provided scaler parameters
850      */

851     public byte[] scaleImage(CmsFile file) {
852
853         return scaleImage(file.getContents(), file.getRootPath());
854     }
855
856     /**
857      * Sets the color.<p>
858      *
859      * @param color the color to set
860      */

861     public void setColor(Color JavaDoc color) {
862
863         m_color = color;
864     }
865
866     /**
867      * Sets the color as a String.<p>
868      *
869      * @param value the color to set
870      */

871     public void setColor(String JavaDoc value) {
872
873         if (COLOR_TRANSPARENT.indexOf(value) == 0) {
874             setColor(Simapi.COLOR_TRANSPARENT);
875         } else {
876             setColor(CmsStringUtil.getColorValue(value, Color.WHITE, SCALE_PARAM_COLOR));
877         }
878     }
879
880     /**
881      * Sets the list of filters as a String.<p>
882      *
883      * @param value the list of filters to set
884      */

885     public void setFilters(String JavaDoc value) {
886
887         m_filters = new ArrayList JavaDoc();
888         List JavaDoc filters = CmsStringUtil.splitAsList(value, ':');
889         Iterator JavaDoc i = filters.iterator();
890         while (i.hasNext()) {
891             String JavaDoc filter = (String JavaDoc)i.next();
892             filter = filter.trim().toLowerCase();
893             Iterator JavaDoc j = FILTERS.iterator();
894             while (j.hasNext()) {
895                 String JavaDoc candidate = (String JavaDoc)j.next();
896                 if (candidate.startsWith(filter)) {
897                     // found a matching filter
898
addFilter(candidate);
899                     break;
900                 }
901             }
902         }
903     }
904
905     /**
906      * Sets the height.<p>
907      *
908      * @param height the height to set
909      */

910     public void setHeight(int height) {
911
912         m_height = height;
913     }
914
915     /**
916      * Sets the maximum image size (width * height) to apply image blurring when downscaling images.<p>
917      *
918      * @param maxBlurSize the maximum image blur size to set
919      *
920      * @see #getMaxBlurSize() for a more detailed description about this parameter
921      */

922     public void setMaxBlurSize(int maxBlurSize) {
923
924         m_maxBlurSize = maxBlurSize;
925     }
926
927     /**
928      * Sets the scale position.<p>
929      *
930      * @param position the position to set
931      */

932     public void setPosition(int position) {
933
934         switch (position) {
935             case Simapi.POS_DOWN_LEFT:
936             case Simapi.POS_DOWN_RIGHT:
937             case Simapi.POS_STRAIGHT_DOWN:
938             case Simapi.POS_STRAIGHT_LEFT:
939             case Simapi.POS_STRAIGHT_RIGHT:
940             case Simapi.POS_STRAIGHT_UP:
941             case Simapi.POS_UP_LEFT:
942             case Simapi.POS_UP_RIGHT:
943                 // pos is fine
944
m_position = position;
945                 break;
946             default:
947                 m_position = Simapi.POS_CENTER;
948         }
949     }
950
951     /**
952      * Sets the image saving quality in percent.<p>
953      *
954      * @param quality the image saving quality (in percent) to set
955      */

956     public void setQuality(int quality) {
957
958         if (quality < 0) {
959             m_quality = 0;
960         } else if (quality > 100) {
961             m_quality = 100;
962         } else {
963             m_quality = quality;
964         }
965     }
966
967     /**
968      * Sets the image rendering mode constant.<p>
969      *
970      * @param renderMode the image rendering mode to set
971      *
972      * @see #getRenderMode() for a list of allowed values for the rendering mode
973      */

974     public void setRenderMode(int renderMode) {
975
976         if ((renderMode < Simapi.RENDER_QUALITY) || (renderMode > Simapi.RENDER_SPEED)) {
977             renderMode = Simapi.RENDER_QUALITY;
978         }
979         m_renderMode = renderMode;
980     }
981
982     /**
983      * Sets the scale type.<p>
984      *
985      * @param type the scale type to set
986      *
987      * @see #getType() for a detailed description of the possible values for the type
988      */

989     public void setType(int type) {
990
991         if ((type < 0) || (type > 4)) {
992             // invalid type, use 0
993
m_type = 0;
994         } else {
995             m_type = type;
996         }
997     }
998
999     /**
1000     * Sets the width.<p>
1001     *
1002     * @param width the width to set
1003     */

1004    public void setWidth(int width) {
1005
1006        m_width = width;
1007    }
1008
1009    /**
1010     * Creates a request parameter configured with the values from this image scaler, also
1011     * appends a <code>'?'</code> char as a prefix so that this may be direclty appended to an image URL.<p>
1012     *
1013     * This can be appended to an image request in order to apply image scaling parameters.<p>
1014     *
1015     * @return a request parameter configured with the values from this image scaler
1016     */

1017    public String JavaDoc toRequestParam() {
1018
1019        StringBuffer JavaDoc result = new StringBuffer JavaDoc(128);
1020        result.append('?');
1021        result.append(PARAM_SCALE);
1022        result.append('=');
1023        result.append(toString());
1024
1025        return result.toString();
1026    }
1027
1028    /**
1029     * @see java.lang.Object#toString()
1030     */

1031    public String JavaDoc toString() {
1032
1033        if (m_scaleParameters != null) {
1034            return m_scaleParameters;
1035        }
1036
1037        StringBuffer JavaDoc result = new StringBuffer JavaDoc(64);
1038        result.append(CmsImageScaler.SCALE_PARAM_WIDTH);
1039        result.append(':');
1040        result.append(m_width);
1041        result.append(',');
1042        result.append(CmsImageScaler.SCALE_PARAM_HEIGHT);
1043        result.append(':');
1044        result.append(m_height);
1045        if (m_type > 0) {
1046            result.append(',');
1047            result.append(CmsImageScaler.SCALE_PARAM_TYPE);
1048            result.append(':');
1049            result.append(m_type);
1050        }
1051        if (m_position > 0) {
1052            result.append(',');
1053            result.append(CmsImageScaler.SCALE_PARAM_POS);
1054            result.append(':');
1055            result.append(m_position);
1056        }
1057        if (m_color != Color.WHITE) {
1058            result.append(',');
1059            result.append(CmsImageScaler.SCALE_PARAM_COLOR);
1060            result.append(':');
1061            result.append(getColorString());
1062        }
1063        if (m_quality > 0) {
1064            result.append(',');
1065            result.append(CmsImageScaler.SCALE_PARAM_QUALITY);
1066            result.append(':');
1067            result.append(m_quality);
1068        }
1069        if (m_renderMode > 0) {
1070            result.append(',');
1071            result.append(CmsImageScaler.SCALE_PARAM_RENDERMODE);
1072            result.append(':');
1073            result.append(m_renderMode);
1074        }
1075        if (!m_filters.isEmpty()) {
1076            result.append(',');
1077            result.append(CmsImageScaler.SCALE_PARAM_FILTER);
1078            result.append(':');
1079            result.append(getFiltersString());
1080        }
1081        m_scaleParameters = result.toString();
1082        return m_scaleParameters;
1083    }
1084
1085    /**
1086     * Initializes the members with the default values.<p>
1087     */

1088    private void init() {
1089
1090        m_height = -1;
1091        m_width = -1;
1092        m_type = 0;
1093        m_position = 0;
1094        m_renderMode = 0;
1095        m_quality = 0;
1096        m_color = Color.WHITE;
1097        m_filters = new ArrayList JavaDoc();
1098        m_maxBlurSize = CmsImageLoader.getMaxBlurSize();
1099    }
1100
1101    /**
1102     * Copys all values from the given scaler into this scaler.<p>
1103     *
1104     * @param source the source scaler
1105     */

1106    private void initValuesFrom(CmsImageScaler source) {
1107
1108        m_width = source.m_width;
1109        m_height = source.m_height;
1110        m_type = source.m_type;
1111        m_position = source.m_position;
1112        m_renderMode = source.m_renderMode;
1113        m_quality = source.m_quality;
1114        m_color = source.m_color;
1115        m_filters = new ArrayList JavaDoc(source.m_filters);
1116        m_maxBlurSize = source.m_maxBlurSize;
1117    }
1118
1119    /**
1120     * Parses the scaler parameters.<p>
1121     *
1122     * @param parameters the parameters to parse
1123     */

1124    private void parseParameters(String JavaDoc parameters) {
1125
1126        m_width = -1;
1127        m_height = -1;
1128        m_position = 0;
1129        m_type = 0;
1130        m_color = Color.WHITE;
1131
1132        List JavaDoc tokens = CmsStringUtil.splitAsList(parameters, ',');
1133        Iterator JavaDoc it = tokens.iterator();
1134        String JavaDoc k;
1135        String JavaDoc v;
1136        while (it.hasNext()) {
1137            String JavaDoc t = (String JavaDoc)it.next();
1138            // extract key and value
1139
k = null;
1140            v = null;
1141            int idx = t.indexOf(':');
1142            if (idx >= 0) {
1143                k = t.substring(0, idx).trim();
1144                if (t.length() > idx) {
1145                    v = t.substring(idx + 1).trim();
1146                }
1147            }
1148            if (CmsStringUtil.isNotEmpty(k) && CmsStringUtil.isNotEmpty(v)) {
1149                // key and value are available
1150
if (SCALE_PARAM_HEIGHT.equals(k)) {
1151                    // image height
1152
m_height = CmsStringUtil.getIntValue(v, Integer.MIN_VALUE, k);
1153                } else if (SCALE_PARAM_WIDTH.equals(k)) {
1154                    // image width
1155
m_width = CmsStringUtil.getIntValue(v, Integer.MIN_VALUE, k);
1156                } else if (SCALE_PARAM_TYPE.equals(k)) {
1157                    // scaling type
1158
setType(CmsStringUtil.getIntValue(v, -1, CmsImageScaler.SCALE_PARAM_TYPE));
1159                } else if (SCALE_PARAM_COLOR.equals(k)) {
1160                    // image background color
1161
setColor(v);
1162                } else if (SCALE_PARAM_POS.equals(k)) {
1163                    // image position (depends on scale type)
1164
setPosition(CmsStringUtil.getIntValue(v, -1, CmsImageScaler.SCALE_PARAM_POS));
1165                } else if (SCALE_PARAM_QUALITY.equals(k)) {
1166                    // image position (depends on scale type)
1167
setQuality(CmsStringUtil.getIntValue(v, 0, k));
1168                } else if (SCALE_PARAM_RENDERMODE.equals(k)) {
1169                    // image position (depends on scale type)
1170
setRenderMode(CmsStringUtil.getIntValue(v, 0, k));
1171                } else if (SCALE_PARAM_FILTER.equals(k)) {
1172                    // image filters to apply
1173
setFilters(v);
1174                } else {
1175                    if (LOG.isDebugEnabled()) {
1176                        LOG.debug(Messages.get().getBundle().key(Messages.ERR_INVALID_IMAGE_SCALE_PARAMS_2, k, v));
1177                    }
1178                }
1179            } else {
1180                if (LOG.isDebugEnabled()) {
1181                    LOG.debug(Messages.get().getBundle().key(Messages.ERR_INVALID_IMAGE_SCALE_PARAMS_2, k, v));
1182                }
1183            }
1184        }
1185    }
1186}
Popular Tags