Package com.esri.core.geometry

Source Code of com.esri.core.geometry.RasterizedGeometry2DImpl$ScanCallbackImpl

/*
Copyright 1995-2013 Esri

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

For additional information, contact:
Environmental Systems Research Institute, Inc.
Attn: Contracts Dept
380 New York Street
Redlands, California, USA 92373

email: contracts@esri.com
*/

package com.esri.core.geometry;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import com.esri.core.geometry.Envelope2D;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.GeometryException;
import com.esri.core.geometry.NumberUtils;
import com.esri.core.geometry.Point2D;
import com.esri.core.geometry.Segment;
import com.esri.core.geometry.SegmentIteratorImpl;
import com.esri.core.geometry.SimpleRasterizer;

final class RasterizedGeometry2DImpl extends RasterizedGeometry2D {
  int[] m_bitmap;
  int m_scanLineSize;
  int m_width;
  double m_dx;
  double m_dy;
  double m_x0;
  double m_y0;
  double m_toleranceXY;
  double m_stroke_half_widthX_pix;
  double m_stroke_half_widthY_pix;
  double m_stroke_half_width;

  Envelope2D m_geomEnv;// envelope of the raster in world coordinates
  Transformation2D m_transform;
  int m_dbgTestCount;
  SimpleRasterizer m_rasterizer;
  ScanCallbackImpl m_callback;

  class ScanCallbackImpl implements SimpleRasterizer.ScanCallback {
    int[] m_bitmap;
    int m_scanlineWidth;
    int m_color;

    public ScanCallbackImpl(int[] bitmap, int scanlineWidth) {
      m_scanlineWidth = scanlineWidth;
      m_bitmap = bitmap;
    }

    public void setColor(SimpleRasterizer rasterizer, int color) {
      m_color = color;// set new color
    }

    @Override
    public void drawScan(int y, int x, int numPxls) {
      int x0 = x;
      int x1 = x + numPxls;
      if (x1 > m_width)
        x1 = m_width;

      int scanlineStart = y * m_scanlineWidth;
      for (int xx = x0; xx < x1; xx++) {
        m_bitmap[scanlineStart + (xx >> 4)] |= m_color << ((xx & 15) * 2);// 2
        // bit
        // per
        // color
      }
    }
  }

  void fillMultiPath(SimpleRasterizer rasterizer, Transformation2D trans, MultiPathImpl polygon, boolean isWinding) {
    SegmentIteratorImpl segIter = polygon.querySegmentIterator();
    Point2D p1 = new Point2D();
    Point2D p2 = new Point2D();
    while (segIter.nextPath()) {
      while (segIter.hasNextSegment()) {
        Segment seg = segIter.nextSegment();
        if (seg.getType() != Geometry.Type.Line)
          throw GeometryException.GeometryInternalError(); // TODO:
        // densify
        // the
        // segment
        // here
        trans.transform(seg.getStartXY(), p1);
        trans.transform(seg.getEndXY(), p2);
        m_rasterizer.addEdge(p1.x, p1.y, p2.x, p2.y);
      }
    }
   
    m_rasterizer.renderEdges(isWinding ? SimpleRasterizer.WINDING : SimpleRasterizer.EVEN_ODD);
  }
 
  void fillPoints(SimpleRasterizer rasterizer, MultiPointImpl geom, double stroke_half_width) {
    throw GeometryException.GeometryInternalError();
  }

  void fillConvexPolygon(SimpleRasterizer rasterizer, Point2D[] fan, int len) {
    for (int i = 1, n = len; i < n; i++) {
      rasterizer.addEdge(fan[i-1].x, fan[i-1].y, fan[i].x, fan[i].y);
    }
    rasterizer.addEdge(fan[len-1].x, fan[len-1].y, fan[0].x, fan[0].y);
    m_rasterizer.renderEdges(SimpleRasterizer.EVEN_ODD);
  }

