gt:                                             int iH = bitmapTile.getHeight();
context: /*
   * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Codename One designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *  
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   * 
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   * 
   * Please contact Codename One through http://www.codenameone.com/ if you 
   * need additional information or have any questions.
   */
  package com.codename1.impl.android;
  
  import android.app.Activity;
  import android.graphics.Bitmap;
  import android.graphics.BitmapShader;
  import android.graphics.Canvas;
  import android.graphics.LinearGradient;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.RadialGradient;
  import android.graphics.Rect;
  import android.graphics.RectF;
  import android.graphics.Shader;
  import android.view.KeyEvent;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.inputmethod.EditorInfo;
  import android.view.inputmethod.InputConnection;
  
  import com.codename1.io.Log;
  import com.codename1.ui.Component;
  import com.codename1.ui.Display;
  import com.codename1.ui.Font;
  import com.codename1.ui.Form;
  import com.codename1.ui.Image;
  import com.codename1.ui.Label;
  import com.codename1.ui.PeerComponent;
  import com.codename1.ui.Stroke;
  import com.codename1.ui.TextField;
  import com.codename1.ui.geom.GeneralPath;
  import com.codename1.ui.geom.Rectangle;
  import java.util.ArrayList;
  
  import com.codename1.ui.Transform;
  import com.codename1.ui.plaf.Style;
  import com.codename1.ui.plaf.StyleAccessor;
  import java.util.WeakHashMap;
  
  public class AndroidAsyncView extends ViewGroup implements CodenameOneSurface {
  
  
      private RectF tmpRectf = new RectF();
  
      public static class LayoutParams extends ViewGroup.MarginLayoutParams {
          public PeerComponent pc;
          public LayoutParams(int x, int y, int w, int h, PeerComponent pc) {
              super(w, h);
              this.pc = pc;
              this.x = x;
              this.y = y;
              this.w = w;
              this.h = h;
          }
  
          public int x, y, w, h;
          public boolean dirty;
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          final int count = getChildCount();
          for(int iter = 0 ; iter < count ; iter++) {
              View child = getChildAt(iter);
              LayoutParams lp = (LayoutParams) child.getLayoutParams();
              child.measure(View.MeasureSpec.makeMeasureSpec(lp.w, MeasureSpec.EXACTLY),
                      View.MeasureSpec.makeMeasureSpec(lp.h, MeasureSpec.EXACTLY));
          }
          setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
      }
  
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          final int count = getChildCount();
          for(int iter = 0 ; iter < count ; iter++) {
              View child = getChildAt(iter);
              LayoutParams lp = (LayoutParams) child.getLayoutParams();
              child.layout(lp.x, lp.y, lp.x + lp.w, lp.y + lp.h);
          }
      }
  
  
      @Override
      public boolean shouldDelayChildPressedState() {
          return false;
      }
  
  
      abstract class AsyncOp {
  
          int clipX;
          int clipY;
          int clipW;
          int clipH;
          
          Path clipPath;
          boolean clipIsPath;
  
          public AsyncOp(Rectangle clip, Path clipP, boolean clipIsPath) {
              this.clipIsPath = clipIsPath;
              if (clipIsPath) {
                  this.clipPath = new Path();
                  this.clipPath.set(clipP);
              } else {
                  if (clip == null) {
                      clipW = cn1View.width;
                      clipH = cn1View.height;
                  } else {
                      clipX = clip.getX();
                      clipY = clip.getY();
                      clipW = clip.getWidth();
                      clipH = clip.getHeight();
                  }
              }
  
          }
          
          public void prepare() {}
          
          public void executeWithClip(AndroidGraphics underlying) {
              if(clipIsPath){
                  underlying.setClipRaw(clipPath);
              }else{
                  underlying.setClipRaw(clipX, clipY, clipW, clipH);
              }
              execute(underlying);
          }
  
          public abstract void execute(AndroidGraphics underlying);
      }
      private static final Object RENDERING_OPERATIONS_LOCK = new Object();
      private ArrayList<AsyncOp> renderingOperations = new ArrayList<AsyncOp>();
      private ArrayList<AsyncOp> pendingRenderingOperations = new ArrayList<AsyncOp>();
      private ArrayList<AsyncOp> currentlyRendering = new ArrayList<AsyncOp>();
      private final CodenameOneView cn1View;
      private final AndroidGraphics graphics;
      private final AndroidGraphics internalGraphics;
      private final AndroidImplementation implementation;
      static boolean legacyPaintLogic = true;
      private static ArrayList<Path> pathPool = new ArrayList<Path>();
  
      private synchronized static Path createPathFromPool() {
          if (!pathPool.isEmpty()) {
              Path out = pathPool.remove(pathPool.size()-1);
              out.rewind();
              return out;
          }
          return new Path();
      }
  
      private synchronized static void recycle(Path p) {
          if (pathPool.size() < 5 && p != null) {
              pathPool.add(p);
          }
      }
  
  
      public AndroidAsyncView(Activity activity, AndroidImplementation implementation) {
          super(activity);
          setId(2001);
          this.implementation = implementation;
          graphics = new AsyncGraphics(implementation);
          internalGraphics = new AndroidGraphics(implementation, null, false);
          cn1View = new CodenameOneView(activity, this, implementation, false);
          setWillNotCacheDrawing(true);
          setWillNotDraw(true);
          setBackgroundDrawable(null);
          this.setFocusable(true);
          this.setFocusableInTouchMode(false);
          this.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
      }
  
  
      @Override
      protected void dispatchDraw(Canvas c) {
          // **************************
          // Commented out debuging code useful for hand tuning the drawing logic performance
          // **************************
          //long time = System.currentTimeMillis();
          
          //boolean paintOnBuffer = implementation.isEditingText() ||
          //        InPlaceEditView.isKeyboardShowing();
          
          boolean paintOnBuffer = false;
          
          internalGraphics.setCanvasNoSave(c);
          AndroidGraphics g = internalGraphics;
          if (paintOnBuffer) {
              g = cn1View.getGraphics();
          }
  
          //final HashMap<String, Long> slowest = new HashMap<>();
          //final HashMap<String, Long> counts = new HashMap<>();
          int count = renderingOperations.size();
  
          // this works around the case of a blank screen when an invalidate occurs out of nowhere
          // and no operations are in the queue
          if(count > 0) {
              currentlyRendering.clear();
              currentlyRendering.addAll(renderingOperations);
          } else {
              count = currentlyRendering.size();
          }
          int offset = 0;
          for(; offset < count ; offset++) {
              AsyncOp o = currentlyRendering.get(offset);
              o.executeWithClip(g);
              /*ntime = System.nanoTime() - ntime;
              String s = o.toString();
              Long l = slowest.get(s);
              if(l == null) {
                  slowest.put(s, ntime);
                  counts.put(s, Long.valueOf(1));
              } else {
                  slowest.put(s, l.longValue() + ntime);
                  Long ll = counts.get(s);
                  counts.put(s, ll.longValue() + 1);
              }*/
          }
          
          synchronized (RENDERING_OPERATIONS_LOCK) {
              renderingOperations.clear();
              RENDERING_OPERATIONS_LOCK.notify();
          }
  
          if (paintOnBuffer) {
              cn1View.d(c);
          }
          if (implementation.isAsyncEditMode() && implementation.isEditingText()) {
              InPlaceEditView.reLayoutEdit();
          }
          /*long etime = System.currentTimeMillis();
          if(etime - time >= 16) {
              System.out.println("JANK... " + (etime - time));
  
              ArrayList<String> sortedSlowest = new ArrayList<>(slowest.keySet());
              Collections.sort(sortedSlowest, new Comparator<String>() {
                  @Override
                  public int compare(String lhs, String rhs) {
                      return (int) (slowest.get(rhs).longValue() - slowest.get(lhs).longValue());
                  }
              });
  
  
              ArrayList<String> sortedCounts = new ArrayList<>(counts.keySet());
              Collections.sort(sortedCounts, new Comparator<String>() {
                  @Override
                  public int compare(String lhs, String rhs) {
                      return (int) (counts.get(rhs).longValue() - counts.get(lhs).longValue());
                  }
              });
              for(int iter = 0 ; iter < Math.min(5, sortedSlowest.size()) ; iter++) {
                  String name = sortedSlowest.get(iter);
                  System.out.println("Slowest entry " + iter + " is " + name + " for " + slowest.get(name));
  
                  name = sortedCounts.get(iter);
                  System.out.println("Most entries " + iter + " is " + name + " for " + counts.get(name));
              }
          }*/
      }
  
      public void setPaintViewOnBuffer(boolean paintViewOnBuffer) {
          //this.paintViewOnBuffer = paintViewOnBuffer;
      }
  
      @Override
      public boolean isOpaque() {
          return true;
      }
  
      private void visibilityChangedTo(boolean visible) {
          cn1View.visibilityChangedTo(visible);
      }
  
      @Override
      protected void onWindowVisibilityChanged(int visibility) {
          super.onWindowVisibilityChanged(visibility);
          this.visibilityChangedTo(visibility == View.VISIBLE);
          /*if(visibility != View.VISIBLE){
              paintViewOnBuffer = true;
          }*/
      }
  
      @Override
      protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
          super.onSizeChanged(w, h, oldw, oldh);
  
          if (!Display.isInitialized()) {
              return;
          }
          //if the input is pan mode, there is no need to resize the screen
          // It turns out that this was only necessary because the InPlaceEditView was causing
          // an onSizeChanged event to be fired when its visibility was set to GONE.
          // Removing that visibility setting removes the erroneous resize event.
          // Keeping this was problematic because sometimes a resize event is correct even when
          // the keyboard is showing, e.g. when the device is rotated.
          //if((InPlaceEditView.isEditing() || InPlaceEditView.isKeyboardShowing()) && !InPlaceEditView.isInputResize()){
          //    return;
          //}
          Display.getInstance().callSerially(new Runnable() {
              public void run() {
                  cn1View.handleSizeChange(w, h);
              }
          });
      }
  
      public void resizeViews() {
          int children = getChildCount();
          if(children > 0) {
              for (int iter = 0; iter < children; iter++) {
                  final View v = getChildAt(iter);
                  final AndroidAsyncView.LayoutParams lp = (AndroidAsyncView.LayoutParams) v.getLayoutParams();
                  if (lp.dirty) {
                      lp.dirty = false;
                      v.post(new Runnable() {
                          @Override
                          public void run() {
                              v.requestLayout();
  
                              Display.getInstance().getInstance().callSerially(new Runnable() {
                                  @Override
                                  public void run() {
                                      Display.getInstance().getCurrent().repaint();
                                  }
                              });
                          }
                      });
                  }
              }
          }
      }
  
      @Override
      public void flushGraphics(Rect rect) {
          //Log.d(Display.getInstance().getProperty("AppName", "CodenameOne"), "Flush graphics invoked with pending: " + pendingRenderingOperations.size() + " and current " + renderingOperations.size());
  
          // we might have pending entries in the rendering queue
          int counter = 0;
          while (!renderingOperations.isEmpty()) {
              try {
                  synchronized (RENDERING_OPERATIONS_LOCK) {
                      RENDERING_OPERATIONS_LOCK.wait(5);
                  }
  
                  // don't let the EDT die here
                  counter++;
                  if (counter > 10) {
                      //Log.d(Display.getInstance().getProperty("AppName", "CodenameOne"), "Flush graphics timed out!!!");
                      return;
                  }
              } catch (InterruptedException err) {
              }
          }
          ArrayList<AsyncOp> tmp = renderingOperations;
          renderingOperations = pendingRenderingOperations;
          pendingRenderingOperations = tmp;
          try {
              for (AsyncOp o : renderingOperations) {
                  o.prepare();
              }
          } catch (java.util.ConcurrentModificationException ex) {
              // This is a race condition that sometimes occurs
              // Rather than add synchronized here (which may have performance implications)
              // we'll just "give up" and issue a repaint.
              Log.p("NOTICE: Hit concurrent modification race condition in flushGraphics.  Skipping flush, and issuing another repaint.");
              Log.e(ex);
              Display.getInstance().callSerially(new Runnable() {
  
                  @Override
                  public void run() {
                      Form f = Display.getInstance().getCurrent();
                      if (f != null) {
                          f.repaint();
                      }
                  }
                  
              });
              return;
          }
  
          int children = getChildCount();
          if(children > 0) {
              com.codename1.ui.Form c = Display.getInstance().getCurrent();
              for (int iter = 0; iter < children; iter++) {
                  final View v = getChildAt(iter);
                  final AndroidAsyncView.LayoutParams lp = (AndroidAsyncView.LayoutParams) v.getLayoutParams();
                  //if (lp != null && c == lp.pc.getComponentForm()) {
                      //v.postInvalidate();
  
                      /*if(lp.dirty) {
                          lp.dirty = false;
                          v.post(new Runnable() {
                              @Override
                              public void run() {
                                  if (v.getVisibility() == View.INVISIBLE) {
                                      v.setVisibility(View.VISIBLE);
                                  }
                                  v.requestLayout();
                              }
                          });
                      } else {
                          if (v.getVisibility() == View.INVISIBLE) {
                              v.post(new Runnable() {
                                  @Override
                                  public void run() {
                                      v.setVisibility(View.VISIBLE);
                                  }
                              });
                          }
                      }*/
                  //} else {
                      /*if(v.getVisibility() == View.VISIBLE) {
                          v.post(new Runnable() {
                              @Override
                              public void run() {
                                  v.setVisibility(View.INVISIBLE);
                              }
                          });
                      }*/
                  //}
              }
          }
          if (rect == null) {
              postInvalidate();
          } else {
              postInvalidate(rect.left, rect.top, rect.right, rect.bottom);
          }
          graphics.setClip(0, 0, cn1View.width, cn1View.height);
          graphics.setAlpha(255);
          graphics.setColor(0);
      }
  
      @Override
      public void flushGraphics() {
          flushGraphics(null);
      }
  
      @Override
      public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
          return true;
      }
  
      @Override
      public boolean onKeyDown(int keyCode, KeyEvent event) {
          if (InPlaceEditView.isEditing()) {
              return true;
          }
          return cn1View.onKeyUpDown(true, keyCode, event);
      }
  
      @Override
      public boolean onKeyUp(int keyCode, KeyEvent event) {
          if (InPlaceEditView.isEditing()) {
              return true;
          }
          return cn1View.onKeyUpDown(false, keyCode, event);
      }
  
      @Override
      public boolean dispatchTouchEvent(MotionEvent event) {
          boolean res = false;
          if (!(res = cn1View.onTouchEvent(event))) {
              return super.dispatchTouchEvent(event);
          }
          return res;
          
      }
  
      @Override
      public AndroidGraphics getGraphics() {
          return graphics;
      }
  
      @Override
      public int getViewHeight() {
          return cn1View.height;
      }
  
      @Override
      public int getViewWidth() {
          return cn1View.width;
      }
  
      @Override
      public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
          if (!Display.isInitialized() || Display.getInstance().getCurrent() == null) {
              return super.onCreateInputConnection(editorInfo);
          }
          cn1View.setInputType(editorInfo);
          return super.onCreateInputConnection(editorInfo);
      }
  
      @Override
      public boolean onCheckIsTextEditor() {
          if (!Display.isInitialized() || Display.getInstance().getCurrent() == null) {
              return false;
          }
          Component txtCmp = Display.getInstance().getCurrent().getFocused();
          if (txtCmp != null && txtCmp instanceof TextField) {
              return true;
          }
          return false;
      }
  
      @Override
      public View getAndroidView() {
          return this;
      }
      
      public void removePeerView(final View v) {
          if(v.getParent() != null) {
              ((Activity)v.getContext()).runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                      // this might happen twice if draw view is invoked twice before the screen has time to draw
                      if(v.getParent() != null) {
                          removeView(v);
                      }
                  }
              });
          }
      }
      
  
      class AsyncGraphics extends AndroidGraphics {
  
          private boolean clipIsPath;
          private Matrix convertedTransform;
          private boolean clipFresh;
          private boolean transformDirty=true;
          private Rectangle clip = null;
          private Path clipP = null;
          private GeneralPath clipGP = null;
          private int clipX, clipY, clipW, clipH;
  
          private int alpha;
          private int color;
          private Paint imagePaint = new Paint();
          private Transform transform;
          private Transform inverseTransform;
          private boolean inverseTransformDirty=true;
          private com.codename1.ui.geom.Rectangle tmpClipBoundsRect = new com.codename1.ui.geom.Rectangle();
          private com.codename1.ui.geom.Rectangle tmpRect = new com.codename1.ui.geom.Rectangle();
          private GeneralPath tmpClipBoundsPath = new GeneralPath();
  
          private float[] tmpMatrix3x3 = new float[9];
  
          private Transform getInverseTransform() {
              if (inverseTransform == null) {
                  inverseTransform = Transform.makeIdentity();
                  inverseTransformDirty = true;
              }
              if (inverseTransformDirty) {
  
                  try {
                      getTransform().getInverse(inverseTransform);
                      inverseTransformDirty = false;
                  } catch (Exception ex) {
                      ex.printStackTrace();
                  }
              }
              return inverseTransform;
          }
  
          AsyncGraphics(AndroidImplementation impl) {
              super(impl, null, false);
          }
  
  
          private void freshClip() {
              if (!clipFresh) {
                  clipFresh = true;
                  if (clipIsPath) {
                      if (clipGP == null) {
                          clipX = 0;
                          clipY = 0;
                          clipW = cn1View.width;
                          clipH = cn1View.height;
                      } else {
                          tmpClipBoundsPath.setPath(clipGP, getInverseTransform());
                          tmpClipBoundsPath.getBounds(tmpClipBoundsRect);
                          clipX = tmpClipBoundsRect.getX();
                          clipY = tmpClipBoundsRect.getY();
                          clipW = tmpClipBoundsRect.getWidth();
                          clipH = tmpClipBoundsRect.getHeight();
                      }
                  } else {
                      if (clip == null) {
                          clip = new Rectangle(0, 0, cn1View.width, cn1View.height);
                          clipX = 0;
                          clipY = 0;
                          clipW = cn1View.width;
                          clipH = cn1View.height;
                      } else {
                          clipX = clip.getX();
                          clipY = clip.getY();
                          clipW = clip.getWidth();
                          clipH = clip.getHeight();
                      }
                      if (!getTransform().isIdentity()) {
                          tmpClipBoundsPath.setRect(clip, getInverseTransform());
                          tmpClipBoundsPath.getBounds(tmpClipBoundsRect);
                          clipX = tmpClipBoundsRect.getX();
                          clipY = tmpClipBoundsRect.getY();
                          clipW = tmpClipBoundsRect.getWidth();
                          clipH = tmpClipBoundsRect.getHeight();
                      }
                  }
              }
          }
  
          @Override
          public void drawView(final View v, final AndroidAsyncView.LayoutParams lp) {
              if(v.getParent() == null) {
                  ((Activity)v.getContext()).runOnUiThread(new Runnable() {
                      @Override
                      public void run() {
                          // this might happen twice if draw view is invoked twice before the screen has time to draw
                          if(v.getParent() == null) {
                              v.setLayoutParams(lp);
                              addView(v);
                              ArrayList<View> toRemove = new ArrayList<View>();
                              ViewGroup parentGroup = (ViewGroup)v.getParent();
                              int childCount = parentGroup.getChildCount();
                              for (int i=0; i<childCount; i++) {
                                  View child = parentGroup.getChildAt(i);
                                  if (child == v) {
                                      continue;
                                  }
                                  AndroidImplementation.AndroidPeer peer = AndroidImplementation.activePeers.get(child);
  
                                  if (peer != null) {
                                      if (!peer._initialized()) {
                                          toRemove.add(child);
                                      }
                                  }
                              }
                              for (View child : toRemove) {
                                  removeView(child);
                                  synchronized(AndroidImplementation.activePeers) {
                                      AndroidImplementation.activePeers.remove(child);
                                  }
                              }
  
                          }
                      }
                  });
  
              }
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      if (v.getParent() != null) {
                          drawChild(underlying.canvas, v, getDrawingTime());
                      }
                  }
  
                  public String toString() {
                      return "drawView(PeerComponent)";
                  }
              });
  
          }
  
          @Override
          public void rotate(final float angle, final int x, final int y) {
              getTransform().rotate(angle, x, y);
              transformDirty = true;
              inverseTransformDirty = true;
              clipFresh = false;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.rotate(angle, x, y);
                  }
  
                  @Override
                  public void executeWithClip(AndroidGraphics underlying) {
                      execute(underlying);
                  }
  
                  public String toString() {
                      return "rotate";
                  }
              });
          }
  
          @Override
          public void rotate(final float angle) {
              getTransform().rotate(angle, 0, 0);
              transformDirty = true;
              inverseTransformDirty = true;
              clipFresh = false;
              pendingRenderingOperations.add(new AsyncOp(clip,clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.rotate(angle);
                  }
  
                  @Override
                  public void executeWithClip(AndroidGraphics underlying) {
                      execute(underlying);
                  }
  
                  public String toString() {
                      return "rotate (no pivot)";
                  }
              });
          }
  
  
          @Override
          public void scale(final float x, final float y) {
              getTransform().scale(x, y);
              transformDirty = true;
              inverseTransformDirty = true;
              clipFresh = false;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.scale(x, y);
                  }
  
                  @Override
                  public void executeWithClip(AndroidGraphics underlying) {
                      execute(underlying);
                  }
  
                  public String toString() {
                      return "scale";
                  }
              });
          }
  
          @Override
          public void resetAffine() {
              getTransform().setIdentity();
              transformDirty = true;
              inverseTransformDirty = true;
              clipFresh = false;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.resetAffine();
                  }
  
                  @Override
                  public void executeWithClip(AndroidGraphics underlying) {
                      execute(underlying);
                  }
  
                  public String toString() {
                      return "resetAffine";
                  }
              });
          }
  
  
          /*
          private Matrix getTransformMatrix(){
              if ( transformDirty ){
                  // Conversion from 4x4 to 3x3
                  // See http://www.w3.org/TR/2009/WD-SVG-Transforms-20090320/#_4x4-to-3x3-conversion
                  // for formula
                  CN1Matrix4f m = (CN1Matrix4f)getTransform().getNativeTransform();
                  float[] mMatrix3x3 = tmpMatrix3x3;
                  float[] mMatrix4x4 = m.getData();
  
  
                  mMatrix3x3[0] = mMatrix4x4[0];
                  mMatrix3x3[1] = mMatrix4x4[4];
                  mMatrix3x3[2] = mMatrix4x4[12];
                  mMatrix3x3[3] = mMatrix4x4[1];
                  mMatrix3x3[4] = mMatrix4x4[5];
                  mMatrix3x3[5] = mMatrix4x4[13];
                  mMatrix3x3[6] = mMatrix4x4[3];
                  mMatrix3x3[7] = mMatrix4x4[7];
                  mMatrix3x3[8] = mMatrix4x4[15];
  
                  if (convertedTransform == null) {
                      convertedTransform = new Matrix();
                  }
                  convertedTransform.setValues(mMatrix3x3);
  
                  transformDirty = false;
  
              }
              return convertedTransform;
  
          }
          */
  
          @Override
          public int getColor() {
              return color;
          }
  
  
          private void getClipShape(GeneralPath out) {
              if (clipIsPath) {
                  out.reset();
                  if (getTransform().isIdentity()) {
                      out.setPath(clipGP, null);
                  } else {
                      out.setPath(clipGP, getInverseTransform());
                  }
              } else {
                  out.reset();
                  if (clip == null) {
                      clip = new Rectangle(0, 0, cn1View.width, cn1View.height);
                  }
                  out.setRect(clip, getInverseTransform());
              }
          }
  
          @Override
          public void clipRect(final int x, final int y, final int width, final int height) {
              boolean isIdentity = getTransform().isIdentity();
              clipFresh = false;
              if (clipIsPath) {
                  if (clipGP == null) {
                      setClip(x, y, width, height);
                      return;
                  }
                  if (!isIdentity) {
                      clipGP.transform(getInverseTransform());
                  }
                  if (!clipGP.intersect(x, y, width, height)) {
                      if (clip == null) {
                          clip = new Rectangle();
                      }
                      clip.setBounds(x,y,0,0);
                      clipIsPath = false;
                      return;
                  }
                  if (!isIdentity) {
                      clipGP.transform(getTransform());
                  }
                  if (clipGP.isRectangle()) {
                      if (clip == null) {
                          clip = new Rectangle();
                      }
                      clipGP.getBounds(clip);
                      clipIsPath = false;
                  } else {
                      if (clipP == null) {
                          clipP = new Path();
                      }
                      AndroidImplementation.cn1ShapeToAndroidPath(clipGP, clipP);
                  }
              } else {
                  if (clip == null) {
                      setClip(x, y, width, height);
                      return;
                  }
                  if (clip.getWidth() <= 0 || clip.getHeight() <= 0) {
                      return;
                  }
                  if (isIdentity) {
                      tmpRect.setBounds(x, y, width, height);
                      clip.intersection(tmpRect, clip);
                  } else {
                      clipIsPath = true;
                      if (clipGP == null) {
                          clipGP = new GeneralPath();
                      }
                      clipGP.setRect(clip, null);
                      clipGP.transform(getInverseTransform());
                      if (!clipGP.intersect(x, y, width, height)) {
                          clip.setBounds(x, y, 0, 0);
                          clipIsPath = false;
                          return;
                      }
                      clipGP.transform(getTransform());
  
                      if (clipGP.isRectangle()) {
                          clipGP.getBounds(clip);
                          clipIsPath = false;
                      } else {
                          if (clipP == null) {
                              clipP = new Path();
                          }
                          AndroidImplementation.cn1ShapeToAndroidPath(clipGP, clipP);
                      }
                  }
              }
              //not implemented - if the current clipping is a Path we should intersect it with 
              //the shape
          }
  
          @Override
          public void setClip(final int x, final int y, final int width, final int height) {
              boolean isIdentity = getTransform().isIdentity();
              clipFresh = false;
              if (isIdentity) {
                  clipIsPath = false;
                  if (clip == null) {
                      clip = new Rectangle(x, y, width, height);
                  } else {
                      clip.setBounds(x, y, width, height);
                  }
              } else {
                  clipIsPath = true;
                  if (clipGP == null) {
                      clipGP = new GeneralPath();
                  }
                  if (clip == null) {
                      clip = new Rectangle();
                  }
                  clip.setBounds(x, y, width, height);
                  clipGP.setRect(clip, getTransform());
  
                  if (clipGP.isRectangle()) {
                      clipIsPath = false;
                      if (clip == null) {
                          clip = new Rectangle();
                      }
                      clipGP.getBounds(clip);
                  } else {
                      if (clipP == null) {
                          clipP = new Path();
                      }
                      AndroidImplementation.cn1ShapeToAndroidPath(clipGP, clipP);
                  }
              }
  
          }
  
          @Override
          public void setClip(com.codename1.ui.geom.Shape shape) {
              clipIsPath = true;
              clipFresh = false;
              if (clipGP == null) {
                  clipGP = new GeneralPath();
              }
              boolean isIdentity = getTransform().isIdentity();
              if (isIdentity) {
                  clipGP.setShape(shape, null);
              } else {
                  clipGP.setShape(shape, getTransform());
              }
              if (clipGP.isRectangle()) {
                  clipIsPath = false;
                  if (clip == null) {
                      clip = new Rectangle();
                  }
                  clipGP.getBounds(clip);
              } else {
                  if (clipP == null) {
                      clipP = new Path();
                  }
                  AndroidImplementation.cn1ShapeToAndroidPath(clipGP, clipP);
  
              }
  
          }
  
          @Override
          public int getClipY() {
              freshClip();
              return clipY;
          }
  
          @Override
          public int getClipX() {
              freshClip();
              return clipX;
  
          }
          @Override
          public int getClipWidth() {
              freshClip();
              return clipW;
          }
  
          @Override
          public int getClipHeight() {
              freshClip();
              return clipH;
          }
  
          @Override
          public void setAlpha(final int a) {
              this.alpha = a;
          }
  
          @Override
          public int getAlpha() {
              return alpha;
          }
  
          @Override
          public void fillRoundRect(final int x, final int y, final int width, final int height, final int arcWidth, final int arcHeight) {
              final int alph = alpha;
              final int col = color;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.setAlpha(alph);
                      underlying.setColor(col);
                      underlying.fillRoundRect(x, y, width, height, arcWidth, arcHeight);
                  }
                  public String toString() {
                      return "fillRoundRect";
                  }
              });
          }
  
          @Override
          public void fillRect(final int x, final int y, final int width, final int height) {
              if (alpha == 0) {
                  return;
              }
              final int al = alpha;
              final int col = color;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.setColor(col);
                      underlying.setAlpha(al);
                      underlying.fillRect(x, y, width, height);
                  }
                  public String toString() {
                      return "fillRectA";
                  }
              });
          }
  
          @Override
          public void fillRect(final int x, final int y, final int w, final int h, byte alpha) {
              if (alpha == 0) {
                  return;
              }
              final int preAlpha = this.alpha;
              final int al = alpha & 0xff;
              final int col = color;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.setColor(col);
                      underlying.setAlpha(al);
                      underlying.fillRect(x, y, w, h);
                      underlying.setAlpha(preAlpha);
                  }
                  public String toString() {
                      return "fillRectB";
                  }
              });
          }
  
          @Override
          public void fillLinearGradient(final int startColor, final int endColor, final int x, final int y, final int width, final int height, final boolean horizontal) {
              if (alpha == 0) {
                  return;
              }
              final int al = alpha;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.setAlpha(al);
                      underlying.fillLinearGradient(startColor, endColor, x, y, width, height, horizontal);
                  }
                  public String toString() {
                      return "fillLinearGradient";
                  }
              });
          }
  
          @Override
          public void fillRectRadialGradient(final int startColor, final int endColor, final int x, final int y,
                                             final int width, final int height, final float relativeX, final float relativeY, final float relativeSize) {
              if (alpha == 0) {
                  return;
              }
              final int al = alpha;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.setAlpha(al);
                      underlying.fillRectRadialGradient(startColor, endColor, x, y, width, height, relativeX, relativeY, relativeSize);
                  }
                  public String toString() {
                      return "fillRectRadialGradient";
                  }
              });
          }
  
          @Override
          public void fillRadialGradient(final int startColor, final int endColor, final int x, final int y, final int width, final int height) {
              fillRadialGradient(startColor, endColor, x, y, width, height, 0, 360);
              
          }
          
          @Override
          public void fillRadialGradient(final int startColor, final int endColor, final int x, final int y, final int width, final int height, final int startAngle, final int arcAngle) {
              if (alpha == 0) {
                  return;
              }
              final int al = alpha;
              pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                  @Override
                  public void execute(AndroidGraphics underlying) {
                      underlying.setAlpha(al);
                      underlying.fillRadialGradient(startColor, endColor, x, y, width, height, startAngle, arcAngle);
                  }
                  public String toString() {
                      return "fillRadialGradient";
                  }
              });
          }
          
          class AndroidStyleCache {
              AsyncPaintPosition backgroundPainter;
              CodenameOneTextPaint textPaint;
              Paint iconPaint;
          }
          
          abstract class AsyncPaintPosition extends AsyncOp{
              int x;
              int y;
              int width;
              int height;
              int alpha;
              
              // the pending values allow changing this op on the Codename One EDT while the Android thread
              // renders it
              int pendingX;
              int pendingY;
              int pendingWidth;
              int pendingHeight;
              int pendingAlpha;
  
              int pendingClipX;
              int pendingClipY;
              int pendingClipW;
              int pendingClipH;
              
              Path pendingClipP;
              boolean pendingClipIsPath;
              
              public AsyncPaintPosition(Rectangle clip, Path clipP, boolean clipIsPath) {
                  super(clip, clipP, clipIsPath);
                  pendingClipIsPath = clipIsPath;
  
  
                  if (clipIsPath) {
                      pendingClipP = new Path();
                      pendingClipP.set(this.clipPath);
                  } else {
                      pendingClipX = clipX;
                      pendingClipY = clipY;
                      pendingClipW = clipW;
                      pendingClipH= clipH;
                  }
              }
  
              public void updateClip(Rectangle clip, Path clipP, boolean clipIsPath) {
                  pendingClipIsPath = clipIsPath;
                  if (clipIsPath) {
                      if (pendingClipP == null) {
                          pendingClipP = new Path();
                      }
                      pendingClipP.set(clipP);
                  } else {
                      pendingClipX = clip.getX();
                      pendingClipY = clip.getY();
                      pendingClipW = clip.getWidth();
                      pendingClipH = clip.getHeight();
                  }
              }
  
              @Override
              public void prepare() {
                  x = pendingX;
                  y = pendingY;
                  height = pendingHeight;
                  width = pendingWidth;
                  alpha = pendingAlpha;
                  clipIsPath = pendingClipIsPath;
                  if (clipIsPath) {
                      if (clipPath == null) {
                          clipPath = new Path();
                      }
                      clipPath.set(pendingClipP);
                  } else {
                      clipX = pendingClipX;
                      clipY = pendingClipY;
                      clipW = pendingClipW;
                      clipH = pendingClipH;
                  }
              }
  
              @Override
              public void execute(AndroidGraphics underlying) {
                  executeImpl(underlying);
              }
              
              public abstract void executeImpl(AndroidGraphics underlying);
          }
                  
          AsyncPaintPosition createGradientPaint(Style s) {
              final Paint paint = new Paint();
              paint.setStyle(Paint.Style.FILL);
              paint.setAntiAlias(true);
              paint.setAlpha(255);
              final byte bgType = s.getBackgroundType();
              final int startColor = s.getBackgroundGradientStartColor();
              final int endColor = s.getBackgroundGradientEndColor();
              
              AsyncPaintPosition ap = new AsyncPaintPosition(clip, clipP, clipIsPath) {
                  int lastHeight;
                  int lastWidth;
                  @Override
                  public void executeImpl(AndroidGraphics underlying) {
                      if(width != lastWidth || height != lastHeight) {
                          lastWidth = width;
                          lastHeight = height;
                          switch(bgType) {
                              case Style.BACKGROUND_GRADIENT_LINEAR_VERTICAL:
                                  paint.setShader(new LinearGradient(0, 0, 0, height, startColor, endColor, Shader.TileMode.MIRROR));
                                  break;
                              case Style.BACKGROUND_GRADIENT_LINEAR_HORIZONTAL:
                                  paint.setShader(new LinearGradient(0, 0, width, 0, startColor, endColor, Shader.TileMode.MIRROR));
                                  break;
                              case Style.BACKGROUND_GRADIENT_RADIAL:
                                  paint.setShader(new RadialGradient(x, y, Math.max(width, height), startColor, endColor, Shader.TileMode.MIRROR));
                                  break;
                          }
                          underlying.canvas.drawRect(x, y, x + width, y + height, paint);
                      }
                  }
                  public String toString() {
                      return "GradientPaint";
                  }
              };
              return ap;
          }
          
          @Override
          public void paintComponentBackground(final int x, final int y, final int width, final int height, final Style s) {
              if (alpha == 0 || width <= 0 || height <= 0) {
                  return;
              }
              
              if(legacyPaintLogic || StyleAccessor.isRendererStyle(s)) {
                  final int al = alpha;
                  final byte backgroundType = s.getBackgroundType();
                  final Image bgImage = s.getBgImage();
                  final int bgColor = s.getBgColor();
                  final byte bgTransparency = s.getBgTransparency();
                  final int backgroundGradientStartColor = s.getBackgroundGradientStartColor();
                  final int backgroundGradientEndColor = s.getBackgroundGradientEndColor();
                  final float backgroundGradientRelativeX = s.getBackgroundGradientRelativeX();
                  final float backgroundGradientRelativeY = s.getBackgroundGradientRelativeY();
                  final float backgroundGradientRelativeSize = s.getBackgroundGradientRelativeSize();
                  pendingRenderingOperations.add(new AsyncOp(clip, clipP, clipIsPath) {
                      @Override
                      public void execute(AndroidGraphics underlying) {
                          underlying.setAlpha(al);
                          underlying.paintComponentBackground(backgroundType, bgImage, bgColor, bgTransparency,
                                  backgroundGradientStartColor, backgroundGradientEndColor,
                                  backgroundGradientRelativeX, backgroundGradientRelativeY,
                                  backgroundGradientRelativeSize, x, y, width, height);
                      }
                      public String toString() {
                          return "paintComponentBackground - Legacy";
                      }
                  });
                  return;
              }
  
              AndroidStyleCache sc = (AndroidStyleCache)StyleAccessor.getCachedData(s);
              AsyncPaintPosition bgPaint = null;
              if(sc != null) {
                  bgPaint = sc.backgroundPainter;
              }
              if(bgPaint == null) {
                  final byte backgroundType = s.getBackgroundType() ;
                  Image bgImageOrig = s.getBgImage();
                  if (bgImageOrig == null) {
                      if(backgroundType >= Style.BACKGROUND_GRADIENT_LINEAR_VERTICAL) {
                          bgPaint = createGradientPaint(s);
                      } else {
                          // solid color paint
                          byte bgt = s.getBgTransparency();
                          if(bgt == 0) {
                              return;
                          }
                          bgPaint = paintBackgroundSolidColor(bgt, s, bgPaint);
                      }
                  } else {
                      switch(backgroundType) {
                          case Style.BACKGROUND_NONE:
                              byte bgt = s.getBgTransparency();
                              if(bgt == 0) {
                                  return;
                              }
                              bgPaint = paintBackgroundSolidColor(bgt, s, bgPaint);
                              break;
                          case Style.BACKGROUND_IMAGE_SCALED:
                              final Paint bgImageScalePaint = new Paint();
                              bgImageScalePaint.setXfermode(PORTER);
                              final Bitmap b = (Bitmap) bgImageOrig.getImage();
                              final Rect src = new Rect();
                              src.top = 0;
                              src.bottom = b.getHeight();
                              src.left = 0;
                              src.right = b.getWidth();
                              
                              bgPaint = new AsyncPaintPosition(clip, clipP, clipIsPath) {
                                  @Override
                                  public void executeImpl(AndroidGraphics underlying) {
                                      Rect dest = new Rect();
                                      dest.top = y;
                                      dest.bottom = y + height;
                                      dest.left = x;
                                      dest.right = x + width;
                                      bgImageScalePaint.setAlpha(alpha);
                                      underlying.canvas.drawBitmap(b, src, dest, bgImageScalePaint);
                                  }
                                  public String toString() {
                                      return "BackgroundScaledImage";
                                  }
                              };
                              break;
                          case Style.BACKGROUND_IMAGE_SCALED_FILL:
                              final Paint bgImageScaleFillPaint = new Paint();
                              bgImageScaleFillPaint.setXfermode(PORTER);
                              final Bitmap bFill = (Bitmap) bgImageOrig.getImage();
                              final Rect srcFill = new Rect();
                              srcFill.top = 0;
                              srcFill.bottom = bFill.getHeight();
                              srcFill.left = 0;
                              srcFill.right = bFill.getWidth();
                              final int iW = bgImageOrig.getWidth();
                              final int iH = bgImageOrig.getHeight();
                              
                              bgPaint = new AsyncPaintPosition(clip, clipP, clipIsPath) {
                                  @Override
                                  public void executeImpl(AndroidGraphics underlying) {
                                      Rect dest = new Rect();
                                      float r = Math.max(((float)width) / ((float)iW), ((float)height) / ((float)iH));
                                      int bwidth = (int)(((float)iW) * r);
                                      int bheight = (int)(((float)iH) * r);
                                      x = x + (width - bwidth) / 2;
                                      y = y + (height - bheight) / 2; 
                                      dest.top = y;
                                      dest.bottom = y + bheight;
                                      dest.left = x;
                                      dest.right = x + bwidth;
                                      bgImageScaleFillPaint.setAlpha(alpha);
                                      underlying.canvas.drawBitmap(bFill, srcFill, dest, bgImageScaleFillPaint);
                                  }
                                  public String toString() {
                                      return "ScaledImageFill";
                                  }
                              };
                              break;
                          case Style.BACKGROUND_IMAGE_SCALED_FIT:
                              final Paint bgImageScaleFitPaint = new Paint();
                              final Paint bgImageScaleFitColorPaint = new Paint();
                              final byte bgtScaleFitColorAlpha = s.getBgTransparency();
                              int cc = ((bgtScaleFitColorAlpha << 24) & 0xff000000) | (s.getBgColor() & 0xffffff);
                              bgImageScaleFitColorPaint.setAntiAlias(false);
                              bgImageScaleFitColorPaint.setStyle(Paint.Style.FILL);
                              bgImageScaleFitColorPaint.setColor(cc);
                              bgImageScaleFitPaint.setAlpha(255);
  
                              final Bitmap bFit = (Bitmap) bgImageOrig.getImage();
                              final Rect srcFit = new Rect();
                              srcFit.top = 0;
                              srcFit.bottom = bFit.getHeight();
                              srcFit.left = 0;
                              srcFit.right = bFit.getWidth();
                              final int iWFit = bgImageOrig.getWidth();
                              final int iHFit = bgImageOrig.getHeight();
                              
                              bgPaint = new AsyncPaintPosition(clip, clipP, clipIsPath) {
                                  @Override
                                  public void executeImpl(AndroidGraphics underlying) {
                                      if(alpha > 0) {
                                          bgImageScaleFitColorPaint.setAlpha(alpha);
                                          underlying.canvas.drawRect(x, y, x + width, y + height, bgImageScaleFitColorPaint);
                                      }
                                      Rect dest = new Rect();
                                      float r2 = Math.min(((float)width) / ((float)iWFit), ((float)height) / ((float)iHFit));
                                      int awidth = (int)(((float)iWFit) * r2);
                                      int aheight = (int)(((float)iHFit) * r2);
  
                                      x = x + (width - awidth) / 2;
                                      y = y + (height - aheight) / 2; 
                                      dest.top = y;
                                      dest.bottom = y + aheight;
                                      dest.left = x;
                                      dest.right = x + awidth;
                                      underlying.canvas.drawBitmap(bFit, srcFit, dest, bgImageScaleFitPaint);
                                  }
                                  public String toString() {
                                      return "ScaledImageFit";
                                  }
                              };
                              break;
                          case Style.BACKGROUND_IMAGE_TILE_BOTH:
                              final Paint bgImageTiledBothPaint = new Paint();
                              Bitmap bitmapTileBoth = (Bitmap) bgImageOrig.getImage();
                              BitmapShader shader = new BitmapShader(bitmapTileBoth, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
                              bgImageTiledBothPaint.setShader(shader);
                              bgImageTiledBothPaint.setAntiAlias(false);
                              
                              bgPaint = new AsyncPaintPosition(clip, clipP, clipIsPath) {
                                  @Override
                                  public void executeImpl(AndroidGraphics underlying) {
                                      Rect dest = new Rect();
                                      dest.top = 0;
                                      dest.bottom = height;
                                      dest.left = 0;
                                      dest.right = width;
                                      bgImageTiledBothPaint.setAlpha(alpha);
                                      underlying.canvas.save();
                                      underlying.canvas.translate(x, y);
                                      underlying.applyTransform();
                                      underlying.canvas.drawRect(dest, bgImageTiledBothPaint);
                                      underlying.unapplyTransform();
                                      underlying.canvas.restore();
                                  }
                                  public String toString() {
                                      return "ImageTileBoth";
                                  }
                              };
                              break;
                          case Style.BACKGROUND_IMAGE_TILE_HORIZONTAL_ALIGN_TOP:
                          case Style.BACKGROUND_IMAGE_TILE_HORIZONTAL_ALIGN_CENTER:
                          case Style.BACKGROUND_IMAGE_TILE_HORIZONTAL_ALIGN_BOTTOM:
                          case Style.BACKGROUND_IMAGE_TILE_VERTICAL_ALIGN_LEFT:
                          case Style.BACKGROUND_IMAGE_TILE_VERTICAL_ALIGN_CENTER:
                          case Style.BACKGROUND_IMAGE_TILE_VERTICAL_ALIGN_RIGHT:
                              final Paint bgImageTiledPaint = new Paint();
                              final Paint bgColorTiledPaint = new Paint();
                              final byte bgtTiledColorAlpha = s.getBgTransparency();
                              int c = ((bgtTiledColorAlpha << 24) & 0xff000000) | (s.getBgColor() & 0xffffff);
                              bgColorTiledPaint.setAntiAlias(false);
                              bgColorTiledPaint.setStyle(Paint.Style.FILL);
                              bgColorTiledPaint.setColor(c);
                              bgImageTiledPaint.setAlpha(255);
                              final Bitmap bitmapTile = (Bitmap) bgImageOrig.getImage();
                              BitmapShader shaderTile = new BitmapShader(bitmapTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
                              bgImageTiledPaint.setShader(shaderTile);
                              bgImageTiledPaint.setAntiAlias(false);
                              
                              bgPaint = new AsyncPaintPosition(clip, clipP, clipIsPath) {
                                  @Override
                                  public void executeImpl(AndroidGraphics underlying) {
                                      // fill the solid color
                                      underlying.canvas.drawRect(x, y, x + width, y + height, bgColorTiledPaint);
  
                                      switch(backgroundType) {
                                          case Style.BACKGROUND_IMAGE_TILE_HORIZONTAL_ALIGN_TOP:
                                              height = Math.min(bitmapTile.getHeight(), height);
                                              break;
                                                      
                                          case Style.BACKGROUND_IMAGE_TILE_HORIZONTAL_ALIGN_CENTER: {
  