comparison servlet/src/digilib/servlet/Scaler.java @ 100:cc6a0b9ac78e

digilib V1.9b1 - bugfixes with parameters - ongoing work with rotation (still doesn't work as I want) - ongoing work with absolute scale function
author robcast
date Mon, 05 May 2003 22:18:37 +0200
parents d9bfec4f046e
children 0e49645f98c8
comparison
equal deleted inserted replaced
99:226624784fe3 100:cc6a0b9ac78e
20 20
21 package digilib.servlet; 21 package digilib.servlet;
22 22
23 import java.awt.Dimension; 23 import java.awt.Dimension;
24 import java.awt.geom.AffineTransform; 24 import java.awt.geom.AffineTransform;
25 import java.awt.geom.Point2D; 25 import java.awt.geom.NoninvertibleTransformException;
26 import java.awt.geom.Rectangle2D; 26 import java.awt.geom.Rectangle2D;
27 import java.io.File; 27 import java.io.File;
28 import java.io.IOException; 28 import java.io.IOException;
29 import java.util.List; 29 import java.util.List;
30 30
54 */ 54 */
55 //public class Scaler extends HttpServlet implements SingleThreadModel { 55 //public class Scaler extends HttpServlet implements SingleThreadModel {
56 public class Scaler extends HttpServlet { 56 public class Scaler extends HttpServlet {
57 57
58 // digilib servlet version (for all components) 58 // digilib servlet version (for all components)
59 public static final String dlVersion = "1.8b4"; 59 public static final String dlVersion = "1.9b1";
60 60
61 // Utils instance with debuglevel 61 // Utils instance with debuglevel
62 Utils util; 62 Utils util;
63 // FileOps instance 63 // FileOps instance
64 FileOps fileOp; 64 FileOps fileOp;
72 // DigilibConfiguration instance 72 // DigilibConfiguration instance
73 DigilibConfiguration dlConfig; 73 DigilibConfiguration dlConfig;
74 74
75 // use authorization database 75 // use authorization database
76 boolean useAuthentication = true; 76 boolean useAuthentication = true;
77
78 // EXPRIMENTAL
79 // try to enlarge cropping area for "oblique" angles
80 boolean wholeRotArea = false;
77 81
78 /** Initialisation on first run. 82 /** Initialisation on first run.
79 * 83 *
80 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) 84 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
81 */ 85 */
160 * parameters for a session 164 * parameters for a session
161 */ 165 */
162 166
163 // scale the image file to fit window size i.e. respect dw,dh 167 // scale the image file to fit window size i.e. respect dw,dh
164 boolean scaleToFit = true; 168 boolean scaleToFit = true;
169 // scale the image by a fixed factor only
170 boolean absoluteScale = false;
165 // crop the image if needed 171 // crop the image if needed
166 boolean cropToFit = true; 172 boolean cropToFit = true;
167 // use heuristics (GIF?) to scale or send as is 173 // use heuristics (GIF?) to scale or send as is
168 boolean autoScale = true; 174 boolean autoScale = true;
169 // try prescaled images first 175 // try prescaled images first
212 * "clip": send original resolution cropped, "file": send whole file (if 218 * "clip": send original resolution cropped, "file": send whole file (if
213 * allowed) 219 * allowed)
214 */ 220 */
215 if (dlRequest.isOption("clip")) { 221 if (dlRequest.isOption("clip")) {
216 scaleToFit = false; 222 scaleToFit = false;
223 absoluteScale = false;
217 cropToFit = true; 224 cropToFit = true;
218 autoScale = false; 225 autoScale = false;
226 preScaledFirst = false;
219 } else if (dlRequest.isOption("fit")) { 227 } else if (dlRequest.isOption("fit")) {
220 scaleToFit = true; 228 scaleToFit = true;
229 absoluteScale = false;
221 cropToFit = true; 230 cropToFit = true;
222 autoScale = false; 231 autoScale = false;
232 } else if (dlRequest.isOption("scale")) {
233 scaleToFit = false;
234 absoluteScale = true;
235 cropToFit = true;
236 autoScale = false;
237 preScaledFirst = false;
223 } else if (dlRequest.isOption("file")) { 238 } else if (dlRequest.isOption("file")) {
224 scaleToFit = false; 239 scaleToFit = false;
240 absoluteScale = false;
225 if (dlConfig.isSendFileAllowed()) { 241 if (dlConfig.isSendFileAllowed()) {
226 cropToFit = false; 242 cropToFit = false;
227 } else { 243 } else {
228 cropToFit = true; 244 cropToFit = true;
229 } 245 }
230 autoScale = false; 246 autoScale = false;
247 preScaledFirst = false;
231 } 248 }
232 // operation mode: "errtxt": error message in html, "errimg": error image 249 // operation mode: "errtxt": error message in html, "errimg": error image
233 if (dlRequest.isOption("errtxt")) { 250 if (dlRequest.isOption("errtxt")) {
234 errorMsgHtml = true; 251 errorMsgHtml = true;
235 } else if (dlRequest.isOption("errimg")) { 252 } else if (dlRequest.isOption("errimg")) {
246 // operation mode: "lores": try to use scaled image, "hires": use unscaled image 263 // operation mode: "lores": try to use scaled image, "hires": use unscaled image
247 if (dlRequest.isOption("lores")) { 264 if (dlRequest.isOption("lores")) {
248 preScaledFirst = true; 265 preScaledFirst = true;
249 } else if (dlRequest.isOption("hires")) { 266 } else if (dlRequest.isOption("hires")) {
250 preScaledFirst = false; 267 preScaledFirst = false;
251 }
252 // operation mode: "hmir": mirror horizontally, "vmir": mirror vertically
253 if (dlRequest.isOption("hmir")) {
254 doMirror = true;
255 mirrorAngle = 0;
256 } else if (dlRequest.isOption("vmir")) {
257 doMirror = true;
258 mirrorAngle = 90;
259 } 268 }
260 269
261 //"big" try for all file/image actions 270 //"big" try for all file/image actions
262 try { 271 try {
263 272
302 return; 311 return;
303 } 312 }
304 } 313 }
305 } 314 }
306 315
307 // if it's zoomed, try hires version (to be optimized...)
308 if ((paramWW < 1f) || (paramWH < 1f)) {
309 preScaledFirst = false;
310 }
311
312 // find the file 316 // find the file
313 DocuFile fileToLoad; 317 DocuFile fileToLoad;
314 DocuFileset fileset = 318 DocuFileset fileset =
315 dirCache.getFileset(loadPathName, dlRequest.getPn()); 319 dirCache.getFileset(loadPathName, dlRequest.getPn());
316 if (fileset == null) { 320 if (fileset == null) {
320 + "(" 324 + "("
321 + dlRequest.getPn() 325 + dlRequest.getPn()
322 + ") not found."); 326 + ") not found.");
323 } 327 }
324 328
329 // if it's zoomed, try hires version (to be optimized...)
330 if ((paramWW < 1f) || (paramWH < 1f)) {
331 preScaledFirst = false;
332 }
333
325 // simplistic selection of resolution 334 // simplistic selection of resolution
326 if (preScaledFirst) { 335 if (preScaledFirst) {
327 // get last element 336 // get last element
328 fileToLoad = fileset.get(fileset.size() - 1); 337 fileToLoad = fileset.get(fileset.size() - 1);
329 } else { 338 } else {
337 // get the source image type 346 // get the source image type
338 mimeType = fileToLoad.getMimetype(); 347 mimeType = fileToLoad.getMimetype();
339 348
340 /* if autoScale and not zoomed and source is GIF/PNG 349 /* if autoScale and not zoomed and source is GIF/PNG
341 * then send as is. 350 * then send as is.
351 * if not autoScale and not scaleToFit nor cropToFit
352 * then send as is
342 */ 353 */
343 if ((autoScale 354 if ((autoScale
344 && (mimeType == "image/gif" || mimeType == "image/png") 355 && (mimeType == "image/gif" || mimeType == "image/png")
345 && (paramWW == 1f) 356 && (paramWW == 1f)
346 && (paramWH == 1f)) 357 && (paramWH == 1f))
347 || (autoScale && !(scaleToFit || cropToFit))) { 358 || (!autoScale && !scaleToFit && !cropToFit)) {
348 359
349 util.dprintln(1, "Sending File as is."); 360 util.dprintln(1, "Sending File as is.");
350 361
351 servletOp.sendFile(fileToLoad.getFile(), response); 362 servletOp.sendFile(fileToLoad.getFile(), response);
352 363
379 util.dprintln(2, "IMG: " + imgWidth + "x" + imgHeight); 390 util.dprintln(2, "IMG: " + imgWidth + "x" + imgHeight);
380 util.dprintln( 391 util.dprintln(
381 2, 392 2,
382 "time " + (System.currentTimeMillis() - startTime) + "ms"); 393 "time " + (System.currentTimeMillis() - startTime) + "ms");
383 394
384 // coordinates using Java2D
385 // image size
386 Rectangle2D imgBounds =
387 new Rectangle2D.Double(0, 0, imgWidth, imgHeight);
388 // user window area in 4-point form (ul, ur, ll, lr)
389 Point2D[] userAreaC =
390 {
391 new Point2D.Double(paramWX, paramWY),
392 new Point2D.Double(paramWX + paramWW, paramWY),
393 new Point2D.Double(paramWX, paramWY + paramWH),
394 new Point2D.Double(paramWX + paramWW, paramWY + paramWH)};
395 // transformation from relative [0,1] to image coordinates.
396 AffineTransform imgTrafo = new AffineTransform();
397 imgTrafo.scale(imgWidth, imgHeight);
398 // rotate coordinates
399 //imgTrafo.rotate(Math.toRadians(-paramROT));
400
401 // coordinates and scaling 395 // coordinates and scaling
402 double areaXoff; 396 double areaXoff;
403 double areaYoff; 397 double areaYoff;
404 double areaWidth; 398 double areaWidth;
405 double areaHeight; 399 double areaHeight;
406 double scaleX; 400 double scaleX;
407 double scaleY; 401 double scaleY;
408 double scaleXY; 402 double scaleXY;
409 403
410 /* if (scaleToFit) { 404 // coordinates using Java2D
411 // calculate absolute from relative coordinates 405 // image size in pixels
412 areaXoff = paramWX * imgWidth; 406 Rectangle2D imgBounds =
413 areaYoff = paramWY * imgHeight; 407 new Rectangle2D.Double(0, 0, imgWidth, imgHeight);
414 areaWidth = paramWW * imgWidth; 408 // user window area in [0,1] coordinates
415 areaHeight = paramWH * imgHeight; 409 Rectangle2D relUserArea =
416 // calculate scaling factors 410 new Rectangle2D.Double(paramWX, paramWY, paramWW, paramWH);
417 scaleX = paramDW / areaWidth * paramWS; 411 // transform from relative [0,1] to image coordinates.
418 scaleY = paramDH / areaHeight * paramWS; 412 AffineTransform imgTrafo =
419 scaleXY = (scaleX > scaleY) ? scaleY : scaleX; 413 AffineTransform.getScaleInstance(imgWidth, imgHeight);
420 } else {
421 // crop to fit
422 // calculate absolute from relative coordinates
423 areaXoff = paramWX * imgWidth;
424 areaYoff = paramWY * imgHeight;
425 areaWidth = paramDW;
426 areaHeight = paramDH;
427 // calculate scaling factors
428 scaleX = 1f;
429 scaleY = 1f;
430 scaleXY = 1f;
431 }
432
433 util.dprintln(
434 1,
435 "Scale "
436 + scaleXY
437 + "("
438 + scaleX
439 + ","
440 + scaleY
441 + ") on "
442 + areaXoff
443 + ","
444 + areaYoff
445 + " "
446 + areaWidth
447 + "x"
448 + areaHeight);
449 */
450 // Java2D
451 // area in image pixel coordinates
452 Point2D[] imgAreaC = { null, null, null, null };
453 // transform user coordinate area to image coordinate area 414 // transform user coordinate area to image coordinate area
454 imgTrafo.transform(userAreaC, 0, imgAreaC, 0, 4); 415 Rectangle2D userImgArea =
455 areaXoff = imgAreaC[0].getX(); 416 imgTrafo.createTransformedShape(relUserArea).getBounds2D();
456 areaYoff = imgAreaC[0].getY(); 417
457 // calculate scaling factors 418 // calculate scaling factors based on inner user area
458 if (scaleToFit) { 419 if (scaleToFit) {
459 areaWidth = imgAreaC[0].distance(imgAreaC[1]); 420 areaWidth = userImgArea.getWidth();
460 areaHeight = imgAreaC[0].distance(imgAreaC[2]); 421 areaHeight = userImgArea.getHeight();
461 scaleX = paramDW / areaWidth * paramWS; 422 scaleX = paramDW / areaWidth * paramWS;
462 scaleY = paramDH / areaHeight * paramWS; 423 scaleY = paramDH / areaHeight * paramWS;
463 scaleXY = (scaleX > scaleY) ? scaleY : scaleX; 424 scaleXY = (scaleX > scaleY) ? scaleY : scaleX;
425 } else if (absoluteScale) {
426 // absolute scale
427 areaWidth = paramDW * paramWS;
428 areaHeight = paramDH * paramWS;
429 // reset user area size
430 userImgArea.setRect(
431 userImgArea.getX(),
432 userImgArea.getY(),
433 areaWidth,
434 areaHeight);
435 scaleX = 1f;
436 scaleY = 1f;
437 scaleXY = 1f;
464 } else { 438 } else {
465 // crop to fit 439 // crop to fit
466 areaWidth = paramDW * paramWS; 440 areaWidth = paramDW * paramWS;
467 areaHeight = paramDH * paramWS; 441 areaHeight = paramDH * paramWS;
442 // reset user area size
443 userImgArea.setRect(
444 userImgArea.getX(),
445 userImgArea.getY(),
446 areaWidth,
447 areaHeight);
468 scaleX = 1f; 448 scaleX = 1f;
469 scaleY = 1f; 449 scaleY = 1f;
470 scaleXY = 1f; 450 scaleXY = 1f;
471 451 }
452
453 // enlarge image area for rotations to cover additional pixels
454 Rectangle2D outerUserImgArea = userImgArea;
455 Rectangle2D innerUserImgArea = userImgArea;
456 if (wholeRotArea) {
457 if (paramROT != 0) {
458 try {
459 // rotate user area coordinates around center of user area
460 AffineTransform rotTrafo =
461 AffineTransform.getRotateInstance(
462 Math.toRadians(paramROT),
463 userImgArea.getCenterX(),
464 userImgArea.getCenterY());
465 // get bounds from rotated end position
466 innerUserImgArea =
467 rotTrafo
468 .createTransformedShape(userImgArea)
469 .getBounds2D();
470 // get bounds from back-rotated bounds
471 outerUserImgArea =
472 rotTrafo
473 .createInverse()
474 .createTransformedShape(innerUserImgArea)
475 .getBounds2D();
476 } catch (NoninvertibleTransformException e1) {
477 // this shouldn't happen anyway
478 e1.printStackTrace();
479 }
480 }
472 } 481 }
473 482
474 util.dprintln( 483 util.dprintln(
475 1, 484 1,
476 "Scale " 485 "Scale "
478 + "(" 487 + "("
479 + scaleX 488 + scaleX
480 + "," 489 + ","
481 + scaleY 490 + scaleY
482 + ") on " 491 + ") on "
483 + areaXoff 492 + outerUserImgArea);
484 + ","
485 + areaYoff
486 + " "
487 + areaWidth
488 + "x"
489 + areaHeight);
490 493
491 // clip area at the image border 494 // clip area at the image border
492 /* areaWidth = 495 outerUserImgArea = outerUserImgArea.createIntersection(imgBounds);
493 (areaXoff + areaWidth > imgWidth) 496
494 ? imgWidth - areaXoff 497 areaWidth = outerUserImgArea.getWidth();
495 : areaWidth; 498 areaHeight = outerUserImgArea.getHeight();
496 areaHeight = 499 areaXoff = outerUserImgArea.getX();
497 (areaYoff + areaHeight > imgHeight) 500 areaYoff = outerUserImgArea.getY();
498 ? imgHeight - areaYoff
499 : areaHeight;
500 */
501
502 // create new rectangle from coordinates
503 Rectangle2D imgArea =
504 new Rectangle2D.Double(
505 areaXoff,
506 areaYoff,
507 areaWidth,
508 areaHeight);
509 // clip area at the image border
510 imgArea = imgArea.createIntersection(imgBounds);
511 areaWidth = imgArea.getWidth();
512 areaHeight = imgArea.getHeight();
513 501
514 util.dprintln( 502 util.dprintln(
515 2, 503 2,
516 "crop: " 504 "crop: "
517 + areaXoff 505 + areaXoff
549 "Using subsampling: " + subsamp + " rest " + scaleXY); 537 "Using subsampling: " + subsamp + " rest " + scaleXY);
550 } 538 }
551 539
552 docuImage.loadSubimage( 540 docuImage.loadSubimage(
553 fileToLoad.getFile(), 541 fileToLoad.getFile(),
554 imgArea.getBounds(), 542 outerUserImgArea.getBounds(),
555 (int) subsamp); 543 (int) subsamp);
556 544
557 System.out.println( 545 System.out.println(
558 "SUBSAMP: " 546 "SUBSAMP: "
559 + subsamp 547 + subsamp
563 + docuImage.getHeight()); 551 + docuImage.getHeight());
564 552
565 docuImage.scale(scaleXY); 553 docuImage.scale(scaleXY);
566 554
567 } else { 555 } else {
568 // else load the whole file 556 // else load and crop the whole file
569 docuImage.loadImage(fileToLoad.getFile()); 557 docuImage.loadImage(fileToLoad.getFile());
570 docuImage.crop( 558 docuImage.crop(
571 (int) areaXoff, 559 (int) areaXoff,
572 (int) areaYoff, 560 (int) areaYoff,
573 (int) areaWidth, 561 (int) areaWidth,
575 563
576 docuImage.scale(scaleXY); 564 docuImage.scale(scaleXY);
577 } 565 }
578 566
579 // mirror image 567 // mirror image
580 if (doMirror) { 568 // operation mode: "hmir": mirror horizontally, "vmir": mirror vertically
581 docuImage.mirror(mirrorAngle); 569 if (dlRequest.isOption("hmir")) {
582 } 570 docuImage.mirror(0);
583 571 }
584 // rotate image (first shot :-) 572 if (dlRequest.isOption("vmir")) {
573 docuImage.mirror(90);
574 }
575
576 // rotate image
585 if (paramROT != 0) { 577 if (paramROT != 0) {
586 docuImage.rotate(paramROT); 578 docuImage.rotate(paramROT);
587 } 579 if (wholeRotArea) {
588 580 // crop to the inner bounding box
589 // contrast and brightness enhancement 581 double xcrop =
590 if ((paramCONT != 0) || (paramBRGT != 0)) { 582 docuImage.getWidth()
591 double mult = Math.pow(2, paramCONT); 583 - innerUserImgArea.getWidth() * scaleXY;
592 docuImage.enhance((float) mult, (float) paramBRGT); 584 double ycrop =
585 docuImage.getHeight()
586 - innerUserImgArea.getHeight() * scaleXY;
587 if ((xcrop > 0) || (ycrop > 0)) {
588 // only crop smaller
589 xcrop = (xcrop > 0) ? xcrop : 0;
590 ycrop = (ycrop > 0) ? ycrop : 0;
591 // crop image
592 docuImage.crop(
593 (int) (xcrop / 2),
594 (int) (ycrop / 2),
595 (int) (docuImage.getWidth() - xcrop),
596 (int) (docuImage.getHeight() - ycrop));
597 }
598 }
599
593 } 600 }
594 601
595 // color modification 602 // color modification
596 if ((paramRGBM != null) || (paramRGBA != null)) { 603 if ((paramRGBM != null) || (paramRGBA != null)) {
597 // make shure we actually have two arrays 604 // make shure we actually have two arrays
599 paramRGBM = new float[3]; 606 paramRGBM = new float[3];
600 } 607 }
601 if (paramRGBA == null) { 608 if (paramRGBA == null) {
602 paramRGBA = new float[3]; 609 paramRGBA = new float[3];
603 } 610 }
604 // calculate "contrast" values 611 // calculate "contrast" values (c=2^x)
605 float[] mult = new float[3]; 612 float[] mult = new float[3];
606 for (int i = 0; i < 3; i++) { 613 for (int i = 0; i < 3; i++) {
607 mult[i] = (float) Math.pow(2, (double) paramRGBM[i]); 614 mult[i] = (float) Math.pow(2, (double) paramRGBM[i]);
608 } 615 }
609 docuImage.enhanceRGB(mult, paramRGBA); 616 docuImage.enhanceRGB(mult, paramRGBA);
610 } 617 }
611 618
619 // contrast and brightness enhancement
620 if ((paramCONT != 0) || (paramBRGT != 0)) {
621 double mult = Math.pow(2, paramCONT);
622 docuImage.enhance((float) mult, (float) paramBRGT);
623 }
624
612 util.dprintln( 625 util.dprintln(
613 2, 626 2,
614 "time " + (System.currentTimeMillis() - startTime) + "ms"); 627 "time " + (System.currentTimeMillis() - startTime) + "ms");
615 628
616 /* 629 /*
617 * write the resulting image 630 * write the resulting image
618 */ 631 */
619 632
620 // setup output -- if source is JPG then dest will be JPG else it's PNG 633 // setup output -- if source is JPG then dest will be JPG else it's PNG
621 if (mimeType != "image/jpeg") { 634 if (mimeType.equals("image/jpeg")
635 || mimeType.equals("image/jp2")) {
636 mimeType = "image/jpeg";
637 } else {
622 mimeType = "image/png"; 638 mimeType = "image/png";
623 } 639 }
624 response.setContentType(mimeType); 640 response.setContentType(mimeType);
625 641
626 // write the image 642 // write the image