Coverage Report

Created: 2024-06-18 06:04

/src/leptonica/src/rotateam.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
/*!
29
 * \file rotateam.c
30
 * <pre>
31
 *
32
 *     Grayscale and color rotation for area mapping (== interpolation)
33
 *
34
 *         Rotation about the image center
35
 *                PIX         *pixRotateAM()
36
 *                PIX         *pixRotateAMColor()
37
 *                PIX         *pixRotateAMGray()
38
 *                static void  rotateAMColorLow()
39
 *                static void  rotateAMGrayLow()
40
 *
41
 *         Rotation about the UL corner of the image
42
 *                PIX         *pixRotateAMCorner()
43
 *                PIX         *pixRotateAMColorCorner()
44
 *                PIX         *pixRotateAMGrayCorner()
45
 *                static void  rotateAMColorCornerLow()
46
 *                static void  rotateAMGrayCornerLow()
47
 *
48
 *         Faster color rotation about the image center
49
 *                PIX         *pixRotateAMColorFast()
50
 *                static void  rotateAMColorFastLow()
51
 *
52
 *     Rotations are measured in radians; clockwise is positive.
53
 *
54
 *     The basic area mapping grayscale rotation works on 8 bpp images.
55
 *     For color, the same method is applied to each color separately.
56
 *     This can be done in two ways: (1) as here, computing each dest
57
 *     rgb pixel from the appropriate four src rgb pixels, or (2) separating
58
 *     the color image into three 8 bpp images, rotate each of these,
59
 *     and then combine the result.  Method (1) is about 2.5x faster.
60
 *     We have also implemented a fast approximation for color area-mapping
61
 *     rotation (pixRotateAMColorFast()), which is about 25% faster
62
 *     than the standard color rotator.  If you need the extra speed,
63
 *     use it.
64
 *
65
 *     Area mapping works as follows.  For each dest
66
 *     pixel you find the 4 source pixels that it partially
67
 *     covers.  You then compute the dest pixel value as
68
 *     the area-weighted average of those 4 source pixels.
69
 *     We make two simplifying approximations:
70
 *
71
 *       ~  For simplicity, compute the areas as if the dest
72
 *          pixel were translated but not rotated.
73
 *
74
 *       ~  Compute area overlaps on a discrete sub-pixel grid.
75
 *          Because we are using 8 bpp images with 256 levels,
76
 *          it is convenient to break each pixel into a
77
 *          16x16 sub-pixel grid, and count the number of
78
 *          overlapped sub-pixels.
79
 *
80
 *     It is interesting to note that the digital filter that
81
 *     implements the area mapping algorithm for rotation
82
 *     is identical to the digital filter used for linear
83
 *     interpolation when arbitrarily scaling grayscale images.
84
 *
85
 *     The advantage of area mapping over pixel sampling
86
 *     in grayscale rotation is that the former naturally
87
 *     blurs sharp edges ("anti-aliasing"), so that stair-step
88
 *     artifacts are not introduced.  The disadvantage is that
89
 *     it is significantly slower.
90
 *
91
 *     But it is still pretty fast.  With standard 3 GHz hardware,
92
 *     the anti-aliased (area-mapped) color rotation speed is
93
 *     about 15 million pixels/sec.
94
 *
95
 *     The function pixRotateAMColorFast() is about 10-20% faster
96
 *     than pixRotateAMColor().  The quality is slightly worse,
97
 *     and if you make many successive small rotations, with a
98
 *     total angle of 360 degrees, it has been noted that the
99
 *     center wanders -- it seems to be doing a 1 pixel translation
100
 *     in addition to the rotation.
101
 *
102
 *     Consider again the comparison of image quality between sampling
103
 *     and area mapping.  With sampling, sharp edges such as found in
104
 *     text images remain sharp.  However, sampling artifacts such as
105
 *     characters randomly bouncing up and down by one pixel, or
106
 *     one pixel horizontal shear lines going through a line of text
107
 *     (causing the characters to look like badly rendered italic),
108
 *     are highly visible.  It does not help to sample the source pixel
109
 *     with the largest area covering each dest pixel; the result has
110
 *     the same ugly sampling artifacts.
111
 *
112
 *     With area mapping, these annoying artifacts are avoided, but the
113
 *     blurring of edges makes small text a bit more difficult to read.
114
 *     However, if you are willing to do more computation, you can have
115
 *     the best of both worlds: no sampling artifacts and sharp edges.
116
 *     Use area mapping to avoid sampling issues, and follow it with
117
 *     unsharp masking.  Experiment with the sharpening parameters.
118
 *     I have found that a small amount of sharpening is sufficient to
119
 *     restore the sharp edges in text; e.g.,
120
 *         pix2 = pixUnsharpMasking(pix1, 1, 0.3);
121
 * </pre>
122
 */
123
124
#ifdef HAVE_CONFIG_H
125
#include <config_auto.h>
126
#endif  /* HAVE_CONFIG_H */
127
128
#include <string.h>
129
#include <math.h>   /* required for sin and tan */
130
#include "allheaders.h"
131
132
static void rotateAMColorLow(l_uint32 *datad, l_int32 w, l_int32 h,
133
                             l_int32 wpld, l_uint32 *datas, l_int32 wpls,
134
                             l_float32 angle, l_uint32 colorval);
135
static void rotateAMGrayLow(l_uint32 *datad, l_int32 w, l_int32 h,
136
                            l_int32 wpld, l_uint32 *datas, l_int32 wpls,
137
                            l_float32 angle, l_uint8 grayval);
138
static void rotateAMColorCornerLow(l_uint32 *datad, l_int32 w, l_int32 h,
139
                                   l_int32 wpld, l_uint32 *datas,
140
                                   l_int32 wpls, l_float32 angle,
141
                                   l_uint32 colorval);
