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