  void fillEnvelope(SimpleRasterizer rasterizer, Envelope2D envIn) {
    /*if (!m_identity) {
      Point2D fan[] = new Point2D[4];
      envIn.queryCorners(fan);
      fillConvexPolygon(rasterizer, fan, 4);
      return;
    }*/

    Envelope2D env = new Envelope2D(0, 0, m_width, m_width);
    if (!env.intersect(envIn))
      return;

    int x0 = (int) Math.round(env.xmin);
    int x = (int) Math.round(env.xmax);

    int xn = NumberUtils.snap(x0, 0, m_width);
    int xm = NumberUtils.snap(x, 0, m_width);
    if (x0 < m_width && xn < xm) {
      int y0 = (int) Math.round(env.ymin);
      int y1 = (int) Math.round(env.ymax);
      y0 = NumberUtils.snap(y0, 0, m_width);
      y1 = NumberUtils.snap(y1, 0, m_width);
      if (y0 < m_width) {
        for (int y = y0; y < y1; y++) {
          m_rasterizer.callback_.drawScan(y, xn, xm - xn);
        }
      }
    }
  }
 
  void strokeDrawPolyPath(SimpleRasterizer rasterizer,
      MultiPathImpl polyPath, double tol) {

    Point2D[] fan = new Point2D[4];
    for (int i = 0; i < fan.length; i++)
      fan[i] = new Point2D();

    SegmentIteratorImpl segIter = polyPath.querySegmentIterator();
    double strokeHalfWidth = m_transform.transform(tol) + 1.5;
    double shortSegment = 0.5;
    Point2D vec = new Point2D();
    Point2D vecA = new Point2D();
    Point2D vecB = new Point2D();

    // TODO check this Java workaroung
    Point2D ptStart = new Point2D();
    Point2D ptEnd = new Point2D();
    Envelope2D segEnv = new Envelope2D();
    Point2D ptOld = new Point2D();
    while (segIter.nextPath()) {
      boolean hasFan = false;
      boolean first = true;
      ptOld.setCoords(0, 0);
      while (segIter.hasNextSegment()) {
        Segment seg = segIter.nextSegment();
        ptStart.x = seg.getStartX();
        ptStart.y = seg.getStartY();// Point2D ptStart =
        // seg.getStartXY();
        ptEnd.x = seg.getEndX();
        ptEnd.y = seg.getEndY();// Point2D ptEnd = seg.getEndXY();
        segEnv.setEmpty();
        segEnv.merge(ptStart.x, ptStart.y);
        segEnv.mergeNE(ptEnd.x, ptEnd.y);
        if (!m_geomEnv.isIntersectingNE(segEnv)) {
          if (hasFan) {
            fillConvexPolygon(rasterizer, fan, 4);
            hasFan = false;
          }
          first = true;
          continue;
        }

        m_transform.transform(ptEnd, ptEnd);

        if (first) {
          m_transform.transform(ptStart, ptStart);
          ptOld.setCoords(ptStart);
          first = false;
        } else {
          ptStart.setCoords(ptOld);
        }

        vec.sub(ptEnd, ptStart);
        double len = vec.length();
        boolean bShort = len < shortSegment;
        if (len == 0) {
          vec.setCoords(1.0, 0);
          len = 1.0;
          continue;
        }

        if (!bShort)
          ptOld.setCoords(ptEnd);

        vec.scale(strokeHalfWidth / len);
        vecA.setCoords(-vec.y, vec.x);
        vecB.setCoords(vec.y, -vec.x);
        ptStart.sub(vec);
        ptEnd.add(vec);
        fan[0].add(ptStart, vecA);
        fan[1].add(ptStart, vecB);
        fan[2].add(ptEnd, vecB);
        fan[3].add(ptEnd, vecA);
        if (!bShort)
          fillConvexPolygon(rasterizer, fan, 4);
        else
          hasFan = true;
      }
      if (hasFan)
        fillConvexPolygon(rasterizer, fan, 4);
    }
  }

  int worldToPixX(double x) {
    return (int) Math.round(x * m_dx + m_x0);
  }

  int worldToPixY(double y) {
    return (int) Math.round(y * m_dy + m_y0);
  }

  RasterizedGeometry2DImpl(Geometry geom, double toleranceXY,
      int rasterSizeBytes) {
    // //_ASSERT(CanUseAccelerator(geom));
    init((MultiVertexGeometryImpl) geom._getImpl(), toleranceXY,
        rasterSizeBytes);
  }

  static RasterizedGeometry2DImpl createImpl(Geometry geom,
      double toleranceXY, int rasterSizeBytes) {
    RasterizedGeometry2DImpl rgImpl = new RasterizedGeometry2DImpl(geom,
        toleranceXY, rasterSizeBytes);
    rgImpl.init((MultiVertexGeometryImpl) geom._getImpl(), toleranceXY,
        rasterSizeBytes);
    return rgImpl;
  }

