/src/leptonica/src/scale1.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file scale1.c |
29 | | * <pre> |
30 | | * Top-level scaling |
31 | | * PIX *pixScale() |
32 | | * PIX *pixScaleToSizeRel() |
33 | | * PIX *pixScaleToSize() |
34 | | * PIX *pixScaleToResolution() |
35 | | * PIX *pixScaleGeneral() |
36 | | * |
37 | | * Linearly interpreted (usually up-) scaling |
38 | | * PIX *pixScaleLI() |
39 | | * PIX *pixScaleColorLI() |
40 | | * PIX *pixScaleColor2xLI() |
41 | | * PIX *pixScaleColor4xLI() |
42 | | * PIX *pixScaleGrayLI() |
43 | | * PIX *pixScaleGray2xLI() |
44 | | * PIX *pixScaleGray4xLI() |
45 | | * |
46 | | * Upscale 2x followed by binarization |
47 | | * PIX *pixScaleGray2xLIThresh() |
48 | | * PIX *pixScaleGray2xLIDither() |
49 | | * |
50 | | * Upscale 4x followed by binarization |
51 | | * PIX *pixScaleGray4xLIThresh() |
52 | | * PIX *pixScaleGray4xLIDither() |
53 | | * |
54 | | * Scaling by closest pixel sampling |
55 | | * PIX *pixScaleBySampling() |
56 | | * PIX *pixScaleBySamplingWithShift() |
57 | | * PIX *pixScaleBySamplingToSize() |
58 | | * PIX *pixScaleByIntSampling() |
59 | | * |
60 | | * Fast integer factor subsampling RGB to gray and to binary |
61 | | * PIX *pixScaleRGBToGrayFast() |
62 | | * PIX *pixScaleRGBToBinaryFast() |
63 | | * PIX *pixScaleGrayToBinaryFast() |
64 | | * |
65 | | * Downscaling with (antialias) smoothing |
66 | | * PIX *pixScaleSmooth() |
67 | | * PIX *pixScaleSmoothToSize() |
68 | | * PIX *pixScaleRGBToGray2() [special 2x reduction to gray] |
69 | | * |
70 | | * Downscaling with (antialias) area mapping |
71 | | * PIX *pixScaleAreaMap() |
72 | | * PIX *pixScaleAreaMap2() |
73 | | * PIX *pixScaleAreaMapToSize() |
74 | | * |
75 | | * Binary scaling by closest pixel sampling |
76 | | * PIX *pixScaleBinary() |
77 | | * PIX *pixScaleBinaryWithShift() |
78 | | * |
79 | | * Low-level static functions: |
80 | | * |
81 | | * Color (interpolated) scaling: general case |
82 | | * static void scaleColorLILow() |
83 | | * |
84 | | * Grayscale (interpolated) scaling: general case |
85 | | * static void scaleGrayLILow() |
86 | | * |
87 | | * Color (interpolated) scaling: 2x upscaling |
88 | | * static void scaleColor2xLILow() |
89 | | * static void scaleColor2xLILineLow() |
90 | | * |
91 | | * Grayscale (interpolated) scaling: 2x upscaling |
92 | | * static void scaleGray2xLILow() |
93 | | * static void scaleGray2xLILineLow() |
94 | | * |
95 | | * Grayscale (interpolated) scaling: 4x upscaling |
96 | | * static void scaleGray4xLILow() |
97 | | * static void scaleGray4xLILineLow() |
98 | | * |
99 | | * Grayscale and color scaling by closest pixel sampling |
100 | | * static l_int32 scaleBySamplingLow() |
101 | | * |
102 | | * Color and grayscale downsampling with (antialias) lowpass filter |
103 | | * static l_int32 scaleSmoothLow() |
104 | | * static void scaleRGBToGray2Low() |
105 | | * |
106 | | * Color and grayscale downsampling with (antialias) area mapping |
107 | | * static l_int32 scaleColorAreaMapLow() |
108 | | * static l_int32 scaleGrayAreaMapLow() |
109 | | * static l_int32 scaleAreaMapLow2() |
110 | | * |
111 | | * Binary scaling by closest pixel sampling |
112 | | * static l_int32 scaleBinaryLow() |
113 | | * </pre> |
114 | | */ |
115 | | |
116 | | #ifdef HAVE_CONFIG_H |
117 | | #include <config_auto.h> |
118 | | #endif /* HAVE_CONFIG_H */ |
119 | | |
120 | | #include <string.h> |
121 | | #include "allheaders.h" |
122 | | |
123 | | static void scaleColorLILow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
124 | | l_int32 wpld, l_uint32 *datas, l_int32 ws, |
125 | | l_int32 hs, l_int32 wpls); |
126 | | static void scaleGrayLILow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
127 | | l_int32 wpld, l_uint32 *datas, l_int32 ws, |
128 | | l_int32 hs, l_int32 wpls); |
129 | | static void scaleColor2xLILow(l_uint32 *datad, l_int32 wpld, l_uint32 *datas, |
130 | | l_int32 ws, l_int32 hs, l_int32 wpls); |
131 | | static void scaleColor2xLILineLow(l_uint32 *lined, l_int32 wpld, |
132 | | l_uint32 *lines, l_int32 ws, l_int32 wpls, |
133 | | l_int32 lastlineflag); |
134 | | static void scaleGray2xLILow(l_uint32 *datad, l_int32 wpld, l_uint32 *datas, |
135 | | l_int32 ws, l_int32 hs, l_int32 wpls); |
136 | | static void scaleGray2xLILineLow(l_uint32 *lined, l_int32 wpld, |
137 | | l_uint32 *lines, l_int32 ws, l_int32 wpls, |
138 | | l_int32 lastlineflag); |
139 | | static void scaleGray4xLILow(l_uint32 *datad, l_int32 wpld, l_uint32 *datas, |
140 | | l_int32 ws, l_int32 hs, l_int32 wpls); |
141 | | static void scaleGray4xLILineLow(l_uint32 *lined, l_int32 wpld, |
142 | | l_uint32 *lines, l_int32 ws, l_int32 wpls, |
143 | | l_int32 lastlineflag); |
144 | | static l_int32 scaleBySamplingLow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
145 | | l_int32 wpld, l_uint32 *datas, l_int32 ws, |
146 | | l_int32 hs, l_int32 d, l_int32 wpls, |
147 | | l_float32 shiftx, l_float32 shifty); |
148 | | static l_int32 scaleSmoothLow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
149 | | l_int32 wpld, l_uint32 *datas, l_int32 ws, |
150 | | l_int32 hs, l_int32 d, l_int32 wpls, |
151 | | l_int32 size); |
152 | | static void scaleRGBToGray2Low(l_uint32 *datad, l_int32 wd, l_int32 hd, |
153 | | l_int32 wpld, l_uint32 *datas, l_int32 wpls, |
154 | | l_float32 rwt, l_float32 gwt, l_float32 bwt); |
155 | | static void scaleColorAreaMapLow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
156 | | l_int32 wpld, l_uint32 *datas, l_int32 ws, |
157 | | l_int32 hs, l_int32 wpls); |
158 | | static void scaleGrayAreaMapLow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
159 | | l_int32 wpld, l_uint32 *datas, l_int32 ws, |
160 | | l_int32 hs, l_int32 wpls); |
161 | | static void scaleAreaMapLow2(l_uint32 *datad, l_int32 wd, l_int32 hd, |
162 | | l_int32 wpld, l_uint32 *datas, l_int32 d, |
163 | | l_int32 wpls); |
164 | | static l_int32 scaleBinaryLow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
165 | | l_int32 wpld, l_uint32 *datas, l_int32 ws, |
166 | | l_int32 hs, l_int32 wpls, |
167 | | l_float32 shiftx, l_float32 shifty); |
168 | | |
169 | | #ifndef NO_CONSOLE_IO |
170 | | #define DEBUG_OVERFLOW 0 |
171 | | #define DEBUG_UNROLLING 0 |
172 | | #endif /* ~NO_CONSOLE_IO */ |
173 | | |
174 | | /*------------------------------------------------------------------* |
175 | | * Top level scaling dispatcher * |
176 | | *------------------------------------------------------------------*/ |
177 | | /*! |
178 | | * \brief pixScale() |
179 | | * |
180 | | * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp |
181 | | * \param[in] scalex, scaley |
182 | | * \return pixd, or NULL on error |
183 | | * |
184 | | * This function scales 32 bpp RGB; 2, 4 or 8 bpp palette color; |
185 | | * 2, 4, 8 or 16 bpp gray; and binary images. |
186 | | * |
187 | | * When the input has palette color, the colormap is removed and |
188 | | * the result is either 8 bpp gray or 32 bpp RGB, depending on whether |
189 | | * the colormap has color entries. Images with 2, 4 or 16 bpp are |
190 | | * converted to 8 bpp. |
191 | | * |
192 | | * Because pixScale is meant to be a very simple interface to a |
193 | | * number of scaling functions, including the use of unsharp masking, |
194 | | * the type of scaling and the sharpening parameters are chosen |
195 | | * by default. Grayscale and color images are scaled using one |
196 | | * of five methods, depending on the scale factors: |
197 | | * 1. antialiased subsampling (lowpass filtering followed by |
198 | | * subsampling, implemented by convolution, for tiny scale factors: |
199 | | * min(scalex, scaley) < 0.02. |
200 | | * 2. antialiased subsampling (implemented by area mapping, for |
201 | | * small scale factors: |
202 | | * max(scalex, scaley) < 0.2 and min(scalex, scaley) >= 0.02. |
203 | | * 3. antialiased subsampling with sharpening, for scale factors |
204 | | * between 0.2 and 0.7 |
205 | | * 4. linear interpolation with sharpening, for scale factors between |
206 | | * 0.7 and 1.4 |
207 | | * 5. linear interpolation without sharpening, for scale factors >= 1.4. |
208 | | * |
209 | | * One could use subsampling for scale factors very close to 1.0, |
210 | | * because it preserves sharp edges. Linear interpolation blurs |
211 | | * edges because the dest pixels will typically straddle two src edge |
212 | | * pixels. Subsmpling removes entire columns and rows, so the edge is |
213 | | * not blurred. However, there are two reasons for not doing this. |
214 | | * First, it moves edges, so that a straight line at a large angle to |
215 | | * both horizontal and vertical will have noticeable kinks where |
216 | | * horizontal and vertical rasters are removed. Second, although it |
217 | | * is very fast, you get good results on sharp edges by applying |
218 | | * a sharpening filter. |
219 | | * |
220 | | * For images with sharp edges, sharpening substantially improves the |
221 | | * image quality for scale factors between about 0.2 and about 2.0. |
222 | | * pixScale uses a small amount of sharpening by default because |
223 | | * it strengthens edge pixels that are weak due to anti-aliasing. |
224 | | * The default sharpening factors are: |
225 | | * * for scaling factors < 0.7: sharpfract = 0.2 sharpwidth = 1 |
226 | | * * for scaling factors >= 0.7: sharpfract = 0.4 sharpwidth = 2 |
227 | | * The cases where the sharpening halfwidth is 1 or 2 have special |
228 | | * implementations and are about twice as fast as the general case. |
229 | | * |
230 | | * However, sharpening is computationally expensive, and one needs |
231 | | * to consider the speed-quality tradeoff: |
232 | | * * For upscaling of RGB images, linear interpolation plus default |
233 | | * sharpening is about 5 times slower than upscaling alone. |
234 | | * * For downscaling, area mapping plus default sharpening is |
235 | | * about 10 times slower than downscaling alone. |
236 | | * When the scale factor is larger than 1.4, the cost of sharpening, |
237 | | * which is proportional to image area, is very large compared to the |
238 | | * incremental quality improvement, so we cut off the default use of |
239 | | * sharpening at 1.4. Thus, for scale factors greater than 1.4, |
240 | | * pixScale only does linear interpolation. |
241 | | * |
242 | | * In many situations you will get a satisfactory result by scaling |
243 | | * without sharpening: call pixScaleGeneral with %sharpfract = 0.0. |
244 | | * Alternatively, if you wish to sharpen but not use the default |
245 | | * value, first call pixScaleGeneral with %sharpfract = 0.0, and |
246 | | * then sharpen explicitly using pixUnsharpMasking. |
247 | | * |
248 | | * Binary images are scaled to binary by sampling the closest pixel, |
249 | | * without any low-pass filtering averaging of neighboring pixels. |
250 | | * This will introduce aliasing for reductions. Aliasing can be |
251 | | * prevented by using pixScaleToGray instead. |
252 | | */ |
253 | | PIX * |
254 | | pixScale(PIX *pixs, |
255 | | l_float32 scalex, |
256 | | l_float32 scaley) |
257 | 12.4k | { |
258 | 12.4k | l_int32 sharpwidth; |
259 | 12.4k | l_float32 maxscale, sharpfract; |
260 | | |
261 | 12.4k | if (!pixs) |
262 | 544 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
263 | | |
264 | | /* Reduce the default sharpening factors by 2 if maxscale < 0.7 */ |
265 | 11.9k | maxscale = L_MAX(scalex, scaley); |
266 | 11.9k | sharpfract = (maxscale < 0.7) ? 0.2f : 0.4f; |
267 | 11.9k | sharpwidth = (maxscale < 0.7) ? 1 : 2; |
268 | | |
269 | 11.9k | return pixScaleGeneral(pixs, scalex, scaley, sharpfract, sharpwidth); |
270 | 12.4k | } |
271 | | |
272 | | |
273 | | /*! |
274 | | * \brief pixScaleToSizeRel() |
275 | | * |
276 | | * \param[in] pixs |
277 | | * \param[in] delw change in width, in pixels; 0 means no change |
278 | | * \param[in] delh change in height, in pixels; 0 means no change |
279 | | * \return pixd, or NULL on error |
280 | | */ |
281 | | PIX * |
282 | | pixScaleToSizeRel(PIX *pixs, |
283 | | l_int32 delw, |
284 | | l_int32 delh) |
285 | 0 | { |
286 | 0 | l_int32 w, h, wd, hd; |
287 | |
|
288 | 0 | if (!pixs) |
289 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
290 | | |
291 | 0 | if (delw == 0 && delh == 0) |
292 | 0 | return pixCopy(NULL, pixs); |
293 | | |
294 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
295 | 0 | wd = w + delw; |
296 | 0 | hd = h + delh; |
297 | 0 | if (wd <= 0 || hd <= 0) |
298 | 0 | return (PIX *)ERROR_PTR("pix dimension reduced to 0", __func__, NULL); |
299 | | |
300 | 0 | return pixScaleToSize(pixs, wd, hd); |
301 | 0 | } |
302 | | |
303 | | |
304 | | /*! |
305 | | * \brief pixScaleToSize() |
306 | | * |
307 | | * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp |
308 | | * \param[in] wd target width; use 0 if using height as target |
309 | | * \param[in] hd target height; use 0 if using width as target |
310 | | * \return pixd, or NULL on error |
311 | | * |
312 | | * <pre> |
313 | | * Notes: |
314 | | * (1) The output scaled image has the dimension(s) you specify: |
315 | | * * To specify the width with isotropic scaling, set %hd = 0. |
316 | | * * To specify the height with isotropic scaling, set %wd = 0. |
317 | | * * If both %wd and %hd are specified, the image is scaled |
318 | | * (in general, anisotropically) to that size. |
319 | | * * It is an error to set both %wd and %hd to 0. |
320 | | * </pre> |
321 | | */ |
322 | | PIX * |
323 | | pixScaleToSize(PIX *pixs, |
324 | | l_int32 wd, |
325 | | l_int32 hd) |
326 | 0 | { |
327 | 0 | l_int32 w, h; |
328 | 0 | l_float32 scalex, scaley; |
329 | |
|
330 | 0 | if (!pixs) |
331 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
332 | 0 | if (wd <= 0 && hd <= 0) |
333 | 0 | return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL); |
334 | | |
335 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
336 | 0 | if (wd <= 0) { |
337 | 0 | scaley = (l_float32)hd / (l_float32)h; |
338 | 0 | scalex = scaley; |
339 | 0 | } else if (hd <= 0) { |
340 | 0 | scalex = (l_float32)wd / (l_float32)w; |
341 | 0 | scaley = scalex; |
342 | 0 | } else { |
343 | 0 | scalex = (l_float32)wd / (l_float32)w; |
344 | 0 | scaley = (l_float32)hd / (l_float32)h; |
345 | 0 | } |
346 | |
|
347 | 0 | return pixScale(pixs, scalex, scaley); |
348 | 0 | } |
349 | | |
350 | | |
351 | | /*! |
352 | | * \brief pixScaleToResolution() |
353 | | * |
354 | | * \param[in] pixs |
355 | | * \param[in] target desired resolution |
356 | | * \param[in] assumed assumed resolution if not defined; typ. 300. |
357 | | * \param[out] pscalefact [optional] actual scaling factor used |
358 | | * \return pixd, or NULL on error |
359 | | */ |
360 | | PIX * |
361 | | pixScaleToResolution(PIX *pixs, |
362 | | l_float32 target, |
363 | | l_float32 assumed, |
364 | | l_float32 *pscalefact) |
365 | 0 | { |
366 | 0 | l_int32 xres; |
367 | 0 | l_float32 factor; |
368 | |
|
369 | 0 | if (pscalefact) *pscalefact = 1.0; |
370 | 0 | if (!pixs) |
371 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
372 | 0 | if (target <= 0) |
373 | 0 | return (PIX *)ERROR_PTR("target resolution <= 0", __func__, NULL); |
374 | | |
375 | 0 | xres = pixGetXRes(pixs); |
376 | 0 | if (xres <= 0) { |
377 | 0 | if (assumed == 0) |
378 | 0 | return pixCopy(NULL, pixs); |
379 | 0 | xres = assumed; |
380 | 0 | } |
381 | 0 | factor = target / (l_float32)xres; |
382 | 0 | if (pscalefact) *pscalefact = factor; |
383 | |
|
384 | 0 | return pixScale(pixs, factor, factor); |
385 | 0 | } |
386 | | |
387 | | |
388 | | /*! |
389 | | * \brief pixScaleGeneral() |
390 | | * |
391 | | * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp |
392 | | * \param[in] scalex must be > 0.0 |
393 | | * \param[in] scaley must be > 0.0 |
394 | | * \param[in] sharpfract use 0.0 to skip sharpening |
395 | | * \param[in] sharpwidth halfwidth of low-pass filter; typ. 1 or 2 |
396 | | * \return pixd, or NULL on error |
397 | | * |
398 | | * <pre> |
399 | | * Notes: |
400 | | * (1) See pixScale() for usage. |
401 | | * (2) This interface may change in the future, as other special |
402 | | * cases are added. |
403 | | * (3) For tiny scaling factors |
404 | | * minscale < 0.02: use a simple lowpass filter |
405 | | * (4) The actual sharpening factors used depend on the maximum |
406 | | * of the two scale factors (maxscale): |
407 | | * maxscale <= 0.2: no sharpening |
408 | | * 0.2 < maxscale < 1.4: uses the input parameters |
409 | | * maxscale >= 1.4: no sharpening |
410 | | * (5) To avoid sharpening for grayscale and color images with |
411 | | * scaling factors between 0.2 and 1.4, call this function |
412 | | * with %sharpfract == 0.0. |
413 | | * (6) To use arbitrary sharpening in conjunction with scaling, |
414 | | * call this function with %sharpfract = 0.0, and follow this |
415 | | * with a call to pixUnsharpMasking() with your chosen parameters. |
416 | | * </pre> |
417 | | */ |
418 | | PIX * |
419 | | pixScaleGeneral(PIX *pixs, |
420 | | l_float32 scalex, |
421 | | l_float32 scaley, |
422 | | l_float32 sharpfract, |
423 | | l_int32 sharpwidth) |
424 | 11.9k | { |
425 | 11.9k | l_int32 d; |
426 | 11.9k | l_float32 maxscale, minscale; |
427 | 11.9k | PIX *pix1, *pix2, *pixd; |
428 | | |
429 | 11.9k | if (!pixs) |
430 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
431 | 11.9k | d = pixGetDepth(pixs); |
432 | 11.9k | if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
433 | 0 | return (PIX *)ERROR_PTR("pixs not {1,2,4,8,16,32} bpp", __func__, NULL); |
434 | 11.9k | if (scalex <= 0.0 || scaley <= 0.0) |
435 | 0 | return (PIX *)ERROR_PTR("scale factor <= 0", __func__, NULL); |
436 | 11.9k | if (scalex == 1.0 && scaley == 1.0) |
437 | 0 | return pixCopy(NULL, pixs); |
438 | | |
439 | 11.9k | if (d == 1) |
440 | 11.9k | return pixScaleBinary(pixs, scalex, scaley); |
441 | | |
442 | | /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ |
443 | 0 | if ((pix1 = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) |
444 | 0 | return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); |
445 | | |
446 | | /* Scale (up or down) */ |
447 | 0 | d = pixGetDepth(pix1); |
448 | 0 | maxscale = L_MAX(scalex, scaley); |
449 | 0 | minscale = L_MIN(scalex, scaley); |
450 | 0 | if (maxscale < 0.7) { /* use low-pass filter for anti-aliasing */ |
451 | 0 | if (minscale < 0.02) { /* whole-pixel low-pass filter */ |
452 | 0 | pix2 = pixScaleSmooth(pix1, scalex, scaley); |
453 | 0 | } else { /* fractional pixel low-pass filter */ |
454 | 0 | pix2 = pixScaleAreaMap(pix1, scalex, scaley); |
455 | 0 | } |
456 | 0 | if (maxscale > 0.2 && sharpfract > 0.0 && sharpwidth > 0) { |
457 | 0 | pixd = pixUnsharpMasking(pix2, sharpwidth, sharpfract); |
458 | 0 | } else { |
459 | 0 | pixd = pixClone(pix2); |
460 | 0 | } |
461 | 0 | } else { /* use linear interpolation */ |
462 | 0 | if (d == 8) { |
463 | 0 | pix2 = pixScaleGrayLI(pix1, scalex, scaley); |
464 | 0 | } else { /* d == 32 */ |
465 | 0 | pix2 = pixScaleColorLI(pix1, scalex, scaley); |
466 | 0 | } |
467 | 0 | if (maxscale < 1.4 && sharpfract > 0.0 && sharpwidth > 0) { |
468 | 0 | pixd = pixUnsharpMasking(pix2, sharpwidth, sharpfract); |
469 | 0 | } else { |
470 | 0 | pixd = pixClone(pix2); |
471 | 0 | } |
472 | 0 | } |
473 | |
|
474 | 0 | pixDestroy(&pix1); |
475 | 0 | pixDestroy(&pix2); |
476 | 0 | pixCopyText(pixd, pixs); |
477 | 0 | pixCopyInputFormat(pixd, pixs); |
478 | 0 | return pixd; |
479 | 0 | } |
480 | | |
481 | | |
482 | | /*------------------------------------------------------------------* |
483 | | * Scaling by linear interpolation * |
484 | | *------------------------------------------------------------------*/ |
485 | | /*! |
486 | | * \brief pixScaleLI() |
487 | | * |
488 | | * \param[in] pixs 2, 4, 8 or 32 bpp; with or without colormap |
489 | | * \param[in] scalex must be >= 0.7 |
490 | | * \param[in] scaley must be >= 0.7 |
491 | | * \return pixd, or NULL on error |
492 | | * |
493 | | * <pre> |
494 | | * Notes: |
495 | | * (1) This function should only be used when the scale factors are |
496 | | * greater than or equal to 0.7, and typically greater than 1. |
497 | | * If both scale factors are smaller than 0.7, we issue a warning |
498 | | * and call pixScaleGeneral(), which will invoke area mapping |
499 | | * without sharpening. |
500 | | * (2) This works on 2, 4, 8, 16 and 32 bpp images, as well as on |
501 | | * 2, 4 and 8 bpp images that have a colormap. If there is a |
502 | | * colormap, it is removed to either gray or RGB, depending |
503 | | * on the colormap. |
504 | | * (3) This does a linear interpolation on the src image. |
505 | | * (4) It dispatches to much faster implementations for |
506 | | * the special cases of 2x and 4x expansion. |
507 | | * </pre> |
508 | | */ |
509 | | PIX * |
510 | | pixScaleLI(PIX *pixs, |
511 | | l_float32 scalex, |
512 | | l_float32 scaley) |
513 | 0 | { |
514 | 0 | l_int32 d; |
515 | 0 | l_float32 maxscale; |
516 | 0 | PIX *pixt, *pixd; |
517 | |
|
518 | 0 | if (!pixs || (pixGetDepth(pixs) == 1)) |
519 | 0 | return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", __func__, NULL); |
520 | 0 | maxscale = L_MAX(scalex, scaley); |
521 | 0 | if (maxscale < 0.7) { |
522 | 0 | L_WARNING("scaling factors < 0.7; do regular scaling\n", __func__); |
523 | 0 | return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0); |
524 | 0 | } |
525 | 0 | d = pixGetDepth(pixs); |
526 | 0 | if (d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
527 | 0 | return (PIX *)ERROR_PTR("pixs not {2,4,8,16,32} bpp", __func__, NULL); |
528 | | |
529 | | /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ |
530 | 0 | if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) |
531 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
532 | | |
533 | 0 | d = pixGetDepth(pixt); |
534 | 0 | if (d == 8) |
535 | 0 | pixd = pixScaleGrayLI(pixt, scalex, scaley); |
536 | 0 | else /* d == 32 */ |
537 | 0 | pixd = pixScaleColorLI(pixt, scalex, scaley); |
538 | |
|
539 | 0 | pixDestroy(&pixt); |
540 | 0 | pixCopyInputFormat(pixd, pixs); |
541 | 0 | return pixd; |
542 | 0 | } |
543 | | |
544 | | |
545 | | /*! |
546 | | * \brief pixScaleColorLI() |
547 | | * |
548 | | * \param[in] pixs 32 bpp, representing rgb |
549 | | * \param[in] scalex must be >= 0.7 |
550 | | * \param[in] scaley must be >= 0.7 |
551 | | * \return pixd, or NULL on error |
552 | | * |
553 | | * <pre> |
554 | | * Notes: |
555 | | * (1) If both scale factors are smaller than 0.7, we issue a warning |
556 | | * and call pixScaleGeneral(), which will invoke area mapping |
557 | | * without sharpening. This is particularly important for |
558 | | * document images with sharp edges. |
559 | | * (2) For the general case, it's about 4x faster to manipulate |
560 | | * the color pixels directly, rather than to make images |
561 | | * out of each of the 3 components, scale each component |
562 | | * using the pixScaleGrayLI(), and combine the results back |
563 | | * into an rgb image. |
564 | | * </pre> |
565 | | */ |
566 | | PIX * |
567 | | pixScaleColorLI(PIX *pixs, |
568 | | l_float32 scalex, |
569 | | l_float32 scaley) |
570 | 0 | { |
571 | 0 | l_int32 ws, hs, wpls, wd, hd, wpld; |
572 | 0 | l_uint32 *datas, *datad; |
573 | 0 | l_float32 maxscale; |
574 | 0 | PIX *pixd; |
575 | |
|
576 | 0 | if (!pixs || (pixGetDepth(pixs) != 32)) |
577 | 0 | return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); |
578 | 0 | maxscale = L_MAX(scalex, scaley); |
579 | 0 | if (maxscale < 0.7) { |
580 | 0 | L_WARNING("scaling factors < 0.7; do regular scaling\n", __func__); |
581 | 0 | return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0); |
582 | 0 | } |
583 | | |
584 | | /* Do fast special cases if possible */ |
585 | 0 | if (scalex == 1.0 && scaley == 1.0) |
586 | 0 | return pixCopy(NULL, pixs); |
587 | 0 | if (scalex == 2.0 && scaley == 2.0) |
588 | 0 | return pixScaleColor2xLI(pixs); |
589 | 0 | if (scalex == 4.0 && scaley == 4.0) |
590 | 0 | return pixScaleColor4xLI(pixs); |
591 | | |
592 | | /* General case */ |
593 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
594 | 0 | datas = pixGetData(pixs); |
595 | 0 | wpls = pixGetWpl(pixs); |
596 | 0 | wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
597 | 0 | hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
598 | 0 | if ((pixd = pixCreate(wd, hd, 32)) == NULL) |
599 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
600 | 0 | pixCopyResolution(pixd, pixs); |
601 | 0 | pixScaleResolution(pixd, scalex, scaley); |
602 | 0 | datad = pixGetData(pixd); |
603 | 0 | wpld = pixGetWpl(pixd); |
604 | 0 | scaleColorLILow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
605 | 0 | if (pixGetSpp(pixs) == 4) |
606 | 0 | pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); |
607 | |
|
608 | 0 | pixCopyInputFormat(pixd, pixs); |
609 | 0 | return pixd; |
610 | 0 | } |
611 | | |
612 | | |
613 | | /*! |
614 | | * \brief pixScaleColor2xLI() |
615 | | * |
616 | | * \param[in] pixs 32 bpp, representing rgb |
617 | | * \return pixd, or NULL on error |
618 | | * |
619 | | * <pre> |
620 | | * Notes: |
621 | | * (1) This is a special case of linear interpolated scaling, |
622 | | * for 2x upscaling. It is about 8x faster than using |
623 | | * the generic pixScaleColorLI(), and about 4x faster than |
624 | | * using the special 2x scale function pixScaleGray2xLI() |
625 | | * on each of the three components separately. |
626 | | * </pre> |
627 | | */ |
628 | | PIX * |
629 | | pixScaleColor2xLI(PIX *pixs) |
630 | 0 | { |
631 | 0 | l_int32 ws, hs, wpls, wpld; |
632 | 0 | l_uint32 *datas, *datad; |
633 | 0 | PIX *pixd; |
634 | |
|
635 | 0 | if (!pixs || (pixGetDepth(pixs) != 32)) |
636 | 0 | return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); |
637 | | |
638 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
639 | 0 | datas = pixGetData(pixs); |
640 | 0 | wpls = pixGetWpl(pixs); |
641 | 0 | if ((pixd = pixCreate(2 * ws, 2 * hs, 32)) == NULL) |
642 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
643 | 0 | pixCopyResolution(pixd, pixs); |
644 | 0 | pixScaleResolution(pixd, 2.0, 2.0); |
645 | 0 | datad = pixGetData(pixd); |
646 | 0 | wpld = pixGetWpl(pixd); |
647 | 0 | scaleColor2xLILow(datad, wpld, datas, ws, hs, wpls); |
648 | 0 | if (pixGetSpp(pixs) == 4) |
649 | 0 | pixScaleAndTransferAlpha(pixd, pixs, 2.0, 2.0); |
650 | |
|
651 | 0 | pixCopyInputFormat(pixd, pixs); |
652 | 0 | return pixd; |
653 | 0 | } |
654 | | |
655 | | |
656 | | /*! |
657 | | * \brief pixScaleColor4xLI() |
658 | | * |
659 | | * \param[in] pixs 32 bpp, representing rgb |
660 | | * \return pixd, or NULL on error |
661 | | * |
662 | | * <pre> |
663 | | * Notes: |
664 | | * (1) This is a special case of color linear interpolated scaling, |
665 | | * for 4x upscaling. It is about 3x faster than using |
666 | | * the generic pixScaleColorLI(). |
667 | | * (2) This scales each component separately, using pixScaleGray4xLI(). |
668 | | * It would be about 4x faster to inline the color code properly, |
669 | | * in analogy to scaleColor4xLILow(), and I leave this as |
670 | | * an exercise for someone who really needs it. |
671 | | * </pre> |
672 | | */ |
673 | | PIX * |
674 | | pixScaleColor4xLI(PIX *pixs) |
675 | 0 | { |
676 | 0 | PIX *pixr, *pixg, *pixb; |
677 | 0 | PIX *pixrs, *pixgs, *pixbs; |
678 | 0 | PIX *pixd; |
679 | |
|
680 | 0 | if (!pixs || (pixGetDepth(pixs) != 32)) |
681 | 0 | return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); |
682 | | |
683 | 0 | pixr = pixGetRGBComponent(pixs, COLOR_RED); |
684 | 0 | pixrs = pixScaleGray4xLI(pixr); |
685 | 0 | pixDestroy(&pixr); |
686 | 0 | pixg = pixGetRGBComponent(pixs, COLOR_GREEN); |
687 | 0 | pixgs = pixScaleGray4xLI(pixg); |
688 | 0 | pixDestroy(&pixg); |
689 | 0 | pixb = pixGetRGBComponent(pixs, COLOR_BLUE); |
690 | 0 | pixbs = pixScaleGray4xLI(pixb); |
691 | 0 | pixDestroy(&pixb); |
692 | |
|
693 | 0 | if ((pixd = pixCreateRGBImage(pixrs, pixgs, pixbs)) == NULL) { |
694 | 0 | L_ERROR("pixd not made\n", __func__); |
695 | 0 | } else { |
696 | 0 | if (pixGetSpp(pixs) == 4) |
697 | 0 | pixScaleAndTransferAlpha(pixd, pixs, 4.0, 4.0); |
698 | 0 | pixCopyInputFormat(pixd, pixs); |
699 | 0 | } |
700 | |
|
701 | 0 | pixDestroy(&pixrs); |
702 | 0 | pixDestroy(&pixgs); |
703 | 0 | pixDestroy(&pixbs); |
704 | 0 | return pixd; |
705 | 0 | } |
706 | | |
707 | | |
708 | | /*! |
709 | | * \brief pixScaleGrayLI() |
710 | | * |
711 | | * \param[in] pixs 8 bpp grayscale, no cmap |
712 | | * \param[in] scalex must be >= 0.7 |
713 | | * \param[in] scaley must be >= 0.7 |
714 | | * \return pixd, or NULL on error |
715 | | * |
716 | | * <pre> |
717 | | * Notes: |
718 | | * (1) This function is appropriate for upscaling magnification, where the |
719 | | * scale factor is > 1, as well as for a small amount of downscaling |
720 | | * reduction, with scale factor >= 0.7. If the scale factor is < 0.7, |
721 | | * the best result is obtained by area mapping. |
722 | | * (2) Here are some details: |
723 | | * - For each pixel in the dest, this does a linear |
724 | | * interpolation of 4 neighboring pixels in the src. |
725 | | * Specifically, consider the UL corner of src and |
726 | | * dest pixels. The UL corner of the dest falls within |
727 | | * a src pixel, whose four corners are the UL corners |
728 | | * of 4 adjacent src pixels. The value of the dest |
729 | | * is taken by linear interpolation using the values of |
730 | | * the four src pixels and the distance of the UL corner |
731 | | * of the dest from each corner. |
732 | | * - If the image is expanded so that the dest pixel is |
733 | | * smaller than the src pixel, such interpolation |
734 | | * is a reasonable approach. This interpolation is |
735 | | * also good for a small image reduction factor that |
736 | | * is not more than a 2x reduction. |
737 | | * - The linear interpolation algorithm for scaling is |
738 | | * identical in form to the area-mapping algorithm |
739 | | * for grayscale rotation. The latter corresponds to a |
740 | | * translation of each pixel without scaling. |
741 | | * - This function is NOT optimal if the scaling involves |
742 | | * a large reduction. If the image is significantly |
743 | | * reduced, so that the dest pixel is much larger than |
744 | | * the src pixels, this interpolation, which is over src |
745 | | * pixels only near the UL corner of the dest pixel, |
746 | | * is not going to give a good area-mapping average. |
747 | | * Because area mapping for image scaling is considerably |
748 | | * more computationally intensive than linear interpolation, |
749 | | * we choose not to use it. For large image reduction, |
750 | | * linear interpolation over adjacent src pixels |
751 | | * degenerates asymptotically to subsampling. But |
752 | | * subsampling without a low-pass pre-filter causes |
753 | | * aliasing by the nyquist theorem. To avoid aliasing, |
754 | | * a low-pass filter e.g., an averaging filter of |
755 | | * size roughly equal to the dest pixel i.e., the reduction |
756 | | * factor should be applied to the src before subsampling. |
757 | | * - As an alternative to low-pass filtering and subsampling |
758 | | * for large reduction factors, linear interpolation can |
759 | | * also be done between the widely separated src pixels in |
760 | | * which the corners of the dest pixel lie. This also is |
761 | | * not optimal, as it samples src pixels only near the |
762 | | * corners of the dest pixel, and it is not implemented. |
763 | | * </pre> |
764 | | */ |
765 | | PIX * |
766 | | pixScaleGrayLI(PIX *pixs, |
767 | | l_float32 scalex, |
768 | | l_float32 scaley) |
769 | 0 | { |
770 | 0 | l_int32 ws, hs, wpls, wd, hd, wpld; |
771 | 0 | l_uint32 *datas, *datad; |
772 | 0 | l_float32 maxscale; |
773 | 0 | PIX *pixd; |
774 | |
|
775 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
776 | 0 | return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp", |
777 | 0 | __func__, NULL); |
778 | 0 | maxscale = L_MAX(scalex, scaley); |
779 | 0 | if (maxscale < 0.7) { |
780 | 0 | L_WARNING("scaling factors < 0.7; do regular scaling\n", __func__); |
781 | 0 | return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0); |
782 | 0 | } |
783 | | |
784 | | /* Do fast special cases if possible */ |
785 | 0 | if (scalex == 1.0 && scaley == 1.0) |
786 | 0 | return pixCopy(NULL, pixs); |
787 | 0 | if (scalex == 2.0 && scaley == 2.0) |
788 | 0 | return pixScaleGray2xLI(pixs); |
789 | 0 | if (scalex == 4.0 && scaley == 4.0) |
790 | 0 | return pixScaleGray4xLI(pixs); |
791 | | |
792 | | /* General case */ |
793 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
794 | 0 | datas = pixGetData(pixs); |
795 | 0 | wpls = pixGetWpl(pixs); |
796 | 0 | wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
797 | 0 | hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
798 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
799 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
800 | 0 | pixCopyText(pixd, pixs); |
801 | 0 | pixCopyResolution(pixd, pixs); |
802 | 0 | pixCopyInputFormat(pixd, pixs); |
803 | 0 | pixScaleResolution(pixd, scalex, scaley); |
804 | 0 | datad = pixGetData(pixd); |
805 | 0 | wpld = pixGetWpl(pixd); |
806 | 0 | scaleGrayLILow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
807 | 0 | return pixd; |
808 | 0 | } |
809 | | |
810 | | |
811 | | /*! |
812 | | * \brief pixScaleGray2xLI() |
813 | | * |
814 | | * \param[in] pixs 8 bpp grayscale, not cmapped |
815 | | * \return pixd, or NULL on error |
816 | | * |
817 | | * <pre> |
818 | | * Notes: |
819 | | * (1) This is a special case of gray linear interpolated scaling, |
820 | | * for 2x upscaling. It is about 6x faster than using |
821 | | * the generic pixScaleGrayLI(). |
822 | | * </pre> |
823 | | */ |
824 | | PIX * |
825 | | pixScaleGray2xLI(PIX *pixs) |
826 | 0 | { |
827 | 0 | l_int32 ws, hs, wpls, wpld; |
828 | 0 | l_uint32 *datas, *datad; |
829 | 0 | PIX *pixd; |
830 | |
|
831 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
832 | 0 | return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp", |
833 | 0 | __func__, NULL); |
834 | | |
835 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
836 | 0 | datas = pixGetData(pixs); |
837 | 0 | wpls = pixGetWpl(pixs); |
838 | 0 | if ((pixd = pixCreate(2 * ws, 2 * hs, 8)) == NULL) |
839 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
840 | 0 | pixCopyResolution(pixd, pixs); |
841 | 0 | pixCopyInputFormat(pixd, pixs); |
842 | 0 | pixScaleResolution(pixd, 2.0, 2.0); |
843 | 0 | datad = pixGetData(pixd); |
844 | 0 | wpld = pixGetWpl(pixd); |
845 | 0 | scaleGray2xLILow(datad, wpld, datas, ws, hs, wpls); |
846 | 0 | return pixd; |
847 | 0 | } |
848 | | |
849 | | |
850 | | /*! |
851 | | * \brief pixScaleGray4xLI() |
852 | | * |
853 | | * \param[in] pixs 8 bpp grayscale, not cmapped |
854 | | * \return pixd, or NULL on error |
855 | | * |
856 | | * <pre> |
857 | | * Notes: |
858 | | * (1) This is a special case of gray linear interpolated scaling, |
859 | | * for 4x upscaling. It is about 12x faster than using |
860 | | * the generic pixScaleGrayLI(). |
861 | | * </pre> |
862 | | */ |
863 | | PIX * |
864 | | pixScaleGray4xLI(PIX *pixs) |
865 | 0 | { |
866 | 0 | l_int32 ws, hs, wpls, wpld; |
867 | 0 | l_uint32 *datas, *datad; |
868 | 0 | PIX *pixd; |
869 | |
|
870 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
871 | 0 | return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp", |
872 | 0 | __func__, NULL); |
873 | | |
874 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
875 | 0 | datas = pixGetData(pixs); |
876 | 0 | wpls = pixGetWpl(pixs); |
877 | 0 | if ((pixd = pixCreate(4 * ws, 4 * hs, 8)) == NULL) |
878 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
879 | 0 | pixCopyResolution(pixd, pixs); |
880 | 0 | pixCopyInputFormat(pixd, pixs); |
881 | 0 | pixScaleResolution(pixd, 4.0, 4.0); |
882 | 0 | datad = pixGetData(pixd); |
883 | 0 | wpld = pixGetWpl(pixd); |
884 | 0 | scaleGray4xLILow(datad, wpld, datas, ws, hs, wpls); |
885 | 0 | return pixd; |
886 | 0 | } |
887 | | |
888 | | |
889 | | /*------------------------------------------------------------------* |
890 | | * Scale 2x followed by binarization * |
891 | | *------------------------------------------------------------------*/ |
892 | | /*! |
893 | | * \brief pixScaleGray2xLIThresh() |
894 | | * |
895 | | * \param[in] pixs 8 bpp, not cmapped |
896 | | * \param[in] thresh between 0 and 256 |
897 | | * \return pixd 1 bpp, or NULL on error |
898 | | * |
899 | | * <pre> |
900 | | * Notes: |
901 | | * (1) This does 2x upscale on pixs, using linear interpolation, |
902 | | * followed by thresholding to binary. |
903 | | * (2) Buffers are used to avoid making a large grayscale image. |
904 | | * </pre> |
905 | | */ |
906 | | PIX * |
907 | | pixScaleGray2xLIThresh(PIX *pixs, |
908 | | l_int32 thresh) |
909 | 0 | { |
910 | 0 | l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
911 | 0 | l_uint32 *datas, *datad, *lines, *lined, *lineb; |
912 | 0 | PIX *pixd; |
913 | |
|
914 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
915 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
916 | 0 | __func__, NULL); |
917 | 0 | if (thresh < 0 || thresh > 256) |
918 | 0 | return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]", |
919 | 0 | __func__, NULL); |
920 | | |
921 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
922 | 0 | wd = 2 * ws; |
923 | 0 | hd = 2 * hs; |
924 | 0 | hsm = hs - 1; |
925 | 0 | datas = pixGetData(pixs); |
926 | 0 | wpls = pixGetWpl(pixs); |
927 | | |
928 | | /* Make line buffer for 2 lines of virtual intermediate image */ |
929 | 0 | wplb = (wd + 3) / 4; |
930 | 0 | if ((lineb = (l_uint32 *)LEPT_CALLOC(2 * wplb, sizeof(l_uint32))) == NULL) |
931 | 0 | return (PIX *)ERROR_PTR("lineb not made", __func__, NULL); |
932 | | |
933 | | /* Make dest binary image */ |
934 | 0 | if ((pixd = pixCreate(wd, hd, 1)) == NULL) { |
935 | 0 | LEPT_FREE(lineb); |
936 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
937 | 0 | } |
938 | 0 | pixCopyInputFormat(pixd, pixs); |
939 | 0 | pixCopyResolution(pixd, pixs); |
940 | 0 | pixScaleResolution(pixd, 2.0, 2.0); |
941 | 0 | wpld = pixGetWpl(pixd); |
942 | 0 | datad = pixGetData(pixd); |
943 | | |
944 | | /* Do all but last src line */ |
945 | 0 | for (i = 0; i < hsm; i++) { |
946 | 0 | lines = datas + i * wpls; |
947 | 0 | lined = datad + 2 * i * wpld; /* do 2 dest lines at a time */ |
948 | 0 | scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 0); |
949 | 0 | thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh); |
950 | 0 | thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh); |
951 | 0 | } |
952 | | |
953 | | /* Do last src line */ |
954 | 0 | lines = datas + hsm * wpls; |
955 | 0 | lined = datad + 2 * hsm * wpld; |
956 | 0 | scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 1); |
957 | 0 | thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh); |
958 | 0 | thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh); |
959 | |
|
960 | 0 | LEPT_FREE(lineb); |
961 | 0 | return pixd; |
962 | 0 | } |
963 | | |
964 | | |
965 | | /*! |
966 | | * \brief pixScaleGray2xLIDither() |
967 | | * |
968 | | * \param[in] pixs 8 bpp, not cmapped |
969 | | * \return pixd 1 bpp, or NULL on error |
970 | | * |
971 | | * <pre> |
972 | | * Notes: |
973 | | * (1) This does 2x upscale on pixs, using linear interpolation, |
974 | | * followed by Floyd-Steinberg dithering to binary. |
975 | | * (2) Buffers are used to avoid making a large grayscale image. |
976 | | * ~ Two line buffers are used for the src, required for the 2x |
977 | | * LI upscale. |
978 | | * ~ Three line buffers are used for the intermediate image. |
979 | | * Two are filled with each 2xLI row operation; the third is |
980 | | * needed because the upscale and dithering ops are out of sync. |
981 | | * </pre> |
982 | | */ |
983 | | PIX * |
984 | | pixScaleGray2xLIDither(PIX *pixs) |
985 | 0 | { |
986 | 0 | l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
987 | 0 | l_uint32 *datas, *datad; |
988 | 0 | l_uint32 *lined; |
989 | 0 | l_uint32 *lineb = NULL; /* 2 intermediate buffer lines */ |
990 | 0 | l_uint32 *linebp = NULL; /* 1 intermediate buffer line */ |
991 | 0 | l_uint32 *bufs = NULL; /* 2 source buffer lines */ |
992 | 0 | PIX *pixd = NULL; |
993 | |
|
994 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
995 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
996 | 0 | __func__, NULL); |
997 | | |
998 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
999 | 0 | wd = 2 * ws; |
1000 | 0 | hd = 2 * hs; |
1001 | 0 | hsm = hs - 1; |
1002 | 0 | datas = pixGetData(pixs); |
1003 | 0 | wpls = pixGetWpl(pixs); |
1004 | | |
1005 | | /* Make line buffers for 2 lines of src image */ |
1006 | 0 | if ((bufs = (l_uint32 *)LEPT_CALLOC(2 * wpls, sizeof(l_uint32))) == NULL) |
1007 | 0 | return (PIX *)ERROR_PTR("bufs not made", __func__, NULL); |
1008 | | |
1009 | | /* Make line buffer for 2 lines of virtual intermediate image */ |
1010 | 0 | wplb = (wd + 3) / 4; |
1011 | 0 | if ((lineb = (l_uint32 *)LEPT_CALLOC(2 * wplb, sizeof(l_uint32))) == NULL) { |
1012 | 0 | L_ERROR("lineb not made\n", __func__); |
1013 | 0 | goto cleanup; |
1014 | 0 | } |
1015 | | |
1016 | | /* Make line buffer for 1 line of virtual intermediate image */ |
1017 | 0 | if ((linebp = (l_uint32 *)LEPT_CALLOC(wplb, sizeof(l_uint32))) == NULL) { |
1018 | 0 | L_ERROR("linebp not made\n", __func__); |
1019 | 0 | goto cleanup; |
1020 | 0 | } |
1021 | | |
1022 | | /* Make dest binary image */ |
1023 | 0 | if ((pixd = pixCreate(wd, hd, 1)) == NULL) { |
1024 | 0 | L_ERROR("pixd not made\n", __func__); |
1025 | 0 | goto cleanup; |
1026 | 0 | } |
1027 | 0 | pixCopyInputFormat(pixd, pixs); |
1028 | 0 | pixCopyResolution(pixd, pixs); |
1029 | 0 | pixScaleResolution(pixd, 2.0, 2.0); |
1030 | 0 | wpld = pixGetWpl(pixd); |
1031 | 0 | datad = pixGetData(pixd); |
1032 | | |
1033 | | /* Start with the first src and the first dest line */ |
1034 | 0 | memcpy(bufs, datas, 4 * wpls); /* first src line */ |
1035 | 0 | memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */ |
1036 | 0 | scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */ |
1037 | 0 | lined = datad; |
1038 | 0 | ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb, |
1039 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1040 | | /* 1st d line */ |
1041 | | |
1042 | | /* Do all but last src line */ |
1043 | 0 | for (i = 1; i < hsm; i++) { |
1044 | 0 | memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */ |
1045 | 0 | memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls); |
1046 | 0 | memcpy(linebp, lineb + wplb, 4 * wplb); |
1047 | 0 | scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */ |
1048 | 0 | lined = datad + 2 * i * wpld; |
1049 | 0 | ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, |
1050 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1051 | | /* odd dest line */ |
1052 | 0 | ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb, |
1053 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1054 | | /* even dest line */ |
1055 | 0 | } |
1056 | | |
1057 | | /* Do the last src line and the last 3 dest lines */ |
1058 | 0 | memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */ |
1059 | 0 | memcpy(linebp, lineb + wplb, 4 * wplb); /* 1 i line */ |
1060 | 0 | scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 2 i lines */ |
1061 | 0 | ditherToBinaryLineLow(lined + wpld, wd, linebp, lineb, |
1062 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1063 | | /* odd dest line */ |
1064 | 0 | ditherToBinaryLineLow(lined + 2 * wpld, wd, lineb, lineb + wplb, |
1065 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1066 | | /* even dest line */ |
1067 | 0 | ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + wplb, NULL, |
1068 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1); |
1069 | | /* last dest line */ |
1070 | |
|
1071 | 0 | cleanup: |
1072 | 0 | LEPT_FREE(bufs); |
1073 | 0 | LEPT_FREE(lineb); |
1074 | 0 | LEPT_FREE(linebp); |
1075 | 0 | return pixd; |
1076 | 0 | } |
1077 | | |
1078 | | |
1079 | | /*------------------------------------------------------------------* |
1080 | | * Scale 4x followed by binarization * |
1081 | | *------------------------------------------------------------------*/ |
1082 | | /*! |
1083 | | * \brief pixScaleGray4xLIThresh() |
1084 | | * |
1085 | | * \param[in] pixs 8 bpp |
1086 | | * \param[in] thresh between 0 and 256 |
1087 | | * \return pixd 1 bpp, or NULL on error |
1088 | | * |
1089 | | * <pre> |
1090 | | * Notes: |
1091 | | * (1) This does 4x upscale on pixs, using linear interpolation, |
1092 | | * followed by thresholding to binary. |
1093 | | * (2) Buffers are used to avoid making a large grayscale image. |
1094 | | * (3) If a full 4x expanded grayscale image can be kept in memory, |
1095 | | * this function is only about 10% faster than separately doing |
1096 | | * a linear interpolation to a large grayscale image, followed |
1097 | | * by thresholding to binary. |
1098 | | * </pre> |
1099 | | */ |
1100 | | PIX * |
1101 | | pixScaleGray4xLIThresh(PIX *pixs, |
1102 | | l_int32 thresh) |
1103 | 0 | { |
1104 | 0 | l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
1105 | 0 | l_uint32 *datas, *datad, *lines, *lined, *lineb; |
1106 | 0 | PIX *pixd; |
1107 | |
|
1108 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
1109 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
1110 | 0 | __func__, NULL); |
1111 | 0 | if (thresh < 0 || thresh > 256) |
1112 | 0 | return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]", |
1113 | 0 | __func__, NULL); |
1114 | | |
1115 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1116 | 0 | wd = 4 * ws; |
1117 | 0 | hd = 4 * hs; |
1118 | 0 | hsm = hs - 1; |
1119 | 0 | datas = pixGetData(pixs); |
1120 | 0 | wpls = pixGetWpl(pixs); |
1121 | | |
1122 | | /* Make line buffer for 4 lines of virtual intermediate image */ |
1123 | 0 | wplb = (wd + 3) / 4; |
1124 | 0 | if ((lineb = (l_uint32 *)LEPT_CALLOC(4 * wplb, sizeof(l_uint32))) == NULL) |
1125 | 0 | return (PIX *)ERROR_PTR("lineb not made", __func__, NULL); |
1126 | | |
1127 | | /* Make dest binary image */ |
1128 | 0 | if ((pixd = pixCreate(wd, hd, 1)) == NULL) { |
1129 | 0 | LEPT_FREE(lineb); |
1130 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1131 | 0 | } |
1132 | 0 | pixCopyInputFormat(pixd, pixs); |
1133 | 0 | pixCopyResolution(pixd, pixs); |
1134 | 0 | pixScaleResolution(pixd, 4.0, 4.0); |
1135 | 0 | wpld = pixGetWpl(pixd); |
1136 | 0 | datad = pixGetData(pixd); |
1137 | | |
1138 | | /* Do all but last src line */ |
1139 | 0 | for (i = 0; i < hsm; i++) { |
1140 | 0 | lines = datas + i * wpls; |
1141 | 0 | lined = datad + 4 * i * wpld; /* do 4 dest lines at a time */ |
1142 | 0 | scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 0); |
1143 | 0 | for (j = 0; j < 4; j++) { |
1144 | 0 | thresholdToBinaryLineLow(lined + j * wpld, wd, |
1145 | 0 | lineb + j * wplb, 8, thresh); |
1146 | 0 | } |
1147 | 0 | } |
1148 | | |
1149 | | /* Do last src line */ |
1150 | 0 | lines = datas + hsm * wpls; |
1151 | 0 | lined = datad + 4 * hsm * wpld; |
1152 | 0 | scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 1); |
1153 | 0 | for (j = 0; j < 4; j++) { |
1154 | 0 | thresholdToBinaryLineLow(lined + j * wpld, wd, |
1155 | 0 | lineb + j * wplb, 8, thresh); |
1156 | 0 | } |
1157 | |
|
1158 | 0 | LEPT_FREE(lineb); |
1159 | 0 | return pixd; |
1160 | 0 | } |
1161 | | |
1162 | | |
1163 | | /*! |
1164 | | * \brief pixScaleGray4xLIDither() |
1165 | | * |
1166 | | * \param[in] pixs 8 bpp, not cmapped |
1167 | | * \return pixd 1 bpp, or NULL on error |
1168 | | * |
1169 | | * <pre> |
1170 | | * Notes: |
1171 | | * (1) This does 4x upscale on pixs, using linear interpolation, |
1172 | | * followed by Floyd-Steinberg dithering to binary. |
1173 | | * (2) Buffers are used to avoid making a large grayscale image. |
1174 | | * ~ Two line buffers are used for the src, required for the |
1175 | | * 4xLI upscale. |
1176 | | * ~ Five line buffers are used for the intermediate image. |
1177 | | * Four are filled with each 4xLI row operation; the fifth |
1178 | | * is needed because the upscale and dithering ops are |
1179 | | * out of sync. |
1180 | | * (3) If a full 4x expanded grayscale image can be kept in memory, |
1181 | | * this function is only about 5% faster than separately doing |
1182 | | * a linear interpolation to a large grayscale image, followed |
1183 | | * by error-diffusion dithering to binary. |
1184 | | * </pre> |
1185 | | */ |
1186 | | PIX * |
1187 | | pixScaleGray4xLIDither(PIX *pixs) |
1188 | 0 | { |
1189 | 0 | l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
1190 | 0 | l_uint32 *datas, *datad; |
1191 | 0 | l_uint32 *lined; |
1192 | 0 | l_uint32 *lineb = NULL; /* 4 intermediate buffer lines */ |
1193 | 0 | l_uint32 *linebp = NULL; /* 1 intermediate buffer line */ |
1194 | 0 | l_uint32 *bufs = NULL; /* 2 source buffer lines */ |
1195 | 0 | PIX *pixd = NULL; |
1196 | |
|
1197 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
1198 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
1199 | 0 | __func__, NULL); |
1200 | | |
1201 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1202 | 0 | wd = 4 * ws; |
1203 | 0 | hd = 4 * hs; |
1204 | 0 | hsm = hs - 1; |
1205 | 0 | datas = pixGetData(pixs); |
1206 | 0 | wpls = pixGetWpl(pixs); |
1207 | | |
1208 | | /* Make line buffers for 2 lines of src image */ |
1209 | 0 | if ((bufs = (l_uint32 *)LEPT_CALLOC(2 * wpls, sizeof(l_uint32))) == NULL) |
1210 | 0 | return (PIX *)ERROR_PTR("bufs not made", __func__, NULL); |
1211 | | |
1212 | | /* Make line buffer for 4 lines of virtual intermediate image */ |
1213 | 0 | wplb = (wd + 3) / 4; |
1214 | 0 | if ((lineb = (l_uint32 *)LEPT_CALLOC(4 * wplb, sizeof(l_uint32))) == NULL) { |
1215 | 0 | L_ERROR("lineb not made\n", __func__); |
1216 | 0 | goto cleanup; |
1217 | 0 | } |
1218 | | |
1219 | | /* Make line buffer for 1 line of virtual intermediate image */ |
1220 | 0 | if ((linebp = (l_uint32 *)LEPT_CALLOC(wplb, sizeof(l_uint32))) == NULL) { |
1221 | 0 | L_ERROR("linebp not made\n", __func__); |
1222 | 0 | goto cleanup; |
1223 | 0 | } |
1224 | | |
1225 | | /* Make dest binary image */ |
1226 | 0 | if ((pixd = pixCreate(wd, hd, 1)) == NULL) { |
1227 | 0 | L_ERROR("pixd not made\n", __func__); |
1228 | 0 | goto cleanup; |
1229 | 0 | } |
1230 | 0 | pixCopyInputFormat(pixd, pixs); |
1231 | 0 | pixCopyResolution(pixd, pixs); |
1232 | 0 | pixScaleResolution(pixd, 4.0, 4.0); |
1233 | 0 | wpld = pixGetWpl(pixd); |
1234 | 0 | datad = pixGetData(pixd); |
1235 | | |
1236 | | /* Start with the first src and the first 3 dest lines */ |
1237 | 0 | memcpy(bufs, datas, 4 * wpls); /* first src line */ |
1238 | 0 | memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */ |
1239 | 0 | scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */ |
1240 | 0 | lined = datad; |
1241 | 0 | for (j = 0; j < 3; j++) { /* first 3 d lines of Q */ |
1242 | 0 | ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, |
1243 | 0 | lineb + (j + 1) * wplb, |
1244 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1245 | 0 | } |
1246 | | |
1247 | | /* Do all but last src line */ |
1248 | 0 | for (i = 1; i < hsm; i++) { |
1249 | 0 | memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */ |
1250 | 0 | memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls); |
1251 | 0 | memcpy(linebp, lineb + 3 * wplb, 4 * wplb); |
1252 | 0 | scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */ |
1253 | 0 | lined = datad + 4 * i * wpld; |
1254 | 0 | ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, |
1255 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1256 | | /* 4th dest line of Q */ |
1257 | 0 | for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */ |
1258 | 0 | ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, |
1259 | 0 | lineb + (j + 1) * wplb, |
1260 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1261 | 0 | } |
1262 | 0 | } |
1263 | | |
1264 | | /* Do the last src line and the last 5 dest lines */ |
1265 | 0 | memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */ |
1266 | 0 | memcpy(linebp, lineb + 3 * wplb, 4 * wplb); /* 1 b line */ |
1267 | 0 | scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 4 b lines */ |
1268 | 0 | lined = datad + 4 * hsm * wpld; |
1269 | 0 | ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, |
1270 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1271 | | /* 4th dest line of Q */ |
1272 | 0 | for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */ |
1273 | 0 | ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, |
1274 | 0 | lineb + (j + 1) * wplb, |
1275 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
1276 | 0 | } |
1277 | | /* And finally, the last dest line */ |
1278 | 0 | ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + 3 * wplb, NULL, |
1279 | 0 | DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1); |
1280 | |
|
1281 | 0 | cleanup: |
1282 | 0 | LEPT_FREE(bufs); |
1283 | 0 | LEPT_FREE(lineb); |
1284 | 0 | LEPT_FREE(linebp); |
1285 | 0 | return pixd; |
1286 | 0 | } |
1287 | | |
1288 | | |
1289 | | /*------------------------------------------------------------------* |
1290 | | * Scaling by closest pixel sampling * |
1291 | | *------------------------------------------------------------------*/ |
1292 | | /*! |
1293 | | * \brief pixScaleBySampling() |
1294 | | * |
1295 | | * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp |
1296 | | * \param[in] scalex must be > 0.0 |
1297 | | * \param[in] scaley must be > 0.0 |
1298 | | * \return pixd, or NULL on error |
1299 | | * |
1300 | | * <pre> |
1301 | | * Notes: |
1302 | | * (1) This function samples from the source without |
1303 | | * filtering. As a result, aliasing will occur for |
1304 | | * subsampling (%scalex and/or %scaley < 1.0). |
1305 | | * (2) If %scalex == 1.0 and %scaley == 1.0, returns a copy. |
1306 | | * (3) For upscaling by an integer, use pixExpandReplicate(). |
1307 | | * (4) By default, indexing for the sampled source pixel is done |
1308 | | * by rounding. This shifts the source pixel sampling down |
1309 | | * and to the right by half a pixel, which has the effect of |
1310 | | * shifting the destination image up and to the left by a |
1311 | | * number of pixels approximately equal to half the scaling |
1312 | | * factor. To avoid this shift in the destination image, |
1313 | | * call pixScalebySamplingWithShift() using 0 for both shifts. |
1314 | | * </pre> |
1315 | | */ |
1316 | | PIX * |
1317 | | pixScaleBySampling(PIX *pixs, |
1318 | | l_float32 scalex, |
1319 | | l_float32 scaley) |
1320 | 0 | { |
1321 | 0 | if (!pixs) |
1322 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1323 | 0 | return pixScaleBySamplingWithShift(pixs, scalex, scaley, 0.5, 0.5); |
1324 | 0 | } |
1325 | | |
1326 | | |
1327 | | /*! |
1328 | | * \brief pixScaleBySamplingWithShift() |
1329 | | * |
1330 | | * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp |
1331 | | * \param[in] scalex must be > 0.0 |
1332 | | * \param[in] scaley must be > 0.0 |
1333 | | * \param[in] shiftx 0.5 for default; 0.0 to mihimize edge effects |
1334 | | * \param[in] shifty 0.5 for default; 0.0 to mihimize edge effects |
1335 | | * \return pixd, or NULL on error |
1336 | | * |
1337 | | * <pre> |
1338 | | * Notes: |
1339 | | * (1) The @shiftx and @shifty parameters are usually unimportant. |
1340 | | * Visible artifacts are minimized by using 0.0. |
1341 | | * Allowed values are 0.0 and 0.5. |
1342 | | * </pre> |
1343 | | */ |
1344 | | PIX * |
1345 | | pixScaleBySamplingWithShift(PIX *pixs, |
1346 | | l_float32 scalex, |
1347 | | l_float32 scaley, |
1348 | | l_float32 shiftx, |
1349 | | l_float32 shifty) |
1350 | 0 | { |
1351 | 0 | l_int32 ws, hs, d, wpls, wd, hd, wpld; |
1352 | 0 | l_uint32 *datas, *datad; |
1353 | 0 | PIX *pixd; |
1354 | |
|
1355 | 0 | if (!pixs) |
1356 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1357 | 0 | if (scalex <= 0.0 || scaley <= 0.0) |
1358 | 0 | return (PIX *)ERROR_PTR("scale factor <= 0", __func__, NULL); |
1359 | 0 | if (scalex == 1.0 && scaley == 1.0) |
1360 | 0 | return pixCopy(NULL, pixs); |
1361 | 0 | if (shiftx != 0.0 && shiftx != 0.5) |
1362 | 0 | return (PIX *)ERROR_PTR("shiftx != 0.0 or 0.5", __func__, NULL); |
1363 | 0 | if (shifty != 0.0 && shifty != 0.5) |
1364 | 0 | return (PIX *)ERROR_PTR("shifty != 0.0 or 0.5", __func__, NULL); |
1365 | 0 | if ((d = pixGetDepth(pixs)) == 1) |
1366 | 0 | return pixScaleBinaryWithShift(pixs, scalex, scaley, shiftx, shifty); |
1367 | | |
1368 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1369 | 0 | datas = pixGetData(pixs); |
1370 | 0 | wpls = pixGetWpl(pixs); |
1371 | 0 | wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
1372 | 0 | hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
1373 | 0 | if ((pixd = pixCreate(wd, hd, d)) == NULL) |
1374 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1375 | 0 | pixCopyResolution(pixd, pixs); |
1376 | 0 | pixScaleResolution(pixd, scalex, scaley); |
1377 | 0 | pixCopyColormap(pixd, pixs); |
1378 | 0 | pixCopyText(pixd, pixs); |
1379 | 0 | pixCopyInputFormat(pixd, pixs); |
1380 | 0 | pixCopySpp(pixd, pixs); |
1381 | 0 | datad = pixGetData(pixd); |
1382 | 0 | wpld = pixGetWpl(pixd); |
1383 | 0 | scaleBySamplingLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls, |
1384 | 0 | shiftx, shifty); |
1385 | 0 | if (d == 32 && pixGetSpp(pixs) == 4) |
1386 | 0 | pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); |
1387 | |
|
1388 | 0 | return pixd; |
1389 | 0 | } |
1390 | | |
1391 | | |
1392 | | /*! |
1393 | | * \brief pixScaleBySamplingToSize() |
1394 | | * |
1395 | | * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp |
1396 | | * \param[in] wd target width; use 0 if using height as target |
1397 | | * \param[in] hd target height; use 0 if using width as target |
1398 | | * \return pixd, or NULL on error |
1399 | | * |
1400 | | * <pre> |
1401 | | * Notes: |
1402 | | * (1) This guarantees that the output scaled image has the |
1403 | | * dimension(s) you specify. |
1404 | | * ~ To specify the width with isotropic scaling, set %hd = 0. |
1405 | | * ~ To specify the height with isotropic scaling, set %wd = 0. |
1406 | | * ~ If both %wd and %hd are specified, the image is scaled |
1407 | | * (in general, anisotropically) to that size. |
1408 | | * ~ It is an error to set both %wd and %hd to 0. |
1409 | | * </pre> |
1410 | | */ |
1411 | | PIX * |
1412 | | pixScaleBySamplingToSize(PIX *pixs, |
1413 | | l_int32 wd, |
1414 | | l_int32 hd) |
1415 | 0 | { |
1416 | 0 | l_int32 w, h; |
1417 | 0 | l_float32 scalex, scaley; |
1418 | |
|
1419 | 0 | if (!pixs) |
1420 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1421 | 0 | if (wd <= 0 && hd <= 0) |
1422 | 0 | return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL); |
1423 | | |
1424 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
1425 | 0 | if (wd <= 0) { |
1426 | 0 | scaley = (l_float32)hd / (l_float32)h; |
1427 | 0 | scalex = scaley; |
1428 | 0 | } else if (hd <= 0) { |
1429 | 0 | scalex = (l_float32)wd / (l_float32)w; |
1430 | 0 | scaley = scalex; |
1431 | 0 | } else { |
1432 | 0 | scalex = (l_float32)wd / (l_float32)w; |
1433 | 0 | scaley = (l_float32)hd / (l_float32)h; |
1434 | 0 | } |
1435 | |
|
1436 | 0 | return pixScaleBySampling(pixs, scalex, scaley); |
1437 | 0 | } |
1438 | | |
1439 | | |
1440 | | /*! |
1441 | | * \brief pixScaleByIntSampling() |
1442 | | * |
1443 | | * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp (all depths) |
1444 | | * \param[in] factor integer subsampling; >= 1 |
1445 | | * \return pixd, or NULL on error |
1446 | | * |
1447 | | * <pre> |
1448 | | * Notes: |
1449 | | * (1) Simple interface to pixScaleBySampling(), for isotropic |
1450 | | * integer reduction. If %factor == 1, returns a copy. |
1451 | | * </pre> |
1452 | | */ |
1453 | | PIX * |
1454 | | pixScaleByIntSampling(PIX *pixs, |
1455 | | l_int32 factor) |
1456 | 0 | { |
1457 | 0 | l_float32 scale; |
1458 | |
|
1459 | 0 | if (!pixs) |
1460 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1461 | 0 | if (factor <= 1) { |
1462 | 0 | if (factor < 1) |
1463 | 0 | L_ERROR("factor must be >= 1; returning a copy\n", __func__); |
1464 | 0 | return pixCopy(NULL, pixs); |
1465 | 0 | } |
1466 | | |
1467 | 0 | scale = 1.f / (l_float32)factor; |
1468 | 0 | return pixScaleBySampling(pixs, scale, scale); |
1469 | 0 | } |
1470 | | |
1471 | | |
1472 | | /*------------------------------------------------------------------* |
1473 | | * Fast integer factor subsampling RGB to gray * |
1474 | | *------------------------------------------------------------------*/ |
1475 | | /*! |
1476 | | * \brief pixScaleRGBToGrayFast() |
1477 | | * |
1478 | | * \param[in] pixs 32 bpp rgb |
1479 | | * \param[in] factor integer reduction factor >= 1 |
1480 | | * \param[in] color one of COLOR_RED, COLOR_GREEN, COLOR_BLUE |
1481 | | * \return pixd 8 bpp, or NULL on error |
1482 | | * |
1483 | | * <pre> |
1484 | | * Notes: |
1485 | | * (1) This does simultaneous subsampling by an integer factor and |
1486 | | * extraction of the color from the RGB pix. |
1487 | | * (2) It is designed for maximum speed, and is used for quickly |
1488 | | * generating a downsized grayscale image from a higher resolution |
1489 | | * RGB image. This would typically be used for image analysis. |
1490 | | * (3) The standard color byte order (RGBA) is assumed. |
1491 | | * </pre> |
1492 | | */ |
1493 | | PIX * |
1494 | | pixScaleRGBToGrayFast(PIX *pixs, |
1495 | | l_int32 factor, |
1496 | | l_int32 color) |
1497 | 0 | { |
1498 | 0 | l_int32 byteval, shift; |
1499 | 0 | l_int32 i, j, ws, hs, wd, hd, wpls, wpld; |
1500 | 0 | l_uint32 *datas, *words, *datad, *lined; |
1501 | 0 | l_float32 scale; |
1502 | 0 | PIX *pixd; |
1503 | |
|
1504 | 0 | if (!pixs) |
1505 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1506 | 0 | if (pixGetDepth(pixs) != 32) |
1507 | 0 | return (PIX *)ERROR_PTR("depth not 32 bpp", __func__, NULL); |
1508 | 0 | if (factor < 1) |
1509 | 0 | return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL); |
1510 | | |
1511 | 0 | if (color == COLOR_RED) |
1512 | 0 | shift = L_RED_SHIFT; |
1513 | 0 | else if (color == COLOR_GREEN) |
1514 | 0 | shift = L_GREEN_SHIFT; |
1515 | 0 | else if (color == COLOR_BLUE) |
1516 | 0 | shift = L_BLUE_SHIFT; |
1517 | 0 | else |
1518 | 0 | return (PIX *)ERROR_PTR("invalid color", __func__, NULL); |
1519 | | |
1520 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1521 | 0 | datas = pixGetData(pixs); |
1522 | 0 | wpls = pixGetWpl(pixs); |
1523 | |
|
1524 | 0 | wd = ws / factor; |
1525 | 0 | hd = hs / factor; |
1526 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
1527 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1528 | 0 | pixCopyResolution(pixd, pixs); |
1529 | 0 | pixCopyInputFormat(pixd, pixs); |
1530 | 0 | scale = 1.f / (l_float32) factor; |
1531 | 0 | pixScaleResolution(pixd, scale, scale); |
1532 | 0 | datad = pixGetData(pixd); |
1533 | 0 | wpld = pixGetWpl(pixd); |
1534 | |
|
1535 | 0 | for (i = 0; i < hd; i++) { |
1536 | 0 | words = datas + i * factor * wpls; |
1537 | 0 | lined = datad + i * wpld; |
1538 | 0 | for (j = 0; j < wd; j++, words += factor) { |
1539 | 0 | byteval = ((*words) >> shift) & 0xff; |
1540 | 0 | SET_DATA_BYTE(lined, j, byteval); |
1541 | 0 | } |
1542 | 0 | } |
1543 | |
|
1544 | 0 | return pixd; |
1545 | 0 | } |
1546 | | |
1547 | | |
1548 | | /*! |
1549 | | * \brief pixScaleRGBToBinaryFast() |
1550 | | * |
1551 | | * \param[in] pixs 32 bpp RGB |
1552 | | * \param[in] factor integer reduction factor >= 1 |
1553 | | * \param[in] thresh binarization threshold |
1554 | | * \return pixd 1 bpp, or NULL on error |
1555 | | * |
1556 | | * <pre> |
1557 | | * Notes: |
1558 | | * (1) This does simultaneous subsampling by an integer factor and |
1559 | | * conversion from RGB to gray to binary. |
1560 | | * (2) It is designed for maximum speed, and is used for quickly |
1561 | | * generating a downsized binary image from a higher resolution |
1562 | | * RGB image. This would typically be used for image analysis. |
1563 | | * (3) It uses the green channel to represent the RGB pixel intensity. |
1564 | | * </pre> |
1565 | | */ |
1566 | | PIX * |
1567 | | pixScaleRGBToBinaryFast(PIX *pixs, |
1568 | | l_int32 factor, |
1569 | | l_int32 thresh) |
1570 | 0 | { |
1571 | 0 | l_int32 byteval; |
1572 | 0 | l_int32 i, j, ws, hs, wd, hd, wpls, wpld; |
1573 | 0 | l_uint32 *datas, *words, *datad, *lined; |
1574 | 0 | l_float32 scale; |
1575 | 0 | PIX *pixd; |
1576 | |
|
1577 | 0 | if (!pixs) |
1578 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1579 | 0 | if (factor < 1) |
1580 | 0 | return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL); |
1581 | 0 | if (pixGetDepth(pixs) != 32) |
1582 | 0 | return (PIX *)ERROR_PTR("depth not 32 bpp", __func__, NULL); |
1583 | | |
1584 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1585 | 0 | datas = pixGetData(pixs); |
1586 | 0 | wpls = pixGetWpl(pixs); |
1587 | |
|
1588 | 0 | wd = ws / factor; |
1589 | 0 | hd = hs / factor; |
1590 | 0 | if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
1591 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1592 | 0 | pixCopyResolution(pixd, pixs); |
1593 | 0 | pixCopyInputFormat(pixd, pixs); |
1594 | 0 | scale = 1. / (l_float32) factor; |
1595 | 0 | pixScaleResolution(pixd, scale, scale); |
1596 | 0 | datad = pixGetData(pixd); |
1597 | 0 | wpld = pixGetWpl(pixd); |
1598 | |
|
1599 | 0 | for (i = 0; i < hd; i++) { |
1600 | 0 | words = datas + i * factor * wpls; |
1601 | 0 | lined = datad + i * wpld; |
1602 | 0 | for (j = 0; j < wd; j++, words += factor) { |
1603 | 0 | byteval = ((*words) >> L_GREEN_SHIFT) & 0xff; |
1604 | 0 | if (byteval < thresh) |
1605 | 0 | SET_DATA_BIT(lined, j); |
1606 | 0 | } |
1607 | 0 | } |
1608 | |
|
1609 | 0 | return pixd; |
1610 | 0 | } |
1611 | | |
1612 | | |
1613 | | /*! |
1614 | | * \brief pixScaleGrayToBinaryFast() |
1615 | | * |
1616 | | * \param[in] pixs 8 bpp grayscale |
1617 | | * \param[in] factor integer reduction factor >= 1 |
1618 | | * \param[in] thresh binarization threshold |
1619 | | * \return pixd 1 bpp, or NULL on error |
1620 | | * |
1621 | | * <pre> |
1622 | | * Notes: |
1623 | | * (1) This does simultaneous subsampling by an integer factor and |
1624 | | * thresholding from gray to binary. |
1625 | | * (2) It is designed for maximum speed, and is used for quickly |
1626 | | * generating a downsized binary image from a higher resolution |
1627 | | * gray image. This would typically be used for image analysis. |
1628 | | * </pre> |
1629 | | */ |
1630 | | PIX * |
1631 | | pixScaleGrayToBinaryFast(PIX *pixs, |
1632 | | l_int32 factor, |
1633 | | l_int32 thresh) |
1634 | 0 | { |
1635 | 0 | l_int32 byteval; |
1636 | 0 | l_int32 i, j, ws, hs, wd, hd, wpls, wpld, sj; |
1637 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
1638 | 0 | l_float32 scale; |
1639 | 0 | PIX *pixd; |
1640 | |
|
1641 | 0 | if (!pixs) |
1642 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1643 | 0 | if (factor < 1) |
1644 | 0 | return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL); |
1645 | 0 | if (pixGetDepth(pixs) != 8) |
1646 | 0 | return (PIX *)ERROR_PTR("depth not 8 bpp", __func__, NULL); |
1647 | | |
1648 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1649 | 0 | datas = pixGetData(pixs); |
1650 | 0 | wpls = pixGetWpl(pixs); |
1651 | |
|
1652 | 0 | wd = ws / factor; |
1653 | 0 | hd = hs / factor; |
1654 | 0 | if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
1655 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1656 | 0 | pixCopyResolution(pixd, pixs); |
1657 | 0 | pixCopyInputFormat(pixd, pixs); |
1658 | 0 | scale = 1.f / (l_float32) factor; |
1659 | 0 | pixScaleResolution(pixd, scale, scale); |
1660 | 0 | datad = pixGetData(pixd); |
1661 | 0 | wpld = pixGetWpl(pixd); |
1662 | |
|
1663 | 0 | for (i = 0; i < hd; i++) { |
1664 | 0 | lines = datas + i * factor * wpls; |
1665 | 0 | lined = datad + i * wpld; |
1666 | 0 | for (j = 0, sj = 0; j < wd; j++, sj += factor) { |
1667 | 0 | byteval = GET_DATA_BYTE(lines, sj); |
1668 | 0 | if (byteval < thresh) |
1669 | 0 | SET_DATA_BIT(lined, j); |
1670 | 0 | } |
1671 | 0 | } |
1672 | |
|
1673 | 0 | return pixd; |
1674 | 0 | } |
1675 | | |
1676 | | |
1677 | | /*------------------------------------------------------------------* |
1678 | | * Downscaling with (antialias) smoothing * |
1679 | | *------------------------------------------------------------------*/ |
1680 | | /*! |
1681 | | * \brief pixScaleSmooth() |
1682 | | * |
1683 | | * \param[in] pix 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap |
1684 | | * \param[in] scalex must be < 0.7 |
1685 | | * \param[in] scaley must be < 0.7 |
1686 | | * \return pixd, or NULL on error |
1687 | | * |
1688 | | * <pre> |
1689 | | * Notes: |
1690 | | * (1) This function should only be used when the scale factors are less |
1691 | | * than 0.7. If either scale factor is >= 0.7, issue a warning |
1692 | | * and call pixScaleGeneral(), which will invoke linear interpolation |
1693 | | * without sharpening. |
1694 | | * (2) This works only on 2, 4, 8 and 32 bpp images, and if there is |
1695 | | * a colormap, it is removed by converting to RGB. |
1696 | | * (3) It does simple (flat filter) convolution, with a filter size |
1697 | | * commensurate with the amount of reduction, to avoid antialiasing. |
1698 | | * (4) It does simple subsampling after smoothing, which is appropriate |
1699 | | * for this range of scaling. Linear interpolation gives essentially |
1700 | | * the same result with more computation for these scale factors, |
1701 | | * so we don't use it. |
1702 | | * (5) The result is the same as doing a full block convolution followed by |
1703 | | * subsampling, but this is faster because the results of the block |
1704 | | * convolution are only computed at the subsampling locations. |
1705 | | * In fact, the computation time is approximately independent of |
1706 | | * the scale factor, because the convolution kernel is adjusted |
1707 | | * so that each source pixel is summed approximately once. |
1708 | | * </pre> |
1709 | | */ |
1710 | | PIX * |
1711 | | pixScaleSmooth(PIX *pix, |
1712 | | l_float32 scalex, |
1713 | | l_float32 scaley) |
1714 | 0 | { |
1715 | 0 | l_int32 ws, hs, d, wd, hd, wpls, wpld, isize; |
1716 | 0 | l_uint32 val; |
1717 | 0 | l_uint32 *datas, *datad; |
1718 | 0 | l_float32 minscale, size; |
1719 | 0 | PIX *pixs, *pixd; |
1720 | |
|
1721 | 0 | if (!pix) |
1722 | 0 | return (PIX *)ERROR_PTR("pix not defined", __func__, NULL); |
1723 | 0 | if (scalex >= 0.7 || scaley >= 0.7) { |
1724 | 0 | L_WARNING("scaling factor not < 0.7; do regular scaling\n", __func__); |
1725 | 0 | return pixScaleGeneral(pix, scalex, scaley, 0.0, 0); |
1726 | 0 | } |
1727 | 0 | d = pixGetDepth(pix); |
1728 | 0 | if (d != 2 && d != 4 && d !=8 && d != 32) |
1729 | 0 | return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", __func__, NULL); |
1730 | | |
1731 | | /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ |
1732 | 0 | if ((pixs = pixConvertTo8Or32(pix, L_CLONE, 0)) == NULL) |
1733 | 0 | return (PIX *)ERROR_PTR("pixs not made", __func__, NULL); |
1734 | 0 | d = pixGetDepth(pixs); |
1735 | | |
1736 | | /* If 1.42 < 1/minscale < 2.5, use isize = 2 |
1737 | | * If 2.5 =< 1/minscale < 3.5, use isize = 3, etc. |
1738 | | * Under no conditions use isize < 2 */ |
1739 | 0 | minscale = L_MIN(scalex, scaley); |
1740 | 0 | size = 1.0f / minscale; /* ideal filter full width */ |
1741 | 0 | isize = L_MIN(10000, L_MAX(2, (l_int32)(size + 0.5))); |
1742 | |
|
1743 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1744 | 0 | if ((ws < isize) || (hs < isize)) { |
1745 | 0 | pixd = pixCreate(1, 1, d); |
1746 | 0 | pixGetPixel(pixs, ws / 2, hs / 2, &val); |
1747 | 0 | pixSetPixel(pixd, 0, 0, val); |
1748 | 0 | L_WARNING("ridiculously small scaling factor %f\n", __func__, minscale); |
1749 | 0 | pixDestroy(&pixs); |
1750 | 0 | return pixd; |
1751 | 0 | } |
1752 | | |
1753 | 0 | datas = pixGetData(pixs); |
1754 | 0 | wpls = pixGetWpl(pixs); |
1755 | 0 | wd = L_MAX(1, (l_int32)(scalex * (l_float32)ws + 0.5)); |
1756 | 0 | hd = L_MAX(1, (l_int32)(scaley * (l_float32)hs + 0.5)); |
1757 | 0 | if ((pixd = pixCreate(wd, hd, d)) == NULL) { |
1758 | 0 | pixDestroy(&pixs); |
1759 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1760 | 0 | } |
1761 | 0 | pixCopyResolution(pixd, pixs); |
1762 | 0 | pixCopyInputFormat(pixd, pixs); |
1763 | 0 | pixScaleResolution(pixd, scalex, scaley); |
1764 | 0 | datad = pixGetData(pixd); |
1765 | 0 | wpld = pixGetWpl(pixd); |
1766 | 0 | scaleSmoothLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls, isize); |
1767 | 0 | if (d == 32 && pixGetSpp(pixs) == 4) |
1768 | 0 | pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); |
1769 | |
|
1770 | 0 | pixDestroy(&pixs); |
1771 | 0 | return pixd; |
1772 | 0 | } |
1773 | | |
1774 | | |
1775 | | /*! |
1776 | | * \brief pixScaleSmoothToSize() |
1777 | | * |
1778 | | * \param[in] pixs 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap |
1779 | | * \param[in] wd target width; use 0 if using height as target |
1780 | | * \param[in] hd target height; use 0 if using width as target |
1781 | | * \return pixd, or NULL on error |
1782 | | * |
1783 | | * <pre> |
1784 | | * Notes: |
1785 | | * (1) See notes in pixScaleSmooth(). |
1786 | | * (2) The output scaled image has the dimension(s) you specify: |
1787 | | * - To specify the width with isotropic scaling, set %hd = 0. |
1788 | | * - To specify the height with isotropic scaling, set %wd = 0. |
1789 | | * - If both %wd and %hd are specified, the image is scaled |
1790 | | * (in general, anisotropically) to that size. |
1791 | | * - It is an error to set both %wd and %hd to 0. |
1792 | | * </pre> |
1793 | | */ |
1794 | | PIX * |
1795 | | pixScaleSmoothToSize(PIX *pixs, |
1796 | | l_int32 wd, |
1797 | | l_int32 hd) |
1798 | 0 | { |
1799 | 0 | l_int32 w, h; |
1800 | 0 | l_float32 scalex, scaley; |
1801 | |
|
1802 | 0 | if (!pixs) |
1803 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1804 | 0 | if (wd <= 0 && hd <= 0) |
1805 | 0 | return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL); |
1806 | | |
1807 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
1808 | 0 | if (wd <= 0) { |
1809 | 0 | scaley = (l_float32)hd / (l_float32)h; |
1810 | 0 | scalex = scaley; |
1811 | 0 | } else if (hd <= 0) { |
1812 | 0 | scalex = (l_float32)wd / (l_float32)w; |
1813 | 0 | scaley = scalex; |
1814 | 0 | } else { |
1815 | 0 | scalex = (l_float32)wd / (l_float32)w; |
1816 | 0 | scaley = (l_float32)hd / (l_float32)h; |
1817 | 0 | } |
1818 | |
|
1819 | 0 | return pixScaleSmooth(pixs, scalex, scaley); |
1820 | 0 | } |
1821 | | |
1822 | | |
1823 | | /*! |
1824 | | * \brief pixScaleRGBToGray2() |
1825 | | * |
1826 | | * \param[in] pixs 32 bpp rgb |
1827 | | * \param[in] rwt, gwt, bwt must sum to 1.0 |
1828 | | * \return pixd, 8 bpp, 2x reduced, or NULL on error |
1829 | | */ |
1830 | | PIX * |
1831 | | pixScaleRGBToGray2(PIX *pixs, |
1832 | | l_float32 rwt, |
1833 | | l_float32 gwt, |
1834 | | l_float32 bwt) |
1835 | 0 | { |
1836 | 0 | l_int32 wd, hd, wpls, wpld; |
1837 | 0 | l_uint32 *datas, *datad; |
1838 | 0 | PIX *pixd; |
1839 | |
|
1840 | 0 | if (!pixs) |
1841 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1842 | 0 | if (pixGetDepth(pixs) != 32) |
1843 | 0 | return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); |
1844 | 0 | if (rwt + gwt + bwt < 0.98 || rwt + gwt + bwt > 1.02) |
1845 | 0 | return (PIX *)ERROR_PTR("sum of wts should be 1.0", __func__, NULL); |
1846 | | |
1847 | 0 | wd = pixGetWidth(pixs) / 2; |
1848 | 0 | hd = pixGetHeight(pixs) / 2; |
1849 | 0 | wpls = pixGetWpl(pixs); |
1850 | 0 | datas = pixGetData(pixs); |
1851 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
1852 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1853 | 0 | pixCopyResolution(pixd, pixs); |
1854 | 0 | pixCopyInputFormat(pixd, pixs); |
1855 | 0 | pixScaleResolution(pixd, 0.5, 0.5); |
1856 | 0 | wpld = pixGetWpl(pixd); |
1857 | 0 | datad = pixGetData(pixd); |
1858 | 0 | scaleRGBToGray2Low(datad, wd, hd, wpld, datas, wpls, rwt, gwt, bwt); |
1859 | 0 | return pixd; |
1860 | 0 | } |
1861 | | |
1862 | | |
1863 | | /*------------------------------------------------------------------* |
1864 | | * Downscaling with (antialias) area mapping * |
1865 | | *------------------------------------------------------------------*/ |
1866 | | /*! |
1867 | | * \brief pixScaleAreaMap() |
1868 | | * |
1869 | | * \param[in] pix 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap |
1870 | | * \param[in] scalex must be < 0.7; minimum is 0.02 |
1871 | | * \param[in] scaley must be < 0.7; minimum is 0.02 |
1872 | | * \return pixd, or NULL on error |
1873 | | * |
1874 | | * <pre> |
1875 | | * Notes: |
1876 | | * (1) This is a low-pass filter that averages over fractional pixels. |
1877 | | * It should only be used when the scale factors are less than 0.7. |
1878 | | * If either scale factor is greater than or equal to 0.7, we |
1879 | | * issue a warning and call pixScaleGeneral(), which will invoke |
1880 | | * linear interpolation without sharpening. |
1881 | | * (2) The minimum scale factor allowed for area mapping reduction |
1882 | | * is 0.02. Various overflows will occur when scale factors are |
1883 | | * less than about 1/256. If a scale factor smaller than 0.02 |
1884 | | * is given, we use pixScaleSmooth(), which is a low-pass filter |
1885 | | * that averages over entire pixels. |
1886 | | * (3) This works only on 2, 4, 8 and 32 bpp images. If there is |
1887 | | * a colormap, it is removed by converting to RGB. In other |
1888 | | * cases, we issue a warning and call pixScaleGeneral(). |
1889 | | * (4) This is faster than pixScale() because it does not do sharpening. |
1890 | | * (5) It does a relatively expensive area mapping computation, to |
1891 | | * avoid antialiasing. It is about 2x slower than pixScaleSmooth(), |
1892 | | * but the results are much better on fine text. |
1893 | | * (6) pixScaleAreaMap2() is typically about 7x faster for the special |
1894 | | * case of 2x reduction for color images, and about 9x faster |
1895 | | * for grayscale images. Surprisingly, the improvement in speed |
1896 | | * when using a cascade of 2x reductions for small scale factors is |
1897 | | * less than one might expect, and in most situations gives |
1898 | | * poorer image quality. But see (6). |
1899 | | * (7) For reductions between 0.35 and 0.5, a 2x area map reduction |
1900 | | * followed by using pixScaleGeneral() on a 2x larger scalefactor |
1901 | | * (which further reduces the image size using bilinear interpolation) |
1902 | | * would give a significant speed increase, with little loss of |
1903 | | * quality, but this is not enabled as it would break too many tests. |
1904 | | * For scaling factors below 0.35, scaling atomically is nearly |
1905 | | * as fast as using a cascade of 2x scalings, and gives |
1906 | | * better results. |
1907 | | * </pre> |
1908 | | */ |
1909 | | PIX * |
1910 | | pixScaleAreaMap(PIX *pix, |
1911 | | l_float32 scalex, |
1912 | | l_float32 scaley) |
1913 | 0 | { |
1914 | 0 | l_int32 ws, hs, d, wd, hd, wpls, wpld; |
1915 | 0 | l_uint32 *datas, *datad; |
1916 | 0 | l_float32 maxscale, minscale; |
1917 | 0 | PIX *pixs, *pixd, *pix1, *pix2, *pix3; |
1918 | |
|
1919 | 0 | if (!pix) |
1920 | 0 | return (PIX *)ERROR_PTR("pix not defined", __func__, NULL); |
1921 | 0 | d = pixGetDepth(pix); |
1922 | 0 | if (d != 2 && d != 4 && d != 8 && d != 32) |
1923 | 0 | return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", __func__, NULL); |
1924 | | |
1925 | 0 | minscale = L_MIN(scalex, scaley); |
1926 | 0 | if (minscale < 0.02) { /* too small for area mapping */ |
1927 | 0 | L_WARNING("tiny scaling factor; using pixScaleSmooth()\n", __func__); |
1928 | 0 | return pixScaleSmooth(pix, scalex, scaley); |
1929 | 0 | } |
1930 | | |
1931 | 0 | maxscale = L_MAX(scalex, scaley); |
1932 | 0 | if (maxscale >= 0.7) { /* too large for area mapping */ |
1933 | 0 | L_WARNING("scaling factor >= 0.7; do regular scaling\n", __func__); |
1934 | 0 | return pixScaleGeneral(pix, scalex, scaley, 0.0, 0); |
1935 | 0 | } |
1936 | | |
1937 | | /* Special cases: 2x, 4x, 8x, 16x reduction */ |
1938 | 0 | if (scalex == 0.5 && scaley == 0.5) |
1939 | 0 | return pixScaleAreaMap2(pix); |
1940 | 0 | if (scalex == 0.25 && scaley == 0.25) { |
1941 | 0 | pix1 = pixScaleAreaMap2(pix); |
1942 | 0 | pixd = pixScaleAreaMap2(pix1); |
1943 | 0 | pixDestroy(&pix1); |
1944 | 0 | return pixd; |
1945 | 0 | } |
1946 | 0 | if (scalex == 0.125 && scaley == 0.125) { |
1947 | 0 | pix1 = pixScaleAreaMap2(pix); |
1948 | 0 | pix2 = pixScaleAreaMap2(pix1); |
1949 | 0 | pixd = pixScaleAreaMap2(pix2); |
1950 | 0 | pixDestroy(&pix1); |
1951 | 0 | pixDestroy(&pix2); |
1952 | 0 | return pixd; |
1953 | 0 | } |
1954 | 0 | if (scalex == 0.0625 && scaley == 0.0625) { |
1955 | 0 | pix1 = pixScaleAreaMap2(pix); |
1956 | 0 | pix2 = pixScaleAreaMap2(pix1); |
1957 | 0 | pix3 = pixScaleAreaMap2(pix2); |
1958 | 0 | pixd = pixScaleAreaMap2(pix3); |
1959 | 0 | pixDestroy(&pix1); |
1960 | 0 | pixDestroy(&pix2); |
1961 | 0 | pixDestroy(&pix3); |
1962 | 0 | return pixd; |
1963 | 0 | } |
1964 | | |
1965 | | #if 0 /* Not enabled because it breaks too many tests that rely on exact |
1966 | | * pixel matches. */ |
1967 | | /* Special case where it is significantly faster to downscale first |
1968 | | * by 2x, with relatively little degradation in image quality. */ |
1969 | | if (scalex > 0.35 && scalex < 0.5) { |
1970 | | pix1 = pixScaleAreaMap2(pix); |
1971 | | pixd = pixScaleAreaMap(pix1, 2.0 * scalex, 2.0 * scaley); |
1972 | | pixDestroy(&pix1); |
1973 | | return pixd; |
1974 | | } |
1975 | | #endif |
1976 | | |
1977 | | /* Remove colormap if necessary. |
1978 | | * If 2 bpp or 4 bpp gray, convert to 8 bpp */ |
1979 | 0 | if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { |
1980 | 0 | L_WARNING("pix has colormap; removing\n", __func__); |
1981 | 0 | pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
1982 | 0 | d = pixGetDepth(pixs); |
1983 | 0 | } else if (d == 2 || d == 4) { |
1984 | 0 | pixs = pixConvertTo8(pix, FALSE); |
1985 | 0 | d = 8; |
1986 | 0 | } else { |
1987 | 0 | pixs = pixClone(pix); |
1988 | 0 | } |
1989 | |
|
1990 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1991 | 0 | datas = pixGetData(pixs); |
1992 | 0 | wpls = pixGetWpl(pixs); |
1993 | 0 | wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
1994 | 0 | hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
1995 | 0 | if (wd < 1 || hd < 1) { |
1996 | 0 | pixDestroy(&pixs); |
1997 | 0 | return (PIX *)ERROR_PTR("pixd too small", __func__, NULL); |
1998 | 0 | } |
1999 | 0 | if ((pixd = pixCreate(wd, hd, d)) == NULL) { |
2000 | 0 | pixDestroy(&pixs); |
2001 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
2002 | 0 | } |
2003 | 0 | pixCopyInputFormat(pixd, pixs); |
2004 | 0 | pixCopyResolution(pixd, pixs); |
2005 | 0 | pixScaleResolution(pixd, scalex, scaley); |
2006 | 0 | datad = pixGetData(pixd); |
2007 | 0 | wpld = pixGetWpl(pixd); |
2008 | 0 | if (d == 8) { |
2009 | 0 | scaleGrayAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
2010 | 0 | } else { /* RGB, d == 32 */ |
2011 | 0 | scaleColorAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
2012 | 0 | if (pixGetSpp(pixs) == 4) |
2013 | 0 | pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); |
2014 | 0 | } |
2015 | |
|
2016 | 0 | pixDestroy(&pixs); |
2017 | 0 | return pixd; |
2018 | 0 | } |
2019 | | |
2020 | | |
2021 | | /*! |
2022 | | * \brief pixScaleAreaMap2() |
2023 | | * |
2024 | | * \param[in] pix 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap |
2025 | | * \return pixd, or NULL on error |
2026 | | * |
2027 | | * <pre> |
2028 | | * Notes: |
2029 | | * (1) This function does an area mapping (average) for 2x |
2030 | | * reduction. |
2031 | | * (2) This works only on 2, 4, 8 and 32 bpp images. If there is |
2032 | | * a colormap, it is removed by converting to RGB. |
2033 | | * (3) Compared to the general pixScaleAreaMap(), for this function |
2034 | | * gray processing is about 14x faster and color processing |
2035 | | * is about 4x faster. Consequently, pixScaleAreaMap2() is |
2036 | | * incorporated into the general area map scaling function, |
2037 | | * for the special cases of 2x, 4x, 8x and 16x reduction. |
2038 | | * </pre> |
2039 | | */ |
2040 | | PIX * |
2041 | | pixScaleAreaMap2(PIX *pix) |
2042 | 0 | { |
2043 | 0 | l_int32 wd, hd, d, wpls, wpld; |
2044 | 0 | l_uint32 *datas, *datad; |
2045 | 0 | PIX *pixs, *pixd; |
2046 | |
|
2047 | 0 | if (!pix) |
2048 | 0 | return (PIX *)ERROR_PTR("pix not defined", __func__, NULL); |
2049 | 0 | d = pixGetDepth(pix); |
2050 | 0 | if (d != 2 && d != 4 && d != 8 && d != 32) |
2051 | 0 | return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", __func__, NULL); |
2052 | | |
2053 | | /* Remove colormap if necessary. |
2054 | | * If 2 bpp or 4 bpp gray, convert to 8 bpp */ |
2055 | 0 | if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { |
2056 | 0 | L_WARNING("pix has colormap; removing\n", __func__); |
2057 | 0 | pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
2058 | 0 | d = pixGetDepth(pixs); |
2059 | 0 | } else if (d == 2 || d == 4) { |
2060 | 0 | pixs = pixConvertTo8(pix, FALSE); |
2061 | 0 | d = 8; |
2062 | 0 | } else { |
2063 | 0 | pixs = pixClone(pix); |
2064 | 0 | } |
2065 | |
|
2066 | 0 | wd = pixGetWidth(pixs) / 2; |
2067 | 0 | hd = pixGetHeight(pixs) / 2; |
2068 | 0 | datas = pixGetData(pixs); |
2069 | 0 | wpls = pixGetWpl(pixs); |
2070 | 0 | pixd = pixCreate(wd, hd, d); |
2071 | 0 | datad = pixGetData(pixd); |
2072 | 0 | wpld = pixGetWpl(pixd); |
2073 | 0 | pixCopyInputFormat(pixd, pixs); |
2074 | 0 | pixCopyResolution(pixd, pixs); |
2075 | 0 | pixScaleResolution(pixd, 0.5, 0.5); |
2076 | 0 | scaleAreaMapLow2(datad, wd, hd, wpld, datas, d, wpls); |
2077 | 0 | if (pixGetSpp(pixs) == 4) |
2078 | 0 | pixScaleAndTransferAlpha(pixd, pixs, 0.5, 0.5); |
2079 | 0 | pixDestroy(&pixs); |
2080 | 0 | return pixd; |
2081 | 0 | } |
2082 | | |
2083 | | |
2084 | | /*! |
2085 | | * \brief pixScaleAreaMapToSize() |
2086 | | * |
2087 | | * \param[in] pixs 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap |
2088 | | * \param[in] wd target width; use 0 if using height as target |
2089 | | * \param[in] hd target height; use 0 if using width as target |
2090 | | * \return pixd, or NULL on error |
2091 | | * |
2092 | | * <pre> |
2093 | | * Notes: |
2094 | | * (1) See notes in pixScaleAreaMap(). |
2095 | | * (2) The output scaled image has the dimension(s) you specify: |
2096 | | * - To specify the width with isotropic scaling, set %hd = 0. |
2097 | | * - To specify the height with isotropic scaling, set %wd = 0. |
2098 | | * - If both %wd and %hd are specified, the image is scaled |
2099 | | * (in general, anisotropically) to that size. |
2100 | | * - It is an error to set both %wd and %hd to 0. |
2101 | | * </pre> |
2102 | | */ |
2103 | | PIX * |
2104 | | pixScaleAreaMapToSize(PIX *pixs, |
2105 | | l_int32 wd, |
2106 | | l_int32 hd) |
2107 | 0 | { |
2108 | 0 | l_int32 w, h; |
2109 | 0 | l_float32 scalex, scaley; |
2110 | |
|
2111 | 0 | if (!pixs) |
2112 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
2113 | 0 | if (wd <= 0 && hd <= 0) |
2114 | 0 | return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL); |
2115 | | |
2116 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
2117 | 0 | if (wd <= 0) { |
2118 | 0 | scaley = (l_float32)hd / (l_float32)h; |
2119 | 0 | scalex = scaley; |
2120 | 0 | } else if (hd <= 0) { |
2121 | 0 | scalex = (l_float32)wd / (l_float32)w; |
2122 | 0 | scaley = scalex; |
2123 | 0 | } else { |
2124 | 0 | scalex = (l_float32)wd / (l_float32)w; |
2125 | 0 | scaley = (l_float32)hd / (l_float32)h; |
2126 | 0 | } |
2127 | |
|
2128 | 0 | return pixScaleAreaMap(pixs, scalex, scaley); |
2129 | 0 | } |
2130 | | |
2131 | | |
2132 | | /*------------------------------------------------------------------* |
2133 | | * Binary scaling by closest pixel sampling * |
2134 | | *------------------------------------------------------------------*/ |
2135 | | /*! |
2136 | | * \brief pixScaleBinary() |
2137 | | * |
2138 | | * \param[in] pixs 1 bpp |
2139 | | * \param[in] scalex must be > 0.0 |
2140 | | * \param[in] scaley must be > 0.0 |
2141 | | * \return pixd, or NULL on error |
2142 | | * |
2143 | | * <pre> |
2144 | | * Notes: |
2145 | | * (1) This function samples from the source without |
2146 | | * filtering. As a result, aliasing will occur for |
2147 | | * subsampling (scalex and scaley < 1.0). |
2148 | | * (2) By default, indexing for the sampled source pixel is done |
2149 | | * by rounding. This shifts the source pixel sampling down |
2150 | | * and to the right by half a pixel, which has the effect of |
2151 | | * shifting the destination image up and to the left by a |
2152 | | * number of pixels approximately equal to half the scaling |
2153 | | * factor. To avoid this shift in the destination image, |
2154 | | * call pixScalebySamplingWithShift() using 0 for both shifts. |
2155 | | * </pre> |
2156 | | */ |
2157 | | PIX * |
2158 | | pixScaleBinary(PIX *pixs, |
2159 | | l_float32 scalex, |
2160 | | l_float32 scaley) |
2161 | 11.9k | { |
2162 | 11.9k | if (!pixs) |
2163 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
2164 | 11.9k | if (pixGetDepth(pixs) != 1) |
2165 | 0 | return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL); |
2166 | 11.9k | return pixScaleBinaryWithShift(pixs, scalex, scaley, 0.5, 0.5); |
2167 | 11.9k | } |
2168 | | |
2169 | | |
2170 | | /*! |
2171 | | * \brief pixScaleBinaryWithShift() |
2172 | | * |
2173 | | * \param[in] pixs 1 bpp |
2174 | | * \param[in] scalex must be > 0.0 |
2175 | | * \param[in] scaley must be > 0.0 |
2176 | | * \param[in] shiftx 0.5 for default; 0.0 to mihimize edge effects |
2177 | | * \param[in] shifty 0.5 for default; 0.0 to mihimize edge effects |
2178 | | * \return pixd, or NULL on error |
2179 | | * |
2180 | | * <pre> |
2181 | | * Notes: |
2182 | | * (1) The @shiftx and @shifty parameters are usually unimportant. |
2183 | | * Visible artifacts are minimized by using 0.0. |
2184 | | * Allowed values are 0.0 and 0.5. |
2185 | | * </pre> |
2186 | | */ |
2187 | | PIX * |
2188 | | pixScaleBinaryWithShift(PIX *pixs, |
2189 | | l_float32 scalex, |
2190 | | l_float32 scaley, |
2191 | | l_float32 shiftx, |
2192 | | l_float32 shifty) |
2193 | 11.9k | { |
2194 | 11.9k | l_int32 ws, hs, wpls, wd, hd, wpld; |
2195 | 11.9k | l_uint32 *datas, *datad; |
2196 | 11.9k | PIX *pixd; |
2197 | | |
2198 | 11.9k | if (!pixs) |
2199 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
2200 | 11.9k | if (pixGetDepth(pixs) != 1) |
2201 | 0 | return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL); |
2202 | 11.9k | if (scalex <= 0.0 || scaley <= 0.0) |
2203 | 0 | return (PIX *)ERROR_PTR("scale factor <= 0", __func__, NULL); |
2204 | 11.9k | if (scalex == 1.0 && scaley == 1.0) |
2205 | 0 | return pixCopy(NULL, pixs); |
2206 | 11.9k | if (shiftx != 0.0 && shiftx != 0.5) |
2207 | 0 | return (PIX *)ERROR_PTR("shiftx != 0.0 or 0.5", __func__, NULL); |
2208 | 11.9k | if (shifty != 0.0 && shifty != 0.5) |
2209 | 0 | return (PIX *)ERROR_PTR("shifty != 0.0 or 0.5", __func__, NULL); |
2210 | | |
2211 | 11.9k | pixGetDimensions(pixs, &ws, &hs, NULL); |
2212 | 11.9k | datas = pixGetData(pixs); |
2213 | 11.9k | wpls = pixGetWpl(pixs); |
2214 | 11.9k | wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
2215 | 11.9k | hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
2216 | 11.9k | if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
2217 | 193 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
2218 | 11.7k | pixCopyColormap(pixd, pixs); |
2219 | 11.7k | pixCopyText(pixd, pixs); |
2220 | 11.7k | pixCopyInputFormat(pixd, pixs); |
2221 | 11.7k | pixCopyResolution(pixd, pixs); |
2222 | 11.7k | pixScaleResolution(pixd, scalex, scaley); |
2223 | 11.7k | datad = pixGetData(pixd); |
2224 | 11.7k | wpld = pixGetWpl(pixd); |
2225 | 11.7k | scaleBinaryLow(datad, wd, hd, wpld, datas, ws, hs, wpls, shiftx, shifty); |
2226 | 11.7k | return pixd; |
2227 | 11.9k | } |
2228 | | |
2229 | | |
2230 | | /* ================================================================ * |
2231 | | * Low level static functions * |
2232 | | * ================================================================ */ |
2233 | | |
2234 | | /*------------------------------------------------------------------* |
2235 | | * General linear interpolated color scaling * |
2236 | | *------------------------------------------------------------------*/ |
2237 | | /*! |
2238 | | * \brief scaleColorLILow() |
2239 | | * |
2240 | | * <pre> |
2241 | | * Notes: |
2242 | | * (1) We choose to divide each pixel into 16 x 16 sub-pixels. |
2243 | | * Linear interpolation is equivalent to finding the |
2244 | | * fractional area (i.e., number of sub-pixels divided |
2245 | | * by 256) associated with each of the four nearest src pixels, |
2246 | | * and weighting each pixel value by this fractional area. |
2247 | | * </pre> |
2248 | | */ |
2249 | | static void |
2250 | | scaleColorLILow(l_uint32 *datad, |
2251 | | l_int32 wd, |
2252 | | l_int32 hd, |
2253 | | l_int32 wpld, |
2254 | | l_uint32 *datas, |
2255 | | l_int32 ws, |
2256 | | l_int32 hs, |
2257 | | l_int32 wpls) |
2258 | 0 | { |
2259 | 0 | l_int32 i, j, wm2, hm2; |
2260 | 0 | l_int32 xpm, ypm; /* location in src image, to 1/16 of a pixel */ |
2261 | 0 | l_int32 xp, yp, xf, yf; /* src pixel and pixel fraction coordinates */ |
2262 | 0 | l_uint32 v00r, v01r, v10r, v11r, v00g, v01g, v10g, v11g; |
2263 | 0 | l_uint32 v00b, v01b, v10b, v11b, area00, area01, area10, area11; |
2264 | 0 | l_uint32 pixels1, pixels2, pixels3, pixels4, pixel; |
2265 | 0 | l_uint32 *lines, *lined; |
2266 | 0 | l_float32 scx, scy; |
2267 | | |
2268 | | /* (scx, scy) are scaling factors that are applied to the |
2269 | | * dest coords to get the corresponding src coords. |
2270 | | * We need them because we iterate over dest pixels |
2271 | | * and must find the corresponding set of src pixels. */ |
2272 | 0 | scx = 16.f * (l_float32)ws / (l_float32)wd; |
2273 | 0 | scy = 16.f * (l_float32)hs / (l_float32)hd; |
2274 | 0 | wm2 = ws - 2; |
2275 | 0 | hm2 = hs - 2; |
2276 | | |
2277 | | /* Iterate over the destination pixels */ |
2278 | 0 | for (i = 0; i < hd; i++) { |
2279 | 0 | ypm = (l_int32)(scy * (l_float32)i); |
2280 | 0 | yp = ypm >> 4; |
2281 | 0 | yf = ypm & 0x0f; |
2282 | 0 | lined = datad + i * wpld; |
2283 | 0 | lines = datas + yp * wpls; |
2284 | 0 | for (j = 0; j < wd; j++) { |
2285 | 0 | xpm = (l_int32)(scx * (l_float32)j); |
2286 | 0 | xp = xpm >> 4; |
2287 | 0 | xf = xpm & 0x0f; |
2288 | | |
2289 | | /* Do bilinear interpolation. This is a simple |
2290 | | * generalization of the calculation in scaleGrayLILow(). |
2291 | | * Without this, we could simply subsample: |
2292 | | * *(lined + j) = *(lines + xp); |
2293 | | * which is faster but gives lousy results! */ |
2294 | 0 | pixels1 = *(lines + xp); |
2295 | |
|
2296 | 0 | if (xp > wm2 || yp > hm2) { |
2297 | 0 | if (yp > hm2 && xp <= wm2) { /* pixels near bottom */ |
2298 | 0 | pixels2 = *(lines + xp + 1); |
2299 | 0 | pixels3 = pixels1; |
2300 | 0 | pixels4 = pixels2; |
2301 | 0 | } else if (xp > wm2 && yp <= hm2) { /* pixels near rt side */ |
2302 | 0 | pixels2 = pixels1; |
2303 | 0 | pixels3 = *(lines + wpls + xp); |
2304 | 0 | pixels4 = pixels3; |
2305 | 0 | } else { /* pixels at LR corner */ |
2306 | 0 | pixels4 = pixels3 = pixels2 = pixels1; |
2307 | 0 | } |
2308 | 0 | } else { |
2309 | 0 | pixels2 = *(lines + xp + 1); |
2310 | 0 | pixels3 = *(lines + wpls + xp); |
2311 | 0 | pixels4 = *(lines + wpls + xp + 1); |
2312 | 0 | } |
2313 | |
|
2314 | 0 | area00 = (16 - xf) * (16 - yf); |
2315 | 0 | area10 = xf * (16 - yf); |
2316 | 0 | area01 = (16 - xf) * yf; |
2317 | 0 | area11 = xf * yf; |
2318 | 0 | v00r = area00 * ((pixels1 >> L_RED_SHIFT) & 0xff); |
2319 | 0 | v00g = area00 * ((pixels1 >> L_GREEN_SHIFT) & 0xff); |
2320 | 0 | v00b = area00 * ((pixels1 >> L_BLUE_SHIFT) & 0xff); |
2321 | 0 | v10r = area10 * ((pixels2 >> L_RED_SHIFT) & 0xff); |
2322 | 0 | v10g = area10 * ((pixels2 >> L_GREEN_SHIFT) & 0xff); |
2323 | 0 | v10b = area10 * ((pixels2 >> L_BLUE_SHIFT) & 0xff); |
2324 | 0 | v01r = area01 * ((pixels3 >> L_RED_SHIFT) & 0xff); |
2325 | 0 | v01g = area01 * ((pixels3 >> L_GREEN_SHIFT) & 0xff); |
2326 | 0 | v01b = area01 * ((pixels3 >> L_BLUE_SHIFT) & 0xff); |
2327 | 0 | v11r = area11 * ((pixels4 >> L_RED_SHIFT) & 0xff); |
2328 | 0 | v11g = area11 * ((pixels4 >> L_GREEN_SHIFT) & 0xff); |
2329 | 0 | v11b = area11 * ((pixels4 >> L_BLUE_SHIFT) & 0xff); |
2330 | 0 | pixel = (((v00r + v10r + v01r + v11r + 128) << 16) & 0xff000000) | |
2331 | 0 | (((v00g + v10g + v01g + v11g + 128) << 8) & 0x00ff0000) | |
2332 | 0 | ((v00b + v10b + v01b + v11b + 128) & 0x0000ff00); |
2333 | 0 | *(lined + j) = pixel; |
2334 | 0 | } |
2335 | 0 | } |
2336 | 0 | } |
2337 | | |
2338 | | |
2339 | | /*------------------------------------------------------------------* |
2340 | | * General linear interpolated gray scaling * |
2341 | | *------------------------------------------------------------------*/ |
2342 | | /*! |
2343 | | * \brief scaleGrayLILow() |
2344 | | * |
2345 | | * <pre> |
2346 | | * Notes: |
2347 | | * (1) We choose to divide each pixel into 16 x 16 sub-pixels. |
2348 | | * Linear interpolation is equivalent to finding the |
2349 | | * fractional area (i.e., number of sub-pixels divided |
2350 | | * by 256) associated with each of the four nearest src pixels, |
2351 | | * and weighting each pixel value by this fractional area. |
2352 | | * </pre> |
2353 | | */ |
2354 | | static void |
2355 | | scaleGrayLILow(l_uint32 *datad, |
2356 | | l_int32 wd, |
2357 | | l_int32 hd, |
2358 | | l_int32 wpld, |
2359 | | l_uint32 *datas, |
2360 | | l_int32 ws, |
2361 | | l_int32 hs, |
2362 | | l_int32 wpls) |
2363 | 0 | { |
2364 | 0 | l_int32 i, j, wm2, hm2; |
2365 | 0 | l_int32 xpm, ypm; /* location in src image, to 1/16 of a pixel */ |
2366 | 0 | l_int32 xp, yp, xf, yf; /* src pixel and pixel fraction coordinates */ |
2367 | 0 | l_int32 v00, v01, v10, v11, v00_val, v01_val, v10_val, v11_val; |
2368 | 0 | l_uint8 val; |
2369 | 0 | l_uint32 *lines, *lined; |
2370 | 0 | l_float32 scx, scy; |
2371 | | |
2372 | | /* (scx, scy) are scaling factors that are applied to the |
2373 | | * dest coords to get the corresponding src coords. |
2374 | | * We need them because we iterate over dest pixels |
2375 | | * and must find the corresponding set of src pixels. */ |
2376 | 0 | scx = 16.f * (l_float32)ws / (l_float32)wd; |
2377 | 0 | scy = 16.f * (l_float32)hs / (l_float32)hd; |
2378 | 0 | wm2 = ws - 2; |
2379 | 0 | hm2 = hs - 2; |
2380 | | |
2381 | | /* Iterate over the destination pixels */ |
2382 | 0 | for (i = 0; i < hd; i++) { |
2383 | 0 | ypm = (l_int32)(scy * (l_float32)i); |
2384 | 0 | yp = ypm >> 4; |
2385 | 0 | yf = ypm & 0x0f; |
2386 | 0 | lined = datad + i * wpld; |
2387 | 0 | lines = datas + yp * wpls; |
2388 | 0 | for (j = 0; j < wd; j++) { |
2389 | 0 | xpm = (l_int32)(scx * (l_float32)j); |
2390 | 0 | xp = xpm >> 4; |
2391 | 0 | xf = xpm & 0x0f; |
2392 | | |
2393 | | /* Do bilinear interpolation. Without this, we could |
2394 | | * simply subsample: |
2395 | | * SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp)); |
2396 | | * which is faster but gives lousy results! */ |
2397 | 0 | v00_val = GET_DATA_BYTE(lines, xp); |
2398 | 0 | if (xp > wm2 || yp > hm2) { |
2399 | 0 | if (yp > hm2 && xp <= wm2) { /* pixels near bottom */ |
2400 | 0 | v01_val = v00_val; |
2401 | 0 | v10_val = GET_DATA_BYTE(lines, xp + 1); |
2402 | 0 | v11_val = v10_val; |
2403 | 0 | } else if (xp > wm2 && yp <= hm2) { /* pixels near rt side */ |
2404 | 0 | v01_val = GET_DATA_BYTE(lines + wpls, xp); |
2405 | 0 | v10_val = v00_val; |
2406 | 0 | v11_val = v01_val; |
2407 | 0 | } else { /* pixels at LR corner */ |
2408 | 0 | v10_val = v01_val = v11_val = v00_val; |
2409 | 0 | } |
2410 | 0 | } else { |
2411 | 0 | v10_val = GET_DATA_BYTE(lines, xp + 1); |
2412 | 0 | v01_val = GET_DATA_BYTE(lines + wpls, xp); |
2413 | 0 | v11_val = GET_DATA_BYTE(lines + wpls, xp + 1); |
2414 | 0 | } |
2415 | |
|
2416 | 0 | v00 = (16 - xf) * (16 - yf) * v00_val; |
2417 | 0 | v10 = xf * (16 - yf) * v10_val; |
2418 | 0 | v01 = (16 - xf) * yf * v01_val; |
2419 | 0 | v11 = xf * yf * v11_val; |
2420 | |
|
2421 | 0 | val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256); |
2422 | 0 | SET_DATA_BYTE(lined, j, val); |
2423 | 0 | } |
2424 | 0 | } |
2425 | 0 | } |
2426 | | |
2427 | | |
2428 | | /*------------------------------------------------------------------* |
2429 | | * 2x linear interpolated color scaling * |
2430 | | *------------------------------------------------------------------*/ |
2431 | | /*! |
2432 | | * \brief scaleColor2xLILow() |
2433 | | * |
2434 | | * <pre> |
2435 | | * Notes: |
2436 | | * (1) This is a special case of 2x expansion by linear |
2437 | | * interpolation. Each src pixel contains 4 dest pixels. |
2438 | | * The 4 dest pixels in src pixel 1 are numbered at |
2439 | | * their UL corners. The 4 dest pixels in src pixel 1 |
2440 | | * are related to that src pixel and its 3 neighboring |
2441 | | * src pixels as follows: |
2442 | | * |
2443 | | * 1-----2-----|-----|-----| |
2444 | | * | | | | | |
2445 | | * | | | | | |
2446 | | * src 1 --> 3-----4-----| | | <-- src 2 |
2447 | | * | | | | | |
2448 | | * | | | | | |
2449 | | * |-----|-----|-----|-----| |
2450 | | * | | | | | |
2451 | | * | | | | | |
2452 | | * src 3 --> | | | | | <-- src 4 |
2453 | | * | | | | | |
2454 | | * | | | | | |
2455 | | * |-----|-----|-----|-----| |
2456 | | * |
2457 | | * dest src |
2458 | | * ---- --- |
2459 | | * dp1 = sp1 |
2460 | | * dp2 = (sp1 + sp2) / 2 |
2461 | | * dp3 = (sp1 + sp3) / 2 |
2462 | | * dp4 = (sp1 + sp2 + sp3 + sp4) / 4 |
2463 | | * |
2464 | | * (2) We iterate over the src pixels, and unroll the calculation |
2465 | | * for each set of 4 dest pixels corresponding to that src |
2466 | | * pixel, caching pixels for the next src pixel whenever possible. |
2467 | | * The method is exactly analogous to the one we use for |
2468 | | * scaleGray2xLILow() and its line version. |
2469 | | * </pre> |
2470 | | */ |
2471 | | static void |
2472 | | scaleColor2xLILow(l_uint32 *datad, |
2473 | | l_int32 wpld, |
2474 | | l_uint32 *datas, |
2475 | | l_int32 ws, |
2476 | | l_int32 hs, |
2477 | | l_int32 wpls) |
2478 | 0 | { |
2479 | 0 | l_int32 i, hsm; |
2480 | 0 | l_uint32 *lines, *lined; |
2481 | |
|
2482 | 0 | hsm = hs - 1; |
2483 | | |
2484 | | /* We're taking 2 src and 2 dest lines at a time, |
2485 | | * and for each src line, we're computing 2 dest lines. |
2486 | | * Call these 2 dest lines: destline1 and destline2. |
2487 | | * The first src line is used for destline 1. |
2488 | | * On all but the last src line, both src lines are |
2489 | | * used in the linear interpolation for destline2. |
2490 | | * On the last src line, both destline1 and destline2 |
2491 | | * are computed using only that src line (because there |
2492 | | * isn't a lower src line). */ |
2493 | | |
2494 | | /* iterate over all but the last src line */ |
2495 | 0 | for (i = 0; i < hsm; i++) { |
2496 | 0 | lines = datas + i * wpls; |
2497 | 0 | lined = datad + 2 * i * wpld; |
2498 | 0 | scaleColor2xLILineLow(lined, wpld, lines, ws, wpls, 0); |
2499 | 0 | } |
2500 | | |
2501 | | /* last src line */ |
2502 | 0 | lines = datas + hsm * wpls; |
2503 | 0 | lined = datad + 2 * hsm * wpld; |
2504 | 0 | scaleColor2xLILineLow(lined, wpld, lines, ws, wpls, 1); |
2505 | 0 | } |
2506 | | |
2507 | | |
2508 | | /*! |
2509 | | * \brief scaleColor2xLILineLow() |
2510 | | * |
2511 | | * \param[in] lined ptr to top destline, to be made from current src line |
2512 | | * \param[in] wpld |
2513 | | * \param[in] lines ptr to current src line |
2514 | | * \param[in] ws |
2515 | | * \param[in] wpls |
2516 | | * \param[in] lastlineflag 1 if last src line; 0 otherwise |
2517 | | * \return void |
2518 | | */ |
2519 | | static void |
2520 | | scaleColor2xLILineLow(l_uint32 *lined, |
2521 | | l_int32 wpld, |
2522 | | l_uint32 *lines, |
2523 | | l_int32 ws, |
2524 | | l_int32 wpls, |
2525 | | l_int32 lastlineflag) |
2526 | 0 | { |
2527 | 0 | l_int32 j, jd, wsm; |
2528 | 0 | l_uint32 rval1, rval2, rval3, rval4, gval1, gval2, gval3, gval4; |
2529 | 0 | l_uint32 bval1, bval2, bval3, bval4; |
2530 | 0 | l_uint32 pixels1, pixels2, pixels3, pixels4, pixel; |
2531 | 0 | l_uint32 *linesp, *linedp; |
2532 | |
|
2533 | 0 | wsm = ws - 1; |
2534 | |
|
2535 | 0 | if (lastlineflag == 0) { |
2536 | 0 | linesp = lines + wpls; |
2537 | 0 | linedp = lined + wpld; |
2538 | 0 | pixels1 = *lines; |
2539 | 0 | pixels3 = *linesp; |
2540 | | |
2541 | | /* initialize with v(2) and v(4) */ |
2542 | 0 | rval2 = pixels1 >> 24; |
2543 | 0 | gval2 = (pixels1 >> 16) & 0xff; |
2544 | 0 | bval2 = (pixels1 >> 8) & 0xff; |
2545 | 0 | rval4 = pixels3 >> 24; |
2546 | 0 | gval4 = (pixels3 >> 16) & 0xff; |
2547 | 0 | bval4 = (pixels3 >> 8) & 0xff; |
2548 | |
|
2549 | 0 | for (j = 0, jd = 0; j < wsm; j++, jd += 2) { |
2550 | | /* shift in previous src values */ |
2551 | 0 | rval1 = rval2; |
2552 | 0 | gval1 = gval2; |
2553 | 0 | bval1 = bval2; |
2554 | 0 | rval3 = rval4; |
2555 | 0 | gval3 = gval4; |
2556 | 0 | bval3 = bval4; |
2557 | | /* get new src values */ |
2558 | 0 | pixels2 = *(lines + j + 1); |
2559 | 0 | pixels4 = *(linesp + j + 1); |
2560 | 0 | rval2 = pixels2 >> 24; |
2561 | 0 | gval2 = (pixels2 >> 16) & 0xff; |
2562 | 0 | bval2 = (pixels2 >> 8) & 0xff; |
2563 | 0 | rval4 = pixels4 >> 24; |
2564 | 0 | gval4 = (pixels4 >> 16) & 0xff; |
2565 | 0 | bval4 = (pixels4 >> 8) & 0xff; |
2566 | | /* save dest values */ |
2567 | 0 | pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); |
2568 | 0 | *(lined + jd) = pixel; /* pix 1 */ |
2569 | 0 | pixel = ((((rval1 + rval2) << 23) & 0xff000000) | |
2570 | 0 | (((gval1 + gval2) << 15) & 0x00ff0000) | |
2571 | 0 | (((bval1 + bval2) << 7) & 0x0000ff00)); |
2572 | 0 | *(lined + jd + 1) = pixel; /* pix 2 */ |
2573 | 0 | pixel = ((((rval1 + rval3) << 23) & 0xff000000) | |
2574 | 0 | (((gval1 + gval3) << 15) & 0x00ff0000) | |
2575 | 0 | (((bval1 + bval3) << 7) & 0x0000ff00)); |
2576 | 0 | *(linedp + jd) = pixel; /* pix 3 */ |
2577 | 0 | pixel = ((((rval1 + rval2 + rval3 + rval4) << 22) & 0xff000000) | |
2578 | 0 | (((gval1 + gval2 + gval3 + gval4) << 14) & 0x00ff0000) | |
2579 | 0 | (((bval1 + bval2 + bval3 + bval4) << 6) & 0x0000ff00)); |
2580 | 0 | *(linedp + jd + 1) = pixel; /* pix 4 */ |
2581 | 0 | } |
2582 | | /* last src pixel on line */ |
2583 | 0 | rval1 = rval2; |
2584 | 0 | gval1 = gval2; |
2585 | 0 | bval1 = bval2; |
2586 | 0 | rval3 = rval4; |
2587 | 0 | gval3 = gval4; |
2588 | 0 | bval3 = bval4; |
2589 | 0 | pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); |
2590 | 0 | *(lined + 2 * wsm) = pixel; /* pix 1 */ |
2591 | 0 | *(lined + 2 * wsm + 1) = pixel; /* pix 2 */ |
2592 | 0 | pixel = ((((rval1 + rval3) << 23) & 0xff000000) | |
2593 | 0 | (((gval1 + gval3) << 15) & 0x00ff0000) | |
2594 | 0 | (((bval1 + bval3) << 7) & 0x0000ff00)); |
2595 | 0 | *(linedp + 2 * wsm) = pixel; /* pix 3 */ |
2596 | 0 | *(linedp + 2 * wsm + 1) = pixel; /* pix 4 */ |
2597 | 0 | } else { /* last row of src pixels: lastlineflag == 1 */ |
2598 | 0 | linedp = lined + wpld; |
2599 | 0 | pixels2 = *lines; |
2600 | 0 | rval2 = pixels2 >> 24; |
2601 | 0 | gval2 = (pixels2 >> 16) & 0xff; |
2602 | 0 | bval2 = (pixels2 >> 8) & 0xff; |
2603 | 0 | for (j = 0, jd = 0; j < wsm; j++, jd += 2) { |
2604 | 0 | rval1 = rval2; |
2605 | 0 | gval1 = gval2; |
2606 | 0 | bval1 = bval2; |
2607 | 0 | pixels2 = *(lines + j + 1); |
2608 | 0 | rval2 = pixels2 >> 24; |
2609 | 0 | gval2 = (pixels2 >> 16) & 0xff; |
2610 | 0 | bval2 = (pixels2 >> 8) & 0xff; |
2611 | 0 | pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); |
2612 | 0 | *(lined + jd) = pixel; /* pix 1 */ |
2613 | 0 | *(linedp + jd) = pixel; /* pix 2 */ |
2614 | 0 | pixel = ((((rval1 + rval2) << 23) & 0xff000000) | |
2615 | 0 | (((gval1 + gval2) << 15) & 0x00ff0000) | |
2616 | 0 | (((bval1 + bval2) << 7) & 0x0000ff00)); |
2617 | 0 | *(lined + jd + 1) = pixel; /* pix 3 */ |
2618 | 0 | *(linedp + jd + 1) = pixel; /* pix 4 */ |
2619 | 0 | } |
2620 | 0 | rval1 = rval2; |
2621 | 0 | gval1 = gval2; |
2622 | 0 | bval1 = bval2; |
2623 | 0 | pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); |
2624 | 0 | *(lined + 2 * wsm) = pixel; /* pix 1 */ |
2625 | 0 | *(lined + 2 * wsm + 1) = pixel; /* pix 2 */ |
2626 | 0 | *(linedp + 2 * wsm) = pixel; /* pix 3 */ |
2627 | 0 | *(linedp + 2 * wsm + 1) = pixel; /* pix 4 */ |
2628 | 0 | } |
2629 | 0 | } |
2630 | | |
2631 | | |
2632 | | /*------------------------------------------------------------------* |
2633 | | * 2x linear interpolated gray scaling * |
2634 | | *------------------------------------------------------------------*/ |
2635 | | /*! |
2636 | | * \brief scaleGray2xLILow() |
2637 | | * |
2638 | | * <pre> |
2639 | | * Notes: |
2640 | | * (1) This is a special case of 2x expansion by linear |
2641 | | * interpolation. Each src pixel contains 4 dest pixels. |
2642 | | * The 4 dest pixels in src pixel 1 are numbered at |
2643 | | * their UL corners. The 4 dest pixels in src pixel 1 |
2644 | | * are related to that src pixel and its 3 neighboring |
2645 | | * src pixels as follows: |
2646 | | * |
2647 | | * 1-----2-----|-----|-----| |
2648 | | * | | | | | |
2649 | | * | | | | | |
2650 | | * src 1 --> 3-----4-----| | | <-- src 2 |
2651 | | * | | | | | |
2652 | | * | | | | | |
2653 | | * |-----|-----|-----|-----| |
2654 | | * | | | | | |
2655 | | * | | | | | |
2656 | | * src 3 --> | | | | | <-- src 4 |
2657 | | * | | | | | |
2658 | | * | | | | | |
2659 | | * |-----|-----|-----|-----| |
2660 | | * |
2661 | | * dest src |
2662 | | * ---- --- |
2663 | | * dp1 = sp1 |
2664 | | * dp2 = (sp1 + sp2) / 2 |
2665 | | * dp3 = (sp1 + sp3) / 2 |
2666 | | * dp4 = (sp1 + sp2 + sp3 + sp4) / 4 |
2667 | | * |
2668 | | * (2) We iterate over the src pixels, and unroll the calculation |
2669 | | * for each set of 4 dest pixels corresponding to that src |
2670 | | * pixel, caching pixels for the next src pixel whenever possible. |
2671 | | * </pre> |
2672 | | */ |
2673 | | static void |
2674 | | scaleGray2xLILow(l_uint32 *datad, |
2675 | | l_int32 wpld, |
2676 | | l_uint32 *datas, |
2677 | | l_int32 ws, |
2678 | | l_int32 hs, |
2679 | | l_int32 wpls) |
2680 | 0 | { |
2681 | 0 | l_int32 i, hsm; |
2682 | 0 | l_uint32 *lines, *lined; |
2683 | |
|
2684 | 0 | hsm = hs - 1; |
2685 | | |
2686 | | /* We're taking 2 src and 2 dest lines at a time, |
2687 | | * and for each src line, we're computing 2 dest lines. |
2688 | | * Call these 2 dest lines: destline1 and destline2. |
2689 | | * The first src line is used for destline 1. |
2690 | | * On all but the last src line, both src lines are |
2691 | | * used in the linear interpolation for destline2. |
2692 | | * On the last src line, both destline1 and destline2 |
2693 | | * are computed using only that src line (because there |
2694 | | * isn't a lower src line). */ |
2695 | | |
2696 | | /* iterate over all but the last src line */ |
2697 | 0 | for (i = 0; i < hsm; i++) { |
2698 | 0 | lines = datas + i * wpls; |
2699 | 0 | lined = datad + 2 * i * wpld; |
2700 | 0 | scaleGray2xLILineLow(lined, wpld, lines, ws, wpls, 0); |
2701 | 0 | } |
2702 | | |
2703 | | /* last src line */ |
2704 | 0 | lines = datas + hsm * wpls; |
2705 | 0 | lined = datad + 2 * hsm * wpld; |
2706 | 0 | scaleGray2xLILineLow(lined, wpld, lines, ws, wpls, 1); |
2707 | 0 | } |
2708 | | |
2709 | | |
2710 | | /*! |
2711 | | * \brief scaleGray2xLILineLow() |
2712 | | * |
2713 | | * \param[in] lined ptr to top destline, to be made from current src line |
2714 | | * \param[in] wpld |
2715 | | * \param[in] lines ptr to current src line |
2716 | | * \param[in] ws |
2717 | | * \param[in] wpls |
2718 | | * \param[in] lastlineflag 1 if last src line; 0 otherwise |
2719 | | * \return void |
2720 | | */ |
2721 | | static void |
2722 | | scaleGray2xLILineLow(l_uint32 *lined, |
2723 | | l_int32 wpld, |
2724 | | l_uint32 *lines, |
2725 | | l_int32 ws, |
2726 | | l_int32 wpls, |
2727 | | l_int32 lastlineflag) |
2728 | 0 | { |
2729 | 0 | l_int32 j, jd, wsm, w; |
2730 | 0 | l_uint32 sval1, sval2, sval3, sval4; |
2731 | 0 | l_uint32 *linesp, *linedp; |
2732 | 0 | l_uint32 words, wordsp, wordd, worddp; |
2733 | |
|
2734 | 0 | wsm = ws - 1; |
2735 | |
|
2736 | 0 | if (lastlineflag == 0) { |
2737 | 0 | linesp = lines + wpls; |
2738 | 0 | linedp = lined + wpld; |
2739 | | |
2740 | | /* Unroll the loop 4x and work on full words */ |
2741 | 0 | words = lines[0]; |
2742 | 0 | wordsp = linesp[0]; |
2743 | 0 | sval2 = (words >> 24) & 0xff; |
2744 | 0 | sval4 = (wordsp >> 24) & 0xff; |
2745 | 0 | for (j = 0, jd = 0, w = 0; j + 3 < wsm; j += 4, jd += 8, w++) { |
2746 | | /* At the top of the loop, |
2747 | | * words == lines[w], wordsp == linesp[w] |
2748 | | * and the top bytes of those have been loaded into |
2749 | | * sval2 and sval4. */ |
2750 | 0 | sval1 = sval2; |
2751 | 0 | sval2 = (words >> 16) & 0xff; |
2752 | 0 | sval3 = sval4; |
2753 | 0 | sval4 = (wordsp >> 16) & 0xff; |
2754 | 0 | wordd = (sval1 << 24) | (((sval1 + sval2) >> 1) << 16); |
2755 | 0 | worddp = (((sval1 + sval3) >> 1) << 24) | |
2756 | 0 | (((sval1 + sval2 + sval3 + sval4) >> 2) << 16); |
2757 | |
|
2758 | 0 | sval1 = sval2; |
2759 | 0 | sval2 = (words >> 8) & 0xff; |
2760 | 0 | sval3 = sval4; |
2761 | 0 | sval4 = (wordsp >> 8) & 0xff; |
2762 | 0 | wordd |= (sval1 << 8) | ((sval1 + sval2) >> 1); |
2763 | 0 | worddp |= (((sval1 + sval3) >> 1) << 8) | |
2764 | 0 | ((sval1 + sval2 + sval3 + sval4) >> 2); |
2765 | 0 | lined[w * 2] = wordd; |
2766 | 0 | linedp[w * 2] = worddp; |
2767 | |
|
2768 | 0 | sval1 = sval2; |
2769 | 0 | sval2 = words & 0xff; |
2770 | 0 | sval3 = sval4; |
2771 | 0 | sval4 = wordsp & 0xff; |
2772 | 0 | wordd = (sval1 << 24) | /* pix 1 */ |
2773 | 0 | (((sval1 + sval2) >> 1) << 16); /* pix 2 */ |
2774 | 0 | worddp = (((sval1 + sval3) >> 1) << 24) | /* pix 3 */ |
2775 | 0 | (((sval1 + sval2 + sval3 + sval4) >> 2) << 16); /* pix 4 */ |
2776 | | |
2777 | | /* Load the next word as we need its first byte */ |
2778 | 0 | words = lines[w + 1]; |
2779 | 0 | wordsp = linesp[w + 1]; |
2780 | 0 | sval1 = sval2; |
2781 | 0 | sval2 = (words >> 24) & 0xff; |
2782 | 0 | sval3 = sval4; |
2783 | 0 | sval4 = (wordsp >> 24) & 0xff; |
2784 | 0 | wordd |= (sval1 << 8) | /* pix 1 */ |
2785 | 0 | ((sval1 + sval2) >> 1); /* pix 2 */ |
2786 | 0 | worddp |= (((sval1 + sval3) >> 1) << 8) | /* pix 3 */ |
2787 | 0 | ((sval1 + sval2 + sval3 + sval4) >> 2); /* pix 4 */ |
2788 | 0 | lined[w * 2 + 1] = wordd; |
2789 | 0 | linedp[w * 2 + 1] = worddp; |
2790 | 0 | } |
2791 | | |
2792 | | /* Finish up the last word */ |
2793 | 0 | for (; j < wsm; j++, jd += 2) { |
2794 | 0 | sval1 = sval2; |
2795 | 0 | sval3 = sval4; |
2796 | 0 | sval2 = GET_DATA_BYTE(lines, j + 1); |
2797 | 0 | sval4 = GET_DATA_BYTE(linesp, j + 1); |
2798 | 0 | SET_DATA_BYTE(lined, jd, sval1); /* pix 1 */ |
2799 | 0 | SET_DATA_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */ |
2800 | 0 | SET_DATA_BYTE(linedp, jd, (sval1 + sval3) / 2); /* pix 3 */ |
2801 | 0 | SET_DATA_BYTE(linedp, jd + 1, |
2802 | 0 | (sval1 + sval2 + sval3 + sval4) / 4); /* pix 4 */ |
2803 | 0 | } |
2804 | 0 | sval1 = sval2; |
2805 | 0 | sval3 = sval4; |
2806 | 0 | SET_DATA_BYTE(lined, 2 * wsm, sval1); /* pix 1 */ |
2807 | 0 | SET_DATA_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */ |
2808 | 0 | SET_DATA_BYTE(linedp, 2 * wsm, (sval1 + sval3) / 2); /* pix 3 */ |
2809 | 0 | SET_DATA_BYTE(linedp, 2 * wsm + 1, (sval1 + sval3) / 2); /* pix 4 */ |
2810 | |
|
2811 | | #if DEBUG_UNROLLING |
2812 | | #define CHECK_BYTE(a, b, c) if (GET_DATA_BYTE(a, b) != c) {\ |
2813 | | lept_stderr("Error: mismatch at %d, %d vs %d\n", \ |
2814 | | j, GET_DATA_BYTE(a, b), c); } |
2815 | | |
2816 | | sval2 = GET_DATA_BYTE(lines, 0); |
2817 | | sval4 = GET_DATA_BYTE(linesp, 0); |
2818 | | for (j = 0, jd = 0; j < wsm; j++, jd += 2) { |
2819 | | sval1 = sval2; |
2820 | | sval3 = sval4; |
2821 | | sval2 = GET_DATA_BYTE(lines, j + 1); |
2822 | | sval4 = GET_DATA_BYTE(linesp, j + 1); |
2823 | | CHECK_BYTE(lined, jd, sval1); /* pix 1 */ |
2824 | | CHECK_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */ |
2825 | | CHECK_BYTE(linedp, jd, (sval1 + sval3) / 2); /* pix 3 */ |
2826 | | CHECK_BYTE(linedp, jd + 1, |
2827 | | (sval1 + sval2 + sval3 + sval4) / 4); /* pix 4 */ |
2828 | | } |
2829 | | sval1 = sval2; |
2830 | | sval3 = sval4; |
2831 | | CHECK_BYTE(lined, 2 * wsm, sval1); /* pix 1 */ |
2832 | | CHECK_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */ |
2833 | | CHECK_BYTE(linedp, 2 * wsm, (sval1 + sval3) / 2); /* pix 3 */ |
2834 | | CHECK_BYTE(linedp, 2 * wsm + 1, (sval1 + sval3) / 2); /* pix 4 */ |
2835 | | #undef CHECK_BYTE |
2836 | | #endif |
2837 | 0 | } else { /* last row of src pixels: lastlineflag == 1 */ |
2838 | 0 | linedp = lined + wpld; |
2839 | 0 | sval2 = GET_DATA_BYTE(lines, 0); |
2840 | 0 | for (j = 0, jd = 0; j < wsm; j++, jd += 2) { |
2841 | 0 | sval1 = sval2; |
2842 | 0 | sval2 = GET_DATA_BYTE(lines, j + 1); |
2843 | 0 | SET_DATA_BYTE(lined, jd, sval1); /* pix 1 */ |
2844 | 0 | SET_DATA_BYTE(linedp, jd, sval1); /* pix 3 */ |
2845 | 0 | SET_DATA_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */ |
2846 | 0 | SET_DATA_BYTE(linedp, jd + 1, (sval1 + sval2) / 2); /* pix 4 */ |
2847 | 0 | } |
2848 | 0 | sval1 = sval2; |
2849 | 0 | SET_DATA_BYTE(lined, 2 * wsm, sval1); /* pix 1 */ |
2850 | 0 | SET_DATA_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */ |
2851 | 0 | SET_DATA_BYTE(linedp, 2 * wsm, sval1); /* pix 3 */ |
2852 | 0 | SET_DATA_BYTE(linedp, 2 * wsm + 1, sval1); /* pix 4 */ |
2853 | 0 | } |
2854 | 0 | } |
2855 | | |
2856 | | |
2857 | | /*------------------------------------------------------------------* |
2858 | | * 4x linear interpolated gray scaling * |
2859 | | *------------------------------------------------------------------*/ |
2860 | | /*! |
2861 | | * \brief scaleGray4xLILow() |
2862 | | * |
2863 | | * <pre> |
2864 | | * Notes: |
2865 | | * (1) This is a special case of 4x expansion by linear |
2866 | | * interpolation. Each src pixel contains 16 dest pixels. |
2867 | | * The 16 dest pixels in src pixel 1 are numbered at |
2868 | | * their UL corners. The 16 dest pixels in src pixel 1 |
2869 | | * are related to that src pixel and its 3 neighboring |
2870 | | * src pixels as follows: |
2871 | | * |
2872 | | * 1---2---3---4---|---|---|---|---| |
2873 | | * | | | | | | | | | |
2874 | | * 5---6---7---8---|---|---|---|---| |
2875 | | * | | | | | | | | | |
2876 | | * src 1 --> 9---a---b---c---|---|---|---|---| <-- src 2 |
2877 | | * | | | | | | | | | |
2878 | | * d---e---f---g---|---|---|---|---| |
2879 | | * | | | | | | | | | |
2880 | | * |===|===|===|===|===|===|===|===| |
2881 | | * | | | | | | | | | |
2882 | | * |---|---|---|---|---|---|---|---| |
2883 | | * | | | | | | | | | |
2884 | | * src 3 --> |---|---|---|---|---|---|---|---| <-- src 4 |
2885 | | * | | | | | | | | | |
2886 | | * |---|---|---|---|---|---|---|---| |
2887 | | * | | | | | | | | | |
2888 | | * |---|---|---|---|---|---|---|---| |
2889 | | * |
2890 | | * dest src |
2891 | | * ---- --- |
2892 | | * dp1 = sp1 |
2893 | | * dp2 = (3 * sp1 + sp2) / 4 |
2894 | | * dp3 = (sp1 + sp2) / 2 |
2895 | | * dp4 = (sp1 + 3 * sp2) / 4 |
2896 | | * dp5 = (3 * sp1 + sp3) / 4 |
2897 | | * dp6 = (9 * sp1 + 3 * sp2 + 3 * sp3 + sp4) / 16 |
2898 | | * dp7 = (3 * sp1 + 3 * sp2 + sp3 + sp4) / 8 |
2899 | | * dp8 = (3 * sp1 + 9 * sp2 + 1 * sp3 + 3 * sp4) / 16 |
2900 | | * dp9 = (sp1 + sp3) / 2 |
2901 | | * dp10 = (3 * sp1 + sp2 + 3 * sp3 + sp4) / 8 |
2902 | | * dp11 = (sp1 + sp2 + sp3 + sp4) / 4 |
2903 | | * dp12 = (sp1 + 3 * sp2 + sp3 + 3 * sp4) / 8 |
2904 | | * dp13 = (sp1 + 3 * sp3) / 4 |
2905 | | * dp14 = (3 * sp1 + sp2 + 9 * sp3 + 3 * sp4) / 16 |
2906 | | * dp15 = (sp1 + sp2 + 3 * sp3 + 3 * sp4) / 8 |
2907 | | * dp16 = (sp1 + 3 * sp2 + 3 * sp3 + 9 * sp4) / 16 |
2908 | | * |
2909 | | * (2) We iterate over the src pixels, and unroll the calculation |
2910 | | * for each set of 16 dest pixels corresponding to that src |
2911 | | * pixel, caching pixels for the next src pixel whenever possible. |
2912 | | * </pre> |
2913 | | */ |
2914 | | static void |
2915 | | scaleGray4xLILow(l_uint32 *datad, |
2916 | | l_int32 wpld, |
2917 | | l_uint32 *datas, |
2918 | | l_int32 ws, |
2919 | | l_int32 hs, |
2920 | | l_int32 wpls) |
2921 | 0 | { |
2922 | 0 | l_int32 i, hsm; |
2923 | 0 | l_uint32 *lines, *lined; |
2924 | |
|
2925 | 0 | hsm = hs - 1; |
2926 | | |
2927 | | /* We're taking 2 src and 4 dest lines at a time, |
2928 | | * and for each src line, we're computing 4 dest lines. |
2929 | | * Call these 4 dest lines: destline1 - destline4. |
2930 | | * The first src line is used for destline 1. |
2931 | | * Two src lines are used for all other dest lines, |
2932 | | * except for the last 4 dest lines, which are computed |
2933 | | * using only the last src line. */ |
2934 | | |
2935 | | /* iterate over all but the last src line */ |
2936 | 0 | for (i = 0; i < hsm; i++) { |
2937 | 0 | lines = datas + i * wpls; |
2938 | 0 | lined = datad + 4 * i * wpld; |
2939 | 0 | scaleGray4xLILineLow(lined, wpld, lines, ws, wpls, 0); |
2940 | 0 | } |
2941 | | |
2942 | | /* last src line */ |
2943 | 0 | lines = datas + hsm * wpls; |
2944 | 0 | lined = datad + 4 * hsm * wpld; |
2945 | 0 | scaleGray4xLILineLow(lined, wpld, lines, ws, wpls, 1); |
2946 | 0 | } |
2947 | | |
2948 | | |
2949 | | /*! |
2950 | | * \brief scaleGray4xLILineLow() |
2951 | | * |
2952 | | * \param[in] lined ptr to top destline, to be made from current src line |
2953 | | * \param[in] wpld |
2954 | | * \param[in] lines ptr to current src line |
2955 | | * \param[in] ws |
2956 | | * \param[in] wpls |
2957 | | * \param[in] lastlineflag 1 if last src line; 0 otherwise |
2958 | | * \return void |
2959 | | */ |
2960 | | static void |
2961 | | scaleGray4xLILineLow(l_uint32 *lined, |
2962 | | l_int32 wpld, |
2963 | | l_uint32 *lines, |
2964 | | l_int32 ws, |
2965 | | l_int32 wpls, |
2966 | | l_int32 lastlineflag) |
2967 | 0 | { |
2968 | 0 | l_int32 j, jd, wsm, wsm4; |
2969 | 0 | l_int32 s1, s2, s3, s4, s1t, s2t, s3t, s4t; |
2970 | 0 | l_uint32 *linesp, *linedp1, *linedp2, *linedp3; |
2971 | |
|
2972 | 0 | wsm = ws - 1; |
2973 | 0 | wsm4 = 4 * wsm; |
2974 | |
|
2975 | 0 | if (lastlineflag == 0) { |
2976 | 0 | linesp = lines + wpls; |
2977 | 0 | linedp1 = lined + wpld; |
2978 | 0 | linedp2 = lined + 2 * wpld; |
2979 | 0 | linedp3 = lined + 3 * wpld; |
2980 | 0 | s2 = GET_DATA_BYTE(lines, 0); |
2981 | 0 | s4 = GET_DATA_BYTE(linesp, 0); |
2982 | 0 | for (j = 0, jd = 0; j < wsm; j++, jd += 4) { |
2983 | 0 | s1 = s2; |
2984 | 0 | s3 = s4; |
2985 | 0 | s2 = GET_DATA_BYTE(lines, j + 1); |
2986 | 0 | s4 = GET_DATA_BYTE(linesp, j + 1); |
2987 | 0 | s1t = 3 * s1; |
2988 | 0 | s2t = 3 * s2; |
2989 | 0 | s3t = 3 * s3; |
2990 | 0 | s4t = 3 * s4; |
2991 | 0 | SET_DATA_BYTE(lined, jd, s1); /* d1 */ |
2992 | 0 | SET_DATA_BYTE(lined, jd + 1, (s1t + s2) / 4); /* d2 */ |
2993 | 0 | SET_DATA_BYTE(lined, jd + 2, (s1 + s2) / 2); /* d3 */ |
2994 | 0 | SET_DATA_BYTE(lined, jd + 3, (s1 + s2t) / 4); /* d4 */ |
2995 | 0 | SET_DATA_BYTE(linedp1, jd, (s1t + s3) / 4); /* d5 */ |
2996 | 0 | SET_DATA_BYTE(linedp1, jd + 1, (9*s1 + s2t + s3t + s4) / 16); /*d6*/ |
2997 | 0 | SET_DATA_BYTE(linedp1, jd + 2, (s1t + s2t + s3 + s4) / 8); /* d7 */ |
2998 | 0 | SET_DATA_BYTE(linedp1, jd + 3, (s1t + 9*s2 + s3 + s4t) / 16);/*d8*/ |
2999 | 0 | SET_DATA_BYTE(linedp2, jd, (s1 + s3) / 2); /* d9 */ |
3000 | 0 | SET_DATA_BYTE(linedp2, jd + 1, (s1t + s2 + s3t + s4) / 8);/* d10 */ |
3001 | 0 | SET_DATA_BYTE(linedp2, jd + 2, (s1 + s2 + s3 + s4) / 4); /* d11 */ |
3002 | 0 | SET_DATA_BYTE(linedp2, jd + 3, (s1 + s2t + s3 + s4t) / 8);/* d12 */ |
3003 | 0 | SET_DATA_BYTE(linedp3, jd, (s1 + s3t) / 4); /* d13 */ |
3004 | 0 | SET_DATA_BYTE(linedp3, jd + 1, (s1t + s2 + 9*s3 + s4t) / 16);/*d14*/ |
3005 | 0 | SET_DATA_BYTE(linedp3, jd + 2, (s1 + s2 + s3t + s4t) / 8); /* d15 */ |
3006 | 0 | SET_DATA_BYTE(linedp3, jd + 3, (s1 + s2t + s3t + 9*s4) / 16);/*d16*/ |
3007 | 0 | } |
3008 | 0 | s1 = s2; |
3009 | 0 | s3 = s4; |
3010 | 0 | s1t = 3 * s1; |
3011 | 0 | s3t = 3 * s3; |
3012 | 0 | SET_DATA_BYTE(lined, wsm4, s1); /* d1 */ |
3013 | 0 | SET_DATA_BYTE(lined, wsm4 + 1, s1); /* d2 */ |
3014 | 0 | SET_DATA_BYTE(lined, wsm4 + 2, s1); /* d3 */ |
3015 | 0 | SET_DATA_BYTE(lined, wsm4 + 3, s1); /* d4 */ |
3016 | 0 | SET_DATA_BYTE(linedp1, wsm4, (s1t + s3) / 4); /* d5 */ |
3017 | 0 | SET_DATA_BYTE(linedp1, wsm4 + 1, (s1t + s3) / 4); /* d6 */ |
3018 | 0 | SET_DATA_BYTE(linedp1, wsm4 + 2, (s1t + s3) / 4); /* d7 */ |
3019 | 0 | SET_DATA_BYTE(linedp1, wsm4 + 3, (s1t + s3) / 4); /* d8 */ |
3020 | 0 | SET_DATA_BYTE(linedp2, wsm4, (s1 + s3) / 2); /* d9 */ |
3021 | 0 | SET_DATA_BYTE(linedp2, wsm4 + 1, (s1 + s3) / 2); /* d10 */ |
3022 | 0 | SET_DATA_BYTE(linedp2, wsm4 + 2, (s1 + s3) / 2); /* d11 */ |
3023 | 0 | SET_DATA_BYTE(linedp2, wsm4 + 3, (s1 + s3) / 2); /* d12 */ |
3024 | 0 | SET_DATA_BYTE(linedp3, wsm4, (s1 + s3t) / 4); /* d13 */ |
3025 | 0 | SET_DATA_BYTE(linedp3, wsm4 + 1, (s1 + s3t) / 4); /* d14 */ |
3026 | 0 | SET_DATA_BYTE(linedp3, wsm4 + 2, (s1 + s3t) / 4); /* d15 */ |
3027 | 0 | SET_DATA_BYTE(linedp3, wsm4 + 3, (s1 + s3t) / 4); /* d16 */ |
3028 | 0 | } else { /* last row of src pixels: lastlineflag == 1 */ |
3029 | 0 | linedp1 = lined + wpld; |
3030 | 0 | linedp2 = lined + 2 * wpld; |
3031 | 0 | linedp3 = lined + 3 * wpld; |
3032 | 0 | s2 = GET_DATA_BYTE(lines, 0); |
3033 | 0 | for (j = 0, jd = 0; j < wsm; j++, jd += 4) { |
3034 | 0 | s1 = s2; |
3035 | 0 | s2 = GET_DATA_BYTE(lines, j + 1); |
3036 | 0 | s1t = 3 * s1; |
3037 | 0 | s2t = 3 * s2; |
3038 | 0 | SET_DATA_BYTE(lined, jd, s1); /* d1 */ |
3039 | 0 | SET_DATA_BYTE(lined, jd + 1, (s1t + s2) / 4 ); /* d2 */ |
3040 | 0 | SET_DATA_BYTE(lined, jd + 2, (s1 + s2) / 2 ); /* d3 */ |
3041 | 0 | SET_DATA_BYTE(lined, jd + 3, (s1 + s2t) / 4 ); /* d4 */ |
3042 | 0 | SET_DATA_BYTE(linedp1, jd, s1); /* d5 */ |
3043 | 0 | SET_DATA_BYTE(linedp1, jd + 1, (s1t + s2) / 4 ); /* d6 */ |
3044 | 0 | SET_DATA_BYTE(linedp1, jd + 2, (s1 + s2) / 2 ); /* d7 */ |
3045 | 0 | SET_DATA_BYTE(linedp1, jd + 3, (s1 + s2t) / 4 ); /* d8 */ |
3046 | 0 | SET_DATA_BYTE(linedp2, jd, s1); /* d9 */ |
3047 | 0 | SET_DATA_BYTE(linedp2, jd + 1, (s1t + s2) / 4 ); /* d10 */ |
3048 | 0 | SET_DATA_BYTE(linedp2, jd + 2, (s1 + s2) / 2 ); /* d11 */ |
3049 | 0 | SET_DATA_BYTE(linedp2, jd + 3, (s1 + s2t) / 4 ); /* d12 */ |
3050 | 0 | SET_DATA_BYTE(linedp3, jd, s1); /* d13 */ |
3051 | 0 | SET_DATA_BYTE(linedp3, jd + 1, (s1t + s2) / 4 ); /* d14 */ |
3052 | 0 | SET_DATA_BYTE(linedp3, jd + 2, (s1 + s2) / 2 ); /* d15 */ |
3053 | 0 | SET_DATA_BYTE(linedp3, jd + 3, (s1 + s2t) / 4 ); /* d16 */ |
3054 | 0 | } |
3055 | 0 | s1 = s2; |
3056 | 0 | SET_DATA_BYTE(lined, wsm4, s1); /* d1 */ |
3057 | 0 | SET_DATA_BYTE(lined, wsm4 + 1, s1); /* d2 */ |
3058 | 0 | SET_DATA_BYTE(lined, wsm4 + 2, s1); /* d3 */ |
3059 | 0 | SET_DATA_BYTE(lined, wsm4 + 3, s1); /* d4 */ |
3060 | 0 | SET_DATA_BYTE(linedp1, wsm4, s1); /* d5 */ |
3061 | 0 | SET_DATA_BYTE(linedp1, wsm4 + 1, s1); /* d6 */ |
3062 | 0 | SET_DATA_BYTE(linedp1, wsm4 + 2, s1); /* d7 */ |
3063 | 0 | SET_DATA_BYTE(linedp1, wsm4 + 3, s1); /* d8 */ |
3064 | 0 | SET_DATA_BYTE(linedp2, wsm4, s1); /* d9 */ |
3065 | 0 | SET_DATA_BYTE(linedp2, wsm4 + 1, s1); /* d10 */ |
3066 | 0 | SET_DATA_BYTE(linedp2, wsm4 + 2, s1); /* d11 */ |
3067 | 0 | SET_DATA_BYTE(linedp2, wsm4 + 3, s1); /* d12 */ |
3068 | 0 | SET_DATA_BYTE(linedp3, wsm4, s1); /* d13 */ |
3069 | 0 | SET_DATA_BYTE(linedp3, wsm4 + 1, s1); /* d14 */ |
3070 | 0 | SET_DATA_BYTE(linedp3, wsm4 + 2, s1); /* d15 */ |
3071 | 0 | SET_DATA_BYTE(linedp3, wsm4 + 3, s1); /* d16 */ |
3072 | 0 | } |
3073 | 0 | } |
3074 | | |
3075 | | |
3076 | | /*------------------------------------------------------------------* |
3077 | | * Grayscale and color scaling by closest pixel sampling * |
3078 | | *------------------------------------------------------------------*/ |
3079 | | /*! |
3080 | | * \brief scaleBySamplingLow() |
3081 | | * |
3082 | | * <pre> |
3083 | | * Notes: |
3084 | | * (1) The dest must be cleared prior to this operation, |
3085 | | * and we clear it here in the low-level code. |
3086 | | * (2) We reuse dest pixels and dest pixel rows whenever |
3087 | | * possible. This speeds the upscaling; downscaling |
3088 | | * is done by strict subsampling and is unaffected. |
3089 | | * (3) Because we are sampling and not interpolating, this |
3090 | | * routine works directly, without conversion to full |
3091 | | * RGB color, for 2, 4 or 8 bpp palette color images. |
3092 | | * </pre> |
3093 | | */ |
3094 | | static l_int32 |
3095 | | scaleBySamplingLow(l_uint32 *datad, |
3096 | | l_int32 wd, |
3097 | | l_int32 hd, |
3098 | | l_int32 wpld, |
3099 | | l_uint32 *datas, |
3100 | | l_int32 ws, |
3101 | | l_int32 hs, |
3102 | | l_int32 d, |
3103 | | l_int32 wpls, |
3104 | | l_float32 shiftx, |
3105 | | l_float32 shifty) |
3106 | 0 | { |
3107 | 0 | l_int32 i, j; |
3108 | 0 | l_int32 xs, prevxs, sval; |
3109 | 0 | l_int32 *srow, *scol; |
3110 | 0 | l_uint32 csval; |
3111 | 0 | l_uint32 *lines, *prevlines, *lined, *prevlined; |
3112 | 0 | l_float32 wratio, hratio; |
3113 | |
|
3114 | 0 | if (d != 2 && d != 4 && d !=8 && d != 16 && d != 32) |
3115 | 0 | return ERROR_INT("pixel depth not supported", __func__, 1); |
3116 | | |
3117 | | /* Clear dest */ |
3118 | 0 | memset(datad, 0, 4LL * hd * wpld); |
3119 | | |
3120 | | /* the source row corresponding to dest row i ==> srow[i] |
3121 | | * the source col corresponding to dest col j ==> scol[j] */ |
3122 | 0 | if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) |
3123 | 0 | return ERROR_INT("srow not made", __func__, 1); |
3124 | 0 | if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { |
3125 | 0 | LEPT_FREE(srow); |
3126 | 0 | return ERROR_INT("scol not made", __func__, 1); |
3127 | 0 | } |
3128 | | |
3129 | 0 | wratio = (l_float32)ws / (l_float32)wd; |
3130 | 0 | hratio = (l_float32)hs / (l_float32)hd; |
3131 | 0 | for (i = 0; i < hd; i++) |
3132 | 0 | srow[i] = L_MIN((l_int32)(hratio * i + shifty), hs - 1); |
3133 | 0 | for (j = 0; j < wd; j++) |
3134 | 0 | scol[j] = L_MIN((l_int32)(wratio * j + shiftx), ws - 1); |
3135 | |
|
3136 | 0 | prevlines = NULL; |
3137 | 0 | for (i = 0; i < hd; i++) { |
3138 | 0 | lines = datas + srow[i] * wpls; |
3139 | 0 | lined = datad + i * wpld; |
3140 | 0 | if (lines != prevlines) { /* make dest from new source row */ |
3141 | 0 | prevxs = -1; |
3142 | 0 | sval = 0; |
3143 | 0 | csval = 0; |
3144 | 0 | if (d == 2) { |
3145 | 0 | for (j = 0; j < wd; j++) { |
3146 | 0 | xs = scol[j]; |
3147 | 0 | if (xs != prevxs) { /* get dest pix from source col */ |
3148 | 0 | sval = GET_DATA_DIBIT(lines, xs); |
3149 | 0 | SET_DATA_DIBIT(lined, j, sval); |
3150 | 0 | prevxs = xs; |
3151 | 0 | } else { /* copy prev dest pix */ |
3152 | 0 | SET_DATA_DIBIT(lined, j, sval); |
3153 | 0 | } |
3154 | 0 | } |
3155 | 0 | } else if (d == 4) { |
3156 | 0 | for (j = 0; j < wd; j++) { |
3157 | 0 | xs = scol[j]; |
3158 | 0 | if (xs != prevxs) { /* get dest pix from source col */ |
3159 | 0 | sval = GET_DATA_QBIT(lines, xs); |
3160 | 0 | SET_DATA_QBIT(lined, j, sval); |
3161 | 0 | prevxs = xs; |
3162 | 0 | } else { /* copy prev dest pix */ |
3163 | 0 | SET_DATA_QBIT(lined, j, sval); |
3164 | 0 | } |
3165 | 0 | } |
3166 | 0 | } else if (d == 8) { |
3167 | 0 | for (j = 0; j < wd; j++) { |
3168 | 0 | xs = scol[j]; |
3169 | 0 | if (xs != prevxs) { /* get dest pix from source col */ |
3170 | 0 | sval = GET_DATA_BYTE(lines, xs); |
3171 | 0 | SET_DATA_BYTE(lined, j, sval); |
3172 | 0 | prevxs = xs; |
3173 | 0 | } else { /* copy prev dest pix */ |
3174 | 0 | SET_DATA_BYTE(lined, j, sval); |
3175 | 0 | } |
3176 | 0 | } |
3177 | 0 | } else if (d == 16) { |
3178 | 0 | for (j = 0; j < wd; j++) { |
3179 | 0 | xs = scol[j]; |
3180 | 0 | if (xs != prevxs) { /* get dest pix from source col */ |
3181 | 0 | sval = GET_DATA_TWO_BYTES(lines, xs); |
3182 | 0 | SET_DATA_TWO_BYTES(lined, j, sval); |
3183 | 0 | prevxs = xs; |
3184 | 0 | } else { /* copy prev dest pix */ |
3185 | 0 | SET_DATA_TWO_BYTES(lined, j, sval); |
3186 | 0 | } |
3187 | 0 | } |
3188 | 0 | } else { /* d == 32 */ |
3189 | 0 | for (j = 0; j < wd; j++) { |
3190 | 0 | xs = scol[j]; |
3191 | 0 | if (xs != prevxs) { /* get dest pix from source col */ |
3192 | 0 | csval = lines[xs]; |
3193 | 0 | lined[j] = csval; |
3194 | 0 | prevxs = xs; |
3195 | 0 | } else { /* copy prev dest pix */ |
3196 | 0 | lined[j] = csval; |
3197 | 0 | } |
3198 | 0 | } |
3199 | 0 | } |
3200 | 0 | } else { /* lines == prevlines; copy prev dest row */ |
3201 | 0 | prevlined = lined - wpld; |
3202 | 0 | memcpy(lined, prevlined, 4 * wpld); |
3203 | 0 | } |
3204 | 0 | prevlines = lines; |
3205 | 0 | } |
3206 | |
|
3207 | 0 | LEPT_FREE(srow); |
3208 | 0 | LEPT_FREE(scol); |
3209 | 0 | return 0; |
3210 | 0 | } |
3211 | | |
3212 | | |
3213 | | /*------------------------------------------------------------------* |
3214 | | * Color and grayscale downsampling with (antialias) smoothing * |
3215 | | *------------------------------------------------------------------*/ |
3216 | | /*! |
3217 | | * \brief scaleSmoothLow() |
3218 | | * |
3219 | | * <pre> |
3220 | | * Notes: |
3221 | | * (1) This function is called on 8 or 32 bpp src and dest images. |
3222 | | * (2) size is the full width of the lowpass smoothing filter. |
3223 | | * It is correlated with the reduction ratio, being the |
3224 | | * nearest integer such that size is approximately equal to hs / hd. |
3225 | | * </pre> |
3226 | | */ |
3227 | | static l_int32 |
3228 | | scaleSmoothLow(l_uint32 *datad, |
3229 | | l_int32 wd, |
3230 | | l_int32 hd, |
3231 | | l_int32 wpld, |
3232 | | l_uint32 *datas, |
3233 | | l_int32 ws, |
3234 | | l_int32 hs, |
3235 | | l_int32 d, |
3236 | | l_int32 wpls, |
3237 | | l_int32 size) |
3238 | 0 | { |
3239 | 0 | l_int32 i, j, m, n, xstart; |
3240 | 0 | l_int32 val, rval, gval, bval; |
3241 | 0 | l_int32 *srow, *scol; |
3242 | 0 | l_uint32 *lines, *lined, *line, *ppixel; |
3243 | 0 | l_uint32 pixel; |
3244 | 0 | l_float32 wratio, hratio, norm; |
3245 | | |
3246 | | /* Clear dest */ |
3247 | 0 | memset(datad, 0, 4LL * wpld * hd); |
3248 | | |
3249 | | /* Each dest pixel at (j,i) is computed as the average |
3250 | | of size^2 corresponding src pixels. |
3251 | | We store the UL corner location of the square of |
3252 | | src pixels that correspond to dest pixel (j,i). |
3253 | | The are labeled by the arrays srow[i] and scol[j]. */ |
3254 | 0 | if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) |
3255 | 0 | return ERROR_INT("srow not made", __func__, 1); |
3256 | 0 | if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { |
3257 | 0 | LEPT_FREE(srow); |
3258 | 0 | return ERROR_INT("scol not made", __func__, 1); |
3259 | 0 | } |
3260 | | |
3261 | 0 | norm = 1.f / (l_float32)(size * size); |
3262 | 0 | wratio = (l_float32)ws / (l_float32)wd; |
3263 | 0 | hratio = (l_float32)hs / (l_float32)hd; |
3264 | 0 | for (i = 0; i < hd; i++) |
3265 | 0 | srow[i] = L_MIN((l_int32)(hratio * i), hs - size); |
3266 | 0 | for (j = 0; j < wd; j++) |
3267 | 0 | scol[j] = L_MIN((l_int32)(wratio * j), ws - size); |
3268 | | |
3269 | | /* For each dest pixel, compute average */ |
3270 | 0 | if (d == 8) { |
3271 | 0 | for (i = 0; i < hd; i++) { |
3272 | 0 | lines = datas + srow[i] * wpls; |
3273 | 0 | lined = datad + i * wpld; |
3274 | 0 | for (j = 0; j < wd; j++) { |
3275 | 0 | xstart = scol[j]; |
3276 | 0 | val = 0; |
3277 | 0 | for (m = 0; m < size; m++) { |
3278 | 0 | line = lines + m * wpls; |
3279 | 0 | for (n = 0; n < size; n++) { |
3280 | 0 | val += GET_DATA_BYTE(line, xstart + n); |
3281 | 0 | } |
3282 | 0 | } |
3283 | 0 | val = (l_int32)((l_float32)val * norm); |
3284 | 0 | SET_DATA_BYTE(lined, j, val); |
3285 | 0 | } |
3286 | 0 | } |
3287 | 0 | } else { /* d == 32 */ |
3288 | 0 | for (i = 0; i < hd; i++) { |
3289 | 0 | lines = datas + srow[i] * wpls; |
3290 | 0 | lined = datad + i * wpld; |
3291 | 0 | for (j = 0; j < wd; j++) { |
3292 | 0 | xstart = scol[j]; |
3293 | 0 | rval = gval = bval = 0; |
3294 | 0 | for (m = 0; m < size; m++) { |
3295 | 0 | ppixel = lines + m * wpls + xstart; |
3296 | 0 | for (n = 0; n < size; n++) { |
3297 | 0 | pixel = *(ppixel + n); |
3298 | 0 | rval += (pixel >> L_RED_SHIFT) & 0xff; |
3299 | 0 | gval += (pixel >> L_GREEN_SHIFT) & 0xff; |
3300 | 0 | bval += (pixel >> L_BLUE_SHIFT) & 0xff; |
3301 | 0 | } |
3302 | 0 | } |
3303 | 0 | rval = (l_int32)((l_float32)rval * norm); |
3304 | 0 | gval = (l_int32)((l_float32)gval * norm); |
3305 | 0 | bval = (l_int32)((l_float32)bval * norm); |
3306 | 0 | composeRGBPixel(rval, gval, bval, lined + j); |
3307 | 0 | } |
3308 | 0 | } |
3309 | 0 | } |
3310 | |
|
3311 | 0 | LEPT_FREE(srow); |
3312 | 0 | LEPT_FREE(scol); |
3313 | 0 | return 0; |
3314 | 0 | } |
3315 | | |
3316 | | |
3317 | | /*! |
3318 | | * \brief scaleRGBToGray2Low() |
3319 | | * |
3320 | | * <pre> |
3321 | | * Notes: |
3322 | | * (1) This function is called with 32 bpp RGB src and 8 bpp, |
3323 | | * half-resolution dest. The weights should add to 1.0. |
3324 | | * </pre> |
3325 | | */ |
3326 | | static void |
3327 | | scaleRGBToGray2Low(l_uint32 *datad, |
3328 | | l_int32 wd, |
3329 | | l_int32 hd, |
3330 | | l_int32 wpld, |
3331 | | l_uint32 *datas, |
3332 | | l_int32 wpls, |
3333 | | l_float32 rwt, |
3334 | | l_float32 gwt, |
3335 | | l_float32 bwt) |
3336 | 0 | { |
3337 | 0 | l_int32 i, j, val, rval, gval, bval; |
3338 | 0 | l_uint32 *lines, *lined; |
3339 | 0 | l_uint32 pixel; |
3340 | |
|
3341 | 0 | rwt *= 0.25; |
3342 | 0 | gwt *= 0.25; |
3343 | 0 | bwt *= 0.25; |
3344 | 0 | for (i = 0; i < hd; i++) { |
3345 | 0 | lines = datas + 2 * i * wpls; |
3346 | 0 | lined = datad + i * wpld; |
3347 | 0 | for (j = 0; j < wd; j++) { |
3348 | | /* Sum each of the color components from 4 src pixels */ |
3349 | 0 | pixel = *(lines + 2 * j); |
3350 | 0 | rval = (pixel >> L_RED_SHIFT) & 0xff; |
3351 | 0 | gval = (pixel >> L_GREEN_SHIFT) & 0xff; |
3352 | 0 | bval = (pixel >> L_BLUE_SHIFT) & 0xff; |
3353 | 0 | pixel = *(lines + 2 * j + 1); |
3354 | 0 | rval += (pixel >> L_RED_SHIFT) & 0xff; |
3355 | 0 | gval += (pixel >> L_GREEN_SHIFT) & 0xff; |
3356 | 0 | bval += (pixel >> L_BLUE_SHIFT) & 0xff; |
3357 | 0 | pixel = *(lines + wpls + 2 * j); |
3358 | 0 | rval += (pixel >> L_RED_SHIFT) & 0xff; |
3359 | 0 | gval += (pixel >> L_GREEN_SHIFT) & 0xff; |
3360 | 0 | bval += (pixel >> L_BLUE_SHIFT) & 0xff; |
3361 | 0 | pixel = *(lines + wpls + 2 * j + 1); |
3362 | 0 | rval += (pixel >> L_RED_SHIFT) & 0xff; |
3363 | 0 | gval += (pixel >> L_GREEN_SHIFT) & 0xff; |
3364 | 0 | bval += (pixel >> L_BLUE_SHIFT) & 0xff; |
3365 | | /* Generate the dest byte as a weighted sum of the averages */ |
3366 | 0 | val = (l_int32)(rwt * rval + gwt * gval + bwt * bval); |
3367 | 0 | SET_DATA_BYTE(lined, j, val); |
3368 | 0 | } |
3369 | 0 | } |
3370 | 0 | } |
3371 | | |
3372 | | |
3373 | | /*------------------------------------------------------------------* |
3374 | | * General area mapped gray scaling * |
3375 | | *------------------------------------------------------------------*/ |
3376 | | /*! |
3377 | | * \brief scaleColorAreaMapLow() |
3378 | | * |
3379 | | * <pre> |
3380 | | * Notes: |
3381 | | * (1) This should only be used for downscaling. |
3382 | | * We choose to divide each pixel into 16 x 16 sub-pixels. |
3383 | | * This is much slower than scaleSmoothLow(), but it gives a |
3384 | | * better representation, esp. for downscaling factors between |
3385 | | * 1.5 and 5. All src pixels are subdivided into 256 sub-pixels, |
3386 | | * and are weighted by the number of sub-pixels covered by |
3387 | | * the dest pixel. This is about 2x slower than scaleSmoothLow(), |
3388 | | * but the results are significantly better on small text. |
3389 | | * </pre> |
3390 | | */ |
3391 | | static void |
3392 | | scaleColorAreaMapLow(l_uint32 *datad, |
3393 | | l_int32 wd, |
3394 | | l_int32 hd, |
3395 | | l_int32 wpld, |
3396 | | l_uint32 *datas, |
3397 | | l_int32 ws, |
3398 | | l_int32 hs, |
3399 | | l_int32 wpls) |
3400 | 0 | { |
3401 | 0 | l_int32 i, j, k, m, wm2, hm2; |
3402 | 0 | l_int32 area00, area10, area01, area11, areal, arear, areat, areab; |
3403 | 0 | l_int32 xu, yu; /* UL corner in src image, to 1/16 of a pixel */ |
3404 | 0 | l_int32 xl, yl; /* LR corner in src image, to 1/16 of a pixel */ |
3405 | 0 | l_int32 xup, yup, xuf, yuf; /* UL src pixel: integer and fraction */ |
3406 | 0 | l_int32 xlp, ylp, xlf, ylf; /* LR src pixel: integer and fraction */ |
3407 | 0 | l_int32 delx, dely, area; |
3408 | 0 | l_int32 v00r, v00g, v00b; /* contrib. from UL src pixel */ |
3409 | 0 | l_int32 v01r, v01g, v01b; /* contrib. from LL src pixel */ |
3410 | 0 | l_int32 v10r, v10g, v10b; /* contrib from UR src pixel */ |
3411 | 0 | l_int32 v11r, v11g, v11b; /* contrib from LR src pixel */ |
3412 | 0 | l_int32 vinr, ving, vinb; /* contrib from all full interior src pixels */ |
3413 | 0 | l_int32 vmidr, vmidg, vmidb; /* contrib from side parts */ |
3414 | 0 | l_int32 rval, gval, bval; |
3415 | 0 | l_uint32 pixel00, pixel10, pixel01, pixel11, pixel; |
3416 | 0 | l_uint32 *lines, *lined; |
3417 | 0 | l_float32 scx, scy; |
3418 | | |
3419 | | /* (scx, scy) are scaling factors that are applied to the |
3420 | | * dest coords to get the corresponding src coords. |
3421 | | * We need them because we iterate over dest pixels |
3422 | | * and must find the corresponding set of src pixels. */ |
3423 | 0 | scx = 16.f * (l_float32)ws / (l_float32)wd; |
3424 | 0 | scy = 16.f * (l_float32)hs / (l_float32)hd; |
3425 | 0 | wm2 = ws - 2; |
3426 | 0 | hm2 = hs - 2; |
3427 | | |
3428 | | /* Iterate over the destination pixels */ |
3429 | 0 | for (i = 0; i < hd; i++) { |
3430 | 0 | yu = (l_int32)(scy * i); |
3431 | 0 | yl = (l_int32)(scy * (i + 1.0)); |
3432 | 0 | yup = yu >> 4; |
3433 | 0 | yuf = yu & 0x0f; |
3434 | 0 | ylp = yl >> 4; |
3435 | 0 | ylf = yl & 0x0f; |
3436 | 0 | dely = ylp - yup; |
3437 | 0 | lined = datad + i * wpld; |
3438 | 0 | lines = datas + yup * wpls; |
3439 | 0 | for (j = 0; j < wd; j++) { |
3440 | 0 | xu = (l_int32)(scx * j); |
3441 | 0 | xl = (l_int32)(scx * (j + 1.0)); |
3442 | 0 | xup = xu >> 4; |
3443 | 0 | xuf = xu & 0x0f; |
3444 | 0 | xlp = xl >> 4; |
3445 | 0 | xlf = xl & 0x0f; |
3446 | 0 | delx = xlp - xup; |
3447 | | |
3448 | | /* If near the edge, just use a src pixel value */ |
3449 | 0 | if (xlp > wm2 || ylp > hm2) { |
3450 | 0 | *(lined + j) = *(lines + xup); |
3451 | 0 | continue; |
3452 | 0 | } |
3453 | | |
3454 | | /* Area summed over, in subpixels. This varies |
3455 | | * due to the quantization, so we can't simply take |
3456 | | * the area to be a constant: area = scx * scy. */ |
3457 | 0 | area = ((16 - xuf) + 16 * (delx - 1) + xlf) * |
3458 | 0 | ((16 - yuf) + 16 * (dely - 1) + ylf); |
3459 | | |
3460 | | /* Do area map summation */ |
3461 | 0 | pixel00 = *(lines + xup); |
3462 | 0 | pixel10 = *(lines + xlp); |
3463 | 0 | pixel01 = *(lines + dely * wpls + xup); |
3464 | 0 | pixel11 = *(lines + dely * wpls + xlp); |
3465 | 0 | area00 = (16 - xuf) * (16 - yuf); |
3466 | 0 | area10 = xlf * (16 - yuf); |
3467 | 0 | area01 = (16 - xuf) * ylf; |
3468 | 0 | area11 = xlf * ylf; |
3469 | 0 | v00r = area00 * ((pixel00 >> L_RED_SHIFT) & 0xff); |
3470 | 0 | v00g = area00 * ((pixel00 >> L_GREEN_SHIFT) & 0xff); |
3471 | 0 | v00b = area00 * ((pixel00 >> L_BLUE_SHIFT) & 0xff); |
3472 | 0 | v10r = area10 * ((pixel10 >> L_RED_SHIFT) & 0xff); |
3473 | 0 | v10g = area10 * ((pixel10 >> L_GREEN_SHIFT) & 0xff); |
3474 | 0 | v10b = area10 * ((pixel10 >> L_BLUE_SHIFT) & 0xff); |
3475 | 0 | v01r = area01 * ((pixel01 >> L_RED_SHIFT) & 0xff); |
3476 | 0 | v01g = area01 * ((pixel01 >> L_GREEN_SHIFT) & 0xff); |
3477 | 0 | v01b = area01 * ((pixel01 >> L_BLUE_SHIFT) & 0xff); |
3478 | 0 | v11r = area11 * ((pixel11 >> L_RED_SHIFT) & 0xff); |
3479 | 0 | v11g = area11 * ((pixel11 >> L_GREEN_SHIFT) & 0xff); |
3480 | 0 | v11b = area11 * ((pixel11 >> L_BLUE_SHIFT) & 0xff); |
3481 | 0 | vinr = ving = vinb = 0; |
3482 | 0 | for (k = 1; k < dely; k++) { /* for full src pixels */ |
3483 | 0 | for (m = 1; m < delx; m++) { |
3484 | 0 | pixel = *(lines + k * wpls + xup + m); |
3485 | 0 | vinr += 256 * ((pixel >> L_RED_SHIFT) & 0xff); |
3486 | 0 | ving += 256 * ((pixel >> L_GREEN_SHIFT) & 0xff); |
3487 | 0 | vinb += 256 * ((pixel >> L_BLUE_SHIFT) & 0xff); |
3488 | 0 | } |
3489 | 0 | } |
3490 | 0 | vmidr = vmidg = vmidb = 0; |
3491 | 0 | areal = (16 - xuf) * 16; |
3492 | 0 | arear = xlf * 16; |
3493 | 0 | areat = 16 * (16 - yuf); |
3494 | 0 | areab = 16 * ylf; |
3495 | 0 | for (k = 1; k < dely; k++) { /* for left side */ |
3496 | 0 | pixel = *(lines + k * wpls + xup); |
3497 | 0 | vmidr += areal * ((pixel >> L_RED_SHIFT) & 0xff); |
3498 | 0 | vmidg += areal * ((pixel >> L_GREEN_SHIFT) & 0xff); |
3499 | 0 | vmidb += areal * ((pixel >> L_BLUE_SHIFT) & 0xff); |
3500 | 0 | } |
3501 | 0 | for (k = 1; k < dely; k++) { /* for right side */ |
3502 | 0 | pixel = *(lines + k * wpls + xlp); |
3503 | 0 | vmidr += arear * ((pixel >> L_RED_SHIFT) & 0xff); |
3504 | 0 | vmidg += arear * ((pixel >> L_GREEN_SHIFT) & 0xff); |
3505 | 0 | vmidb += arear * ((pixel >> L_BLUE_SHIFT) & 0xff); |
3506 | 0 | } |
3507 | 0 | for (m = 1; m < delx; m++) { /* for top side */ |
3508 | 0 | pixel = *(lines + xup + m); |
3509 | 0 | vmidr += areat * ((pixel >> L_RED_SHIFT) & 0xff); |
3510 | 0 | vmidg += areat * ((pixel >> L_GREEN_SHIFT) & 0xff); |
3511 | 0 | vmidb += areat * ((pixel >> L_BLUE_SHIFT) & 0xff); |
3512 | 0 | } |
3513 | 0 | for (m = 1; m < delx; m++) { /* for bottom side */ |
3514 | 0 | pixel = *(lines + dely * wpls + xup + m); |
3515 | 0 | vmidr += areab * ((pixel >> L_RED_SHIFT) & 0xff); |
3516 | 0 | vmidg += areab * ((pixel >> L_GREEN_SHIFT) & 0xff); |
3517 | 0 | vmidb += areab * ((pixel >> L_BLUE_SHIFT) & 0xff); |
3518 | 0 | } |
3519 | | |
3520 | | /* Sum all the contributions */ |
3521 | 0 | rval = (v00r + v01r + v10r + v11r + vinr + vmidr + 128) / area; |
3522 | 0 | gval = (v00g + v01g + v10g + v11g + ving + vmidg + 128) / area; |
3523 | 0 | bval = (v00b + v01b + v10b + v11b + vinb + vmidb + 128) / area; |
3524 | | #if DEBUG_OVERFLOW |
3525 | | if (rval > 255) lept_stderr("rval ovfl: %d\n", rval); |
3526 | | if (gval > 255) lept_stderr("gval ovfl: %d\n", gval); |
3527 | | if (bval > 255) lept_stderr("bval ovfl: %d\n", bval); |
3528 | | #endif /* DEBUG_OVERFLOW */ |
3529 | 0 | composeRGBPixel(rval, gval, bval, lined + j); |
3530 | 0 | } |
3531 | 0 | } |
3532 | 0 | } |
3533 | | |
3534 | | |
3535 | | /*! |
3536 | | * \brief scaleGrayAreaMapLow() |
3537 | | * |
3538 | | * <pre> |
3539 | | * Notes: |
3540 | | * (1) This should only be used for downscaling. |
3541 | | * We choose to divide each pixel into 16 x 16 sub-pixels. |
3542 | | * This is about 2x slower than scaleSmoothLow(), but the results |
3543 | | * are significantly better on small text, esp. for downscaling |
3544 | | * factors between 1.5 and 5. All src pixels are subdivided |
3545 | | * into 256 sub-pixels, and are weighted by the number of |
3546 | | * sub-pixels covered by the dest pixel. |
3547 | | * </pre> |
3548 | | */ |
3549 | | static void |
3550 | | scaleGrayAreaMapLow(l_uint32 *datad, |
3551 | | l_int32 wd, |
3552 | | l_int32 hd, |
3553 | | l_int32 wpld, |
3554 | | l_uint32 *datas, |
3555 | | l_int32 ws, |
3556 | | l_int32 hs, |
3557 | | l_int32 wpls) |
3558 | 0 | { |
3559 | 0 | l_int32 i, j, k, m, wm2, hm2; |
3560 | 0 | l_int32 xu, yu; /* UL corner in src image, to 1/16 of a pixel */ |
3561 | 0 | l_int32 xl, yl; /* LR corner in src image, to 1/16 of a pixel */ |
3562 | 0 | l_int32 xup, yup, xuf, yuf; /* UL src pixel: integer and fraction */ |
3563 | 0 | l_int32 xlp, ylp, xlf, ylf; /* LR src pixel: integer and fraction */ |
3564 | 0 | l_int32 delx, dely, area; |
3565 | 0 | l_int32 v00; /* contrib. from UL src pixel */ |
3566 | 0 | l_int32 v01; /* contrib. from LL src pixel */ |
3567 | 0 | l_int32 v10; /* contrib from UR src pixel */ |
3568 | 0 | l_int32 v11; /* contrib from LR src pixel */ |
3569 | 0 | l_int32 vin; /* contrib from all full interior src pixels */ |
3570 | 0 | l_int32 vmid; /* contrib from side parts that are full in 1 direction */ |
3571 | 0 | l_int32 val; |
3572 | 0 | l_uint32 *lines, *lined; |
3573 | 0 | l_float32 scx, scy; |
3574 | | |
3575 | | /* (scx, scy) are scaling factors that are applied to the |
3576 | | * dest coords to get the corresponding src coords. |
3577 | | * We need them because we iterate over dest pixels |
3578 | | * and must find the corresponding set of src pixels. */ |
3579 | 0 | scx = 16.f * (l_float32)ws / (l_float32)wd; |
3580 | 0 | scy = 16.f * (l_float32)hs / (l_float32)hd; |
3581 | 0 | wm2 = ws - 2; |
3582 | 0 | hm2 = hs - 2; |
3583 | | |
3584 | | /* Iterate over the destination pixels */ |
3585 | 0 | for (i = 0; i < hd; i++) { |
3586 | 0 | yu = (l_int32)(scy * i); |
3587 | 0 | yl = (l_int32)(scy * (i + 1.0)); |
3588 | 0 | yup = yu >> 4; |
3589 | 0 | yuf = yu & 0x0f; |
3590 | 0 | ylp = yl >> 4; |
3591 | 0 | ylf = yl & 0x0f; |
3592 | 0 | dely = ylp - yup; |
3593 | 0 | lined = datad + i * wpld; |
3594 | 0 | lines = datas + yup * wpls; |
3595 | 0 | for (j = 0; j < wd; j++) { |
3596 | 0 | xu = (l_int32)(scx * j); |
3597 | 0 | xl = (l_int32)(scx * (j + 1.0)); |
3598 | 0 | xup = xu >> 4; |
3599 | 0 | xuf = xu & 0x0f; |
3600 | 0 | xlp = xl >> 4; |
3601 | 0 | xlf = xl & 0x0f; |
3602 | 0 | delx = xlp - xup; |
3603 | | |
3604 | | /* If near the edge, just use a src pixel value */ |
3605 | 0 | if (xlp > wm2 || ylp > hm2) { |
3606 | 0 | SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xup)); |
3607 | 0 | continue; |
3608 | 0 | } |
3609 | | |
3610 | | /* Area summed over, in subpixels. This varies |
3611 | | * due to the quantization, so we can't simply take |
3612 | | * the area to be a constant: area = scx * scy. */ |
3613 | 0 | area = ((16 - xuf) + 16 * (delx - 1) + xlf) * |
3614 | 0 | ((16 - yuf) + 16 * (dely - 1) + ylf); |
3615 | | |
3616 | | /* Do area map summation */ |
3617 | 0 | v00 = (16 - xuf) * (16 - yuf) * GET_DATA_BYTE(lines, xup); |
3618 | 0 | v10 = xlf * (16 - yuf) * GET_DATA_BYTE(lines, xlp); |
3619 | 0 | v01 = (16 - xuf) * ylf * GET_DATA_BYTE(lines + dely * wpls, xup); |
3620 | 0 | v11 = xlf * ylf * GET_DATA_BYTE(lines + dely * wpls, xlp); |
3621 | 0 | for (vin = 0, k = 1; k < dely; k++) { /* for full src pixels */ |
3622 | 0 | for (m = 1; m < delx; m++) { |
3623 | 0 | vin += 256 * GET_DATA_BYTE(lines + k * wpls, xup + m); |
3624 | 0 | } |
3625 | 0 | } |
3626 | 0 | for (vmid = 0, k = 1; k < dely; k++) /* for left side */ |
3627 | 0 | vmid += (16 - xuf) * 16 * GET_DATA_BYTE(lines + k * wpls, xup); |
3628 | 0 | for (k = 1; k < dely; k++) /* for right side */ |
3629 | 0 | vmid += xlf * 16 * GET_DATA_BYTE(lines + k * wpls, xlp); |
3630 | 0 | for (m = 1; m < delx; m++) /* for top side */ |
3631 | 0 | vmid += 16 * (16 - yuf) * GET_DATA_BYTE(lines, xup + m); |
3632 | 0 | for (m = 1; m < delx; m++) /* for bottom side */ |
3633 | 0 | vmid += 16 * ylf * GET_DATA_BYTE(lines + dely * wpls, xup + m); |
3634 | 0 | val = (v00 + v01 + v10 + v11 + vin + vmid + 128) / area; |
3635 | | #if DEBUG_OVERFLOW |
3636 | | if (val > 255) lept_stderr("val overflow: %d\n", val); |
3637 | | #endif /* DEBUG_OVERFLOW */ |
3638 | 0 | SET_DATA_BYTE(lined, j, val); |
3639 | 0 | } |
3640 | 0 | } |
3641 | 0 | } |
3642 | | |
3643 | | |
3644 | | /*------------------------------------------------------------------* |
3645 | | * 2x area mapped downscaling * |
3646 | | *------------------------------------------------------------------*/ |
3647 | | /*! |
3648 | | * \brief scaleAreaMapLow2() |
3649 | | * |
3650 | | * <pre> |
3651 | | * Notes: |
3652 | | * (1) This function is called with either 8 bpp gray or 32 bpp RGB. |
3653 | | * The result is a 2x reduced dest. |
3654 | | * </pre> |
3655 | | */ |
3656 | | static void |
3657 | | scaleAreaMapLow2(l_uint32 *datad, |
3658 | | l_int32 wd, |
3659 | | l_int32 hd, |
3660 | | l_int32 wpld, |
3661 | | l_uint32 *datas, |
3662 | | l_int32 d, |
3663 | | l_int32 wpls) |
3664 | 0 | { |
3665 | 0 | l_int32 i, j, val, rval, gval, bval; |
3666 | 0 | l_uint32 *lines, *lined; |
3667 | 0 | l_uint32 pixel; |
3668 | |
|
3669 | 0 | if (d == 8) { |
3670 | 0 | for (i = 0; i < hd; i++) { |
3671 | 0 | lines = datas + 2 * i * wpls; |
3672 | 0 | lined = datad + i * wpld; |
3673 | 0 | for (j = 0; j < wd; j++) { |
3674 | | /* Average each dest pixel using 4 src pixels */ |
3675 | 0 | val = GET_DATA_BYTE(lines, 2 * j); |
3676 | 0 | val += GET_DATA_BYTE(lines, 2 * j + 1); |
3677 | 0 | val += GET_DATA_BYTE(lines + wpls, 2 * j); |
3678 | 0 | val += GET_DATA_BYTE(lines + wpls, 2 * j + 1); |
3679 | 0 | val >>= 2; |
3680 | 0 | SET_DATA_BYTE(lined, j, val); |
3681 | 0 | } |
3682 | 0 | } |
3683 | 0 | } else { /* d == 32 */ |
3684 | 0 | for (i = 0; i < hd; i++) { |
3685 | 0 | lines = datas + 2 * i * wpls; |
3686 | 0 | lined = datad + i * wpld; |
3687 | 0 | for (j = 0; j < wd; j++) { |
3688 | | /* Average each of the color components from 4 src pixels */ |
3689 | 0 | pixel = *(lines + 2 * j); |
3690 | 0 | rval = (pixel >> L_RED_SHIFT) & 0xff; |
3691 | 0 | gval = (pixel >> L_GREEN_SHIFT) & 0xff; |
3692 | 0 | bval = (pixel >> L_BLUE_SHIFT) & 0xff; |
3693 | 0 | pixel = *(lines + 2 * j + 1); |
3694 | 0 | rval += (pixel >> L_RED_SHIFT) & 0xff; |
3695 | 0 | gval += (pixel >> L_GREEN_SHIFT) & 0xff; |
3696 | 0 | bval += (pixel >> L_BLUE_SHIFT) & 0xff; |
3697 | 0 | pixel = *(lines + wpls + 2 * j); |
3698 | 0 | rval += (pixel >> L_RED_SHIFT) & 0xff; |
3699 | 0 | gval += (pixel >> L_GREEN_SHIFT) & 0xff; |
3700 | 0 | bval += (pixel >> L_BLUE_SHIFT) & 0xff; |
3701 | 0 | pixel = *(lines + wpls + 2 * j + 1); |
3702 | 0 | rval += (pixel >> L_RED_SHIFT) & 0xff; |
3703 | 0 | gval += (pixel >> L_GREEN_SHIFT) & 0xff; |
3704 | 0 | bval += (pixel >> L_BLUE_SHIFT) & 0xff; |
3705 | 0 | composeRGBPixel(rval >> 2, gval >> 2, bval >> 2, &pixel); |
3706 | 0 | *(lined + j) = pixel; |
3707 | 0 | } |
3708 | 0 | } |
3709 | 0 | } |
3710 | 0 | } |
3711 | | |
3712 | | |
3713 | | /*------------------------------------------------------------------* |
3714 | | * Binary scaling by closest pixel sampling * |
3715 | | *------------------------------------------------------------------*/ |
3716 | | /* |
3717 | | * \brief scaleBinaryLow() |
3718 | | * |
3719 | | * <pre> |
3720 | | * Notes: |
3721 | | * (1) The dest must be cleared prior to this operation, |
3722 | | * and we clear it here in the low-level code. |
3723 | | * (2) We reuse dest pixels and dest pixel rows whenever |
3724 | | * possible for upscaling; downscaling is done by |
3725 | | * strict subsampling. |
3726 | | * </pre> |
3727 | | */ |
3728 | | static l_int32 |
3729 | | scaleBinaryLow(l_uint32 *datad, |
3730 | | l_int32 wd, |
3731 | | l_int32 hd, |
3732 | | l_int32 wpld, |
3733 | | l_uint32 *datas, |
3734 | | l_int32 ws, |
3735 | | l_int32 hs, |
3736 | | l_int32 wpls, |
3737 | | l_float32 shiftx, |
3738 | | l_float32 shifty) |
3739 | 11.7k | { |
3740 | 11.7k | l_int32 i, j; |
3741 | 11.7k | l_int32 xs, prevxs, sval; |
3742 | 11.7k | l_int32 *srow, *scol; |
3743 | 11.7k | l_uint32 *lines, *prevlines, *lined, *prevlined; |
3744 | 11.7k | l_float32 wratio, hratio; |
3745 | | |
3746 | | /* Clear dest */ |
3747 | 11.7k | memset(datad, 0, 4LL * hd * wpld); |
3748 | | |
3749 | | /* The source row corresponding to dest row i ==> srow[i] |
3750 | | * The source col corresponding to dest col j ==> scol[j] */ |
3751 | 11.7k | if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) |
3752 | 0 | return ERROR_INT("srow not made", __func__, 1); |
3753 | 11.7k | if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { |
3754 | 0 | LEPT_FREE(srow); |
3755 | 0 | return ERROR_INT("scol not made", __func__, 1); |
3756 | 0 | } |
3757 | | |
3758 | 11.7k | wratio = (l_float32)ws / (l_float32)wd; |
3759 | 11.7k | hratio = (l_float32)hs / (l_float32)hd; |
3760 | 2.03M | for (i = 0; i < hd; i++) |
3761 | 2.02M | srow[i] = L_MIN((l_int32)(hratio * i + shifty), hs - 1); |
3762 | 2.45M | for (j = 0; j < wd; j++) |
3763 | 2.44M | scol[j] = L_MIN((l_int32)(wratio * j + shiftx), ws - 1); |
3764 | | |
3765 | 11.7k | prevlines = NULL; |
3766 | 11.7k | prevxs = -1; |
3767 | 11.7k | sval = 0; |
3768 | 2.03M | for (i = 0; i < hd; i++) { |
3769 | 2.02M | lines = datas + srow[i] * wpls; |
3770 | 2.02M | lined = datad + i * wpld; |
3771 | 2.02M | if (lines != prevlines) { /* make dest from new source row */ |
3772 | 131M | for (j = 0; j < wd; j++) { |
3773 | 129M | xs = scol[j]; |
3774 | 129M | if (xs != prevxs) { /* get dest pix from source col */ |
3775 | 123M | if ((sval = GET_DATA_BIT(lines, xs))) |
3776 | 24.3M | SET_DATA_BIT(lined, j); |
3777 | 123M | prevxs = xs; |
3778 | 123M | } else { /* copy prev dest pix, if set */ |
3779 | 6.20M | if (sval) |
3780 | 1.21M | SET_DATA_BIT(lined, j); |
3781 | 6.20M | } |
3782 | 129M | } |
3783 | 1.68M | } else { /* lines == prevlines; copy prev dest row */ |
3784 | 336k | prevlined = lined - wpld; |
3785 | 336k | memcpy(lined, prevlined, 4 * wpld); |
3786 | 336k | } |
3787 | 2.02M | prevlines = lines; |
3788 | 2.02M | } |
3789 | | |
3790 | 11.7k | LEPT_FREE(srow); |
3791 | 11.7k | LEPT_FREE(scol); |
3792 | 11.7k | return 0; |
3793 | 11.7k | } |