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