142
static void rotateAMGrayCornerLow(l_uint32 *datad, l_int32 w, l_int32 h,
143
                                  l_int32 wpld, l_uint32 *datas, l_int32 wpls,
144
                                  l_float32 angle, l_uint8 grayval);
145
146
static void rotateAMColorFastLow(l_uint32 *datad, l_int32 w, l_int32 h,
147
                                 l_int32 wpld, l_uint32 *datas, l_int32 wpls,
148
                                 l_float32 angle, l_uint32 colorval);
149
150
static const l_float32  MinAngleToRotate = 0.001f;  /* radians; ~0.06 deg */
151
152
153
/*------------------------------------------------------------------*
154
 *                     Rotation about the center                    *
155
 *------------------------------------------------------------------*/
156
/*!
157
 * \brief   pixRotateAM()
158
 *
159
 * \param[in]    pixs 2, 4, 8 bpp gray or colormapped, or 32 bpp RGB
160
 * \param[in]    angle radians; clockwise is positive
161
 * \param[in]    incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
162
 * \return  pixd, or NULL on error
163
 *
164
 * <pre>
165
 * Notes:
166
 *      (1) Rotates about image center.
167
 *      (2) A positive angle gives a clockwise rotation.
168
 *      (3) Brings in either black or white pixels from the boundary.
169
 * </pre>
170
 */
171
PIX *
172
pixRotateAM(PIX       *pixs,
173
            l_float32  angle,
174
            l_int32    incolor)
175
0
{
176
0
l_int32   d;
177
0
l_uint32  fillval;
178
0
PIX      *pixt1, *pixt2, *pixd;
179
180
0
    if (!pixs)
181
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
182
0
    if (pixGetDepth(pixs) == 1)
183
0
        return (PIX *)ERROR_PTR("pixs is 1 bpp", __func__, NULL);
184
185
0
    if (L_ABS(angle) < MinAngleToRotate)
186
0
        return pixClone(pixs);
187
188
        /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
189
0
    pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
190
0
    d = pixGetDepth(pixt1);
191
0
    if (d < 8)
192
0
        pixt2 = pixConvertTo8(pixt1, FALSE);
193
0
    else
194
0
        pixt2 = pixClone(pixt1);
195
0
    d = pixGetDepth(pixt2);
196
197
        /* Compute actual incoming color */
198
0
    fillval = 0;
199
0
    if (incolor == L_BRING_IN_WHITE) {
200
0
        if (d == 8)
201
0
            fillval = 255;
202
0
        else  /* d == 32 */
203
0
            fillval = 0xffffff00;
204
0
    }
205
206
0
    if (d == 8)
207
0
        pixd = pixRotateAMGray(pixt2, angle, fillval);
208
0
    else   /* d == 32 */
209
0
        pixd = pixRotateAMColor(pixt2, angle, fillval);
210
211
0
    pixDestroy(&pixt1);
212
0
    pixDestroy(&pixt2);
213
0
    return pixd;
214
0
}
215
216
217
/*!
218
 * \brief   pixRotateAMColor()
219
 *
220
 * \param[in]    pixs 32 bpp
221
 * \param[in]    angle radians; clockwise is positive
222
 * \param[in]    colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
223
 * \return  pixd, or NULL on error
224
 *
225
 * <pre>
226
 * Notes:
227
 *      (1) Rotates about image center.
228
 *      (2) A positive angle gives a clockwise rotation.
229
 *      (3) Specify the color to be brought in from outside the image.
230
 * </pre>
231
 */
232
PIX *
233
pixRotateAMColor(PIX       *pixs,
234
                 l_float32  angle,
235
                 l_uint32   colorval)
236
0
{
237
0
l_int32    w, h, wpls, wpld;
238
0
l_uint32  *datas, *datad;
239
0
PIX       *pix1, *pix2, *pixd;
240
241
0
    if (!pixs)
242
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
243
0
    if (pixGetDepth(pixs) != 32)
244
0
        return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
245
246
0
    if (L_ABS(angle) < MinAngleToRotate)
247
0
        return pixClone(pixs);
248
249
0
    pixGetDimensions(pixs, &w, &h, NULL);
250
0
    datas = pixGetData(pixs);
251
0
    wpls = pixGetWpl(pixs);
252
0
    pixd = pixCreateTemplate(pixs);
253
0
    datad = pixGetData(pixd);
254
0
    wpld = pixGetWpl(pixd);
255
256
0
    rotateAMColorLow(datad, w, h, wpld, datas, wpls, angle, colorval);
257
0
    if (pixGetSpp(pixs) == 4) {
258
0
        pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
259
0
        pix2 = pixRotateAMGray(pix1, angle, 255);  /* bring in opaque */
260
0
        pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
261
0
        pixDestroy(&pix1);
262
0
        pixDestroy(&pix2);
263
0
    }
264
265
0
    return pixd;
266
0
}
267
268
269
/*!
270
 * \brief   pixRotateAMGray()
271
 *
272
 * \param[in]    pixs 8 bpp
273
 * \param[in]    angle radians; clockwise is positive
274
 * \param[in]    grayval 0 to bring in BLACK, 255 for WHITE
275
 * \return  pixd, or NULL on error
276
 *
277
 * <pre>
278
 * Notes:
279
 *      (1) Rotates about image center.
280
 *      (2) A positive angle gives a clockwise rotation.
281
 *      (3) Specify the grayvalue to be brought in from outside the image.
282
 * </pre>
283
 */
284
PIX *
285
pixRotateAMGray(PIX       *pixs,
286
                l_float32  angle,
287
                l_uint8    grayval)
