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

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27 /*!
28 * \file pixtiling.c
29 * <pre>
30 *
31 * PIXTILING *pixTilingCreate()
32 * void *pixTilingDestroy()
33 * l_int32 pixTilingGetCount()
34 * l_int32 pixTilingGetSize()
35 * PIX *pixTilingGetTile()
36 * l_int32 pixTilingNoStripOnPaint()
37 * l_int32 pixTilingPaintTile()
38 *
39 * This provides a simple way to split an image into tiles
40 * and to perform operations independently on each tile.
41 *
42 * The tile created with pixTilingGetTile() can have pixels in
43 * adjacent tiles for computation. The number of extra pixels
44 * on each side of the tile is given by an 'overlap' parameter
45 * to pixTilingCreate(). For tiles at the boundary of
46 * the input image, quasi-overlap pixels are created by reflection
47 * symmetry into the tile.
48 *
49 * Here's a typical intended usage. Suppose you want to parallelize
50 * the operation on an image, by operating on tiles. For each
51 * tile, you want to generate an in-place image result at the same
52 * resolution. Suppose you choose a one-dimensional vertical tiling,
53 * where the desired tile width is 256 pixels and the overlap is
54 * 30 pixels on left and right sides:
55 *
56 * PIX *pixd = pixCreateTemplate(pixs); // output
57 * PIXTILING *pt = pixTilingCreate(pixs, 0, 1, 256, 30, 0);
58 * pixTilingGetCount(pt, &nx, NULL);
59 * for (j = 0; j < nx; j++) {
60 * PIX *pixt = pixTilingGetTile(pt, 0, j);
61 * SomeInPlaceOperation(pixt, 30, 0, ...);
62 * pixTilingPaintTile(pixd, 0, j, pixt, pt);
63 * pixDestroy(&pixt);
64 * }
65 *
66 * In this example, note the following:
67 * ~ The unspecfified in-place operation could instead generate
68 * a new pix. If this is done, the resulting pix must be the
69 * same size as pixt, because pixTilingPaintTile() makes that
70 * assumption, removing the overlap pixels before painting
71 * into the destination.
72 * ~ The 'overlap' parameters have been included in your function,
73 * to indicate which pixels are not in the exterior overlap region.
74 * You will need to change only pixels that are not in the overlap
75 * region, because those are the pixels that will be painted
76 * into the destination.
77 * ~ For tiles on the outside of the image, mirrored pixels are
78 * added to substitute for the overlap that is added to interior
79 * tiles. This allows you to implement your function without
80 * reference to which tile it is; no special coding is necessary
81 * for pixels that are near the image boundary.
82 * ~ The tiles are labeled by (i, j) = (row, column),
83 * and in this example there is one row and nx columns.
84 * </pre>
85 */
86
87 #ifdef HAVE_CONFIG_H
88 #include <config_auto.h>
89 #endif /* HAVE_CONFIG_H */
90
91 #include "allheaders.h"
92 #include "pix_internal.h"
93
94 /*!
95 * \brief pixTilingCreate()
96 *
97 * \param[in] pixs pix to be tiled; any depth; colormap OK
98 * \param[in] nx number of tiles across image
99 * \param[in] ny number of tiles down image
100 * \param[in] w desired width of each tile
101 * \param[in] h desired height of each tile
102 * \param[in] xoverlap overlap into neighboring tiles on each side
103 * \param[in] yoverlap overlap into neighboring tiles above and below
104 * \return pixtiling, or NULL on error
105 *
106 * <pre>
107 * Notes:
108 * (1) We put a clone of pixs in the PixTiling.
109 * (2) The input to pixTilingCreate() for horizontal tiling can be
110 * either the number of tiles across the image or the approximate
111 * width of the tiles. If the latter, the actual width will be
112 * determined by making all tiles but the last of equal width, and
113 * making the last as close to the others as possible. The same
114 * consideration is applied independently to the vertical tiling.
115 * To specify tile width, set nx = 0; to specify the number of
116 * tiles horizontally across the image, set w = 0.
117 * (3) If pixs is to be tiled in one-dimensional strips, use ny = 1 for
118 * vertical strips and nx = 1 for horizontal strips.
119 * (4) The overlap must not be larger than the width or height of
120 * the leftmost or topmost tile(s).
121 * </pre>
122 */
123 PIXTILING *
124 pixTilingCreate(PIX *pixs,
125 l_int32 nx,
126 l_int32 ny,
127 l_int32 w,
128 l_int32 h,
129 l_int32 xoverlap,
130 l_int32 yoverlap)
131 {
132 l_int32 width, height;
133 PIXTILING *pt;
134
135 if (!pixs)
136 return (PIXTILING *)ERROR_PTR("pixs not defined", __func__, NULL);
137 if (nx < 1 && w < 1)
138 return (PIXTILING *)ERROR_PTR("invalid width spec", __func__, NULL);
139 if (ny < 1 && h < 1)
140 return (PIXTILING *)ERROR_PTR("invalid height spec", __func__, NULL);
141
142 /* Find the tile width and number of tiles. All tiles except the
143 * rightmost ones have the same width. The width of the
144 * rightmost ones are at least the width of the others and
145 * less than twice that width. Ditto for tile height. */
146 pixGetDimensions(pixs, &width, &height, NULL);
147 if (nx == 0)
148 nx = L_MAX(1, width / w);
149 w = width / nx; /* possibly reset */
150 if (ny == 0)
151 ny = L_MAX(1, height / h);
152 h = height / ny; /* possibly reset */
153 if (xoverlap > w || yoverlap > h) {
154 L_INFO("tile width = %d, tile height = %d\n", __func__, w, h);
155 return (PIXTILING *)ERROR_PTR("overlap too large", __func__, NULL);
156 }
157
158 pt = (PIXTILING *)LEPT_CALLOC(1, sizeof(PIXTILING));
159 pt->pix = pixClone(pixs);
160 pt->xoverlap = xoverlap;
161 pt->yoverlap = yoverlap;
162 pt->nx = nx;
163 pt->ny = ny;
164 pt->w = w;
165 pt->h = h;
166 pt->strip = TRUE;
167 return pt;
168 }
169
170
171 /*!
172 * \brief pixTilingDestroy()
173 *
174 * \param[in,out] ppt will be set to null before returning
175 * \return void
176 */
177 void
178 pixTilingDestroy(PIXTILING **ppt)
179 {
180 PIXTILING *pt;
181
182 if (ppt == NULL) {
183 L_WARNING("ptr address is null!\n", __func__);
184 return;
185 }
186
187 if ((pt = *ppt) == NULL)
188 return;
189
190 pixDestroy(&pt->pix);
191 LEPT_FREE(pt);
192 *ppt = NULL;
193 }
194
195
196 /*!
197 * \brief pixTilingGetCount()
198 *
199 * \param[in] pt pixtiling
200 * \param[out] pnx [optional] nx; can be null
201 * \param[out] pny [optional] ny; can be null
202 * \return 0 if OK, 1 on error
203 */
204 l_ok
205 pixTilingGetCount(PIXTILING *pt,
206 l_int32 *pnx,
207 l_int32 *pny)
208 {
209 if (!pt)
210 return ERROR_INT("pt not defined", __func__, 1);
211 if (pnx) *pnx = pt->nx;
212 if (pny) *pny = pt->ny;
213 return 0;
214 }
215
216
217 /*!
218 * \brief pixTilingGetSize()
219 *
220 * \param[in] pt pixtiling
221 * \param[out] pw [optional] tile width; can be null
222 * \param[out] ph [optional] tile height; can be null
223 * \return 0 if OK, 1 on error
224 */
225 l_ok
226 pixTilingGetSize(PIXTILING *pt,
227 l_int32 *pw,
228 l_int32 *ph)
229 {
230 if (!pt)
231 return ERROR_INT("pt not defined", __func__, 1);
232 if (pw) *pw = pt->w;
233 if (ph) *ph = pt->h;
234 return 0;
235 }
236
237
238 /*!
239 * \brief pixTilingGetTile()
240 *
241 * \param[in] pt pixtiling
242 * \param[in] i tile row index
243 * \param[in] j tile column index
244 * \return pixd tile with appropriate boundary (overlap) pixels added,
245 * or NULL on error
246 */
247 PIX *
248 pixTilingGetTile(PIXTILING *pt,
249 l_int32 i,
250 l_int32 j)
251 {
252 l_int32 wpix, hpix, wt, ht, nx, ny;
253 l_int32 xoverlap, yoverlap, wtlast, htlast;
254 l_int32 left, top, xtraleft, xtraright, xtratop, xtrabot, width, height;
255 BOX *box;
256 PIX *pixs, *pixt, *pixd;
257
258 if (!pt)
259 return (PIX *)ERROR_PTR("pt not defined", __func__, NULL);
260 if ((pixs = pt->pix) == NULL)
261 return (PIX *)ERROR_PTR("pix not found", __func__, NULL);
262 pixTilingGetCount(pt, &nx, &ny);
263 if (i < 0 || i >= ny)
264 return (PIX *)ERROR_PTR("invalid row index i", __func__, NULL);
265 if (j < 0 || j >= nx)
266 return (PIX *)ERROR_PTR("invalid column index j", __func__, NULL);
267
268 /* Grab the tile with as much overlap as exists within the
269 * input pix. First, compute the (left, top) coordinates. */
270 pixGetDimensions(pixs, &wpix, &hpix, NULL);
271 pixTilingGetSize(pt, &wt, &ht);
272 xoverlap = pt->xoverlap;
273 yoverlap = pt->yoverlap;
274 wtlast = wpix - wt * (nx - 1);
275 htlast = hpix - ht * (ny - 1);
276 left = L_MAX(0, j * wt - xoverlap);
277 top = L_MAX(0, i * ht - yoverlap);
278
279 /* Get the width and height of the tile, including whatever
280 * overlap is available. */
281 if (nx == 1)
282 width = wpix;
283 else if (j == 0)
284 width = wt + xoverlap;
285 else if (j == nx - 1)
286 width = wtlast + xoverlap;
287 else
288 width = wt + 2 * xoverlap;
289
290 if (ny == 1)
291 height = hpix;
292 else if (i == 0)
293 height = ht + yoverlap;
294 else if (i == ny - 1)
295 height = htlast + yoverlap;
296 else
297 height = ht + 2 * yoverlap;
298 box = boxCreate(left, top, width, height);
299 pixt = pixClipRectangle(pixs, box, NULL);
300 boxDestroy(&box);
301
302 /* If no overlap, do not add any special case borders */
303 if (xoverlap == 0 && yoverlap == 0)
304 return pixt;
305
306 /* Add overlap as a mirrored border, in the 8 special cases where
307 * the tile touches the border of the input pix. The xtratop (etc)
308 * parameters are required where the tile is either full width
309 * or full height. */
310 xtratop = xtrabot = xtraleft = xtraright = 0;
311 if (nx == 1)
312 xtraleft = xtraright = xoverlap;
313 if (ny == 1)
314 xtratop = xtrabot = yoverlap;
315 if (i == 0 && j == 0)
316 pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright,
317 yoverlap, xtrabot);
318 else if (i == 0 && j == nx - 1)
319 pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap,
320 yoverlap, xtrabot);
321 else if (i == ny - 1 && j == 0)
322 pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright,
323 xtratop, yoverlap);
324 else if (i == ny - 1 && j == nx - 1)
325 pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap,
326 xtratop, yoverlap);
327 else if (i == 0)
328 pixd = pixAddMirroredBorder(pixt, 0, 0, yoverlap, xtrabot);
329 else if (i == ny - 1)
330 pixd = pixAddMirroredBorder(pixt, 0, 0, xtratop, yoverlap);
331 else if (j == 0)
332 pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, 0, 0);
333 else if (j == nx - 1)
334 pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, 0, 0);
335 else
336 pixd = pixClone(pixt);
337 pixDestroy(&pixt);
338
339 return pixd;
340 }
341
342
343 /*!
344 * \brief pixTilingNoStripOnPaint()
345 *
346 * \param[in] pt pixtiling
347 * \return 0 if OK, 1 on error
348 *
349 * <pre>
350 * Notes:
351 * (1) The default for paint is to strip out the overlap pixels
352 * that are added by pixTilingGetTile(). However, some
353 * operations will generate an image with these pixels
354 * stripped off. This tells the paint operation not
355 * to strip the added boundary pixels when painting.
356 * </pre>
357 */
358 l_ok
359 pixTilingNoStripOnPaint(PIXTILING *pt)
360 {
361 if (!pt)
362 return ERROR_INT("pt not defined", __func__, 1);
363 pt->strip = FALSE;
364 return 0;
365 }
366
367
368 /*!
369 * \brief pixTilingPaintTile()
370 *
371 * \param[in] pixd dest: paint tile onto this, without overlap
372 * \param[in] i tile row index
373 * \param[in] j tile column index
374 * \param[in] pixs source: tile to be painted from
375 * \param[in] pt pixtiling struct
376 * \return 0 if OK, 1 on error
377 */
378 l_ok
379 pixTilingPaintTile(PIX *pixd,
380 l_int32 i,
381 l_int32 j,
382 PIX *pixs,
383 PIXTILING *pt)
384 {
385 l_int32 w, h;
386
387 if (!pixd)
388 return ERROR_INT("pixd not defined", __func__, 1);
389 if (!pixs)
390 return ERROR_INT("pixs not defined", __func__, 1);
391 if (!pt)
392 return ERROR_INT("pt not defined", __func__, 1);
393 if (i < 0 || i >= pt->ny)
394 return ERROR_INT("invalid row index i", __func__, 1);
395 if (j < 0 || j >= pt->nx)
396 return ERROR_INT("invalid column index j", __func__, 1);
397
398 /* Strip added border pixels off if requested */
399 pixGetDimensions(pixs, &w, &h, NULL);
400 if (pt->strip == TRUE) {
401 pixRasterop(pixd, j * pt->w, i * pt->h,
402 w - 2 * pt->xoverlap, h - 2 * pt->yoverlap, PIX_SRC,
403 pixs, pt->xoverlap, pt->yoverlap);
404 } else {
405 pixRasterop(pixd, j * pt->w, i * pt->h, w, h, PIX_SRC, pixs, 0, 0);
406 }
407
408 return 0;
409 }