  private RasterizedGeometry2DImpl(MultiVertexGeometryImpl geom,
      double toleranceXY, int rasterSizeBytes) {
    init(geom, toleranceXY, rasterSizeBytes);
  }

  static RasterizedGeometry2DImpl createImpl(MultiVertexGeometryImpl geom,
      double toleranceXY, int rasterSizeBytes) {
    RasterizedGeometry2DImpl rgImpl = new RasterizedGeometry2DImpl(geom,
        toleranceXY, rasterSizeBytes);
    rgImpl.init(geom, toleranceXY, rasterSizeBytes);
    return rgImpl;
  }
 
  void init(MultiVertexGeometryImpl geom, double toleranceXY,
      int rasterSizeBytes) {
    // _ASSERT(CanUseAccelerator(geom));
    m_width = Math.max((int) (Math.sqrt(rasterSizeBytes) * 2 + 0.5), 64);
    m_scanLineSize = (m_width * 2 + 31) / 32; // 2 bits per pixel
    m_geomEnv = new Envelope2D();

    m_toleranceXY = toleranceXY;

    // calculate bitmap size
    int size = 0;
    int width = m_width;
    int scanLineSize = m_scanLineSize;
    while (width >= 8) {
      size += width * scanLineSize;
      width /= 2;
      scanLineSize = (width * 2 + 31) / 32;
    }

    // allocate the bitmap, that contains the base and the mip-levels
    m_bitmap = new int[size];
    for (int i = 0; i < size; i++)
      m_bitmap[i] = 0;

    m_rasterizer = new SimpleRasterizer();
    ScanCallbackImpl callback = new ScanCallbackImpl(m_bitmap,
        m_scanLineSize);
    m_callback = callback;
    m_rasterizer.setup(m_width, m_width, callback);
    geom.queryEnvelope2D(m_geomEnv);
    if (m_geomEnv.getWidth() > m_width * m_geomEnv.getHeight()
        || m_geomEnv.getHeight() > m_geomEnv.getWidth() * m_width) {
      // the geometry is thin and the rasterizer is not needed.
    }
    m_geomEnv.inflate(toleranceXY, toleranceXY);
    Envelope2D worldEnv = new Envelope2D();

    Envelope2D pixEnv = Envelope2D
        .construct(1, 1, m_width - 2, m_width - 2);

    double minWidth = toleranceXY * pixEnv.getWidth(); // min width is such
    // that the size of
    // one pixel is
    // equal to the
    // tolerance
    double minHeight = toleranceXY * pixEnv.getHeight();

    worldEnv.setCoords(m_geomEnv.getCenter(),
        Math.max(minWidth, m_geomEnv.getWidth()),
        Math.max(minHeight, m_geomEnv.getHeight()));

    m_stroke_half_widthX_pix = worldEnv.getWidth() / pixEnv.getWidth();
    m_stroke_half_widthY_pix = worldEnv.getHeight() / pixEnv.getHeight();

    // The stroke half width. Later it will be inflated to account for
    // pixels size.
    m_stroke_half_width = m_toleranceXY;

    m_transform = new Transformation2D();
    m_transform.initializeFromRect(worldEnv, pixEnv);// geom to pixels

    Transformation2D identityTransform = new Transformation2D();

    switch (geom.getType().value()) {
    case Geometry.GeometryType.MultiPoint:
      callback.setColor(m_rasterizer, 2);
         fillPoints(m_rasterizer, (MultiPointImpl) geom, m_stroke_half_width);
      break;
    case Geometry.GeometryType.Polyline:
      callback.setColor(m_rasterizer, 2);
      strokeDrawPolyPath(m_rasterizer, (MultiPathImpl) geom._getImpl(),
          m_stroke_half_width);
      break;
    case Geometry.GeometryType.Polygon: {
      boolean isWinding = false;// NOTE: change when winding is supported
      callback.setColor(m_rasterizer, 1);
      fillMultiPath(m_rasterizer, m_transform, (MultiPathImpl) geom, isWinding);
      callback.setColor(m_rasterizer, 2);
      strokeDrawPolyPath(m_rasterizer, (MultiPathImpl) geom._getImpl(),
          m_stroke_half_width);
    }
      break;
    }

    m_dx = m_transform.xx;
    m_dy = m_transform.yy;
    m_x0 = m_transform.xd;
    m_y0 = m_transform.yd;
    buildLevels();
  }

