Coverage Report - wjhk.jupload2.filedata.helper.ImageHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
ImageHelper
61 %
132/215
55 %
50/90
10,167
 
 1  
 //
 2  
 // $Id$
 3  
 //
 4  
 // jupload - A file upload applet.
 5  
 //
 6  
 // Copyright 2008 The JUpload Team
 7  
 //
 8  
 // Created: 12 fevr. 08
 9  
 // Creator: etienne_sf
 10  
 // Last modified: $Date$
 11  
 //
 12  
 // This program is free software; you can redistribute it and/or modify
 13  
 // it under the terms of the GNU General Public License as published by
 14  
 // the Free Software Foundation; either version 2 of the License, or
 15  
 // (at your option) any later version.
 16  
 //
 17  
 // This program is distributed in the hope that it will be useful,
 18  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 19  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 20  
 // GNU General Public License for more details.
 21  
 //
 22  
 // You should have received a copy of the GNU General Public License
 23  
 // along with this program; if not, write to the Free Software
 24  
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 25  
 
 26  
 package wjhk.jupload2.filedata.helper;
 27  
 
 28  
 import java.awt.Image;
 29  
 import java.awt.geom.AffineTransform;
 30  
 import java.awt.image.AffineTransformOp;
 31  
 import java.awt.image.BufferedImage;
 32  
 import java.awt.image.ImageObserver;
 33  
 
 34  
 import wjhk.jupload2.exception.JUploadException;
 35  
 import wjhk.jupload2.exception.JUploadIOException;
 36  
 import wjhk.jupload2.filedata.DefaultFileData;
 37  
 import wjhk.jupload2.filedata.PictureFileData;
 38  
 import wjhk.jupload2.policies.PictureUploadPolicy;
 39  
 
 40  
 /**
 41  
  * Class that contains various utilities about picture, mainly about picture transformation.
 42  
  * 
 43  
  * @author etienne_sf
 44  
  */
 45  
 public class ImageHelper implements ImageObserver {
 46  
 
 47  
     /**
 48  
      * hasToTransformPicture indicates whether the picture should be transformed. Null if unknown. This can happen (for
 49  
      * instance) if no calcul where done (during initialization), or after rotating the picture back to the original
 50  
      * orientation. <BR>
 51  
      * <B>Note:</B> this attribute is from the class Boolean (and not a simple boolean), to allow null value, meaning
 52  
      * <I>unknown</I>.
 53  
      */
 54  10
     private Boolean hasToTransformPicture = null;
 55  
 
 56  
     /**
 57  
      * The {@link PictureFileData} that this helper will have to help.
 58  
      */
 59  
     private PictureFileData pictureFileData;
 60  
 
 61  
     /**
 62  
      * Current rotation of the picture: 0 to 3.
 63  
      * 
 64  
      * @see PictureFileData
 65  
      */
 66  
     private int quarterRotation;
 67  
 
 68  
     /**
 69  
      * Maximum width for the current transformation
 70  
      */
 71  
     private int maxWidth;
 72  
 
 73  
     /**
 74  
      * Maximum height for the current transformation
 75  
      */
 76  
     private int maxHeight;
 77  
 
 78  
     /**
 79  
      * Defines the number of pixel for the current picture. Used to update the progress bar.
 80  
      * 
 81  
      * @see #getBufferedImage(boolean, BufferedImage)
 82  
      * @see #imageUpdate(Image, int, int, int, int, int)
 83  
      */
 84  10
     private int nbPixelsTotal = -1;
 85  
 
 86  
     /**
 87  
      * Indicates the number of pixels that have been read.
 88  
      * 
 89  
      * @see #nbPixelsTotal
 90  
      * @see #imageUpdate(Image, int, int, int, int, int)
 91  
      */
 92  10
     private int nbPixelsRead = 0;
 93  
 
 94  
     /**
 95  
      * Width of picture, after rescaling but without rotation. It should be scale*originalWidth, but, due to rounding
 96  
      * number, it can be transformed to scale*originalWidth-1.
 97  
      * 
 98  
      * @see #initScale()
 99  
      */
 100  10
     private int scaledNonRotatedWidth = -1;
 101  
 
 102  
     /**
 103  
      * Same as {@link #scaledNonRotatedWidth}
 104  
      */
 105  10
     private int scaledNonRotatedHeight = -1;
 106  
 
 107  
     /**
 108  
      * The value that has the progress bar when starting to load the picture. The
 109  
      * {@link #imageUpdate(Image, int, int, int, int, int)} method will add from 0 to 100, to indicate progress with a
 110  
      * percentage value of picture loading.
 111  
      */
 112  10
     private int progressBarBaseValue = 0;
 113  
 
 114  
     /**
 115  
      * Current scaling factor. If less than 1, means a picture reduction.
 116  
      * 
 117  
      * @see #initScale()
 118  
      */
 119  10
     private double scale = 1;
 120  
 
 121  
     /**
 122  
      * Width of picture, after re-scaling and rotation. It should be scale*originalWidth or scale*originalHeight
 123  
      * (depending on the rotation). But, due to rounding number, it can be transformed to scale*originalWidth-1 or
 124  
      * scale*originalHeight-1.
 125  
      * 
 126  
      * @see #initScale()
 127  
      */
 128  10
     private int scaledRotatedWidth = -1;
 129  
 
 130  
     /**
 131  
      * Same as {@link #scaledRotatedWidth}, for the height.
 132  
      */
 133  10
     private int scaledRotatedHeight = -1;
 134  
 
 135  
     /**
 136  
      * The current upload policy must be a {@link PictureUploadPolicy}
 137  
      */
 138  
     PictureUploadPolicy uploadPolicy;
 139  
 
 140  
     /**
 141  
      * Standard constructor.
 142  
      * 
 143  
      * @param uploadPolicy The current upload policy
 144  
      * @param pictureFileData The picture file data to help
 145  
      * @param targetMaxWidth
 146  
      * @param targetMaxHeight
 147  
      * @param quarterRotation Current quarter rotation (from 0 to 3)
 148  
      * @throws JUploadIOException
 149  
      */
 150  
     public ImageHelper(PictureUploadPolicy uploadPolicy, PictureFileData pictureFileData, int targetMaxWidth,
 151  10
             int targetMaxHeight, int quarterRotation) throws JUploadIOException {
 152  10
         this.uploadPolicy = uploadPolicy;
 153  10
         this.pictureFileData = pictureFileData;
 154  10
         this.maxWidth = targetMaxWidth;
 155  10
         this.maxHeight = targetMaxHeight;
 156  10
         this.quarterRotation = quarterRotation;
 157  
 
 158  
         // Pre-calculation: should the current picture be rescaled, to match the
 159  
         // given target size ?
 160  10
         initScale();
 161  10
     }
 162  
 
 163  
     /**
 164  
      * Intialization of scale factor, for the current picture state. The scale is based on the maximum width and height,
 165  
      * the current rotation, and the picture size.
 166  
      */
 167  
     private void initScale() throws JUploadIOException {
 168  10
         double theta = Math.toRadians(90 * this.quarterRotation);
 169  
 
 170  
         // The width and height depend on the current rotation :
 171  
         // calculation of the width and height of picture after
 172  
         // rotation.
 173  10
         int nonScaledRotatedWidth = this.pictureFileData.getOriginalWidth();
 174  10
         int nonScaledRotatedHeight = this.pictureFileData.getOriginalHeight();
 175  10
         if (this.quarterRotation % 2 != 0) {
 176  
             // 90 degrees or 270 degrees rotation: width and height are
 177  
             // switched.
 178  3
             nonScaledRotatedWidth = this.pictureFileData.getOriginalHeight();
 179  3
             nonScaledRotatedHeight = this.pictureFileData.getOriginalWidth();
 180  
         }
 181  
         // Now, we can compare these width and height to the maximum
 182  
         // width and height
 183  10
         double scaleWidth = ((this.maxWidth < 0) ? 1 : ((double) this.maxWidth) / nonScaledRotatedWidth);
 184  10
         double scaleHeight = ((this.maxHeight < 0) ? 1 : ((double) this.maxHeight) / nonScaledRotatedHeight);
 185  10
         this.scale = Math.min(scaleWidth, scaleHeight);
 186  10
         if (this.scale < 1) {
 187  
             // With number rounding, it can happen that width or size
 188  
             // became one pixel too big. Let's correct it.
 189  4
             if ((this.maxWidth > 0 && this.maxWidth < (int) (this.scale * Math.cos(theta) * nonScaledRotatedWidth))
 190  4
                     || (this.maxHeight > 0 && this.maxHeight < (int) (this.scale * Math.cos(theta) * nonScaledRotatedHeight))) {
 191  0
                 scaleWidth = ((this.maxWidth < 0) ? 1 : ((double) this.maxWidth - 1) / (nonScaledRotatedWidth));
 192  0
                 scaleHeight = ((this.maxHeight < 0) ? 1 : ((double) this.maxHeight - 1) / (nonScaledRotatedHeight));
 193  0
                 this.scale = Math.min(scaleWidth, scaleHeight);
 194  
             }
 195  
         }
 196  
 
 197  
         // These variables contain the actual width and height after
 198  
         // rescaling, and before rotation.
 199  10
         this.scaledRotatedWidth = nonScaledRotatedWidth;
 200  10
         this.scaledRotatedHeight = nonScaledRotatedHeight;
 201  
         // Is there any rescaling to do ?
 202  
         // Patch for the first bug, tracked in the sourceforge bug
 203  
         // tracker ! ;-)
 204  10
         if (this.scale < 1) {
 205  4
             this.scaledRotatedWidth *= this.scale;
 206  4
             this.scaledRotatedHeight *= this.scale;
 207  4
             this.uploadPolicy.displayDebug("Resizing factor (scale): " + this.scale, 30);
 208  
         } else {
 209  6
             this.uploadPolicy.displayDebug("Resizing factor (scale): no resizing (calculated scale was " + this.scale
 210  
                     + ")", 30);
 211  
         }
 212  
         // Due to rounded numbers, the resulting targetWidth or
 213  
         // targetHeight
 214  
         // may be one pixel too big. Let's check that.
 215  10
         if (this.scaledRotatedWidth > this.maxWidth) {
 216  0
             this.uploadPolicy.displayDebug("Correcting rounded width: " + this.scaledRotatedWidth + " to "
 217  
                     + this.maxWidth, 50);
 218  0
             this.scaledRotatedWidth = this.maxWidth;
 219  
         }
 220  10
         if (this.scaledRotatedHeight > this.maxHeight) {
 221  0
             this.uploadPolicy.displayDebug("Correcting rounded height: " + this.scaledRotatedHeight + " to "
 222  
                     + this.maxHeight, 50);
 223  0
             this.scaledRotatedHeight = this.maxHeight;
 224  
         }
 225  
 
 226  
         // getBufferedImage will need the two following value:
 227  10
         if (this.quarterRotation % 2 == 0) {
 228  7
             this.scaledNonRotatedWidth = this.scaledRotatedWidth;
 229  7
             this.scaledNonRotatedHeight = this.scaledRotatedHeight;
 230  
         } else {
 231  3
             this.scaledNonRotatedWidth = this.scaledRotatedHeight;
 232  3
             this.scaledNonRotatedHeight = this.scaledRotatedWidth;
 233  
         }
 234  10
     }
 235  
 
 236  
     /**
 237  
      * This function indicate if the picture has to be modified. For instance : a maximum width, height, a target
 238  
      * format...
 239  
      * 
 240  
      * @return true if the picture must be transformed. false if the file can be directly transmitted.
 241  
      * @throws JUploadException Contains any exception that could be thrown in this method
 242  
      */
 243  
     public boolean hasToTransformPicture() throws JUploadException {
 244  
         // Animated gif must be transmit as is, as I can't find a way to
 245  
         // recreate them.
 246  8
         if (DefaultFileData.getExtension(this.pictureFileData.getFileName()).equalsIgnoreCase("gif")) {
 247  
             // If this is an animated gif, no transformation... I can't succeed
 248  
             // to create a transformed picture file for them.
 249  0
             ImageReaderWriterHelper irwh = new ImageReaderWriterHelper(this.uploadPolicy, this.pictureFileData);
 250  0
             int nbImages = irwh.getNumImages(true);
 251  0
             irwh.dispose();
 252  0
             irwh = null;
 253  0
             if (nbImages > 1) {
 254  
                 // Too bad. We can not transform it.
 255  0
                 this.hasToTransformPicture = Boolean.FALSE;
 256  0
                 this.uploadPolicy
 257  0
                         .displayWarn("No transformation for gif picture file, that contain several pictures. (see JUpload documentation for details)");
 258  
             }
 259  
         }
 260  
 
 261  
         // Did we already estimate if transformation is needed ?
 262  8
         if (this.hasToTransformPicture == null) {
 263  
 
 264  
             // First : the easiest test. Should we block metadata ?
 265  8
             if (this.hasToTransformPicture == null && !(this.uploadPolicy).getPictureTransmitMetadata()) {
 266  0
                 this.hasToTransformPicture = Boolean.TRUE;
 267  0
                 this.uploadPolicy.displayDebug(this.pictureFileData.getFileName()
 268  
                         + " : hasToTransformPicture=true (pictureTransmitMetadata is false)", 80);
 269  
             }
 270  
             // Second : another easy test. A rotation is needed ?
 271  8
             if (this.hasToTransformPicture == null && this.quarterRotation != 0) {
 272  3
                 this.uploadPolicy.displayDebug(this.pictureFileData.getFileName()
 273  
                         + " : hasToTransformPicture = true (quarterRotation != 0)", 10);
 274  3
                 this.hasToTransformPicture = Boolean.TRUE;
 275  
             }
 276  
 
 277  
             // Third : the picture format is the same ?
 278  16
             String targetFormat = this.uploadPolicy.getImageFileConversionInfo().getTargetFormatOrNull(
 279  8
                     this.pictureFileData.getFileExtension());
 280  8
             if (this.hasToTransformPicture == null && targetFormat != null) {
 281  0
                 this.uploadPolicy.displayDebug(this.pictureFileData.getFileName()
 282  
                         + " : hasToTransformPicture = true (targetPictureFormat)", 10);
 283  0
                 this.hasToTransformPicture = Boolean.TRUE;
 284  
             }
 285  
 
 286  
             // Fourth : should we resize the picture ?
 287  8
             if (this.hasToTransformPicture == null && this.scale < 1) {
 288  2
                 this.uploadPolicy.displayDebug(this.pictureFileData.getFileName()
 289  
                         + " : hasToTransformPicture = true (scale < 1)", 10);
 290  2
                 this.hasToTransformPicture = Boolean.TRUE;
 291  
             }
 292  
 
 293  
             // If we find no reason to transform the picture, then let's let the
 294  
             // picture unmodified.
 295  8
             if (this.hasToTransformPicture == null) {
 296  3
                 this.uploadPolicy.displayDebug(this.pictureFileData.getFileName() + " : hasToTransformPicture = false",
 297  
                         10);
 298  3
                 this.hasToTransformPicture = Boolean.FALSE;
 299  3
                 this.uploadPolicy.displayDebug(this.pictureFileData.getFileName() + " : hasToTransformPicture = false",
 300  
                         10);
 301  
             }
 302  
         }
 303  
 
 304  8
         return this.hasToTransformPicture.booleanValue();
 305  
     }// end of hasToTransformPicture
 306  
 
 307  
     /**
 308  
      * This function resizes the picture, if necessary, according to the maxWidth and maxHeight, given to the
 309  
      * ImageHelper constructor. <BR>
 310  
      * This function should only be called if isPicture is true. Otherwise, an exception is raised. <BR>
 311  
      * Note (Update given by David Gnedt): the highquality will condition the call of getScaledInstance, instead of a
 312  
      * basic scale Transformation. The generated picture is of better quality, but this is longer, especially on 'small'
 313  
      * CPU. Time samples, with one picture from my canon EOS20D, on a PII 500M: <BR>
 314  
      * ~3s for the full screen preview with highquality to false, and a quarter rotation. 12s to 20s with highquality to
 315  
      * true. <BR>
 316  
      * ~5s for the first (small) preview of the picture, with both highquality to false or true.
 317  
      * 
 318  
      * @param highquality (added by David Gnedt): if set to true, the BufferedImage.getScaledInstance() is called. This
 319  
      *            generates better image, but consumes more CPU.
 320  
      * @param sourceBufferedImage The image to resize or rotate or both or no tranformation...
 321  
      * @return A BufferedImage which contains the picture according to current parameters (resizing, rotation...), or
 322  
      *         null if this is not a picture.
 323  
      * @throws JUploadException Contains any exception thrown from within this method.
 324  
      */
 325  
     public BufferedImage getBufferedImage(boolean highquality, BufferedImage sourceBufferedImage)
 326  
             throws JUploadException {
 327  7
         long msGetBufferedImage = System.currentTimeMillis();
 328  7
         double theta = Math.toRadians(90 * this.quarterRotation);
 329  
 
 330  7
         BufferedImage returnedBufferedImage = null;
 331  
 
 332  7
         this.uploadPolicy.displayDebug("getBufferedImage: start", 10);
 333  
 
 334  
         try {
 335  7
             AffineTransform transform = new AffineTransform();
 336  
 
 337  7
             if (this.quarterRotation != 0) {
 338  3
                 double translationX = 0, translationY = 0;
 339  3
                 this.uploadPolicy.displayDebug("getBufferedImage: quarter: " + this.quarterRotation, 50);
 340  
 
 341  
                 // quarterRotation is one of 0, 1, 2, 3 : see addRotation.
 342  
                 // If we're here : it's not 0, so it's one of 1, 2 or 3.
 343  3
                 switch (this.quarterRotation) {
 344  
                     case 1:
 345  3
                         translationX = 0;
 346  3
                         translationY = -this.scaledRotatedWidth;
 347  3
                         break;
 348  
                     case 2:
 349  0
                         translationX = -this.scaledRotatedWidth;
 350  0
                         translationY = -this.scaledRotatedHeight;
 351  0
                         break;
 352  
                     case 3:
 353  0
                         translationX = -this.scaledRotatedHeight;
 354  0
                         translationY = 0;
 355  0
                         break;
 356  
                     default:
 357  0
                         this.uploadPolicy.displayWarn("Invalid quarterRotation : " + this.quarterRotation);
 358  0
                         this.quarterRotation = 0;
 359  0
                         theta = 0;
 360  
                 }
 361  3
                 transform.rotate(theta);
 362  3
                 transform.translate(translationX, translationY);
 363  
             }
 364  
 
 365  
             // If we have to rescale the picture, we first do it:
 366  7
             if (this.scale < 1) {
 367  4
                 if (highquality) {
 368  2
                     this.uploadPolicy
 369  2
                             .displayDebug("getBufferedImage: Resizing picture(using high quality picture)", 30);
 370  
 
 371  
                     // SCALE_AREA_AVERAGING forces the picture calculation
 372  
                     // algorithm.
 373  
                     // Other parameters give bad picture quality.
 374  2
                     Image img = sourceBufferedImage.getScaledInstance(this.scaledNonRotatedWidth,
 375  
                             this.scaledNonRotatedHeight, Image.SCALE_AREA_AVERAGING);
 376  
 
 377  
                     // the localBufferedImage may be 'unknown'.
 378  2
                     int localImageType = sourceBufferedImage.getType();
 379  2
                     if (localImageType == BufferedImage.TYPE_CUSTOM) {
 380  0
                         localImageType = BufferedImage.TYPE_INT_BGR;
 381  
                     }
 382  
 
 383  2
                     BufferedImage tempBufferedImage = new BufferedImage(this.scaledNonRotatedWidth,
 384  
                             this.scaledNonRotatedHeight, localImageType);
 385  
 
 386  
                     // drawImage can be long. Let's follow its progress,
 387  
                     // with the applet progress bar.
 388  2
                     this.nbPixelsTotal = this.scaledNonRotatedWidth * this.scaledNonRotatedHeight;
 389  2
                     this.nbPixelsRead = 0;
 390  
 
 391  
                     // Let's draw the picture: this code do the rescaling.
 392  2
                     this.uploadPolicy.displayDebug("getBufferedImage: Before drawImage", 50);
 393  2
                     tempBufferedImage.getGraphics().drawImage(img, 0, 0, this);
 394  2
                     this.uploadPolicy.displayDebug("getBufferedImage: After drawImage", 50);
 395  
 
 396  2
                     tempBufferedImage.flush();
 397  
 
 398  2
                     img.flush();
 399  2
                     img = null;
 400  2
                     PictureFileData.freeMemory("ImageHelper.getBufferedImage()", this.uploadPolicy);
 401  
 
 402  
                     // tempBufferedImage contains the rescaled picture. It's
 403  
                     // the source image for the next step (rotation).
 404  2
                     sourceBufferedImage = tempBufferedImage;
 405  2
                     tempBufferedImage = null;
 406  2
                 } else {
 407  
                     // 'low' quality
 408  
                     //
 409  
                     // The scale method adds scaling before current
 410  
                     // transformation.
 411  2
                     this.uploadPolicy.displayDebug(
 412  
                             "getBufferedImage: Resizing picture(using standard quality picture)", 50);
 413  2
                     transform.scale(this.scale, this.scale);
 414  
                 }
 415  
             }
 416  
 
 417  7
             if (transform.isIdentity()) {
 418  2
                 returnedBufferedImage = sourceBufferedImage;
 419  
             } else {
 420  5
                 AffineTransformOp affineTransformOp = null;
 421  
                 // Pictures are Ok.
 422  5
                 affineTransformOp = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
 423  5
                 returnedBufferedImage = affineTransformOp.createCompatibleDestImage(sourceBufferedImage, null);
 424  
                 // Checks, after the fact the pictures produces by the Canon
 425  
                 // EOS 30D are not properly resized: colors are 'strange'
 426  
                 // after resizing.
 427  10
                 this.uploadPolicy.displayDebug("getBufferedImage: returnedBufferedImage.getColorModel(): "
 428  5
                         + sourceBufferedImage.getColorModel().toString(), 50);
 429  10
                 this.uploadPolicy.displayDebug("getBufferedImage: returnedBufferedImage.getColorModel(): "
 430  5
                         + sourceBufferedImage.getColorModel().toString(), 50);
 431  5
                 affineTransformOp.filter(sourceBufferedImage, returnedBufferedImage);
 432  5
                 affineTransformOp = null;
 433  
 
 434  5
                 returnedBufferedImage.flush();
 435  
             }
 436  0
         } catch (Exception e) {
 437  0
             throw new JUploadException(e.getClass().getName() + " (" + this.getClass().getName()
 438  0
                     + ".getBufferedImage()) : " + e.getMessage());
 439  7
         }
 440  
 
 441  7
         if (returnedBufferedImage != null && this.uploadPolicy.getDebugLevel() >= 50) {
 442  7
             this.uploadPolicy.displayDebug("getBufferedImage: " + returnedBufferedImage, 50);
 443  7
             this.uploadPolicy.displayDebug("getBufferedImage: MinX=" + returnedBufferedImage.getMinX(), 50);
 444  7
             this.uploadPolicy.displayDebug("getBufferedImage: MinY=" + returnedBufferedImage.getMinY(), 50);
 445  
         }
 446  
 
 447  7
         this.uploadPolicy.displayDebug("getBufferedImage: was " + (System.currentTimeMillis() - msGetBufferedImage)
 448  
                 + " ms long", 50);
 449  7
         PictureFileData.freeMemory("ImageHelper.getBufferedImage()", this.uploadPolicy);
 450  7
         return returnedBufferedImage;
 451  
     }
 452  
 
 453  
     /**
 454  
      * This method is a work in progress
 455  
      * 
 456  
      * @param highquality
 457  
      * @param sourceBufferedImage
 458  
      * @return The calculated BufferedImage, resized and rotated according to the current configuration.
 459  
      * @throws JUploadException
 460  
      */
 461  
     BufferedImage getBufferedImage2(boolean highquality, BufferedImage sourceBufferedImage) throws JUploadException {
 462  0
         long msGetBufferedImage = System.currentTimeMillis();
 463  0
         BufferedImage dest = null;
 464  
 
 465  
         // Scale factor calculation
 466  0
         this.uploadPolicy.displayDebug("getBufferedImage: quarter: " + this.quarterRotation, 50);
 467  
 
 468  
         // quarterRotation is one of 0, 1, 2, 3 : see addRotation.
 469  
         @SuppressWarnings("unused")
 470  
         int maxWidthBeforeRotation, maxHeigthBeforeRotation, widthBeforeRotation, heigthBeforeRotation, widthAfterRotation, heigthAfterRotation;
 471  
         @SuppressWarnings("unused")
 472  0
         double theta = Math.toRadians(90 * this.quarterRotation);
 473  0
         switch (this.quarterRotation) {
 474  
             case 0:
 475  
             case 2:
 476  0
                 maxWidthBeforeRotation = this.uploadPolicy.getMaxWidth();
 477  0
                 maxHeigthBeforeRotation = this.uploadPolicy.getMaxHeight();
 478  0
                 widthBeforeRotation = sourceBufferedImage.getWidth();
 479  0
                 heigthBeforeRotation = sourceBufferedImage.getHeight();
 480  0
                 widthAfterRotation = sourceBufferedImage.getWidth();
 481  0
                 heigthAfterRotation = sourceBufferedImage.getHeight();
 482  0
                 break;
 483  
             case 1:
 484  
             case 3:
 485  0
                 maxWidthBeforeRotation = this.uploadPolicy.getMaxHeight();
 486  0
                 maxHeigthBeforeRotation = this.uploadPolicy.getMaxWidth();
 487  0
                 widthBeforeRotation = sourceBufferedImage.getHeight();
 488  0
                 heigthBeforeRotation = sourceBufferedImage.getWidth();
 489  0
                 widthAfterRotation = sourceBufferedImage.getHeight();
 490  0
                 heigthAfterRotation = sourceBufferedImage.getWidth();
 491  0
                 break;
 492  
             default:
 493  0
                 throw new JUploadException("Invalid quarter rotation: <" + this.quarterRotation + ">");
 494  
         }
 495  0
         double scaleWidthBeforeRotation = widthBeforeRotation / maxWidthBeforeRotation;
 496  0
         double scaleHeigthBeforeRotation = heigthBeforeRotation / maxHeigthBeforeRotation;
 497  0
         double scale = Math.min(scaleWidthBeforeRotation, scaleHeigthBeforeRotation);
 498  
 
 499  
         // First: we scale the picture... if necessary.
 500  
         @SuppressWarnings("unused")
 501  0
         Image scaledPicture = sourceBufferedImage;
 502  0
         if (scale < 1) {
 503  
             int targetWidthBeforeRotation, targetHeigthBeforeRotation;
 504  0
             if (scaleWidthBeforeRotation < scaleHeigthBeforeRotation) {
 505  
                 // The constraint is on the width.
 506  0
                 targetWidthBeforeRotation = maxWidthBeforeRotation;
 507  0
                 targetHeigthBeforeRotation = (int) (heigthBeforeRotation * scale);
 508  
             } else {
 509  
                 // The constraint is on the heigth
 510  0
                 targetHeigthBeforeRotation = maxHeigthBeforeRotation;
 511  0
                 targetWidthBeforeRotation = (int) (widthBeforeRotation * scale);
 512  
             }
 513  0
             int scale_xxx = highquality ? Image.SCALE_SMOOTH : Image.SCALE_FAST;
 514  0
             scaledPicture = sourceBufferedImage.getScaledInstance(targetWidthBeforeRotation,
 515  
                     targetHeigthBeforeRotation, scale_xxx);
 516  
         }// if (scale < 1)
 517  
 
 518  
         // Then, rotation of the scaled picture.
 519  0
         if (this.quarterRotation != 0) {
 520  
             @SuppressWarnings("unused")
 521  
             AffineTransform rotationTransform;
 522  0
             switch (this.quarterRotation) {
 523  
                 case 0:
 524  
                 case 2:
 525  0
                     maxWidthBeforeRotation = this.uploadPolicy.getMaxWidth();
 526  0
                     maxHeigthBeforeRotation = this.uploadPolicy.getMaxHeight();
 527  0
                     widthBeforeRotation = sourceBufferedImage.getWidth();
 528  0
                     heigthBeforeRotation = sourceBufferedImage.getHeight();
 529  0
                     widthAfterRotation = sourceBufferedImage.getWidth();
 530  0
                     heigthAfterRotation = sourceBufferedImage.getHeight();
 531  0
                     break;
 532  
                 case 1:
 533  
                 case 3:
 534  0
                     maxWidthBeforeRotation = this.uploadPolicy.getMaxHeight();
 535  0
                     maxHeigthBeforeRotation = this.uploadPolicy.getMaxWidth();
 536  0
                     widthBeforeRotation = sourceBufferedImage.getHeight();
 537  0
                     heigthBeforeRotation = sourceBufferedImage.getWidth();
 538  0
                     widthAfterRotation = sourceBufferedImage.getHeight();
 539  0
                     heigthAfterRotation = sourceBufferedImage.getWidth();
 540  0
                     break;
 541  
                 default:
 542  0
                     throw new JUploadException("Invalid quarter rotation: <" + this.quarterRotation + ">");
 543  
             }
 544  
             // TODO finish this new version
 545  
             // dest = new BufferedImage(widthAfterRotation, heigthAfterRotation,
 546  
             // BufferedImage.TYPE_BYTE_INDEXED,sourceBufferedImage.getColorModel())
 547  
 
 548  
             // It's finished !
 549  0
             this.uploadPolicy.displayDebug("getBufferedImage: was " + (System.currentTimeMillis() - msGetBufferedImage)
 550  
                     + " ms long", 50);
 551  
         }
 552  0
         return dest;
 553  
     }
 554  
 
 555  
     /**
 556  
      * Implementation of the ImageObserver interface. Used to follow the drawImage progression, and update the applet
 557  
      * progress bar.
 558  
      * 
 559  
      * @param img
 560  
      * @param infoflags
 561  
      * @param x
 562  
      * @param y
 563  
      * @param width
 564  
      * @param height
 565  
      * @return Whether or not the work must go on.
 566  
      */
 567  
     public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
 568  33
         if ((infoflags & ImageObserver.WIDTH) == ImageObserver.WIDTH) {
 569  2
             this.progressBarBaseValue = this.uploadPolicy.getContext().getUploadPanel().getPreparationProgressBar()
 570  2
                     .getValue();
 571  2
             this.uploadPolicy.displayDebug("  imageUpdate (start of), progressBar geValue: "
 572  
                     + this.progressBarBaseValue, 50);
 573  2
             int max = this.uploadPolicy.getContext().getUploadPanel().getPreparationProgressBar().getMaximum();
 574  2
             this.uploadPolicy.displayDebug("  imageUpdate (start of), progressBar maximum: " + max, 50);
 575  2
         } else if ((infoflags & ImageObserver.SOMEBITS) == ImageObserver.SOMEBITS) {
 576  23
             this.nbPixelsRead += width * height;
 577  23
             int percentage = (int) ((long) this.nbPixelsRead * 100 / this.nbPixelsTotal);
 578  23
             this.uploadPolicy.getContext().getUploadPanel().getPreparationProgressBar()
 579  23
                     .setValue(this.progressBarBaseValue + percentage);
 580  
             // TODO: drawImage in another thread, to allow repaint of the
 581  
             // progress bar ?
 582  
             // Current status: the progress bar is only updated ... when
 583  
             // draImage returns, that is: when everything is finished. NO
 584  
             // interest.
 585  23
             this.uploadPolicy.getContext().getUploadPanel().getPreparationProgressBar().repaint();
 586  23
         } else if ((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS) {
 587  2
             this.uploadPolicy.displayDebug("  imageUpdate, total number of pixels: " + this.nbPixelsRead + " read", 50);
 588  
         }
 589  
 
 590  
         // We want to go on, after these bits
 591  33
         return true;
 592  
     }
 593  
 }