expand SkLua to handle creation of its own State

add lua sample

BUG=
R=robertphillips@google.com

Review URL: https://codereview.chromium.org/15742009

git-svn-id: http://skia.googlecode.com/svn/trunk@9247 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index a240f32..5531605 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -76,6 +76,7 @@
         '../samplecode/SampleLayers.cpp',
         '../samplecode/SampleLCD.cpp',
         '../samplecode/SampleLines.cpp',
+        '../samplecode/SampleLua.cpp',
         '../samplecode/SampleManyRects.cpp',
         '../samplecode/SampleMeasure.cpp',
         '../samplecode/SampleMipMap.cpp',
@@ -129,6 +130,10 @@
         # TiledPipeController
         '../src/pipe/utils/SamplePipeControllers.h',
         '../src/pipe/utils/SamplePipeControllers.cpp',
+
+        # Lua
+        '../src/utils/SkLuaCanvas.cpp',
+        '../src/utils/SkLua.cpp',
       ],
       'sources!': [
         '../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile
@@ -146,6 +151,7 @@
         'experimental.gyp:experimental',
         'pdf.gyp:pdf',
         'views_animated.gyp:views_animated',
+        'lua.gyp:lua',
       ],
       'conditions' : [
        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
diff --git a/include/utils/SkLua.h b/include/utils/SkLua.h
index 232ba9b..8f48bc1 100644
--- a/include/utils/SkLua.h
+++ b/include/utils/SkLua.h
@@ -10,6 +10,7 @@
 
 #include "SkColor.h"
 #include "SkScalar.h"
+#include "SkString.h"
 
 struct lua_State;
 
@@ -19,7 +20,6 @@
 class SkPath;
 struct SkRect;
 class SkRRect;
-class SkString;
 
 #define SkScalarToLua(x)    SkScalarToDouble(x)
 #define SkLuaToScalar(x)    SkDoubleToScalar(x)
@@ -28,11 +28,17 @@
 public:
     static void Load(lua_State*);
 
-    SkLua(lua_State*);
+    SkLua(const char termCode[] = NULL);    // creates a new L, will close it
+    SkLua(lua_State*);                      // uses L, will not close it
     ~SkLua();
 
-    lua_State* getL() const { return fL; }
-
+    lua_State* get() const { return fL; }
+    lua_State* operator*() const { return fL; }
+    lua_State* operator->() const { return fL; }
+    
+    bool runCode(const char code[]);
+    bool runCode(const void* code, size_t size);
+    
     void pushBool(bool, const char tableKey[] = NULL);
     void pushString(const char[], const char tableKey[] = NULL);
     void pushString(const SkString&, const char tableKey[] = NULL);
@@ -46,7 +52,9 @@
     void pushCanvas(SkCanvas*, const char tableKey[] = NULL);
 
 private:
-    lua_State* fL;
+    lua_State*  fL;
+    SkString    fTermCode;
+    bool        fWeOwnL;
 };
 
 #endif
