Mercurial > hg > digilib-old
annotate servlet/src/digilib/servlet/Scaler.java @ 149:04ad64b2137a
Servlet version 1.14b1
- better performance with thumbnails (really, this time :-)
- new DocuInfo class
- new Directory class
- DocuFile uses String and Directory as data members
- parameter rearrangements
author | robcast |
---|---|
date | Tue, 26 Aug 2003 22:28:43 +0200 |
parents | 837a633a0407 |
children | bc8df0133c04 |
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 | |
149 | 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1 | 18 |
19 */ | |
20 | |
21 package digilib.servlet; | |
22 | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
23 import java.awt.geom.AffineTransform; |
100 | 24 import java.awt.geom.NoninvertibleTransformException; |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
25 import java.awt.geom.Rectangle2D; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
26 import java.io.File; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
27 import java.io.IOException; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
28 import java.util.List; |
1 | 29 |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
30 import javax.servlet.ServletConfig; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
31 import javax.servlet.ServletContext; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
32 import javax.servlet.ServletException; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
33 import javax.servlet.http.HttpServlet; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
34 import javax.servlet.http.HttpServletRequest; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
35 import javax.servlet.http.HttpServletResponse; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
36 |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
37 import digilib.Utils; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
38 import digilib.auth.AuthOpException; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
39 import digilib.auth.AuthOps; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
40 import digilib.image.DocuImage; |
122 | 41 import digilib.image.DocuInfo; |
149 | 42 import digilib.image.ImageLoaderImageInfoDocuInfo; |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
43 import digilib.image.ImageOpException; |
149 | 44 import digilib.image.ImageSize; |
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) |
149 | 61 public static final String dlVersion = "1.14b1"; |
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; | |
149 | 179 // send the file as is |
180 boolean sendFile = false; | |
181 // use low resolution images only | |
182 boolean loresOnly = false; | |
183 // use hires images only | |
122 | 184 boolean hiresOnly = false; |
73 | 185 // interpolation to use for scaling |
142 | 186 int scaleQual = 1; |
73 | 187 // send html error message (or image file) |
188 boolean errorMsgHtml = false; | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
189 // mirror the image |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
190 boolean doMirror = false; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
191 // angle of mirror axis |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
192 double mirrorAngle = 0; |
130 | 193 // original (hires) image resolution |
194 double origResX = 0; | |
195 double origResY = 0; | |
1 | 196 |
73 | 197 /* |
198 * request parameters | |
199 */ | |
200 | |
201 DigilibRequest dlRequest = | |
202 (DigilibRequest) request.getAttribute("digilib.servlet.request"); | |
1 | 203 |
73 | 204 // destination image width |
205 int paramDW = dlRequest.getDw(); | |
206 // destination image height | |
207 int paramDH = dlRequest.getDh(); | |
122 | 208 // dw and dh shouldn't be empty, if they are, set dw=dh |
209 if (paramDW <= 0) { | |
210 paramDW = paramDH; | |
211 } | |
212 if (paramDH <= 0) { | |
213 paramDH = paramDW; | |
214 } | |
73 | 215 // relative area x_offset (0..1) |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
216 double paramWX = dlRequest.getWx(); |
73 | 217 // relative area y_offset |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
218 double paramWY = dlRequest.getWy(); |
73 | 219 // relative area width (0..1) |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
220 double paramWW = dlRequest.getWw(); |
73 | 221 // relative area height |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
222 double paramWH = dlRequest.getWh(); |
73 | 223 // scale factor (additional to dw/width, dh/height) |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
224 double paramWS = dlRequest.getWs(); |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
225 // rotation angle |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
226 double paramROT = dlRequest.getRot(); |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
227 // contrast enhancement |
86 | 228 float paramCONT = dlRequest.getCont(); |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
229 // brightness enhancement |
86 | 230 float paramBRGT = dlRequest.getBrgt(); |
231 // color modification | |
232 float[] paramRGBM = dlRequest.getRgbm(); | |
233 float[] paramRGBA = dlRequest.getRgba(); | |
130 | 234 // destination resolution (DPI) |
235 float paramDDPIX = dlRequest.getDdpix(); | |
236 float paramDDPIY = dlRequest.getDdpiy(); | |
237 if ((paramDDPIX == 0) || (paramDDPIY == 0)) { | |
238 // if X or Y resolution isn't set, use DDPI | |
239 paramDDPIX = dlRequest.getDdpi(); | |
240 paramDDPIY = dlRequest.getDdpi(); | |
241 } | |
1 | 242 |
73 | 243 /* operation mode: "fit": always fit to page, |
244 * "clip": send original resolution cropped, "file": send whole file (if | |
245 * allowed) | |
246 */ | |
247 if (dlRequest.isOption("clip")) { | |
248 scaleToFit = false; | |
100 | 249 absoluteScale = false; |
73 | 250 cropToFit = true; |
149 | 251 sendFile = false; |
252 hiresOnly = true; | |
73 | 253 } else if (dlRequest.isOption("fit")) { |
254 scaleToFit = true; | |
100 | 255 absoluteScale = false; |
122 | 256 cropToFit = false; |
149 | 257 sendFile = false; |
258 hiresOnly = false; | |
130 | 259 } else if (dlRequest.isOption("osize")) { |
100 | 260 scaleToFit = false; |
261 absoluteScale = true; | |
122 | 262 cropToFit = false; |
149 | 263 sendFile = false; |
122 | 264 hiresOnly = true; |
73 | 265 } else if (dlRequest.isOption("file")) { |
266 scaleToFit = false; | |
100 | 267 absoluteScale = false; |
73 | 268 if (dlConfig.isSendFileAllowed()) { |
269 cropToFit = false; | |
149 | 270 sendFile = true; |
73 | 271 } else { |
122 | 272 // crop to fit if send file not allowed |
73 | 273 cropToFit = true; |
149 | 274 sendFile = false; |
73 | 275 } |
122 | 276 hiresOnly = true; |
73 | 277 } |
149 | 278 // operation mode: "lores": try to use scaled image, "hires": use unscaled image |
279 // "autores": try best fitting resolution | |
280 if (dlRequest.isOption("lores")) { | |
281 loresOnly = true; | |
282 hiresOnly = false; | |
283 } else if (dlRequest.isOption("hires")) { | |
284 loresOnly = false; | |
285 hiresOnly = true; | |
286 } else if (dlRequest.isOption("autores")) { | |
287 loresOnly = false; | |
288 hiresOnly = false; | |
289 } | |
73 | 290 // operation mode: "errtxt": error message in html, "errimg": error image |
291 if (dlRequest.isOption("errtxt")) { | |
292 errorMsgHtml = true; | |
293 } else if (dlRequest.isOption("errimg")) { | |
294 errorMsgHtml = false; | |
295 } | |
296 // operation mode: "q0" - "q2": interpolation quality | |
297 if (dlRequest.isOption("q0")) { | |
298 scaleQual = 0; | |
299 } else if (dlRequest.isOption("q1")) { | |
300 scaleQual = 1; | |
301 } else if (dlRequest.isOption("q2")) { | |
302 scaleQual = 2; | |
303 } | |
1 | 304 |
73 | 305 //"big" try for all file/image actions |
306 try { | |
307 | |
149 | 308 // DocuFileset of the image to load |
309 DocuFileset fileset = null; | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
310 |
122 | 311 // new DocuInfo instance |
149 | 312 DocuInfo docuInfo = new ImageLoaderImageInfoDocuInfo(); |
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; |
149 | 350 fileset = dirCache.getFileset(loadPathName, dlRequest.getPn()); |
94 | 351 if (fileset == null) { |
352 throw new FileOpException( | |
353 "File " | |
354 + loadPathName | |
355 + "(" | |
356 + dlRequest.getPn() | |
357 + ") not found."); | |
358 } | |
359 | |
122 | 360 /* |
361 * calculate expected source image size | |
362 * | |
363 */ | |
149 | 364 ImageSize expectedSourceSize = new ImageSize(); |
122 | 365 if (scaleToFit) { |
366 double scale = (1 / Math.min(paramWW, paramWH)) * paramWS; | |
149 | 367 expectedSourceSize.setSize( |
368 (int) (paramDW * scale), | |
369 (int) (paramDH * scale)); | |
122 | 370 } else { |
371 expectedSourceSize.setSize( | |
149 | 372 (int) (paramDW * paramWS), |
373 (int) (paramDH * paramWS)); | |
100 | 374 } |
375 | |
122 | 376 /* |
377 * select a resolution | |
378 */ | |
149 | 379 if (hiresOnly) { |
380 // get first element (= highest resolution) | |
381 fileToLoad = fileset.get(0); | |
382 } else if (loresOnly) { | |
383 // enforced lores uses next smaller resolution | |
384 fileToLoad = | |
385 fileset.getNextSmaller(expectedSourceSize, docuInfo); | |
386 if (fileToLoad == null) { | |
387 // this is the smallest we have | |
388 fileToLoad = fileset.get(fileset.size() - 1); | |
389 } | |
390 } else { | |
130 | 391 // autores: use next higher resolution |
122 | 392 fileToLoad = |
393 fileset.getNextBigger(expectedSourceSize, docuInfo); | |
394 if (fileToLoad == null) { | |
130 | 395 // this is the highest we have |
122 | 396 fileToLoad = fileset.get(0); |
397 } | |
86 | 398 } |
399 util.dprintln(1, "Loading: " + fileToLoad.getFile()); | |
73 | 400 |
130 | 401 if (absoluteScale) { |
402 // get original resolution from metadata | |
403 fileset.checkMeta(); | |
404 origResX = fileset.getResX(); | |
405 origResY = fileset.getResY(); | |
139
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
406 if ((origResX == 0) || (origResY == 0)) { |
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
407 throw new ImageOpException("Missing image DPI information!"); |
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
408 } |
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
409 |
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
410 if ((paramDDPIX == 0) || (paramDDPIY == 0)) { |
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
411 throw new ImageOpException("Missing display DPI information!"); |
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
412 } |
130 | 413 } |
414 | |
86 | 415 // check the source image |
122 | 416 if (!fileToLoad.isChecked()) { |
149 | 417 docuInfo.checkFile(fileToLoad); |
122 | 418 } |
86 | 419 // get the source image type |
420 mimeType = fileToLoad.getMimetype(); | |
149 | 421 // get the source image size |
422 ImageSize imgSize = fileToLoad.getSize(); | |
423 | |
130 | 424 // decide if the image can be sent as is |
425 boolean mimetypeSendable = | |
122 | 426 mimeType.equals("image/jpeg") |
427 || mimeType.equals("image/png") | |
428 || mimeType.equals("image/gif"); | |
130 | 429 boolean imagoOptions = |
430 dlRequest.isOption("hmir") | |
431 || dlRequest.isOption("vmir") | |
432 || (paramROT != 0) | |
433 || (paramRGBM != null) | |
434 || (paramRGBA != null) | |
435 || (paramCONT != 0) | |
436 || (paramBRGT != 0); | |
139
11cfe4c89fdc
Servlet version 1.11b1 with improved original-size.
robcast
parents:
130
diff
changeset
|
437 boolean imageSendable = mimetypeSendable && !imagoOptions; |
73 | 438 |
122 | 439 /* if not autoRes and image smaller than requested |
147 | 440 * size then send as is. |
441 * if autoRes and image has requested size then send as is. | |
100 | 442 * if not autoScale and not scaleToFit nor cropToFit |
122 | 443 * then send as is (mo=file) |
73 | 444 */ |
149 | 445 if ((loresOnly |
122 | 446 && imageSendable |
149 | 447 && fileToLoad.getSize().isSmallerThan(expectedSourceSize)) |
448 || (!(loresOnly || hiresOnly) && fileToLoad.getSize().fitsIn(expectedSourceSize)) | |
449 || sendFile) { | |
73 | 450 |
451 util.dprintln(1, "Sending File as is."); | |
1 | 452 |
86 | 453 servletOp.sendFile(fileToLoad.getFile(), response); |
1 | 454 |
73 | 455 util.dprintln( |
456 1, | |
457 "Done in " | |
458 + (System.currentTimeMillis() - startTime) | |
459 + "ms"); | |
460 return; | |
461 } | |
1 | 462 |
149 | 463 // new DocuImage instance |
464 DocuImage docuImage = dlConfig.getDocuImageInstance(); | |
465 if (docuImage == null) { | |
466 throw new ImageOpException("Unable to load DocuImage class!"); | |
467 } | |
468 | |
469 // set interpolation quality | |
470 docuImage.setQuality(scaleQual); | |
471 | |
73 | 472 /* |
473 * crop and scale the image | |
474 */ | |
1 | 475 |
149 | 476 util.dprintln( |
477 2, | |
478 "IMG: " + imgSize.getWidth() + "x" + imgSize.getHeight()); | |
73 | 479 util.dprintln( |
480 2, | |
481 "time " + (System.currentTimeMillis() - startTime) + "ms"); | |
1 | 482 |
73 | 483 // coordinates and scaling |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
484 double areaXoff; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
485 double areaYoff; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
486 double areaWidth; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
487 double areaHeight; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
488 double scaleX; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
489 double scaleY; |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
490 double scaleXY; |
1 | 491 |
100 | 492 // coordinates using Java2D |
493 // image size in pixels | |
494 Rectangle2D imgBounds = | |
149 | 495 new Rectangle2D.Double( |
496 0, | |
497 0, | |
498 imgSize.getWidth(), | |
499 imgSize.getHeight()); | |
100 | 500 // user window area in [0,1] coordinates |
501 Rectangle2D relUserArea = | |
502 new Rectangle2D.Double(paramWX, paramWY, paramWW, paramWH); | |
503 // transform from relative [0,1] to image coordinates. | |
504 AffineTransform imgTrafo = | |
149 | 505 AffineTransform.getScaleInstance( |
506 imgSize.getWidth(), | |
507 imgSize.getHeight()); | |
85 | 508 // transform user coordinate area to image coordinate area |
100 | 509 Rectangle2D userImgArea = |
510 imgTrafo.createTransformedShape(relUserArea).getBounds2D(); | |
511 | |
512 // calculate scaling factors based on inner user area | |
73 | 513 if (scaleToFit) { |
100 | 514 areaWidth = userImgArea.getWidth(); |
515 areaHeight = userImgArea.getHeight(); | |
73 | 516 scaleX = paramDW / areaWidth * paramWS; |
517 scaleY = paramDH / areaHeight * paramWS; | |
518 scaleXY = (scaleX > scaleY) ? scaleY : scaleX; | |
100 | 519 } else if (absoluteScale) { |
520 // absolute scale | |
130 | 521 scaleX = paramDDPIX / origResX; |
522 scaleY = paramDDPIY / origResY; | |
523 // currently only same scale :-( | |
524 scaleXY = scaleX; | |
525 areaWidth = paramDW / scaleXY * paramWS; | |
526 areaHeight = paramDH / scaleXY * paramWS; | |
100 | 527 // reset user area size |
528 userImgArea.setRect( | |
529 userImgArea.getX(), | |
530 userImgArea.getY(), | |
531 areaWidth, | |
532 areaHeight); | |
73 | 533 } else { |
534 // crop to fit | |
85 | 535 areaWidth = paramDW * paramWS; |
536 areaHeight = paramDH * paramWS; | |
100 | 537 // reset user area size |
538 userImgArea.setRect( | |
539 userImgArea.getX(), | |
540 userImgArea.getY(), | |
541 areaWidth, | |
542 areaHeight); | |
73 | 543 scaleX = 1f; |
544 scaleY = 1f; | |
545 scaleXY = 1f; | |
100 | 546 } |
85 | 547 |
100 | 548 // enlarge image area for rotations to cover additional pixels |
549 Rectangle2D outerUserImgArea = userImgArea; | |
550 Rectangle2D innerUserImgArea = userImgArea; | |
551 if (wholeRotArea) { | |
552 if (paramROT != 0) { | |
553 try { | |
554 // rotate user area coordinates around center of user area | |
555 AffineTransform rotTrafo = | |
556 AffineTransform.getRotateInstance( | |
557 Math.toRadians(paramROT), | |
558 userImgArea.getCenterX(), | |
559 userImgArea.getCenterY()); | |
560 // get bounds from rotated end position | |
561 innerUserImgArea = | |
562 rotTrafo | |
563 .createTransformedShape(userImgArea) | |
564 .getBounds2D(); | |
565 // get bounds from back-rotated bounds | |
566 outerUserImgArea = | |
567 rotTrafo | |
568 .createInverse() | |
569 .createTransformedShape(innerUserImgArea) | |
570 .getBounds2D(); | |
571 } catch (NoninvertibleTransformException e1) { | |
572 // this shouldn't happen anyway | |
573 e1.printStackTrace(); | |
574 } | |
575 } | |
73 | 576 } |
1 | 577 |
73 | 578 util.dprintln( |
579 1, | |
580 "Scale " | |
581 + scaleXY | |
582 + "(" | |
583 + scaleX | |
584 + "," | |
585 + scaleY | |
586 + ") on " | |
100 | 587 + outerUserImgArea); |
1 | 588 |
73 | 589 // clip area at the image border |
100 | 590 outerUserImgArea = outerUserImgArea.createIntersection(imgBounds); |
85 | 591 |
100 | 592 areaWidth = outerUserImgArea.getWidth(); |
593 areaHeight = outerUserImgArea.getHeight(); | |
594 areaXoff = outerUserImgArea.getX(); | |
595 areaYoff = outerUserImgArea.getY(); | |
1 | 596 |
73 | 597 util.dprintln( |
598 2, | |
85 | 599 "crop: " |
73 | 600 + areaXoff |
601 + "," | |
602 + areaYoff | |
603 + " " | |
604 + areaWidth | |
605 + "x" | |
606 + areaHeight); | |
1 | 607 |
73 | 608 // check image parameters sanity |
609 if ((areaWidth < 1) | |
610 || (areaHeight < 1) | |
611 || (scaleXY * areaWidth < 2) | |
612 || (scaleXY * areaHeight < 2)) { | |
613 util.dprintln(1, "ERROR: invalid scale parameter set!"); | |
614 throw new ImageOpException("Invalid scale parameter set!"); | |
615 } | |
1 | 616 |
85 | 617 /* |
618 * crop and scale image | |
619 */ | |
620 | |
621 // use subimage loading if possible | |
622 if (docuImage.isSubimageSupported()) { | |
623 System.out.println( | |
624 "Subimage: scale " + scaleXY + " = " + (1 / scaleXY)); | |
625 double subf = 1d; | |
626 double subsamp = 1d; | |
627 if (scaleXY < 1) { | |
628 subf = 1 / scaleXY; | |
140 | 629 // for higher quality reduce subsample factor by minSubsample |
630 if (scaleQual > 0) { | |
631 subsamp = | |
632 Math.max( | |
633 Math.floor(subf / dlConfig.getMinSubsample()), | |
634 1d); | |
635 } else { | |
636 subsamp = Math.floor(subf); | |
637 } | |
85 | 638 scaleXY = subsamp / subf; |
639 System.out.println( | |
640 "Using subsampling: " + subsamp + " rest " + scaleXY); | |
641 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
642 |
85 | 643 docuImage.loadSubimage( |
149 | 644 fileToLoad, |
100 | 645 outerUserImgArea.getBounds(), |
85 | 646 (int) subsamp); |
647 | |
648 System.out.println( | |
649 "SUBSAMP: " | |
650 + subsamp | |
651 + " -> " | |
652 + docuImage.getWidth() | |
653 + "x" | |
654 + docuImage.getHeight()); | |
655 | |
149 | 656 docuImage.scale(scaleXY, scaleXY); |
85 | 657 |
658 } else { | |
100 | 659 // else load and crop the whole file |
149 | 660 docuImage.loadImage(fileToLoad); |
85 | 661 docuImage.crop( |
662 (int) areaXoff, | |
663 (int) areaYoff, | |
664 (int) areaWidth, | |
665 (int) areaHeight); | |
666 | |
149 | 667 docuImage.scale(scaleXY, scaleXY); |
85 | 668 } |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
669 |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
670 // mirror image |
100 | 671 // operation mode: "hmir": mirror horizontally, "vmir": mirror vertically |
672 if (dlRequest.isOption("hmir")) { | |
673 docuImage.mirror(0); | |
674 } | |
675 if (dlRequest.isOption("vmir")) { | |
676 docuImage.mirror(90); | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
677 } |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
678 |
100 | 679 // rotate image |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
680 if (paramROT != 0) { |
94 | 681 docuImage.rotate(paramROT); |
100 | 682 if (wholeRotArea) { |
683 // crop to the inner bounding box | |
684 double xcrop = | |
685 docuImage.getWidth() | |
686 - innerUserImgArea.getWidth() * scaleXY; | |
687 double ycrop = | |
688 docuImage.getHeight() | |
689 - innerUserImgArea.getHeight() * scaleXY; | |
690 if ((xcrop > 0) || (ycrop > 0)) { | |
691 // only crop smaller | |
692 xcrop = (xcrop > 0) ? xcrop : 0; | |
693 ycrop = (ycrop > 0) ? ycrop : 0; | |
694 // crop image | |
695 docuImage.crop( | |
696 (int) (xcrop / 2), | |
697 (int) (ycrop / 2), | |
698 (int) (docuImage.getWidth() - xcrop), | |
699 (int) (docuImage.getHeight() - ycrop)); | |
700 } | |
701 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
702 |
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
703 } |
1 | 704 |
86 | 705 // color modification |
706 if ((paramRGBM != null) || (paramRGBA != null)) { | |
707 // make shure we actually have two arrays | |
708 if (paramRGBM == null) { | |
709 paramRGBM = new float[3]; | |
710 } | |
711 if (paramRGBA == null) { | |
712 paramRGBA = new float[3]; | |
713 } | |
100 | 714 // calculate "contrast" values (c=2^x) |
86 | 715 float[] mult = new float[3]; |
716 for (int i = 0; i < 3; i++) { | |
94 | 717 mult[i] = (float) Math.pow(2, (double) paramRGBM[i]); |
86 | 718 } |
719 docuImage.enhanceRGB(mult, paramRGBA); | |
720 } | |
94 | 721 |
100 | 722 // contrast and brightness enhancement |
723 if ((paramCONT != 0) || (paramBRGT != 0)) { | |
724 double mult = Math.pow(2, paramCONT); | |
725 docuImage.enhance((float) mult, (float) paramBRGT); | |
726 } | |
727 | |
73 | 728 util.dprintln( |
729 2, | |
730 "time " + (System.currentTimeMillis() - startTime) + "ms"); | |
731 | |
732 /* | |
733 * write the resulting image | |
734 */ | |
1 | 735 |
73 | 736 // setup output -- if source is JPG then dest will be JPG else it's PNG |
100 | 737 if (mimeType.equals("image/jpeg") |
738 || mimeType.equals("image/jp2")) { | |
739 mimeType = "image/jpeg"; | |
740 } else { | |
73 | 741 mimeType = "image/png"; |
742 } | |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
743 response.setContentType(mimeType); |
1 | 744 |
73 | 745 // write the image |
79
63c8186455c1
Servlet version 1.6b. Further cleanup and new functionality:
robcast
parents:
73
diff
changeset
|
746 docuImage.writeImage(mimeType, response.getOutputStream()); |
1 | 747 |
73 | 748 util.dprintln( |
749 1, | |
750 "Done in " + (System.currentTimeMillis() - startTime) + "ms"); | |
751 | |
752 /* | |
753 * error handling | |
754 */ | |
1 | 755 |
73 | 756 } // end of "big" try |
757 catch (FileOpException e) { | |
758 util.dprintln(1, "ERROR: File IO Error: " + e); | |
759 try { | |
760 if (errorMsgHtml) { | |
761 ServletOps.htmlMessage( | |
762 "ERROR: File IO Error: " + e, | |
763 response); | |
764 } else { | |
765 servletOp.sendFile( | |
766 new File(dlConfig.getErrorImgFileName()), | |
767 response); | |
768 } | |
769 } catch (FileOpException ex) { | |
770 } // so we don't get a loop | |
771 } catch (AuthOpException e) { | |
772 util.dprintln(1, "ERROR: Authorization error: " + e); | |
773 try { | |
774 if (errorMsgHtml) { | |
775 ServletOps.htmlMessage( | |
776 "ERROR: Authorization error: " + e, | |
777 response); | |
778 } else { | |
779 servletOp.sendFile( | |
780 new File(dlConfig.getErrorImgFileName()), | |
781 response); | |
782 } | |
783 } catch (FileOpException ex) { | |
784 } // so we don't get a loop | |
785 } catch (ImageOpException e) { | |
786 util.dprintln(1, "ERROR: Image Error: " + e); | |
787 try { | |
788 if (errorMsgHtml) { | |
789 ServletOps.htmlMessage( | |
790 "ERROR: Image Operation Error: " + e, | |
791 response); | |
792 } else { | |
793 servletOp.sendFile( | |
794 new File(dlConfig.getErrorImgFileName()), | |
795 response); | |
796 } | |
797 } catch (FileOpException ex) { | |
798 } // so we don't get a loop | |
799 } catch (RuntimeException e) { | |
800 // JAI likes to throw RuntimeExceptions ;-( | |
801 util.dprintln(1, "ERROR: Other Image Error: " + e); | |
802 try { | |
803 if (errorMsgHtml) { | |
804 ServletOps.htmlMessage( | |
805 "ERROR: Other Image Operation Error: " + e, | |
806 response); | |
807 } else { | |
808 servletOp.sendFile( | |
809 new File(dlConfig.getErrorImgFileName()), | |
810 response); | |
811 } | |
812 } catch (FileOpException ex) { | |
813 } // so we don't get a loop | |
149 | 814 |
73 | 815 } |
816 } | |
1 | 817 |
73 | 818 } //Scaler class |