text: /*====================================================================*
   -  Copyright (C) 2001 Leptonica.  All rights reserved.
   -
   -  Redistribution and use in source and binary forms, with or without
   -  modification, are permitted provided that the following conditions
   -  are met:
   -  1. Redistributions of source code must retain the above copyright
   -     notice, this list of conditions and the following disclaimer.
   -  2. Redistributions in binary form must reproduce the above
   -     copyright notice, this list of conditions and the following
   -     disclaimer in the documentation and/or other materials
   -     provided with the distribution.
   -
   -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
   -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *====================================================================*/
  
  
  /*!
   * \file  pixtiling.c
   * <pre>
   *
   *        PIXTILING       *pixTilingCreate()
   *        void            *pixTilingDestroy()
   *        l_int32          pixTilingGetCount()
   *        l_int32          pixTilingGetSize()
   *        PIX             *pixTilingGetTile()
   *        l_int32          pixTilingNoStripOnPaint()
   *        l_int32          pixTilingPaintTile()
   *
   *   This provides a simple way to split an image into tiles
   *   and to perform operations independently on each tile.
   *
   *   The tile created with pixTilingGetTile() can have pixels in
   *   adjacent tiles for computation.  The number of extra pixels
   *   on each side of the tile is given by an 'overlap' parameter
   *   to pixTilingCreate().  For tiles at the boundary of
   *   the input image, quasi-overlap pixels are created by reflection
   *   symmetry into the tile.
   *
   *   Here's a typical intended usage.  Suppose you want to parallelize
   *   the operation on an image, by operating on tiles.  For each
   *   tile, you want to generate an in-place image result at the same
   *   resolution.  Suppose you choose a one-dimensional vertical tiling,
   *   where the desired tile width is 256 pixels and the overlap is
   *   30 pixels on left and right sides:
   *
   *     PIX *pixd = pixCreateTemplate(pixs);  // output
   *     PIXTILING  *pt = pixTilingCreate(pixs, 0, 1, 256, 30, 0);
   *     pixTilingGetCount(pt, &nx, NULL);
   *     for (j = 0; j < nx; j++) {
   *         PIX *pixt = pixTilingGetTile(pt, 0, j);
   *         SomeInPlaceOperation(pixt, 30, 0, ...);
   *         pixTilingPaintTile(pixd, 0, j, pixt, pt);
   *         pixDestroy(&pixt);
   *     }
   *
   *   In this example, note the following:
   *    ~ The unspecfified in-place operation could instead generate
   *      a new pix.  If this is done, the resulting pix must be the
   *      same size as pixt, because pixTilingPaintTile() makes that
   *      assumption, removing the overlap pixels before painting
   *      into the destination.
   *    ~ The 'overlap' parameters have been included in your function,
   *      to indicate which pixels are not in the exterior overlap region.
   *      You will need to change only pixels that are not in the overlap
   *      region, because those are the pixels that will be painted
   *      into the destination.
   *    ~ For tiles on the outside of the image, mirrored pixels are
   *      added to substitute for the overlap that is added to interior
   *      tiles.  This allows you to implement your function without
   *      reference to which tile it is; no special coding is necessary
   *      for pixels that are near the image boundary.
   *    ~ The tiles are labeled by (i, j) = (row, column),
   *      and in this example there is one row and nx columns.
   * </pre>
   */
  
  #include "allheaders.h"
  
  
  /*!
   * \brief   pixTilingCreate()
   *
   * \param[in]    pixs  pix to be tiled; any depth; colormap OK
   * \param[in]    nx    number of tiles across image
   * \param[in]    ny    number of tiles down image
   * \param[in]    w     desired width of each tile
   * \param[in]    h     desired height of each tile
   * \param[in]    xoverlap overlap into neighboring tiles on each side
   * \param[in]    yoverlap overlap into neighboring tiles above and below
   * \return  pixtiling, or NULL on error
   *
   * <pre>
   * Notes:
   *      (1) We put a clone of pixs in the PixTiling.
   *      (2) The input to pixTilingCreate() for horizontal tiling can be
   *          either the number of tiles across the image or the approximate
   *          width of the tiles.  If the latter, the actual width will be
   *          determined by making all tiles but the last of equal width, and
   *          making the last as close to the others as possible.  The same
   *          consideration is applied independently to the vertical tiling.
   *          To specify tile width, set nx = 0; to specify the number of
   *          tiles horizontally across the image, set w = 0.
   *      (3) If pixs is to be tiled in one-dimensional strips, use ny = 1 for
   *          vertical strips and nx = 1 for horizontal strips.
   *      (4) The overlap must not be larger than the width or height of
   *          the leftmost or topmost tile(s).
   * </pre>
   */
  PIXTILING *
  pixTilingCreate(PIX     *pixs,
                  l_int32  nx,
                  l_int32  ny,
                  l_int32  w,
                  l_int32  h,
                  l_int32  xoverlap,
                  l_int32  yoverlap)
  {
  l_int32     width, height;
  PIXTILING  *pt;
  
      PROCNAME("pixTilingCreate");
  
      if (!pixs)
          return (PIXTILING *)ERROR_PTR("pixs not defined", procName, NULL);
      if (nx < 1 && w < 1)
          return (PIXTILING *)ERROR_PTR("invalid width spec", procName, NULL);
      if (ny < 1 && h < 1)
          return (PIXTILING *)ERROR_PTR("invalid height spec", procName, NULL);
  
          /* Find the tile width and number of tiles.  All tiles except the
           * rightmost ones have the same width.  The width of the
           * rightmost ones are at least the width of the others and
           * less than twice that width.  Ditto for tile height. */
      pixGetDimensions(pixs, &width, &height, NULL);
      if (nx == 0)
          nx = L_MAX(1, width / w);
      w = width / nx;  /* possibly reset */
      if (ny == 0)
          ny = L_MAX(1, height / h);
      h = height / ny;  /* possibly reset */
      if (xoverlap > w || yoverlap > h) {
          L_INFO("tile width = %d, tile height = %d\n", procName, w, h);
          return (PIXTILING *)ERROR_PTR("overlap too large", procName, NULL);
      }
  
      if ((pt = (PIXTILING *)LEPT_CALLOC(1, sizeof(PIXTILING))) == NULL)
          return (PIXTILING *)ERROR_PTR("pt not made", procName, NULL);
      pt->pix = pixClone(pixs);
      pt->xoverlap = xoverlap;
      pt->yoverlap = yoverlap;
      pt->nx = nx;
      pt->ny = ny;
      pt->w = w;
      pt->h = h;
      pt->strip = TRUE;
      return pt;
  }
  
  
  /*!
   * \brief   pixTilingDestroy()
   *
   * \param[in,out]   ppt will be set to null before returning
   * \return  void
   */
  void
  pixTilingDestroy(PIXTILING  **ppt)
  {
  PIXTILING  *pt;
  
      PROCNAME("pixTilingDestroy");
  
      if (ppt == NULL) {
          L_WARNING("ptr address is null!\n", procName);
          return;
      }
  
      if ((pt = *ppt) == NULL)
          return;
  
      pixDestroy(&pt->pix);
      LEPT_FREE(pt);
      *ppt = NULL;
      return;
  }
  
  
  /*!
   * \brief   pixTilingGetCount()
   *
   * \param[in]    pt pixtiling
   * \param[out]   pnx [optional] nx; can be null
   * \param[out]   pny [optional] ny; can be null
   * \return  0 if OK, 1 on error
   */
  l_ok
  pixTilingGetCount(PIXTILING  *pt,
                    l_int32    *pnx,
                    l_int32    *pny)
  {
      PROCNAME("pixTilingGetCount");
  
      if (!pt)
          return ERROR_INT("pt not defined", procName, 1);
      if (pnx) *pnx = pt->nx;
      if (pny) *pny = pt->ny;
      return 0;
  }
  
  
  /*!
   * \brief   pixTilingGetSize()
   *
   * \param[in]    pt pixtiling
   * \param[out]   pw [optional] tile width; can be null
   * \param[out]   ph [optional] tile height; can be null
   * \return  0 if OK, 1 on error
   */
  l_ok
  pixTilingGetSize(PIXTILING  *pt,
                   l_int32    *pw,
                   l_int32    *ph)
  {
      PROCNAME("pixTilingGetSize");
  
      if (!pt)
          return ERROR_INT("pt not defined", procName, 1);
      if (pw) *pw = pt->w;
      if (ph) *ph = pt->h;
      return 0;
  }
  
  
  /*!
   * \brief   pixTilingGetTile()
   *
   * \param[in]    pt pixtiling
   * \param[in]    i tile row index
   * \param[in]    j tile column index
   * \return  pixd tile with appropriate boundary (overlap) pixels added,
   *                    or NULL on error
   */
  PIX *
  pixTilingGetTile(PIXTILING  *pt,
                   l_int32     i,
                   l_int32     j)
  {
  l_int32  wpix, hpix, wt, ht, nx, ny;
  l_int32  xoverlap, yoverlap, wtlast, htlast;
  l_int32  left, top, xtraleft, xtraright, xtratop, xtrabot, width, height;
  BOX     *box;
  PIX     *pixs, *pixt, *pixd;
  
      PROCNAME("pixTilingGetTile");
  
      if (!pt)
          return (PIX *)ERROR_PTR("pt not defined", procName, NULL);
      if ((pixs = pt->pix) == NULL)
          return (PIX *)ERROR_PTR("pix not found", procName, NULL);
      pixTilingGetCount(pt, &nx, &ny);
      if (i < 0 || i >= ny)
          return (PIX *)ERROR_PTR("invalid row index i", procName, NULL);
      if (j < 0 || j >= nx)
          return (PIX *)ERROR_PTR("invalid column index j", procName, NULL);
  
          /* Grab the tile with as much overlap as exists within the
           * input pix.   First, compute the (left, top) coordinates.  */
      pixGetDimensions(pixs, &wpix, &hpix, NULL);
      pixTilingGetSize(pt, &wt, &ht);
      xoverlap = pt->xoverlap;
      yoverlap = pt->yoverlap;
      wtlast = wpix - wt * (nx - 1);
      htlast = hpix - ht * (ny - 1);
      left = L_MAX(0, j * wt - xoverlap);
      top = L_MAX(0, i * ht - yoverlap);
  
          /* Get the width and height of the tile, including whatever
           * overlap is available. */
      if (nx == 1)
          width = wpix;
      else if (j == 0)
          width = wt + xoverlap;
      else if (j == nx - 1)
          width = wtlast + xoverlap;
      else
          width = wt + 2 * xoverlap;
  
      if (ny == 1)
          height = hpix;
      else if (i == 0)
          height = ht + yoverlap;
      else if (i == ny - 1)
          height = htlast + yoverlap;
      else
          height = ht + 2 * yoverlap;
      box = boxCreate(left, top, width, height);
      pixt = pixClipRectangle(pixs, box, NULL);
      boxDestroy(&box);
  
          /* If no overlap, do not add any special case borders */
      if (xoverlap == 0 && yoverlap == 0)
          return pixt;
  
          /* Add overlap as a mirrored border, in the 8 special cases where
           * the tile touches the border of the input pix.  The xtratop (etc)
           * parameters are required where the tile is either full width
           * or full height.  */
      xtratop = xtrabot = xtraleft = xtraright = 0;
      if (nx == 1)
          xtraleft = xtraright = xoverlap;
      if (ny == 1)
          xtratop = xtrabot = yoverlap;
      if (i == 0 && j == 0)
          pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright,
                                      yoverlap, xtrabot);
      else if (i == 0 && j == nx - 1)
          pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap,
                                      yoverlap, xtrabot);
      else if (i == ny - 1 && j == 0)
          pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright,
                                      xtratop, yoverlap);
      else if (i == ny - 1 && j == nx - 1)
          pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap,
                                      xtratop, yoverlap);
      else if (i == 0)
          pixd = pixAddMirroredBorder(pixt, 0, 0, yoverlap, xtrabot);
      else if (i == ny - 1)
          pixd = pixAddMirroredBorder(pixt, 0, 0, xtratop, yoverlap);
      else if (j == 0)
          pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, 0, 0);
      else if (j == nx - 1)
          pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, 0, 0);
      else
          pixd = pixClone(pixt);
      pixDestroy(&pixt);
  
      return pixd;
  }
  
  
  /*!
   * \brief   pixTilingNoStripOnPaint()
   *
   * \param[in]    pt pixtiling
   * \return  0 if OK, 1 on error
   *
   * <pre>
   * Notes:
   *      (1) The default for paint is to strip out the overlap pixels
   *          that are added by pixTilingGetTile().  However, some
   *          operations will generate an image with these pixels
   *          stripped off.  This tells the paint operation not
   *          to strip the added boundary pixels when painting.
   * </pre>
   */
  l_ok
  pixTilingNoStripOnPaint(PIXTILING  *pt)
  {
      PROCNAME("pixTilingNoStripOnPaint");
  
      if (!pt)
          return ERROR_INT("pt not defined", procName, 1);
      pt->strip = FALSE;
      return 0;
  }
  
  
  /*!
   * \brief   pixTilingPaintTile()
   *
   * \param[in]    pixd dest: paint tile onto this, without overlap
   * \param[in]    i tile row index
   * \param[in]    j tile column index
   * \param[in]    pixs source: tile to be painted from
   * \param[in]    pt pixtiling struct
   * \return  0 if OK, 1 on error
   */
  l_ok
  pixTilingPaintTile(PIX        *pixd,
                     l_int32     i,
                     l_int32     j,
                     PIX        *pixs,
                     PIXTILING  *pt)
  {
  l_int32  w, h;
  
      PROCNAME("pixTilingPaintTile");
  
      if (!pixd)
          return ERROR_INT("pixd not defined", procName, 1);
      if (!pixs)
          return ERROR_INT("pixs not defined", procName, 1);
      if (!pt)
          return ERROR_INT("pt not defined", procName, 1);
      if (i < 0 || i >= pt->ny)
          return ERROR_INT("invalid row index i", procName, 1);
      if (j < 0 || j >= pt->nx)
          return ERROR_INT("invalid column index j", procName, 1);
  
          /* Strip added border pixels off if requested */
      pixGetDimensions(pixs, &w, &h, NULL);
      if (pt->strip == TRUE) {
          pixRasterop(pixd, j * pt->w, i * pt->h,
                      w - 2 * pt->xoverlap, h - 2 * pt->yoverlap, PIX_SRC,
                      pixs, pt->xoverlap, pt->yoverlap);
      } else {
          pixRasterop(pixd, j * pt->w, i * pt->h, w, h, PIX_SRC, pixs, 0, 0);
      }
  
      return 0;
  }
  