docs: function (noBorder) {
          if (!this.image) return false;
  
          var ind, // array of indices of the boundary points
              nx0, nx1, ny0, ny1, // result of the intersection image with the viewport
              rx0, rx1, ry0, ry1, // result of the intersection image with the viewport (+1px on each side)
              w, h; // size of the intersection(+1px on each side)
  
          if (!noBorder) { // need create border
              // copy visible image data (+1 px on each side) for border search
              var offset = { // viewport offset in the global basis
                      x: Math.round(-this.layer.map.minPx.x),
                      y: Math.round(-this.layer.map.minPx.y)
                  },
                  img = this.image.data,
                  w1 = this.image.width,
                  b = this.image.bounds,
                  of = this.image.globalOffset, // image offset in the global basis
                  dx = of.x - offset.x, // delta for the transformation to viewport basis
                  dy = of.y - offset.y,
                  x0 = dx + b.minX, // left top of image (in viewport basis)
                  y0 = dy + b.minY,
                  x1 = dx + b.maxX, // right bottom of image (in viewport basis)
                  y1 = dy + b.maxY,
                  ix = this.size.w - 1, // right bottom of viewport (left top = [0,0])
                  iy = this.size.h - 1;
  
              // intersection of the image with viewport 
              if (!(x1 < 0 || x0 > ix || y1 < 0 || y0 > iy)) {
                  nx0 = x0 > 0 ? x0 : 0, // image fully visible in the viewport
                  ny0 = y0 > 0 ? y0 : 0,
                  nx1 = x1 < ix ? x1 : ix,
                  ny1 = y1 < iy ? y1 : iy,
                  rx0 = x0 > -1 ? x0 : -1; // intersection +1 px on each side (for search border)
                  ry0 = y0 > -1 ? y0 : -1;
                  rx1 = x1 < ix + 1 ? x1 : ix + 1;
                  ry1 = y1 < iy + 1 ? y1 : iy + 1;
              } else {
                  return false;
              }
              // copy result of the intersection(+1px on each side) to mask data for getBorderIndices
              w = rx1 - rx0 + 1;
              h = ry1 - ry0 + 1;
              var data = new Uint8Array(w * h),
                  i = 0,
                  k1 = (ry0 - dy) * w1 + (rx0 - dx),
                  k2 = (ry1 - dy) * w1 + (rx0 - dx) + 1;
              // walk through rows (Y)
              for (var k = k1; k < k2; k += w1) {
                  data.set(img.subarray(k, k + w), i); // copy row
                  i += w;
              }
              ind = MagicWand.getBorderIndices({ data: data, width: w, height: h });
  
              //save border for animation
              this.border = {
                  data: ind,
                  offsetX: rx0,
                  offsetY: ry0,
                  minX: nx0,
                  minY: ny0,
                  maxX: nx1,
                  maxY: ny1,
                  width: w
              };
          } else {
              // restore the existing border
              ind = this.border.data;
              rx0 = this.border.offsetX;
              ry0 = this.border.offsetY;
              nx0 = this.border.minX;
              ny0 = this.border.minY;
              nx1 = this.border.maxX;
              ny1 = this.border.maxY;
              w = this.border.width;
          }
  
          var nw = nx1 - nx0 + 1,
              nh = ny1 - ny0 + 1,
              imgData = this.canvasContext.createImageData(nw, nh), // result image
              res = imgData.data,
              len = ind.length,
              x, y, j,
              hatchLength = this.layer.hatchLength,
              hatchLength2 = hatchLength * 2,
              hatchOffset = this.hatchOffset;
  
          for (j = 0; j < len; j++) {
              i = ind[j],
              x = i % w; // calc x by index
              y = (i - x) / w; // calc y by index
              x += rx0; // viewport coordinates
              y += ry0;
              if (x < nx0 || x > nx1 || y < ny0 || y > ny1) continue;
              k = ((y - ny0) * nw + x - nx0) * 4; // result image index by viewport coordinates
              if ((x + y + hatchOffset) % hatchLength2 < hatchLength) { // detect hatch color 
                  res[k + 3] = 255; // black, change only alpha
              } else {
                  res[k] = 255; // white
                  res[k + 1] = 255;
                  res[k + 2] = 255;
                  res[k + 3] = 255;
              }
          }
  
          this.canvasContext.clearRect(0, 0, this.size.w, this.size.h);
          this.canvasContext.putImageData(imgData, nx0, ny0);
  
          return true;
      }