288
0
{
289
0
l_int32    w, h, wpls, wpld;
290
0
l_uint32  *datas, *datad;
291
0
PIX        *pixd;
292
293
0
    if (!pixs)
294
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
295
0
    if (pixGetDepth(pixs) != 8)
296
0
        return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
297
298
0
    if (L_ABS(angle) < MinAngleToRotate)
299
0
        return pixClone(pixs);
300
301
0
    pixGetDimensions(pixs, &w, &h, NULL);
302
0
    datas = pixGetData(pixs);
303
0
    wpls = pixGetWpl(pixs);
304
0
    pixd = pixCreateTemplate(pixs);
305
0
    datad = pixGetData(pixd);
306
0
    wpld = pixGetWpl(pixd);
307
308
0
    rotateAMGrayLow(datad, w, h, wpld, datas, wpls, angle, grayval);
309
310
0
    return pixd;
311
0
}
312
313
314
static void
315
rotateAMColorLow(l_uint32  *datad,
316
                 l_int32    w,
317
                 l_int32    h,
318
                 l_int32    wpld,
319
                 l_uint32  *datas,
320
                 l_int32    wpls,
321
                 l_float32  angle,
322
                 l_uint32   colorval)
323
0
{
324
0
l_int32    i, j, xcen, ycen, wm2, hm2;
325
0
l_int32    xdif, ydif, xpm, ypm, xp, yp, xf, yf;
326
0
l_int32    rval, gval, bval;
327
0
l_uint32   word00, word01, word10, word11;
328
0
l_uint32  *lines, *lined;
329
0
l_float32  sina, cosa;
330
331
0
    xcen = w / 2;
332
0
    wm2 = w - 2;
333
0
    ycen = h / 2;
334
0
    hm2 = h - 2;
335
0
    sina = 16.f * sin(angle);
336
0
    cosa = 16.f * cos(angle);
337
338
0
    for (i = 0; i < h; i++) {
339
0
        ydif = ycen - i;
340
0
        lined = datad + i * wpld;
341
0
        for (j = 0; j < w; j++) {
342
0
            xdif = xcen - j;
343
0
            xpm = (l_int32)(-xdif * cosa - ydif * sina);
344
0
            ypm = (l_int32)(-ydif * cosa + xdif * sina);
345
0
            xp = xcen + (xpm >> 4);
346
0
            yp = ycen + (ypm >> 4);
347
0
            xf = xpm & 0x0f;
348
0
            yf = ypm & 0x0f;
349
350
                /* if off the edge, write input colorval */
351
0
            if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
352
0
                *(lined + j) = colorval;
353
0
                continue;
354
0
            }
355
356
0
            lines = datas + yp * wpls;
357
358
                /* do area weighting.  Without this, we would
359
                 * simply do:
360
                 *   *(lined + j) = *(lines + xp);
361
                 * which is faster but gives lousy results!
362
                 */
363
0
            word00 = *(lines + xp);
364
0
            word10 = *(lines + xp + 1);
365
0
            word01 = *(lines + wpls + xp);
366
0
            word11 = *(lines + wpls + xp + 1);
367
0
            rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) +
368
0
                    xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) +
369
0
                    (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) +
370
0
                    xf * yf * ((word11 >> L_RED_SHIFT) & 0xff) + 128) / 256;
371
0
            gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) +
372
0
                    xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) +
373
0
                    (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) +
374
0
                    xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff) + 128) / 256;
375
0
            bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) +
376
0
                    xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) +
377
0
                    (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) +
378
0
                    xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff) + 128) / 256;
379
0
            composeRGBPixel(rval, gval, bval, lined + j);
380
0
        }
381
0
    }
382
0
}
383
384
385
static void
386
rotateAMGrayLow(l_uint32  *datad,
387
                l_int32    w,
388
                l_int32    h,
389
                l_int32    wpld,
390
                l_uint32  *datas,
391
                l_int32    wpls,
392
                l_float32  angle,
393
                l_uint8    grayval)
394
0
{
395
0
l_int32    i, j, xcen, ycen, wm2, hm2;
396
0
l_int32    xdif, ydif, xpm, ypm, xp, yp, xf, yf;
397
0
l_int32    v00, v01, v10, v11;
398
0
l_uint8    val;
399
0
l_uint32  *lines, *lined;
400
0
l_float32  sina, cosa;
401
402
0
    xcen = w / 2;
403
0
    wm2 = w - 2;
404
0
    ycen = h / 2;
405
0
    hm2 = h - 2;
406
0
    sina = 16.f * sin(angle);
407
0
    cosa = 16.f * cos(angle);
408
409
0
    for (i = 0; i < h; i++) {
410
0
        ydif = ycen - i;
411
0
        lined = datad + i * wpld;
412
0
        for (j = 0; j < w; j++) {
413
0
            xdif = xcen - j;
414
0
            xpm = (l_int32)(-xdif * cosa - ydif * sina);
415
0
            ypm = (l_int32)(-ydif * cosa + xdif * sina);
416
0
            xp = xcen + (xpm >> 4);
417
0
            yp = ycen + (ypm >> 4);
418
0
            xf = xpm & 0x0f;
419
0
            yf = ypm & 0x0f;
420
421
                /* if off the edge, write input grayval */
422
0
            if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
423
0
                SET_DATA_BYTE(lined, j, grayval);
424
0
                continue;
425
0
            }
426
427
0
            lines = datas + yp * wpls;
428
429
                /* do area weighting.  Without this, we would
430
                 * simply do:
431
                 *   SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp));
432
                 * which is faster but gives lousy results!
433
                 */
434
0
            v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp);
435
0
            v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp + 1);
436
0
            v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp);
437
0
            v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp + 1);
438
0
            val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256);
439
0
            SET_DATA_BYTE(lined, j, val);
440
0
        }
441
0
    }
442
0
}
443
444
445
/*------------------------------------------------------------------*
446
 *                    Rotation about the UL corner                  *
447
 *------------------------------------------------------------------*/
448
/*!
449
 * \brief   pixRotateAMCorner()
450
 *
451
 * \param[in]    pixs 1, 2, 4, 8 bpp gray or colormapped, or 32 bpp RGB
452
 * \param[in]    angle radians; clockwise is positive
453
 * \param[in]    incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
454
 * \return  pixd, or NULL on error
455
 *
456
 * <pre>
457
 * Notes:
458
 *      (1) Rotates about the UL corner of the image.
459
 *      (2) A positive angle gives a clockwise rotation.
460
 *      (3) Brings in either black or white pixels from the boundary.
461
 * </pre>
462
 */
