Mercurial > hg > digilib-old
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 |