Mercurial > hg > digilib-old
annotate servlet/src/digilib/servlet/Scaler.java @ 122:a32e8c80e2f2
Servlet Version 1.10b1
- more intelligent handling of resolutions
- different handling of mo=lores
author | robcast |
---|---|
date | Wed, 11 Jun 2003 22:51:28 +0200 |
parents | 55bc0e928ac5 |
children | ed7c1e4dd177 |
rev | line source |
---|---|
1 | 1 /* Scaler -- Scaler servlet main class |
2 | |
3 Digital Image Library servlet components | |
4 | |
73 | 5 Copyright (C) 2001, 2002, 2003 Robert Casties (robcast@mail.berlios.de) |
1 | 6 |
7 This program is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 2 of the License, or (at your | |
10 option) any later version. | |
11 | |
12 Please read license.txt for the full details. A copy of the GPL | |
13 may be found at http://www.gnu.org/copyleft/lgpl.html | |
14 | |
15 You should have received a copy of the GNU General Public License | |
16 along with this program; if not, write to the Free Software | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 */ | |
20 | |
21 package digilib.servlet; | |
22 | |
86 | 23 import java.awt.Dimension; |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
24 import java.awt.geom.AffineTransform; |
100 | 25 import java.awt.geom.NoninvertibleTransformException; |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
26 import java.awt.geom.Rectangle2D; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
27 import java.io.File; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
28 import java.io.IOException; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
29 import java.util.List; |
1 | 30 |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
31 import javax.servlet.ServletConfig; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
32 import javax.servlet.ServletContext; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
33 import javax.servlet.ServletException; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
34 import javax.servlet.http.HttpServlet; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
35 import javax.servlet.http.HttpServletRequest; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
36 import javax.servlet.http.HttpServletResponse; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
37 |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
38 import digilib.Utils; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
39 import digilib.auth.AuthOpException; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
40 import digilib.auth.AuthOps; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
41 import digilib.image.DocuImage; |
122 | 42 import digilib.image.DocuInfo; |
43 import digilib.image.ImageLoaderDocuInfo; | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
44 import digilib.image.ImageOpException; |
86 | 45 import digilib.io.DocuDirCache; |
46 import digilib.io.DocuFile; | |
47 import digilib.io.DocuFileset; | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
48 import digilib.io.FileOpException; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
49 import digilib.io.FileOps; |
1 | 50 |
64
5ea1999befd4
New JAI ImageLoader plugin. Currently uses first beta version of the plugin.
robcast
parents:
62
diff
changeset
|
51 //import tilecachetool.*; |
1 | 52 |
73 | 53 /** |
54 * @author casties | |
55 * | |
56 */ | |
1 | 57 //public class Scaler extends HttpServlet implements SingleThreadModel { |
58 public class Scaler extends HttpServlet { | |
59 | |
73 | 60 // digilib servlet version (for all components) |
122 | 61 public static final String dlVersion = "1.10b1"; |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
62 |
73 | 63 // Utils instance with debuglevel |
64 Utils util; | |
65 // FileOps instance | |
66 FileOps fileOp; | |
67 // AuthOps instance | |
68 AuthOps authOp; | |
69 // ServletOps instance | |
70 ServletOps servletOp; | |
86 | 71 // DocuDirCache instance |
72 DocuDirCache dirCache; | |
1 | 73 |
94 | 74 // DigilibConfiguration instance |
73 | 75 DigilibConfiguration dlConfig; |
1 | 76 |
73 | 77 // use authorization database |
78 boolean useAuthentication = true; | |
1 | 79 |
100 | 80 // EXPRIMENTAL |
81 // try to enlarge cropping area for "oblique" angles | |
82 boolean wholeRotArea = false; | |
83 | |
73 | 84 /** Initialisation on first run. |
85 * | |
86 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) | |
87 */ | |
88 public void init(ServletConfig config) throws ServletException { | |
89 super.init(config); | |
1 | 90 |
73 | 91 // Debuggin! |
92 //TCTool tctool = new TCTool(); | |
1 | 93 |
73 | 94 // get our ServletContext |
95 ServletContext context = config.getServletContext(); | |
96 // see if there is a Configuration instance | |
97 dlConfig = | |
98 (DigilibConfiguration) context.getAttribute( | |
86 | 99 "digilib.servlet.configuration"); |
73 | 100 if (dlConfig == null) { |
101 // create new Configuration | |
102 try { | |
103 dlConfig = new DigilibConfiguration(config); | |
86 | 104 context.setAttribute("digilib.servlet.configuration", dlConfig); |
73 | 105 } catch (Exception e) { |
106 throw new ServletException(e); | |
107 } | |
108 } | |
109 // set the servlet version | |
110 dlConfig.setServletVersion(dlVersion); | |
111 // first we need an Utils | |
112 util = dlConfig.getUtil(); | |
113 // set our AuthOps | |
114 useAuthentication = dlConfig.isUseAuthentication(); | |
115 authOp = dlConfig.getAuthOp(); | |
116 // FileOps instance | |
117 fileOp = new FileOps(util); | |
118 // AuthOps instance | |
119 servletOp = new ServletOps(util); | |
86 | 120 // DocuDirCache instance |
121 dirCache = dlConfig.getDirCache(); | |
73 | 122 } |
1 | 123 |
73 | 124 /** Process the HTTP Get request*/ |
125 public void doGet(HttpServletRequest request, HttpServletResponse response) | |
126 throws ServletException, IOException { | |
127 util.dprintln(1, "The servlet has received a GET!"); | |
128 // create new request with defaults | |
129 DigilibRequest dlReq = new DigilibRequest(); | |
130 // set with request parameters | |
131 dlReq.setWithRequest(request); | |
132 // add DigilibRequest to ServletRequest | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
133 request.setAttribute("digilib.servlet.request", dlReq); |
73 | 134 // do the processing |
135 processRequest(request, response); | |
136 } | |
1 | 137 |
73 | 138 /**Process the HTTP Post request*/ |
139 public void doPost( | |
140 HttpServletRequest request, | |
141 HttpServletResponse response) | |
142 throws ServletException, IOException { | |
143 util.dprintln(1, "The servlet has received a POST!"); | |
144 // create new request with defaults | |
145 DigilibRequest dlReq = new DigilibRequest(); | |
146 // set with request parameters | |
147 dlReq.setWithRequest(request); | |
148 // add DigilibRequest to ServletRequest | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
149 request.setAttribute("digilib.servlet.request", dlReq); |
73 | 150 // do the processing |
151 processRequest(request, response); | |
152 } | |
153 | |
154 /** main request handler. */ | |
155 void processRequest( | |
156 HttpServletRequest request, | |
157 HttpServletResponse response) | |
158 throws ServletException, IOException { | |
1 | 159 |
73 | 160 // time for benchmarking |
161 long startTime = System.currentTimeMillis(); | |
162 // output mime/type | |
163 String mimeType = "image/png"; | |
1 | 164 |
73 | 165 /* |
166 * parameters for a session | |
167 */ | |
1 | 168 |
73 | 169 // scale the image file to fit window size i.e. respect dw,dh |
170 boolean scaleToFit = true; | |
100 | 171 // scale the image by a fixed factor only |
172 boolean absoluteScale = false; | |
122 | 173 // only crop the image to fit |
174 boolean cropToFit = false; | |
175 // try different resolution images automatically | |
176 boolean autoRes = true; | |
177 // use hires images (if autoRes == false) | |
178 boolean hiresOnly = false; | |
73 | 179 // interpolation to use for scaling |
180 int scaleQual = 0; | |
181 // send html error message (or image file) | |
182 boolean errorMsgHtml = false; | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
183 // mirror the image |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
184 boolean doMirror = false; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
185 // angle of mirror axis |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
186 double mirrorAngle = 0; |
1 | 187 |
73 | 188 /* |
189 * request parameters | |
190 */ | |
191 | |
192 DigilibRequest dlRequest = | |
193 (DigilibRequest) request.getAttribute("digilib.servlet.request"); | |
1 | 194 |
73 | 195 // destination image width |
196 int paramDW = dlRequest.getDw(); | |
197 // destination image height | |
198 int paramDH = dlRequest.getDh(); | |
122 | 199 // dw and dh shouldn't be empty, if they are, set dw=dh |
200 if (paramDW <= 0) { | |
201 paramDW = paramDH; | |
202 } | |
203 if (paramDH <= 0) { | |
204 paramDH = paramDW; | |
205 } | |
73 | 206 // relative area x_offset (0..1) |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
207 double paramWX = dlRequest.getWx(); |
73 | 208 // relative area y_offset |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
209 double paramWY = dlRequest.getWy(); |
73 | 210 // relative area width (0..1) |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
211 double paramWW = dlRequest.getWw(); |
73 | 212 // relative area height |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
213 double paramWH = dlRequest.getWh(); |
73 | 214 // scale factor (additional to dw/width, dh/height) |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
215 double paramWS = dlRequest.getWs(); |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
216 // rotation angle |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
217 double paramROT = dlRequest.getRot(); |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
218 // contrast enhancement |
86 | 219 float paramCONT = dlRequest.getCont(); |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
220 // brightness enhancement |
86 | 221 float paramBRGT = dlRequest.getBrgt(); |
222 // color modification | |
223 float[] paramRGBM = dlRequest.getRgbm(); | |
224 float[] paramRGBA = dlRequest.getRgba(); | |
1 | 225 |
73 | 226 /* operation mode: "fit": always fit to page, |
227 * "clip": send original resolution cropped, "file": send whole file (if | |
228 * allowed) | |
229 */ | |
230 if (dlRequest.isOption("clip")) { | |
231 scaleToFit = false; | |
100 | 232 absoluteScale = false; |
73 | 233 cropToFit = true; |
122 | 234 autoRes = true; |
73 | 235 } else if (dlRequest.isOption("fit")) { |
236 scaleToFit = true; | |
100 | 237 absoluteScale = false; |
122 | 238 cropToFit = false; |
239 autoRes = true; | |
100 | 240 } else if (dlRequest.isOption("scale")) { |
241 scaleToFit = false; | |
242 absoluteScale = true; | |
122 | 243 cropToFit = false; |
244 autoRes = false; | |
245 hiresOnly = true; | |
73 | 246 } else if (dlRequest.isOption("file")) { |
247 scaleToFit = false; | |
100 | 248 absoluteScale = false; |
73 | 249 if (dlConfig.isSendFileAllowed()) { |
250 cropToFit = false; | |
251 } else { | |
122 | 252 // crop to fit if send file not allowed |
73 | 253 cropToFit = true; |
254 } | |
122 | 255 autoRes = false; |
256 hiresOnly = true; | |
73 | 257 } |
258 // operation mode: "errtxt": error message in html, "errimg": error image | |
259 if (dlRequest.isOption("errtxt")) { | |
260 errorMsgHtml = true; | |
261 } else if (dlRequest.isOption("errimg")) { | |
262 errorMsgHtml = false; | |
263 } | |
264 // operation mode: "q0" - "q2": interpolation quality | |
265 if (dlRequest.isOption("q0")) { | |
266 scaleQual = 0; | |
267 } else if (dlRequest.isOption("q1")) { | |
268 scaleQual = 1; | |
269 } else if (dlRequest.isOption("q2")) { | |
270 scaleQual = 2; | |
271 } | |
272 // operation mode: "lores": try to use scaled image, "hires": use unscaled image | |
122 | 273 // "autores": try best fitting resolution |
73 | 274 if (dlRequest.isOption("lores")) { |
122 | 275 autoRes = false; |
276 hiresOnly = false; | |
73 | 277 } else if (dlRequest.isOption("hires")) { |
122 | 278 autoRes = false; |
279 hiresOnly = true; | |
280 } else if (dlRequest.isOption("autores")) { | |
281 autoRes = true; | |
73 | 282 } |
1 | 283 |
73 | 284 //"big" try for all file/image actions |
285 try { | |
286 | |
86 | 287 // new DocuImage instance |
73 | 288 DocuImage docuImage = dlConfig.getDocuImageInstance(); |
289 if (docuImage == null) { | |
290 throw new ImageOpException("Unable to load DocuImage class!"); | |
291 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
292 |
122 | 293 // new DocuInfo instance |
294 DocuInfo docuInfo = new ImageLoaderDocuInfo(); | |
295 | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
296 // set interpolation quality |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
297 docuImage.setQuality(scaleQual); |
73 | 298 |
299 /* | |
300 * find the file to load/send | |
301 */ | |
1 | 302 |
73 | 303 // get PathInfo |
304 String loadPathName = dlRequest.getFilePath(); | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
305 |
73 | 306 /* |
307 * check permissions | |
308 */ | |
309 if (useAuthentication) { | |
310 // get a list of required roles (empty if no restrictions) | |
311 List rolesRequired = authOp.rolesForPath(loadPathName, request); | |
312 if (rolesRequired != null) { | |
313 util.dprintln(1, "Role required: " + rolesRequired); | |
314 util.dprintln(2, "User: " + request.getRemoteUser()); | |
315 // is the current request/user authorized? | |
316 if (!authOp.isRoleAuthorized(rolesRequired, request)) { | |
317 // send deny answer and abort | |
318 util.dprintln(1, "ERROR: access denied!"); | |
319 if (errorMsgHtml) { | |
320 ServletOps.htmlMessage( | |
321 "ERROR: Unauthorized access!", | |
322 response); | |
323 } else { | |
324 servletOp.sendFile( | |
325 new File(dlConfig.getDenyImgFileName()), | |
326 response); | |
327 } | |
328 return; | |
329 } | |
330 } | |
331 } | |
1 | 332 |
122 | 333 // find the file(set) |
86 | 334 DocuFile fileToLoad; |
94 | 335 DocuFileset fileset = |
336 dirCache.getFileset(loadPathName, dlRequest.getPn()); | |
337 if (fileset == null) { | |
338 throw new FileOpException( | |
339 "File " | |
340 + loadPathName | |
341 + "(" | |
342 + dlRequest.getPn() | |
343 + ") not found."); | |
344 } | |
345 | |
122 | 346 /* |
347 * calculate expected source image size | |
348 * | |
349 */ | |
350 Dimension expectedSourceSize = new Dimension(); | |
351 if (scaleToFit) { | |
352 double scale = (1 / Math.min(paramWW, paramWH)) * paramWS; | |
353 expectedSourceSize.setSize(paramDW * scale, paramDH * scale); | |
354 } else { | |
355 expectedSourceSize.setSize( | |
356 paramDW * paramWS, | |
357 paramDH * paramWS); | |
100 | 358 } |
359 | |
122 | 360 /* |
361 * select a resolution | |
362 */ | |
363 if (autoRes) { | |
364 // autores: use next bigger resolution | |
365 fileToLoad = | |
366 fileset.getNextBigger(expectedSourceSize, docuInfo); | |
367 if (fileToLoad == null) { | |
368 // this is the biggest we have | |
369 fileToLoad = fileset.get(0); | |
370 } | |
86 | 371 } else { |
122 | 372 // enforced hires or lores |
373 if (hiresOnly) { | |
374 // get first element | |
375 fileToLoad = fileset.get(0); | |
376 } else { | |
377 // enforced lores uses next smaller resolution | |
378 fileToLoad = | |
379 fileset.getNextSmaller(expectedSourceSize, docuInfo); | |
380 if (fileToLoad == null) { | |
381 // this is the smallest we have | |
382 fileToLoad = fileset.get(fileset.size() - 1); | |
383 } | |
384 } | |
86 | 385 } |
386 util.dprintln(1, "Loading: " + fileToLoad.getFile()); | |
73 | 387 |
86 | 388 // check the source image |
122 | 389 if (!fileToLoad.isChecked()) { |
390 fileToLoad.check(docuInfo); | |
391 } | |
86 | 392 // get the source image type |
393 mimeType = fileToLoad.getMimetype(); | |
122 | 394 boolean imageSendable = |
395 mimeType.equals("image/jpeg") | |
396 || mimeType.equals("image/png") | |
397 || mimeType.equals("image/gif"); | |
73 | 398 |
122 | 399 /* if not autoRes and image smaller than requested |
400 * size then send as is. | |
100 | 401 * if not autoScale and not scaleToFit nor cropToFit |
122 | 402 * then send as is (mo=file) |
73 | 403 */ |
122 | 404 if ((!autoRes |
405 && imageSendable | |
406 && (fileToLoad.getSize().width <= expectedSourceSize.width) | |
407 && (fileToLoad.getSize().height <= expectedSourceSize.height)) | |
408 || (!autoRes && !scaleToFit && !cropToFit)) { | |
73 | 409 |
410 util.dprintln(1, "Sending File as is."); | |
1 | 411 |
86 | 412 servletOp.sendFile(fileToLoad.getFile(), response); |
1 | 413 |
73 | 414 util.dprintln( |
415 1, | |
416 "Done in " | |
417 + (System.currentTimeMillis() - startTime) | |
418 + "ms"); | |
419 return; | |
420 } | |
1 | 421 |
73 | 422 /* |
423 * crop and scale the image | |
424 */ | |
1 | 425 |
86 | 426 int imgWidth = 0; |
427 int imgHeight = 0; | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
428 // get image size |
86 | 429 if (fileToLoad.getSize() == null) { |
430 // size unknown so far | |
431 imgWidth = docuImage.getWidth(); | |
432 imgHeight = docuImage.getHeight(); | |
433 // remember size | |
434 fileToLoad.setSize(new Dimension(imgWidth, imgHeight)); | |
435 } else { | |
436 imgWidth = fileToLoad.getSize().width; | |
437 imgHeight = fileToLoad.getSize().height; | |
438 } | |
94 | 439 |
73 | 440 util.dprintln(2, "IMG: " + imgWidth + "x" + imgHeight); |
441 util.dprintln( | |
442 2, | |
443 "time " + (System.currentTimeMillis() - startTime) + "ms"); | |
1 | 444 |
73 | 445 // coordinates and scaling |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
446 double areaXoff; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
447 double areaYoff; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
448 double areaWidth; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
449 double areaHeight; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
450 double scaleX; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
451 double scaleY; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
452 double scaleXY; |
1 | 453 |
100 | 454 // coordinates using Java2D |
455 // image size in pixels | |
456 Rectangle2D imgBounds = | |
457 new Rectangle2D.Double(0, 0, imgWidth, imgHeight); | |
458 // user window area in [0,1] coordinates | |
459 Rectangle2D relUserArea = | |
460 new Rectangle2D.Double(paramWX, paramWY, paramWW, paramWH); | |
461 // transform from relative [0,1] to image coordinates. | |
462 AffineTransform imgTrafo = | |
463 AffineTransform.getScaleInstance(imgWidth, imgHeight); | |
85 | 464 // transform user coordinate area to image coordinate area |
100 | 465 Rectangle2D userImgArea = |
466 imgTrafo.createTransformedShape(relUserArea).getBounds2D(); | |
467 | |
468 // calculate scaling factors based on inner user area | |
73 | 469 if (scaleToFit) { |
100 | 470 areaWidth = userImgArea.getWidth(); |
471 areaHeight = userImgArea.getHeight(); | |
73 | 472 scaleX = paramDW / areaWidth * paramWS; |
473 scaleY = paramDH / areaHeight * paramWS; | |
474 scaleXY = (scaleX > scaleY) ? scaleY : scaleX; | |
100 | 475 } else if (absoluteScale) { |
476 // absolute scale | |
477 areaWidth = paramDW * paramWS; | |
478 areaHeight = paramDH * paramWS; | |
479 // reset user area size | |
480 userImgArea.setRect( | |
481 userImgArea.getX(), | |
482 userImgArea.getY(), | |
483 areaWidth, | |
484 areaHeight); | |
485 scaleX = 1f; | |
486 scaleY = 1f; | |
487 scaleXY = 1f; | |
73 | 488 } else { |
489 // crop to fit | |
85 | 490 areaWidth = paramDW * paramWS; |
491 areaHeight = paramDH * paramWS; | |
100 | 492 // reset user area size |
493 userImgArea.setRect( | |
494 userImgArea.getX(), | |
495 userImgArea.getY(), | |
496 areaWidth, | |
497 areaHeight); | |
73 | 498 scaleX = 1f; |
499 scaleY = 1f; | |
500 scaleXY = 1f; | |
100 | 501 } |
85 | 502 |
100 | 503 // enlarge image area for rotations to cover additional pixels |
504 Rectangle2D outerUserImgArea = userImgArea; | |
505 Rectangle2D innerUserImgArea = userImgArea; | |
506 if (wholeRotArea) { | |
507 if (paramROT != 0) { | |
508 try { | |
509 // rotate user area coordinates around center of user area | |
510 AffineTransform rotTrafo = | |
511 AffineTransform.getRotateInstance( | |
512 Math.toRadians(paramROT), | |
513 userImgArea.getCenterX(), | |
514 userImgArea.getCenterY()); | |
515 // get bounds from rotated end position | |
516 innerUserImgArea = | |
517 rotTrafo | |
518 .createTransformedShape(userImgArea) | |
519 .getBounds2D(); | |
520 // get bounds from back-rotated bounds | |
521 outerUserImgArea = | |
522 rotTrafo | |
523 .createInverse() | |
524 .createTransformedShape(innerUserImgArea) | |
525 .getBounds2D(); | |
526 } catch (NoninvertibleTransformException e1) { | |
527 // this shouldn't happen anyway | |
528 e1.printStackTrace(); | |
529 } | |
530 } | |
73 | 531 } |
1 | 532 |
73 | 533 util.dprintln( |
534 1, | |
535 "Scale " | |
536 + scaleXY | |
537 + "(" | |
538 + scaleX | |
539 + "," | |
540 + scaleY | |
541 + ") on " | |
100 | 542 + outerUserImgArea); |
1 | 543 |
73 | 544 // clip area at the image border |
100 | 545 outerUserImgArea = outerUserImgArea.createIntersection(imgBounds); |
85 | 546 |
100 | 547 areaWidth = outerUserImgArea.getWidth(); |
548 areaHeight = outerUserImgArea.getHeight(); | |
549 areaXoff = outerUserImgArea.getX(); | |
550 areaYoff = outerUserImgArea.getY(); | |
1 | 551 |
73 | 552 util.dprintln( |
553 2, | |
85 | 554 "crop: " |
73 | 555 + areaXoff |
556 + "," | |
557 + areaYoff | |
558 + " " | |
559 + areaWidth | |
560 + "x" | |
561 + areaHeight); | |
1 | 562 |
73 | 563 // check image parameters sanity |
564 if ((areaWidth < 1) | |
565 || (areaHeight < 1) | |
566 || (scaleXY * areaWidth < 2) | |
567 || (scaleXY * areaHeight < 2)) { | |
568 util.dprintln(1, "ERROR: invalid scale parameter set!"); | |
569 throw new ImageOpException("Invalid scale parameter set!"); | |
570 } | |
1 | 571 |
85 | 572 /* |
573 * crop and scale image | |
574 */ | |
575 | |
576 // use subimage loading if possible | |
577 if (docuImage.isSubimageSupported()) { | |
578 System.out.println( | |
579 "Subimage: scale " + scaleXY + " = " + (1 / scaleXY)); | |
580 double subf = 1d; | |
581 double subsamp = 1d; | |
582 if (scaleXY < 1) { | |
583 subf = 1 / scaleXY; | |
584 subsamp = Math.floor(subf); | |
585 scaleXY = subsamp / subf; | |
586 System.out.println( | |
587 "Using subsampling: " + subsamp + " rest " + scaleXY); | |
588 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
589 |
85 | 590 docuImage.loadSubimage( |
86 | 591 fileToLoad.getFile(), |
100 | 592 outerUserImgArea.getBounds(), |
85 | 593 (int) subsamp); |
594 | |
595 System.out.println( | |
596 "SUBSAMP: " | |
597 + subsamp | |
598 + " -> " | |
599 + docuImage.getWidth() | |
600 + "x" | |
601 + docuImage.getHeight()); | |
602 | |
603 docuImage.scale(scaleXY); | |
604 | |
605 } else { | |
100 | 606 // else load and crop the whole file |
86 | 607 docuImage.loadImage(fileToLoad.getFile()); |
85 | 608 docuImage.crop( |
609 (int) areaXoff, | |
610 (int) areaYoff, | |
611 (int) areaWidth, | |
612 (int) areaHeight); | |
613 | |
614 docuImage.scale(scaleXY); | |
615 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
616 |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
617 // mirror image |
100 | 618 // operation mode: "hmir": mirror horizontally, "vmir": mirror vertically |
619 if (dlRequest.isOption("hmir")) { | |
620 docuImage.mirror(0); | |
621 } | |
622 if (dlRequest.isOption("vmir")) { | |
623 docuImage.mirror(90); | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
624 } |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
625 |
100 | 626 // rotate image |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
627 if (paramROT != 0) { |
94 | 628 docuImage.rotate(paramROT); |
100 | 629 if (wholeRotArea) { |
630 // crop to the inner bounding box | |
631 double xcrop = | |
632 docuImage.getWidth() | |
633 - innerUserImgArea.getWidth() * scaleXY; | |
634 double ycrop = | |
635 docuImage.getHeight() | |
636 - innerUserImgArea.getHeight() * scaleXY; | |
637 if ((xcrop > 0) || (ycrop > 0)) { | |
638 // only crop smaller | |
639 xcrop = (xcrop > 0) ? xcrop : 0; | |
640 ycrop = (ycrop > 0) ? ycrop : 0; | |
641 // crop image | |
642 docuImage.crop( | |
643 (int) (xcrop / 2), | |
644 (int) (ycrop / 2), | |
645 (int) (docuImage.getWidth() - xcrop), | |
646 (int) (docuImage.getHeight() - ycrop)); | |
647 } | |
648 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
649 |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
650 } |
1 | 651 |
86 | 652 // color modification |
653 if ((paramRGBM != null) || (paramRGBA != null)) { | |
654 // make shure we actually have two arrays | |
655 if (paramRGBM == null) { | |
656 paramRGBM = new float[3]; | |
657 } | |
658 if (paramRGBA == null) { | |
659 paramRGBA = new float[3]; | |
660 } | |
100 | 661 // calculate "contrast" values (c=2^x) |
86 | 662 float[] mult = new float[3]; |
663 for (int i = 0; i < 3; i++) { | |
94 | 664 mult[i] = (float) Math.pow(2, (double) paramRGBM[i]); |
86 | 665 } |
666 docuImage.enhanceRGB(mult, paramRGBA); | |
667 } | |
94 | 668 |
100 | 669 // contrast and brightness enhancement |
670 if ((paramCONT != 0) || (paramBRGT != 0)) { | |
671 double mult = Math.pow(2, paramCONT); | |
672 docuImage.enhance((float) mult, (float) paramBRGT); | |
673 } | |
674 | |
73 | 675 util.dprintln( |
676 2, | |
677 "time " + (System.currentTimeMillis() - startTime) + "ms"); | |
678 | |
679 /* | |
680 * write the resulting image | |
681 */ | |
1 | 682 |
73 | 683 // setup output -- if source is JPG then dest will be JPG else it's PNG |
100 | 684 if (mimeType.equals("image/jpeg") |
685 || mimeType.equals("image/jp2")) { | |
686 mimeType = "image/jpeg"; | |
687 } else { | |
73 | 688 mimeType = "image/png"; |
689 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
690 response.setContentType(mimeType); |
1 | 691 |
73 | 692 // write the image |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
693 docuImage.writeImage(mimeType, response.getOutputStream()); |
1 | 694 |
73 | 695 util.dprintln( |
696 1, | |
697 "Done in " + (System.currentTimeMillis() - startTime) + "ms"); | |
698 | |
699 /* | |
700 * error handling | |
701 */ | |
1 | 702 |
73 | 703 } // end of "big" try |
704 catch (FileOpException e) { | |
705 util.dprintln(1, "ERROR: File IO Error: " + e); | |
706 try { | |
707 if (errorMsgHtml) { | |
708 ServletOps.htmlMessage( | |
709 "ERROR: File IO Error: " + e, | |
710 response); | |
711 } else { | |
712 servletOp.sendFile( | |
713 new File(dlConfig.getErrorImgFileName()), | |
714 response); | |
715 } | |
716 } catch (FileOpException ex) { | |
717 } // so we don't get a loop | |
718 return; | |
719 } catch (AuthOpException e) { | |
720 util.dprintln(1, "ERROR: Authorization error: " + e); | |
721 try { | |
722 if (errorMsgHtml) { | |
723 ServletOps.htmlMessage( | |
724 "ERROR: Authorization error: " + e, | |
725 response); | |
726 } else { | |
727 servletOp.sendFile( | |
728 new File(dlConfig.getErrorImgFileName()), | |
729 response); | |
730 } | |
731 } catch (FileOpException ex) { | |
732 } // so we don't get a loop | |
733 return; | |
734 } catch (ImageOpException e) { | |
735 util.dprintln(1, "ERROR: Image Error: " + e); | |
736 try { | |
737 if (errorMsgHtml) { | |
738 ServletOps.htmlMessage( | |
739 "ERROR: Image Operation Error: " + e, | |
740 response); | |
741 } else { | |
742 servletOp.sendFile( | |
743 new File(dlConfig.getErrorImgFileName()), | |
744 response); | |
745 } | |
746 } catch (FileOpException ex) { | |
747 } // so we don't get a loop | |
748 return; | |
749 } catch (RuntimeException e) { | |
750 // JAI likes to throw RuntimeExceptions ;-( | |
751 util.dprintln(1, "ERROR: Other Image Error: " + e); | |
752 try { | |
753 if (errorMsgHtml) { | |
754 ServletOps.htmlMessage( | |
755 "ERROR: Other Image Operation Error: " + e, | |
756 response); | |
757 } else { | |
758 servletOp.sendFile( | |
759 new File(dlConfig.getErrorImgFileName()), | |
760 response); | |
761 } | |
762 } catch (FileOpException ex) { | |
763 } // so we don't get a loop | |
764 return; | |
765 } | |
766 } | |
1 | 767 |
73 | 768 } //Scaler class |