463
PIX *
464
pixRotateAMCorner(PIX       *pixs,
465
                  l_float32  angle,
466
                  l_int32    incolor)
467
0
{
468
0
l_int32   d;
469
0
l_uint32  fillval;
470
0
PIX      *pixt1, *pixt2, *pixd;
471
472
0
    if (!pixs)
473
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
474
475
0
    if (L_ABS(angle) < MinAngleToRotate)
476
0
        return pixClone(pixs);
477
478
        /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
479
0
    pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
480
0
    d = pixGetDepth(pixt1);
481
0
    if (d < 8)
482
0
        pixt2 = pixConvertTo8(pixt1, FALSE);
483
0
    else
484
0
        pixt2 = pixClone(pixt1);
485
0
    d = pixGetDepth(pixt2);
486
487
        /* Compute actual incoming color */
488
0
    fillval = 0;
489
0
    if (incolor == L_BRING_IN_WHITE) {
490
0
        if (d == 8)
491
0
            fillval = 255;
492
0
        else  /* d == 32 */
493
0
            fillval = 0xffffff00;
494
0
    }
495
496
0
    if (d == 8)
497
0
        pixd = pixRotateAMGrayCorner(pixt2, angle, fillval);
498
0
    else   /* d == 32 */
499
0
        pixd = pixRotateAMColorCorner(pixt2, angle, fillval);
500
501
0
    pixDestroy(&pixt1);
502
0
    pixDestroy(&pixt2);
503
0
    return pixd;
504
0
}
505
506
507
/*!
508
 * \brief   pixRotateAMColorCorner()
509
 *
510
 * \param[in]    pixs
511
 * \param[in]    angle radians; clockwise is positive
512
 * \param[in]    fillval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
513
 * \return  pixd, or NULL on error
514
 *
515
 * <pre>
516
 * Notes:
517
 *      (1) Rotates the image about the UL corner.
518
 *      (2) A positive angle gives a clockwise rotation.
519
 *      (3) Specify the color to be brought in from outside the image.
520
 * </pre>
521
 */
522
PIX *
523
pixRotateAMColorCorner(PIX       *pixs,
524
                       l_float32  angle,
525
                       l_uint32   fillval)
526
0
{
527
0
l_int32    w, h, wpls, wpld;
528
0
l_uint32  *datas, *datad;
529
0
PIX       *pix1, *pix2, *pixd;
530
531
0
    if (!pixs)
532
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
533
0
    if (pixGetDepth(pixs) != 32)
534
0
        return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
535
536
0
    if (L_ABS(angle) < MinAngleToRotate)
537
0
        return pixClone(pixs);
538
539
0
    pixGetDimensions(pixs, &w, &h, NULL);
540
0
    datas = pixGetData(pixs);
541
0
    wpls = pixGetWpl(pixs);
542
0
    pixd = pixCreateTemplate(pixs);
543
0
    datad = pixGetData(pixd);
544
0
    wpld = pixGetWpl(pixd);
545
546
0
    rotateAMColorCornerLow(datad, w, h, wpld, datas, wpls, angle, fillval);
547
0
    if (pixGetSpp(pixs) == 4) {
548
0
        pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
549
0
        pix2 = pixRotateAMGrayCorner(pix1, angle, 255);  /* bring in opaque */
550
0
        pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
551
0
        pixDestroy(&pix1);
552
0
        pixDestroy(&pix2);
553
0
    }
554
555
0
    return pixd;
556
0
}
557
558
559
/*!
560
 * \brief   pixRotateAMGrayCorner()
561
 *
562
 * \param[in]    pixs
563
 * \param[in]    angle radians; clockwise is positive
564
 * \param[in]    grayval 0 to bring in BLACK, 255 for WHITE
565
 * \return  pixd, or NULL on error
566
 *
567
 * <pre>
568
 * Notes:
569
 *      (1) Rotates the image about the UL corner.
570
 *      (2) A positive angle gives a clockwise rotation.
571
 *      (3) Specify the grayvalue to be brought in from outside the image.
572
 * </pre>
573
 */
574
PIX *
575
pixRotateAMGrayCorner(PIX       *pixs,
576
                      l_float32  angle,
577
                      l_uint8    grayval)
578
0
{
579
0
l_int32    w, h, wpls, wpld;
580
0
l_uint32  *datas, *datad;
581
0
PIX       *pixd;
582
583
0
    if (!pixs)
584
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
585
0
    if (pixGetDepth(pixs) != 8)
586
0
        return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
587
588
0
    if (L_ABS(angle) < MinAngleToRotate)
589
0
        return pixClone(pixs);
590
591
0
    pixGetDimensions(pixs, &w, &h, NULL);
592
0
    datas = pixGetData(pixs);
593
0
    wpls = pixGetWpl(pixs);
594
0
    pixd = pixCreateTemplate(pixs);
595
0
    datad = pixGetData(pixd);
596
0
    wpld = pixGetWpl(pixd);
597
598
0
    rotateAMGrayCornerLow(datad, w, h, wpld, datas, wpls, angle, grayval);
599
600
0
    return pixd;
601
0
}
602
603
604
static void
605
rotateAMColorCornerLow(l_uint32  *datad,
606
                       l_int32    w,
607
                       l_int32    h,
608
                       l_int32    wpld,
609
                       l_uint32  *datas,
610
                       l_int32    wpls,
611
                       l_float32  angle,
612
                       l_uint32   colorval)
