Add implementations for clipRect(), save() and restore().

The current implementation of clipRect() does not apply local transformations
before setting the new clip.

Change-Id: I5997871bb638dfcd1a8ef96354846af52427e445
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index d8fa850..fbfea95 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -137,9 +137,10 @@
 
     @Override
     public boolean clipRect(float left, float top, float right, float bottom) {
-        // TODO: Implement
-        return false;
+        return nClipRect(mRenderer, left, top, right, bottom);
     }
+    
+    private native boolean nClipRect(int renderer, float left, float top, float right, float bottom);
 
     @Override
     public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
@@ -148,14 +149,14 @@
 
     @Override
     public boolean clipRect(int left, int top, int right, int bottom) {
-        // TODO: Implement
-        return false;        
+        return nClipRect(mRenderer, left, top, right, bottom);        
     }
+    
+    private native boolean nClipRect(int renderer, int left, int top, int right, int bottom);
 
     @Override
     public boolean clipRect(Rect rect) {
-        // TODO: Implement
-        return false;        
+        return clipRect(rect.left, rect.top, rect.right, rect.bottom);        
     }
 
     @Override
@@ -165,8 +166,7 @@
 
     @Override
     public boolean clipRect(RectF rect) {
-        // TODO: Implement
-        return false;        
+        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
     }
 
     @Override