  boolean tryRenderAsSmallEnvelope_(Envelope2D env) {
    if (!env.isIntersecting(m_geomEnv))
      return true;

    Envelope2D envPix = new Envelope2D();
    envPix.setCoords(env);
    m_transform.transform(env);
    double strokeHalfWidthPixX = m_stroke_half_widthX_pix;
    double strokeHalfWidthPixY = m_stroke_half_widthY_pix;
    if (envPix.getWidth() > 2 * strokeHalfWidthPixX + 1
        || envPix.getHeight() > 2 * strokeHalfWidthPixY + 1)
      return false;

    // This envelope is too narrow/small, so that it can be just drawn as a
    // rectangle using only boundary color.

    envPix.inflate(strokeHalfWidthPixX, strokeHalfWidthPixY);
    envPix.xmax += 1.0;
    envPix.ymax += 1.0;// take into account that it does not draw right and
    // bottom edges.

    m_callback.setColor(m_rasterizer, 2);
    fillEnvelope(m_rasterizer, envPix);
    return true;
  }

  void buildLevels() {
    int iStart = 0;
    int iStartNext = m_width * m_scanLineSize;
    int width = m_width;
    int widthNext = m_width / 2;
    int scanLineSize = m_scanLineSize;
    int scanLineSizeNext = (widthNext * 2 + 31) / 32;
    while (width > 8) {
      for (int iy = 0; iy < widthNext; iy++) {
        int iysrc1 = iy * 2;
        int iysrc2 = iy * 2 + 1;
        for (int ix = 0; ix < widthNext; ix++) {
          int ixsrc1 = ix * 2;
          int ixsrc2 = ix * 2 + 1;
          int divix1 = ixsrc1 >> 4;
          int modix1 = (ixsrc1 & 15) * 2;
          int divix2 = ixsrc2 >> 4;
          int modix2 = (ixsrc2 & 15) * 2;
          int res = (m_bitmap[iStart + scanLineSize * iysrc1 + divix1] >> modix1) & 3;
          res |= (m_bitmap[iStart + scanLineSize * iysrc1 + divix2] >> modix2) & 3;
          res |= (m_bitmap[iStart + scanLineSize * iysrc2 + divix1] >> modix1) & 3;
          res |= (m_bitmap[iStart + scanLineSize * iysrc2 + divix2] >> modix2) & 3;
          int divixDst = ix >> 4;
          int modixDst = (ix & 15) * 2;
          m_bitmap[iStartNext + scanLineSizeNext * iy + divixDst] |= res << modixDst;
        }
      }

      width = widthNext;
      scanLineSize = scanLineSizeNext;
      iStart = iStartNext;
      widthNext = width / 2;
      scanLineSizeNext = (widthNext * 2 + 31) / 32;
      iStartNext = iStart + scanLineSize * width;
    }
  }

  @Override
  public HitType queryPointInGeometry(double x, double y) {
    int ix = worldToPixX(x);
    int iy = worldToPixY(y);
    if (ix < 0 || ix >= m_width || iy < 0 || iy >= m_width)
      return HitType.Outside;
    int divix = ix >> 4;
    int modix = (ix & 15) * 2;
    int res = (m_bitmap[m_scanLineSize * iy + divix] >> modix) & 3;
    if (res == 0)
      return HitType.Outside;
    else if (res == 1)
      return HitType.Inside;
    else
      return HitType.Border;
  }

