Mercurial > hg > digilib-old
comparison common/src/main/java/digilib/image/ImageJobDescription.java @ 903:7779b37d1d05
refactored into maven modules per servlet type.
can build servlet-api 2.3 and 3.0 via profile now!
author | robcast |
---|---|
date | Tue, 26 Apr 2011 20:24:31 +0200 |
parents | servlet/src/main/java/digilib/image/ImageJobDescription.java@ba1eb2d821a2 |
children | 28d007673346 |
comparison
equal
deleted
inserted
replaced
902:89ba3ffcf552 | 903:7779b37d1d05 |
---|---|
1 package digilib.image; | |
2 | |
3 import java.awt.geom.AffineTransform; | |
4 import java.awt.geom.Rectangle2D; | |
5 import java.io.IOException; | |
6 | |
7 import org.apache.log4j.Logger; | |
8 | |
9 import digilib.image.DocuImage.ColorOp; | |
10 import digilib.io.DocuDirCache; | |
11 import digilib.io.DocuDirectory; | |
12 import digilib.io.FileOpException; | |
13 import digilib.io.FileOps; | |
14 import digilib.io.FileOps.FileClass; | |
15 import digilib.io.ImageInput; | |
16 import digilib.io.ImageSet; | |
17 import digilib.servlet.DigilibConfiguration; | |
18 import digilib.util.ImageSize; | |
19 import digilib.util.OptionsSet; | |
20 import digilib.util.Parameter; | |
21 import digilib.util.ParameterMap; | |
22 | |
23 /** | |
24 * A class for storing the set of parameters necessary for scaling images | |
25 * with an ImageWorker. | |
26 * | |
27 * This contains the functionality formerly found in Scaler.processRequest(), | |
28 * only factorized. | |
29 * | |
30 * @author cmielack, casties | |
31 * | |
32 */ | |
33 | |
34 public class ImageJobDescription extends ParameterMap { | |
35 | |
36 DigilibConfiguration dlConfig = null; | |
37 protected static Logger logger = Logger.getLogger("digilib.servlet"); | |
38 | |
39 ImageInput input = null; | |
40 ImageSet imageSet = null; | |
41 DocuDirectory fileDir = null; | |
42 String filePath = null; | |
43 ImageSize expectedSourceSize = null; | |
44 Float scaleXY = null; | |
45 Rectangle2D userImgArea = null; | |
46 Rectangle2D outerUserImgArea = null; | |
47 Boolean imageSendable = null; | |
48 String mimeType = null; | |
49 Integer paramDW = null; | |
50 Integer paramDH = null; | |
51 | |
52 /** create empty ImageJobDescription. | |
53 * @param dlcfg | |
54 */ | |
55 public ImageJobDescription(DigilibConfiguration dlcfg) { | |
56 super(30); | |
57 dlConfig = dlcfg; | |
58 } | |
59 | |
60 | |
61 /** set up Parameters | |
62 * @see digilib.util.ParameterMap#initParams() | |
63 */ | |
64 @Override | |
65 protected void initParams() { | |
66 // url of the page/document (second part) | |
67 newParameter("fn", "", null, 's'); | |
68 // page number | |
69 newParameter("pn", new Integer(1), null, 's'); | |
70 // width of client in pixels | |
71 newParameter("dw", new Integer(0), null, 's'); | |
72 // height of client in pixels | |
73 newParameter("dh", new Integer(0), null, 's'); | |
74 // left edge of image (float from 0 to 1) | |
75 newParameter("wx", new Float(0), null, 's'); | |
76 // top edge in image (float from 0 to 1) | |
77 newParameter("wy", new Float(0), null, 's'); | |
78 // width of image (float from 0 to 1) | |
79 newParameter("ww", new Float(1), null, 's'); | |
80 // height of image (float from 0 to 1) | |
81 newParameter("wh", new Float(1), null, 's'); | |
82 // scale factor | |
83 newParameter("ws", new Float(1), null, 's'); | |
84 // special options like 'fit' for gifs | |
85 newParameter("mo", this.options, null, 's'); | |
86 // rotation angle (degree) | |
87 newParameter("rot", new Float(0), null, 's'); | |
88 // contrast enhancement factor | |
89 newParameter("cont", new Float(0), null, 's'); | |
90 // brightness enhancement factor | |
91 newParameter("brgt", new Float(0), null, 's'); | |
92 // color multiplicative factors | |
93 newParameter("rgbm", "0/0/0", null, 's'); | |
94 // color additive factors | |
95 newParameter("rgba", "0/0/0", null, 's'); | |
96 // display dpi resolution (total) | |
97 newParameter("ddpi", new Float(0), null, 's'); | |
98 // display dpi X resolution | |
99 newParameter("ddpix", new Float(0), null, 's'); | |
100 // display dpi Y resolution | |
101 newParameter("ddpiy", new Float(0), null, 's'); | |
102 // scale factor for mo=ascale | |
103 newParameter("scale", new Float(1), null, 's'); | |
104 // color conversion operation | |
105 newParameter("colop", "", null, 's'); | |
106 } | |
107 | |
108 | |
109 /* (non-Javadoc) | |
110 * @see digilib.servlet.ParameterMap#initOptions() | |
111 */ | |
112 @Override | |
113 protected void initOptions() { | |
114 String s = this.getAsString("mo"); | |
115 options = new OptionsSet(s); | |
116 } | |
117 | |
118 | |
119 /** Creates new ImageJobDescription by merging Parameters from another ParameterMap. | |
120 * @param pm | |
121 * @param dlcfg | |
122 * @return | |
123 */ | |
124 public static ImageJobDescription getInstance(ParameterMap pm, DigilibConfiguration dlcfg) { | |
125 ImageJobDescription newMap = new ImageJobDescription(dlcfg); | |
126 // add all params to this map | |
127 newMap.params.putAll(pm.getParams()); | |
128 newMap.initOptions(); | |
129 return newMap; | |
130 } | |
131 | |
132 | |
133 /** Returns the mime-type (of the input). | |
134 * @return | |
135 * @throws IOException | |
136 */ | |
137 public String getMimeType() throws IOException { | |
138 if (mimeType == null) { | |
139 input = getInput(); | |
140 mimeType = input.getMimetype(); | |
141 } | |
142 return mimeType; | |
143 } | |
144 | |
145 /** Returns the ImageInput to use. | |
146 * @return | |
147 * @throws IOException | |
148 */ | |
149 public ImageInput getInput() throws IOException { | |
150 if(input == null){ | |
151 imageSet = getImageSet(); | |
152 | |
153 /* select a resolution */ | |
154 if (isHiresOnly()) { | |
155 // get first element (= highest resolution) | |
156 input = imageSet.getBiggest(); | |
157 } else if (isLoresOnly()) { | |
158 // enforced lores uses next smaller resolution | |
159 input = imageSet.getNextSmaller(getExpectedSourceSize()); | |
160 if (input == null) { | |
161 // this is the smallest we have | |
162 input = imageSet.getSmallest(); | |
163 } | |
164 } else { | |
165 // autores: use next higher resolution | |
166 input = imageSet.getNextBigger(getExpectedSourceSize()); | |
167 if (input == null) { | |
168 // this is the highest we have | |
169 input = imageSet.getBiggest(); | |
170 } | |
171 } | |
172 if (input == null || input.getMimetype() == null) { | |
173 throw new FileOpException("Unable to load "+input); | |
174 } | |
175 logger.info("Planning to load: " + input); | |
176 } | |
177 return input; | |
178 } | |
179 | |
180 /** Returns the DocuDirectory for the input (file). | |
181 * @return | |
182 * @throws FileOpException | |
183 */ | |
184 public DocuDirectory getFileDirectory() throws FileOpException { | |
185 if(fileDir == null){ | |
186 DocuDirCache dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); | |
187 String fp = getFilePath(); | |
188 fileDir = dirCache.getDirectory(fp); | |
189 if (fileDir == null) { | |
190 throw new FileOpException("Directory " + getFilePath() + " not found."); | |
191 } | |
192 } | |
193 return fileDir; | |
194 } | |
195 | |
196 /** Returns the ImageSet to load. | |
197 * @return | |
198 * @throws FileOpException | |
199 */ | |
200 public ImageSet getImageSet() throws FileOpException { | |
201 if(imageSet==null){ | |
202 DocuDirCache dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); | |
203 | |
204 imageSet = (ImageSet) dirCache.getFile(getFilePath(), getAsInt("pn"), FileClass.IMAGE); | |
205 if (imageSet == null) { | |
206 throw new FileOpException("File " + getFilePath() + "(" | |
207 + getAsInt("pn") + ") not found."); | |
208 } | |
209 } | |
210 return imageSet; | |
211 } | |
212 | |
213 /** Returns the file path name from the request. | |
214 * @return | |
215 */ | |
216 public String getFilePath() { | |
217 if(filePath == null){ | |
218 String s = this.getAsString("request.path"); | |
219 s += this.getAsString("fn"); | |
220 filePath = FileOps.normalName(s); | |
221 } | |
222 return filePath; | |
223 } | |
224 | |
225 public boolean isHiresOnly(){ | |
226 return hasOption("clip") || hasOption("hires"); | |
227 } | |
228 | |
229 public boolean isLoresOnly(){ | |
230 return hasOption("lores"); | |
231 } | |
232 | |
233 public boolean isScaleToFit() { | |
234 return !(hasOption("clip") || hasOption("osize") || hasOption("ascale")); | |
235 } | |
236 | |
237 public boolean isAbsoluteScale(){ | |
238 return hasOption("osize") || hasOption("ascale"); | |
239 } | |
240 | |
241 | |
242 /** Returns the minimum size the source image should have for scaling. | |
243 * @return | |
244 * @throws IOException | |
245 */ | |
246 public ImageSize getExpectedSourceSize() throws IOException { | |
247 if (expectedSourceSize == null){ | |
248 expectedSourceSize = new ImageSize(); | |
249 if (isScaleToFit()) { | |
250 // scale to fit -- calculate minimum source size | |
251 float scale = (1 / Math.min(getAsFloat("ww"), getAsFloat("wh"))) * getAsFloat("ws"); | |
252 expectedSourceSize.setSize((int) (getDw() * scale), | |
253 (int) (getDh() * scale)); | |
254 } else if (isAbsoluteScale() && hasOption("ascale")) { | |
255 // absolute scale -- apply scale to hires size | |
256 expectedSourceSize = getHiresSize().getScaled(getAsFloat("scale")); | |
257 } else { | |
258 // clip to fit -- source = destination size | |
259 expectedSourceSize.setSize((int) (getDw() * getAsFloat("ws")), | |
260 (int) (getDh() * getAsFloat("ws"))); | |
261 } | |
262 } | |
263 return expectedSourceSize; | |
264 } | |
265 | |
266 /** Returns the size of the highest resolution image. | |
267 * @return | |
268 * @throws IOException | |
269 */ | |
270 public ImageSize getHiresSize() throws IOException { | |
271 logger.debug("get_hiresSize()"); | |
272 | |
273 ImageSize hiresSize = null; | |
274 ImageSet fileset = getImageSet(); | |
275 if (isAbsoluteScale()) { | |
276 ImageInput hiresFile = fileset.getBiggest(); | |
277 hiresSize = hiresFile.getSize(); | |
278 } | |
279 return hiresSize; | |
280 } | |
281 | |
282 /** Returns image scaling factor. | |
283 * Uses image size and user parameters. | |
284 * Modifies scaleXY, userImgArea. | |
285 * @return | |
286 * @throws IOException | |
287 * @throws ImageOpException | |
288 */ | |
289 public float getScaleXY() throws IOException, ImageOpException { | |
290 //logger.debug("get_scaleXY()"); | |
291 if(scaleXY == null){ | |
292 // coordinates and scaling | |
293 float areaWidth; | |
294 float areaHeight; | |
295 float ws = getAsFloat("ws"); | |
296 ImageSize imgSize = getInput().getSize(); | |
297 // user window area in [0,1] coordinates | |
298 Rectangle2D relUserArea = new Rectangle2D.Float(getAsFloat("wx"), getAsFloat("wy"), | |
299 getAsFloat("ww"), getAsFloat("wh")); | |
300 // transform from relative [0,1] to image coordinates. | |
301 AffineTransform imgTrafo = AffineTransform.getScaleInstance(imgSize | |
302 .getWidth(), imgSize.getHeight()); | |
303 // transform user coordinate area to image coordinate area | |
304 userImgArea = imgTrafo.createTransformedShape( | |
305 relUserArea).getBounds2D(); | |
306 | |
307 if (isScaleToFit()) { | |
308 // calculate scaling factors based on inner user area | |
309 areaWidth = (float) userImgArea.getWidth(); | |
310 areaHeight = (float) userImgArea.getHeight(); | |
311 float scaleX = getDw() / areaWidth * ws; | |
312 float scaleY = getDh() / areaHeight * ws; | |
313 scaleXY = (scaleX > scaleY) ? scaleY : scaleX; | |
314 } else if (isAbsoluteScale()) { | |
315 // absolute scaling factor | |
316 if (hasOption("osize")) { | |
317 // get original resolution from metadata | |
318 imageSet.checkMeta(); | |
319 float origResX = imageSet.getResX(); | |
320 float origResY = imageSet.getResY(); | |
321 if ((origResX == 0) || (origResY == 0)) { | |
322 throw new ImageOpException("Missing image DPI information!"); | |
323 } | |
324 float ddpix = getAsFloat("ddpix"); | |
325 float ddpiy = getAsFloat("ddpiy"); | |
326 if (ddpix == 0 || ddpiy == 0) { | |
327 float ddpi = getAsFloat("ddpi"); | |
328 if (ddpi == 0) { | |
329 throw new ImageOpException("Missing display DPI information!"); | |
330 } else { | |
331 ddpix = ddpi; | |
332 ddpiy = ddpi; | |
333 } | |
334 } | |
335 // calculate absolute scale factor | |
336 float sx = ddpix / origResX; | |
337 float sy = ddpiy / origResY; | |
338 // currently only same scale -- mean value | |
339 scaleXY = (sx + sy) / 2f; | |
340 } else { | |
341 scaleXY = getAsFloat("scale"); | |
342 } | |
343 // we need to correct the factor if we use a pre-scaled image | |
344 ImageSize hiresSize = getHiresSize(); | |
345 if (imgSize.getWidth() != hiresSize.getWidth()) { | |
346 scaleXY *= (float)hiresSize.getWidth() / (float)imgSize.getWidth(); | |
347 } | |
348 areaWidth = getDw() / scaleXY * ws; | |
349 areaHeight = getDh() / scaleXY * ws; | |
350 // reset user area size | |
351 userImgArea.setRect(userImgArea.getX(), userImgArea.getY(), | |
352 areaWidth, areaHeight); | |
353 } else { | |
354 // crop to fit -- don't scale | |
355 areaWidth = getDw() * ws; | |
356 areaHeight = getDh() * ws; | |
357 // reset user area size | |
358 userImgArea.setRect(userImgArea.getX(), userImgArea.getY(), | |
359 areaWidth, areaHeight); | |
360 scaleXY = 1f; | |
361 } | |
362 } | |
363 return (float) scaleXY; | |
364 } | |
365 | |
366 /** Returns the width of the destination image. | |
367 * Uses dh parameter and aspect ratio if dw parameter is empty. | |
368 * @return | |
369 * @throws IOException | |
370 */ | |
371 public int getDw() throws IOException { | |
372 logger.debug("get_paramDW()"); | |
373 if (paramDW == null) { | |
374 | |
375 paramDW = getAsInt("dw"); | |
376 paramDH = getAsInt("dh"); | |
377 | |
378 float imgAspect = getInput().getAspect(); | |
379 if (paramDW == 0) { | |
380 // calculate dw | |
381 paramDW = Math.round(paramDH * imgAspect); | |
382 setValue("dw", paramDW); | |
383 } else if (paramDH == 0) { | |
384 // calculate dh | |
385 paramDH = Math.round(paramDW / imgAspect); | |
386 setValue("dh", paramDH); | |
387 } | |
388 } | |
389 return paramDW; | |
390 } | |
391 | |
392 /** Returns the height of the destination image. | |
393 * Uses dw parameter and aspect ratio if dh parameter is empty. | |
394 * @return | |
395 * @throws IOException | |
396 */ | |
397 public int getDh() throws IOException { | |
398 logger.debug("get_paramDH()"); | |
399 if (paramDH == null) { | |
400 | |
401 paramDW = getAsInt("dw"); | |
402 paramDH = getAsInt("dh"); | |
403 | |
404 float imgAspect = getInput().getAspect(); | |
405 if (paramDW == 0) { | |
406 // calculate dw | |
407 paramDW = Math.round(paramDH * imgAspect); | |
408 setValue("dw", paramDW); | |
409 } else if (paramDH == 0) { | |
410 // calculate dh | |
411 paramDH = Math.round(paramDW / imgAspect); | |
412 setValue("dh", paramDH); | |
413 } | |
414 } | |
415 return paramDH; | |
416 } | |
417 | |
418 /** Returns image quality as an integer. | |
419 * @return | |
420 */ | |
421 public int getScaleQual(){ | |
422 logger.debug("get_scaleQual()"); | |
423 int qual = dlConfig.getAsInt("default-quality"); | |
424 if(hasOption("q0")) | |
425 qual = 0; | |
426 else if(hasOption("q1")) | |
427 qual = 1; | |
428 else if(hasOption("q2")) | |
429 qual = 2; | |
430 return qual; | |
431 } | |
432 | |
433 public ColorOp getColOp() { | |
434 String op = getAsString("colop"); | |
435 if (op == null || op.length() == 0) { | |
436 return null; | |
437 } | |
438 try { | |
439 return ColorOp.valueOf(op.toUpperCase()); | |
440 } catch (Exception e) { | |
441 logger.error("Invalid color op: " + op); | |
442 } | |
443 return null; | |
444 } | |
445 | |
446 /** | |
447 * Returns the area of the source image that will be transformed into the | |
448 * destination image. | |
449 * | |
450 * @return | |
451 * @throws IOException | |
452 * @throws ImageOpException | |
453 */ | |
454 public Rectangle2D getUserImgArea() throws IOException, ImageOpException{ | |
455 if(userImgArea == null) { | |
456 // getScaleXY sets userImgArea | |
457 getScaleXY(); | |
458 } | |
459 return userImgArea; | |
460 } | |
461 | |
462 /** Returns the maximal area of the source image that will be used. | |
463 * @return | |
464 * @throws IOException | |
465 * @throws ImageOpException | |
466 */ | |
467 public Rectangle2D getOuterUserImgArea() throws IOException, ImageOpException { | |
468 if(outerUserImgArea == null){ | |
469 outerUserImgArea = getUserImgArea(); | |
470 | |
471 // image size in pixels | |
472 ImageSize imgSize = getInput().getSize(); | |
473 Rectangle2D imgBounds = new Rectangle2D.Float(0, 0, imgSize.getWidth(), | |
474 imgSize.getHeight()); | |
475 | |
476 // clip area at the image border | |
477 outerUserImgArea = outerUserImgArea.createIntersection(imgBounds); | |
478 | |
479 // check image parameters sanity | |
480 scaleXY = getScaleXY(); | |
481 logger.debug("outerUserImgArea.getWidth()=" + outerUserImgArea.getWidth()); | |
482 logger.debug("get_scaleXY() * outerUserImgArea.getWidth() = " + (scaleXY * outerUserImgArea.getWidth())); | |
483 | |
484 if ((outerUserImgArea.getWidth() < 1) | |
485 || (outerUserImgArea.getHeight() < 1) | |
486 || (scaleXY * outerUserImgArea.getWidth() < 2) | |
487 || (scaleXY * outerUserImgArea.getHeight() < 2)) { | |
488 logger.error("ERROR: invalid scale parameter set!"); | |
489 throw new ImageOpException("Invalid scale parameter set!"); | |
490 } | |
491 } | |
492 return outerUserImgArea; | |
493 } | |
494 | |
495 | |
496 public float[] getRGBM(){ | |
497 float[] paramRGBM = null;//{0f,0f,0f}; | |
498 Parameter p = params.get("rgbm"); | |
499 if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) { | |
500 return p.parseAsFloatArray("/"); | |
501 } | |
502 return paramRGBM; | |
503 } | |
504 | |
505 public float[] getRGBA(){ | |
506 float[] paramRGBA = null;//{0f,0f,0f}; | |
507 Parameter p = params.get("rgba"); | |
508 if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) { | |
509 paramRGBA = p.parseAsFloatArray("/"); | |
510 } | |
511 return paramRGBA; | |
512 } | |
513 | |
514 /** Has send-as-file been requested? | |
515 * @return | |
516 */ | |
517 public boolean getSendAsFile(){ | |
518 return hasOption("file") | |
519 || hasOption("rawfile"); | |
520 } | |
521 | |
522 /** | |
523 * Returns if the image can be sent without processing. Takes image type and | |
524 * additional image operations into account. Does not check requested size | |
525 * transformation. | |
526 * | |
527 * @return | |
528 * @throws IOException | |
529 */ | |
530 public boolean isImageSendable() throws IOException { | |
531 if (imageSendable == null) { | |
532 String mimeType = getMimeType(); | |
533 imageSendable = (mimeType != null | |
534 && (mimeType.equals("image/jpeg") || mimeType.equals("image/png") | |
535 || mimeType.equals("image/gif")) | |
536 && !(hasOption("hmir") | |
537 || hasOption("vmir") || (getAsFloat("rot") != 0.0) | |
538 || (getRGBM() != null) || (getRGBA() != null) | |
539 || (getAsFloat("cont") != 0.0) || (getAsFloat("brgt") != 0.0))); | |
540 } | |
541 return imageSendable; | |
542 } | |
543 | |
544 | |
545 /** | |
546 * Returns if any transformation of the source image (image manipulation or | |
547 * format conversion) is required. | |
548 * | |
549 * @return | |
550 * @throws IOException | |
551 */ | |
552 public boolean isTransformRequired() throws IOException { | |
553 ImageSize is = getInput().getSize(); | |
554 ImageSize ess = getExpectedSourceSize(); | |
555 // nt = no transform required | |
556 boolean nt = isImageSendable() && ( | |
557 // lores: send if smaller | |
558 (isLoresOnly() && is.isSmallerThan(ess)) | |
559 // else send if it fits | |
560 || (!(isLoresOnly() || isHiresOnly()) && is.fitsIn(ess))); | |
561 return ! nt; | |
562 } | |
563 } |