613
0
{
614
0
l_int32    i, j, wm2, hm2;
615
0
l_int32    xpm, ypm, xp, yp, xf, yf;
616
0
l_int32    rval, gval, bval;
617
0
l_uint32   word00, word01, word10, word11;
618
0
l_uint32  *lines, *lined;
619
0
l_float32  sina, cosa;
620
621
0
    wm2 = w - 2;
622
0
    hm2 = h - 2;
623
0
    sina = 16.f * sin(angle);
624
0
    cosa = 16.f * cos(angle);
625
626
0
    for (i = 0; i < h; i++) {
627
0
        lined = datad + i * wpld;
628
0
        for (j = 0; j < w; j++) {
629
0
            xpm = (l_int32)(j * cosa + i * sina);
630
0
            ypm = (l_int32)(i * cosa - j * sina);
631
0
            xp = xpm >> 4;
632
0
            yp = ypm >> 4;
633
0
            xf = xpm & 0x0f;
634
0
            yf = ypm & 0x0f;
635
636
                /* if off the edge, write input colorval */
637
0
            if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
638
0
                *(lined + j) = colorval;
639
0
                continue;
640
0
            }
641
642
0
            lines = datas + yp * wpls;
643
644
                /* do area weighting.  Without this, we would
645
                 * simply do:
646
                 *   *(lined + j) = *(lines + xp);
647
                 * which is faster but gives lousy results!
648
                 */
649
0
            word00 = *(lines + xp);
650
0
            word10 = *(lines + xp + 1);
651
0
            word01 = *(lines + wpls + xp);
652
0
            word11 = *(lines + wpls + xp + 1);
653
0
            rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) +
654
0
                    xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) +
655
0
                    (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) +
656
0
                    xf * yf * ((word11 >> L_RED_SHIFT) & 0xff) + 128) / 256;
657
0
            gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) +
658
0
                    xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) +
659
0
                    (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) +
660
0
                    xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff) + 128) / 256;
661
0
            bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) +
662
0
                    xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) +
663
0
                    (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) +
664
0
                    xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff) + 128) / 256;
665
0
            composeRGBPixel(rval, gval, bval, lined + j);
666
0
        }
667
0
    }
668
0
}
669
670
671
static void
672
rotateAMGrayCornerLow(l_uint32  *datad,
673
                      l_int32    w,
674
                      l_int32    h,
675
                      l_int32    wpld,
676
                      l_uint32  *datas,
677
                      l_int32    wpls,
678
                      l_float32  angle,
679
                      l_uint8    grayval)
680
0
{
681
0
l_int32    i, j, wm2, hm2;
682
0
l_int32    xpm, ypm, xp, yp, xf, yf;
683
0
l_int32    v00, v01, v10, v11;
684
0
l_uint8    val;
685
0
l_uint32  *lines, *lined;
686
0
l_float32  sina, cosa;
687
688
0
    wm2 = w - 2;
689
0
    hm2 = h - 2;
690
0
    sina = 16.f * sin(angle);
691
0
    cosa = 16.f * cos(angle);
692
693
0
    for (i = 0; i < h; i++) {
694
0
        lined = datad + i * wpld;
695
0
        for (j = 0; j < w; j++) {
696
0
            xpm = (l_int32)(j * cosa + i * sina);
697
0
            ypm = (l_int32)(i * cosa - j * sina);
698
0
            xp = xpm >> 4;
699
0
            yp = ypm >> 4;
700
0
            xf = xpm & 0x0f;
701
0
            yf = ypm & 0x0f;
702
703
                /* if off the edge, write input grayval */
704
0
            if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
705
0
                SET_DATA_BYTE(lined, j, grayval);
706
0
                continue;
707
0
            }
708
709
0
            lines = datas + yp * wpls;
710
711
                /* do area weighting.  Without this, we would
712
                 * simply do:
713
                 *   SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp));
714
                 * which is faster but gives lousy results!
715
                 */
716
0
            v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp);
717
0
            v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp + 1);
718
0
            v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp);
719
0
            v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp + 1);
720
0
            val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256);
721
0
            SET_DATA_BYTE(lined, j, val);
722
0
        }
723
0
    }
724
0
}
725
726
727
/*------------------------------------------------------------------*
728
 *               Fast RGB color rotation about center               *
729
 *------------------------------------------------------------------*/
730
/*!
731
 * \brief   pixRotateAMColorFast()
732
 *
733
 * \param[in]    pixs
734
 * \param[in]    angle radians; clockwise is positive
735
 * \param[in]    colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
736
 * \return  pixd, or NULL on error
737
 *
738
 * <pre>
739
 * Notes:
740
 *      (1) This rotates a color image about the image center.
741
 *      (2) A positive angle gives a clockwise rotation.
742
 *      (3) It uses area mapping, dividing each pixel into
743
 *          16 subpixels.
744
 *      (4) It is about 10% to 20% faster than the more accurate linear
745
 *          interpolation function pixRotateAMColor(),
746
 *          which uses 256 subpixels.
747
 *      (5) For some reason it shifts the image center.
748
 *          No attempt is made to rotate the alpha component.
749
 * </pre>
750
 */
751
PIX *
752
pixRotateAMColorFast(PIX       *pixs,
753
                     l_float32  angle,
754
                     l_uint32   colorval)
