Coverage Report

Created: 2025-05-12 06:13

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