gt:             if (hasTypes) {
context: /*
   * RetinaEventExtractor.java
   *
   * Created on October 21, 2005, 6:24 PM
   *
   * To change this template, choose Tools | Options and locate the template under
   * the Source Creation and Management node. Right-click the template and choose
   * Open. You can then make changes to the template in the Source Editor.
   */
  package net.sf.jaer.chip;
  
  import java.util.logging.Logger;
  
  import net.sf.jaer.aemonitor.AEPacketRaw;
  import net.sf.jaer.event.BasicEvent;
  import net.sf.jaer.event.EventPacket;
  import net.sf.jaer.event.OutputEventIterator;
  import net.sf.jaer.event.TypedEvent;
  
  /**
   * An abstract 2D event extractor for 16 bit raw addresses. It is called with
   * addresses and timestamps and extracts these to {X, Y, type} arrays based on
   * methods that you define by sub-classing and overriding the abstract methods.
   *
   * xMask, yMask, typeMask mask for x, y address and cell type, and xShift,
   * yShift, typeShift say how many bits to shift after masking,
   * xFlip,yFlip,typeFlip use the chip size to flip the x,y, and type to invert
   * the addresses.
   *
   * @author tobi
   */
  abstract public class TypedEventExtractor<T extends BasicEvent> implements EventExtractor2D<T>, java.io.Serializable {
  
      static Logger log = Logger.getLogger("net.sf.jaer.chip");
  
  //    protected AEChip chip;
      protected int xmask, ymask;
      protected byte xshift, yshift;
      protected int typemask;
      protected byte typeshift;
      private AEChip chip = null;
      protected boolean flipx = false, flipy = false, rotate = false;
      protected boolean fliptype = false;
      protected boolean hexArrangement = false;
      // the reused output eventpacket
      protected EventPacket<T> out = null;
  
      /**
       * determines subSampling of rendered events (for speed)
       */
      private boolean subsamplingEnabled = false;
  
      private int subsampleThresholdEventCount = 50000;
  
      private short sizexm1, sizeym1; // these are size-1 (e.g. if 128 pixels, sizex=127). used for flipping below.
      private byte sizetypem1;
  
  //    /** Creates a new instance of RetinaEventExtractor */
  //    public TypedEventExtractor() {
  //    }
      /**
       * Creates a new instance of RetinaEventExtractor
       *
       * @param aechip the chip this extractor extracts
       */
      public TypedEventExtractor(AEChip aechip) {
          chip = aechip;
          setChipSizes();
      }
  
      private void setChipSizes() {
          sizexm1 = (short) (chip.getSizeX() - 1);
          sizeym1 = (short) (chip.getSizeY() - 1);
          sizetypem1 = (byte) (chip.getNumCellTypes() - 1);
      }
  
      /**
       * Gets X from raw address. declared final for speed, cannot be overridden
       * in subclass. It expects addr to be from a certain format like ???? XXX?
       * X?X? ????. The mask 0000 1110 1010 0000 will get those Xs. So, what's
       * left is 0000 XXX0 X0X0 0000. Then it is shifted (the triple means
       * zero-padding regardless of sign, necessary because addr is not defined as
       * unsigned!) with the xshift parameter. So, in this case e.g. 5, what will
       * make the result 0000 0000 0XXX 0X0X (0-64). If the incoming addr is
       * big-endian versus small-endian the entire bit sequence has to be reversed
       * of course. This is not what is done by flipx! That is just a boolean that
       * results in the chip size minus the result otherwise. So, in the above
       * example the place for X is still expected in the same spots (the mask is
       * not inverted). Last remark: the masks are normally continuous, no weird
       * blanks within them. :-) Ultimate remark: a mask of size 1 will invert the
       * value if the flip parameter is set. Super-last remark: the parameter
       * sizex should be 7 (not 8) for a mask of size 3 (000 - 111), like
       * mentioned before. So, the sizex and sizey parameters in this
       * TypedEventExtractor class are decremented with one compared to the same
       * parameters in the class AEChip.
       *
       * @param addr the raw address.
       * @return physical address
       */
      @Override
      public short getXFromAddress(int addr) {
          if (!flipx) {
              return ((short) ((addr & xmask) >>> xshift));
          }
  
          return (short) (sizexm1 - ((addr & xmask) >>> xshift)); // e.g. chip.sizex=32, sizex=31, addr=0, getX=31, addr=31, getX=0
      }
  
      /**
       * gets Y from raw address. declared final for speed, cannot be overridden
       * in subclass.
       *
       * @param addr the raw address.
       * @return physical address
       */
      @Override
      public short getYFromAddress(int addr) {
          if (!flipy) {
              return ((short) ((addr & ymask) >>> yshift));
          }
  
          return (short) (sizeym1 - ((addr & ymask) >>> yshift));
  
      }
  
      /**
       * gets type from raw address. declared final for speed, cannot be
       * overridden in subclass.
       *
       * @param addr the raw address.
       * @return physical address
       */
      @Override
      public byte getTypeFromAddress(int addr) {
          if (!fliptype) {
              return (byte) ((addr & typemask) >>> typeshift);
          }
  
          return (byte) (sizetypem1 - (byte) ((addr & typemask) >>> typeshift));
      }
  
      /**
       * extracts the meaning of the raw events.
       *
       * @param in the raw events, can be null
       * @return out the processed events. these are partially processed in-place.
       * empty packet is returned if null is supplied as in.
       */
      @Override
      synchronized public EventPacket<T> extractPacket(AEPacketRaw in) {
          if (out == null) {
              out = new EventPacket<>(chip.getEventClass());
          } else {
              out.clear();
          }
          if (in == null) {
              return out;
          }
          extractPacket(in, out);
          return out;
      }
  
      /**
       * Extracts the meaning of the raw events. This form is used to supply an
       * output packet. This method is used for real time event filtering using a
       * buffer of output events local to data acquisition. An AEPacketRaw may
       * contain multiple events, not all of them have to sent out as
       * EventPackets. An AEPacketRaw is a set(!) of addresses and corresponding
       * timing moments.
       *
       * A first filter (independent from the other ones) is implemented by
       * subSamplingEnabled and getSubsampleThresholdEventCount. The latter may
       * limit the amount of samples in one package to say 50,000. If there are
       * 160,000 events and there is a sub sample threshold of 50,000, a "skip
       * parameter" set to 3. Every so now and then the routine skips with 4, so
       * we end up with 50,000. It's an approximation, the amount of events may be
       * less than 50,000. The events are extracted uniform from the input.
       *
       * @param in the raw events, can be null
       * @param out the processed events. these are partially processed in-place.
       * empty packet is returned if null is supplied as input.
       */
      @Override
      synchronized public void extractPacket(AEPacketRaw in, EventPacket<T> out) {
  
          // TODO there could be a real problem here as exposed by AutomaticReplayPlayer
          out.clear();
          if (in == null) {
              return;
          }
          int n = in.getNumEvents(); //addresses.length;
  
          int skipBy = 1, incEach = 0, j = 0;
          if (subsamplingEnabled) {
              skipBy = n / getSubsampleThresholdEventCount();
              incEach = getSubsampleThresholdEventCount() / (n % getSubsampleThresholdEventCount());
          }
          if (skipBy == 0) {
              incEach = 0;
              skipBy = 1;
          }
  
          int[] a = in.getAddresses();
          int[] timestamps = in.getTimestamps();
          boolean hasTypes = false;
          if (chip != null) {
              hasTypes = chip.getNumCellTypes() > 1;
          }
  
          OutputEventIterator<?> outItr = out.outputIterator();
          for (int i = 0; i < n; i += skipBy) {
              int addr = a[i];
              BasicEvent e = outItr.nextOutput();
              e.address = addr;
              e.timestamp = (timestamps[i]);
              e.x = getXFromAddress(addr);
              e.y = getYFromAddress(addr);
  