755
0
{
756
0
l_int32    w, h, wpls, wpld;
757
0
l_uint32  *datas, *datad;
758
0
PIX       *pixd;
759
760
0
    if (!pixs)
761
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
762
0
    if (pixGetDepth(pixs) != 32)
763
0
        return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
764
765
0
    if (L_ABS(angle) < MinAngleToRotate)
766
0
        return pixClone(pixs);
767
768
0
    pixGetDimensions(pixs, &w, &h, NULL);
769
0
    datas = pixGetData(pixs);
770
0
    wpls = pixGetWpl(pixs);
771
0
    pixd = pixCreateTemplate(pixs);
772
0
    datad = pixGetData(pixd);
773
0
    wpld = pixGetWpl(pixd);
774
775
0
    rotateAMColorFastLow(datad, w, h, wpld, datas, wpls, angle, colorval);
776
0
    return pixd;
777
0
}
778
779
780
/*!
781
 * \brief   rotateAMColorFastLow()
782
 *
783
 *     This is a special simplification of area mapping with division
784
 *     of each pixel into 16 sub-pixels.  The exact coefficients that
785
 *     should be used are the same as for the 4x linear interpolation
786
 *     scaling case, and are given there.  I tried to approximate these
787
 *     as weighted coefficients with a maximum sum of 4, which
788
 *     allows us to do the arithmetic in parallel for the R, G and B
789
 *     components in a 32 bit pixel.  However, there are three reasons
790
 *     for not doing that:
791
 *        (1) the loss of accuracy in the parallel implementation
792
 *            is visually significant
793
 *        (2) the parallel implementation (described below) is slower
794
 *        (3) the parallel implementation requires allocation of
795
 *            a temporary color image
796
 *
797
 *     There are 16 cases for the choice of the subpixel, and
798
 *     for each, the mapping to the relevant source
799
 *     pixels is as follows:
800
 *
801
 *      subpixel      src pixel weights
802
 *      --------      -----------------
803
 *         0          sp1
804
 *         1          (3 * sp1 + sp2) / 4
805
 *         2          (sp1 + sp2) / 2
806
 *         3          (sp1 + 3 * sp2) / 4
807
 *         4          (3 * sp1 + sp3) / 4
808
 *         5          (9 * sp1 + 3 * sp2 + 3 * sp3 + sp4) / 16
809
 *         6          (3 * sp1 + 3 * sp2 + sp3 + sp4) / 8
810
 *         7          (3 * sp1 + 9 * sp2 + sp3 + 3 * sp4) / 16
811
 *         8          (sp1 + sp3) / 2
812
 *         9          (3 * sp1 + sp2 + 3 * sp3 + sp4) / 8
813
 *         10         (sp1 + sp2 + sp3 + sp4) / 4
814
 *         11         (sp1 + 3 * sp2 + sp3 + 3 * sp4) / 8
815
 *         12         (sp1 + 3 * sp3) / 4
816
 *         13         (3 * sp1 + sp2 + 9 * sp3 + 3 * sp4) / 16
817
 *         14         (sp1 + sp2 + 3 * sp3 + 3 * sp4) / 8
818
 *         15         (sp1 + 3 * sp2 + 3 * sp3 + 9 * sp4) / 16
819
 *
820
 *     Another way to visualize this is to consider the area mapping
821
 *     (or linear interpolation) coefficients  for the pixel sp1.
822
 *     Expressed in fourths, they can be written as asymmetric matrix:
823
 *
824
 *           4      3      2      1
825
 *           3      2.25   1.5    0.75
826
 *           2      1.5    1      0.5
827
 *           1      0.75   0.5    0.25
828
 *
829
 *     The coefficients for the three neighboring pixels can be
830
 *     similarly written.
831
 *
832
 *     This is implemented here, where, for each color component,
833
 *     we inline its extraction from each participating word,
834
 *     construct the linear combination, and combine the results
835
 *     into the destination 32 bit RGB pixel, using the appropriate shifts.
836
 *
837
 *     It is interesting to note that an alternative method, where
838
 *     we do the arithmetic on the 32 bit pixels directly (after
839
 *     shifting the components so they won't overflow into each other)
840
 *     is significantly inferior.  Because we have only 8 bits for
841
 *     internal overflows, which can be distributed as 2, 3, 3, it
842
 *     is impossible to add these with the correct linear
843
 *     interpolation coefficients, which require a sum of up to 16.
844
 *     Rounding off to a sum of 4 causes appreciable visual artifacts
845
 *     in the rotated image.  The code for the inferior method
846
 *     can be found in prog/rotatefastalt.c, for reference.
847
 */
848
static void
849
rotateAMColorFastLow(l_uint32  *datad,
850
                     l_int32    w,
851
                     l_int32    h,
852
                     l_int32    wpld,
853
                     l_uint32  *datas,
854
                     l_int32    wpls,
855
                     l_float32  angle,
856
                     l_uint32   colorval)
