Coverage Report

Created: 2024-06-18 06:05

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