1 34 package net.myvietnam.mvncore.util; 35 36 import java.io.*; 37 import java.awt.image.BufferedImage ; 38 import java.awt.geom.AffineTransform ; 39 import java.awt.*; 40 import java.awt.image.*; 41 import javax.swing.ImageIcon ; 42 import com.sun.image.codec.jpeg.*; 43 import org.apache.commons.logging.Log; 44 import org.apache.commons.logging.LogFactory; 45 import net.myvietnam.mvncore.thirdparty.JpegEncoder; 46 import net.myvietnam.mvncore.exception.*; 47 import net.myvietnam.mvncore.util.FileUtil; 48 49 public final class ImageUtil { 50 51 private static Log log = LogFactory.getLog(ImageUtil.class); 52 53 private ImageUtil() { } 55 56 71 public static void createThumbnail(InputStream inputStream, String thumbnailFile, int maxWidth, int maxHeight) 72 throws IOException, AssertionException { 73 74 if (thumbnailFile.toLowerCase().endsWith(".jpg") == false) { 76 throw new IllegalArgumentException ("Cannot create a thumbnail with the extension other than '.jpg'."); 77 } 78 if (maxWidth <= 0) { 79 throw new IllegalArgumentException ("maxWidth must >= 0"); 80 } 81 if (maxHeight <= 0) { 82 throw new IllegalArgumentException ("maxHeight must >= 0"); 83 } 84 85 OutputStream outputStream = null; 86 try { 87 byte[] srcByte = FileUtil.getBytes(inputStream); 90 ImageIcon imageIcon = new ImageIcon (srcByte); 91 Image srcImage = imageIcon.getImage(); 92 93 int imgWidth = srcImage.getWidth(null); 94 int imgHeight = srcImage.getHeight(null); 95 if ( (imgWidth <= 0) || (imgHeight <= 0) ) { 97 throw new AssertionException("Assertion: ImageUtil: cannot get the image size."); 99 } 100 101 AffineTransform tx = new AffineTransform (); 103 if ((imgWidth > maxWidth) || (imgHeight > maxHeight)) { 104 double scaleX = (double)maxWidth/imgWidth; 105 double scaleY = (double)maxHeight/imgHeight; 106 double scaleRatio = (scaleX < scaleY) ? scaleX : scaleY; 107 imgWidth = (int)(imgWidth * scaleRatio); 108 imgHeight = (int)(imgHeight * scaleRatio); 109 tx.scale(scaleRatio, scaleRatio); 111 } else { outputStream = new FileOutputStream(thumbnailFile); 113 outputStream.write(srcByte); 114 return; 115 } 116 117 BufferedImage bufferedImage = new BufferedImage (imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB); 119 120 Graphics2D g = bufferedImage.createGraphics(); 121 boolean useTransform = false; 122 if (useTransform) { g.drawImage(srcImage, tx, null); 125 } else { Image scaleImage = getScaledInstance(srcImage, imgWidth, imgHeight); 128 g.drawImage(scaleImage, 0, 0, null); 129 } 130 g.dispose(); 132 outputStream = new FileOutputStream(thumbnailFile); 134 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream); 135 encoder.encode(bufferedImage); 136 } catch (IOException e) { 137 log.error("Error", e); 138 throw e; 139 } finally { try { 141 inputStream.close(); 142 } catch (IOException e) {} 143 if (outputStream != null) { 144 try { 145 outputStream.close(); 146 } catch (IOException e) {} 147 } 148 } 149 } 150 151 155 public static Image getFitSizeImage(Image srcImage, int fitWidth, int fitHeight) 156 throws IOException { 157 if ((fitWidth < 100) || (fitHeight < 100)) throw new IllegalArgumentException ("Cannot accept values < 100"); 158 159 int srcWidth = srcImage.getWidth(null); int srcHeight = srcImage.getHeight(null); 161 163 if ((srcWidth == fitWidth) && (srcHeight == fitHeight)) return srcImage; 165 166 int newWidth = srcWidth; 167 int newHeight = srcHeight; 168 169 double fitRatio = (double)fitWidth / fitHeight; 170 double srcRatio = (double)srcWidth / srcHeight; 171 if (srcRatio > fitRatio) { newWidth = (int)(srcHeight * fitRatio); 173 } else { newHeight = (int)(srcWidth / fitRatio); 175 } 176 178 ImageFilter cropFilter = new CropImageFilter((srcWidth-newWidth)/2, (srcHeight-newHeight)/2, newWidth, newHeight); 179 ImageProducer cropProd = new FilteredImageSource(srcImage.getSource(), cropFilter); 180 Image cropImage = Toolkit.getDefaultToolkit().createImage(cropProd); 181 182 Image retImage = new ImageIcon (getScaledInstance(cropImage, fitWidth, fitHeight)).getImage(); 183 184 return retImage; 185 } 186 187 191 public static Image getFitSizeImage(InputStream inputStream, int fitWidth, int fitHeight) 192 throws IOException { 193 try { 194 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream); 195 BufferedImage srcImage = decoder.decodeAsBufferedImage(); 196 197 return getFitSizeImage(srcImage, fitWidth, fitHeight); 198 } catch (IOException e) { 199 log.error("Cannot run getFitSizeImage", e); 200 throw e; 201 } finally { inputStream.close(); 203 } 204 } 205 206 215 public static void writeJpegImage_Sun(Image image, OutputStream outputStream) throws IOException { 216 217 if (outputStream == null) { 218 return; 219 } 220 221 try { 222 BufferedImage bufferedImage = null; 223 if (image instanceof BufferedImage ) { 224 bufferedImage = (BufferedImage )image; 225 } else { 226 bufferedImage = new BufferedImage (image.getWidth(null), image.getHeight(null), 228 BufferedImage.TYPE_INT_RGB); 229 230 Graphics2D g = bufferedImage.createGraphics(); 232 233 g.drawImage(image, 0, 0, null); 235 g.dispose(); } 237 238 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream); 241 encoder.encode(bufferedImage); 242 } finally { 243 try { 244 if (outputStream != null) { 245 outputStream.close(); 246 } 247 } catch (IOException ex1) { 248 } 249 } 250 } 251 252 public static void writeJpegImage_Sun(Image image, String fileName) throws IOException { 253 OutputStream outputStream = new FileOutputStream(fileName); 254 writeJpegImage_Sun(image, outputStream); 255 } 256 257 264 public static void writeJpegImage_nonSun(Image image, OutputStream outputStream) throws IOException { 265 if (outputStream == null) { 266 return; 267 } 268 try { 269 JpegEncoder encoder = new JpegEncoder(image, 80, outputStream); 270 encoder.Compress(); 271 } catch (Exception ex) { 272 } finally { 273 try { 274 if (outputStream != null) { 275 outputStream.close(); 276 } 277 } catch (IOException ex1) { 278 } 279 } 280 } 281 282 public static void writeJpegImage_nonSun(Image image, String fileName) throws IOException { 283 OutputStream outputStream = new FileOutputStream(fileName); 284 285 writeJpegImage_nonSun(image, outputStream); 287 } 288 289 public static Image getScaledInstance(Image srcImage, int width, int height) { 290 boolean useSun = false; 291 ImageFilter filter; 292 if (useSun){ 293 filter = new java.awt.image.AreaAveragingScaleFilter (width, height); 295 } else { 296 filter = new net.myvietnam.mvncore.util.AreaAveragingScaleFilter(width, height); 298 } 299 ImageProducer prod = new FilteredImageSource(srcImage.getSource(), filter); 300 Image newImage = Toolkit.getDefaultToolkit().createImage(prod); 301 ImageIcon imageIcon = new ImageIcon (newImage); 302 return imageIcon.getImage(); 303 } 304 315 } 316 317 318 319 320 321 325 class AreaAveragingScaleFilter extends ImageFilter { 328 private ColorModel rgbModel; 329 private long [] alphas; 330 private long [] reds; 331 private long [] greens; 332 private long [] blues; 333 334 protected int srcWidth; 335 protected int srcHeight; 336 private int [] srcPixels; 337 protected int destWidth; 338 protected int destHeight; 339 protected int [] destPixels; 340 341 { 342 boolean classColorModelAccessible = isClassAccessible ("java.awt.image.ColorModel"); 346 if (classColorModelAccessible) 347 rgbModel = ColorModel.getRGBdefault (); 348 } 349 350 356 public AreaAveragingScaleFilter(int width, int height) { 357 destWidth = width; 358 destHeight = height; 359 } 360 361 public void setDimensions (int w, int h) 362 { 363 srcWidth = w; 364 srcHeight = h; 365 if (destWidth < 0) 366 { 367 if (destHeight < 0) 368 { 369 destWidth = srcWidth; 370 destHeight = srcHeight; 371 } 372 else 373 destWidth = srcWidth * destHeight / srcHeight; 374 } 375 else if (destHeight < 0) 376 destHeight = srcHeight * destWidth / srcWidth; 377 378 consumer.setDimensions (destWidth, destHeight); 379 } 380 381 public void setHints (int hints) 382 { 383 consumer.setHints ( (hints & (SINGLEPASS | SINGLEFRAME)) 385 | TOPDOWNLEFTRIGHT); 386 } 387 388 public void imageComplete (int status) 389 { 390 if ( status == STATICIMAGEDONE 391 || status == SINGLEFRAMEDONE) 392 accumPixels (0, 0, srcWidth, srcHeight, rgbModel, srcPixels, 0, srcWidth); 393 consumer.imageComplete (status); 394 } 395 396 public void setPixels (int x, int y, int width, int height, 397 ColorModel model, byte pixels [], int offset, int scansize) 398 { 399 if (srcPixels == null) 401 srcPixels = new int [srcWidth * srcHeight]; 402 for (int row = 0, destRow = y * srcWidth; 403 row < height; 404 row++, destRow += srcWidth) 405 { 406 int rowOff = offset + row * scansize; 407 for (int col = 0; col < width; col++) 408 srcPixels [destRow + x + col] = model.getRGB (pixels [rowOff + col] & 0xFF); 410 } 411 } 412 413 public void setPixels (int x, int y, int width, int height, 414 ColorModel model, int pixels[], int offset, int scansize) 415 { 416 if (srcPixels == null) 418 srcPixels = new int [srcWidth * srcHeight]; 419 for (int row = 0, destRow = y * srcWidth; 420 row < height; 421 row++, destRow += srcWidth) 422 { 423 int rowOff = offset + row * scansize; 424 for (int col = 0; col < width; col++) 425 srcPixels [destRow + x + col] = model == null 427 ? pixels [rowOff + col] 428 : model.getRGB (pixels [rowOff + col]); 429 } 430 } 431 432 private int [] calcRow () 433 { 434 long mult = (srcWidth * srcHeight) << 32; 435 if (destPixels == null) 436 destPixels = new int [destWidth]; 437 438 for (int x = 0; x < destWidth; x++) 439 { 440 int a = (int)roundDiv (alphas [x], mult); 441 int r = (int)roundDiv (reds [x], mult); 442 int g = (int)roundDiv (greens [x], mult); 443 int b = (int)roundDiv (blues [x], mult); 444 a = Math.max (Math.min (a, 255), 0); 445 r = Math.max (Math.min (r, 255), 0); 446 g = Math.max (Math.min (g, 255), 0); 447 b = Math.max (Math.min (b, 255), 0); 448 destPixels [x] = (a << 24 | r << 16 | g << 8 | b); 449 } 450 451 return destPixels; 452 } 453 454 private void accumPixels (int x, int y, int w, int h, 455 ColorModel model, int [] pixels, int off, 456 int scansize) 457 { 458 reds = new long [destWidth]; 459 greens = new long [destWidth]; 460 blues = new long [destWidth]; 461 alphas = new long [destWidth]; 462 463 int sy = y; 464 int syrem = destHeight; 465 int dy = 0; 466 int dyrem = 0; 467 while (sy < y + h) 468 { 469 if (dyrem == 0) 470 { 471 for (int i = 0; i < destWidth; i++) 472 alphas [i] = 473 reds [i] = 474 greens [i] = 475 blues [i] = 0; 476 477 dyrem = srcHeight; 478 } 479 480 int amty = Math.min (syrem, dyrem); 481 int sx = 0; 482 int dx = 0; 483 int sxrem = 0; 484 int dxrem = srcWidth; 485 int a = 0, 486 r = 0, 487 g = 0, 488 b = 0; 489 while (sx < w) 490 { 491 if (sxrem == 0) 492 { 493 sxrem = destWidth; 494 int rgb = pixels [off + sx]; 495 a = rgb >>> 24; 496 r = (rgb >> 16) & 0xFF; 497 g = (rgb >> 8) & 0xFF; 498 b = rgb & 0xFF; 499 } 500 501 int amtx = Math.min (sxrem, dxrem); 502 long mult = (amtx * amty) << 32; 503 alphas [dx] += mult * a; 504 reds [dx] += mult * r; 505 greens [dx] += mult * g; 506 blues [dx] += mult * b; 507 508 if ((sxrem -= amtx) == 0) 509 sx++; 510 511 if ((dxrem -= amtx) == 0) 512 { 513 dx++; 514 dxrem = srcWidth; 515 } 516 } 517 518 if ((dyrem -= amty) == 0) 519 { 520 int outpix [] = calcRow (); 521 do 522 { 523 consumer.setPixels (0, dy, destWidth, 1, 524 rgbModel, outpix, 0, destWidth); 525 dy++; 526 } 527 while ((syrem -= amty) >= amty && amty == srcHeight); 528 } 529 else 530 syrem -= amty; 531 532 if (syrem == 0) 533 { 534 syrem = destHeight; 535 sy++; 536 off += scansize; 537 } 538 } 539 } 540 551 public static int roundDiv(int dividend, int divisor) { 552 final int remainder = dividend % divisor; 553 if (Math.abs(remainder) * 2 <= Math.abs(divisor)) 554 return dividend / divisor; 555 else 556 if (dividend * divisor < 0) 557 return dividend / divisor - 1; 558 else 559 return dividend / divisor + 1; 560 } 561 562 569 public static long roundDiv (long dividend, long divisor) { 570 final long remainder = dividend % divisor; 571 if (Math.abs (remainder) * 2 <= Math.abs (divisor)) 572 return dividend / divisor; 573 else 574 if (dividend * divisor < 0) 575 return dividend / divisor - 1; 576 else 577 return dividend / divisor + 1; 578 } 579 580 590 public static boolean isClassAccessible(String className) { 591 try { 593 Class.forName(className); 594 return true; 596 } catch (ClassNotFoundException e) {} catch (LinkageError error) {} 598 return false; 599 } 600 } 601 | Popular Tags |