857
0
{
858
0
l_int32    i, j, xcen, ycen, wm2, hm2;
859
0
l_int32    xdif, ydif, xpm, ypm, xp, yp, xf, yf;
860
0
l_uint32   word1, word2, word3, word4, red, blue, green;
861
0
l_uint32  *pword, *lines, *lined;
862
0
l_float32  sina, cosa;
863
864
0
    xcen = w / 2;
865
0
    wm2 = w - 2;
866
0
    ycen = h / 2;
867
0
    hm2 = h - 2;
868
0
    sina = 4.f * sin(angle);
869
0
    cosa = 4.f * cos(angle);
870
871
0
    for (i = 0; i < h; i++) {
872
0
        ydif = ycen - i;
873
0
        lined = datad + i * wpld;
874
0
        for (j = 0; j < w; j++) {
875
0
            xdif = xcen - j;
876
0
            xpm = (l_int32)(-xdif * cosa - ydif * sina);
877
0
            ypm = (l_int32)(-ydif * cosa + xdif * sina);
878
0
            xp = xcen + (xpm >> 2);
879
0
            yp = ycen + (ypm >> 2);
880
0
            xf = xpm & 0x03;
881
0
            yf = ypm & 0x03;
882
883
                /* if off the edge, write input grayval */
884
0
            if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
885
0
                *(lined + j) = colorval;
886
0
                continue;
887
0
            }
888
889
0
            lines = datas + yp * wpls;
890
0
            pword = lines + xp;
891
892
0
            switch (xf + 4 * yf)
893
0
            {
894
0
            case 0:
895
0
                *(lined + j) = *pword;
896
0
                break;
897
0
            case 1:
898
0
                word1 = *pword;
899
0
                word2 = *(pword + 1);
900
0
                red = 3 * (word1 >> 24) + (word2 >> 24);
901
0
                green = 3 * ((word1 >> 16) & 0xff) +
902
0
                            ((word2 >> 16) & 0xff);
903
0
                blue = 3 * ((word1 >> 8) & 0xff) +
904
0
                            ((word2 >> 8) & 0xff);
905
0
                *(lined + j) = ((red << 22) & 0xff000000) |
906
0
                               ((green << 14) & 0x00ff0000) |
907
0
                               ((blue << 6) & 0x0000ff00);
908
0
                break;
909
0
            case 2:
910
0
                word1 = *pword;
911
0
                word2 = *(pword + 1);
912
0
                red = (word1 >> 24) + (word2 >> 24);
913
0
                green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff);
914
0
                blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff);
915
0
                *(lined + j) = ((red << 23) & 0xff000000) |
916
0
                               ((green << 15) & 0x00ff0000) |
917
0
                               ((blue << 7) & 0x0000ff00);
918
0
                break;
919
0
            case 3:
920
0
                word1 = *pword;
921
0
                word2 = *(pword + 1);
922
0
                red = (word1 >> 24) + 3 * (word2 >> 24);
923
0
                green = ((word1 >> 16) & 0xff) +
924
0
                          3 * ((word2 >> 16) & 0xff);
925
0
                blue = ((word1 >> 8) & 0xff) +
926
0
                          3 * ((word2 >> 8) & 0xff);
927
0
                *(lined + j) = ((red << 22) & 0xff000000) |
928
0
                               ((green << 14) & 0x00ff0000) |
929
0
                               ((blue << 6) & 0x0000ff00);
930
0
                break;
931
0
            case 4:
932
0
                word1 = *pword;
933
0
                word3 = *(pword + wpls);
934
0
                red = 3 * (word1 >> 24) + (word3 >> 24);
935
0
                green = 3 * ((word1 >> 16) & 0xff) +
936
0
                            ((word3 >> 16) & 0xff);
937
0
                blue = 3 * ((word1 >> 8) & 0xff) +
938
0
                            ((word3 >> 8) & 0xff);
939
0
                *(lined + j) = ((red << 22) & 0xff000000) |
940
0
                               ((green << 14) & 0x00ff0000) |
941
0
                               ((blue << 6) & 0x0000ff00);
942
0
                break;
943
0
            case 5:
944
0
                word1 = *pword;
945
0
                word2 = *(pword + 1);
946
0
                word3 = *(pword + wpls);
947
0
                word4 = *(pword + wpls + 1);
948
0
                red = 9 * (word1 >> 24) + 3 * (word2 >> 24) +
949
0
                      3 * (word3 >> 24) + (word4 >> 24);
950
0
                green = 9 * ((word1 >> 16) & 0xff) +
951
0
                        3 * ((word2 >> 16) & 0xff) +
952
0
                        3 * ((word3 >> 16) & 0xff) +
953
0
                        ((word4 >> 16) & 0xff);
954
0
                blue = 9 * ((word1 >> 8) & 0xff) +
955
0
                       3 * ((word2 >> 8) & 0xff) +
956
0
                       3 * ((word3 >> 8) & 0xff) +
957
0
                       ((word4 >> 8) & 0xff);
958
0
                *(lined + j) = ((red << 20) & 0xff000000) |
959
0
                               ((green << 12) & 0x00ff0000) |
960
0
                               ((blue << 4) & 0x0000ff00);
961
0
                break;
962
0
            case 6:
963
0
                word1 = *pword;
964
0
                word2 = *(pword + 1);
965
0
                word3 = *(pword + wpls);
966
0
                word4 = *(pword + wpls + 1);
967
0
                red = 3 * (word1 >> 24) +  3 * (word2 >> 24) +
968
0
                      (word3 >> 24) + (word4 >> 24);
969
0
                green = 3 * ((word1 >> 16) & 0xff) +
970
0
                        3 * ((word2 >> 16) & 0xff) +
971
0
                        ((word3 >> 16) & 0xff) +
972
0
                        ((word4 >> 16) & 0xff);
973
0
                blue = 3 * ((word1 >> 8) & 0xff) +
974
0
                       3 * ((word2 >> 8) & 0xff) +
975
0
                       ((word3 >> 8) & 0xff) +
976
0
                       ((word4 >> 8) & 0xff);
977
0
                *(lined + j) = ((red << 21) & 0xff000000) |
978
0
                               ((green << 13) & 0x00ff0000) |
979
0
                               ((blue << 5) & 0x0000ff00);
980
0
                break;
981
0
            case 7:
982
0
                word1 = *pword;
983
0
                word2 = *(pword + 1);
984
0
                word3 = *(pword + wpls);
985
0
                word4 = *(pword + wpls + 1);
986
0
                red = 3 * (word1 >> 24) + 9 * (word2 >> 24) +
987
0
                      (word3 >> 24) + 3 * (word4 >> 24);
988
0
                green = 3 * ((word1 >> 16) & 0xff) +
989
0
                        9 * ((word2 >> 16) & 0xff) +
990
0
                        ((word3 >> 16) & 0xff) +
991
0
                        3 * ((word4 >> 16) & 0xff);
992
0
                blue = 3 * ((word1 >> 8) & 0xff) +
993
0
                       9 * ((word2 >> 8) & 0xff) +
994
0
                         ((word3 >> 8) & 0xff) +
995
0
                         3 * ((word4 >> 8) & 0xff);
996
0
                *(lined + j) = ((red << 20) & 0xff000000) |
997
0
                               ((green << 12) & 0x00ff0000) |
998
0
                               ((blue << 4) & 0x0000ff00);
999
0
                break;
1000
0
            case 8:
1001
0
                word1 = *pword;
1002
0
                word3 = *(pword + wpls);
1003
0
                red = (word1 >> 24) + (word3 >> 24);
1004
0
                green = ((word1 >> 16) & 0xff) + ((word3 >> 16) & 0xff);
1005
0
                blue = ((word1 >> 8) & 0xff) + ((word3 >> 8) & 0xff);
1006
0
                *(lined + j) = ((red << 23) & 0xff000000) |
1007
0
                               ((green << 15) & 0x00ff0000) |
1008
0
                               ((blue << 7) & 0x0000ff00);
1009
0
                break;
1010
0
            case 9:
1011
0
                word1 = *pword;
1012
0
                word2 = *(pword + 1);
1013
0
                word3 = *(pword + wpls);
1014
0
                word4 = *(pword + wpls + 1);
1015
0
                red = 3 * (word1 >> 24) + (word2 >> 24) +
1016
0
                      3 * (word3 >> 24) + (word4 >> 24);
1017
0
                green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) +
