/src/leptonica/src/scale2.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 scale2.c |
29 | | * <pre> |
30 | | * Scale-to-gray (1 bpp --> 8 bpp; arbitrary downscaling) |
31 | | * PIX *pixScaleToGray() |
32 | | * PIX *pixScaleToGrayFast() |
33 | | * |
34 | | * Scale-to-gray (1 bpp --> 8 bpp; integer downscaling) |
35 | | * PIX *pixScaleToGray2() |
36 | | * PIX *pixScaleToGray3() |
37 | | * PIX *pixScaleToGray4() |
38 | | * PIX *pixScaleToGray6() |
39 | | * PIX *pixScaleToGray8() |
40 | | * PIX *pixScaleToGray16() |
41 | | * |
42 | | * Scale-to-gray by mipmap(1 bpp --> 8 bpp, arbitrary reduction) |
43 | | * PIX *pixScaleToGrayMipmap() |
44 | | * |
45 | | * Grayscale scaling using mipmap |
46 | | * PIX *pixScaleMipmap() |
47 | | * |
48 | | * Replicated (integer) expansion (all depths) |
49 | | * PIX *pixExpandReplicate() |
50 | | * |
51 | | * Grayscale downscaling using min and max |
52 | | * PIX *pixScaleGrayMinMax() |
53 | | * PIX *pixScaleGrayMinMax2() |
54 | | * |
55 | | * Grayscale downscaling using rank value |
56 | | * PIX *pixScaleGrayRankCascade() |
57 | | * PIX *pixScaleGrayRank2() |
58 | | * |
59 | | * Helper function for transferring alpha with scaling |
60 | | * l_int32 pixScaleAndTransferAlpha() |
61 | | * |
62 | | * RGB scaling including alpha (blend) component |
63 | | * PIX *pixScaleWithAlpha() |
64 | | * |
65 | | * Low-level static functions: |
66 | | * |
67 | | * Scale-to-gray 2x |
68 | | * static void scaleToGray2Low() |
69 | | * static l_uint32 *makeSumTabSG2() |
70 | | * static l_uint8 *makeValTabSG2() |
71 | | * |
72 | | * Scale-to-gray 3x |
73 | | * static void scaleToGray3Low() |
74 | | * static l_uint32 *makeSumTabSG3() |
75 | | * static l_uint8 *makeValTabSG3() |
76 | | * |
77 | | * Scale-to-gray 4x |
78 | | * static void scaleToGray4Low() |
79 | | * static l_uint32 *makeSumTabSG4() |
80 | | * static l_uint8 *makeValTabSG4() |
81 | | * |
82 | | * Scale-to-gray 6x |
83 | | * static void scaleToGray6Low() |
84 | | * static l_uint8 *makeValTabSG6() |
85 | | * |
86 | | * Scale-to-gray 8x |
87 | | * static void scaleToGray8Low() |
88 | | * static l_uint8 *makeValTabSG8() |
89 | | * |
90 | | * Scale-to-gray 16x |
91 | | * static void scaleToGray16Low() |
92 | | * |
93 | | * Grayscale mipmap |
94 | | * static l_int32 scaleMipmapLow() |
95 | | * </pre> |
96 | | */ |
97 | | |
98 | | #ifdef HAVE_CONFIG_H |
99 | | #include <config_auto.h> |
100 | | #endif /* HAVE_CONFIG_H */ |
101 | | |
102 | | #include <string.h> |
103 | | #include "allheaders.h" |
104 | | |
105 | | static void scaleToGray2Low(l_uint32 *datad, l_int32 wd, l_int32 hd, |
106 | | l_int32 wpld, l_uint32 *datas, l_int32 wpls, |
107 | | l_uint32 *sumtab, l_uint8 *valtab); |
108 | | static l_uint32 *makeSumTabSG2(void); |
109 | | static l_uint8 *makeValTabSG2(void); |
110 | | static void scaleToGray3Low(l_uint32 *datad, l_int32 wd, l_int32 hd, |
111 | | l_int32 wpld, l_uint32 *datas, l_int32 wpls, |
112 | | l_uint32 *sumtab, l_uint8 *valtab); |
113 | | static l_uint32 *makeSumTabSG3(void); |
114 | | static l_uint8 *makeValTabSG3(void); |
115 | | static void scaleToGray4Low(l_uint32 *datad, l_int32 wd, l_int32 hd, |
116 | | l_int32 wpld, l_uint32 *datas, l_int32 wpls, |
117 | | l_uint32 *sumtab, l_uint8 *valtab); |
118 | | static l_uint32 *makeSumTabSG4(void); |
119 | | static l_uint8 *makeValTabSG4(void); |
120 | | static void scaleToGray6Low(l_uint32 *datad, l_int32 wd, l_int32 hd, |
121 | | l_int32 wpld, l_uint32 *datas, l_int32 wpls, |
122 | | l_int32 *tab8, l_uint8 *valtab); |
123 | | static l_uint8 *makeValTabSG6(void); |
124 | | static void scaleToGray8Low(l_uint32 *datad, l_int32 wd, l_int32 hd, |
125 | | l_int32 wpld, l_uint32 *datas, l_int32 wpls, |
126 | | l_int32 *tab8, l_uint8 *valtab); |
127 | | static l_uint8 *makeValTabSG8(void); |
128 | | static void scaleToGray16Low(l_uint32 *datad, l_int32 wd, l_int32 hd, |
129 | | l_int32 wpld, l_uint32 *datas, l_int32 wpls, |
130 | | l_int32 *tab8); |
131 | | static l_int32 scaleMipmapLow(l_uint32 *datad, l_int32 wd, l_int32 hd, |
132 | | l_int32 wpld, l_uint32 *datas1, l_int32 wpls1, |
133 | | l_uint32 *datas2, l_int32 wpls2, l_float32 red); |
134 | | |
135 | | extern l_float32 AlphaMaskBorderVals[2]; |
136 | | |
137 | | |
138 | | /*------------------------------------------------------------------* |
139 | | * Scale-to-gray (1 bpp --> 8 bpp; arbitrary downscaling) * |
140 | | *------------------------------------------------------------------*/ |
141 | | /*! |
142 | | * \brief pixScaleToGray() |
143 | | * |
144 | | * \param[in] pixs 1 bpp |
145 | | * \param[in] scalefactor reduction: must be > 0.0 and < 1.0 |
146 | | * \return pixd 8 bpp, scaled down by scalefactor in each direction, |
147 | | * or NULL on error. |
148 | | * |
149 | | * <pre> |
150 | | * Notes: |
151 | | * |
152 | | * For faster scaling in the range of scalefactors from 0.0625 to 0.5, |
153 | | * with very little difference in quality, use pixScaleToGrayFast(). |
154 | | * |
155 | | * Binary images have sharp edges, so they intrinsically have very |
156 | | * high frequency content. To avoid aliasing, they must be low-pass |
157 | | * filtered, which tends to blur the edges. How can we keep relatively |
158 | | * crisp edges without aliasing? The trick is to do binary upscaling |
159 | | * followed by a power-of-2 scaleToGray. For large reductions, where |
160 | | * you don't end up with much detail, some corners can be cut. |
161 | | * |
162 | | * The intent here is to get high quality reduced grayscale |
163 | | * images with relatively little computation. We do binary |
164 | | * pre-scaling followed by scaleToGrayN() for best results, |
165 | | * esp. to avoid excess blur when the scale factor is near |
166 | | * an inverse power of 2. Where a low-pass filter is required, |
167 | | * we use simple convolution kernels: either the hat filter for |
168 | | * linear interpolation or a flat filter for larger downscaling. |
169 | | * Other choices, such as a perfect bandpass filter with infinite extent |
170 | | * (the sinc) or various approximations to it (e.g., lanczos), are |
171 | | * unnecessarily expensive. |
172 | | * |
173 | | * The choices made are as follows: |
174 | | * (1) Do binary upscaling before scaleToGrayN() for scalefactors > 1/8 |
175 | | * (2) Do binary downscaling before scaleToGray8() for scalefactors |
176 | | * between 1/16 and 1/8. |
177 | | * (3) Use scaleToGray16() before grayscale downscaling for |
178 | | * scalefactors less than 1/16 |
179 | | * Another reasonable choice would be to start binary downscaling |
180 | | * for scalefactors below 1/4, rather than below 1/8 as we do here. |
181 | | * |
182 | | * The general scaling rules, not all of which are used here, go as follows: |
183 | | * (1) For grayscale upscaling, use pixScaleGrayLI(). However, |
184 | | * note that edges will be visibly blurred for scalefactors |
185 | | * near (but above) 1.0. Replication will avoid edge blur, |
186 | | * and should be considered for factors very near 1.0. |
187 | | * (2) For grayscale downscaling with a scale factor larger than |
188 | | * about 0.7, use pixScaleGrayLI(). For scalefactors near |
189 | | * (but below) 1.0, you tread between Scylla and Charybdis. |
190 | | * pixScaleGrayLI() again gives edge blurring, but |
191 | | * pixScaleBySampling() gives visible aliasing. |
192 | | * (3) For grayscale downscaling with a scale factor smaller than |
193 | | * about 0.7, use pixScaleSmooth() |
194 | | * (4) For binary input images, do as much scale to gray as possible |
195 | | * using the special integer functions (2, 3, 4, 8 and 16). |
196 | | * (5) It is better to upscale in binary, followed by scaleToGrayN() |
197 | | * than to do scaleToGrayN() followed by an upscale using either |
198 | | * LI or oversampling. |
199 | | * (6) It may be better to downscale in binary, followed by |
200 | | * scaleToGrayN() than to first use scaleToGrayN() followed by |
201 | | * downscaling. For downscaling between 8x and 16x, this is |
202 | | * a reasonable option. |
203 | | * (7) For reductions greater than 16x, it's reasonable to use |
204 | | * scaleToGray16() followed by further grayscale downscaling. |
205 | | * </pre> |
206 | | */ |
207 | | PIX * |
208 | | pixScaleToGray(PIX *pixs, |
209 | | l_float32 scalefactor) |
210 | 0 | { |
211 | 0 | l_int32 w, h, minsrc, mindest; |
212 | 0 | l_float32 mag, red; |
213 | 0 | PIX *pixt, *pixd; |
214 | |
|
215 | 0 | if (!pixs) |
216 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
217 | 0 | if (pixGetDepth(pixs) != 1) |
218 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); |
219 | 0 | if (scalefactor <= 0.0) |
220 | 0 | return (PIX *)ERROR_PTR("scalefactor <= 0.0", __func__, NULL); |
221 | 0 | if (scalefactor >= 1.0) |
222 | 0 | return (PIX *)ERROR_PTR("scalefactor >= 1.0", __func__, NULL); |
223 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
224 | 0 | minsrc = L_MIN(w, h); |
225 | 0 | mindest = (l_int32)((l_float32)minsrc * scalefactor); |
226 | 0 | if (mindest < 2) |
227 | 0 | return (PIX *)ERROR_PTR("scalefactor too small", __func__, NULL); |
228 | | |
229 | 0 | if (scalefactor > 0.5) { /* see note (5) */ |
230 | 0 | mag = 2.0f * scalefactor; /* will be < 2.0 */ |
231 | | /* lept_stderr("2x with mag %7.3f\n", mag); */ |
232 | 0 | if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
233 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
234 | 0 | pixd = pixScaleToGray2(pixt); |
235 | 0 | } else if (scalefactor == 0.5) { |
236 | 0 | return pixd = pixScaleToGray2(pixs); |
237 | 0 | } else if (scalefactor > 0.33333) { /* see note (5) */ |
238 | 0 | mag = 3.0f * scalefactor; /* will be < 1.5 */ |
239 | | /* lept_stderr("3x with mag %7.3f\n", mag); */ |
240 | 0 | if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
241 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
242 | 0 | pixd = pixScaleToGray3(pixt); |
243 | 0 | } else if (scalefactor > 0.25) { /* see note (5) */ |
244 | 0 | mag = 4.0f * scalefactor; /* will be < 1.3333 */ |
245 | | /* lept_stderr("4x with mag %7.3f\n", mag); */ |
246 | 0 | if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
247 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
248 | 0 | pixd = pixScaleToGray4(pixt); |
249 | 0 | } else if (scalefactor == 0.25) { |
250 | 0 | return pixd = pixScaleToGray4(pixs); |
251 | 0 | } else if (scalefactor > 0.16667) { /* see note (5) */ |
252 | 0 | mag = 6.0f * scalefactor; /* will be < 1.5 */ |
253 | | /* lept_stderr("6x with mag %7.3f\n", mag); */ |
254 | 0 | if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
255 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
256 | 0 | pixd = pixScaleToGray6(pixt); |
257 | 0 | } else if (scalefactor == 0.16667) { |
258 | 0 | return pixd = pixScaleToGray6(pixs); |
259 | 0 | } else if (scalefactor > 0.125) { /* see note (5) */ |
260 | 0 | mag = 8.0f * scalefactor; /* will be < 1.3333 */ |
261 | | /* lept_stderr("8x with mag %7.3f\n", mag); */ |
262 | 0 | if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
263 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
264 | 0 | pixd = pixScaleToGray8(pixt); |
265 | 0 | } else if (scalefactor == 0.125) { |
266 | 0 | return pixd = pixScaleToGray8(pixs); |
267 | 0 | } else if (scalefactor > 0.0625) { /* see note (6) */ |
268 | 0 | red = 8.0f * scalefactor; /* will be > 0.5 */ |
269 | | /* lept_stderr("8x with red %7.3f\n", red); */ |
270 | 0 | if ((pixt = pixScaleBinary(pixs, red, red)) == NULL) |
271 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
272 | 0 | pixd = pixScaleToGray8(pixt); |
273 | 0 | } else if (scalefactor == 0.0625) { |
274 | 0 | return pixd = pixScaleToGray16(pixs); |
275 | 0 | } else { /* see note (7) */ |
276 | 0 | red = 16.0f * scalefactor; /* will be <= 1.0 */ |
277 | | /* lept_stderr("16x with red %7.3f\n", red); */ |
278 | 0 | if ((pixt = pixScaleToGray16(pixs)) == NULL) |
279 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
280 | 0 | if (red < 0.7) |
281 | 0 | pixd = pixScaleSmooth(pixt, red, red); /* see note (3) */ |
282 | 0 | else |
283 | 0 | pixd = pixScaleGrayLI(pixt, red, red); /* see note (2) */ |
284 | 0 | } |
285 | | |
286 | 0 | pixDestroy(&pixt); |
287 | 0 | if (!pixd) |
288 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
289 | 0 | pixCopyInputFormat(pixd, pixs); |
290 | 0 | return pixd; |
291 | 0 | } |
292 | | |
293 | | |
294 | | /*! |
295 | | * \brief pixScaleToGrayFast() |
296 | | * |
297 | | * \param[in] pixs 1 bpp |
298 | | * \param[in] scalefactor reduction: must be > 0.0 and < 1.0 |
299 | | * \return pixd 8 bpp, scaled down by scalefactor in each direction, |
300 | | * or NULL on error. |
301 | | * |
302 | | * <pre> |
303 | | * Notes: |
304 | | * (1) See notes in pixScaleToGray() for the basic approach. |
305 | | * (2) This function is considerably less expensive than pixScaleToGray() |
306 | | * for scalefactor in the range (0.0625 ... 0.5), and the |
307 | | * quality is nearly as good. |
308 | | * (3) Unlike pixScaleToGray(), which does binary upscaling before |
309 | | * downscaling for scale factors >= 0.0625, pixScaleToGrayFast() |
310 | | * first downscales in binary for all scale factors < 0.5, and |
311 | | * then does a 2x scale-to-gray as the final step. For |
312 | | * scale factors < 0.0625, both do a 16x scale-to-gray, followed |
313 | | * by further grayscale reduction. |
314 | | * </pre> |
315 | | */ |
316 | | PIX * |
317 | | pixScaleToGrayFast(PIX *pixs, |
318 | | l_float32 scalefactor) |
319 | 0 | { |
320 | 0 | l_int32 w, h, minsrc, mindest; |
321 | 0 | l_float32 eps, factor; |
322 | 0 | PIX *pixt, *pixd; |
323 | |
|
324 | 0 | if (!pixs) |
325 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
326 | 0 | if (pixGetDepth(pixs) != 1) |
327 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); |
328 | 0 | if (scalefactor <= 0.0) |
329 | 0 | return (PIX *)ERROR_PTR("scalefactor <= 0.0", __func__, NULL); |
330 | 0 | if (scalefactor >= 1.0) |
331 | 0 | return (PIX *)ERROR_PTR("scalefactor >= 1.0", __func__, NULL); |
332 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
333 | 0 | minsrc = L_MIN(w, h); |
334 | 0 | mindest = (l_int32)((l_float32)minsrc * scalefactor); |
335 | 0 | if (mindest < 2) |
336 | 0 | return (PIX *)ERROR_PTR("scalefactor too small", __func__, NULL); |
337 | 0 | eps = 0.0001f; |
338 | | |
339 | | /* Handle the special cases */ |
340 | 0 | if (scalefactor > 0.5 - eps && scalefactor < 0.5 + eps) |
341 | 0 | return pixScaleToGray2(pixs); |
342 | 0 | else if (scalefactor > 0.33333 - eps && scalefactor < 0.33333 + eps) |
343 | 0 | return pixScaleToGray3(pixs); |
344 | 0 | else if (scalefactor > 0.25 - eps && scalefactor < 0.25 + eps) |
345 | 0 | return pixScaleToGray4(pixs); |
346 | 0 | else if (scalefactor > 0.16666 - eps && scalefactor < 0.16666 + eps) |
347 | 0 | return pixScaleToGray6(pixs); |
348 | 0 | else if (scalefactor > 0.125 - eps && scalefactor < 0.125 + eps) |
349 | 0 | return pixScaleToGray8(pixs); |
350 | 0 | else if (scalefactor > 0.0625 - eps && scalefactor < 0.0625 + eps) |
351 | 0 | return pixScaleToGray16(pixs); |
352 | | |
353 | 0 | if (scalefactor > 0.0625) { /* scale binary first */ |
354 | 0 | factor = 2.0f * scalefactor; |
355 | 0 | if ((pixt = pixScaleBinary(pixs, factor, factor)) == NULL) |
356 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
357 | 0 | pixd = pixScaleToGray2(pixt); |
358 | 0 | } else { /* scalefactor < 0.0625; scale-to-gray first */ |
359 | 0 | factor = 16.0f * scalefactor; /* will be < 1.0 */ |
360 | 0 | if ((pixt = pixScaleToGray16(pixs)) == NULL) |
361 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
362 | 0 | if (factor < 0.7) |
363 | 0 | pixd = pixScaleSmooth(pixt, factor, factor); |
364 | 0 | else |
365 | 0 | pixd = pixScaleGrayLI(pixt, factor, factor); |
366 | 0 | } |
367 | 0 | pixDestroy(&pixt); |
368 | 0 | if (!pixd) |
369 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
370 | 0 | pixCopyInputFormat(pixd, pixs); |
371 | 0 | return pixd; |
372 | 0 | } |
373 | | |
374 | | |
375 | | /*-----------------------------------------------------------------------* |
376 | | * Scale-to-gray (1 bpp --> 8 bpp; integer downscaling) * |
377 | | *-----------------------------------------------------------------------*/ |
378 | | /*! |
379 | | * \brief pixScaleToGray2() |
380 | | * |
381 | | * \param[in] pixs 1 bpp |
382 | | * \return pixd 8 bpp, scaled down by 2x in each direction, |
383 | | * or NULL on error. |
384 | | */ |
385 | | PIX * |
386 | | pixScaleToGray2(PIX *pixs) |
387 | 0 | { |
388 | 0 | l_uint8 *valtab; |
389 | 0 | l_int32 ws, hs, wd, hd; |
390 | 0 | l_int32 wpld, wpls; |
391 | 0 | l_uint32 *sumtab; |
392 | 0 | l_uint32 *datas, *datad; |
393 | 0 | PIX *pixd; |
394 | |
|
395 | 0 | if (!pixs) |
396 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
397 | 0 | if (pixGetDepth(pixs) != 1) |
398 | 0 | return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL); |
399 | | |
400 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
401 | 0 | wd = ws / 2; |
402 | 0 | hd = hs / 2; |
403 | 0 | if (wd == 0 || hd == 0) |
404 | 0 | return (PIX *)ERROR_PTR("pixs too small", __func__, NULL); |
405 | | |
406 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
407 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
408 | 0 | pixSetPadBits(pixs, 0); |
409 | 0 | pixCopyInputFormat(pixd, pixs); |
410 | 0 | pixCopyResolution(pixd, pixs); |
411 | 0 | pixScaleResolution(pixd, 0.5, 0.5); |
412 | 0 | datas = pixGetData(pixs); |
413 | 0 | datad = pixGetData(pixd); |
414 | 0 | wpls = pixGetWpl(pixs); |
415 | 0 | wpld = pixGetWpl(pixd); |
416 | |
|
417 | 0 | sumtab = makeSumTabSG2(); |
418 | 0 | valtab = makeValTabSG2(); |
419 | 0 | scaleToGray2Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); |
420 | 0 | LEPT_FREE(sumtab); |
421 | 0 | LEPT_FREE(valtab); |
422 | 0 | return pixd; |
423 | 0 | } |
424 | | |
425 | | |
426 | | /*! |
427 | | * \brief pixScaleToGray3() |
428 | | * |
429 | | * \param[in] pixs 1 bpp |
430 | | * \return pixd 8 bpp, scaled down by 3x in each direction, |
431 | | * or NULL on error. |
432 | | * |
433 | | * <pre> |
434 | | * Notes: |
435 | | * (1) Speed is about 100 x 10^6 src-pixels/sec/GHz. |
436 | | * Another way to express this is it processes 1 src pixel |
437 | | * in about 10 cycles. |
438 | | * (2) The width of pixd is truncated is truncated to a factor of 8. |
439 | | * </pre> |
440 | | */ |
441 | | PIX * |
442 | | pixScaleToGray3(PIX *pixs) |
443 | 0 | { |
444 | 0 | l_uint8 *valtab; |
445 | 0 | l_int32 ws, hs, wd, hd; |
446 | 0 | l_int32 wpld, wpls; |
447 | 0 | l_uint32 *sumtab; |
448 | 0 | l_uint32 *datas, *datad; |
449 | 0 | PIX *pixd; |
450 | |
|
451 | 0 | if (!pixs) |
452 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
453 | 0 | if (pixGetDepth(pixs) != 1) |
454 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); |
455 | | |
456 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
457 | 0 | wd = (ws / 3) & 0xfffffff8; /* truncate to factor of 8 */ |
458 | 0 | hd = hs / 3; |
459 | 0 | if (wd == 0 || hd == 0) |
460 | 0 | return (PIX *)ERROR_PTR("pixs too small", __func__, NULL); |
461 | | |
462 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
463 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
464 | 0 | pixCopyInputFormat(pixd, pixs); |
465 | 0 | pixCopyResolution(pixd, pixs); |
466 | 0 | pixScaleResolution(pixd, 0.33333f, 0.33333f); |
467 | 0 | datas = pixGetData(pixs); |
468 | 0 | datad = pixGetData(pixd); |
469 | 0 | wpls = pixGetWpl(pixs); |
470 | 0 | wpld = pixGetWpl(pixd); |
471 | |
|
472 | 0 | sumtab = makeSumTabSG3(); |
473 | 0 | valtab = makeValTabSG3(); |
474 | 0 | scaleToGray3Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); |
475 | 0 | LEPT_FREE(sumtab); |
476 | 0 | LEPT_FREE(valtab); |
477 | 0 | return pixd; |
478 | 0 | } |
479 | | |
480 | | |
481 | | /*! |
482 | | * \brief pixScaleToGray4() |
483 | | * |
484 | | * \param[in] pixs 1 bpp |
485 | | * \return pixd 8 bpp, scaled down by 4x in each direction, |
486 | | * or NULL on error. |
487 | | * |
488 | | * <pre> |
489 | | * Notes: |
490 | | * (1) The width of pixd is truncated is truncated to a factor of 2. |
491 | | * </pre> |
492 | | */ |
493 | | PIX * |
494 | | pixScaleToGray4(PIX *pixs) |
495 | 0 | { |
496 | 0 | l_uint8 *valtab; |
497 | 0 | l_int32 ws, hs, wd, hd; |
498 | 0 | l_int32 wpld, wpls; |
499 | 0 | l_uint32 *sumtab; |
500 | 0 | l_uint32 *datas, *datad; |
501 | 0 | PIX *pixd; |
502 | |
|
503 | 0 | if (!pixs) |
504 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
505 | 0 | if (pixGetDepth(pixs) != 1) |
506 | 0 | return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL); |
507 | | |
508 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
509 | 0 | wd = (ws / 4) & 0xfffffffe; /* truncate to factor of 2 */ |
510 | 0 | hd = hs / 4; |
511 | 0 | if (wd == 0 || hd == 0) |
512 | 0 | return (PIX *)ERROR_PTR("pixs too small", __func__, NULL); |
513 | | |
514 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
515 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
516 | 0 | pixCopyInputFormat(pixd, pixs); |
517 | 0 | pixCopyResolution(pixd, pixs); |
518 | 0 | pixScaleResolution(pixd, 0.25, 0.25); |
519 | 0 | datas = pixGetData(pixs); |
520 | 0 | datad = pixGetData(pixd); |
521 | 0 | wpls = pixGetWpl(pixs); |
522 | 0 | wpld = pixGetWpl(pixd); |
523 | |
|
524 | 0 | sumtab = makeSumTabSG4(); |
525 | 0 | valtab = makeValTabSG4(); |
526 | 0 | scaleToGray4Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); |
527 | 0 | LEPT_FREE(sumtab); |
528 | 0 | LEPT_FREE(valtab); |
529 | 0 | return pixd; |
530 | 0 | } |
531 | | |
532 | | |
533 | | |
534 | | /*! |
535 | | * \brief pixScaleToGray6() |
536 | | * |
537 | | * \param[in] pixs 1 bpp |
538 | | * \return pixd 8 bpp, scaled down by 6x in each direction, |
539 | | * or NULL on error. |
540 | | * |
541 | | * <pre> |
542 | | * Notes: |
543 | | * (1) The width of pixd is truncated is truncated to a factor of 8. |
544 | | * </pre> |
545 | | */ |
546 | | PIX * |
547 | | pixScaleToGray6(PIX *pixs) |
548 | 0 | { |
549 | 0 | l_uint8 *valtab; |
550 | 0 | l_int32 ws, hs, wd, hd, wpld, wpls; |
551 | 0 | l_int32 *tab8; |
552 | 0 | l_uint32 *datas, *datad; |
553 | 0 | PIX *pixd; |
554 | |
|
555 | 0 | if (!pixs) |
556 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
557 | 0 | if (pixGetDepth(pixs) != 1) |
558 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); |
559 | | |
560 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
561 | 0 | wd = (ws / 6) & 0xfffffff8; /* truncate to factor of 8 */ |
562 | 0 | hd = hs / 6; |
563 | 0 | if (wd == 0 || hd == 0) |
564 | 0 | return (PIX *)ERROR_PTR("pixs too small", __func__, NULL); |
565 | | |
566 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
567 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
568 | 0 | pixCopyInputFormat(pixd, pixs); |
569 | 0 | pixCopyResolution(pixd, pixs); |
570 | 0 | pixScaleResolution(pixd, 0.16667f, 0.16667f); |
571 | 0 | datas = pixGetData(pixs); |
572 | 0 | datad = pixGetData(pixd); |
573 | 0 | wpls = pixGetWpl(pixs); |
574 | 0 | wpld = pixGetWpl(pixd); |
575 | |
|
576 | 0 | tab8 = makePixelSumTab8(); |
577 | 0 | valtab = makeValTabSG6(); |
578 | 0 | scaleToGray6Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab); |
579 | 0 | LEPT_FREE(tab8); |
580 | 0 | LEPT_FREE(valtab); |
581 | 0 | return pixd; |
582 | 0 | } |
583 | | |
584 | | |
585 | | /*! |
586 | | * \brief pixScaleToGray8() |
587 | | * |
588 | | * \param[in] pixs 1 bpp |
589 | | * \return pixd 8 bpp, scaled down by 8x in each direction, |
590 | | * or NULL on error |
591 | | */ |
592 | | PIX * |
593 | | pixScaleToGray8(PIX *pixs) |
594 | 0 | { |
595 | 0 | l_uint8 *valtab; |
596 | 0 | l_int32 ws, hs, wd, hd; |
597 | 0 | l_int32 wpld, wpls; |
598 | 0 | l_int32 *tab8; |
599 | 0 | l_uint32 *datas, *datad; |
600 | 0 | PIX *pixd; |
601 | |
|
602 | 0 | if (!pixs) |
603 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
604 | 0 | if (pixGetDepth(pixs) != 1) |
605 | 0 | return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL); |
606 | | |
607 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
608 | 0 | wd = ws / 8; /* truncate to nearest dest byte */ |
609 | 0 | hd = hs / 8; |
610 | 0 | if (wd == 0 || hd == 0) |
611 | 0 | return (PIX *)ERROR_PTR("pixs too small", __func__, NULL); |
612 | | |
613 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
614 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
615 | 0 | pixCopyInputFormat(pixd, pixs); |
616 | 0 | pixCopyResolution(pixd, pixs); |
617 | 0 | pixScaleResolution(pixd, 0.125, 0.125); |
618 | 0 | datas = pixGetData(pixs); |
619 | 0 | datad = pixGetData(pixd); |
620 | 0 | wpls = pixGetWpl(pixs); |
621 | 0 | wpld = pixGetWpl(pixd); |
622 | |
|
623 | 0 | tab8 = makePixelSumTab8(); |
624 | 0 | valtab = makeValTabSG8(); |
625 | 0 | scaleToGray8Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab); |
626 | 0 | LEPT_FREE(tab8); |
627 | 0 | LEPT_FREE(valtab); |
628 | 0 | return pixd; |
629 | 0 | } |
630 | | |
631 | | |
632 | | /*! |
633 | | * \brief pixScaleToGray16() |
634 | | * |
635 | | * \param[in] pixs 1 bpp |
636 | | * \return pixd 8 bpp, scaled down by 16x in each direction, |
637 | | * or NULL on error. |
638 | | */ |
639 | | PIX * |
640 | | pixScaleToGray16(PIX *pixs) |
641 | 0 | { |
642 | 0 | l_int32 ws, hs, wd, hd; |
643 | 0 | l_int32 wpld, wpls; |
644 | 0 | l_int32 *tab8; |
645 | 0 | l_uint32 *datas, *datad; |
646 | 0 | PIX *pixd; |
647 | |
|
648 | 0 | if (!pixs) |
649 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
650 | 0 | if (pixGetDepth(pixs) != 1) |
651 | 0 | return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, NULL); |
652 | | |
653 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
654 | 0 | wd = ws / 16; |
655 | 0 | hd = hs / 16; |
656 | 0 | if (wd == 0 || hd == 0) |
657 | 0 | return (PIX *)ERROR_PTR("pixs too small", __func__, NULL); |
658 | | |
659 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
660 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
661 | 0 | pixCopyInputFormat(pixd, pixs); |
662 | 0 | pixCopyResolution(pixd, pixs); |
663 | 0 | pixScaleResolution(pixd, 0.0625, 0.0625); |
664 | 0 | datas = pixGetData(pixs); |
665 | 0 | datad = pixGetData(pixd); |
666 | 0 | wpls = pixGetWpl(pixs); |
667 | 0 | wpld = pixGetWpl(pixd); |
668 | |
|
669 | 0 | tab8 = makePixelSumTab8(); |
670 | 0 | scaleToGray16Low(datad, wd, hd, wpld, datas, wpls, tab8); |
671 | 0 | LEPT_FREE(tab8); |
672 | 0 | return pixd; |
673 | 0 | } |
674 | | |
675 | | |
676 | | /*------------------------------------------------------------------* |
677 | | * Scale-to-gray mipmap(1 bpp --> 8 bpp, arbitrary reduction) * |
678 | | *------------------------------------------------------------------*/ |
679 | | /*! |
680 | | * \brief pixScaleToGrayMipmap() |
681 | | * |
682 | | * \param[in] pixs 1 bpp |
683 | | * \param[in] scalefactor reduction: must be > 0.0 and < 1.0 |
684 | | * \return pixd 8 bpp, scaled down by scalefactor in each direction, |
685 | | * or NULL on error. |
686 | | * |
687 | | * <pre> |
688 | | * Notes: |
689 | | * |
690 | | * This function is here mainly for pedagogical reasons. |
691 | | * Mip-mapping is widely used in graphics for texture mapping, because |
692 | | * the texture changes smoothly with scale. This is accomplished by |
693 | | * constructing a multiresolution pyramid and, for each pixel, |
694 | | * doing a linear interpolation between corresponding pixels in |
695 | | * the two planes of the pyramid that bracket the desired resolution. |
696 | | * The computation is very efficient, and is implemented in hardware |
697 | | * in high-end graphics cards. |
698 | | * |
699 | | * We can use mip-mapping for scale-to-gray by using two scale-to-gray |
700 | | * reduced images (we don't need the entire pyramid) selected from |
701 | | * the set {2x, 4x, ... 16x}, and interpolating. However, we get |
702 | | * severe aliasing, probably because we are subsampling from the |
703 | | * higher resolution image. The method is very fast, but the result |
704 | | * is very poor. In fact, the results don't look any better than |
705 | | * either subsampling off the higher-res grayscale image or oversampling |
706 | | * on the lower-res image. Consequently, this method should NOT be used |
707 | | * for generating reduced images, scale-to-gray or otherwise. |
708 | | * </pre> |
709 | | */ |
710 | | PIX * |
711 | | pixScaleToGrayMipmap(PIX *pixs, |
712 | | l_float32 scalefactor) |
713 | 0 | { |
714 | 0 | l_int32 w, h, minsrc, mindest; |
715 | 0 | l_float32 red; |
716 | 0 | PIX *pixs1, *pixs2, *pixt, *pixd; |
717 | |
|
718 | 0 | if (!pixs) |
719 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
720 | 0 | if (pixGetDepth(pixs) != 1) |
721 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); |
722 | 0 | if (scalefactor <= 0.0) |
723 | 0 | return (PIX *)ERROR_PTR("scalefactor <= 0.0", __func__, NULL); |
724 | 0 | if (scalefactor >= 1.0) |
725 | 0 | return (PIX *)ERROR_PTR("scalefactor >= 1.0", __func__, NULL); |
726 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
727 | 0 | minsrc = L_MIN(w, h); |
728 | 0 | mindest = (l_int32)((l_float32)minsrc * scalefactor); |
729 | 0 | if (mindest < 2) |
730 | 0 | return (PIX *)ERROR_PTR("scalefactor too small", __func__, NULL); |
731 | | |
732 | 0 | if (scalefactor > 0.5) { |
733 | 0 | pixs1 = pixConvert1To8(NULL, pixs, 255, 0); |
734 | 0 | pixs2 = pixScaleToGray2(pixs); |
735 | 0 | red = scalefactor; |
736 | 0 | } else if (scalefactor == 0.5) { |
737 | 0 | return pixScaleToGray2(pixs); |
738 | 0 | } else if (scalefactor > 0.25) { |
739 | 0 | pixs1 = pixScaleToGray2(pixs); |
740 | 0 | pixs2 = pixScaleToGray4(pixs); |
741 | 0 | red = 2.f * scalefactor; |
742 | 0 | } else if (scalefactor == 0.25) { |
743 | 0 | return pixScaleToGray4(pixs); |
744 | 0 | } else if (scalefactor > 0.125) { |
745 | 0 | pixs1 = pixScaleToGray4(pixs); |
746 | 0 | pixs2 = pixScaleToGray8(pixs); |
747 | 0 | red = 4.f * scalefactor; |
748 | 0 | } else if (scalefactor == 0.125) { |
749 | 0 | return pixScaleToGray8(pixs); |
750 | 0 | } else if (scalefactor > 0.0625) { |
751 | 0 | pixs1 = pixScaleToGray8(pixs); |
752 | 0 | pixs2 = pixScaleToGray16(pixs); |
753 | 0 | red = 8.f * scalefactor; |
754 | 0 | } else if (scalefactor == 0.0625) { |
755 | 0 | return pixScaleToGray16(pixs); |
756 | 0 | } else { /* end of the pyramid; just do it */ |
757 | 0 | red = 16.0f * scalefactor; /* will be <= 1.0 */ |
758 | 0 | if ((pixt = pixScaleToGray16(pixs)) == NULL) |
759 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
760 | 0 | if (red < 0.7) |
761 | 0 | pixd = pixScaleSmooth(pixt, red, red); |
762 | 0 | else |
763 | 0 | pixd = pixScaleGrayLI(pixt, red, red); |
764 | 0 | pixDestroy(&pixt); |
765 | 0 | return pixd; |
766 | 0 | } |
767 | | |
768 | 0 | pixd = pixScaleMipmap(pixs1, pixs2, red); |
769 | 0 | pixCopyInputFormat(pixd, pixs); |
770 | |
|
771 | 0 | pixDestroy(&pixs1); |
772 | 0 | pixDestroy(&pixs2); |
773 | 0 | return pixd; |
774 | 0 | } |
775 | | |
776 | | |
777 | | /*------------------------------------------------------------------* |
778 | | * Grayscale scaling using mipmap * |
779 | | *------------------------------------------------------------------*/ |
780 | | /*! |
781 | | * \brief pixScaleMipmap() |
782 | | * |
783 | | * \param[in] pixs1 high res 8 bpp, no cmap |
784 | | * \param[in] pixs2 low res -- 2x reduced -- 8 bpp, no cmap |
785 | | * \param[in] scale reduction with respect to high res image, > 0.5 |
786 | | * \return 8 bpp pix, scaled down by reduction in each direction, |
787 | | * or NULL on error. |
788 | | * |
789 | | * <pre> |
790 | | * Notes: |
791 | | * (1) See notes in pixScaleToGrayMipmap(). |
792 | | * (2) This function suffers from aliasing effects that are |
793 | | * easily seen in document images. |
794 | | * </pre> |
795 | | */ |
796 | | PIX * |
797 | | pixScaleMipmap(PIX *pixs1, |
798 | | PIX *pixs2, |
799 | | l_float32 scale) |
800 | 0 | { |
801 | 0 | l_int32 ws1, hs1, ws2, hs2, wd, hd, wpls1, wpls2, wpld; |
802 | 0 | l_uint32 *datas1, *datas2, *datad; |
803 | 0 | PIX *pixd; |
804 | |
|
805 | 0 | if (!pixs1 || pixGetDepth(pixs1) != 8 || pixGetColormap(pixs1)) |
806 | 0 | return (PIX *)ERROR_PTR("pixs1 underdefined, not 8 bpp, or cmapped", |
807 | 0 | __func__, NULL); |
808 | 0 | if (!pixs2 || pixGetDepth(pixs2) != 8 || pixGetColormap(pixs2)) |
809 | 0 | return (PIX *)ERROR_PTR("pixs2 underdefined, not 8 bpp, or cmapped", |
810 | 0 | __func__, NULL); |
811 | 0 | pixGetDimensions(pixs1, &ws1, &hs1, NULL); |
812 | 0 | pixGetDimensions(pixs2, &ws2, &hs2, NULL); |
813 | 0 | if (scale > 1.0 || scale < 0.5) |
814 | 0 | return (PIX *)ERROR_PTR("scale not in [0.5, 1.0]", __func__, NULL); |
815 | 0 | if (ws1 < 2 * ws2) |
816 | 0 | return (PIX *)ERROR_PTR("invalid width ratio", __func__, NULL); |
817 | 0 | if (hs1 < 2 * hs2) |
818 | 0 | return (PIX *)ERROR_PTR("invalid height ratio", __func__, NULL); |
819 | | |
820 | | /* Generate wd and hd from the lower resolution dimensions, |
821 | | * to guarantee staying within both src images */ |
822 | 0 | datas1 = pixGetData(pixs1); |
823 | 0 | wpls1 = pixGetWpl(pixs1); |
824 | 0 | datas2 = pixGetData(pixs2); |
825 | 0 | wpls2 = pixGetWpl(pixs2); |
826 | 0 | wd = (l_int32)(2. * scale * pixGetWidth(pixs2)); |
827 | 0 | hd = (l_int32)(2. * scale * pixGetHeight(pixs2)); |
828 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
829 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
830 | 0 | pixCopyInputFormat(pixd, pixs1); |
831 | 0 | pixCopyResolution(pixd, pixs1); |
832 | 0 | pixScaleResolution(pixd, scale, scale); |
833 | 0 | datad = pixGetData(pixd); |
834 | 0 | wpld = pixGetWpl(pixd); |
835 | |
|
836 | 0 | scaleMipmapLow(datad, wd, hd, wpld, datas1, wpls1, datas2, wpls2, scale); |
837 | 0 | return pixd; |
838 | 0 | } |
839 | | |
840 | | |
841 | | /*------------------------------------------------------------------* |
842 | | * Replicated (integer) expansion * |
843 | | *------------------------------------------------------------------*/ |
844 | | /*! |
845 | | * \brief pixExpandReplicate() |
846 | | * |
847 | | * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp |
848 | | * \param[in] factor integer scale factor for replicative expansion |
849 | | * \return pixd scaled up, or NULL on error. |
850 | | */ |
851 | | PIX * |
852 | | pixExpandReplicate(PIX *pixs, |
853 | | l_int32 factor) |
854 | 3.66k | { |
855 | 3.66k | l_int32 w, h, d, wd, hd, wpls, wpld, start, i, j, k; |
856 | 3.66k | l_uint8 sval; |
857 | 3.66k | l_uint16 sval16; |
858 | 3.66k | l_uint32 sval32; |
859 | 3.66k | l_uint32 *lines, *datas, *lined, *datad; |
860 | 3.66k | PIX *pixd; |
861 | | |
862 | 3.66k | if (!pixs) |
863 | 948 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
864 | 2.71k | pixGetDimensions(pixs, &w, &h, &d); |
865 | 2.71k | if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
866 | 0 | return (PIX *)ERROR_PTR("depth not in {1,2,4,8,16,32}", __func__, NULL); |
867 | 2.71k | if (factor <= 0) |
868 | 0 | return (PIX *)ERROR_PTR("factor <= 0; invalid", __func__, NULL); |
869 | 2.71k | if (factor == 1) |
870 | 0 | return pixCopy(NULL, pixs); |
871 | | |
872 | 2.71k | if (d == 1) |
873 | 2.71k | return pixExpandBinaryReplicate(pixs, factor, factor); |
874 | | |
875 | 0 | wd = factor * w; |
876 | 0 | hd = factor * h; |
877 | 0 | if ((pixd = pixCreate(wd, hd, d)) == NULL) |
878 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
879 | 0 | pixCopyColormap(pixd, pixs); |
880 | 0 | pixCopyInputFormat(pixd, pixs); |
881 | 0 | pixCopyResolution(pixd, pixs); |
882 | 0 | pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); |
883 | 0 | datas = pixGetData(pixs); |
884 | 0 | wpls = pixGetWpl(pixs); |
885 | 0 | datad = pixGetData(pixd); |
886 | 0 | wpld = pixGetWpl(pixd); |
887 | |
|
888 | 0 | switch (d) { |
889 | 0 | case 2: |
890 | 0 | for (i = 0; i < h; i++) { |
891 | 0 | lines = datas + i * wpls; |
892 | 0 | lined = datad + factor * i * wpld; |
893 | 0 | for (j = 0; j < w; j++) { |
894 | 0 | sval = GET_DATA_DIBIT(lines, j); |
895 | 0 | start = factor * j; |
896 | 0 | for (k = 0; k < factor; k++) |
897 | 0 | SET_DATA_DIBIT(lined, start + k, sval); |
898 | 0 | } |
899 | 0 | for (k = 1; k < factor; k++) |
900 | 0 | memcpy(lined + k * wpld, lined, 4 * wpld); |
901 | 0 | } |
902 | 0 | break; |
903 | 0 | case 4: |
904 | 0 | for (i = 0; i < h; i++) { |
905 | 0 | lines = datas + i * wpls; |
906 | 0 | lined = datad + factor * i * wpld; |
907 | 0 | for (j = 0; j < w; j++) { |
908 | 0 | sval = GET_DATA_QBIT(lines, j); |
909 | 0 | start = factor * j; |
910 | 0 | for (k = 0; k < factor; k++) |
911 | 0 | SET_DATA_QBIT(lined, start + k, sval); |
912 | 0 | } |
913 | 0 | for (k = 1; k < factor; k++) |
914 | 0 | memcpy(lined + k * wpld, lined, 4 * wpld); |
915 | 0 | } |
916 | 0 | break; |
917 | 0 | case 8: |
918 | 0 | for (i = 0; i < h; i++) { |
919 | 0 | lines = datas + i * wpls; |
920 | 0 | lined = datad + factor * i * wpld; |
921 | 0 | for (j = 0; j < w; j++) { |
922 | 0 | sval = GET_DATA_BYTE(lines, j); |
923 | 0 | start = factor * j; |
924 | 0 | for (k = 0; k < factor; k++) |
925 | 0 | SET_DATA_BYTE(lined, start + k, sval); |
926 | 0 | } |
927 | 0 | for (k = 1; k < factor; k++) |
928 | 0 | memcpy(lined + k * wpld, lined, 4 * wpld); |
929 | 0 | } |
930 | 0 | break; |
931 | 0 | case 16: |
932 | 0 | for (i = 0; i < h; i++) { |
933 | 0 | lines = datas + i * wpls; |
934 | 0 | lined = datad + factor * i * wpld; |
935 | 0 | for (j = 0; j < w; j++) { |
936 | 0 | sval16 = GET_DATA_TWO_BYTES(lines, j); |
937 | 0 | start = factor * j; |
938 | 0 | for (k = 0; k < factor; k++) |
939 | 0 | SET_DATA_TWO_BYTES(lined, start + k, sval16); |
940 | 0 | } |
941 | 0 | for (k = 1; k < factor; k++) |
942 | 0 | memcpy(lined + k * wpld, lined, 4 * wpld); |
943 | 0 | } |
944 | 0 | break; |
945 | 0 | case 32: |
946 | 0 | for (i = 0; i < h; i++) { |
947 | 0 | lines = datas + i * wpls; |
948 | 0 | lined = datad + factor * i * wpld; |
949 | 0 | for (j = 0; j < w; j++) { |
950 | 0 | sval32 = *(lines + j); |
951 | 0 | start = factor * j; |
952 | 0 | for (k = 0; k < factor; k++) |
953 | 0 | *(lined + start + k) = sval32; |
954 | 0 | } |
955 | 0 | for (k = 1; k < factor; k++) |
956 | 0 | memcpy(lined + k * wpld, lined, 4 * wpld); |
957 | 0 | } |
958 | 0 | break; |
959 | 0 | default: |
960 | 0 | lept_stderr("invalid depth\n"); |
961 | 0 | } |
962 | | |
963 | 0 | if (d == 32 && pixGetSpp(pixs) == 4) |
964 | 0 | pixScaleAndTransferAlpha(pixd, pixs, (l_float32)factor, |
965 | 0 | (l_float32)factor); |
966 | 0 | return pixd; |
967 | 0 | } |
968 | | |
969 | | |
970 | | /*-----------------------------------------------------------------------* |
971 | | * Downscaling using min or max * |
972 | | *-----------------------------------------------------------------------*/ |
973 | | /*! |
974 | | * \brief pixScaleGrayMinMax() |
975 | | * |
976 | | * \param[in] pixs 8 bpp, not cmapped |
977 | | * \param[in] xfact x downscaling factor; integer |
978 | | * \param[in] yfact y downscaling factor; integer |
979 | | * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF |
980 | | * \return pixd 8 bpp |
981 | | * |
982 | | * <pre> |
983 | | * Notes: |
984 | | * (1) The downscaled pixels in pixd are the min, max or (max - min) |
985 | | * of the corresponding set of xfact * yfact pixels in pixs. |
986 | | * (2) Using L_CHOOSE_MIN is equivalent to a grayscale erosion, |
987 | | * using a brick Sel of size (xfact * yfact), followed by |
988 | | * subsampling within each (xfact * yfact) cell. Using |
989 | | * L_CHOOSE_MAX is equivalent to the corresponding dilation. |
990 | | * (3) Using L_CHOOSE_MAXDIFF finds the difference between max |
991 | | * and min values in each cell. |
992 | | * (4) For the special case of downscaling by 2x in both directions, |
993 | | * pixScaleGrayMinMax2() is about 2x more efficient. |
994 | | * </pre> |
995 | | */ |
996 | | PIX * |
997 | | pixScaleGrayMinMax(PIX *pixs, |
998 | | l_int32 xfact, |
999 | | l_int32 yfact, |
1000 | | l_int32 type) |
1001 | 0 | { |
1002 | 0 | l_int32 ws, hs, wd, hd, wpls, wpld, i, j, k, m; |
1003 | 0 | l_int32 minval, maxval, val; |
1004 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
1005 | 0 | PIX *pixd; |
1006 | |
|
1007 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
1008 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
1009 | 0 | __func__, NULL); |
1010 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1011 | 0 | if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && |
1012 | 0 | type != L_CHOOSE_MAXDIFF) |
1013 | 0 | return (PIX *)ERROR_PTR("invalid type", __func__, NULL); |
1014 | 0 | if (xfact < 1 || yfact < 1) |
1015 | 0 | return (PIX *)ERROR_PTR("xfact and yfact must be >= 1", __func__, NULL); |
1016 | | |
1017 | 0 | if (xfact == 2 && yfact == 2) |
1018 | 0 | return pixScaleGrayMinMax2(pixs, type); |
1019 | | |
1020 | 0 | wd = ws / xfact; |
1021 | 0 | if (wd == 0) { /* single tile */ |
1022 | 0 | wd = 1; |
1023 | 0 | xfact = ws; |
1024 | 0 | } |
1025 | 0 | hd = hs / yfact; |
1026 | 0 | if (hd == 0) { /* single tile */ |
1027 | 0 | hd = 1; |
1028 | 0 | yfact = hs; |
1029 | 0 | } |
1030 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
1031 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1032 | 0 | pixCopyInputFormat(pixd, pixs); |
1033 | 0 | datas = pixGetData(pixs); |
1034 | 0 | datad = pixGetData(pixd); |
1035 | 0 | wpls = pixGetWpl(pixs); |
1036 | 0 | wpld = pixGetWpl(pixd); |
1037 | 0 | for (i = 0; i < hd; i++) { |
1038 | 0 | lined = datad + i * wpld; |
1039 | 0 | for (j = 0; j < wd; j++) { |
1040 | 0 | if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAXDIFF) { |
1041 | 0 | minval = 255; |
1042 | 0 | for (k = 0; k < yfact; k++) { |
1043 | 0 | lines = datas + (yfact * i + k) * wpls; |
1044 | 0 | for (m = 0; m < xfact; m++) { |
1045 | 0 | val = GET_DATA_BYTE(lines, xfact * j + m); |
1046 | 0 | if (val < minval) |
1047 | 0 | minval = val; |
1048 | 0 | } |
1049 | 0 | } |
1050 | 0 | } |
1051 | 0 | if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAXDIFF) { |
1052 | 0 | maxval = 0; |
1053 | 0 | for (k = 0; k < yfact; k++) { |
1054 | 0 | lines = datas + (yfact * i + k) * wpls; |
1055 | 0 | for (m = 0; m < xfact; m++) { |
1056 | 0 | val = GET_DATA_BYTE(lines, xfact * j + m); |
1057 | 0 | if (val > maxval) |
1058 | 0 | maxval = val; |
1059 | 0 | } |
1060 | 0 | } |
1061 | 0 | } |
1062 | 0 | if (type == L_CHOOSE_MIN) |
1063 | 0 | SET_DATA_BYTE(lined, j, minval); |
1064 | 0 | else if (type == L_CHOOSE_MAX) |
1065 | 0 | SET_DATA_BYTE(lined, j, maxval); |
1066 | 0 | else /* type == L_CHOOSE_MAXDIFF */ |
1067 | 0 | SET_DATA_BYTE(lined, j, maxval - minval); |
1068 | 0 | } |
1069 | 0 | } |
1070 | |
|
1071 | 0 | return pixd; |
1072 | 0 | } |
1073 | | |
1074 | | |
1075 | | /*! |
1076 | | * \brief pixScaleGrayMinMax2() |
1077 | | * |
1078 | | * \param[in] pixs 8 bpp, not cmapped |
1079 | | * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF |
1080 | | * \return pixd 8 bpp downscaled by 2x |
1081 | | * |
1082 | | * <pre> |
1083 | | * Notes: |
1084 | | * (1) Special version for 2x reduction. The downscaled pixels |
1085 | | * in pixd are the min, max or (max - min) of the corresponding |
1086 | | * set of 4 pixels in pixs. |
1087 | | * (2) The max and min operations are a special case (for levels 1 |
1088 | | * and 4) of grayscale analog to the binary rank scaling operation |
1089 | | * pixReduceRankBinary2(). Note, however, that because of |
1090 | | * the photometric definition that higher gray values are |
1091 | | * lighter, the erosion-like L_CHOOSE_MIN will darken |
1092 | | * the resulting image, corresponding to a threshold level 1 |
1093 | | * in the binary case. Likewise, L_CHOOSE_MAX will lighten |
1094 | | * the pixd, corresponding to a threshold level of 4. |
1095 | | * (3) To choose any of the four rank levels in a 2x grayscale |
1096 | | * reduction, use pixScaleGrayRank2(). |
1097 | | * (4) This runs at about 70 MPix/sec/GHz of source data for |
1098 | | * erosion and dilation. |
1099 | | * </pre> |
1100 | | */ |
1101 | | PIX * |
1102 | | pixScaleGrayMinMax2(PIX *pixs, |
1103 | | l_int32 type) |
1104 | 0 | { |
1105 | 0 | l_int32 ws, hs, wd, hd, wpls, wpld, i, j, k; |
1106 | 0 | l_int32 minval, maxval; |
1107 | 0 | l_int32 val[4]; |
1108 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
1109 | 0 | PIX *pixd; |
1110 | |
|
1111 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
1112 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
1113 | 0 | __func__, NULL); |
1114 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1115 | 0 | if (ws < 2 || hs < 2) |
1116 | 0 | return (PIX *)ERROR_PTR("too small: ws < 2 or hs < 2", __func__, NULL); |
1117 | 0 | if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && |
1118 | 0 | type != L_CHOOSE_MAXDIFF) |
1119 | 0 | return (PIX *)ERROR_PTR("invalid type", __func__, NULL); |
1120 | | |
1121 | 0 | wd = ws / 2; |
1122 | 0 | hd = hs / 2; |
1123 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
1124 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1125 | 0 | pixCopyInputFormat(pixd, pixs); |
1126 | 0 | datas = pixGetData(pixs); |
1127 | 0 | datad = pixGetData(pixd); |
1128 | 0 | wpls = pixGetWpl(pixs); |
1129 | 0 | wpld = pixGetWpl(pixd); |
1130 | 0 | for (i = 0; i < hd; i++) { |
1131 | 0 | lines = datas + 2 * i * wpls; |
1132 | 0 | lined = datad + i * wpld; |
1133 | 0 | for (j = 0; j < wd; j++) { |
1134 | 0 | val[0] = GET_DATA_BYTE(lines, 2 * j); |
1135 | 0 | val[1] = GET_DATA_BYTE(lines, 2 * j + 1); |
1136 | 0 | val[2] = GET_DATA_BYTE(lines + wpls, 2 * j); |
1137 | 0 | val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1); |
1138 | 0 | if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAXDIFF) { |
1139 | 0 | minval = 255; |
1140 | 0 | for (k = 0; k < 4; k++) { |
1141 | 0 | if (val[k] < minval) |
1142 | 0 | minval = val[k]; |
1143 | 0 | } |
1144 | 0 | } |
1145 | 0 | if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAXDIFF) { |
1146 | 0 | maxval = 0; |
1147 | 0 | for (k = 0; k < 4; k++) { |
1148 | 0 | if (val[k] > maxval) |
1149 | 0 | maxval = val[k]; |
1150 | 0 | } |
1151 | 0 | } |
1152 | 0 | if (type == L_CHOOSE_MIN) |
1153 | 0 | SET_DATA_BYTE(lined, j, minval); |
1154 | 0 | else if (type == L_CHOOSE_MAX) |
1155 | 0 | SET_DATA_BYTE(lined, j, maxval); |
1156 | 0 | else /* type == L_CHOOSE_MAXDIFF */ |
1157 | 0 | SET_DATA_BYTE(lined, j, maxval - minval); |
1158 | 0 | } |
1159 | 0 | } |
1160 | |
|
1161 | 0 | return pixd; |
1162 | 0 | } |
1163 | | |
1164 | | |
1165 | | /*-----------------------------------------------------------------------* |
1166 | | * Grayscale downscaling using rank value * |
1167 | | *-----------------------------------------------------------------------*/ |
1168 | | /*! |
1169 | | * \brief pixScaleGrayRankCascade() |
1170 | | * |
1171 | | * \param[in] pixs 8 bpp, not cmapped |
1172 | | * \param[in] level1, level2 ... |
1173 | | * \param[in] level3, level4 rank thresholds, in set {0, 1, 2, 3, 4} |
1174 | | * \return pixd 8 bpp, downscaled by up to 16x |
1175 | | * |
1176 | | * <pre> |
1177 | | * Notes: |
1178 | | * (1) This performs up to four cascaded 2x rank reductions. |
1179 | | * (2) Use level = 0 to truncate the cascade. |
1180 | | * </pre> |
1181 | | */ |
1182 | | PIX * |
1183 | | pixScaleGrayRankCascade(PIX *pixs, |
1184 | | l_int32 level1, |
1185 | | l_int32 level2, |
1186 | | l_int32 level3, |
1187 | | l_int32 level4) |
1188 | 0 | { |
1189 | 0 | PIX *pixt1, *pixt2, *pixt3, *pixt4; |
1190 | |
|
1191 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
1192 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
1193 | 0 | __func__, NULL); |
1194 | 0 | if (level1 > 4 || level2 > 4 || level3 > 4 || level4 > 4) |
1195 | 0 | return (PIX *)ERROR_PTR("levels must not exceed 4", __func__, NULL); |
1196 | | |
1197 | 0 | if (level1 <= 0) { |
1198 | 0 | L_WARNING("no reduction because level1 not > 0\n", __func__); |
1199 | 0 | return pixCopy(NULL, pixs); |
1200 | 0 | } |
1201 | | |
1202 | 0 | pixt1 = pixScaleGrayRank2(pixs, level1); |
1203 | 0 | if (level2 <= 0) |
1204 | 0 | return pixt1; |
1205 | | |
1206 | 0 | pixt2 = pixScaleGrayRank2(pixt1, level2); |
1207 | 0 | pixDestroy(&pixt1); |
1208 | 0 | if (level3 <= 0) |
1209 | 0 | return pixt2; |
1210 | | |
1211 | 0 | pixt3 = pixScaleGrayRank2(pixt2, level3); |
1212 | 0 | pixDestroy(&pixt2); |
1213 | 0 | if (level4 <= 0) |
1214 | 0 | return pixt3; |
1215 | | |
1216 | 0 | pixt4 = pixScaleGrayRank2(pixt3, level4); |
1217 | 0 | pixDestroy(&pixt3); |
1218 | 0 | return pixt4; |
1219 | 0 | } |
1220 | | |
1221 | | |
1222 | | /*! |
1223 | | * \brief pixScaleGrayRank2() |
1224 | | * |
1225 | | * \param[in] pixs 8 bpp, no cmap |
1226 | | * \param[in] rank 1 (darkest), 2, 3, 4 (lightest) |
1227 | | * \return pixd 8 bpp, downscaled by 2x |
1228 | | * |
1229 | | * <pre> |
1230 | | * Notes: |
1231 | | * (1) Rank 2x reduction. If rank == 1(4), the downscaled pixels |
1232 | | * in pixd are the min(max) of the corresponding set of |
1233 | | * 4 pixels in pixs. Values 2 and 3 are intermediate. |
1234 | | * (2) This is the grayscale analog to the binary rank scaling operation |
1235 | | * pixReduceRankBinary2(). Here, because of the photometric |
1236 | | * definition that higher gray values are lighter, rank 1 gives |
1237 | | * the darkest pixel, whereas rank 4 gives the lightest pixel. |
1238 | | * This is opposite to the binary rank operation. |
1239 | | * (3) For rank = 1 and 4, this calls pixScaleGrayMinMax2(), |
1240 | | * which runs at about 70 MPix/sec/GHz of source data. |
1241 | | * For rank 2 and 3, this runs 3x slower, at about 25 MPix/sec/GHz. |
1242 | | * </pre> |
1243 | | */ |
1244 | | PIX * |
1245 | | pixScaleGrayRank2(PIX *pixs, |
1246 | | l_int32 rank) |
1247 | 0 | { |
1248 | 0 | l_int32 ws, hs, wd, hd, wpls, wpld, i, j, k, m; |
1249 | 0 | l_int32 minval, maxval, rankval, minindex, maxindex; |
1250 | 0 | l_int32 val[4]; |
1251 | 0 | l_int32 midval[4]; /* should only use 2 of these */ |
1252 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
1253 | 0 | PIX *pixd; |
1254 | |
|
1255 | 0 | if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) |
1256 | 0 | return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", |
1257 | 0 | __func__, NULL); |
1258 | 0 | if (rank < 1 || rank > 4) |
1259 | 0 | return (PIX *)ERROR_PTR("invalid rank", __func__, NULL); |
1260 | | |
1261 | 0 | if (rank == 1) |
1262 | 0 | return pixScaleGrayMinMax2(pixs, L_CHOOSE_MIN); |
1263 | 0 | if (rank == 4) |
1264 | 0 | return pixScaleGrayMinMax2(pixs, L_CHOOSE_MAX); |
1265 | | |
1266 | 0 | pixGetDimensions(pixs, &ws, &hs, NULL); |
1267 | 0 | wd = ws / 2; |
1268 | 0 | hd = hs / 2; |
1269 | 0 | if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
1270 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1271 | 0 | pixCopyInputFormat(pixd, pixs); |
1272 | 0 | datas = pixGetData(pixs); |
1273 | 0 | datad = pixGetData(pixd); |
1274 | 0 | wpls = pixGetWpl(pixs); |
1275 | 0 | wpld = pixGetWpl(pixd); |
1276 | 0 | for (i = 0; i < hd; i++) { |
1277 | 0 | lines = datas + 2 * i * wpls; |
1278 | 0 | lined = datad + i * wpld; |
1279 | 0 | for (j = 0; j < wd; j++) { |
1280 | 0 | val[0] = GET_DATA_BYTE(lines, 2 * j); |
1281 | 0 | val[1] = GET_DATA_BYTE(lines, 2 * j + 1); |
1282 | 0 | val[2] = GET_DATA_BYTE(lines + wpls, 2 * j); |
1283 | 0 | val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1); |
1284 | 0 | minval = maxval = val[0]; |
1285 | 0 | minindex = maxindex = 0; |
1286 | 0 | for (k = 1; k < 4; k++) { |
1287 | 0 | if (val[k] < minval) { |
1288 | 0 | minval = val[k]; |
1289 | 0 | minindex = k; |
1290 | 0 | continue; |
1291 | 0 | } |
1292 | 0 | if (val[k] > maxval) { |
1293 | 0 | maxval = val[k]; |
1294 | 0 | maxindex = k; |
1295 | 0 | } |
1296 | 0 | } |
1297 | 0 | for (k = 0, m = 0; k < 4; k++) { |
1298 | 0 | if (k == minindex || k == maxindex) |
1299 | 0 | continue; |
1300 | 0 | midval[m++] = val[k]; |
1301 | 0 | } |
1302 | 0 | if (m > 2) /* minval == maxval; all val[k] are the same */ |
1303 | 0 | rankval = minval; |
1304 | 0 | else if (rank == 2) |
1305 | 0 | rankval = L_MIN(midval[0], midval[1]); |
1306 | 0 | else /* rank == 3 */ |
1307 | 0 | rankval = L_MAX(midval[0], midval[1]); |
1308 | 0 | SET_DATA_BYTE(lined, j, rankval); |
1309 | 0 | } |
1310 | 0 | } |
1311 | |
|
1312 | 0 | return pixd; |
1313 | 0 | } |
1314 | | |
1315 | | |
1316 | | /*------------------------------------------------------------------------* |
1317 | | * Helper function for transferring alpha with scaling * |
1318 | | *------------------------------------------------------------------------*/ |
1319 | | /*! |
1320 | | * \brief pixScaleAndTransferAlpha() |
1321 | | * |
1322 | | * \param[in] pixd 32 bpp, scaled image |
1323 | | * \param[in] pixs 32 bpp, original unscaled image |
1324 | | * \param[in] scalex must be > 0.0 |
1325 | | * \param[in] scaley must be > 0.0 |
1326 | | * \return 0 if OK; 1 on error |
1327 | | * |
1328 | | * <pre> |
1329 | | * Notes: |
1330 | | * (1) This scales the alpha component of pixs and inserts into pixd. |
1331 | | * </pre> |
1332 | | */ |
1333 | | l_ok |
1334 | | pixScaleAndTransferAlpha(PIX *pixd, |
1335 | | PIX *pixs, |
1336 | | l_float32 scalex, |
1337 | | l_float32 scaley) |
1338 | 0 | { |
1339 | 0 | PIX *pix1, *pix2; |
1340 | |
|
1341 | 0 | if (!pixs || !pixd) |
1342 | 0 | return ERROR_INT("pixs and pixd not both defined", __func__, 1); |
1343 | 0 | if (pixGetDepth(pixs) != 32 || pixGetSpp(pixs) != 4) |
1344 | 0 | return ERROR_INT("pixs not 32 bpp and 4 spp", __func__, 1); |
1345 | 0 | if (pixGetDepth(pixd) != 32) |
1346 | 0 | return ERROR_INT("pixd not 32 bpp", __func__, 1); |
1347 | | |
1348 | 0 | if (scalex == 1.0 && scaley == 1.0) { |
1349 | 0 | pixCopyRGBComponent(pixd, pixs, L_ALPHA_CHANNEL); |
1350 | 0 | return 0; |
1351 | 0 | } |
1352 | | |
1353 | 0 | pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); |
1354 | 0 | pix2 = pixScale(pix1, scalex, scaley); |
1355 | 0 | pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); |
1356 | 0 | pixDestroy(&pix1); |
1357 | 0 | pixDestroy(&pix2); |
1358 | 0 | return 0; |
1359 | 0 | } |
1360 | | |
1361 | | |
1362 | | /*------------------------------------------------------------------------* |
1363 | | * RGB scaling including alpha (blend) component and gamma transform * |
1364 | | *------------------------------------------------------------------------*/ |
1365 | | /*! |
1366 | | * \brief pixScaleWithAlpha() |
1367 | | * |
1368 | | * \param[in] pixs 32 bpp rgb or cmapped |
1369 | | * \param[in] scalex must be > 0.0 |
1370 | | * \param[in] scaley must be > 0.0 |
1371 | | * \param[in] pixg [optional] 8 bpp, can be null |
1372 | | * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent |
1373 | | * and 1.0 fully opaque |
1374 | | * \return pixd 32 bpp rgba, or NULL on error |
1375 | | * |
1376 | | * <pre> |
1377 | | * Notes: |
1378 | | * (1) The alpha channel is transformed separately from pixs, |
1379 | | * and aligns with it, being fully transparent outside the |
1380 | | * boundary of the transformed pixs. For pixels that are fully |
1381 | | * transparent, a blending function like pixBlendWithGrayMask() |
1382 | | * will give zero weight to corresponding pixels in pixs. |
1383 | | * (2) Scaling is done with area mapping or linear interpolation, |
1384 | | * depending on the scale factors. Default sharpening is done. |
1385 | | * (3) If pixg is NULL, it is generated as an alpha layer that is |
1386 | | * partially opaque, using %fract. Otherwise, it is cropped |
1387 | | * to pixs if required, and %fract is ignored. The alpha |
1388 | | * channel in pixs is never used. |
1389 | | * (4) Colormaps are removed to 32 bpp. |
1390 | | * (5) The default setting for the border values in the alpha channel |
1391 | | * is 0 (transparent) for the outermost ring of pixels and |
1392 | | * (0.5 * fract * 255) for the second ring. When blended over |
1393 | | * a second image, this |
1394 | | * (a) shrinks the visible image to make a clean overlap edge |
1395 | | * with an image below, and |
1396 | | * (b) softens the edges by weakening the aliasing there. |
1397 | | * Use l_setAlphaMaskBorder() to change these values. |
1398 | | * (6) A subtle use of gamma correction is to remove gamma correction |
1399 | | * before scaling and restore it afterwards. This is done |
1400 | | * by sandwiching this function between a gamma/inverse-gamma |
1401 | | * photometric transform: |
1402 | | * pixt = pixGammaTRCWithAlpha(NULL, pixs, 1.0 / gamma, 0, 255); |
1403 | | * pixd = pixScaleWithAlpha(pixt, scalex, scaley, NULL, fract); |
1404 | | * pixGammaTRCWithAlpha(pixd, pixd, gamma, 0, 255); |
1405 | | * pixDestroy(&pixt); |
1406 | | * This has the side-effect of producing artifacts in the very |
1407 | | * dark regions. |
1408 | | * </pre> |
1409 | | */ |
1410 | | PIX * |
1411 | | pixScaleWithAlpha(PIX *pixs, |
1412 | | l_float32 scalex, |
1413 | | l_float32 scaley, |
1414 | | PIX *pixg, |
1415 | | l_float32 fract) |
1416 | 0 | { |
1417 | 0 | l_int32 ws, hs, d, spp; |
1418 | 0 | PIX *pixd, *pix32, *pixg2, *pixgs; |
1419 | |
|
1420 | 0 | if (!pixs) |
1421 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1422 | 0 | pixGetDimensions(pixs, &ws, &hs, &d); |
1423 | 0 | if (d != 32 && !pixGetColormap(pixs)) |
1424 | 0 | return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL); |
1425 | 0 | if (scalex <= 0.0 || scaley <= 0.0) |
1426 | 0 | return (PIX *)ERROR_PTR("scale factor <= 0.0", __func__, NULL); |
1427 | 0 | if (pixg && pixGetDepth(pixg) != 8) { |
1428 | 0 | L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", |
1429 | 0 | __func__); |
1430 | 0 | pixg = NULL; |
1431 | 0 | } |
1432 | 0 | if (!pixg && (fract < 0.0 || fract > 1.0)) { |
1433 | 0 | L_WARNING("invalid fract; using fully opaque\n", __func__); |
1434 | 0 | fract = 1.0; |
1435 | 0 | } |
1436 | 0 | if (!pixg && fract == 0.0) |
1437 | 0 | L_WARNING("transparent alpha; image will not be blended\n", __func__); |
1438 | | |
1439 | | /* Make sure input to scaling is 32 bpp rgb, and scale it */ |
1440 | 0 | if (d != 32) |
1441 | 0 | pix32 = pixConvertTo32(pixs); |
1442 | 0 | else |
1443 | 0 | pix32 = pixClone(pixs); |
1444 | 0 | spp = pixGetSpp(pix32); |
1445 | 0 | pixSetSpp(pix32, 3); /* ignore the alpha channel for scaling */ |
1446 | 0 | pixd = pixScale(pix32, scalex, scaley); |
1447 | 0 | pixSetSpp(pix32, spp); /* restore initial value in case it's a clone */ |
1448 | 0 | pixDestroy(&pix32); |
1449 | | |
1450 | | /* Set up alpha layer with a fading border and scale it */ |
1451 | 0 | if (!pixg) { |
1452 | 0 | pixg2 = pixCreate(ws, hs, 8); |
1453 | 0 | if (fract == 1.0) |
1454 | 0 | pixSetAll(pixg2); |
1455 | 0 | else if (fract > 0.0) |
1456 | 0 | pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); |
1457 | 0 | } else { |
1458 | 0 | pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); |
1459 | 0 | } |
1460 | 0 | if (ws > 10 && hs > 10) { /* see note 4 */ |
1461 | 0 | pixSetBorderRingVal(pixg2, 1, |
1462 | 0 | (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); |
1463 | 0 | pixSetBorderRingVal(pixg2, 2, |
1464 | 0 | (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); |
1465 | 0 | } |
1466 | 0 | pixgs = pixScaleGeneral(pixg2, scalex, scaley, 0.0, 0); |
1467 | | |
1468 | | /* Combine into a 4 spp result */ |
1469 | 0 | pixSetRGBComponent(pixd, pixgs, L_ALPHA_CHANNEL); |
1470 | 0 | pixCopyInputFormat(pixd, pixs); |
1471 | |
|
1472 | 0 | pixDestroy(&pixg2); |
1473 | 0 | pixDestroy(&pixgs); |
1474 | 0 | return pixd; |
1475 | 0 | } |
1476 | | |
1477 | | |
1478 | | /* ================================================================ * |
1479 | | * Low level static functions * |
1480 | | * ================================================================ */ |
1481 | | |
1482 | | /*------------------------------------------------------------------* |
1483 | | * Scale-to-gray 2x * |
1484 | | *------------------------------------------------------------------*/ |
1485 | | /*! |
1486 | | * \brief scaleToGray2Low() |
1487 | | * |
1488 | | * \param[in] datad dest data |
1489 | | * \param[in] wd, hd dest width, height |
1490 | | * \param[in] wpld dest words/line |
1491 | | * \param[in] datas src data |
1492 | | * \param[in] wpls src words/line |
1493 | | * \param[in] sumtab made from makeSumTabSG2() |
1494 | | * \param[in] valtab made from makeValTabSG2() |
1495 | | * \return 0 if OK; 1 on error. |
1496 | | * |
1497 | | * <pre> |
1498 | | * Notes: |
1499 | | * (1) The output is processed in sets of 4 output bytes on a row, |
1500 | | * corresponding to 4 2x2 bit-blocks in the input image. |
1501 | | * Two lookup tables are used. The first, sumtab, gets the |
1502 | | * sum of ON pixels in 4 sets of two adjacent bits, |
1503 | | * storing the result in 4 adjacent bytes. After sums from |
1504 | | * two rows have been added, the second table, valtab, |
1505 | | * converts from the sum of ON pixels in the 2x2 block to |
1506 | | * an 8 bpp grayscale value between 0 for 4 bits ON |
1507 | | * and 255 for 0 bits ON. |
1508 | | * </pre> |
1509 | | */ |
1510 | | static void |
1511 | | scaleToGray2Low(l_uint32 *datad, |
1512 | | l_int32 wd, |
1513 | | l_int32 hd, |
1514 | | l_int32 wpld, |
1515 | | l_uint32 *datas, |
1516 | | l_int32 wpls, |
1517 | | l_uint32 *sumtab, |
1518 | | l_uint8 *valtab) |
1519 | 0 | { |
1520 | 0 | l_int32 i, j, l, k, m, wd4, extra; |
1521 | 0 | l_uint32 sbyte1, sbyte2, sum; |
1522 | 0 | l_uint32 *lines, *lined; |
1523 | | |
1524 | | /* i indexes the dest lines |
1525 | | * l indexes the source lines |
1526 | | * j indexes the dest bytes |
1527 | | * k indexes the source bytes |
1528 | | * We take two bytes from the source (in 2 lines of 8 pixels |
1529 | | * each) and convert them into four 8 bpp bytes of the dest. */ |
1530 | 0 | wd4 = wd & 0xfffffffc; |
1531 | 0 | extra = wd - wd4; |
1532 | 0 | for (i = 0, l = 0; i < hd; i++, l += 2) { |
1533 | 0 | lines = datas + l * wpls; |
1534 | 0 | lined = datad + i * wpld; |
1535 | 0 | for (j = 0, k = 0; j < wd4; j += 4, k++) { |
1536 | 0 | sbyte1 = GET_DATA_BYTE(lines, k); |
1537 | 0 | sbyte2 = GET_DATA_BYTE(lines + wpls, k); |
1538 | 0 | sum = sumtab[sbyte1] + sumtab[sbyte2]; |
1539 | 0 | SET_DATA_BYTE(lined, j, valtab[sum >> 24]); |
1540 | 0 | SET_DATA_BYTE(lined, j + 1, valtab[(sum >> 16) & 0xff]); |
1541 | 0 | SET_DATA_BYTE(lined, j + 2, valtab[(sum >> 8) & 0xff]); |
1542 | 0 | SET_DATA_BYTE(lined, j + 3, valtab[sum & 0xff]); |
1543 | 0 | } |
1544 | 0 | if (extra > 0) { |
1545 | 0 | sbyte1 = GET_DATA_BYTE(lines, k); |
1546 | 0 | sbyte2 = GET_DATA_BYTE(lines + wpls, k); |
1547 | 0 | sum = sumtab[sbyte1] + sumtab[sbyte2]; |
1548 | 0 | for (m = 0; m < extra; m++) { |
1549 | 0 | SET_DATA_BYTE(lined, j + m, |
1550 | 0 | valtab[((sum >> (24 - 8 * m)) & 0xff)]); |
1551 | 0 | } |
1552 | 0 | } |
1553 | |
|
1554 | 0 | } |
1555 | 0 | } |
1556 | | |
1557 | | |
1558 | | /*! |
1559 | | * \brief makeSumTabSG2() |
1560 | | * |
1561 | | * <pre> |
1562 | | * Notes: |
1563 | | * (1) Returns a table of 256 l_uint32s, giving the four output |
1564 | | * 8-bit grayscale sums corresponding to 8 input bits of a binary |
1565 | | * image, for a 2x scale-to-gray op. The sums from two |
1566 | | * adjacent scanlines are then added and transformed to |
1567 | | * output four 8 bpp pixel values, using makeValTabSG2(). |
1568 | | * </pre> |
1569 | | */ |
1570 | | static l_uint32 * |
1571 | | makeSumTabSG2(void) |
1572 | 0 | { |
1573 | 0 | l_int32 i; |
1574 | 0 | l_int32 sum[] = {0, 1, 1, 2}; |
1575 | 0 | l_uint32 *tab; |
1576 | | |
1577 | | /* Pack the four sums separately in four bytes */ |
1578 | 0 | tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); |
1579 | 0 | for (i = 0; i < 256; i++) { |
1580 | 0 | tab[i] = (sum[i & 0x3] | sum[(i >> 2) & 0x3] << 8 | |
1581 | 0 | sum[(i >> 4) & 0x3] << 16 | sum[(i >> 6) & 0x3] << 24); |
1582 | 0 | } |
1583 | 0 | return tab; |
1584 | 0 | } |
1585 | | |
1586 | | |
1587 | | /*! |
1588 | | * \brief makeValTabSG2() |
1589 | | * |
1590 | | * <pre> |
1591 | | * Notes: |
1592 | | * (1) Returns an 8 bit value for the sum of ON pixels |
1593 | | * in a 2x2 square, according to |
1594 | | * val = 255 - (255 * sum)/4 |
1595 | | * where sum is in set {0,1,2,3,4} |
1596 | | * </pre> |
1597 | | */ |
1598 | | static l_uint8 * |
1599 | | makeValTabSG2(void) |
1600 | 0 | { |
1601 | 0 | l_int32 i; |
1602 | 0 | l_uint8 *tab; |
1603 | |
|
1604 | 0 | tab = (l_uint8 *)LEPT_CALLOC(5, sizeof(l_uint8)); |
1605 | 0 | for (i = 0; i < 5; i++) |
1606 | 0 | tab[i] = 255 - (i * 255) / 4; |
1607 | 0 | return tab; |
1608 | 0 | } |
1609 | | |
1610 | | |
1611 | | /*------------------------------------------------------------------* |
1612 | | * Scale-to-gray 3x * |
1613 | | *------------------------------------------------------------------*/ |
1614 | | /*! |
1615 | | * \brief scaleToGray3Low() |
1616 | | * |
1617 | | * \param[in] datad dest data |
1618 | | * \param[in] wd, hd dest width, height |
1619 | | * \param[in] wpld dest words/line |
1620 | | * \param[in] datas src data |
1621 | | * \param[in] wpls src words/line |
1622 | | * \param[in] sumtab made from makeSumTabSG3() |
1623 | | * \param[in] valtab made from makeValTabSG3() |
1624 | | * \return 0 if OK; 1 on error |
1625 | | * |
1626 | | * <pre> |
1627 | | * Notes: |
1628 | | * (1) Each set of 8 3x3 bit-blocks in the source image, which |
1629 | | * consist of 72 pixels arranged 24 pixels wide by 3 scanlines, |
1630 | | * is converted to a row of 8 8-bit pixels in the dest image. |
1631 | | * These 72 pixels of the input image are runs of 24 pixels |
1632 | | * in three adjacent scanlines. Each run of 24 pixels is |
1633 | | * stored in the 24 LSbits of a 32-bit word. We use 2 LUTs. |
1634 | | * The first, sumtab, takes 6 of these bits and stores |
1635 | | * sum, taken 3 bits at a time, in two bytes. (See |
1636 | | * makeSumTabSG3). This is done for each of the 3 scanlines, |
1637 | | * and the results are added. We now have the sum of ON pixels |
1638 | | * in the first two 3x3 blocks in two bytes. The valtab LUT |
1639 | | * then converts these values (which go from 0 to 9) to |
1640 | | * grayscale values between between 255 and 0. (See makeValTabSG3). |
1641 | | * This process is repeated for each of the other 3 sets of |
1642 | | * 6x3 input pixels, giving 8 output pixels in total. |
1643 | | * (2) Note: because the input image is processed in groups of |
1644 | | * 24 x 3 pixels, the process clips the input height to |
1645 | | * (h - h % 3) and the input width to (w - w % 24). |
1646 | | * </pre> |
1647 | | */ |
1648 | | static void |
1649 | | scaleToGray3Low(l_uint32 *datad, |
1650 | | l_int32 wd, |
1651 | | l_int32 hd, |
1652 | | l_int32 wpld, |
1653 | | l_uint32 *datas, |
1654 | | l_int32 wpls, |
1655 | | l_uint32 *sumtab, |
1656 | | l_uint8 *valtab) |
1657 | 0 | { |
1658 | 0 | l_int32 i, j, l, k; |
1659 | 0 | l_uint32 threebytes1, threebytes2, threebytes3, sum; |
1660 | 0 | l_uint32 *lines, *lined; |
1661 | | |
1662 | | /* i indexes the dest lines |
1663 | | * l indexes the source lines |
1664 | | * j indexes the dest bytes |
1665 | | * k indexes the source bytes |
1666 | | * We take 9 bytes from the source (72 binary pixels |
1667 | | * in three lines of 24 pixels each) and convert it |
1668 | | * into 8 bytes of the dest (8 8bpp pixels in one line) */ |
1669 | 0 | for (i = 0, l = 0; i < hd; i++, l += 3) { |
1670 | 0 | lines = datas + l * wpls; |
1671 | 0 | lined = datad + i * wpld; |
1672 | 0 | for (j = 0, k = 0; j < wd; j += 8, k += 3) { |
1673 | 0 | threebytes1 = (GET_DATA_BYTE(lines, k) << 16) | |
1674 | 0 | (GET_DATA_BYTE(lines, k + 1) << 8) | |
1675 | 0 | GET_DATA_BYTE(lines, k + 2); |
1676 | 0 | threebytes2 = (GET_DATA_BYTE(lines + wpls, k) << 16) | |
1677 | 0 | (GET_DATA_BYTE(lines + wpls, k + 1) << 8) | |
1678 | 0 | GET_DATA_BYTE(lines + wpls, k + 2); |
1679 | 0 | threebytes3 = (GET_DATA_BYTE(lines + 2 * wpls, k) << 16) | |
1680 | 0 | (GET_DATA_BYTE(lines + 2 * wpls, k + 1) << 8) | |
1681 | 0 | GET_DATA_BYTE(lines + 2 * wpls, k + 2); |
1682 | |
|
1683 | 0 | sum = sumtab[(threebytes1 >> 18)] + |
1684 | 0 | sumtab[(threebytes2 >> 18)] + |
1685 | 0 | sumtab[(threebytes3 >> 18)]; |
1686 | 0 | SET_DATA_BYTE(lined, j, valtab[GET_DATA_BYTE(&sum, 2)]); |
1687 | 0 | SET_DATA_BYTE(lined, j + 1, valtab[GET_DATA_BYTE(&sum, 3)]); |
1688 | |
|
1689 | 0 | sum = sumtab[((threebytes1 >> 12) & 0x3f)] + |
1690 | 0 | sumtab[((threebytes2 >> 12) & 0x3f)] + |
1691 | 0 | sumtab[((threebytes3 >> 12) & 0x3f)]; |
1692 | 0 | SET_DATA_BYTE(lined, j + 2, valtab[GET_DATA_BYTE(&sum, 2)]); |
1693 | 0 | SET_DATA_BYTE(lined, j + 3, valtab[GET_DATA_BYTE(&sum, 3)]); |
1694 | |
|
1695 | 0 | sum = sumtab[((threebytes1 >> 6) & 0x3f)] + |
1696 | 0 | sumtab[((threebytes2 >> 6) & 0x3f)] + |
1697 | 0 | sumtab[((threebytes3 >> 6) & 0x3f)]; |
1698 | 0 | SET_DATA_BYTE(lined, j + 4, valtab[GET_DATA_BYTE(&sum, 2)]); |
1699 | 0 | SET_DATA_BYTE(lined, j + 5, valtab[GET_DATA_BYTE(&sum, 3)]); |
1700 | |
|
1701 | 0 | sum = sumtab[(threebytes1 & 0x3f)] + |
1702 | 0 | sumtab[(threebytes2 & 0x3f)] + |
1703 | 0 | sumtab[(threebytes3 & 0x3f)]; |
1704 | 0 | SET_DATA_BYTE(lined, j + 6, valtab[GET_DATA_BYTE(&sum, 2)]); |
1705 | 0 | SET_DATA_BYTE(lined, j + 7, valtab[GET_DATA_BYTE(&sum, 3)]); |
1706 | 0 | } |
1707 | 0 | } |
1708 | 0 | } |
1709 | | |
1710 | | |
1711 | | |
1712 | | /*! |
1713 | | * \brief makeSumTabSG3() |
1714 | | * |
1715 | | * <pre> |
1716 | | * Notes: |
1717 | | * (1) Returns a table of 64 l_uint32s, giving the two output |
1718 | | * 8-bit grayscale sums corresponding to 6 input bits of a binary |
1719 | | * image, for a 3x scale-to-gray op. In practice, this would |
1720 | | * be used three times (on adjacent scanlines), and the sums would |
1721 | | * be added and then transformed to output 8 bpp pixel values, |
1722 | | * using makeValTabSG3(). |
1723 | | * </pre> |
1724 | | */ |
1725 | | static l_uint32 * |
1726 | | makeSumTabSG3(void) |
1727 | 0 | { |
1728 | 0 | l_int32 i; |
1729 | 0 | l_int32 sum[] = {0, 1, 1, 2, 1, 2, 2, 3}; |
1730 | 0 | l_uint32 *tab; |
1731 | | |
1732 | | /* Pack the two sums separately in two bytes */ |
1733 | 0 | tab = (l_uint32 *)LEPT_CALLOC(64, sizeof(l_uint32)); |
1734 | 0 | for (i = 0; i < 64; i++) { |
1735 | 0 | tab[i] = (sum[i & 0x07]) | (sum[(i >> 3) & 0x07] << 8); |
1736 | 0 | } |
1737 | 0 | return tab; |
1738 | 0 | } |
1739 | | |
1740 | | |
1741 | | /*! |
1742 | | * \brief makeValTabSG3() |
1743 | | * |
1744 | | * <pre> |
1745 | | * Notes: |
1746 | | * (1) Returns an 8 bit value for the sum of ON pixels |
1747 | | * in a 3x3 square, according to |
1748 | | * val = 255 - (255 * sum)/9 |
1749 | | * where sum is in [0,...,9] |
1750 | | * </pre> |
1751 | | */ |
1752 | | static l_uint8 * |
1753 | | makeValTabSG3(void) |
1754 | 0 | { |
1755 | 0 | l_int32 i; |
1756 | 0 | l_uint8 *tab; |
1757 | |
|
1758 | 0 | tab = (l_uint8 *)LEPT_CALLOC(10, sizeof(l_uint8)); |
1759 | 0 | for (i = 0; i < 10; i++) |
1760 | 0 | tab[i] = 0xff - (i * 255) / 9; |
1761 | 0 | return tab; |
1762 | 0 | } |
1763 | | |
1764 | | |
1765 | | /*------------------------------------------------------------------* |
1766 | | * Scale-to-gray 4x * |
1767 | | *------------------------------------------------------------------*/ |
1768 | | /*! |
1769 | | * \brief scaleToGray4Low() |
1770 | | * |
1771 | | * \param[in] datad dest data |
1772 | | * \param[in] wd, hd dest width, height |
1773 | | * \param[in] wpld dest words/line |
1774 | | * \param[in] datas src data |
1775 | | * \param[in] wpls src words/line |
1776 | | * \param[in] sumtab made from makeSumTabSG4() |
1777 | | * \param[in] valtab made from makeValTabSG4() |
1778 | | * \return 0 if OK; 1 on error. |
1779 | | * |
1780 | | * <pre> |
1781 | | * Notes: |
1782 | | * (1) The output is processed in sets of 2 output bytes on a row, |
1783 | | * corresponding to 2 4x4 bit-blocks in the input image. |
1784 | | * Two lookup tables are used. The first, sumtab, gets the |
1785 | | * sum of ON pixels in two sets of four adjacent bits, |
1786 | | * storing the result in 2 adjacent bytes. After sums from |
1787 | | * four rows have been added, the second table, valtab, |
1788 | | * converts from the sum of ON pixels in the 4x4 block to |
1789 | | * an 8 bpp grayscale value between 0 for 16 bits ON |
1790 | | * and 255 for 0 bits ON. |
1791 | | * </pre> |
1792 | | */ |
1793 | | static void |
1794 | | scaleToGray4Low(l_uint32 *datad, |
1795 | | l_int32 wd, |
1796 | | l_int32 hd, |
1797 | | l_int32 wpld, |
1798 | | l_uint32 *datas, |
1799 | | l_int32 wpls, |
1800 | | l_uint32 *sumtab, |
1801 | | l_uint8 *valtab) |
1802 | 0 | { |
1803 | 0 | l_int32 i, j, l, k; |
1804 | 0 | l_uint32 sbyte1, sbyte2, sbyte3, sbyte4, sum; |
1805 | 0 | l_uint32 *lines, *lined; |
1806 | | |
1807 | | /* i indexes the dest lines |
1808 | | * l indexes the source lines |
1809 | | * j indexes the dest bytes |
1810 | | * k indexes the source bytes |
1811 | | * We take four bytes from the source (in 4 lines of 8 pixels |
1812 | | * each) and convert it into two 8 bpp bytes of the dest. */ |
1813 | 0 | for (i = 0, l = 0; i < hd; i++, l += 4) { |
1814 | 0 | lines = datas + l * wpls; |
1815 | 0 | lined = datad + i * wpld; |
1816 | 0 | for (j = 0, k = 0; j < wd; j += 2, k++) { |
1817 | 0 | sbyte1 = GET_DATA_BYTE(lines, k); |
1818 | 0 | sbyte2 = GET_DATA_BYTE(lines + wpls, k); |
1819 | 0 | sbyte3 = GET_DATA_BYTE(lines + 2 * wpls, k); |
1820 | 0 | sbyte4 = GET_DATA_BYTE(lines + 3 * wpls, k); |
1821 | 0 | sum = sumtab[sbyte1] + sumtab[sbyte2] + |
1822 | 0 | sumtab[sbyte3] + sumtab[sbyte4]; |
1823 | 0 | SET_DATA_BYTE(lined, j, valtab[GET_DATA_BYTE(&sum, 2)]); |
1824 | 0 | SET_DATA_BYTE(lined, j + 1, valtab[GET_DATA_BYTE(&sum, 3)]); |
1825 | 0 | } |
1826 | 0 | } |
1827 | 0 | } |
1828 | | |
1829 | | |
1830 | | /*! |
1831 | | * \brief makeSumTabSG4() |
1832 | | * |
1833 | | * <pre> |
1834 | | * Notes: |
1835 | | * (1) Returns a table of 256 l_uint32s, giving the two output |
1836 | | * 8-bit grayscale sums corresponding to 8 input bits of a |
1837 | | * binary image, for a 4x scale-to-gray op. The sums from |
1838 | | * four adjacent scanlines are then added and transformed to |
1839 | | * output 8 bpp pixel values, using makeValTabSG4(). |
1840 | | * </pre> |
1841 | | */ |
1842 | | static l_uint32 * |
1843 | | makeSumTabSG4(void) |
1844 | 0 | { |
1845 | 0 | l_int32 i; |
1846 | 0 | l_int32 sum[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; |
1847 | 0 | l_uint32 *tab; |
1848 | | |
1849 | | /* Pack the two sums separately in two bytes */ |
1850 | 0 | tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); |
1851 | 0 | for (i = 0; i < 256; i++) { |
1852 | 0 | tab[i] = (sum[i & 0xf]) | (sum[(i >> 4) & 0xf] << 8); |
1853 | 0 | } |
1854 | 0 | return tab; |
1855 | 0 | } |
1856 | | |
1857 | | |
1858 | | /*! |
1859 | | * \brief makeValTabSG4() |
1860 | | * |
1861 | | * <pre> |
1862 | | * Notes: |
1863 | | * (1) Returns an 8 bit value for the sum of ON pixels |
1864 | | * in a 4x4 square, according to |
1865 | | * val = 255 - (255 * sum)/16 |
1866 | | * where sum is in [0,...,16] |
1867 | | * </pre> |
1868 | | */ |
1869 | | static l_uint8 * |
1870 | | makeValTabSG4(void) |
1871 | 0 | { |
1872 | 0 | l_int32 i; |
1873 | 0 | l_uint8 *tab; |
1874 | |
|
1875 | 0 | tab = (l_uint8 *)LEPT_CALLOC(17, sizeof(l_uint8)); |
1876 | 0 | for (i = 0; i < 17; i++) |
1877 | 0 | tab[i] = 0xff - (i * 255) / 16; |
1878 | 0 | return tab; |
1879 | 0 | } |
1880 | | |
1881 | | |
1882 | | /*------------------------------------------------------------------* |
1883 | | * Scale-to-gray 6x * |
1884 | | *------------------------------------------------------------------*/ |
1885 | | /*! |
1886 | | * \brief scaleToGray6Low() |
1887 | | * |
1888 | | * \param[in] datad dest data |
1889 | | * \param[in] wd, hd dest width, height |
1890 | | * \param[in] wpld dest words/line |
1891 | | * \param[in] datas src data |
1892 | | * \param[in] wpls src words/line |
1893 | | * \param[in] tab8 made from makePixelSumTab8() |
1894 | | * \param[in] valtab made from makeValTabSG6() |
1895 | | * \return 0 if OK; 1 on error |
1896 | | * |
1897 | | * <pre> |
1898 | | * Notes: |
1899 | | * (1) Each set of 4 6x6 bit-blocks in the source image, which |
1900 | | * consist of 144 pixels arranged 24 pixels wide by 6 scanlines, |
1901 | | * is converted to a row of 4 8-bit pixels in the dest image. |
1902 | | * These 144 pixels of the input image are runs of 24 pixels |
1903 | | * in six adjacent scanlines. Each run of 24 pixels is |
1904 | | * stored in the 24 LSbits of a 32-bit word. We use 2 LUTs. |
1905 | | * The first, tab8, takes 6 of these bits and stores |
1906 | | * sum in one byte. This is done for each of the 6 scanlines, |
1907 | | * and the results are added. |
1908 | | * We now have the sum of ON pixels in the first 6x6 block. The |
1909 | | * valtab LUT then converts these values (which go from 0 to 36) to |
1910 | | * grayscale values between between 255 and 0. (See makeValTabSG6). |
1911 | | * This process is repeated for each of the other 3 sets of |
1912 | | * 6x6 input pixels, giving 4 output pixels in total. |
1913 | | * (2) Note: because the input image is processed in groups of |
1914 | | * 24 x 6 pixels, the process clips the input height to |
1915 | | * (h - h % 6) and the input width to (w - w % 24). |
1916 | | * </pre> |
1917 | | */ |
1918 | | static void |
1919 | | scaleToGray6Low(l_uint32 *datad, |
1920 | | l_int32 wd, |
1921 | | l_int32 hd, |
1922 | | l_int32 wpld, |
1923 | | l_uint32 *datas, |
1924 | | l_int32 wpls, |
1925 | | l_int32 *tab8, |
1926 | | l_uint8 *valtab) |
1927 | 0 | { |
1928 | 0 | l_int32 i, j, l, k; |
1929 | 0 | l_uint32 threebytes1, threebytes2, threebytes3; |
1930 | 0 | l_uint32 threebytes4, threebytes5, threebytes6, sum; |
1931 | 0 | l_uint32 *lines, *lined; |
1932 | | |
1933 | | /* i indexes the dest lines |
1934 | | * l indexes the source lines |
1935 | | * j indexes the dest bytes |
1936 | | * k indexes the source bytes |
1937 | | * We take 18 bytes from the source (144 binary pixels |
1938 | | * in six lines of 24 pixels each) and convert it |
1939 | | * into 4 bytes of the dest (four 8 bpp pixels in one line) */ |
1940 | 0 | for (i = 0, l = 0; i < hd; i++, l += 6) { |
1941 | 0 | lines = datas + l * wpls; |
1942 | 0 | lined = datad + i * wpld; |
1943 | 0 | for (j = 0, k = 0; j < wd; j += 4, k += 3) { |
1944 | | /* First grab the 18 bytes, 3 at a time, and put each set |
1945 | | * of 3 bytes into the LS bytes of a 32-bit word. */ |
1946 | 0 | threebytes1 = (GET_DATA_BYTE(lines, k) << 16) | |
1947 | 0 | (GET_DATA_BYTE(lines, k + 1) << 8) | |
1948 | 0 | GET_DATA_BYTE(lines, k + 2); |
1949 | 0 | threebytes2 = (GET_DATA_BYTE(lines + wpls, k) << 16) | |
1950 | 0 | (GET_DATA_BYTE(lines + wpls, k + 1) << 8) | |
1951 | 0 | GET_DATA_BYTE(lines + wpls, k + 2); |
1952 | 0 | threebytes3 = (GET_DATA_BYTE(lines + 2 * wpls, k) << 16) | |
1953 | 0 | (GET_DATA_BYTE(lines + 2 * wpls, k + 1) << 8) | |
1954 | 0 | GET_DATA_BYTE(lines + 2 * wpls, k + 2); |
1955 | 0 | threebytes4 = (GET_DATA_BYTE(lines + 3 * wpls, k) << 16) | |
1956 | 0 | (GET_DATA_BYTE(lines + 3 * wpls, k + 1) << 8) | |
1957 | 0 | GET_DATA_BYTE(lines + 3 * wpls, k + 2); |
1958 | 0 | threebytes5 = (GET_DATA_BYTE(lines + 4 * wpls, k) << 16) | |
1959 | 0 | (GET_DATA_BYTE(lines + 4 * wpls, k + 1) << 8) | |
1960 | 0 | GET_DATA_BYTE(lines + 4 * wpls, k + 2); |
1961 | 0 | threebytes6 = (GET_DATA_BYTE(lines + 5 * wpls, k) << 16) | |
1962 | 0 | (GET_DATA_BYTE(lines + 5 * wpls, k + 1) << 8) | |
1963 | 0 | GET_DATA_BYTE(lines + 5 * wpls, k + 2); |
1964 | | |
1965 | | /* Sum first set of 36 bits and convert to 0-255 */ |
1966 | 0 | sum = tab8[(threebytes1 >> 18)] + |
1967 | 0 | tab8[(threebytes2 >> 18)] + |
1968 | 0 | tab8[(threebytes3 >> 18)] + |
1969 | 0 | tab8[(threebytes4 >> 18)] + |
1970 | 0 | tab8[(threebytes5 >> 18)] + |
1971 | 0 | tab8[(threebytes6 >> 18)]; |
1972 | 0 | SET_DATA_BYTE(lined, j, valtab[GET_DATA_BYTE(&sum, 3)]); |
1973 | | |
1974 | | /* Ditto for second set */ |
1975 | 0 | sum = tab8[((threebytes1 >> 12) & 0x3f)] + |
1976 | 0 | tab8[((threebytes2 >> 12) & 0x3f)] + |
1977 | 0 | tab8[((threebytes3 >> 12) & 0x3f)] + |
1978 | 0 | tab8[((threebytes4 >> 12) & 0x3f)] + |
1979 | 0 | tab8[((threebytes5 >> 12) & 0x3f)] + |
1980 | 0 | tab8[((threebytes6 >> 12) & 0x3f)]; |
1981 | 0 | SET_DATA_BYTE(lined, j + 1, valtab[GET_DATA_BYTE(&sum, 3)]); |
1982 | |
|
1983 | 0 | sum = tab8[((threebytes1 >> 6) & 0x3f)] + |
1984 | 0 | tab8[((threebytes2 >> 6) & 0x3f)] + |
1985 | 0 | tab8[((threebytes3 >> 6) & 0x3f)] + |
1986 | 0 | tab8[((threebytes4 >> 6) & 0x3f)] + |
1987 | 0 | tab8[((threebytes5 >> 6) & 0x3f)] + |
1988 | 0 | tab8[((threebytes6 >> 6) & 0x3f)]; |
1989 | 0 | SET_DATA_BYTE(lined, j + 2, valtab[GET_DATA_BYTE(&sum, 3)]); |
1990 | |
|
1991 | 0 | sum = tab8[(threebytes1 & 0x3f)] + |
1992 | 0 | tab8[(threebytes2 & 0x3f)] + |
1993 | 0 | tab8[(threebytes3 & 0x3f)] + |
1994 | 0 | tab8[(threebytes4 & 0x3f)] + |
1995 | 0 | tab8[(threebytes5 & 0x3f)] + |
1996 | 0 | tab8[(threebytes6 & 0x3f)]; |
1997 | 0 | SET_DATA_BYTE(lined, j + 3, valtab[GET_DATA_BYTE(&sum, 3)]); |
1998 | 0 | } |
1999 | 0 | } |
2000 | 0 | } |
2001 | | |
2002 | | |
2003 | | /*! |
2004 | | * \brief makeValTabSG6() |
2005 | | * |
2006 | | * <pre> |
2007 | | * Notes: |
2008 | | * (1) Returns an 8 bit value for the sum of ON pixels |
2009 | | * in a 6x6 square, according to |
2010 | | * val = 255 - (255 * sum)/36 |
2011 | | * where sum is in [0,...,36] |
2012 | | * </pre> |
2013 | | */ |
2014 | | static l_uint8 * |
2015 | | makeValTabSG6(void) |
2016 | 0 | { |
2017 | 0 | l_int32 i; |
2018 | 0 | l_uint8 *tab; |
2019 | |
|
2020 | 0 | tab = (l_uint8 *)LEPT_CALLOC(37, sizeof(l_uint8)); |
2021 | 0 | for (i = 0; i < 37; i++) |
2022 | 0 | tab[i] = 0xff - (i * 255) / 36; |
2023 | 0 | return tab; |
2024 | 0 | } |
2025 | | |
2026 | | |
2027 | | /*------------------------------------------------------------------* |
2028 | | * Scale-to-gray 8x * |
2029 | | *------------------------------------------------------------------*/ |
2030 | | /*! |
2031 | | * \brief scaleToGray8Low() |
2032 | | * |
2033 | | * \param[in] datad dest data |
2034 | | * \param[in] wd, hd dest width, height |
2035 | | * \param[in] wpld dest words/line |
2036 | | * \param[in] datas src data |
2037 | | * \param[in] wpls src words/line |
2038 | | * \param[in] tab8 made from makePixelSumTab8() |
2039 | | * \param[in] valtab made from makeValTabSG8() |
2040 | | * \return 0 if OK; 1 on error. |
2041 | | * |
2042 | | * <pre> |
2043 | | * Notes: |
2044 | | * (1) The output is processed one dest byte at a time, |
2045 | | * corresponding to 8 rows of src bytes in the input image. |
2046 | | * Two lookup tables are used. The first, %tab8, gets the |
2047 | | * sum of ON pixels in a byte. After sums from 8 rows have |
2048 | | * been added, the second table, %valtab, converts from this |
2049 | | * value which is between 0 and 64 to an 8 bpp grayscale |
2050 | | * value between 0 and 255: 0 for all 64 bits ON and 255 |
2051 | | * for all 64 bits OFF. |
2052 | | * </pre> |
2053 | | */ |
2054 | | static void |
2055 | | scaleToGray8Low(l_uint32 *datad, |
2056 | | l_int32 wd, |
2057 | | l_int32 hd, |
2058 | | l_int32 wpld, |
2059 | | l_uint32 *datas, |
2060 | | l_int32 wpls, |
2061 | | l_int32 *tab8, |
2062 | | l_uint8 *valtab) |
2063 | 0 | { |
2064 | 0 | l_int32 i, j, k; |
2065 | 0 | l_int32 sbyte0, sbyte1, sbyte2, sbyte3, sbyte4, sbyte5, sbyte6, sbyte7, sum; |
2066 | 0 | l_uint32 *lines, *lined; |
2067 | | |
2068 | | /* i indexes the dest lines |
2069 | | * k indexes the source lines |
2070 | | * j indexes the src and dest bytes |
2071 | | * We take 8 bytes from the source (in 8 lines of 8 pixels |
2072 | | * each) and convert it into one 8 bpp byte of the dest. */ |
2073 | 0 | for (i = 0, k = 0; i < hd; i++, k += 8) { |
2074 | 0 | lines = datas + k * wpls; |
2075 | 0 | lined = datad + i * wpld; |
2076 | 0 | for (j = 0; j < wd; j++) { |
2077 | 0 | sbyte0 = GET_DATA_BYTE(lines, j); |
2078 | 0 | sbyte1 = GET_DATA_BYTE(lines + wpls, j); |
2079 | 0 | sbyte2 = GET_DATA_BYTE(lines + 2 * wpls, j); |
2080 | 0 | sbyte3 = GET_DATA_BYTE(lines + 3 * wpls, j); |
2081 | 0 | sbyte4 = GET_DATA_BYTE(lines + 4 * wpls, j); |
2082 | 0 | sbyte5 = GET_DATA_BYTE(lines + 5 * wpls, j); |
2083 | 0 | sbyte6 = GET_DATA_BYTE(lines + 6 * wpls, j); |
2084 | 0 | sbyte7 = GET_DATA_BYTE(lines + 7 * wpls, j); |
2085 | 0 | sum = tab8[sbyte0] + tab8[sbyte1] + |
2086 | 0 | tab8[sbyte2] + tab8[sbyte3] + |
2087 | 0 | tab8[sbyte4] + tab8[sbyte5] + |
2088 | 0 | tab8[sbyte6] + tab8[sbyte7]; |
2089 | 0 | SET_DATA_BYTE(lined, j, valtab[sum]); |
2090 | 0 | } |
2091 | 0 | } |
2092 | 0 | } |
2093 | | |
2094 | | |
2095 | | /*! |
2096 | | * \brief makeValTabSG8() |
2097 | | * |
2098 | | * <pre> |
2099 | | * Notes: |
2100 | | * (1) Returns an 8 bit value for the sum of ON pixels |
2101 | | * in an 8x8 square, according to |
2102 | | * val = 255 - (255 * sum)/64 |
2103 | | * where sum is in [0,...,64] |
2104 | | * </pre> |
2105 | | */ |
2106 | | static l_uint8 * |
2107 | | makeValTabSG8(void) |
2108 | 0 | { |
2109 | 0 | l_int32 i; |
2110 | 0 | l_uint8 *tab; |
2111 | |
|
2112 | 0 | tab = (l_uint8 *)LEPT_CALLOC(65, sizeof(l_uint8)); |
2113 | 0 | for (i = 0; i < 65; i++) |
2114 | 0 | tab[i] = 0xff - (i * 255) / 64; |
2115 | 0 | return tab; |
2116 | 0 | } |
2117 | | |
2118 | | |
2119 | | /*------------------------------------------------------------------* |
2120 | | * Scale-to-gray 16x * |
2121 | | *------------------------------------------------------------------*/ |
2122 | | /*! |
2123 | | * \brief scaleToGray16Low() |
2124 | | * |
2125 | | * \param[in] datad dest data |
2126 | | * \param[in] wd, hd dest width, height |
2127 | | * \param[in] wpld dest words/line |
2128 | | * \param[in] datas src data |
2129 | | * \param[in] wpls src words/line |
2130 | | * \param[in] tab8 made from makePixelSumTab8() |
2131 | | * \return 0 if OK; 1 on error. |
2132 | | * |
2133 | | * <pre> |
2134 | | * Notes: |
2135 | | * (1) The output is processed one dest byte at a time, corresponding |
2136 | | * to 16 rows consisting each of 2 src bytes in the input image. |
2137 | | * This uses one lookup table, tab8, which gives the sum of |
2138 | | * ON pixels in a byte. After summing for all ON pixels in the |
2139 | | * 32 src bytes, which is between 0 and 256, this is converted |
2140 | | * to an 8 bpp grayscale value between 0 for 255 or 256 bits ON |
2141 | | * and 255 for 0 bits ON. |
2142 | | * </pre> |
2143 | | */ |
2144 | | static void |
2145 | | scaleToGray16Low(l_uint32 *datad, |
2146 | | l_int32 wd, |
2147 | | l_int32 hd, |
2148 | | l_int32 wpld, |
2149 | | l_uint32 *datas, |
2150 | | l_int32 wpls, |
2151 | | l_int32 *tab8) |
2152 | 0 | { |
2153 | 0 | l_int32 i, j, k, m; |
2154 | 0 | l_int32 sum; |
2155 | 0 | l_uint32 *lines, *lined; |
2156 | | |
2157 | | /* i indexes the dest lines |
2158 | | * k indexes the source lines |
2159 | | * j indexes the dest bytes |
2160 | | * m indexes the src bytes |
2161 | | * We take 32 bytes from the source (in 16 lines of 16 pixels |
2162 | | * each) and convert it into one 8 bpp byte of the dest. */ |
2163 | 0 | for (i = 0, k = 0; i < hd; i++, k += 16) { |
2164 | 0 | lines = datas + k * wpls; |
2165 | 0 | lined = datad + i * wpld; |
2166 | 0 | for (j = 0; j < wd; j++) { |
2167 | 0 | m = 2 * j; |
2168 | 0 | sum = tab8[GET_DATA_BYTE(lines, m)]; |
2169 | 0 | sum += tab8[GET_DATA_BYTE(lines, m + 1)]; |
2170 | 0 | sum += tab8[GET_DATA_BYTE(lines + wpls, m)]; |
2171 | 0 | sum += tab8[GET_DATA_BYTE(lines + wpls, m + 1)]; |
2172 | 0 | sum += tab8[GET_DATA_BYTE(lines + 2 * wpls, m)]; |
2173 | 0 | sum += tab8[GET_DATA_BYTE(lines + 2 * wpls, m + 1)]; |
2174 | 0 | sum += tab8[GET_DATA_BYTE(lines + 3 * wpls, m)]; |
2175 | 0 | sum += tab8[GET_DATA_BYTE(lines + 3 * wpls, m + 1)]; |
2176 | 0 | sum += tab8[GET_DATA_BYTE(lines + 4 * wpls, m)]; |
2177 | 0 | sum += tab8[GET_DATA_BYTE(lines + 4 * wpls, m + 1)]; |
2178 | 0 | sum += tab8[GET_DATA_BYTE(lines + 5 * wpls, m)]; |
2179 | 0 | sum += tab8[GET_DATA_BYTE(lines + 5 * wpls, m + 1)]; |
2180 | 0 | sum += tab8[GET_DATA_BYTE(lines + 6 * wpls, m)]; |
2181 | 0 | sum += tab8[GET_DATA_BYTE(lines + 6 * wpls, m + 1)]; |
2182 | 0 | sum += tab8[GET_DATA_BYTE(lines + 7 * wpls, m)]; |
2183 | 0 | sum += tab8[GET_DATA_BYTE(lines + 7 * wpls, m + 1)]; |
2184 | 0 | sum += tab8[GET_DATA_BYTE(lines + 8 * wpls, m)]; |
2185 | 0 | sum += tab8[GET_DATA_BYTE(lines + 8 * wpls, m + 1)]; |
2186 | 0 | sum += tab8[GET_DATA_BYTE(lines + 9 * wpls, m)]; |
2187 | 0 | sum += tab8[GET_DATA_BYTE(lines + 9 * wpls, m + 1)]; |
2188 | 0 | sum += tab8[GET_DATA_BYTE(lines + 10 * wpls, m)]; |
2189 | 0 | sum += tab8[GET_DATA_BYTE(lines + 10 * wpls, m + 1)]; |
2190 | 0 | sum += tab8[GET_DATA_BYTE(lines + 11 * wpls, m)]; |
2191 | 0 | sum += tab8[GET_DATA_BYTE(lines + 11 * wpls, m + 1)]; |
2192 | 0 | sum += tab8[GET_DATA_BYTE(lines + 12 * wpls, m)]; |
2193 | 0 | sum += tab8[GET_DATA_BYTE(lines + 12 * wpls, m + 1)]; |
2194 | 0 | sum += tab8[GET_DATA_BYTE(lines + 13 * wpls, m)]; |
2195 | 0 | sum += tab8[GET_DATA_BYTE(lines + 13 * wpls, m + 1)]; |
2196 | 0 | sum += tab8[GET_DATA_BYTE(lines + 14 * wpls, m)]; |
2197 | 0 | sum += tab8[GET_DATA_BYTE(lines + 14 * wpls, m + 1)]; |
2198 | 0 | sum += tab8[GET_DATA_BYTE(lines + 15 * wpls, m)]; |
2199 | 0 | sum += tab8[GET_DATA_BYTE(lines + 15 * wpls, m + 1)]; |
2200 | 0 | sum = L_MIN(sum, 255); |
2201 | 0 | SET_DATA_BYTE(lined, j, 255 - sum); |
2202 | 0 | } |
2203 | 0 | } |
2204 | 0 | } |
2205 | | |
2206 | | |
2207 | | |
2208 | | /*------------------------------------------------------------------* |
2209 | | * Grayscale mipmap * |
2210 | | *------------------------------------------------------------------*/ |
2211 | | /*! |
2212 | | * \brief scaleMipmapLow() |
2213 | | * |
2214 | | * <pre> |
2215 | | * Notes: |
2216 | | * (1) See notes in scale.c for pixScaleToGrayMipmap(). This function |
2217 | | * is here for pedagogical reasons. It gives poor results on document |
2218 | | * images because of aliasing. |
2219 | | * </pre> |
2220 | | */ |
2221 | | static l_int32 |
2222 | | scaleMipmapLow(l_uint32 *datad, |
2223 | | l_int32 wd, |
2224 | | l_int32 hd, |
2225 | | l_int32 wpld, |
2226 | | l_uint32 *datas1, |
2227 | | l_int32 wpls1, |
2228 | | l_uint32 *datas2, |
2229 | | l_int32 wpls2, |
2230 | | l_float32 red) |
2231 | 0 | { |
2232 | 0 | l_int32 i, j, val1, val2, val, row2, col2; |
2233 | 0 | l_int32 *srow, *scol; |
2234 | 0 | l_uint32 *lines1, *lines2, *lined; |
2235 | 0 | l_float32 ratio, w1, w2; |
2236 | | |
2237 | | /* Clear dest */ |
2238 | 0 | memset(datad, 0, 4LL * wpld * hd); |
2239 | | |
2240 | | /* Each dest pixel at (j,i) is computed by interpolating |
2241 | | between the two src images at the corresponding location. |
2242 | | We store the UL corner locations of the square of |
2243 | | src pixels in thelower-resolution image that correspond |
2244 | | to dest pixel (j,i). The are labeled by the arrays |
2245 | | srow[i], scol[j]. The UL corner locations of the higher |
2246 | | resolution src pixels are obtained from these arrays |
2247 | | by multiplying by 2. */ |
2248 | 0 | if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) |
2249 | 0 | return ERROR_INT("srow not made", __func__, 1); |
2250 | 0 | if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { |
2251 | 0 | LEPT_FREE(srow); |
2252 | 0 | return ERROR_INT("scol not made", __func__, 1); |
2253 | 0 | } |
2254 | 0 | ratio = 1.f / (2.f * red); /* 0.5 for red = 1, 1 for red = 0.5 */ |
2255 | 0 | for (i = 0; i < hd; i++) |
2256 | 0 | srow[i] = (l_int32)(ratio * i); |
2257 | 0 | for (j = 0; j < wd; j++) |
2258 | 0 | scol[j] = (l_int32)(ratio * j); |
2259 | | |
2260 | | /* Get weights for linear interpolation: these are the |
2261 | | * 'distances' of the dest image plane from the two |
2262 | | * src image planes. */ |
2263 | 0 | w1 = 2.f * red - 1.f; /* w1 --> 1 as red --> 1 */ |
2264 | 0 | w2 = 1.f - w1; |
2265 | | |
2266 | | /* For each dest pixel, compute linear interpolation */ |
2267 | 0 | for (i = 0; i < hd; i++) { |
2268 | 0 | row2 = srow[i]; |
2269 | 0 | lines1 = datas1 + 2 * row2 * wpls1; |
2270 | 0 | lines2 = datas2 + row2 * wpls2; |
2271 | 0 | lined = datad + i * wpld; |
2272 | 0 | for (j = 0; j < wd; j++) { |
2273 | 0 | col2 = scol[j]; |
2274 | 0 | val1 = GET_DATA_BYTE(lines1, 2 * col2); |
2275 | 0 | val2 = GET_DATA_BYTE(lines2, col2); |
2276 | 0 | val = (l_int32)(w1 * val1 + w2 * val2); |
2277 | 0 | SET_DATA_BYTE(lined, j, val); |
2278 | 0 | } |
2279 | 0 | } |
2280 | |
|
2281 | 0 | LEPT_FREE(srow); |
2282 | 0 | LEPT_FREE(scol); |
2283 | 0 | return 0; |
2284 | 0 | } |