diff --git a/samplecode/SampleLua.cpp b/samplecode/SampleLua.cpp
new file mode 100644
index 0000000..263138b
--- /dev/null
+++ b/samplecode/SampleLua.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkLua.h"
+#include "SkCanvas.h"
+
+extern "C" {
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+}
+
+static const char gDrawName[] = "onDrawContent";
+
+static const char gCode[] = ""
+    "local r = { left = 10, top = 10, right = 100, bottom = 80 } "
+    "local x = 0;"
+    ""
+    "local paint = Sk.newPaint();"
+    "paint:setAntiAlias(true);"
+    ""
+    "local color = {a = 1, r = 1, g = 0, b = 0};"
+    ""
+    "function onDrawContent(canvas) "
+    "   color.g = x / 100;"
+    "   paint:setColor(color) "
+    "   canvas:translate(x, 0);"
+    "   canvas:drawOval(r, paint) "
+    "   x = x + 1;"
+    "   if x > 100 then x = 0 end;"
+    "end";
+
+class LuaView : public SampleView {
+public:
+    LuaView() : fLua(NULL) {}
+    
+    virtual ~LuaView() {
+        SkDELETE(fLua);
+    }
+
+    lua_State* ensureLua() {
+        if (NULL == fLua) {
+            fLua = SkNEW(SkLua);
+            fLua->runCode(gCode);
+        }
+        return fLua->get();
+    }
+
+protected:
+    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Lua");
+            return true;
+        }
+        SkUnichar uni;
+        if (SampleCode::CharQ(*evt, &uni)) {
+            switch (uni) {
+                default:
+                    break;
+            }
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+        lua_State* L = this->ensureLua();
+
+        lua_getglobal(L, gDrawName);
+        if (!lua_isfunction(L, -1)) {
+            int t = lua_type(L, -1);
+            SkDebugf("--- expected %s function %d, ignoring.\n", gDrawName, t);
+            lua_pop(L, 1);
+        } else {
+            // does it make sense to try to "cache" the lua version of this
+            // canvas between draws?
+            fLua->pushCanvas(canvas);
+            if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
+                SkDebugf("lua err: %s\n", lua_tostring(L, -1));
+            }
+        }
+        // need a way for the lua-sample to tell us if they want animations...
+        // hard-code it ON for now.
+        this->inval(NULL);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
+        return this->INHERITED::onFindClickHandler(x, y, modi);
+    }
+
+    virtual bool onClick(Click* click) SK_OVERRIDE {
+        return this->INHERITED::onClick(click);
+    }
+
+private:
+    SkLua* fLua;
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LuaView; }
+static SkViewRegister reg(MyFactory);
diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp
index 05da5cb..c425a26 100644
--- a/src/utils/SkLua.cpp
+++ b/src/utils/SkLua.cpp
@@ -14,42 +14,44 @@
 #include "SkString.h"
 
 extern "C" {
-#include "lua.h"
-#include "lauxlib.h"
+    #include "lua.h"
+    #include "lualib.h"
+    #include "lauxlib.h"
 }
 
-static const char gSkCanvas_MTName[] = "SkCanvas_LuaMetaTableName";
-static const char gSkMatrix_MTName[] = "SkMatrix_LuaMetaTableName";
-static const char gSkRRect_MTName[] = "SkSkRRect_LuaMetaTableName";
-static const char gSkPath_MTName[] = "SkPath_LuaMetaTableName";
-static const char gSkPaint_MTName[] = "SkPaint_LuaMetaTableName";
+template <typename T> const char* get_mtname();
+template <> const char* get_mtname<SkCanvas>() { return "SkCanvas_LuaMetaTableName"; }
+template <> const char* get_mtname<SkMatrix>() { return "SkMatrix_LuaMetaTableName"; }
+template <> const char* get_mtname<SkRRect>() { return "SkSkRRect_LuaMetaTableName"; }
+template <> const char* get_mtname<SkPath>() { return "SkPath_LuaMetaTableName"; }
+template <> const char* get_mtname<SkPaint>() { return "SkPaint_LuaMetaTableName"; }
 