1018
0
                        3 * ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff);
1019
0
                blue = 3 * ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1020
0
                       3 * ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff);
1021
0
                *(lined + j) = ((red << 21) & 0xff000000) |
1022
0
                               ((green << 13) & 0x00ff0000) |
1023
0
                               ((blue << 5) & 0x0000ff00);
1024
0
                break;
1025
0
            case 10:
1026
0
                word1 = *pword;
1027
0
                word2 = *(pword + 1);
1028
0
                word3 = *(pword + wpls);
1029
0
                word4 = *(pword + wpls + 1);
1030
0
                red = (word1 >> 24) + (word2 >> 24) +
1031
0
                      (word3 >> 24) + (word4 >> 24);
1032
0
                green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) +
1033
0
                        ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff);
1034
0
                blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1035
0
                       ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff);
1036
0
                *(lined + j) = ((red << 22) & 0xff000000) |
1037
0
                               ((green << 14) & 0x00ff0000) |
1038
0
                               ((blue << 6) & 0x0000ff00);
1039
0
                break;
1040
0
            case 11:
1041
0
                word1 = *pword;
1042
0
                word2 = *(pword + 1);
1043
0
                word3 = *(pword + wpls);
1044
0
                word4 = *(pword + wpls + 1);
1045
0
                red = (word1 >> 24) + 3 * (word2 >> 24) +
1046
0
                      (word3 >> 24) + 3 * (word4 >> 24);
1047
0
                green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) +
1048
0
                        ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff);
1049
0
                blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) +
1050
0
                       ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff);
1051
0
                *(lined + j) = ((red << 21) & 0xff000000) |
1052
0
                               ((green << 13) & 0x00ff0000) |
1053
0
                               ((blue << 5) & 0x0000ff00);
1054
0
                break;
1055
0
            case 12:
1056
0
                word1 = *pword;
1057
0
                word3 = *(pword + wpls);
1058
0
                red = (word1 >> 24) + 3 * (word3 >> 24);
1059
0
                green = ((word1 >> 16) & 0xff) +
1060
0
                          3 * ((word3 >> 16) & 0xff);
1061
0
                blue = ((word1 >> 8) & 0xff) +
1062
0
                          3 * ((word3 >> 8) & 0xff);
1063
0
                *(lined + j) = ((red << 22) & 0xff000000) |
1064
0
                               ((green << 14) & 0x00ff0000) |
1065
0
                               ((blue << 6) & 0x0000ff00);
1066
0
                break;
1067
0
            case 13:
1068
0
                word1 = *pword;
1069
0
                word2 = *(pword + 1);
1070
0
                word3 = *(pword + wpls);
1071
0
                word4 = *(pword + wpls + 1);
1072
0
                red = 3 * (word1 >> 24) + (word2 >> 24) +
1073
0
                      9 * (word3 >> 24) + 3 * (word4 >> 24);
1074
0
                green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) +
1075
0
                        9 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff);
1076
0
                blue = 3 *((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1077
0
                       9 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff);
1078
0
                *(lined + j) = ((red << 20) & 0xff000000) |
1079
0
                               ((green << 12) & 0x00ff0000) |
1080
0
                               ((blue << 4) & 0x0000ff00);
1081
0
                break;
1082
0
            case 14:
1083
0
                word1 = *pword;
1084
0
                word2 = *(pword + 1);
1085
0
                word3 = *(pword + wpls);
1086
0
                word4 = *(pword + wpls + 1);
1087
0
                red = (word1 >> 24) + (word2 >> 24) +
1088
0
                      3 * (word3 >> 24) + 3 * (word4 >> 24);
1089
0
                green = ((word1 >> 16) & 0xff) +((word2 >> 16) & 0xff) +
1090
0
                        3 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff);
1091
0
                blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1092
0
                       3 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff);
1093
0
                *(lined + j) = ((red << 21) & 0xff000000) |
1094
0
                               ((green << 13) & 0x00ff0000) |
1095
0
                               ((blue << 5) & 0x0000ff00);
1096
0
                break;
1097
0
            case 15:
1098
0
                word1 = *pword;
1099
0
                word2 = *(pword + 1);
1100
0
                word3 = *(pword + wpls);
1101
0
                word4 = *(pword + wpls + 1);
1102
0
                red = (word1 >> 24) + 3 * (word2 >> 24) +
1103
0
                      3 * (word3 >> 24) + 9 * (word4 >> 24);
1104
0
                green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) +
1105
0
                        3 * ((word3 >> 16) & 0xff) + 9 * ((word4 >> 16) & 0xff);
1106
0
                blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) +
1107
0
                       3 * ((word3 >> 8) & 0xff) + 9 * ((word4 >> 8) & 0xff);
1108
0
                *(lined + j) = ((red << 20) & 0xff000000) |
1109
0
                               ((green << 12) & 0x00ff0000) |
1110
0
                               ((blue << 4) & 0x0000ff00);
1111
0
                break;
1112
0
            default:
1113
0
                lept_stderr("shouldn't get here\n");
1114
0
                break;
1115
0
            }
1116
0
        }
1117
0
    }
1118
0
}