@@ -198,14 +198,12 @@
 
     @Override
     public boolean quickReject(Path path, EdgeType type) {
-        // TODO: Implement
-        return false;
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public boolean quickReject(RectF rect, EdgeType type) {
-        // TODO: Implement
-        return false;
+        return quickReject(rect.left, rect.top, rect.right, rect.bottom, type);
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -254,16 +252,16 @@
 
     @Override
     public int save() {
-        // TODO: Implement
-        return 0;
+        return nSave(mRenderer, 0);
     }
-
+    
     @Override
     public int save(int saveFlags) {
-        // TODO: Implement
-        return 0;
+        return nSave(mRenderer, saveFlags);
     }
 
+    private native int nSave(int renderer, int flags);
+    
     @Override
     public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
         // TODO: Implement
@@ -292,19 +290,24 @@
 
     @Override
     public void restore() {
-        // TODO: Implement
+        nRestore(mRenderer);
     }
+    
+    private native void nRestore(int renderer);
 
     @Override
     public void restoreToCount(int saveCount) {
-        // TODO: Implement
+        nRestoreToCount(mRenderer, saveCount);
     }
 
+    private native void nRestoreToCount(int renderer, int saveCount);
+    
     @Override
     public int getSaveCount() {
-        // TODO: Implement
-        return 0;
+        return nGetSaveCount(mRenderer);
     }
+    
+    private native int nGetSaveCount(int renderer);
 
     ///////////////////////////////////////////////////////////////////////////
     // Filtering
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 5766083..8c2a04c 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -54,7 +54,47 @@
 }
 
 // ----------------------------------------------------------------------------
-// Draw color
+// State
+// ----------------------------------------------------------------------------
+
+static jint android_view_GLES20Renderer_save(JNIEnv* env, jobject canvas, jint renderer,
+        jint flags) {
+
+    return UI->save(flags);
+}
+
+static jint android_view_GLES20Renderer_getSaveCount(JNIEnv* env, jobject canvas, jint renderer) {
+    return UI->getSaveCount();
+}
+
+static void android_view_GLES20Renderer_restore(JNIEnv* env, jobject canvas, jint renderer) {
+    UI->restore();
+}
+
+static void android_view_GLES20Renderer_restoreToCount(JNIEnv* env, jobject canvas, jint renderer,
+        jint saveCount) {
+
+    UI->restoreToCount(saveCount);
+}
+
+// ----------------------------------------------------------------------------
+// Clipping
+// ----------------------------------------------------------------------------
+
+static bool android_view_GLES20Renderer_clipRectF(JNIEnv* env, jobject canvas, jint renderer,
+        jfloat left, jfloat top, jfloat right, jfloat bottom) {
+
+    return UI->clipRect(left, top, right, bottom);
+}
+
+static bool android_view_GLES20Renderer_clipRect(JNIEnv* env, jobject canvas, jint renderer,
+        jint left, jint top, jint right, jint bottom) {
+
+    return UI->clipRect(float(left), float(top), float(right), float(bottom));
+}
+
+// ----------------------------------------------------------------------------
+// Drawing
 // ----------------------------------------------------------------------------
 
 static void android_view_GLES20Renderer_drawColor(JNIEnv* env, jobject canvas, jint renderer,
@@ -70,12 +110,20 @@
 const char* const kClassPathName = "android/view/GLES20Canvas";
 
 static JNINativeMethod gMethods[] = {
-    {   "nCreateRenderer",    "()I",     (void*) android_view_GLES20Renderer_createRenderer },
-    {   "nDestroyRenderer",   "(I)V",    (void*) android_view_GLES20Renderer_destroyRenderer },
-    {   "nSetViewport",       "(III)V",  (void*) android_view_GLES20Renderer_setViewport },
-    {   "nPrepare",           "(I)V",    (void*) android_view_GLES20Renderer_prepare },
+    {   "nCreateRenderer",    "()I",      (void*) android_view_GLES20Renderer_createRenderer },
+    {   "nDestroyRenderer",   "(I)V",     (void*) android_view_GLES20Renderer_destroyRenderer },
+    {   "nSetViewport",       "(III)V",   (void*) android_view_GLES20Renderer_setViewport },
+    {   "nPrepare",           "(I)V",     (void*) android_view_GLES20Renderer_prepare },
 
-    {   "nDrawColor",         "(III)V",  (void*) android_view_GLES20Renderer_drawColor },
+    {   "nSave",              "(II)I",    (void*) android_view_GLES20Renderer_save },
+    {   "nRestore",           "(I)V",     (void*) android_view_GLES20Renderer_restore },
+    {   "nRestoreToCount",    "(II)V",    (void*) android_view_GLES20Renderer_restoreToCount },
+    {   "nGetSaveCount",      "(I)I",     (void*) android_view_GLES20Renderer_getSaveCount },
+
+    {   "nClipRect",          "(IFFFF)Z", (void*) android_view_GLES20Renderer_clipRectF },
+    {   "nClipRect",          "(IIIII)Z", (void*) android_view_GLES20Renderer_clipRect },
+
+    {   "nDrawColor",         "(III)V",   (void*) android_view_GLES20Renderer_drawColor },
 };
 
 int register_android_view_GLES20Canvas(JNIEnv* env) {
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 68b245b..638c1b8 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -140,10 +140,12 @@
 }
 
 void Matrix4::dump() const {
-	LOGD("%f %f %f %f", mMat[0], mMat[4], mMat[ 8], mMat[12]);
-	LOGD("%f %f %f %f", mMat[1], mMat[5], mMat[ 9], mMat[13]);
-	LOGD("%f %f %f %f", mMat[2], mMat[6], mMat[10], mMat[14]);
-	LOGD("%f %f %f %f", mMat[3], mMat[7], mMat[11], mMat[15]);
+	LOGD("Matrix4[");
+	LOGD("  %f %f %f %f", mMat[0], mMat[4], mMat[ 8], mMat[12]);
+	LOGD("  %f %f %f %f", mMat[1], mMat[5], mMat[ 9], mMat[13]);
+	LOGD("  %f %f %f %f", mMat[2], mMat[6], mMat[10], mMat[14]);
+	LOGD("  %f %f %f %f", mMat[3], mMat[7], mMat[11], mMat[15]);
+	LOGD("]");
 }
 
 };
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 35825d2..1416ce1 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -35,6 +35,9 @@
 
 OpenGLRenderer::OpenGLRenderer() {
     LOGD("Create OpenGLRenderer");
+
+    mSnapshot = new Snapshot;
+    mSaveCount = 0;
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -47,13 +50,80 @@
     mat4 ortho;
     ortho.loadOrtho(0, width, height, 0, 0, 1);
     ortho.copyTo(mOrthoMatrix);
+
+    mWidth = width;
+    mHeight = height;
 }
 
 void OpenGLRenderer::prepare() {
     glDisable(GL_SCISSOR_TEST);
+
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     glClear(GL_COLOR_BUFFER_BIT);
+
     glEnable(GL_SCISSOR_TEST);
+    mSnapshot->clipRect.set(0.0f, 0.0f, mWidth, mHeight);
+}
+
+int OpenGLRenderer::getSaveCount() const {
+	return mSaveCount;
+}
+
+int OpenGLRenderer::save(int flags) {
+	return saveSnapshot();
+}
+
+void OpenGLRenderer::restore() {
+	if (mSaveCount == 0) return;
+
+	if (restoreSnapshot()) {
+		setScissorFromClip();
+	}
+}
+
+void OpenGLRenderer::restoreToCount(int saveCount) {
+	if (saveCount <= 0 || saveCount > mSaveCount) return;
+
+	bool restoreClip = false;
+
+	while (mSaveCount != saveCount - 1) {
+		restoreClip |= restoreSnapshot();
+	}
+
+	if (restoreClip) {
+		setScissorFromClip();
+	}
+}
+
+int OpenGLRenderer::saveSnapshot() {
+	mSnapshot = new Snapshot(mSnapshot);
+	mSaveCount++;
+	return mSaveCount;
+}
+
+bool OpenGLRenderer::restoreSnapshot() {
+	// TODO: handle local transformations
+	bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
+
+	mSaveCount--;
+	mSnapshot = mSnapshot->previous;
+
+	return restoreClip;
+}
+
+void OpenGLRenderer::setScissorFromClip() {
+	Rect clip = mSnapshot->clipRect;
+	glScissor(clip.left, clip.top, clip.getWidth(), clip.getHeight());
+}
+
+bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom) {
+	// TODO: take local translate transform into account
+	bool clipped = mSnapshot->clipRect.intersect(left, top, right, bottom);
+	if (clipped) {
+		mSnapshot->flags |= Snapshot::kFlagClipSet;
+		setScissorFromClip();
+	}
+	return clipped;
 }
 
 void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 1236336..8a541fc 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -19,8 +19,32 @@
 
 #include <SkXfermode.h>
 