-static const char* get_mtname(const SkCanvas&) { return gSkCanvas_MTName; }
-static const char* get_mtname(const SkMatrix&) { return gSkMatrix_MTName; }
-static const char* get_mtname(const SkRRect&) { return gSkRRect_MTName; }
-static const char* get_mtname(const SkPath&) { return gSkPath_MTName; }
-static const char* get_mtname(const SkPaint&) { return gSkPaint_MTName; }
+template <typename T> T* push_new(lua_State* L) {
+    T* addr = (T*)lua_newuserdata(L, sizeof(T));
+    new (addr) T;
+    luaL_getmetatable(L, get_mtname<T>());
+    lua_setmetatable(L, -2);
+    return addr;
+}
 
 template <typename T> void push_obj(lua_State* L, const T& obj) {
     new (lua_newuserdata(L, sizeof(T))) T(obj);
-    luaL_getmetatable(L, get_mtname(obj));
+    luaL_getmetatable(L, get_mtname<T>());
     lua_setmetatable(L, -2);
 }
 
 template <typename T> void push_ref(lua_State* L, T* ref) {
     *(T**)lua_newuserdata(L, sizeof(T*)) = SkRef(ref);
-    luaL_getmetatable(L, get_mtname(*ref));
+    luaL_getmetatable(L, get_mtname<T>());
     lua_setmetatable(L, -2);
 }
 
 template <typename T> T* get_ref(lua_State* L, int index) {
-    const T* ref = NULL;
-    return *(T**)luaL_checkudata(L, index, get_mtname(*ref));
+    return *(T**)luaL_checkudata(L, index, get_mtname<T>());
 }
 
 template <typename T> T* get_obj(lua_State* L, int index) {
-    const T* obj = NULL;
-    return (T*)luaL_checkudata(L, index, get_mtname(*obj));
+    return (T*)luaL_checkudata(L, index, get_mtname<T>());
 }
 
 static bool lua2bool(lua_State* L, int index) {
@@ -58,6 +60,44 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkLua::SkLua(const char termCode[]) : fTermCode(termCode), fWeOwnL(true) {
+    fL = luaL_newstate();
+    luaL_openlibs(fL);
+    SkLua::Load(fL);
+}
+
+SkLua::SkLua(lua_State* L) : fL(L), fWeOwnL(false) {}
+
+SkLua::~SkLua() {
+    if (fWeOwnL) {
+        if (fTermCode.size() > 0) {
+            lua_getglobal(fL, fTermCode.c_str());
+            if (lua_pcall(fL, 0, 0, 0) != LUA_OK) {
+                SkDebugf("lua err: %s\n", lua_tostring(fL, -1));
+            }
+        }
+        lua_close(fL);
+    }
+}
+
+bool SkLua::runCode(const char code[]) {
+    int err = luaL_loadstring(fL, code) || lua_pcall(fL, 0, 0, 0);
+    if (err) {
+        SkDebugf("--- lua failed\n");
+        return false;
+    }
+    return true;
+}
+
+bool SkLua::runCode(const void* code, size_t size) {
+    SkString str((const char*)code, size);
+    return this->runCode(str.c_str());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define CHECK_SETFIELD(key) do if (key) lua_setfield(fL, -2, key); while (0)
+
 static void setfield_string(lua_State* L, const char key[], const char value[]) {
     lua_pushstring(L, value);
     lua_setfield(L, -2, key);
@@ -68,19 +108,12 @@
     lua_setfield(L, -2, key);
 }
 
-SkLua::SkLua(lua_State* L) : fL(L) {
-    static bool gOnce;
-    if (!gOnce) {
-        SkLua::Load(L);
-        gOnce = true;
-    }
+static void setfield_function(lua_State* L,
+                              const char key[], lua_CFunction value) {
+    lua_pushcfunction(L, value);
+    lua_setfield(L, -2, key);
 }
 
-SkLua::~SkLua() {
-}
-
-#define CHECK_SETFIELD(key) do if (key) lua_setfield(fL, -2, key); while (0)
-
 void SkLua::pushBool(bool value, const char key[]) {
     lua_pushboolean(fL, value);
     CHECK_SETFIELD(key);
@@ -224,6 +257,11 @@
     return 1;
 }
 
+static int lcanvas_translate(lua_State* L) {
+    get_ref<SkCanvas>(L, 1)->translate(lua2scalar(L, 2), lua2scalar(L, 3));
+    return 0;
+}
+
 static int lcanvas_gc(lua_State* L) {
     get_ref<SkCanvas>(L, 1)->unref();
     return 0;
@@ -236,6 +274,7 @@
     { "drawCircle", lcanvas_drawCircle },
     { "getSaveCount", lcanvas_getSaveCount },
     { "getTotalMatrix", lcanvas_getTotalMatrix },
+    { "translate", lcanvas_translate },
     { "__gc", lcanvas_gc },
     { NULL, NULL }
 };
@@ -460,9 +499,44 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static int lsk_newPaint(lua_State* L) {
+    push_new<SkPaint>(L);
+    return 1;
+}
+
+static int lsk_newPath(lua_State* L) {
+    push_new<SkPath>(L);
+    return 1;
+}
+
+static int lsk_newRRect(lua_State* L) {
+    SkRRect* rr = push_new<SkRRect>(L);
+    rr->setEmpty();
+    return 1;
+}
+
+static const struct luaL_Reg gSk_Functions[] = {
+    { "newPaint", lsk_newPaint },
+    { "newPath", lsk_newPath },
+    { "newRRect", lsk_newRRect },
+    { NULL, NULL }
+};
+
+static void register_Sk(lua_State* L) {
+    lua_newtable(L);
+    lua_pushvalue(L, -1);
+    lua_setglobal(L, "Sk");
+    // the Sk table is still on top
+
+    setfield_function(L, "newPaint", lsk_newPaint);
+    setfield_function(L, "newPath", lsk_newPath);
+    setfield_function(L, "newRRect", lsk_newRRect);
+    lua_pop(L, 1);  // pop off the Sk table
+}
+
 #define REG_CLASS(L, C)                             \
     do {                                            \
-        luaL_newmetatable(L, g##C##_MTName);        \
+        luaL_newmetatable(L, get_mtname<C>());      \
         lua_pushvalue(L, -1);                       \
         lua_setfield(L, -2, "__index");             \
         luaL_setfuncs(L, g##C##_Methods, 0);        \
@@ -470,6 +544,7 @@
     } while (0)
 
 void SkLua::Load(lua_State* L) {
+    register_Sk(L);
     REG_CLASS(L, SkCanvas);
     REG_CLASS(L, SkPath);
     REG_CLASS(L, SkPaint);
diff --git a/src/utils/SkLuaCanvas.cpp b/src/utils/SkLuaCanvas.cpp
index be6cb66..d3ecd74 100644
--- a/src/utils/SkLuaCanvas.cpp
+++ b/src/utils/SkLuaCanvas.cpp
@@ -27,7 +27,7 @@
     }
 
     ~AutoCallLua() {
-        lua_State* L = this->getL();
+        lua_State* L = this->get();
         if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
             SkDebugf("lua err: %s\n", lua_tostring(L, -1));
         }
diff --git a/tools/lua/lua_pictures.cpp b/tools/lua/lua_pictures.cpp
index 1314c699..ae4e1ca 100644
--- a/tools/lua/lua_pictures.cpp
+++ b/tools/lua/lua_pictures.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkLua.h"
 #include "SkLuaCanvas.h"
 #include "SkPicture.h"
 #include "SkCommandLineFlags.h"
@@ -60,50 +61,6 @@
     return SkData::NewFromMalloc(buffer, len);
 }
 
-class SkAutoLua {
-public:
-    SkAutoLua(const char termCode[] = NULL) : fTermCode(termCode) {
-        fL = luaL_newstate();
-        luaL_openlibs(fL);
-    }
-    ~SkAutoLua() {
-        if (fTermCode.size() > 0) {
-            lua_getglobal(fL, fTermCode.c_str());
-            if (lua_pcall(fL, 0, 0, 0) != LUA_OK) {
-                SkDebugf("lua err: %s\n", lua_tostring(fL, -1));
-            }
-        }
-        lua_close(fL);
-    }
-
-    lua_State* get() const { return fL; }
-    lua_State* operator*() const { return fL; }
-    lua_State* operator->() const { return fL; }
-
-    bool load(const char code[]) {
-        int err = luaL_loadstring(fL, code) || lua_pcall(fL, 0, 0, 0);
-        if (err) {
-            SkDebugf("--- lua failed\n");
-            return false;
-        }
-        return true;
-    }
-    bool load(const void* code, size_t size) {
-        SkString str((const char*)code, size);
-        return load(str.c_str());
-        int err = luaL_loadbufferx(fL, (const char*)code, size, NULL, NULL)
-               || lua_pcall(fL, 0, 0, 0);
-        if (err) {
-            SkDebugf("--- lua failed\n");
-            return false;
-        }
-        return true;
-    }
-private:
-    lua_State* fL;
-    SkString   fTermCode;
-};
-
 static void call_canvas(lua_State* L, SkLuaCanvas* canvas,
                         const char pictureFile[], const char funcName[]) {
     lua_getglobal(L, funcName);
@@ -135,12 +92,12 @@
     }
 
     SkAutoGraphics ag;
-    SkAutoLua L(gSummarizeFunc);
+    SkLua L(gSummarizeFunc);
 
     for (int i = 0; i < FLAGS_luaFile.count(); ++i) {
         SkAutoDataUnref data(read_into_data(FLAGS_luaFile[i]));
         SkDebugf("loading %s...\n", FLAGS_luaFile[i]);
-        if (!L.load(data->data(), data->size())) {
+        if (!L.runCode(data->data(), data->size())) {
             SkDebugf("failed to load luaFile %s\n", FLAGS_luaFile[i]);
             exit(-1);
         }