  @Override
  public HitType queryEnvelopeInGeometry(Envelope2D env) {
    if (!env.intersect(m_geomEnv))
      return com.esri.core.geometry.RasterizedGeometry2D.HitType.Outside;
    int ixmin = worldToPixX(env.xmin);
    int ixmax = worldToPixX(env.xmax);
    int iymin = worldToPixY(env.ymin);
    int iymax = worldToPixY(env.ymax);
    if (ixmin < 0)
      ixmin = 0;
    if (iymin < 0)
      iymin = 0;
    if (ixmax >= m_width)
      ixmax = m_width - 1;
    if (iymax >= m_width)
      iymax = m_width - 1;

    if (ixmin > ixmax || iymin > iymax)
      return HitType.Outside;

    int area = Math.max(ixmax - ixmin, 1) * Math.max(iymax - iymin, 1);
    int iStart = 0;
    int scanLineSize = m_scanLineSize;
    int width = m_width;
    int res = 0;
    while (true) {
      if (area < 32 || width < 16) {
        for (int iy = iymin; iy <= iymax; iy++) {
          for (int ix = ixmin; ix <= ixmax; ix++) {
            int divix = ix >> 4;
            int modix = (ix & 15) * 2;
            res = (m_bitmap[iStart + scanLineSize * iy + divix] >> modix) & 3; // read
            // two
            // bit
            // color.
            if (res > 1)
              return HitType.Border;
          }
        }

        if (res == 0)
          return HitType.Outside;
        else if (res == 1)
          return HitType.Inside;
      }

      iStart += scanLineSize * width;
      width /= 2;
      scanLineSize = (width * 2 + 31) / 32;
      ixmin /= 2;
      iymin /= 2;
      ixmax /= 2;
      iymax /= 2;
      area = Math.max(ixmax - ixmin, 1) * Math.max(iymax - iymin, 1);
    }
  }

  @Override
  public double getToleranceXY() {
    return m_toleranceXY;
  }

  @Override
  public int getRasterSize() {
    return m_width * m_scanLineSize;
  }

  @Override
  public boolean dbgSaveToBitmap(String fileName) {
    try {
      FileOutputStream outfile = new FileOutputStream(fileName);

      int height = m_width;
      int width = m_width;
      int sz = 14 + 40 + 4 * m_width * height;
      // Write the BITMAPFILEHEADER
      ByteBuffer byteBuffer = ByteBuffer.allocate(sz);
      byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
      // byteBuffer.put((byte) 'M');
      byteBuffer.put((byte) 66);
      byteBuffer.put((byte) 77);
      // fwrite("BM", 1, 2, f); //bfType
      byteBuffer.putInt(sz);
      // fwrite(&sz, 1, 4, f);//bfSize
      short zero16 = 0;
      byteBuffer.putShort(zero16);
      // fwrite(&zero16, 1, 2, f);//bfReserved1
      byteBuffer.putShort(zero16);
      // fwrite(&zero16, 1, 2, f);//bfReserved2
      int offset = 14 + 40;
      byteBuffer.putInt(offset);
      // fwrite(&offset, 1, 4, f);//bfOffBits

      // Write the BITMAPINFOHEADER
      int biSize = 40;
      int biWidth = width;
      int biHeight = -height;
      short biPlanes = 1;
      short biBitCount = 32;
      int biCompression = 0;
      int biSizeImage = 4 * width * height;
      int biXPelsPerMeter = 0;
      int biYPelsPerMeter = 0;
      int biClrUsed = 0;
      int biClrImportant = 0;
      byteBuffer.putInt(biSize);
      byteBuffer.putInt(biWidth);
      byteBuffer.putInt(biHeight);
      byteBuffer.putShort(biPlanes);
      byteBuffer.putShort(biBitCount);
      byteBuffer.putInt(biCompression);
      byteBuffer.putInt(biSizeImage);
      byteBuffer.putInt(biXPelsPerMeter);
      byteBuffer.putInt(biYPelsPerMeter);
      byteBuffer.putInt(biClrUsed);
      byteBuffer.putInt(biClrImportant);

      int colors[] = { 0xFFFFFFFF, 0xFF000000, 0xFFFF0000, 0xFF00FF00 };
      // int32_t* rgb4 = (int32_t*)malloc(biSizeImage);
      for (int y = 0; y < height; y++) {
        int scanlineIn = y * ((width * 2 + 31) / 32);
        int scanlineOut = offset + width * y;

        for (int x = 0; x < width; x++) {
          int res = (m_bitmap[scanlineIn + (x >> 4)] >> ((x & 15) * 2)) & 3;
          byteBuffer.putInt(colors[res]);
        }
      }

      byte[] b = byteBuffer.array();
      outfile.write(b);
      outfile.close();
      return true;
    } catch (IOException ex) {
      return false;

    }
  }
}
TOP

Related Classes of com.esri.core.geometry.RasterizedGeometry2DImpl$ScanCallbackImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.