comparison mupdf-source/thirdparty/leptonica/src/scale1.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 {
258 l_int32 sharpwidth;
259 l_float32 maxscale, sharpfract;
260
261 if (!pixs)
262 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
263
264 /* Reduce the default sharpening factors by 2 if maxscale < 0.7 */
265 maxscale = L_MAX(scalex, scaley);
266 sharpfract = (maxscale < 0.7) ? 0.2f : 0.4f;
267 sharpwidth = (maxscale < 0.7) ? 1 : 2;
268
269 return pixScaleGeneral(pixs, scalex, scaley, sharpfract, sharpwidth);
270 }
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 {
286 l_int32 w, h, wd, hd;
287
288 if (!pixs)
289 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
290
291 if (delw == 0 && delh == 0)
292 return pixCopy(NULL, pixs);
293
294 pixGetDimensions(pixs, &w, &h, NULL);
295 wd = w + delw;
296 hd = h + delh;
297 if (wd <= 0 || hd <= 0)
298 return (PIX *)ERROR_PTR("pix dimension reduced to 0", __func__, NULL);
299
300 return pixScaleToSize(pixs, wd, hd);
301 }
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 {
327 l_int32 w, h;
328 l_float32 scalex, scaley;
329
330 if (!pixs)
331 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
332 if (wd <= 0 && hd <= 0)
333 return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL);
334
335 pixGetDimensions(pixs, &w, &h, NULL);
336 if (wd <= 0) {
337 scaley = (l_float32)hd / (l_float32)h;
338 scalex = scaley;
339 } else if (hd <= 0) {
340 scalex = (l_float32)wd / (l_float32)w;
341 scaley = scalex;
342 } else {
343 scalex = (l_float32)wd / (l_float32)w;
344 scaley = (l_float32)hd / (l_float32)h;
345 }
346
347 return pixScale(pixs, scalex, scaley);
348 }
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 {
366 l_int32 xres;
367 l_float32 factor;
368
369 if (pscalefact) *pscalefact = 1.0;
370 if (!pixs)
371 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
372 if (target <= 0)
373 return (PIX *)ERROR_PTR("target resolution <= 0", __func__, NULL);
374
375 xres = pixGetXRes(pixs);
376 if (xres <= 0) {
377 if (assumed == 0)
378 return pixCopy(NULL, pixs);
379 xres = assumed;
380 }
381 factor = target / (l_float32)xres;
382 if (pscalefact) *pscalefact = factor;
383
384 return pixScale(pixs, factor, factor);
385 }
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 {
425 l_int32 d;
426 l_float32 maxscale, minscale;
427 PIX *pix1, *pix2, *pixd;
428
429 if (!pixs)
430 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
431 d = pixGetDepth(pixs);
432 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
433 return (PIX *)ERROR_PTR("pixs not {1,2,4,8,16,32} bpp", __func__, NULL);
434 if (scalex <= 0.0 || scaley <= 0.0)
435 return (PIX *)ERROR_PTR("scale factor <= 0", __func__, NULL);
436 if (scalex == 1.0 && scaley == 1.0)
437 return pixCopy(NULL, pixs);
438
439 if (d == 1)
440 return pixScaleBinary(pixs, scalex, scaley);
441
442 /* Remove colormap; clone if possible; result is either 8 or 32 bpp */
443 if ((pix1 = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL)
444 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL);
445
446 /* Scale (up or down) */
447 d = pixGetDepth(pix1);
448 maxscale = L_MAX(scalex, scaley);
449 minscale = L_MIN(scalex, scaley);
450 if (maxscale < 0.7) { /* use low-pass filter for anti-aliasing */
451 if (minscale < 0.02) { /* whole-pixel low-pass filter */
452 pix2 = pixScaleSmooth(pix1, scalex, scaley);
453 } else { /* fractional pixel low-pass filter */
454 pix2 = pixScaleAreaMap(pix1, scalex, scaley);
455 }
456 if (maxscale > 0.2 && sharpfract > 0.0 && sharpwidth > 0) {
457 pixd = pixUnsharpMasking(pix2, sharpwidth, sharpfract);
458 } else {
459 pixd = pixClone(pix2);
460 }
461 } else { /* use linear interpolation */
462 if (d == 8) {
463 pix2 = pixScaleGrayLI(pix1, scalex, scaley);
464 } else { /* d == 32 */
465 pix2 = pixScaleColorLI(pix1, scalex, scaley);
466 }
467 if (maxscale < 1.4 && sharpfract > 0.0 && sharpwidth > 0) {
468 pixd = pixUnsharpMasking(pix2, sharpwidth, sharpfract);
469 } else {
470 pixd = pixClone(pix2);
471 }
472 }
473
474 pixDestroy(&pix1);
475 pixDestroy(&pix2);
476 pixCopyText(pixd, pixs);
477 pixCopyInputFormat(pixd, pixs);
478 return pixd;
479 }
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 {
514 l_int32 d;
515 l_float32 maxscale;
516 PIX *pixt, *pixd;
517
518 if (!pixs || (pixGetDepth(pixs) == 1))
519 return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", __func__, NULL);
520 maxscale = L_MAX(scalex, scaley);
521 if (maxscale < 0.7) {
522 L_WARNING("scaling factors < 0.7; do regular scaling\n", __func__);
523 return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0);
524 }
525 d = pixGetDepth(pixs);
526 if (d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
527 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 if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL)
531 return (PIX *)ERROR_PTR("pixt not made", __func__, NULL);
532
533 d = pixGetDepth(pixt);
534 if (d == 8)
535 pixd = pixScaleGrayLI(pixt, scalex, scaley);
536 else /* d == 32 */
537 pixd = pixScaleColorLI(pixt, scalex, scaley);
538
539 pixDestroy(&pixt);
540 pixCopyInputFormat(pixd, pixs);
541 return pixd;
542 }
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 {
571 l_int32 ws, hs, wpls, wd, hd, wpld;
572 l_uint32 *datas, *datad;
573 l_float32 maxscale;
574 PIX *pixd;
575
576 if (!pixs || (pixGetDepth(pixs) != 32))
577 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
578 maxscale = L_MAX(scalex, scaley);
579 if (maxscale < 0.7) {
580 L_WARNING("scaling factors < 0.7; do regular scaling\n", __func__);
581 return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0);
582 }
583
584 /* Do fast special cases if possible */
585 if (scalex == 1.0 && scaley == 1.0)
586 return pixCopy(NULL, pixs);
587 if (scalex == 2.0 && scaley == 2.0)
588 return pixScaleColor2xLI(pixs);
589 if (scalex == 4.0 && scaley == 4.0)
590 return pixScaleColor4xLI(pixs);
591
592 /* General case */
593 pixGetDimensions(pixs, &ws, &hs, NULL);
594 datas = pixGetData(pixs);
595 wpls = pixGetWpl(pixs);
596 wd = (l_int32)(scalex * (l_float32)ws + 0.5);
597 hd = (l_int32)(scaley * (l_float32)hs + 0.5);
598 if ((pixd = pixCreate(wd, hd, 32)) == NULL)
599 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
600 pixCopyResolution(pixd, pixs);
601 pixScaleResolution(pixd, scalex, scaley);
602 datad = pixGetData(pixd);
603 wpld = pixGetWpl(pixd);
604 scaleColorLILow(datad, wd, hd, wpld, datas, ws, hs, wpls);
605 if (pixGetSpp(pixs) == 4)
606 pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley);
607
608 pixCopyInputFormat(pixd, pixs);
609 return pixd;
610 }
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 {
631 l_int32 ws, hs, wpls, wpld;
632 l_uint32 *datas, *datad;
633 PIX *pixd;
634
635 if (!pixs || (pixGetDepth(pixs) != 32))
636 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
637
638 pixGetDimensions(pixs, &ws, &hs, NULL);
639 datas = pixGetData(pixs);
640 wpls = pixGetWpl(pixs);
641 if ((pixd = pixCreate(2 * ws, 2 * hs, 32)) == NULL)
642 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
643 pixCopyResolution(pixd, pixs);
644 pixScaleResolution(pixd, 2.0, 2.0);
645 datad = pixGetData(pixd);
646 wpld = pixGetWpl(pixd);
647 scaleColor2xLILow(datad, wpld, datas, ws, hs, wpls);
648 if (pixGetSpp(pixs) == 4)
649 pixScaleAndTransferAlpha(pixd, pixs, 2.0, 2.0);
650
651 pixCopyInputFormat(pixd, pixs);
652 return pixd;
653 }
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 {
676 PIX *pixr, *pixg, *pixb;
677 PIX *pixrs, *pixgs, *pixbs;
678 PIX *pixd;
679
680 if (!pixs || (pixGetDepth(pixs) != 32))
681 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
682
683 pixr = pixGetRGBComponent(pixs, COLOR_RED);
684 pixrs = pixScaleGray4xLI(pixr);
685 pixDestroy(&pixr);
686 pixg = pixGetRGBComponent(pixs, COLOR_GREEN);
687 pixgs = pixScaleGray4xLI(pixg);
688 pixDestroy(&pixg);
689 pixb = pixGetRGBComponent(pixs, COLOR_BLUE);
690 pixbs = pixScaleGray4xLI(pixb);
691 pixDestroy(&pixb);
692
693 if ((pixd = pixCreateRGBImage(pixrs, pixgs, pixbs)) == NULL) {
694 L_ERROR("pixd not made\n", __func__);
695 } else {
696 if (pixGetSpp(pixs) == 4)
697 pixScaleAndTransferAlpha(pixd, pixs, 4.0, 4.0);
698 pixCopyInputFormat(pixd, pixs);
699 }
700
701 pixDestroy(&pixrs);
702 pixDestroy(&pixgs);
703 pixDestroy(&pixbs);
704 return pixd;
705 }
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 {
770 l_int32 ws, hs, wpls, wd, hd, wpld;
771 l_uint32 *datas, *datad;
772 l_float32 maxscale;
773 PIX *pixd;
774
775 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs))
776 return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp",
777 __func__, NULL);
778 maxscale = L_MAX(scalex, scaley);
779 if (maxscale < 0.7) {
780 L_WARNING("scaling factors < 0.7; do regular scaling\n", __func__);
781 return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0);
782 }
783
784 /* Do fast special cases if possible */
785 if (scalex == 1.0 && scaley == 1.0)
786 return pixCopy(NULL, pixs);
787 if (scalex == 2.0 && scaley == 2.0)
788 return pixScaleGray2xLI(pixs);
789 if (scalex == 4.0 && scaley == 4.0)
790 return pixScaleGray4xLI(pixs);
791
792 /* General case */
793 pixGetDimensions(pixs, &ws, &hs, NULL);
794 datas = pixGetData(pixs);
795 wpls = pixGetWpl(pixs);
796 wd = (l_int32)(scalex * (l_float32)ws + 0.5);
797 hd = (l_int32)(scaley * (l_float32)hs + 0.5);
798 if ((pixd = pixCreate(wd, hd, 8)) == NULL)
799 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
800 pixCopyText(pixd, pixs);
801 pixCopyResolution(pixd, pixs);
802 pixCopyInputFormat(pixd, pixs);
803 pixScaleResolution(pixd, scalex, scaley);
804 datad = pixGetData(pixd);
805 wpld = pixGetWpl(pixd);
806 scaleGrayLILow(datad, wd, hd, wpld, datas, ws, hs, wpls);
807 return pixd;
808 }
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 {
827 l_int32 ws, hs, wpls, wpld;
828 l_uint32 *datas, *datad;
829 PIX *pixd;
830
831 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs))
832 return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp",
833 __func__, NULL);
834
835 pixGetDimensions(pixs, &ws, &hs, NULL);
836 datas = pixGetData(pixs);
837 wpls = pixGetWpl(pixs);
838 if ((pixd = pixCreate(2 * ws, 2 * hs, 8)) == NULL)
839 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
840 pixCopyResolution(pixd, pixs);
841 pixCopyInputFormat(pixd, pixs);
842 pixScaleResolution(pixd, 2.0, 2.0);
843 datad = pixGetData(pixd);
844 wpld = pixGetWpl(pixd);
845 scaleGray2xLILow(datad, wpld, datas, ws, hs, wpls);
846 return pixd;
847 }
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 {
866 l_int32 ws, hs, wpls, wpld;
867 l_uint32 *datas, *datad;
868 PIX *pixd;
869
870 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs))
871 return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp",
872 __func__, NULL);
873
874 pixGetDimensions(pixs, &ws, &hs, NULL);
875 datas = pixGetData(pixs);
876 wpls = pixGetWpl(pixs);
877 if ((pixd = pixCreate(4 * ws, 4 * hs, 8)) == NULL)
878 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
879 pixCopyResolution(pixd, pixs);
880 pixCopyInputFormat(pixd, pixs);
881 pixScaleResolution(pixd, 4.0, 4.0);
882 datad = pixGetData(pixd);
883 wpld = pixGetWpl(pixd);
884 scaleGray4xLILow(datad, wpld, datas, ws, hs, wpls);
885 return pixd;
886 }
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 {
910 l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
911 l_uint32 *datas, *datad, *lines, *lined, *lineb;
912 PIX *pixd;
913
914 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs))
915 return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped",
916 __func__, NULL);
917 if (thresh < 0 || thresh > 256)
918 return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]",
919 __func__, NULL);
920
921 pixGetDimensions(pixs, &ws, &hs, NULL);
922 wd = 2 * ws;
923 hd = 2 * hs;
924 hsm = hs - 1;
925 datas = pixGetData(pixs);
926 wpls = pixGetWpl(pixs);
927
928 /* Make line buffer for 2 lines of virtual intermediate image */
929 wplb = (wd + 3) / 4;
930 if ((lineb = (l_uint32 *)LEPT_CALLOC(2 * wplb, sizeof(l_uint32))) == NULL)
931 return (PIX *)ERROR_PTR("lineb not made", __func__, NULL);
932
933 /* Make dest binary image */
934 if ((pixd = pixCreate(wd, hd, 1)) == NULL) {
935 LEPT_FREE(lineb);
936 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
937 }
938 pixCopyInputFormat(pixd, pixs);
939 pixCopyResolution(pixd, pixs);
940 pixScaleResolution(pixd, 2.0, 2.0);
941 wpld = pixGetWpl(pixd);
942 datad = pixGetData(pixd);
943
944 /* Do all but last src line */
945 for (i = 0; i < hsm; i++) {
946 lines = datas + i * wpls;
947 lined = datad + 2 * i * wpld; /* do 2 dest lines at a time */
948 scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 0);
949 thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh);
950 thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh);
951 }
952
953 /* Do last src line */
954 lines = datas + hsm * wpls;
955 lined = datad + 2 * hsm * wpld;
956 scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 1);
957 thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh);
958 thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh);
959
960 LEPT_FREE(lineb);
961 return pixd;
962 }
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 {
986 l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
987 l_uint32 *datas, *datad;
988 l_uint32 *lined;
989 l_uint32 *lineb = NULL; /* 2 intermediate buffer lines */
990 l_uint32 *linebp = NULL; /* 1 intermediate buffer line */
991 l_uint32 *bufs = NULL; /* 2 source buffer lines */
992 PIX *pixd = NULL;
993
994 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs))
995 return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped",
996 __func__, NULL);
997
998 pixGetDimensions(pixs, &ws, &hs, NULL);
999 wd = 2 * ws;
1000 hd = 2 * hs;
1001 hsm = hs - 1;
1002 datas = pixGetData(pixs);
1003 wpls = pixGetWpl(pixs);
1004
1005 /* Make line buffers for 2 lines of src image */
1006 if ((bufs = (l_uint32 *)LEPT_CALLOC(2 * wpls, sizeof(l_uint32))) == NULL)
1007 return (PIX *)ERROR_PTR("bufs not made", __func__, NULL);
1008
1009 /* Make line buffer for 2 lines of virtual intermediate image */
1010 wplb = (wd + 3) / 4;
1011 if ((lineb = (l_uint32 *)LEPT_CALLOC(2 * wplb, sizeof(l_uint32))) == NULL) {
1012 L_ERROR("lineb not made\n", __func__);
1013 goto cleanup;
1014 }
1015
1016 /* Make line buffer for 1 line of virtual intermediate image */
1017 if ((linebp = (l_uint32 *)LEPT_CALLOC(wplb, sizeof(l_uint32))) == NULL) {
1018 L_ERROR("linebp not made\n", __func__);
1019 goto cleanup;
1020 }
1021
1022 /* Make dest binary image */
1023 if ((pixd = pixCreate(wd, hd, 1)) == NULL) {
1024 L_ERROR("pixd not made\n", __func__);
1025 goto cleanup;
1026 }
1027 pixCopyInputFormat(pixd, pixs);
1028 pixCopyResolution(pixd, pixs);
1029 pixScaleResolution(pixd, 2.0, 2.0);
1030 wpld = pixGetWpl(pixd);
1031 datad = pixGetData(pixd);
1032
1033 /* Start with the first src and the first dest line */
1034 memcpy(bufs, datas, 4 * wpls); /* first src line */
1035 memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */
1036 scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */
1037 lined = datad;
1038 ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb,
1039 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1040 /* 1st d line */
1041
1042 /* Do all but last src line */
1043 for (i = 1; i < hsm; i++) {
1044 memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */
1045 memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls);
1046 memcpy(linebp, lineb + wplb, 4 * wplb);
1047 scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */
1048 lined = datad + 2 * i * wpld;
1049 ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb,
1050 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1051 /* odd dest line */
1052 ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb,
1053 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1054 /* even dest line */
1055 }
1056
1057 /* Do the last src line and the last 3 dest lines */
1058 memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */
1059 memcpy(linebp, lineb + wplb, 4 * wplb); /* 1 i line */
1060 scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 2 i lines */
1061 ditherToBinaryLineLow(lined + wpld, wd, linebp, lineb,
1062 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1063 /* odd dest line */
1064 ditherToBinaryLineLow(lined + 2 * wpld, wd, lineb, lineb + wplb,
1065 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1066 /* even dest line */
1067 ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + wplb, NULL,
1068 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1);
1069 /* last dest line */
1070
1071 cleanup:
1072 LEPT_FREE(bufs);
1073 LEPT_FREE(lineb);
1074 LEPT_FREE(linebp);
1075 return pixd;
1076 }
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 {
1104 l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
1105 l_uint32 *datas, *datad, *lines, *lined, *lineb;
1106 PIX *pixd;
1107
1108 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs))
1109 return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped",
1110 __func__, NULL);
1111 if (thresh < 0 || thresh > 256)
1112 return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]",
1113 __func__, NULL);
1114
1115 pixGetDimensions(pixs, &ws, &hs, NULL);
1116 wd = 4 * ws;
1117 hd = 4 * hs;
1118 hsm = hs - 1;
1119 datas = pixGetData(pixs);
1120 wpls = pixGetWpl(pixs);
1121
1122 /* Make line buffer for 4 lines of virtual intermediate image */
1123 wplb = (wd + 3) / 4;
1124 if ((lineb = (l_uint32 *)LEPT_CALLOC(4 * wplb, sizeof(l_uint32))) == NULL)
1125 return (PIX *)ERROR_PTR("lineb not made", __func__, NULL);
1126
1127 /* Make dest binary image */
1128 if ((pixd = pixCreate(wd, hd, 1)) == NULL) {
1129 LEPT_FREE(lineb);
1130 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1131 }
1132 pixCopyInputFormat(pixd, pixs);
1133 pixCopyResolution(pixd, pixs);
1134 pixScaleResolution(pixd, 4.0, 4.0);
1135 wpld = pixGetWpl(pixd);
1136 datad = pixGetData(pixd);
1137
1138 /* Do all but last src line */
1139 for (i = 0; i < hsm; i++) {
1140 lines = datas + i * wpls;
1141 lined = datad + 4 * i * wpld; /* do 4 dest lines at a time */
1142 scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 0);
1143 for (j = 0; j < 4; j++) {
1144 thresholdToBinaryLineLow(lined + j * wpld, wd,
1145 lineb + j * wplb, 8, thresh);
1146 }
1147 }
1148
1149 /* Do last src line */
1150 lines = datas + hsm * wpls;
1151 lined = datad + 4 * hsm * wpld;
1152 scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 1);
1153 for (j = 0; j < 4; j++) {
1154 thresholdToBinaryLineLow(lined + j * wpld, wd,
1155 lineb + j * wplb, 8, thresh);
1156 }
1157
1158 LEPT_FREE(lineb);
1159 return pixd;
1160 }
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 {
1189 l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
1190 l_uint32 *datas, *datad;
1191 l_uint32 *lined;
1192 l_uint32 *lineb = NULL; /* 4 intermediate buffer lines */
1193 l_uint32 *linebp = NULL; /* 1 intermediate buffer line */
1194 l_uint32 *bufs = NULL; /* 2 source buffer lines */
1195 PIX *pixd = NULL;
1196
1197 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs))
1198 return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped",
1199 __func__, NULL);
1200
1201 pixGetDimensions(pixs, &ws, &hs, NULL);
1202 wd = 4 * ws;
1203 hd = 4 * hs;
1204 hsm = hs - 1;
1205 datas = pixGetData(pixs);
1206 wpls = pixGetWpl(pixs);
1207
1208 /* Make line buffers for 2 lines of src image */
1209 if ((bufs = (l_uint32 *)LEPT_CALLOC(2 * wpls, sizeof(l_uint32))) == NULL)
1210 return (PIX *)ERROR_PTR("bufs not made", __func__, NULL);
1211
1212 /* Make line buffer for 4 lines of virtual intermediate image */
1213 wplb = (wd + 3) / 4;
1214 if ((lineb = (l_uint32 *)LEPT_CALLOC(4 * wplb, sizeof(l_uint32))) == NULL) {
1215 L_ERROR("lineb not made\n", __func__);
1216 goto cleanup;
1217 }
1218
1219 /* Make line buffer for 1 line of virtual intermediate image */
1220 if ((linebp = (l_uint32 *)LEPT_CALLOC(wplb, sizeof(l_uint32))) == NULL) {
1221 L_ERROR("linebp not made\n", __func__);
1222 goto cleanup;
1223 }
1224
1225 /* Make dest binary image */
1226 if ((pixd = pixCreate(wd, hd, 1)) == NULL) {
1227 L_ERROR("pixd not made\n", __func__);
1228 goto cleanup;
1229 }
1230 pixCopyInputFormat(pixd, pixs);
1231 pixCopyResolution(pixd, pixs);
1232 pixScaleResolution(pixd, 4.0, 4.0);
1233 wpld = pixGetWpl(pixd);
1234 datad = pixGetData(pixd);
1235
1236 /* Start with the first src and the first 3 dest lines */
1237 memcpy(bufs, datas, 4 * wpls); /* first src line */
1238 memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */
1239 scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */
1240 lined = datad;
1241 for (j = 0; j < 3; j++) { /* first 3 d lines of Q */
1242 ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb,
1243 lineb + (j + 1) * wplb,
1244 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1245 }
1246
1247 /* Do all but last src line */
1248 for (i = 1; i < hsm; i++) {
1249 memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */
1250 memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls);
1251 memcpy(linebp, lineb + 3 * wplb, 4 * wplb);
1252 scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */
1253 lined = datad + 4 * i * wpld;
1254 ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb,
1255 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1256 /* 4th dest line of Q */
1257 for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */
1258 ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb,
1259 lineb + (j + 1) * wplb,
1260 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1261 }
1262 }
1263
1264 /* Do the last src line and the last 5 dest lines */
1265 memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */
1266 memcpy(linebp, lineb + 3 * wplb, 4 * wplb); /* 1 b line */
1267 scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 4 b lines */
1268 lined = datad + 4 * hsm * wpld;
1269 ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb,
1270 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1271 /* 4th dest line of Q */
1272 for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */
1273 ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb,
1274 lineb + (j + 1) * wplb,
1275 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
1276 }
1277 /* And finally, the last dest line */
1278 ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + 3 * wplb, NULL,
1279 DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1);
1280
1281 cleanup:
1282 LEPT_FREE(bufs);
1283 LEPT_FREE(lineb);
1284 LEPT_FREE(linebp);
1285 return pixd;
1286 }
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 {
1321 if (!pixs)
1322 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1323 return pixScaleBySamplingWithShift(pixs, scalex, scaley, 0.5, 0.5);
1324 }
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 {
1351 l_int32 ws, hs, d, wpls, wd, hd, wpld;
1352 l_uint32 *datas, *datad;
1353 PIX *pixd;
1354
1355 if (!pixs)
1356 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1357 if (scalex <= 0.0 || scaley <= 0.0)
1358 return (PIX *)ERROR_PTR("scale factor <= 0", __func__, NULL);
1359 if (scalex == 1.0 && scaley == 1.0)
1360 return pixCopy(NULL, pixs);
1361 if (shiftx != 0.0 && shiftx != 0.5)
1362 return (PIX *)ERROR_PTR("shiftx != 0.0 or 0.5", __func__, NULL);
1363 if (shifty != 0.0 && shifty != 0.5)
1364 return (PIX *)ERROR_PTR("shifty != 0.0 or 0.5", __func__, NULL);
1365 if ((d = pixGetDepth(pixs)) == 1)
1366 return pixScaleBinaryWithShift(pixs, scalex, scaley, shiftx, shifty);
1367
1368 pixGetDimensions(pixs, &ws, &hs, NULL);
1369 datas = pixGetData(pixs);
1370 wpls = pixGetWpl(pixs);
1371 wd = (l_int32)(scalex * (l_float32)ws + 0.5);
1372 hd = (l_int32)(scaley * (l_float32)hs + 0.5);
1373 if ((pixd = pixCreate(wd, hd, d)) == NULL)
1374 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1375 pixCopyResolution(pixd, pixs);
1376 pixScaleResolution(pixd, scalex, scaley);
1377 pixCopyColormap(pixd, pixs);
1378 pixCopyText(pixd, pixs);
1379 pixCopyInputFormat(pixd, pixs);
1380 pixCopySpp(pixd, pixs);
1381 datad = pixGetData(pixd);
1382 wpld = pixGetWpl(pixd);
1383 scaleBySamplingLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls,
1384 shiftx, shifty);
1385 if (d == 32 && pixGetSpp(pixs) == 4)
1386 pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley);
1387
1388 return pixd;
1389 }
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 {
1416 l_int32 w, h;
1417 l_float32 scalex, scaley;
1418
1419 if (!pixs)
1420 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1421 if (wd <= 0 && hd <= 0)
1422 return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL);
1423
1424 pixGetDimensions(pixs, &w, &h, NULL);
1425 if (wd <= 0) {
1426 scaley = (l_float32)hd / (l_float32)h;
1427 scalex = scaley;
1428 } else if (hd <= 0) {
1429 scalex = (l_float32)wd / (l_float32)w;
1430 scaley = scalex;
1431 } else {
1432 scalex = (l_float32)wd / (l_float32)w;
1433 scaley = (l_float32)hd / (l_float32)h;
1434 }
1435
1436 return pixScaleBySampling(pixs, scalex, scaley);
1437 }
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 {
1457 l_float32 scale;
1458
1459 if (!pixs)
1460 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1461 if (factor <= 1) {
1462 if (factor < 1)
1463 L_ERROR("factor must be >= 1; returning a copy\n", __func__);
1464 return pixCopy(NULL, pixs);
1465 }
1466
1467 scale = 1.f / (l_float32)factor;
1468 return pixScaleBySampling(pixs, scale, scale);
1469 }
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 {
1498 l_int32 byteval, shift;
1499 l_int32 i, j, ws, hs, wd, hd, wpls, wpld;
1500 l_uint32 *datas, *words, *datad, *lined;
1501 l_float32 scale;
1502 PIX *pixd;
1503
1504 if (!pixs)
1505 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1506 if (pixGetDepth(pixs) != 32)
1507 return (PIX *)ERROR_PTR("depth not 32 bpp", __func__, NULL);
1508 if (factor < 1)
1509 return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL);
1510
1511 if (color == COLOR_RED)
1512 shift = L_RED_SHIFT;
1513 else if (color == COLOR_GREEN)
1514 shift = L_GREEN_SHIFT;
1515 else if (color == COLOR_BLUE)
1516 shift = L_BLUE_SHIFT;
1517 else
1518 return (PIX *)ERROR_PTR("invalid color", __func__, NULL);
1519
1520 pixGetDimensions(pixs, &ws, &hs, NULL);
1521 datas = pixGetData(pixs);
1522 wpls = pixGetWpl(pixs);
1523
1524 wd = ws / factor;
1525 hd = hs / factor;
1526 if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1527 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1528 pixCopyResolution(pixd, pixs);
1529 pixCopyInputFormat(pixd, pixs);
1530 scale = 1.f / (l_float32) factor;
1531 pixScaleResolution(pixd, scale, scale);
1532 datad = pixGetData(pixd);
1533 wpld = pixGetWpl(pixd);
1534
1535 for (i = 0; i < hd; i++) {
1536 words = datas + i * factor * wpls;
1537 lined = datad + i * wpld;
1538 for (j = 0; j < wd; j++, words += factor) {
1539 byteval = ((*words) >> shift) & 0xff;
1540 SET_DATA_BYTE(lined, j, byteval);
1541 }
1542 }
1543
1544 return pixd;
1545 }
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 {
1571 l_int32 byteval;
1572 l_int32 i, j, ws, hs, wd, hd, wpls, wpld;
1573 l_uint32 *datas, *words, *datad, *lined;
1574 l_float32 scale;
1575 PIX *pixd;
1576
1577 if (!pixs)
1578 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1579 if (factor < 1)
1580 return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL);
1581 if (pixGetDepth(pixs) != 32)
1582 return (PIX *)ERROR_PTR("depth not 32 bpp", __func__, NULL);
1583
1584 pixGetDimensions(pixs, &ws, &hs, NULL);
1585 datas = pixGetData(pixs);
1586 wpls = pixGetWpl(pixs);
1587
1588 wd = ws / factor;
1589 hd = hs / factor;
1590 if ((pixd = pixCreate(wd, hd, 1)) == NULL)
1591 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1592 pixCopyResolution(pixd, pixs);
1593 pixCopyInputFormat(pixd, pixs);
1594 scale = 1. / (l_float32) factor;
1595 pixScaleResolution(pixd, scale, scale);
1596 datad = pixGetData(pixd);
1597 wpld = pixGetWpl(pixd);
1598
1599 for (i = 0; i < hd; i++) {
1600 words = datas + i * factor * wpls;
1601 lined = datad + i * wpld;
1602 for (j = 0; j < wd; j++, words += factor) {
1603 byteval = ((*words) >> L_GREEN_SHIFT) & 0xff;
1604 if (byteval < thresh)
1605 SET_DATA_BIT(lined, j);
1606 }
1607 }
1608
1609 return pixd;
1610 }
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 {
1635 l_int32 byteval;
1636 l_int32 i, j, ws, hs, wd, hd, wpls, wpld, sj;
1637 l_uint32 *datas, *datad, *lines, *lined;
1638 l_float32 scale;
1639 PIX *pixd;
1640
1641 if (!pixs)
1642 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1643 if (factor < 1)
1644 return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL);
1645 if (pixGetDepth(pixs) != 8)
1646 return (PIX *)ERROR_PTR("depth not 8 bpp", __func__, NULL);
1647
1648 pixGetDimensions(pixs, &ws, &hs, NULL);
1649 datas = pixGetData(pixs);
1650 wpls = pixGetWpl(pixs);
1651
1652 wd = ws / factor;
1653 hd = hs / factor;
1654 if ((pixd = pixCreate(wd, hd, 1)) == NULL)
1655 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1656 pixCopyResolution(pixd, pixs);
1657 pixCopyInputFormat(pixd, pixs);
1658 scale = 1.f / (l_float32) factor;
1659 pixScaleResolution(pixd, scale, scale);
1660 datad = pixGetData(pixd);
1661 wpld = pixGetWpl(pixd);
1662
1663 for (i = 0; i < hd; i++) {
1664 lines = datas + i * factor * wpls;
1665 lined = datad + i * wpld;
1666 for (j = 0, sj = 0; j < wd; j++, sj += factor) {
1667 byteval = GET_DATA_BYTE(lines, sj);
1668 if (byteval < thresh)
1669 SET_DATA_BIT(lined, j);
1670 }
1671 }
1672
1673 return pixd;
1674 }
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 {
1715 l_int32 ws, hs, d, wd, hd, wpls, wpld, isize;
1716 l_uint32 val;
1717 l_uint32 *datas, *datad;
1718 l_float32 minscale, size;
1719 PIX *pixs, *pixd;
1720
1721 if (!pix)
1722 return (PIX *)ERROR_PTR("pix not defined", __func__, NULL);
1723 if (scalex >= 0.7 || scaley >= 0.7) {
1724 L_WARNING("scaling factor not < 0.7; do regular scaling\n", __func__);
1725 return pixScaleGeneral(pix, scalex, scaley, 0.0, 0);
1726 }
1727 d = pixGetDepth(pix);
1728 if (d != 2 && d != 4 && d !=8 && d != 32)
1729 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 if ((pixs = pixConvertTo8Or32(pix, L_CLONE, 0)) == NULL)
1733 return (PIX *)ERROR_PTR("pixs not made", __func__, NULL);
1734 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 minscale = L_MIN(scalex, scaley);
1740 size = 1.0f / minscale; /* ideal filter full width */
1741 isize = L_MIN(10000, L_MAX(2, (l_int32)(size + 0.5)));
1742
1743 pixGetDimensions(pixs, &ws, &hs, NULL);
1744 if ((ws < isize) || (hs < isize)) {
1745 pixd = pixCreate(1, 1, d);
1746 pixGetPixel(pixs, ws / 2, hs / 2, &val);
1747 pixSetPixel(pixd, 0, 0, val);
1748 L_WARNING("ridiculously small scaling factor %f\n", __func__, minscale);
1749 pixDestroy(&pixs);
1750 return pixd;
1751 }
1752
1753 datas = pixGetData(pixs);
1754 wpls = pixGetWpl(pixs);
1755 wd = L_MAX(1, (l_int32)(scalex * (l_float32)ws + 0.5));
1756 hd = L_MAX(1, (l_int32)(scaley * (l_float32)hs + 0.5));
1757 if ((pixd = pixCreate(wd, hd, d)) == NULL) {
1758 pixDestroy(&pixs);
1759 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1760 }
1761 pixCopyResolution(pixd, pixs);
1762 pixCopyInputFormat(pixd, pixs);
1763 pixScaleResolution(pixd, scalex, scaley);
1764 datad = pixGetData(pixd);
1765 wpld = pixGetWpl(pixd);
1766 scaleSmoothLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls, isize);
1767 if (d == 32 && pixGetSpp(pixs) == 4)
1768 pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley);
1769
1770 pixDestroy(&pixs);
1771 return pixd;
1772 }
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 {
1799 l_int32 w, h;
1800 l_float32 scalex, scaley;
1801
1802 if (!pixs)
1803 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1804 if (wd <= 0 && hd <= 0)
1805 return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL);
1806
1807 pixGetDimensions(pixs, &w, &h, NULL);
1808 if (wd <= 0) {
1809 scaley = (l_float32)hd / (l_float32)h;
1810 scalex = scaley;
1811 } else if (hd <= 0) {
1812 scalex = (l_float32)wd / (l_float32)w;
1813 scaley = scalex;
1814 } else {
1815 scalex = (l_float32)wd / (l_float32)w;
1816 scaley = (l_float32)hd / (l_float32)h;
1817 }
1818
1819 return pixScaleSmooth(pixs, scalex, scaley);
1820 }
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 {
1836 l_int32 wd, hd, wpls, wpld;
1837 l_uint32 *datas, *datad;
1838 PIX *pixd;
1839
1840 if (!pixs)
1841 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1842 if (pixGetDepth(pixs) != 32)
1843 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
1844 if (rwt + gwt + bwt < 0.98 || rwt + gwt + bwt > 1.02)
1845 return (PIX *)ERROR_PTR("sum of wts should be 1.0", __func__, NULL);
1846
1847 wd = pixGetWidth(pixs) / 2;
1848 hd = pixGetHeight(pixs) / 2;
1849 wpls = pixGetWpl(pixs);
1850 datas = pixGetData(pixs);
1851 if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1852 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1853 pixCopyResolution(pixd, pixs);
1854 pixCopyInputFormat(pixd, pixs);
1855 pixScaleResolution(pixd, 0.5, 0.5);
1856 wpld = pixGetWpl(pixd);
1857 datad = pixGetData(pixd);
1858 scaleRGBToGray2Low(datad, wd, hd, wpld, datas, wpls, rwt, gwt, bwt);
1859 return pixd;
1860 }
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 {
1914 l_int32 ws, hs, d, wd, hd, wpls, wpld;
1915 l_uint32 *datas, *datad;
1916 l_float32 maxscale, minscale;
1917 PIX *pixs, *pixd, *pix1, *pix2, *pix3;
1918
1919 if (!pix)
1920 return (PIX *)ERROR_PTR("pix not defined", __func__, NULL);
1921 d = pixGetDepth(pix);
1922 if (d != 2 && d != 4 && d != 8 && d != 32)
1923 return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", __func__, NULL);
1924
1925 minscale = L_MIN(scalex, scaley);
1926 if (minscale < 0.02) { /* too small for area mapping */
1927 L_WARNING("tiny scaling factor; using pixScaleSmooth()\n", __func__);
1928 return pixScaleSmooth(pix, scalex, scaley);
1929 }
1930
1931 maxscale = L_MAX(scalex, scaley);
1932 if (maxscale >= 0.7) { /* too large for area mapping */
1933 L_WARNING("scaling factor >= 0.7; do regular scaling\n", __func__);
1934 return pixScaleGeneral(pix, scalex, scaley, 0.0, 0);
1935 }
1936
1937 /* Special cases: 2x, 4x, 8x, 16x reduction */
1938 if (scalex == 0.5 && scaley == 0.5)
1939 return pixScaleAreaMap2(pix);
1940 if (scalex == 0.25 && scaley == 0.25) {
1941 pix1 = pixScaleAreaMap2(pix);
1942 pixd = pixScaleAreaMap2(pix1);
1943 pixDestroy(&pix1);
1944 return pixd;
1945 }
1946 if (scalex == 0.125 && scaley == 0.125) {
1947 pix1 = pixScaleAreaMap2(pix);
1948 pix2 = pixScaleAreaMap2(pix1);
1949 pixd = pixScaleAreaMap2(pix2);
1950 pixDestroy(&pix1);
1951 pixDestroy(&pix2);
1952 return pixd;
1953 }
1954 if (scalex == 0.0625 && scaley == 0.0625) {
1955 pix1 = pixScaleAreaMap2(pix);
1956 pix2 = pixScaleAreaMap2(pix1);
1957 pix3 = pixScaleAreaMap2(pix2);
1958 pixd = pixScaleAreaMap2(pix3);
1959 pixDestroy(&pix1);
1960 pixDestroy(&pix2);
1961 pixDestroy(&pix3);
1962 return pixd;
1963 }
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 if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
1980 L_WARNING("pix has colormap; removing\n", __func__);
1981 pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
1982 d = pixGetDepth(pixs);
1983 } else if (d == 2 || d == 4) {
1984 pixs = pixConvertTo8(pix, FALSE);
1985 d = 8;
1986 } else {
1987 pixs = pixClone(pix);
1988 }
1989
1990 pixGetDimensions(pixs, &ws, &hs, NULL);
1991 datas = pixGetData(pixs);
1992 wpls = pixGetWpl(pixs);
1993 wd = (l_int32)(scalex * (l_float32)ws + 0.5);
1994 hd = (l_int32)(scaley * (l_float32)hs + 0.5);
1995 if (wd < 1 || hd < 1) {
1996 pixDestroy(&pixs);
1997 return (PIX *)ERROR_PTR("pixd too small", __func__, NULL);
1998 }
1999 if ((pixd = pixCreate(wd, hd, d)) == NULL) {
2000 pixDestroy(&pixs);
2001 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2002 }
2003 pixCopyInputFormat(pixd, pixs);
2004 pixCopyResolution(pixd, pixs);
2005 pixScaleResolution(pixd, scalex, scaley);
2006 datad = pixGetData(pixd);
2007 wpld = pixGetWpl(pixd);
2008 if (d == 8) {
2009 scaleGrayAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls);
2010 } else { /* RGB, d == 32 */
2011 scaleColorAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls);
2012 if (pixGetSpp(pixs) == 4)
2013 pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley);
2014 }
2015
2016 pixDestroy(&pixs);
2017 return pixd;
2018 }
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 {
2043 l_int32 wd, hd, d, wpls, wpld;
2044 l_uint32 *datas, *datad;
2045 PIX *pixs, *pixd;
2046
2047 if (!pix)
2048 return (PIX *)ERROR_PTR("pix not defined", __func__, NULL);
2049 d = pixGetDepth(pix);
2050 if (d != 2 && d != 4 && d != 8 && d != 32)
2051 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 if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
2056 L_WARNING("pix has colormap; removing\n", __func__);
2057 pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
2058 d = pixGetDepth(pixs);
2059 } else if (d == 2 || d == 4) {
2060 pixs = pixConvertTo8(pix, FALSE);
2061 d = 8;
2062 } else {
2063 pixs = pixClone(pix);
2064 }
2065
2066 wd = pixGetWidth(pixs) / 2;
2067 hd = pixGetHeight(pixs) / 2;
2068 datas = pixGetData(pixs);
2069 wpls = pixGetWpl(pixs);
2070 pixd = pixCreate(wd, hd, d);
2071 datad = pixGetData(pixd);
2072 wpld = pixGetWpl(pixd);
2073 pixCopyInputFormat(pixd, pixs);
2074 pixCopyResolution(pixd, pixs);
2075 pixScaleResolution(pixd, 0.5, 0.5);
2076 scaleAreaMapLow2(datad, wd, hd, wpld, datas, d, wpls);
2077 if (pixGetSpp(pixs) == 4)
2078 pixScaleAndTransferAlpha(pixd, pixs, 0.5, 0.5);
2079 pixDestroy(&pixs);
2080 return pixd;
2081 }
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 {
2108 l_int32 w, h;
2109 l_float32 scalex, scaley;
2110
2111 if (!pixs)
2112 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2113 if (wd <= 0 && hd <= 0)
2114 return (PIX *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL);
2115
2116 pixGetDimensions(pixs, &w, &h, NULL);
2117 if (wd <= 0) {
2118 scaley = (l_float32)hd / (l_float32)h;
2119 scalex = scaley;
2120 } else if (hd <= 0) {
2121 scalex = (l_float32)wd / (l_float32)w;
2122 scaley = scalex;
2123 } else {
2124 scalex = (l_float32)wd / (l_float32)w;
2125 scaley = (l_float32)hd / (l_float32)h;
2126 }
2127
2128 return pixScaleAreaMap(pixs, scalex, scaley);
2129 }
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 {
2162 if (!pixs)
2163 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2164 if (pixGetDepth(pixs) != 1)
2165 return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL);
2166 return pixScaleBinaryWithShift(pixs, scalex, scaley, 0.5, 0.5);
2167 }
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 {
2194 l_int32 ws, hs, wpls, wd, hd, wpld;
2195 l_uint32 *datas, *datad;
2196 PIX *pixd;
2197
2198 if (!pixs)
2199 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2200 if (pixGetDepth(pixs) != 1)
2201 return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL);
2202 if (scalex <= 0.0 || scaley <= 0.0)
2203 return (PIX *)ERROR_PTR("scale factor <= 0", __func__, NULL);
2204 if (scalex == 1.0 && scaley == 1.0)
2205 return pixCopy(NULL, pixs);
2206 if (shiftx != 0.0 && shiftx != 0.5)
2207 return (PIX *)ERROR_PTR("shiftx != 0.0 or 0.5", __func__, NULL);
2208 if (shifty != 0.0 && shifty != 0.5)
2209 return (PIX *)ERROR_PTR("shifty != 0.0 or 0.5", __func__, NULL);
2210
2211 pixGetDimensions(pixs, &ws, &hs, NULL);
2212 datas = pixGetData(pixs);
2213 wpls = pixGetWpl(pixs);
2214 wd = (l_int32)(scalex * (l_float32)ws + 0.5);
2215 hd = (l_int32)(scaley * (l_float32)hs + 0.5);
2216 if ((pixd = pixCreate(wd, hd, 1)) == NULL)
2217 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2218 pixCopyColormap(pixd, pixs);
2219 pixCopyText(pixd, pixs);
2220 pixCopyInputFormat(pixd, pixs);
2221 pixCopyResolution(pixd, pixs);
2222 pixScaleResolution(pixd, scalex, scaley);
2223 datad = pixGetData(pixd);
2224 wpld = pixGetWpl(pixd);
2225 scaleBinaryLow(datad, wd, hd, wpld, datas, ws, hs, wpls, shiftx, shifty);
2226 return pixd;
2227 }
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 {
2259 l_int32 i, j, wm2, hm2;
2260 l_int32 xpm, ypm; /* location in src image, to 1/16 of a pixel */
2261 l_int32 xp, yp, xf, yf; /* src pixel and pixel fraction coordinates */
2262 l_uint32 v00r, v01r, v10r, v11r, v00g, v01g, v10g, v11g;
2263 l_uint32 v00b, v01b, v10b, v11b, area00, area01, area10, area11;
2264 l_uint32 pixels1, pixels2, pixels3, pixels4, pixel;
2265 l_uint32 *lines, *lined;
2266 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 scx = 16.f * (l_float32)ws / (l_float32)wd;
2273 scy = 16.f * (l_float32)hs / (l_float32)hd;
2274 wm2 = ws - 2;
2275 hm2 = hs - 2;
2276
2277 /* Iterate over the destination pixels */
2278 for (i = 0; i < hd; i++) {
2279 ypm = (l_int32)(scy * (l_float32)i);
2280 yp = ypm >> 4;
2281 yf = ypm & 0x0f;
2282 lined = datad + i * wpld;
2283 lines = datas + yp * wpls;
2284 for (j = 0; j < wd; j++) {
2285 xpm = (l_int32)(scx * (l_float32)j);
2286 xp = xpm >> 4;
2287 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 pixels1 = *(lines + xp);
2295
2296 if (xp > wm2 || yp > hm2) {
2297 if (yp > hm2 && xp <= wm2) { /* pixels near bottom */
2298 pixels2 = *(lines + xp + 1);
2299 pixels3 = pixels1;
2300 pixels4 = pixels2;
2301 } else if (xp > wm2 && yp <= hm2) { /* pixels near rt side */
2302 pixels2 = pixels1;
2303 pixels3 = *(lines + wpls + xp);
2304 pixels4 = pixels3;
2305 } else { /* pixels at LR corner */
2306 pixels4 = pixels3 = pixels2 = pixels1;
2307 }
2308 } else {
2309 pixels2 = *(lines + xp + 1);
2310 pixels3 = *(lines + wpls + xp);
2311 pixels4 = *(lines + wpls + xp + 1);
2312 }
2313
2314 area00 = (16 - xf) * (16 - yf);
2315 area10 = xf * (16 - yf);
2316 area01 = (16 - xf) * yf;
2317 area11 = xf * yf;
2318 v00r = area00 * ((pixels1 >> L_RED_SHIFT) & 0xff);
2319 v00g = area00 * ((pixels1 >> L_GREEN_SHIFT) & 0xff);
2320 v00b = area00 * ((pixels1 >> L_BLUE_SHIFT) & 0xff);
2321 v10r = area10 * ((pixels2 >> L_RED_SHIFT) & 0xff);
2322 v10g = area10 * ((pixels2 >> L_GREEN_SHIFT) & 0xff);
2323 v10b = area10 * ((pixels2 >> L_BLUE_SHIFT) & 0xff);
2324 v01r = area01 * ((pixels3 >> L_RED_SHIFT) & 0xff);
2325 v01g = area01 * ((pixels3 >> L_GREEN_SHIFT) & 0xff);
2326 v01b = area01 * ((pixels3 >> L_BLUE_SHIFT) & 0xff);
2327 v11r = area11 * ((pixels4 >> L_RED_SHIFT) & 0xff);
2328 v11g = area11 * ((pixels4 >> L_GREEN_SHIFT) & 0xff);
2329 v11b = area11 * ((pixels4 >> L_BLUE_SHIFT) & 0xff);
2330 pixel = (((v00r + v10r + v01r + v11r + 128) << 16) & 0xff000000) |
2331 (((v00g + v10g + v01g + v11g + 128) << 8) & 0x00ff0000) |
2332 ((v00b + v10b + v01b + v11b + 128) & 0x0000ff00);
2333 *(lined + j) = pixel;
2334 }
2335 }
2336 }
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 {
2364 l_int32 i, j, wm2, hm2;
2365 l_int32 xpm, ypm; /* location in src image, to 1/16 of a pixel */
2366 l_int32 xp, yp, xf, yf; /* src pixel and pixel fraction coordinates */
2367 l_int32 v00, v01, v10, v11, v00_val, v01_val, v10_val, v11_val;
2368 l_uint8 val;
2369 l_uint32 *lines, *lined;
2370 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 scx = 16.f * (l_float32)ws / (l_float32)wd;
2377 scy = 16.f * (l_float32)hs / (l_float32)hd;
2378 wm2 = ws - 2;
2379 hm2 = hs - 2;
2380
2381 /* Iterate over the destination pixels */
2382 for (i = 0; i < hd; i++) {
2383 ypm = (l_int32)(scy * (l_float32)i);
2384 yp = ypm >> 4;
2385 yf = ypm & 0x0f;
2386 lined = datad + i * wpld;
2387 lines = datas + yp * wpls;
2388 for (j = 0; j < wd; j++) {
2389 xpm = (l_int32)(scx * (l_float32)j);
2390 xp = xpm >> 4;
2391 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 v00_val = GET_DATA_BYTE(lines, xp);
2398 if (xp > wm2 || yp > hm2) {
2399 if (yp > hm2 && xp <= wm2) { /* pixels near bottom */
2400 v01_val = v00_val;
2401 v10_val = GET_DATA_BYTE(lines, xp + 1);
2402 v11_val = v10_val;
2403 } else if (xp > wm2 && yp <= hm2) { /* pixels near rt side */
2404 v01_val = GET_DATA_BYTE(lines + wpls, xp);
2405 v10_val = v00_val;
2406 v11_val = v01_val;
2407 } else { /* pixels at LR corner */
2408 v10_val = v01_val = v11_val = v00_val;
2409 }
2410 } else {
2411 v10_val = GET_DATA_BYTE(lines, xp + 1);
2412 v01_val = GET_DATA_BYTE(lines + wpls, xp);
2413 v11_val = GET_DATA_BYTE(lines + wpls, xp + 1);
2414 }
2415
2416 v00 = (16 - xf) * (16 - yf) * v00_val;
2417 v10 = xf * (16 - yf) * v10_val;
2418 v01 = (16 - xf) * yf * v01_val;
2419 v11 = xf * yf * v11_val;
2420
2421 val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256);
2422 SET_DATA_BYTE(lined, j, val);
2423 }
2424 }
2425 }
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 {
2479 l_int32 i, hsm;
2480 l_uint32 *lines, *lined;
2481
2482 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 for (i = 0; i < hsm; i++) {
2496 lines = datas + i * wpls;
2497 lined = datad + 2 * i * wpld;
2498 scaleColor2xLILineLow(lined, wpld, lines, ws, wpls, 0);
2499 }
2500
2501 /* last src line */
2502 lines = datas + hsm * wpls;
2503 lined = datad + 2 * hsm * wpld;
2504 scaleColor2xLILineLow(lined, wpld, lines, ws, wpls, 1);
2505 }
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 {
2527 l_int32 j, jd, wsm;
2528 l_uint32 rval1, rval2, rval3, rval4, gval1, gval2, gval3, gval4;
2529 l_uint32 bval1, bval2, bval3, bval4;
2530 l_uint32 pixels1, pixels2, pixels3, pixels4, pixel;
2531 l_uint32 *linesp, *linedp;
2532
2533 wsm = ws - 1;
2534
2535 if (lastlineflag == 0) {
2536 linesp = lines + wpls;
2537 linedp = lined + wpld;
2538 pixels1 = *lines;
2539 pixels3 = *linesp;
2540
2541 /* initialize with v(2) and v(4) */
2542 rval2 = pixels1 >> 24;
2543 gval2 = (pixels1 >> 16) & 0xff;
2544 bval2 = (pixels1 >> 8) & 0xff;
2545 rval4 = pixels3 >> 24;
2546 gval4 = (pixels3 >> 16) & 0xff;
2547 bval4 = (pixels3 >> 8) & 0xff;
2548
2549 for (j = 0, jd = 0; j < wsm; j++, jd += 2) {
2550 /* shift in previous src values */
2551 rval1 = rval2;
2552 gval1 = gval2;
2553 bval1 = bval2;
2554 rval3 = rval4;
2555 gval3 = gval4;
2556 bval3 = bval4;
2557 /* get new src values */
2558 pixels2 = *(lines + j + 1);
2559 pixels4 = *(linesp + j + 1);
2560 rval2 = pixels2 >> 24;
2561 gval2 = (pixels2 >> 16) & 0xff;
2562 bval2 = (pixels2 >> 8) & 0xff;
2563 rval4 = pixels4 >> 24;
2564 gval4 = (pixels4 >> 16) & 0xff;
2565 bval4 = (pixels4 >> 8) & 0xff;
2566 /* save dest values */
2567 pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8);
2568 *(lined + jd) = pixel; /* pix 1 */
2569 pixel = ((((rval1 + rval2) << 23) & 0xff000000) |
2570 (((gval1 + gval2) << 15) & 0x00ff0000) |
2571 (((bval1 + bval2) << 7) & 0x0000ff00));
2572 *(lined + jd + 1) = pixel; /* pix 2 */
2573 pixel = ((((rval1 + rval3) << 23) & 0xff000000) |
2574 (((gval1 + gval3) << 15) & 0x00ff0000) |
2575 (((bval1 + bval3) << 7) & 0x0000ff00));
2576 *(linedp + jd) = pixel; /* pix 3 */
2577 pixel = ((((rval1 + rval2 + rval3 + rval4) << 22) & 0xff000000) |
2578 (((gval1 + gval2 + gval3 + gval4) << 14) & 0x00ff0000) |
2579 (((bval1 + bval2 + bval3 + bval4) << 6) & 0x0000ff00));
2580 *(linedp + jd + 1) = pixel; /* pix 4 */
2581 }
2582 /* last src pixel on line */
2583 rval1 = rval2;
2584 gval1 = gval2;
2585 bval1 = bval2;
2586 rval3 = rval4;
2587 gval3 = gval4;
2588 bval3 = bval4;
2589 pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8);
2590 *(lined + 2 * wsm) = pixel; /* pix 1 */
2591 *(lined + 2 * wsm + 1) = pixel; /* pix 2 */
2592 pixel = ((((rval1 + rval3) << 23) & 0xff000000) |
2593 (((gval1 + gval3) << 15) & 0x00ff0000) |
2594 (((bval1 + bval3) << 7) & 0x0000ff00));
2595 *(linedp + 2 * wsm) = pixel; /* pix 3 */
2596 *(linedp + 2 * wsm + 1) = pixel; /* pix 4 */
2597 } else { /* last row of src pixels: lastlineflag == 1 */
2598 linedp = lined + wpld;
2599 pixels2 = *lines;
2600 rval2 = pixels2 >> 24;
2601 gval2 = (pixels2 >> 16) & 0xff;
2602 bval2 = (pixels2 >> 8) & 0xff;
2603 for (j = 0, jd = 0; j < wsm; j++, jd += 2) {
2604 rval1 = rval2;
2605 gval1 = gval2;
2606 bval1 = bval2;
2607 pixels2 = *(lines + j + 1);
2608 rval2 = pixels2 >> 24;
2609 gval2 = (pixels2 >> 16) & 0xff;
2610 bval2 = (pixels2 >> 8) & 0xff;
2611 pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8);
2612 *(lined + jd) = pixel; /* pix 1 */
2613 *(linedp + jd) = pixel; /* pix 2 */
2614 pixel = ((((rval1 + rval2) << 23) & 0xff000000) |
2615 (((gval1 + gval2) << 15) & 0x00ff0000) |
2616 (((bval1 + bval2) << 7) & 0x0000ff00));
2617 *(lined + jd + 1) = pixel; /* pix 3 */
2618 *(linedp + jd + 1) = pixel; /* pix 4 */
2619 }
2620 rval1 = rval2;
2621 gval1 = gval2;
2622 bval1 = bval2;
2623 pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8);
2624 *(lined + 2 * wsm) = pixel; /* pix 1 */
2625 *(lined + 2 * wsm + 1) = pixel; /* pix 2 */
2626 *(linedp + 2 * wsm) = pixel; /* pix 3 */
2627 *(linedp + 2 * wsm + 1) = pixel; /* pix 4 */
2628 }
2629 }
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 {
2681 l_int32 i, hsm;
2682 l_uint32 *lines, *lined;
2683
2684 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 for (i = 0; i < hsm; i++) {
2698 lines = datas + i * wpls;
2699 lined = datad + 2 * i * wpld;
2700 scaleGray2xLILineLow(lined, wpld, lines, ws, wpls, 0);
2701 }
2702
2703 /* last src line */
2704 lines = datas + hsm * wpls;
2705 lined = datad + 2 * hsm * wpld;
2706 scaleGray2xLILineLow(lined, wpld, lines, ws, wpls, 1);
2707 }
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 {
2729 l_int32 j, jd, wsm, w;
2730 l_uint32 sval1, sval2, sval3, sval4;
2731 l_uint32 *linesp, *linedp;
2732 l_uint32 words, wordsp, wordd, worddp;
2733
2734 wsm = ws - 1;
2735
2736 if (lastlineflag == 0) {
2737 linesp = lines + wpls;
2738 linedp = lined + wpld;
2739
2740 /* Unroll the loop 4x and work on full words */
2741 words = lines[0];
2742 wordsp = linesp[0];
2743 sval2 = (words >> 24) & 0xff;
2744 sval4 = (wordsp >> 24) & 0xff;
2745 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 sval1 = sval2;
2751 sval2 = (words >> 16) & 0xff;
2752 sval3 = sval4;
2753 sval4 = (wordsp >> 16) & 0xff;
2754 wordd = (sval1 << 24) | (((sval1 + sval2) >> 1) << 16);
2755 worddp = (((sval1 + sval3) >> 1) << 24) |
2756 (((sval1 + sval2 + sval3 + sval4) >> 2) << 16);
2757
2758 sval1 = sval2;
2759 sval2 = (words >> 8) & 0xff;
2760 sval3 = sval4;
2761 sval4 = (wordsp >> 8) & 0xff;
2762 wordd |= (sval1 << 8) | ((sval1 + sval2) >> 1);
2763 worddp |= (((sval1 + sval3) >> 1) << 8) |
2764 ((sval1 + sval2 + sval3 + sval4) >> 2);
2765 lined[w * 2] = wordd;
2766 linedp[w * 2] = worddp;
2767
2768 sval1 = sval2;
2769 sval2 = words & 0xff;
2770 sval3 = sval4;
2771 sval4 = wordsp & 0xff;
2772 wordd = (sval1 << 24) | /* pix 1 */
2773 (((sval1 + sval2) >> 1) << 16); /* pix 2 */
2774 worddp = (((sval1 + sval3) >> 1) << 24) | /* pix 3 */
2775 (((sval1 + sval2 + sval3 + sval4) >> 2) << 16); /* pix 4 */
2776
2777 /* Load the next word as we need its first byte */
2778 words = lines[w + 1];
2779 wordsp = linesp[w + 1];
2780 sval1 = sval2;
2781 sval2 = (words >> 24) & 0xff;
2782 sval3 = sval4;
2783 sval4 = (wordsp >> 24) & 0xff;
2784 wordd |= (sval1 << 8) | /* pix 1 */
2785 ((sval1 + sval2) >> 1); /* pix 2 */
2786 worddp |= (((sval1 + sval3) >> 1) << 8) | /* pix 3 */
2787 ((sval1 + sval2 + sval3 + sval4) >> 2); /* pix 4 */
2788 lined[w * 2 + 1] = wordd;
2789 linedp[w * 2 + 1] = worddp;
2790 }
2791
2792 /* Finish up the last word */
2793 for (; j < wsm; j++, jd += 2) {
2794 sval1 = sval2;
2795 sval3 = sval4;
2796 sval2 = GET_DATA_BYTE(lines, j + 1);
2797 sval4 = GET_DATA_BYTE(linesp, j + 1);
2798 SET_DATA_BYTE(lined, jd, sval1); /* pix 1 */
2799 SET_DATA_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */
2800 SET_DATA_BYTE(linedp, jd, (sval1 + sval3) / 2); /* pix 3 */
2801 SET_DATA_BYTE(linedp, jd + 1,
2802 (sval1 + sval2 + sval3 + sval4) / 4); /* pix 4 */
2803 }
2804 sval1 = sval2;
2805 sval3 = sval4;
2806 SET_DATA_BYTE(lined, 2 * wsm, sval1); /* pix 1 */
2807 SET_DATA_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */
2808 SET_DATA_BYTE(linedp, 2 * wsm, (sval1 + sval3) / 2); /* pix 3 */
2809 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 } else { /* last row of src pixels: lastlineflag == 1 */
2838 linedp = lined + wpld;
2839 sval2 = GET_DATA_BYTE(lines, 0);
2840 for (j = 0, jd = 0; j < wsm; j++, jd += 2) {
2841 sval1 = sval2;
2842 sval2 = GET_DATA_BYTE(lines, j + 1);
2843 SET_DATA_BYTE(lined, jd, sval1); /* pix 1 */
2844 SET_DATA_BYTE(linedp, jd, sval1); /* pix 3 */
2845 SET_DATA_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */
2846 SET_DATA_BYTE(linedp, jd + 1, (sval1 + sval2) / 2); /* pix 4 */
2847 }
2848 sval1 = sval2;
2849 SET_DATA_BYTE(lined, 2 * wsm, sval1); /* pix 1 */
2850 SET_DATA_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */
2851 SET_DATA_BYTE(linedp, 2 * wsm, sval1); /* pix 3 */
2852 SET_DATA_BYTE(linedp, 2 * wsm + 1, sval1); /* pix 4 */
2853 }
2854 }
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 {
2922 l_int32 i, hsm;
2923 l_uint32 *lines, *lined;
2924
2925 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 for (i = 0; i < hsm; i++) {
2937 lines = datas + i * wpls;
2938 lined = datad + 4 * i * wpld;
2939 scaleGray4xLILineLow(lined, wpld, lines, ws, wpls, 0);
2940 }
2941
2942 /* last src line */
2943 lines = datas + hsm * wpls;
2944 lined = datad + 4 * hsm * wpld;
2945 scaleGray4xLILineLow(lined, wpld, lines, ws, wpls, 1);
2946 }
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 {
2968 l_int32 j, jd, wsm, wsm4;
2969 l_int32 s1, s2, s3, s4, s1t, s2t, s3t, s4t;
2970 l_uint32 *linesp, *linedp1, *linedp2, *linedp3;
2971
2972 wsm = ws - 1;
2973 wsm4 = 4 * wsm;
2974
2975 if (lastlineflag == 0) {
2976 linesp = lines + wpls;
2977 linedp1 = lined + wpld;
2978 linedp2 = lined + 2 * wpld;
2979 linedp3 = lined + 3 * wpld;
2980 s2 = GET_DATA_BYTE(lines, 0);
2981 s4 = GET_DATA_BYTE(linesp, 0);
2982 for (j = 0, jd = 0; j < wsm; j++, jd += 4) {
2983 s1 = s2;
2984 s3 = s4;
2985 s2 = GET_DATA_BYTE(lines, j + 1);
2986 s4 = GET_DATA_BYTE(linesp, j + 1);
2987 s1t = 3 * s1;
2988 s2t = 3 * s2;
2989 s3t = 3 * s3;
2990 s4t = 3 * s4;
2991 SET_DATA_BYTE(lined, jd, s1); /* d1 */
2992 SET_DATA_BYTE(lined, jd + 1, (s1t + s2) / 4); /* d2 */
2993 SET_DATA_BYTE(lined, jd + 2, (s1 + s2) / 2); /* d3 */
2994 SET_DATA_BYTE(lined, jd + 3, (s1 + s2t) / 4); /* d4 */
2995 SET_DATA_BYTE(linedp1, jd, (s1t + s3) / 4); /* d5 */
2996 SET_DATA_BYTE(linedp1, jd + 1, (9*s1 + s2t + s3t + s4) / 16); /*d6*/
2997 SET_DATA_BYTE(linedp1, jd + 2, (s1t + s2t + s3 + s4) / 8); /* d7 */
2998 SET_DATA_BYTE(linedp1, jd + 3, (s1t + 9*s2 + s3 + s4t) / 16);/*d8*/
2999 SET_DATA_BYTE(linedp2, jd, (s1 + s3) / 2); /* d9 */
3000 SET_DATA_BYTE(linedp2, jd + 1, (s1t + s2 + s3t + s4) / 8);/* d10 */
3001 SET_DATA_BYTE(linedp2, jd + 2, (s1 + s2 + s3 + s4) / 4); /* d11 */
3002 SET_DATA_BYTE(linedp2, jd + 3, (s1 + s2t + s3 + s4t) / 8);/* d12 */
3003 SET_DATA_BYTE(linedp3, jd, (s1 + s3t) / 4); /* d13 */
3004 SET_DATA_BYTE(linedp3, jd + 1, (s1t + s2 + 9*s3 + s4t) / 16);/*d14*/
3005 SET_DATA_BYTE(linedp3, jd + 2, (s1 + s2 + s3t + s4t) / 8); /* d15 */
3006 SET_DATA_BYTE(linedp3, jd + 3, (s1 + s2t + s3t + 9*s4) / 16);/*d16*/
3007 }
3008 s1 = s2;
3009 s3 = s4;
3010 s1t = 3 * s1;
3011 s3t = 3 * s3;
3012 SET_DATA_BYTE(lined, wsm4, s1); /* d1 */
3013 SET_DATA_BYTE(lined, wsm4 + 1, s1); /* d2 */
3014 SET_DATA_BYTE(lined, wsm4 + 2, s1); /* d3 */
3015 SET_DATA_BYTE(lined, wsm4 + 3, s1); /* d4 */
3016 SET_DATA_BYTE(linedp1, wsm4, (s1t + s3) / 4); /* d5 */
3017 SET_DATA_BYTE(linedp1, wsm4 + 1, (s1t + s3) / 4); /* d6 */
3018 SET_DATA_BYTE(linedp1, wsm4 + 2, (s1t + s3) / 4); /* d7 */
3019 SET_DATA_BYTE(linedp1, wsm4 + 3, (s1t + s3) / 4); /* d8 */
3020 SET_DATA_BYTE(linedp2, wsm4, (s1 + s3) / 2); /* d9 */
3021 SET_DATA_BYTE(linedp2, wsm4 + 1, (s1 + s3) / 2); /* d10 */
3022 SET_DATA_BYTE(linedp2, wsm4 + 2, (s1 + s3) / 2); /* d11 */
3023 SET_DATA_BYTE(linedp2, wsm4 + 3, (s1 + s3) / 2); /* d12 */
3024 SET_DATA_BYTE(linedp3, wsm4, (s1 + s3t) / 4); /* d13 */
3025 SET_DATA_BYTE(linedp3, wsm4 + 1, (s1 + s3t) / 4); /* d14 */
3026 SET_DATA_BYTE(linedp3, wsm4 + 2, (s1 + s3t) / 4); /* d15 */
3027 SET_DATA_BYTE(linedp3, wsm4 + 3, (s1 + s3t) / 4); /* d16 */
3028 } else { /* last row of src pixels: lastlineflag == 1 */
3029 linedp1 = lined + wpld;
3030 linedp2 = lined + 2 * wpld;
3031 linedp3 = lined + 3 * wpld;
3032 s2 = GET_DATA_BYTE(lines, 0);
3033 for (j = 0, jd = 0; j < wsm; j++, jd += 4) {
3034 s1 = s2;
3035 s2 = GET_DATA_BYTE(lines, j + 1);
3036 s1t = 3 * s1;
3037 s2t = 3 * s2;
3038 SET_DATA_BYTE(lined, jd, s1); /* d1 */
3039 SET_DATA_BYTE(lined, jd + 1, (s1t + s2) / 4 ); /* d2 */
3040 SET_DATA_BYTE(lined, jd + 2, (s1 + s2) / 2 ); /* d3 */
3041 SET_DATA_BYTE(lined, jd + 3, (s1 + s2t) / 4 ); /* d4 */
3042 SET_DATA_BYTE(linedp1, jd, s1); /* d5 */
3043 SET_DATA_BYTE(linedp1, jd + 1, (s1t + s2) / 4 ); /* d6 */
3044 SET_DATA_BYTE(linedp1, jd + 2, (s1 + s2) / 2 ); /* d7 */
3045 SET_DATA_BYTE(linedp1, jd + 3, (s1 + s2t) / 4 ); /* d8 */
3046 SET_DATA_BYTE(linedp2, jd, s1); /* d9 */
3047 SET_DATA_BYTE(linedp2, jd + 1, (s1t + s2) / 4 ); /* d10 */
3048 SET_DATA_BYTE(linedp2, jd + 2, (s1 + s2) / 2 ); /* d11 */
3049 SET_DATA_BYTE(linedp2, jd + 3, (s1 + s2t) / 4 ); /* d12 */
3050 SET_DATA_BYTE(linedp3, jd, s1); /* d13 */
3051 SET_DATA_BYTE(linedp3, jd + 1, (s1t + s2) / 4 ); /* d14 */
3052 SET_DATA_BYTE(linedp3, jd + 2, (s1 + s2) / 2 ); /* d15 */
3053 SET_DATA_BYTE(linedp3, jd + 3, (s1 + s2t) / 4 ); /* d16 */
3054 }
3055 s1 = s2;
3056 SET_DATA_BYTE(lined, wsm4, s1); /* d1 */
3057 SET_DATA_BYTE(lined, wsm4 + 1, s1); /* d2 */
3058 SET_DATA_BYTE(lined, wsm4 + 2, s1); /* d3 */
3059 SET_DATA_BYTE(lined, wsm4 + 3, s1); /* d4 */
3060 SET_DATA_BYTE(linedp1, wsm4, s1); /* d5 */
3061 SET_DATA_BYTE(linedp1, wsm4 + 1, s1); /* d6 */
3062 SET_DATA_BYTE(linedp1, wsm4 + 2, s1); /* d7 */
3063 SET_DATA_BYTE(linedp1, wsm4 + 3, s1); /* d8 */
3064 SET_DATA_BYTE(linedp2, wsm4, s1); /* d9 */
3065 SET_DATA_BYTE(linedp2, wsm4 + 1, s1); /* d10 */
3066 SET_DATA_BYTE(linedp2, wsm4 + 2, s1); /* d11 */
3067 SET_DATA_BYTE(linedp2, wsm4 + 3, s1); /* d12 */
3068 SET_DATA_BYTE(linedp3, wsm4, s1); /* d13 */
3069 SET_DATA_BYTE(linedp3, wsm4 + 1, s1); /* d14 */
3070 SET_DATA_BYTE(linedp3, wsm4 + 2, s1); /* d15 */
3071 SET_DATA_BYTE(linedp3, wsm4 + 3, s1); /* d16 */
3072 }
3073 }
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 {
3107 l_int32 i, j;
3108 l_int32 xs, prevxs, sval;
3109 l_int32 *srow, *scol;
3110 l_uint32 csval;
3111 l_uint32 *lines, *prevlines, *lined, *prevlined;
3112 l_float32 wratio, hratio;
3113
3114 if (d != 2 && d != 4 && d !=8 && d != 16 && d != 32)
3115 return ERROR_INT("pixel depth not supported", __func__, 1);
3116
3117 /* Clear dest */
3118 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 if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL)
3123 return ERROR_INT("srow not made", __func__, 1);
3124 if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) {
3125 LEPT_FREE(srow);
3126 return ERROR_INT("scol not made", __func__, 1);
3127 }
3128
3129 wratio = (l_float32)ws / (l_float32)wd;
3130 hratio = (l_float32)hs / (l_float32)hd;
3131 for (i = 0; i < hd; i++)
3132 srow[i] = L_MIN((l_int32)(hratio * i + shifty), hs - 1);
3133 for (j = 0; j < wd; j++)
3134 scol[j] = L_MIN((l_int32)(wratio * j + shiftx), ws - 1);
3135
3136 prevlines = NULL;
3137 for (i = 0; i < hd; i++) {
3138 lines = datas + srow[i] * wpls;
3139 lined = datad + i * wpld;
3140 if (lines != prevlines) { /* make dest from new source row */
3141 prevxs = -1;
3142 sval = 0;
3143 csval = 0;
3144 if (d == 2) {
3145 for (j = 0; j < wd; j++) {
3146 xs = scol[j];
3147 if (xs != prevxs) { /* get dest pix from source col */
3148 sval = GET_DATA_DIBIT(lines, xs);
3149 SET_DATA_DIBIT(lined, j, sval);
3150 prevxs = xs;
3151 } else { /* copy prev dest pix */
3152 SET_DATA_DIBIT(lined, j, sval);
3153 }
3154 }
3155 } else if (d == 4) {
3156 for (j = 0; j < wd; j++) {
3157 xs = scol[j];
3158 if (xs != prevxs) { /* get dest pix from source col */
3159 sval = GET_DATA_QBIT(lines, xs);
3160 SET_DATA_QBIT(lined, j, sval);
3161 prevxs = xs;
3162 } else { /* copy prev dest pix */
3163 SET_DATA_QBIT(lined, j, sval);
3164 }
3165 }
3166 } else if (d == 8) {
3167 for (j = 0; j < wd; j++) {
3168 xs = scol[j];
3169 if (xs != prevxs) { /* get dest pix from source col */
3170 sval = GET_DATA_BYTE(lines, xs);
3171 SET_DATA_BYTE(lined, j, sval);
3172 prevxs = xs;
3173 } else { /* copy prev dest pix */
3174 SET_DATA_BYTE(lined, j, sval);
3175 }
3176 }
3177 } else if (d == 16) {
3178 for (j = 0; j < wd; j++) {
3179 xs = scol[j];
3180 if (xs != prevxs) { /* get dest pix from source col */
3181 sval = GET_DATA_TWO_BYTES(lines, xs);
3182 SET_DATA_TWO_BYTES(lined, j, sval);
3183 prevxs = xs;
3184 } else { /* copy prev dest pix */
3185 SET_DATA_TWO_BYTES(lined, j, sval);
3186 }
3187 }
3188 } else { /* d == 32 */
3189 for (j = 0; j < wd; j++) {
3190 xs = scol[j];
3191 if (xs != prevxs) { /* get dest pix from source col */
3192 csval = lines[xs];
3193 lined[j] = csval;
3194 prevxs = xs;
3195 } else { /* copy prev dest pix */
3196 lined[j] = csval;
3197 }
3198 }
3199 }
3200 } else { /* lines == prevlines; copy prev dest row */
3201 prevlined = lined - wpld;
3202 memcpy(lined, prevlined, 4 * wpld);
3203 }
3204 prevlines = lines;
3205 }
3206
3207 LEPT_FREE(srow);
3208 LEPT_FREE(scol);
3209 return 0;
3210 }
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 {
3239 l_int32 i, j, m, n, xstart;
3240 l_int32 val, rval, gval, bval;
3241 l_int32 *srow, *scol;
3242 l_uint32 *lines, *lined, *line, *ppixel;
3243 l_uint32 pixel;
3244 l_float32 wratio, hratio, norm;
3245
3246 /* Clear dest */
3247 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 if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL)
3255 return ERROR_INT("srow not made", __func__, 1);
3256 if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) {
3257 LEPT_FREE(srow);
3258 return ERROR_INT("scol not made", __func__, 1);
3259 }
3260
3261 norm = 1.f / (l_float32)(size * size);
3262 wratio = (l_float32)ws / (l_float32)wd;
3263 hratio = (l_float32)hs / (l_float32)hd;
3264 for (i = 0; i < hd; i++)
3265 srow[i] = L_MIN((l_int32)(hratio * i), hs - size);
3266 for (j = 0; j < wd; j++)
3267 scol[j] = L_MIN((l_int32)(wratio * j), ws - size);
3268
3269 /* For each dest pixel, compute average */
3270 if (d == 8) {
3271 for (i = 0; i < hd; i++) {
3272 lines = datas + srow[i] * wpls;
3273 lined = datad + i * wpld;
3274 for (j = 0; j < wd; j++) {
3275 xstart = scol[j];
3276 val = 0;
3277 for (m = 0; m < size; m++) {
3278 line = lines + m * wpls;
3279 for (n = 0; n < size; n++) {
3280 val += GET_DATA_BYTE(line, xstart + n);
3281 }
3282 }
3283 val = (l_int32)((l_float32)val * norm);
3284 SET_DATA_BYTE(lined, j, val);
3285 }
3286 }
3287 } else { /* d == 32 */
3288 for (i = 0; i < hd; i++) {
3289 lines = datas + srow[i] * wpls;
3290 lined = datad + i * wpld;
3291 for (j = 0; j < wd; j++) {
3292 xstart = scol[j];
3293 rval = gval = bval = 0;
3294 for (m = 0; m < size; m++) {
3295 ppixel = lines + m * wpls + xstart;
3296 for (n = 0; n < size; n++) {
3297 pixel = *(ppixel + n);
3298 rval += (pixel >> L_RED_SHIFT) & 0xff;
3299 gval += (pixel >> L_GREEN_SHIFT) & 0xff;
3300 bval += (pixel >> L_BLUE_SHIFT) & 0xff;
3301 }
3302 }
3303 rval = (l_int32)((l_float32)rval * norm);
3304 gval = (l_int32)((l_float32)gval * norm);
3305 bval = (l_int32)((l_float32)bval * norm);
3306 composeRGBPixel(rval, gval, bval, lined + j);
3307 }
3308 }
3309 }
3310
3311 LEPT_FREE(srow);
3312 LEPT_FREE(scol);
3313 return 0;
3314 }
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 {
3337 l_int32 i, j, val, rval, gval, bval;
3338 l_uint32 *lines, *lined;
3339 l_uint32 pixel;
3340
3341 rwt *= 0.25;
3342 gwt *= 0.25;
3343 bwt *= 0.25;
3344 for (i = 0; i < hd; i++) {
3345 lines = datas + 2 * i * wpls;
3346 lined = datad + i * wpld;
3347 for (j = 0; j < wd; j++) {
3348 /* Sum each of the color components from 4 src pixels */
3349 pixel = *(lines + 2 * j);
3350 rval = (pixel >> L_RED_SHIFT) & 0xff;
3351 gval = (pixel >> L_GREEN_SHIFT) & 0xff;
3352 bval = (pixel >> L_BLUE_SHIFT) & 0xff;
3353 pixel = *(lines + 2 * j + 1);
3354 rval += (pixel >> L_RED_SHIFT) & 0xff;
3355 gval += (pixel >> L_GREEN_SHIFT) & 0xff;
3356 bval += (pixel >> L_BLUE_SHIFT) & 0xff;
3357 pixel = *(lines + wpls + 2 * j);
3358 rval += (pixel >> L_RED_SHIFT) & 0xff;
3359 gval += (pixel >> L_GREEN_SHIFT) & 0xff;
3360 bval += (pixel >> L_BLUE_SHIFT) & 0xff;
3361 pixel = *(lines + wpls + 2 * j + 1);
3362 rval += (pixel >> L_RED_SHIFT) & 0xff;
3363 gval += (pixel >> L_GREEN_SHIFT) & 0xff;
3364 bval += (pixel >> L_BLUE_SHIFT) & 0xff;
3365 /* Generate the dest byte as a weighted sum of the averages */
3366 val = (l_int32)(rwt * rval + gwt * gval + bwt * bval);
3367 SET_DATA_BYTE(lined, j, val);
3368 }
3369 }
3370 }
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 {
3401 l_int32 i, j, k, m, wm2, hm2;
3402 l_int32 area00, area10, area01, area11, areal, arear, areat, areab;
3403 l_int32 xu, yu; /* UL corner in src image, to 1/16 of a pixel */
3404 l_int32 xl, yl; /* LR corner in src image, to 1/16 of a pixel */
3405 l_int32 xup, yup, xuf, yuf; /* UL src pixel: integer and fraction */
3406 l_int32 xlp, ylp, xlf, ylf; /* LR src pixel: integer and fraction */
3407 l_int32 delx, dely, area;
3408 l_int32 v00r, v00g, v00b; /* contrib. from UL src pixel */
3409 l_int32 v01r, v01g, v01b; /* contrib. from LL src pixel */
3410 l_int32 v10r, v10g, v10b; /* contrib from UR src pixel */
3411 l_int32 v11r, v11g, v11b; /* contrib from LR src pixel */
3412 l_int32 vinr, ving, vinb; /* contrib from all full interior src pixels */
3413 l_int32 vmidr, vmidg, vmidb; /* contrib from side parts */
3414 l_int32 rval, gval, bval;
3415 l_uint32 pixel00, pixel10, pixel01, pixel11, pixel;
3416 l_uint32 *lines, *lined;
3417 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 scx = 16.f * (l_float32)ws / (l_float32)wd;
3424 scy = 16.f * (l_float32)hs / (l_float32)hd;
3425 wm2 = ws - 2;
3426 hm2 = hs - 2;
3427
3428 /* Iterate over the destination pixels */
3429 for (i = 0; i < hd; i++) {
3430 yu = (l_int32)(scy * i);
3431 yl = (l_int32)(scy * (i + 1.0));
3432 yup = yu >> 4;
3433 yuf = yu & 0x0f;
3434 ylp = yl >> 4;
3435 ylf = yl & 0x0f;
3436 dely = ylp - yup;
3437 lined = datad + i * wpld;
3438 lines = datas + yup * wpls;
3439 for (j = 0; j < wd; j++) {
3440 xu = (l_int32)(scx * j);
3441 xl = (l_int32)(scx * (j + 1.0));
3442 xup = xu >> 4;
3443 xuf = xu & 0x0f;
3444 xlp = xl >> 4;
3445 xlf = xl & 0x0f;
3446 delx = xlp - xup;
3447
3448 /* If near the edge, just use a src pixel value */
3449 if (xlp > wm2 || ylp > hm2) {
3450 *(lined + j) = *(lines + xup);
3451 continue;
3452 }
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 area = ((16 - xuf) + 16 * (delx - 1) + xlf) *
3458 ((16 - yuf) + 16 * (dely - 1) + ylf);
3459
3460 /* Do area map summation */
3461 pixel00 = *(lines + xup);
3462 pixel10 = *(lines + xlp);
3463 pixel01 = *(lines + dely * wpls + xup);
3464 pixel11 = *(lines + dely * wpls + xlp);
3465 area00 = (16 - xuf) * (16 - yuf);
3466 area10 = xlf * (16 - yuf);
3467 area01 = (16 - xuf) * ylf;
3468 area11 = xlf * ylf;
3469 v00r = area00 * ((pixel00 >> L_RED_SHIFT) & 0xff);
3470 v00g = area00 * ((pixel00 >> L_GREEN_SHIFT) & 0xff);
3471 v00b = area00 * ((pixel00 >> L_BLUE_SHIFT) & 0xff);
3472 v10r = area10 * ((pixel10 >> L_RED_SHIFT) & 0xff);
3473 v10g = area10 * ((pixel10 >> L_GREEN_SHIFT) & 0xff);
3474 v10b = area10 * ((pixel10 >> L_BLUE_SHIFT) & 0xff);
3475 v01r = area01 * ((pixel01 >> L_RED_SHIFT) & 0xff);
3476 v01g = area01 * ((pixel01 >> L_GREEN_SHIFT) & 0xff);
3477 v01b = area01 * ((pixel01 >> L_BLUE_SHIFT) & 0xff);
3478 v11r = area11 * ((pixel11 >> L_RED_SHIFT) & 0xff);
3479 v11g = area11 * ((pixel11 >> L_GREEN_SHIFT) & 0xff);
3480 v11b = area11 * ((pixel11 >> L_BLUE_SHIFT) & 0xff);
3481 vinr = ving = vinb = 0;
3482 for (k = 1; k < dely; k++) { /* for full src pixels */
3483 for (m = 1; m < delx; m++) {
3484 pixel = *(lines + k * wpls + xup + m);
3485 vinr += 256 * ((pixel >> L_RED_SHIFT) & 0xff);
3486 ving += 256 * ((pixel >> L_GREEN_SHIFT) & 0xff);
3487 vinb += 256 * ((pixel >> L_BLUE_SHIFT) & 0xff);
3488 }
3489 }
3490 vmidr = vmidg = vmidb = 0;
3491 areal = (16 - xuf) * 16;
3492 arear = xlf * 16;
3493 areat = 16 * (16 - yuf);
3494 areab = 16 * ylf;
3495 for (k = 1; k < dely; k++) { /* for left side */
3496 pixel = *(lines + k * wpls + xup);
3497 vmidr += areal * ((pixel >> L_RED_SHIFT) & 0xff);
3498 vmidg += areal * ((pixel >> L_GREEN_SHIFT) & 0xff);
3499 vmidb += areal * ((pixel >> L_BLUE_SHIFT) & 0xff);
3500 }
3501 for (k = 1; k < dely; k++) { /* for right side */
3502 pixel = *(lines + k * wpls + xlp);
3503 vmidr += arear * ((pixel >> L_RED_SHIFT) & 0xff);
3504 vmidg += arear * ((pixel >> L_GREEN_SHIFT) & 0xff);
3505 vmidb += arear * ((pixel >> L_BLUE_SHIFT) & 0xff);
3506 }
3507 for (m = 1; m < delx; m++) { /* for top side */
3508 pixel = *(lines + xup + m);
3509 vmidr += areat * ((pixel >> L_RED_SHIFT) & 0xff);
3510 vmidg += areat * ((pixel >> L_GREEN_SHIFT) & 0xff);
3511 vmidb += areat * ((pixel >> L_BLUE_SHIFT) & 0xff);
3512 }
3513 for (m = 1; m < delx; m++) { /* for bottom side */
3514 pixel = *(lines + dely * wpls + xup + m);
3515 vmidr += areab * ((pixel >> L_RED_SHIFT) & 0xff);
3516 vmidg += areab * ((pixel >> L_GREEN_SHIFT) & 0xff);
3517 vmidb += areab * ((pixel >> L_BLUE_SHIFT) & 0xff);
3518 }
3519
3520 /* Sum all the contributions */
3521 rval = (v00r + v01r + v10r + v11r + vinr + vmidr + 128) / area;
3522 gval = (v00g + v01g + v10g + v11g + ving + vmidg + 128) / area;
3523 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 composeRGBPixel(rval, gval, bval, lined + j);
3530 }
3531 }
3532 }
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 {
3559 l_int32 i, j, k, m, wm2, hm2;
3560 l_int32 xu, yu; /* UL corner in src image, to 1/16 of a pixel */
3561 l_int32 xl, yl; /* LR corner in src image, to 1/16 of a pixel */
3562 l_int32 xup, yup, xuf, yuf; /* UL src pixel: integer and fraction */
3563 l_int32 xlp, ylp, xlf, ylf; /* LR src pixel: integer and fraction */
3564 l_int32 delx, dely, area;
3565 l_int32 v00; /* contrib. from UL src pixel */
3566 l_int32 v01; /* contrib. from LL src pixel */
3567 l_int32 v10; /* contrib from UR src pixel */
3568 l_int32 v11; /* contrib from LR src pixel */
3569 l_int32 vin; /* contrib from all full interior src pixels */
3570 l_int32 vmid; /* contrib from side parts that are full in 1 direction */
3571 l_int32 val;
3572 l_uint32 *lines, *lined;
3573 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 scx = 16.f * (l_float32)ws / (l_float32)wd;
3580 scy = 16.f * (l_float32)hs / (l_float32)hd;
3581 wm2 = ws - 2;
3582 hm2 = hs - 2;
3583
3584 /* Iterate over the destination pixels */
3585 for (i = 0; i < hd; i++) {
3586 yu = (l_int32)(scy * i);
3587 yl = (l_int32)(scy * (i + 1.0));
3588 yup = yu >> 4;
3589 yuf = yu & 0x0f;
3590 ylp = yl >> 4;
3591 ylf = yl & 0x0f;
3592 dely = ylp - yup;
3593 lined = datad + i * wpld;
3594 lines = datas + yup * wpls;
3595 for (j = 0; j < wd; j++) {
3596 xu = (l_int32)(scx * j);
3597 xl = (l_int32)(scx * (j + 1.0));
3598 xup = xu >> 4;
3599 xuf = xu & 0x0f;
3600 xlp = xl >> 4;
3601 xlf = xl & 0x0f;
3602 delx = xlp - xup;
3603
3604 /* If near the edge, just use a src pixel value */
3605 if (xlp > wm2 || ylp > hm2) {
3606 SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xup));
3607 continue;
3608 }
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 area = ((16 - xuf) + 16 * (delx - 1) + xlf) *
3614 ((16 - yuf) + 16 * (dely - 1) + ylf);
3615
3616 /* Do area map summation */
3617 v00 = (16 - xuf) * (16 - yuf) * GET_DATA_BYTE(lines, xup);
3618 v10 = xlf * (16 - yuf) * GET_DATA_BYTE(lines, xlp);
3619 v01 = (16 - xuf) * ylf * GET_DATA_BYTE(lines + dely * wpls, xup);
3620 v11 = xlf * ylf * GET_DATA_BYTE(lines + dely * wpls, xlp);
3621 for (vin = 0, k = 1; k < dely; k++) { /* for full src pixels */
3622 for (m = 1; m < delx; m++) {
3623 vin += 256 * GET_DATA_BYTE(lines + k * wpls, xup + m);
3624 }
3625 }
3626 for (vmid = 0, k = 1; k < dely; k++) /* for left side */
3627 vmid += (16 - xuf) * 16 * GET_DATA_BYTE(lines + k * wpls, xup);
3628 for (k = 1; k < dely; k++) /* for right side */
3629 vmid += xlf * 16 * GET_DATA_BYTE(lines + k * wpls, xlp);
3630 for (m = 1; m < delx; m++) /* for top side */
3631 vmid += 16 * (16 - yuf) * GET_DATA_BYTE(lines, xup + m);
3632 for (m = 1; m < delx; m++) /* for bottom side */
3633 vmid += 16 * ylf * GET_DATA_BYTE(lines + dely * wpls, xup + m);
3634 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 SET_DATA_BYTE(lined, j, val);
3639 }
3640 }
3641 }
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 {
3665 l_int32 i, j, val, rval, gval, bval;
3666 l_uint32 *lines, *lined;
3667 l_uint32 pixel;
3668
3669 if (d == 8) {
3670 for (i = 0; i < hd; i++) {
3671 lines = datas + 2 * i * wpls;
3672 lined = datad + i * wpld;
3673 for (j = 0; j < wd; j++) {
3674 /* Average each dest pixel using 4 src pixels */
3675 val = GET_DATA_BYTE(lines, 2 * j);
3676 val += GET_DATA_BYTE(lines, 2 * j + 1);
3677 val += GET_DATA_BYTE(lines + wpls, 2 * j);
3678 val += GET_DATA_BYTE(lines + wpls, 2 * j + 1);
3679 val >>= 2;
3680 SET_DATA_BYTE(lined, j, val);
3681 }
3682 }
3683 } else { /* d == 32 */
3684 for (i = 0; i < hd; i++) {
3685 lines = datas + 2 * i * wpls;
3686 lined = datad + i * wpld;
3687 for (j = 0; j < wd; j++) {
3688 /* Average each of the color components from 4 src pixels */
3689 pixel = *(lines + 2 * j);
3690 rval = (pixel >> L_RED_SHIFT) & 0xff;
3691 gval = (pixel >> L_GREEN_SHIFT) & 0xff;
3692 bval = (pixel >> L_BLUE_SHIFT) & 0xff;
3693 pixel = *(lines + 2 * j + 1);
3694 rval += (pixel >> L_RED_SHIFT) & 0xff;
3695 gval += (pixel >> L_GREEN_SHIFT) & 0xff;
3696 bval += (pixel >> L_BLUE_SHIFT) & 0xff;
3697 pixel = *(lines + wpls + 2 * j);
3698 rval += (pixel >> L_RED_SHIFT) & 0xff;
3699 gval += (pixel >> L_GREEN_SHIFT) & 0xff;
3700 bval += (pixel >> L_BLUE_SHIFT) & 0xff;
3701 pixel = *(lines + wpls + 2 * j + 1);
3702 rval += (pixel >> L_RED_SHIFT) & 0xff;
3703 gval += (pixel >> L_GREEN_SHIFT) & 0xff;
3704 bval += (pixel >> L_BLUE_SHIFT) & 0xff;
3705 composeRGBPixel(rval >> 2, gval >> 2, bval >> 2, &pixel);
3706 *(lined + j) = pixel;
3707 }
3708 }
3709 }
3710 }
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 {
3740 l_int32 i, j;
3741 l_int32 xs, prevxs, sval;
3742 l_int32 *srow, *scol;
3743 l_uint32 *lines, *prevlines, *lined, *prevlined;
3744 l_float32 wratio, hratio;
3745
3746 /* Clear dest */
3747 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 if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL)
3752 return ERROR_INT("srow not made", __func__, 1);
3753 if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) {
3754 LEPT_FREE(srow);
3755 return ERROR_INT("scol not made", __func__, 1);
3756 }
3757
3758 wratio = (l_float32)ws / (l_float32)wd;
3759 hratio = (l_float32)hs / (l_float32)hd;
3760 for (i = 0; i < hd; i++)
3761 srow[i] = L_MIN((l_int32)(hratio * i + shifty), hs - 1);
3762 for (j = 0; j < wd; j++)
3763 scol[j] = L_MIN((l_int32)(wratio * j + shiftx), ws - 1);
3764
3765 prevlines = NULL;
3766 prevxs = -1;
3767 sval = 0;
3768 for (i = 0; i < hd; i++) {
3769 lines = datas + srow[i] * wpls;
3770 lined = datad + i * wpld;
3771 if (lines != prevlines) { /* make dest from new source row */
3772 for (j = 0; j < wd; j++) {
3773 xs = scol[j];
3774 if (xs != prevxs) { /* get dest pix from source col */
3775 if ((sval = GET_DATA_BIT(lines, xs)))
3776 SET_DATA_BIT(lined, j);
3777 prevxs = xs;
3778 } else { /* copy prev dest pix, if set */
3779 if (sval)
3780 SET_DATA_BIT(lined, j);
3781 }
3782 }
3783 } else { /* lines == prevlines; copy prev dest row */
3784 prevlined = lined - wpld;
3785 memcpy(lined, prevlined, 4 * wpld);
3786 }
3787 prevlines = lines;
3788 }
3789
3790 LEPT_FREE(srow);
3791 LEPT_FREE(scol);
3792 return 0;
3793 }