+#include <utils/RefBase.h>
+
+#include "Rect.h"
+
 namespace android {
 
+class Snapshot: public LightRefBase<Snapshot> {
+public:
+	Snapshot() { }
+
+	Snapshot(const sp<Snapshot> s): clipRect(s->clipRect), flags(0), previous(s) { }
+
+	enum Flags {
+		kFlagClipSet = 0x1,
+	};
+
+	// Clipping rectangle at the time of this snapshot
+	Rect clipRect;
+
+	// Dirty flags
+	int flags;
+
+	// Previous snapshot in the frames stack
+	sp<Snapshot> previous;
+}; // struct Snapshot
+
 class OpenGLRenderer {
 public:
     OpenGLRenderer();
@@ -29,12 +53,32 @@
     void setViewport(int width, int height);
     void prepare();
 
+    int getSaveCount() const;
+    int save(int flags);
+    void restore();
+    void restoreToCount(int saveCount);
+
+    bool clipRect(float left, float top, float right, float bottom);
+
     void drawColor(int color, SkXfermode::Mode mode);
 
 private:
+    int saveSnapshot();
+    bool restoreSnapshot();
+
+    void setScissorFromClip();
+
+    // Dimensions of the drawing surface
+    int mWidth, mHeight;
+
     // Matrix used for ortho projection in shaders
     float mOrthoMatrix[16];
-};
+
+    // Number of saved states
+    int mSaveCount;
+    // Current state
+    sp<Snapshot> mSnapshot;
+}; // class OpenGLRenderer
 
 }; // namespace android
 
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
new file mode 100644
index 0000000..724bd1a
--- /dev/null
+++ b/libs/hwui/Rect.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_RECT_H
+#define ANDROID_RECT_H
+
+namespace android {
+
+///////////////////////////////////////////////////////////////////////////////
+// Structs
+///////////////////////////////////////////////////////////////////////////////
+
+struct Rect {
+	float left;
+	float top;
+	float right;
+	float bottom;
+
+	Rect(): left(0), top(0), right(0), bottom(0) { }
+
+	Rect(const Rect& r) {
+		set(r);
+	}
+
+	Rect(Rect& r) {
+		set(r);
+	}
+
+	Rect& operator=(const Rect& r) {
+		set(r);
+		return *this;
+	}
+
+	Rect& operator=(Rect& r) {
+		set(r);
+		return *this;
+	}
+
+	friend int operator==(const Rect& a, const Rect& b) {
+		return !memcmp(&a, &b, sizeof(a));
+	}
+
+	friend int operator!=(const Rect& a, const Rect& b) {
+		return memcmp(&a, &b, sizeof(a));
+	}
+
+	bool isEmpty() const {
+		return left >= right || top >= bottom;
+	}
+
+	void setEmpty() {
+		memset(this, 0, sizeof(*this));
+	}
+
+	void set(float left, float top, float right, float bottom) {
+		this->left = left;
+		this->right = right;
+		this->top = top;
+		this->bottom = bottom;
+	}
+
+	void set(const Rect& r) {
+		set(r.left, r.top, r.right, r.bottom);
+	}
+
+	float getWidth() const {
+		return right - left;
+	}
+
+	float getHeight() const {
+		return bottom - top;
+	}
+
+	bool intersects(float left, float top, float right, float bottom) const {
+		return left < right && top < bottom &&
+				this->left < this->right && this->top < this->bottom &&
+			    this->left < right && left < this->right &&
+			    this->top < bottom && top < this->bottom;
+	}
+
+	bool intersects(const Rect& r) const {
+		return intersects(r.left, r.top, r.right, r.bottom);
+	}
+
+	bool intersect(float left, float top, float right, float bottom) {
+		if (left < right && top < bottom && !this->isEmpty() &&
+		        this->left < right && left < this->right &&
+		        this->top < bottom && top < this->bottom) {
+
+			if (this->left < left) this->left = left;
+			if (this->top < top) this->top = top;
+			if (this->right > right) this->right = right;
+			if (this->bottom > bottom) this->bottom = bottom;
+
+			return true;
+		}
+		return false;
+	}
+
+	bool intersect(const Rect& r) {
+		return intersect(r.left, r.top, r.right, r.bottom);
+	}
+
+	void dump() const {
+		LOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom);
+	}
+
+}; // struct Rect
+
+}; // namespace android
+
+#endif // ANDROID_RECT_H