From 80a4c2cd34aedb4f1a2e5e7d1ac26a9aeebe41ae Mon Sep 17 00:00:00 2001 From: Alex Sakhartchouk Date: Mon, 12 Jul 2010 15:50:32 -0700 Subject: [PATCH] Work on synchronizing a3d created files and java layer. Adding culling to ProgramRaster Change-Id: I58ccc82d37edc9539289d5eba44ea0e720874af5 --- .../java/android/renderscript/Allocation.java | 5 + .../java/android/renderscript/BaseObj.java | 5 + graphics/java/android/renderscript/Mesh.java | 32 ++++++ .../android/renderscript/ProgramRaster.java | 61 +++++++---- .../android/renderscript/RenderScript.java | 8 +- .../jni/android_renderscript_RenderScript.cpp | 98 +++++++++++++++--- libs/rs/RenderScript.h | 6 ++ libs/rs/java/ModelViewer/res/raw/robot.a3d | Bin 144500 -> 144500 bytes libs/rs/rs.spec | 29 +++++- libs/rs/rsAllocation.cpp | 8 ++ libs/rs/rsFont.cpp | 5 + libs/rs/rsMesh.cpp | 41 ++++++++ libs/rs/rsProgramRaster.cpp | 50 ++++++++- libs/rs/rsProgramRaster.h | 2 + 14 files changed, 307 insertions(+), 43 deletions(-) diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java index ddb2abf7d7a43..bfa61f35cdbe7 100644 --- a/graphics/java/android/renderscript/Allocation.java +++ b/graphics/java/android/renderscript/Allocation.java @@ -40,6 +40,11 @@ public class Allocation extends BaseObj { mType = t; } + Allocation(int id, RenderScript rs) { + super(rs); + mID = id; + } + public Type getType() { return mType; } diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java index 002fc788326a3..28675dc4b0a87 100644 --- a/graphics/java/android/renderscript/BaseObj.java +++ b/graphics/java/android/renderscript/BaseObj.java @@ -81,5 +81,10 @@ class BaseObj { mRS.nObjDestroy(mID); } + // If an object came from an a3d file, java fields need to be + // created with objects from the native layer + void updateFromNative() { + } + } diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java index 5a538787d6015..4bee97a90c60f 100644 --- a/graphics/java/android/renderscript/Mesh.java +++ b/graphics/java/android/renderscript/Mesh.java @@ -59,6 +59,38 @@ public class Mesh extends BaseObj { return mPrimitives[slot]; } + @Override + void updateFromNative() { + int vtxCount = mRS.nMeshGetVertexBufferCount(mID); + int idxCount = mRS.nMeshGetIndexCount(mID); + + int[] vtxIDs = new int[vtxCount]; + int[] idxIDs = new int[idxCount]; + int[] primitives = new int[idxCount]; + + mRS.nMeshGetVertices(mID, vtxIDs, vtxCount); + mRS.nMeshGetIndices(mID, idxIDs, primitives, vtxCount); + + mVertexBuffers = new Allocation[vtxCount]; + mIndexBuffers = new Allocation[idxCount]; + mPrimitives = new Primitive[idxCount]; + + for(int i = 0; i < vtxCount; i ++) { + if(vtxIDs[i] != 0) { + mVertexBuffers[i] = new Allocation(vtxIDs[i], mRS); + mVertexBuffers[i].updateFromNative(); + } + } + + for(int i = 0; i < idxCount; i ++) { + if(idxIDs[i] != 0) { + mIndexBuffers[i] = new Allocation(idxIDs[i], mRS); + mIndexBuffers[i].updateFromNative(); + } + mPrimitives[i] = Primitive.values()[primitives[i]]; + } + } + public static class Builder { RenderScript mRS; diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java index d7c98aac04d55..c3ab4819a6da3 100644 --- a/graphics/java/android/renderscript/ProgramRaster.java +++ b/graphics/java/android/renderscript/ProgramRaster.java @@ -26,12 +26,23 @@ import android.util.Log; * **/ public class ProgramRaster extends BaseObj { + + public enum CullMode { + BACK (0), + FRONT (1), + NONE (2); + + int mID; + CullMode(int id) { + mID = id; + } + } + boolean mPointSmooth; boolean mLineSmooth; boolean mPointSprite; float mLineWidth; - Element mIn; - Element mOut; + CullMode mCullMode; ProgramRaster(int id, RenderScript rs) { super(rs); @@ -41,6 +52,8 @@ public class ProgramRaster extends BaseObj { mPointSmooth = false; mLineSmooth = false; mPointSprite = false; + + mCullMode = CullMode.BACK; } public void setLineWidth(float w) { @@ -49,45 +62,48 @@ public class ProgramRaster extends BaseObj { mRS.nProgramRasterSetLineWidth(mID, w); } - void internalInit() { - int inID = 0; - int outID = 0; - if (mIn != null) { - inID = mIn.mID; - } - if (mOut != null) { - outID = mOut.mID; - } - mID = mRS.nProgramRasterCreate(inID, outID, mPointSmooth, mLineSmooth, mPointSprite); + public void setCullMode(CullMode m) { + mRS.validate(); + mCullMode = m; + mRS.nProgramRasterSetCullMode(mID, m.mID); } - public static class Builder { RenderScript mRS; - ProgramRaster mPR; + boolean mPointSprite; + boolean mPointSmooth; + boolean mLineSmooth; + // Legacy to not break app in other projects, will be removed in cleanup pass public Builder(RenderScript rs, Element in, Element out) { mRS = rs; - mPR = new ProgramRaster(0, rs); + mPointSmooth = false; + mLineSmooth = false; + mPointSprite = false; + } + + public Builder(RenderScript rs) { + mRS = rs; + mPointSmooth = false; + mLineSmooth = false; + mPointSprite = false; } public void setPointSpriteEnable(boolean enable) { - mPR.mPointSprite = enable; + mPointSprite = enable; } public void setPointSmoothEnable(boolean enable) { - mPR.mPointSmooth = enable; + mPointSmooth = enable; } public void setLineSmoothEnable(boolean enable) { - mPR.mLineSmooth = enable; + mLineSmooth = enable; } - static synchronized ProgramRaster internalCreate(RenderScript rs, Builder b) { - b.mPR.internalInit(); - ProgramRaster pr = b.mPR; - b.mPR = new ProgramRaster(0, b.mRS); + int id = rs.nProgramRasterCreate(b.mPointSmooth, b.mLineSmooth, b.mPointSprite); + ProgramRaster pr = new ProgramRaster(id, rs); return pr; } @@ -103,3 +119,4 @@ public class ProgramRaster extends BaseObj { + diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index fa9eeda739ffe..240d54497db62 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -165,8 +165,9 @@ public class RenderScript { native void nProgramStoreDither(boolean enable); native int nProgramStoreCreate(); - native int nProgramRasterCreate(int in, int out, boolean pointSmooth, boolean lineSmooth, boolean pointSprite); + native int nProgramRasterCreate(boolean pointSmooth, boolean lineSmooth, boolean pointSprite); native void nProgramRasterSetLineWidth(int pr, float v); + native void nProgramRasterSetCullMode(int pr, int mode); native void nProgramBindConstants(int pv, int slot, int mID); native void nProgramBindTexture(int vpf, int slot, int a); @@ -188,6 +189,10 @@ public class RenderScript { native int nMeshCreate(int vtxCount, int indexCount); native void nMeshBindVertex(int id, int alloc, int slot); native void nMeshBindIndex(int id, int alloc, int prim, int slot); + native int nMeshGetVertexBufferCount(int id); + native int nMeshGetIndexCount(int id); + native void nMeshGetVertices(int id, int[] vtxIds, int vtxIdCount); + native void nMeshGetIndices(int id, int[] idxIds, int[] primitives, int vtxIdCount); native void nAnimationBegin(int attribCount, int keyframeCount); native void nAnimationAdd(float time, float[] attribs); @@ -357,3 +362,4 @@ public class RenderScript { } + diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index 66606aaf796bb..13360c35e24ad 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -725,12 +725,10 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jint native_asset) static int nFileA3DGetNumIndexEntries(JNIEnv *_env, jobject _this, jint fileA3D) { - LOGV("______nFileA3D %u", (uint32_t) fileA3D); RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); int32_t numEntries = 0; rsFileA3DGetNumIndexEntries(con, &numEntries, (RsFile)fileA3D); - LOGV("______nFileA3D NumEntries %u", (uint32_t) numEntries); return numEntries; } @@ -1203,13 +1201,12 @@ nProgramVertexCreate2(JNIEnv *_env, jobject _this, jstring shader, jintArray par // --------------------------------------------------------------------------- static jint -nProgramRasterCreate(JNIEnv *_env, jobject _this, jint in, jint out, - jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite) +nProgramRasterCreate(JNIEnv *_env, jobject _this, jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nProgramRasterCreate, con(%p), in(%p), out(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)", - con, (RsElement)in, (RsElement)out, pointSmooth, lineSmooth, pointSprite); - return (jint)rsProgramRasterCreate(con, (RsElement)in, (RsElement)out, pointSmooth, lineSmooth, pointSprite); + LOG_API("nProgramRasterCreate, con(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)", + con, pointSmooth, lineSmooth, pointSprite); + return (jint)rsProgramRasterCreate(con, pointSmooth, lineSmooth, pointSprite); } static void @@ -1217,7 +1214,15 @@ nProgramRasterSetLineWidth(JNIEnv *_env, jobject _this, jint vpr, jfloat v) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); LOG_API("nProgramRasterSetLineWidth, con(%p), vpf(%p), value(%f)", con, (RsProgramRaster)vpr, v); - rsProgramRasterSetLineWidth(con, (RsProgramFragment)vpr, v); + rsProgramRasterSetLineWidth(con, (RsProgramRaster)vpr, v); +} + +static void +nProgramRasterSetCullMode(JNIEnv *_env, jobject _this, jint vpr, jint v) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nProgramRasterSetCullMode, con(%p), vpf(%p), value(%i)", con, (RsProgramRaster)vpr, v); + rsProgramRasterSetCullMode(con, (RsProgramRaster)vpr, (RsCullMode)v); } @@ -1352,19 +1357,75 @@ nMeshCreate(JNIEnv *_env, jobject _this, jint vtxCount, jint idxCount) } static void -nMeshBindVertex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint slot) +nMeshBindVertex(JNIEnv *_env, jobject _this, jint mesh, jint alloc, jint slot) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nMeshBindVertex, con(%p), Mesh(%p), Alloc(%p), slot(%i)", con, (RsMesh)s, (RsAllocation)alloc, slot); - rsMeshBindVertex(con, (RsMesh)s, (RsAllocation)alloc, slot); + LOG_API("nMeshBindVertex, con(%p), Mesh(%p), Alloc(%p), slot(%i)", con, (RsMesh)mesh, (RsAllocation)alloc, slot); + rsMeshBindVertex(con, (RsMesh)mesh, (RsAllocation)alloc, slot); } static void -nMeshBindIndex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint primID, jint slot) +nMeshBindIndex(JNIEnv *_env, jobject _this, jint mesh, jint alloc, jint primID, jint slot) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nMeshBindIndex, con(%p), Mesh(%p), Alloc(%p)", con, (RsMesh)s, (RsAllocation)alloc); - rsMeshBindIndex(con, (RsMesh)s, (RsAllocation)alloc, primID, slot); + LOG_API("nMeshBindIndex, con(%p), Mesh(%p), Alloc(%p)", con, (RsMesh)mesh, (RsAllocation)alloc); + rsMeshBindIndex(con, (RsMesh)mesh, (RsAllocation)alloc, primID, slot); +} + +static jint +nMeshGetVertexBufferCount(JNIEnv *_env, jobject _this, jint mesh) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nMeshGetVertexBufferCount, con(%p), Mesh(%p)", con, (RsMesh)mesh); + jint vtxCount = 0; + rsMeshGetVertexBufferCount(con, (RsMesh)mesh, &vtxCount); + return vtxCount; +} + +static jint +nMeshGetIndexCount(JNIEnv *_env, jobject _this, jint mesh) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nMeshGetIndexCount, con(%p), Mesh(%p)", con, (RsMesh)mesh); + jint idxCount = 0; + rsMeshGetIndexCount(con, (RsMesh)mesh, &idxCount); + return idxCount; +} + +static void +nMeshGetVertices(JNIEnv *_env, jobject _this, jint mesh, jintArray _ids, int numVtxIDs) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nMeshGetVertices, con(%p), Mesh(%p)", con, (RsMesh)mesh); + + RsAllocation *allocs = (RsAllocation*)malloc((uint32_t)numVtxIDs * sizeof(RsAllocation)); + rsMeshGetVertices(con, (RsMesh)mesh, allocs, (uint32_t)numVtxIDs); + + for(jint i = 0; i < numVtxIDs; i ++) { + _env->SetIntArrayRegion(_ids, i, 1, (const jint*)&allocs[i]); + } + + free(allocs); +} + +static void +nMeshGetIndices(JNIEnv *_env, jobject _this, jint mesh, jintArray _idxIds, jintArray _primitives, int numIndices) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nMeshGetVertices, con(%p), Mesh(%p)", con, (RsMesh)mesh); + + RsAllocation *allocs = (RsAllocation*)malloc((uint32_t)numIndices * sizeof(RsAllocation)); + uint32_t *prims= (uint32_t*)malloc((uint32_t)numIndices * sizeof(uint32_t)); + + rsMeshGetIndices(con, (RsMesh)mesh, allocs, prims, (uint32_t)numIndices); + + for(jint i = 0; i < numIndices; i ++) { + _env->SetIntArrayRegion(_idxIds, i, 1, (const jint*)&allocs[i]); + _env->SetIntArrayRegion(_primitives, i, 1, (const jint*)&prims[i]); + } + + free(allocs); + free(prims); } // --------------------------------------------------------------------------- @@ -1473,8 +1534,9 @@ static JNINativeMethod methods[] = { {"nProgramFragmentCreate", "([I)I", (void*)nProgramFragmentCreate }, {"nProgramFragmentCreate2", "(Ljava/lang/String;[I)I", (void*)nProgramFragmentCreate2 }, -{"nProgramRasterCreate", "(IIZZZ)I", (void*)nProgramRasterCreate }, +{"nProgramRasterCreate", "(ZZZ)I", (void*)nProgramRasterCreate }, {"nProgramRasterSetLineWidth", "(IF)V", (void*)nProgramRasterSetLineWidth }, +{"nProgramRasterSetCullMode", "(II)V", (void*)nProgramRasterSetCullMode }, {"nProgramVertexCreate", "(Z)I", (void*)nProgramVertexCreate }, {"nProgramVertexCreate2", "(Ljava/lang/String;[I)I", (void*)nProgramVertexCreate2 }, @@ -1500,6 +1562,11 @@ static JNINativeMethod methods[] = { {"nMeshBindVertex", "(III)V", (void*)nMeshBindVertex }, {"nMeshBindIndex", "(IIII)V", (void*)nMeshBindIndex }, +{"nMeshGetVertexBufferCount", "(I)I", (void*)nMeshGetVertexBufferCount }, +{"nMeshGetIndexCount", "(I)I", (void*)nMeshGetIndexCount }, +{"nMeshGetVertices", "(I[II)V", (void*)nMeshGetVertices }, +{"nMeshGetIndices", "(I[I[II)V", (void*)nMeshGetIndices }, + }; static int registerFuncs(JNIEnv *_env) @@ -1532,3 +1599,4 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) bail: return result; } + diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index 8e6b5c67e3d87..745df95e445a8 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -244,6 +244,12 @@ enum RsA3DClassID { RS_A3D_CLASS_ID_SCRIPT_C }; +enum RsCullMode { + RS_CULL_BACK, + RS_CULL_FRONT, + RS_CULL_NONE +}; + typedef struct { RsA3DClassID classID; const char* objectName; diff --git a/libs/rs/java/ModelViewer/res/raw/robot.a3d b/libs/rs/java/ModelViewer/res/raw/robot.a3d index d220c811c13e7f549c880eab51fe4368cf345561..2d7d32b6fb0233933f262a6a6cf5779ec39e3929 100644 GIT binary patch delta 29726 zcmW*T1wa+++V=5@HEU`u#cnY%5xctsJFyG9uyw0rcQ>|RD|R4uCw7a9Vkg+`{_}kE zUB7#-dB5*G_g?F`8TacspI;iEUmBx#%e9X=iX=-aQMg>(X~ZSDB}39BlNekUcQ&yk zkCa66N=Zo)$wx{qC6iK+lmNm-@rBpZk4V3{0JPShknx*tuIa!I-AxoMh|N6JgjOUotYll-OpqyPp;1?dH8 z`J_Tp5vedKip8Yj^y0KaQVFRfy(F!eR7xr>IVGSZkIP79c~F*CilcMVbJEIicscIM zN#)T`sz9$m%faCl=@n^y99~(fM5>^_RFz(pR)AHi(W}w?rRq`*dJS4Z4zI~FHKkfo z5r@NT^Po1hIEUAv*P)f<@IZPXtu#lMrP+uUYs}%nEE6ns!DdoddRJN#sktL{ z<3TrSGY;?0GTmv-r5;i*sVC`;eWbqhzO)`3-j8MaNg>!)>QC=aYs1m)>FsH4S!Dos z1Ehi2Q5wWDgQUUOSsFqgLhC3Em4?xWNzM>PS7|ug443}HZqf+)2wGQG8A%^W>&D@u zSZ0*uU|(r8eKf5XhmT>IG16G1P%|Shtejo$`bk#+GGx2$}&r( zWjI}0PG3%&$|@`9D`?X>d?m}Qlvd$vX*GQ{Z6>RP(Zgu7rEqDjw1%w1_0k3@+<~<` z-Y9M2Zj-bb7fV}MW{VVoq0(0RR@!1#*+$<+3+3?bEVEtOfy<5yr9-$? zI!r%I+rlbG=tpQmYPU$%PIBh$toS>he?d0&2EOSyig?pvb^wSP?H@lqS z!5JRyjv`oW&^VJa^}%3wThv$TAnDXgn-kqF-nUHtjTr-(i_M(p@~j@n^{$ zjF#?8_ecz0lpfF@(4sl~A^jokBC9;2KcZcdVx=e2V-kngq^B(NRC?wxZb~nt=j0{c zkY3SW(Qb11Yx-;24OV$We?z+^y_Mce@5l$dAjPvxyp(|VrH?H0QTl`rq|fxvwEGn3V#&}7Q6Nri*C71k(X!u!j%O8mgzeu|LnYiW84!aog7oy`g$&|kn3x7zq z{F8X_m*kaylO*^@N-F;%Ui>Th$bU#u{3j)o{}LaRCdsvMFa^nGAA9%p_-~XQr7P?#nX1au!S~XQgMQ zc{w~AJsT~loJ`J6&rb7Ul^iUSL(YkQaxQutHBj;wD+;SdFE9YgIytLFDo{yf7 zmX=lW)AQ5PakxLr_{#+_vm8JVpk?6jf-F-|E`(X-!t}y4Uk)!qFG9=8Dn;ohPc{wFrk-lZ%t;SVFEruR$xu;Wb&N zrd$h4%eCpXX(c(l4!sVoG^+&C18HSAye`YsmFr;zxjwxgtg_C^p>=m9NvoFidLId zTGLz8>Tq})mT4om#rkqPdOKQO4sS0vbf5z}HIh5>pd+mThj*fPqBUZb&h*Z-#vC5Z zGQn~eY%X`DccnGu@NO*AP412@>k^5suc>sL?tv!biqz|NZ7)p8gQ!l*M?C7fl#$#$G6uacb=u#Lwnm>^mf0)s!}W3`J(9MTRrb^O)7Eo%6w5@(2XM1|kbaQ1 zk;4zs579QW%3=Cp+7^eykFd=V`6#a9_-*6}?vRghcT7HxyW|t}6SN(ya*}?Mwu{40 zvCJv?G)Bs2=x1nqS>-JKEG<$#C!eREr|swP3oLU%zKDn9Xu1pg33;o zSmhr59__4r!IAH?&3!orugDMO2jmf6mSgF$v@0C`nEse{nWL}MuhXt__!I7)$Z>c> zeoB8zyT&Td=+9_3G8CO9G*Z=pv7`@96gTqSpFz~5-U7q#<9vL`7_TF7kS3~LVG5EmA}j1 zqYh4B|cmPg5uv z#eGJeW>S2W%v`=o79}fY4puJ?)2`odaTle-hhQs^Q`_tNS_yGC`Y2jY#ZgAnN7MRn_!yQMqm0G= z$~gKsWsJiZpp56icxrzRpFp2L8^9_P=@V%KIeHk&3{xiIP-QZAla(nrT$xIrN*l^5 z)9BM^!#R99%S=~h;3#D#eI{)LtIVR$qK#se*@`n3=CIQ^WiAiq(#CN3Jo-G^I1Zmr zpHCalDhucfXcIVk3d>AU7UE=O5qFD}#W+<7rH9fcbNEta30a2ImF4v1v>6<}g1&+_ z-C>uNJXlGc$>FP5W|guU=PF_JFxqSm52uII=5qKN`Wo6iWv#MaSw}YD6pmj=HsT^> z6L*`G%^0d|p>Lrr;_wKTiBPuUa%CIc*+yN;;oEtzokz=AWe0r+Z3Tz#WSO1HE(}w4 z(|6NWarhqk9$FZy?4|Feg|o^&mf5F7;$n{9NcQ6+|myi{J{E#)=KyjI@e9px?kE$tSoyraLP-Qn=}Ec0IZ zfDe>-dOYnOt0b^Yg7Oh#l}{}5N%^cek2(Ac557=iS>-GJEA0t~e`A?%%6ELF{8WCB zU-(k_P5(`M#o>Qg=8y6h-zxv;|7fpSMN;3Ae>{E9Z-ze*1wSe-RaIT8h6##W{pf&8 z{m9|E`k5H`MKRS+M8~g+rG6nMep77qJMrL8#jE}zN$|IlRQ*Z3_(${CCj8#Q=#KhQ?pHKH4VDev@DaB=Hl>lER#-6kEWV|o`I&TmYR{C zk!Gs4nu%pHshQEM`m&6#ngx@pS?O76UJlR3GTGGZm|V?4&q4EHot*TX4mCNu__2+j znhR5_xmhN+ng`RWdFgp+sX06!%j8q@V+Pfq?oUg{Dh22TXc<@~fMo*Ig6OLjVwpl} zVa%!)p%&A^60NtpjV*fW0i{ZiZp*#sl+ms)XG>$t->-@)T&rStwyh=R&f|b)#_|h zU9Ew|)tdC0w4$t1i(ZRXoKvSD&>dPY4j-eACS$RmI*vY$7Q!my>Emhr)B%n|ror~ktdGvXzGlwx=ozH{$)NvfXfMph_3vrUVh`xw6fmIgM7tr*R$QrWqi>@*%h_c+54KZRsyozO>Q1s7!__@3vq#;FYt()8eY9{6k7Suhbw93G zqv%nzwXAZ0et@=~!w<5|LG=)BRu9t;(>Aio5&99@X7#9gOx@zZ5gu<-kF(8j^#tx# zPpK!#Y22lrp`W4c=J2yDb5=cv`_%LF^Rzu2et~5!s24FxjiyJ_B3b1U{SqyT!!NVU zW%UXkRD7M@aX({IyGv&tR%9oi}Nu6mDt zk9LMt?z7B&H3l!J4_M}b`VgblNAyRu3#<}LkEKO(_+yrN?5Ix|SJgNk#PR4dhd-r1 zrCnu}XY^;ZYaITZWuB`q@V5Gr{*rc+!(Y)~(QdQKYx-;29rca+mj0G@kHg=w%scfx zK2kr>KhPeqiWAR+cnqXmhibXL6BB=`w(AG6@R#aw{UkR2R=qCg z7kFI1I6R5#FG-63R3FzLk_07=|Hmtf4<>V^a3v=xF_kN|D;Y_FhARy{4J{Rir=_Q* z862LDo{pw-czSwzn#tiA=ox61!{HfukdbP;ysk|2Of-)xv&)z6OY^$2xU$l-(lT>& zN_t9K7FRY`4p(-P6a8GdUAbI&NN!hNW^PwLp5>?KXZq6$a94mCKr7%X=ql_g(?mUmTjRp6@V zs^qHds=};@m0VR_)x>=@o>q6&5cf5BTGMgW5~UW8%e!ie``WIWt~#!|u0XE3u6nNe z*nq1pPa9$*uDU#Jj7_-e^0X;7ByyTDpQrD-6Qcqz$&lw!~@2XzOa{ zYEL>~d+bO$VMpvtg0VApAziTxb|c-f8}=YQu?O}dy|EYeA$_qA_9G$K5Brk=*xxn4 zaSh~l5Dvt_WC#w%p=1~i#o^>X9F8N%NF0Hqh=ZeWG#P`VaV!~!V{ts0fa7r@nS>K@ zGMVCXCUY~zHI+=msW_d?!09-X%)*&Co6N!4IG4=Bxj3IJ!1=h4EW(A@g0#fNB$NbU zC@vwbaS1MUpsi~uS6kOI(jJ%La?%l(;|kIlSKvz01y|xK(hXPPYSIH&V;Jd$VHi&O zU^uQJ{csJgCH>J^%UDMS;yPSU2IG3%K!)N5+(?GwM%+Y3;3nKmM&V}MLPp~jj38q% z0=JU!xD~gNiMS27yCyn({M)%EyLOPNxC3{R>9`Yjk(sy)cazz;8~2d8xCi%=`M4MN zk%hPq7n8&7Sp|~HT$P$clIZIpzxLt||@E}=+2k{VDj)(9tS%HV~2w90o@F-b@ zNAVb0jmPjf3B%)ff`sD0;umnob6pF+L$j@Cn9|qZo%z$uWG2&&Y9nhR?|fe2y>3Nqm7XUCv29{+C>*T(8J! ze1)&c8GMaz$XR@YZ^=1)i|@#Je24GJ1$>Vm$VL2s@gy4KF@apd1pG)Y<41?_iCn=? z_?cYA&-jI0!!P)iT*t5YjoiR*_?_IuAFf}npX4|G#6RRO{=qxs9{wYec84#3KSZY8 z<$o`K*X1Z0|GRKa(Ny9>6*b~UU3=s*NGuv!tji>i(bOKhEb;^`?TO1KacFCCE)RK% z9_^{iOP--ud*(_)o?{a2xhpAofl0L&j>|`T!N>2T`Dia)$;c~AroD0{C$BNN_S%(# zyulRO8&^v57E@|(U8%@BOr^bZr6%t&wf5eXhJ3&@+6Px!5|3%Mcvm`-fa$aZS97%$*~-kCn>NzRv;;{0#+oc zup(9>sj(7PCTXyWR!yr)s$*5GL26+vPjCw^ET8<0HM02`9L*bp0$eAo!{lYg%KT4U~-5Pxivu@eT9F4&bXp&KdfFuL(QbSGu7 zJN6)Du?O}f<*=95N9#@cVsGq6La-n9Cj+oQ4kUwcur^d1LaN{p97d|*FdR;5;Ba*Q zgId~uT(z_jq&AMgk)#fe#8D&=N1;RNqJyK!7#z!&Fpf09aX6kd#PK+RG{Onkm?VAy z6Sc|OBr-*t`ZS!b&C+JmXW&e2jy9J*3ukNdv<2FHt_9jc zZIQN^J`WdYq1qDqB3!I3)t1piaf!BETdA$!TB)tlR%_u}7+1KqMswC`>$P=U>$MHq zMs2gUiEFdAMT^k3XJJwnN)VBDi*Ow@cfNyP12mz1lw9gS)jzZ9jc4?$e^Q z1N2DTuN~A5(WCHyc33;A9pO5v9n+3G+DYvM*GcV^c3L~59p^ejJFA_epT;xVdF=xI zES}RYYSHxbctN|QU8Y~eXzhx2O}ol?%ekjZ~QS@Z|?hw zxBM6_g!=*F%|1pO#QhNQG9RN25&3>Rg(jJmnjMX0FW9QY1fOd!_%dH;FEwWeH!rnUT(7x%&9|NSB=I{>{C;L9tXrM)Gw9rGm=)oi;DJDT5k_;Um zMskt@lVeJf3R7Zgk_J;_T9OXaVtSGR(_==G2{U45;)|Iv3(1OEFdNB^*)Rvmi8;{E zozrpWV)$Whk_U5RUXl;BVFj#6+*lDS5d$k> zWny7vtU^3k1*?)ISQV=gABRzm@1Z(Lj@7XSNr^SECP|Gou@*^-wXilxkF~K5$%u6@ zkYvU{tV^0hgb_WO?7wks5V>j$UdSVakMS5c|?BjNNyZbWw@g;KKCkNe%3e14vCA zfCEV_9EgKRZ5)JyNgW)FLr5SF!J(us4#i=l9uC9dq&^OJ82^z5_#ciS4RHjHB#m$+ zjv|e56gs2{Iyjm%#nCv1G{Z4CmNdt)IF7WyaX6j?;dpFGoYIU5?n&;6WHL^~DP$^6 z!D(bV&frU!N!sB|oJBg~ESya`<7}Kmy5Jm~OS<7)oJV@#Je*H@;e2;5$Gw2tKDYoE zl0~=>7n4w2!k4g=48Wzhj10tOxSR~a<+y?j#ud1d48fJSiVVe7xS9;Z)fh&GyPYs@ zhP%Vbe;AHy$Ov46YspAli|fcJT!-t4gX?hv8I2ooBN>AmaT6Jfn{YE3hg;lR-4SFP zM&JYoCh-MqCp*YQ+<`mE6x@ls$TZxAyU8Bh%a^c^%))&bNoHdt?k96_KSq(c7=;JO zJUoC0$$WGUG7gaicnA-Zg?Jc`ki~cekCJ0}oG;-7S%xR@Bw3Cp@f2Bsr|>jciKp=l zS%qitELn|b@tk|L!^eM)E6jbKgyVU_&{lMQ$ouaJ#+ z)qUN4joiR%c#~|wn|O;v;4QZk!ABnh4&Eg@@h;vYyYL>~C%Z9*FW~{% zhY#=}iNuHai0sEl7)zos79W!X_!ys%gAU^f-$NWZgmL(k9LA^kj2yvd_?#TY7kmjX z$q9UkugFP!g|Epee2s6&X?%lk$r*f$@5otvht7LA=YG$1&i#R$#}61!E?_(+kc*gr zA4xQR#82cBe!|b>GJbJ?bAKgQ@hg5O*YG?3AUE*`{v@~DKOJsvxo?y0_>24|cknm< zA$Rc){w4SDFa9IfA@4Db{@$IIe89B&2X{IWkLmPycY2b5>GcG62J#Uz z>Y4RST$y!Wk_9u7tRx#|*T1-Pkgu3S|LV?3zGF`PyW5ZaKtJ92;m)Q1;7iD*=hA=j z1^g6W!*6$P{TD5_o=4BC=hyQQfArT22m_b_dO=|!W+A<>un4n=UQ}3&Sxhf3EWs?H zmlT#_meNZL%Q)PW(aQ?UG0W-Yg%y|;^oqht%u0G?tjw&USJkWQ)wrtbHT0T#EqYa~ zuGiM<&}(8XJy5SpuZ?x|dU}0&AlB6z=#BJ-T#fX`dK0~w?lje#GMeix^dKHI!Df0( zy%oI$2I;N!HuRR*N^h&TqqoL3dV9SCy)CxWJM!^o)H~_HdS|X+y^G#e??&&0!FqSS z2fZtH(|hW@=-sgg>8ba2ppV|0^u@l+e)=DNPY|O2)=IfUxDV0)5n23xAVgPrritGJ zgy=f=CjZ_I(QWa@AEJA?Pb%K>L-b_arw}jpA$ltA(}^Gh@Q#qgy_D! zwTI|g#rt}Qo}K%gyrYNcxp+g*#mqzU@j@P=`-@lc5IunVLgMv1L@&a9G4aYBqL<*l z6tCI+^?~{TGDz58AIvp`K13giL-k=eOdpN|9maoLBPb*EkvLKxg`;!_9ep&8*2mx& zeJqaE$Kg1AJdW2V-~@dlPShviK%C4qg}W*GRPki0K8bE&>exSY9MUm;w{T&b@Tu4b;* z!-V0?aD5G~VXh_X9L8FGoxYxI!1cJ1Y{HGWnQXz$7(upT1a2eSaU1R+J8=i@BD-<7 zz6bZ{dxiU$`}9cRe&&8XN_aqbqVxm$LFxhh5c80JSa^haL_aD##yqAU7oK3A&`;t? z<|%R-PvIGI7SG^0avsm&1#%HDU^Kae(Ri6$(JwpPT+y$RYj_pslX*CwkNY~gAzaK{ ztltzaVJ^{c370XK>9>U|m@D)d?Z)#BVH%-@j4&(C-PZ%lX+ABBD}@CrGFLPX5QAn3GXoP=--8RnRoRc z!h6hn`cL6~=6(Ga{?hL|`fo}M{>DG#0sg_iIpVRoI`x!Y6$B&y_2y?0rbZJQV;{M5Gjm>um~xNMX(qtj>U}!mMIc6;@+bGpb|a2VaBK#G1@n#Dlf4Ht}K|qplH1>R}+(Ck?PZ zHYAO(q2V+#8gttO8)H+_44Yy~k^)om;WsBOFfAW<5NRpQ$joT85@u#*Hd+g_FtZqK zgxQ$cjJCoY%p68LVS8qKql3ffz)eTe2|F8Ij9`)ngRv{gi(RoB@yBl1odjTa>_G}) z59~>bU{CBtieWK6{9dFtmgdv$L;4EK8%|$7@_y9veBAv=h_EuVve92ym08soAgs=; zZVVI-Vh%C}<6!0xG8Bgy{~5zc9UP7$NFa{Dk)%G3#8IT7G0NeFZ-F#M2S<~pI2xOi zl-QgPe+(ImL44ff$arBZW-DWYur;%_F;Uot*~XY8Y|CtGOcu6dwlk&(J20IN##F{s zzJzI{BTnZ_m_dSZ2F@g1aVE|p-EbDpCf#v1&LKT;4$dV#aW2jyy|5P_{yZ`t`|xQm zAPW<9__!C6MZyqfh_P7MpV{9C6%JqyFqQ}hG6xz%_-DR?PME^~MGq$49@BY!Xgj zPB1nLCo(4*TZEIClZ*)AWaeaJt8j|L%@kvsa4K`Eu^qQFcaUkglP_c!nSr}-H<^jM zaSxe=dvGtAjeBt)nS=WSwMa)IUA>m@?V&kwd zlo@Iq5iVgaF^&qCGM5_1gv*%AjN^Emd4ep*lYAkk$Vxngr^zZjjb|KKZJgm+ZJZ@x zcoxr*a6E_S$r?P5>&O_qz(;?PY`|#avT=#)vT+5k7+1Nj7}xNcah>a$aRYA{H@R*Y zxA2y6o9mWw$8c`LU9LOEJ-lb!=elRaV2tsAE5>+;4~<7$4~=lI-s!S&pDi7$;;TrZ8+_}X~G_1bugZ;f}xJBRU}oA<^CuJ=Yf#v2J- z@y18vlkvs)%=N|iieHUyTwjgv_}%!y_1*Z1KaF2pKaJn`+xWxv+xUxrjelH!4axjR z_)qPdlBu9#@}FHm7do!QKhD6d8^v$((cm}xhMD+(4AVl(w7D$PgC6q&AHK(2Yb@Y3 z)oaev+?^n& z#cR3OJj?xg@ha{$FLHlLyncJlE8Jh>jXR0yV>(G8nVHl~&P>7OVzfVD2BZ--a2SnA6Ksr4Ni!_N;mt`4VMS&|Ge}sO zS=nrfEt#!I6>NpANi}SZZAd|^$>D8DJ7FL*&}=WP$E;^M9T*+isS{~{ov<@$jGZx< zG{vT77wlqo#TFdijdT~bWVSSWU=L(okLU9X5kR`Z`WS?*ybDbF}T+dw3GLhzfj3OKH04p6NTkw#1#5_!n;!*P$ z9y5>QHV!{QP6~H0cbKQ}wCU_L&#>1nJcDP+Zaja!;1R#9OwW-+U{umrP&RZ>`rS;{JnrI}?&SuAH&u*#E) zSRN~p%2)}jkg8Y(tC8wh4Qr5^SOaU3+Llv`o7#NfwfXq@52f(o*CByu@Nw5A^@JX# z$Eq((!c1Z{5c)8EtcJqm%;Z)hVM=C7tFbUOGqu$On=qS_G}w$Up}7O;t>#?mtrjE` zw!k3bi$U0uWW|=)ie$%D*qY?T*4T#R!d!g#ZAe?p&&S=4v=15*B9`w>k?;GD}*)!qUvrRu}BT>`KaDH@<}Kq&#-V9;6cXz@DTE_QYPK8ur58 zqz3lJK2{BfkG~IBEk5vCeEfB+zE+6UkMzg7eB1-bKw*7meQS`g0keTMSlE!+&>A9a z#B5{@6*guzwuT9tFq>GzaX9lo(iBHnPBUvH-$Zj9iK9ph9EA=ELI+2amN**6kXASb z$CB1K7RQk`INq9QO(2u79Uu2(GDX;d*}@NPWKH8EpDygo>}<^t1~Y@L znZhp2F4ioZ#hgvL;vBw&xuiSJ#d)L$&cpenC(g$Oq!%v0g`_twL}wB7u@-Ulu@;j$ z*pClClq?Z0WiGXr370dMTPuVsnJcYT!qv>xRu~T9;|?claILl8T1PerH!?R`n{X3z zGudKocDUJMMUbr+f!oM-+=e^IPTYaJ$Zp()d&pkggZoG%?!*0L0#4w=-%p~12bc$} zgTh11L)Ky85vFs*I?6a=9b+D|j^lJb?i1uBp0dtZr^#92Ip#U*Jf3G>AQ$lhMw3ez zjhD$4yo^`LHN1+~$ql@YH_0u$=`e1SJ9ryINk0tbwz$g8D>2cu4AsV9tk%vH(0U4P0UT!V|>hfLY&QvCwvKUBm(2`DcOck@fq2H&+s|f zh0pN?*@G|eCE15B@fF#R`}w$Ek=Me5%!Ae&;bG=s>#gvp!_86ao$xsGxb*YPWUBR4JQ8#gzt@8mXq z$GapH@A7f~AU}oonfI+mP+DKwc{)od5SLkDSxp4DIY(7sNa6ZAL)F?$Il@QYo@)c9tU#&FuSBH;3jh)8+W~C+HF|GZb5BxhH{}0}UR4bjWTIua{ z{PP#x%3$kOMmqz45vG-iCmHNawwE`f%sk0tXSS2_X5`D0%(kzcj5nh!Jn^-&*eQ53 z%F2^0c2+wTZ${a8lGV;;r?s3^R(2j`v$NY7c^%4OXRvbGIqb~54f*jThwW!);cX}v zPyFm$b~fIIa`PmYo!idA+fW{!p?o~aYv;4`@J^JUC;9CB zwtwO~k)6*fVEfwvycq@X#NV+4>_UmJMm!3z3))3^H7dlDf_5Rh7;i>}6Q9_H?Gn5h z72yfpF2$QsQJxgBi`r#*8!E<=qINO6B5y;*?Fv>2ySQDMx1o|eDQ=gvtMWEfiYF!Q zQg(IThD!6KlwI1c$=i@qhDW9CGIniVhsyG(j9r!wyfzxCx2thgw`*VxyC$h6tZvulX&o$Y2lD?qkXDWa+I6`C?Rt(~&#upH zeY*kH<55G>NLb!(%vIiQf=%qEq?xe1-JGW_@TV1I{~&4cC%+tn>|gvs46=XoOEAd( z!!N!d`!B!vg7~E;*+JqLT@b(U6z*L_wH=kA@XIME@jt#K9vhhWizq|mqNT>ffBz>wO^b>DmJi}-RfmtG zB_B|AyA^-cfp%;Dy2{&a?6!7$yB+Bu?8xkBcM^7HcD92tnAwGN#V**5bjNPkgY?9n zb}#H@_ZIeH_Obg4`!W03P6#7}oBm_~_Q!!_5Dvt4q%F4N@WEt=uoJVBJyaOX47P{i zFy?U51&8B*q#OQ+BS;VI!QmswNMT=xo4)oaVF)wCcF}iNAVch zfxGPEta98wfxGRK^pg&C7n_{o!6_c?W{K1GUUCK_?X&c=w0*2`j((07$+*@GSGJeMfkXdCtCz_gLjVIgj@-hFruLd_bb{GRHq8kAzp4SL|3vjH`Am z8$HG+>=Z|?V;nvuH}EMwBRBCDhd(DTgm;*C?3cp3%)9n0e9cO4$bEc+Z%GWk#dqWZ zKIClYJ$$eejb+B#@xsT<$94jKWTj6e4nN^%@)SSg7xE0BbNE;CP56@e(*7=d#e8M| zz@M!2i@d>K_?x`7e>>c~wf~TJ_@2Z6l7GT@X1pzV5=~$x*s@3RC?1zbB^s*eCVJw3 zzy%FrqUo`a_Zp#x>G61lNtj7INiiwY$KxdNBxCqsa*_g*V@i?=Q+ZNjYEK$rT4q{L zI-!f{VwrTF^q7IDm;o~qH)h04#6W|?Gm*?fFVpMsP1IozUy=p0FtZXL%!=7aa?FO= zNlHw~;n_(JVLE0yPfnr2R9Ggbr#*ke_VcvmZ`^(!KTk(~iuifD^7ErBKPx(N-(CEi z@bmQK-s#1U2tQ9Benj|r2JpjS06z!%a6gE<44z#43+CcqGChA~8M({E&KXHAp)WIW z?OY@)W@Xh}ES7_rgS$j?GIM+Kdh(Eb!u-tq9)Dp0hnoVP01RLjB!#db7A8foFcu}n zu$ZSf7Wb48mSmRnloFO^miCmvGR(519G1oMqym=5JR~>f;qZ#2lE+CL{@nO`DhmUc z0iG&Yg;|vp#Hv`06vk>;ofO5Q9A2H&5SC(=^3)WTVV3dK!dlGQq%79PI;1?-!HN## zVV6KpJx^UyU)X@zz|&CJh}p=~7@K&Sd76^u*c4lkAZ&pxNh@saY3pf2+6mh;+j}|) zJ2E?ZI$>u|7f&$hicT=28|jYSum|ahbve8j=`C!?Z0PADY|L!z>5KhXDTFk|5bRG{ zV1FDyT4EaxA4moXJ1{$V1`9heJ2{>qjG^o_j0EE_98S97aQu(-z+N0af{YaQVfOKi z682^G^*A`1mBx?|9D`#?e;kYB$N(J3;p52!p);79!Jdi2A^okt{NcK8rSrC1$hKY|k7V>zPZROB=%)^XT(Da~;M6 z&wO^7?^%EoJqzgzX%kpv5q%MDB1~Zc znTacKC7FeDIDQpbEu8CdR%uSvs;b!J$&jCEhN{2`U9>T*OCxUGbb8Yn;A=_{}haV-! zggco#J;#N+n7ceD@FXjpB75)@o+f+oG@c>*Fp|U1l5@f+W|Zf=@Bs6G=K@}IJeNGt z=4?;5Bj`uX}Fb4bM&CE#@uHZQ&i}9nW37%e+VK<2{Ta4=@HFl1KQ+6N|B) z$HFJfCmttGj7y$4mbi#d@fnH6XZW04!RPpbT*GS|{(`&|-e%tRyb|7J-u1l3*UUHM z9=^f1BnIE&JMs`8dLBEB$Da4XIA)yZgYY8rBFlW>$3|{XJgI>3{NTuog_wo-S&^Un zBI4&nJU<)a`N@!INoG8M*Dpy*V?6(V%kdYJz`tGs|B6q!f6mOnZ`d_~#ojaDbC>7`=10$G&nNOl_?7w9^G*1j`Q7sae=vWNU-%P$lRx+y|B`?B z&m(#NIv{)hqQX?Xs?f!B@!y-F5|`Ia_@n(?iGQ+`KfICn=S%UE$Mk+=iBBAEdB5;` z(nP;8zwx@0=nv)(kJropWhNm%F^Tu5Cn@=jPEzl0j}Q5ae?79-hXs-eRi^4qF4UNs zHwC6(rX+4miK&Qzsn8;ySS7VLtv3xxCrr;w@6900$js=?gqgiryuRKnjyEgA7qgM< zm<@A~oapDx?af8<2=g-Ydh-eMGxK}>v4FRrH-Hqv04z+3U|}puieVZKFHTAbGcYrF zOA4J#++^~W!qV(ihWKI`EK9OsSu96#U@i_XPbvuWG4pvV3jLY>-bz@Rm8y^ctb$cZ zVXTVPNKq`#;nhhEM~sr*8Z1y#Sc+N7TMKKmQXNtT>tG-$i-A~|l*95IUXRolR%BN6 zHV{@~R`NE)My%ABRKdpBgjB^Q=ro0D-liPgj8&R>n`2FH3wjG$4HgNa2hnP>L`#-x z>1~C9-q!Tiv^uQOhTeu2$kALxA5Q)g z_G0$(ju7@{_V$j%QLN;UzUVlN(WD=a#xW!W`*ZkMGEO*d**J%c!Lb}Zm&_B6 zXO8#I7fxVK@GiiG-o@TUBor6n60#JR;4-otmwQ*>3hzqcDurYN&KKraUKRJha z?epGz`UHUBpP$rm%V=E66Un8cyo}; zn9aWG%}%aiR{Od)8@Y;E>>J*!{i)TSurAV~p|9$LS6)*yt_0>{-^U=T-3>-Q$8DJWmx|&?2WrJ-Tm#nb4E_E-{z% z>?bd)%BkvSTv3gC%~f30i&v9Q#fja*6p}L&9 z?%NpNRycXxNUJ+F7}cTd&r z-I@PVHQ{w;U_L+BKR?$;Y!ho7bVWfDWc>eC5mZ4T`vgtU1q1(oH3dsB$+qAK@r3x; z5dwq+LP8-C{{Na-NFpRA``4sGGW`EFxsXCgPEIML5>g9k@c-AeLOLO>%Y*bn1|g%6 z31?umC}c*WkcBF;kQK?~Y^3Z+CFdaJMA^uJLM|aU$|d9x@{;nQKyrSefKU(>5DE!} zNkve8a#2z-RG3_x%KC_$=;ijhl^YM~P3 zETl516uC5OZB$06!|2jNT~wM}PpB_6K=p-&jIJm&LKTI^jIJOwK^4fANlj5Da#d0@ z!EMIUR0twAM^$-LgCZ?ZHK8S=YY44S4RS3~YgCh5hExaDBG+aOMsCC#e_eN^V7w z-l&BT%IH=?AJmFmhtvkOCI_?jMQwyIMzYWRyO4&V5OQ~l3`ac#mnz+b5vV)4H$_IGP+=6KdkdpcZ*m(_80tgrFN_hw z(HLPYqx%cvP=E3Op`R;^NBwv(h&TZaAP=UaqsWMuagGQ6XDKZz072J7@4j1O5a2|{&EkNVQ6G#iu zc=AMwEJBln#f+XPEI|{=Q%DhLGI=URBGEKqDWj(f%g|KvOww{RoxDO=DXc;(h1J3u zQj{x1v8)o-vaUl>!g@x}6*i!`LNuf23megV@p)Wn_01O?IKx!fuAIV*Glziz@4dShQYmgbtC92<}ngGCC?;Ve}EG}0P{q#_@rgd3DdHE3Xrjh5pDTW)i7x6q)WmNT zF+@ZBE|}sEiddp0{uFHS7eyS=5q}Hu#6J{?FUIGYBmSjGfEd8D_~Ji`BoGtuEPyJa zm=K9#BC5z@VkC=6sG^EVkt!ymiXtW#-Q+Au>EaVpARmu(Qc9$e4bc=+AyZ5(rXi(8 z2H6qgiRn;0F+EirF#~eQ0i=v5J~;s?6ABb}6*(P6@}cx%enzJg3!rr5jHH4n1343^ z5Xwl-Op(IK%_0_|OJ=bs%FKi86e)&sh{YM5T`YmJlLJX5QBHC$QYjQj&P|chD34f% zD!Ik7C^tDDMarT4VtGdA6Dy#6Y?&teMXlP8=!Kc zTaVEd#D=H>4=Rxwp^D_n6lsjAh)t+cS!{|bldDmr8LBP@F}j-A991LNB(*>_$hAl< zQB87finKy?#MV@)Ee50763yVwJD7ke_gwb%=_CcDAJ-l!E1+E64E zwH5m?x{cTuwIR1Bg`sxjPGUc?Kk6qAV00&OAnHW!Od5nbl0zsm7^(Ku(y~v>y8HM_YqZu74jzOX1Fj6?`OCC!ahr-AM zDKZ}Q7bj3% zxKvz5T#lyjUQQS<02+>_ife0Q%vc{mL;ufkzid#`6c^hdvT25X?ksWBY zxRcSV#9e3=If}F!ts$?aNGw_>#xZ)WxCgBzZy@bO>&elieQ1NY*Jbo(aX;ED9$@$; z#&3c98NEe3h_;A_7#$-XMls~=q$6l6c?U&~qMhO~M(+@hqaEbkq!VZt`6TH-w41z# zBBxNSc$zAE#51CMhUFBa&x+^JS@Arh4~Q4g0rD2oA+(=-ko6)uBwk|lLGdy=NIpWk zf)109QsgQ+CSGIoQSmxDN-9lA%pPkN8;k{^&hp!?)Yq(|rh`6B~AQQ#BnBX9SavHqtYS-;@V z;#cvT_+9*gzllG^?=IV4Y`==X8U6>oqR?OQpa1)D>7OV{k|axtq+(g}c{@$gy{8hZ zA(@gtYpfPFBwKQ%c*v3BO94^>DIrb-6SF440Wc|RGMrd)lS#?hq`*mGO4d|3IZVWw z8mEM5q_k2xoE~P7GQx~JODAQLGQ-R~ODAQKvcjx9ODAQMvcv2=OUIv+1LojaIw_|V zDCLrJ<3K5olvm2XL1ic}R>ma0kBr5aLA{QuQoBq!OeCDoSdpxRPhsh(6z zs*h@s8;}~JdgMk@W2p&hEH#yyNkLL`6eP8fT9R6!X5`kSVAPV_hSV0dCbuKCM{UT} zNgYr-a!pc4siVu%fi5+rPN*i2>QJOJsw;&sx{lNZ)gd<|bw%~bO(@b0HI=$cJxD!K z6LKqx^g_+0-i&S~g`!sEwxm8NnB1P!7qumKklglC7;4Xhj>LYb1G%o$pYb7Z0HZ^s zfha^8#OTh_VAPr1l{5r(A$Oz5P}E%-MwM>TaMX?Ll18AOCKAT&T4$LK-Qcr-|w!03U}L^O~*gfs~aCJ&{^WHd~g z!swyWR5X-4f;0^cCy%6vJ6)R2GK0}0rI~0Xc?@Y58chx-%|>I$V@Y#RIC&67CZKWB zTt-ij=AjAFd`6F#7NGIuDbhk|5n3oMX7m(k37SHlN{T>}r6n$1rb>}$Di5YpWGR{< zEo1a_X*rrso<&-LW|C)-R z5{(v18>zBL+9bJ~SfUxdMB0p&@F>!nzm6&;eUG5Vl%9UUYeA>BZS$;T;j6P=K5 zNw-ON&~fr9irhsfrF)D%E8RzDr3X|wBRxcC$d9CRuJjn4H!1QK-ICr><)-u=-6Y?o$Om*!`pD=9 z(kJvlaz8WrzVrp%=fPvrSM-qlgd*S2Q|UXSe@H*k59t?GUP!;u3-U{f{6Vjzzl?q< z{X;LwZ%F)eTlqElEk*e5Q+~%M*+)s16(q~5{DC4qSN8Exkv~bFB~8}k&yp^Gp@<U$bTpjAP2~Qr3CUniX@a1 z@+^QV|D;56BAAdWlAIVxauV59Xp$7Eax$tYa&n}QHCdNaAYD#L6-`csG_pxbjSR9S z+j1IY%W0_+Pfmy8$?2)$$Qh7B4v-Vb8Bqc`6IBA_%qW1Ih?GUn;<99>OAVu|N=i;akwBDE&c)~ya&DA@oSKver6Q-HNM4jy&d2EVa(l2pkqmqOXeffOl? za>->F9VnMYf#f`-aws=BFGb3ud~yXw7mzEW0&*p){4Ng)(WDA0ELUZ8A-NhV zL@r9Ij*5_rQKSYcF4tsqNx2p(Dc7b-3Aqj`K`u>^x~Pm?kI|*&`lvLy9H{{+OD<25 zhNyzvh{=^?x3S!qr3qas%1u#4autd+LsjJsU^rcICxj$;pqY!z3JP-|#2QfNC9*jcBT}VSvXL47H3`O1KVT|r74@X_e zJxDId{Pve zM_xdYwP>Ndj?oL`^=JWkF=+!@L|#IXXcQrDWb_hw6Ivp>(R7KFH={@%#K>FZt!Rt9 zjnT{G?P!_2gDNZJooEGl6-9QT)$(peuaaZYDsmJl4y_@trN|z%PTtGtwemi+mb`(q zAFU@JARTn&gDm?Q9W5V1(ehzNACZruBl0n-Y?F_pZRG6~Ie~V_C#kYs{ts;@@1n>l zv|B#S=w0#|w2K@^I*Ve-dnj@a?Um0ndXIbo?IFASi5Jm69vq~|CA6P>Ouj5%L6_yL zj6Nn`L&wO+N!QU)@(t2Wbe#MjMQ))J@@+<+mhYg`@?A!slJB8Ye@D;A&q+VfQ}PSaPxPGplJpC`AipO4MlZ>4DDnrrb>+W|ek1=wZ+P&YBq)ON zjx4hB4KI|HPZUx3uN2V~P5CD4%6Ezwior8Y`9Tp=F?nVvKPh4< z7SBxO7e#Ev=9#7Zrikk(4i9bR4@KfB@sz)EeB~cS0+axr#bfwC@0H~rPZB5zl|(3^ zl2}PXN{SMYby6~vgshR0Bb{uJQXq|Nl2RgrY>`qSQ%UL4#Zpouiw6!V4YJ8;N$HS7 zPEX2!(vlOB5~K9wj7laYGs>i7QL-x8lDy5Y& zN?D~GE)Of<#-U?kZ(9OCK19*T68?A4kFda3Ed_2g1R49UKgY z;`MMS9F8}@;cx_wh9lr8yb+FqWAG+829Cv>;aHbtJdT0m;Y7RzPK1;3RyY|>#oORi zI2~_?)8S0K1I~oA@lH4!&c*ZLT-Z$6rR-*%4;SEAxBxE1ZXC-(C5{V-Q})0`a53Hs z7sDlZA6x<>@O~HpBk=(k376u7a4B4d55Z+{IX(=R!xi`lTme_&qj05i)a4eAD#zd| zxEddatKk}a0?@EnJ6B!F6ywJ`LBy4fqV)0Hg6)7!5b#b8sWvgwHGP zCN}4}fb+@)xEaRai!cUm!I$6`xD{W9Tj4f*1#W}e@m07T?!edJ4!9HVf;-`S<+^f1 zxruHlx0Kt;9j;+FjCH|X?jaV&;d?L+?!our9=I1jfP3LS{1EPg`|%^VA0EJu;Q@FM zKY<6~A^a2`f`{=lco-hR&!KySq1@E3RiUc_JFMR*B+gO}iC z{2g9~SCk*hPvsZ-sr<%&;BWXBUx$C;P30fIy6rkULA`-RctdeTRbnec2`X5H3iPR( zs;l?72wlC84R~KM)Cbsv4-`{I<9@zEBdXFL5IHQc0w~!inK4C9(P%CxNe(BJ_*O}#zQf7kJ0-dL9;blsl@#g+oDzOeQmXDpC6$_5O@mUaX>mH37N*A;V0xGl zXM!1FW;KhNRn3O7s@ZW4m>vF5a;Q1s4<)Di69>YdN}&1&|Kl3|aQxSmTxxF8KP9&+ zu*;(gE}J}DKpr(O6rqgsK^dwzKU84>wV+yv3(=v03qu2%xCk_%g^NN9+PE0Bp@WM< z2gbuCU_2OKbxWuvS>nS0TnYxj1h_Oz02AUeFd6+!nTh?Qnb84tBsDVF%br?W~4y4aL1J!cw>!ECoxe-PImyPt-&0g?qzZFckNJp|CFwgMDE?wZA$*9f$^S0Rz-Qup+F4 z2g6FRDjuS`RoM*T0*0tVVRcvo4}&#eO*|adgtf2>Yr)!h1gs55s-x7=Tti)0508QM zV0|18>%#`PA#4D1sbke~>UcCxo!}BCvY7-Y3EfrdYIO~cf@{@v>Uz8ZMyngu zO?Wfhq{fi9;Qy~%)vcs$Xgj+da3|gdcae7EoiLUZhj+t0q`f!}?o;=x2k=37$W;%k zNAOX2Og*F?$4B4^(n)+A{zp26Pr}orGx!udOFD7M27XL&E$^Su2$Ua{|}ch!67u6iHcRUe?c>O*u_eT43+kNLaf z34d42hYQuGT;wwpqq@(*3-vjUQD34MZajt?-^>licy}DbZN_kmJJna}YxNC&rM^Y4 z)OYH8F6*fJLH($HLLb%7T+(|k=M+4H&%rP1SM?kEs(!~m;4O~-rutL;rT%u+-zyX22O?2ABzFhM8a% zoE2tqS+e2mFdNK)bHW@j5a)t{FgMNvbHltiAIuB$;{q^0EQkxig0L_y0t>^UxEL%7 zi{qbaan_%ld1(f!cfdZ6pStvmTmxBuQ;quT2 zbzA}J(8Lv?32j^n+Atoj4CDDKyWB!$UlkYt6XL2cAxw;`!Nf2rt`3vJ4d zm<#5?jbR>`4>y7NU;*3|7J!9tGgt@~!9lPHEQU+KV(_P{Hutq)Qv#O6En!Jm3b%r# zU};}#U$CzY3ih?d?O?1sC;ZmsbDMMY;l8oHacHb>Je~l@!-;qjoCqiTrue3E4Xt4?o(6+qJ3Jk>gB|b; z*a3FJGhrtff@i@H*cH!)U14`T$7Sj6o5Ll{@y&%j;XFJa&Vvhl3w?{YhCZ+_UJU!f zFuVkY!G1Ub_JjR#B6f!D$ja3o#_N5WBfJsbr``!@KZeH&4mf3I)-UsKvxp+UE3+Lkla6UZfJLEgeH7tUQ@e#NfF2P6P5*UGx!3Y?MkHbi~ z6rX@g;WB&@E^}Fyy$}vu=hl_y&xDTkuV|1#ZQ+;8wWJciVS| zYuEvI;=6Ds+=cJKU2r$P4|l^@`~b$nIQ$UC`5wC5!b9I9xCidVkKtap4?ltX;C}oR z?uQ3_&wS6hhC}c$egO}|Blsme0*~TX@F+ZnU&CYYIDP|?s_YHmJ0>1jb!wc{t{sAw-EBGh8 z0diEJu)4_~dCM~n} zh3m+yeZ^VeS6>$GJI)Hf`?6|3u$ztLhcBD<6aV5eesT%^3;5;BPR^la*K%rsS}qi* zwfK~_=(h8$OS`k=8D~gI}#b7b5I4Y)T3;fL)b`bthr4}^Cj2MKQz~-bDI0&{RwZbi6Yf><71>2C?;$YZL zOU(te*E(n&aVOZB6oNa#E~KtF1a>2J$6aA}t%ugl z#eIMHJ|I*J)&8me?*e3>e=mD){+hSfc-Ifr4DUVP@t!!|yL|$0pTK*aPvY&9c(3p& zynPDqwLJ}cH&jc*n|i30&U;JGxvUsoLIlL!1cnJ^H0=*aTyxx<%-b;5u zZ(q=R(Jt!ki}GsSN9(JFp}tx_*k9{|!?Xb?OdE*8v_U9L8?1%7+7Ol@grO)*8-~KP z;V4XVQJ6LYg=r&Em^KQ9X`@k?HU@=h;V4WSi^8;Vcsv}>Ist`g6Y(TCiFGmx)25&> zZ7K@WreSwF%XBs~P?$Co_k**v+1ea5Tbm2#YV*)sZ9bf@EkN_Ng>a#^2rbkW!^PSX zv{;LP5n3dQ(3Zlb+A_3MTMn0NE6{RnC0ga}SGn41wrk*O7=_ouD7X%2sh&xxEXH2Tj3VC4QDgQxKscp9F? z=ipg*-US!5^XP(h5zmGfq5lFdapZI1Wq6q*pAWCVD;)Vkcokmd$QQ$F@ES)R0k6aB z9Qjgs1K!}sm&2Rzrg!)^wOi;mdv_(vDtrg7f@|5dfM+!K0~hc?`v}j$ z3-}Ve0R0#6i6g%ZKf}))`4#vDe&NWk!mscvM}7@{gWovv>+n1L&XM1MKj04z{wDkh ze|m@iQ~QN}yI%Os1^m|jz}xT+{tNHGyZ9fx3-4h;zsD-*_pu1?Yoh)DOYi}|5#S+~ z;X_Tn+#S}f6AGwy6a=()1PtZeAu&U`g0Cl({)x|f5D;a zy1{DbFFATcH(5>HfB2?uA=}&A`fJVB9r#*v^fx#je51wF-{Sc2trlN@hXdd{EkJ*d z6X@?Y4SQS{B`Z_*wLzc=7ntfZ!Mdi9sbs` z>wmNydQLqM<)^Vu4y=dk!+Nj*b{n!Z&>QNFaAVjAHo=Ku6X?HyrW}4! z*o>nO;>c6N=CCsu?i_wG*n^|* z$%&VOyk=)ZuG9DXx6 zilZORkvE59;24g)1q_Gb9C=GP7LMh}TfuQ~97o<7j)&tp_+U5zPT=I-wk#9TB<~_7 zaTSyGDO^Jb*bz^K9bsoY4R(ehcsdM$UGNOp1$M{hdmBCSEI z^eC=jEyq6wu7m41{&2V+uIKp2!VPc($3G56!)T6wJlqI3x*Y!mwwvH4?;m4tx8Frxqu=#- zkM*8@-{S+;2l_*gk60h+k3BwNeWE}0_>A?L{@mjW)))FqkFQu?>90M$VSVH3Z~cq@ zj`f}X-s1<>5Bf*=N&l>W!CyUoWBsOo_xOYLhyK&!FVCs}f4BO)=-8N2f@U}5WbBtNMs5-_) zz87$ed3X-rAuM5CgctB#!7|oJyoB!rmhr~#7>=>Rd)bfj_EE0)hQGlJ8@#vr&E9^q z_a?v1+i&9yemA?FcspU;(2bDqs|Xg^a?ekWmB`_4Y-L zV(f~;Vz2}*3EdJbrBF$uG%9J7fn|)csEkn#mNUwuaz+JI(c4!vDzU2!E5Rx_AFKlN z8dZ&IMs-xpr~zvjHBk+t7OZ8|MzxGOMqQ(xQ6JSa8W{ClqajNJ*a$a(F^r7dQ&8n0>Q8k?Bmj-4cor3FIC#YFr%N*pHl5$dprQPhaK=h*a3D#1F6y( z4l)KCLyVzlh%t;h!@VlQ4Hu2@_9Lh>(imloMx&@R$`}KCK({AN!YR-Pj)h|>&=-z_ zt%nLh?XrFP2I)|yT86JU0s1gH@8pjOxIIXrA$LVw2I03i9ZTKYI2Dek^KWglRr{F1S z?1HD^X|Kd->YOpo!rd^|IA@$UE}-+&Id5Eqd*MEGkt&DaC3wlWj4rvx6`EY7%TajE zxNh8_)iHP+--O5E349BlfF~((n*yic9e9Ttr{P_A*DG+lAC32(rg_!YbfZ&Bnm1@6E% z@C`NY!ng1(RnEY7#(U!frS8G|_#?azAG+WZtscTh=o1AV!_V+D1)jh!@C#L*!LR6> zxBq5*XZHhshd=Qz_!Is{zl=ZVm+=?=HU6Q$hG71~{t|-up3e)>%xk!LS*qatd}xSf zLGRUnnk_{_2wXjnb0oFZ+W^e_a;I{EG#Co2TOcWqgo-$6)Fv?;FsB zrfDJ5w4rS}$Ts7_cxHSQ&kTS8W&#voCWHyiL@1$|7$!E8pu}cUl+4>FGn1Ps%#>y- z>AFf+~qGsCPn8_Wu`qikjll+Da(2Aa9d+$fis z2j(&JqC93kn9t0Q@|gu-0oN>u3YdjpA+sSqv64i=$#@2~^VCmo!V6 zrOh&CSyaX>hs(oqumY|KE5J&)GOPrv;Ht0+tcI(@YS66#6tf0WybGx5|K%EHO|uq9 zUz;O0VI5e9Be!8)SeGM@2kXIl9C-k&59@Q}31I`+fFn-~8^VU(;WsoJp~l|6F;~&V zY-*-(xrwG+KvT0BObOHAAeaWG!_8qjm;twd8DJ*d5@v!~a4VPvW<#ww{G6~gM<2|Q z=Yeft8;(35Yzy0R;sszk(=7lBp>}3_*q$RV0z1GC9C} zp@}(+3m9e&hfQHK?80U+2#z;ov*Msc@=y_*2bkXu7wb&Q;7X zXL1c8unV3AyTGn^HtY(!;W@Ay?2hNc?yv`*XSzMu%;N&)ne$;!Sd*h)z~T3S3*ka@ z5n5<2hKtQ5Xt5aqBg{w?VJ?MB&1Gn*xtyb4!I2MyE6r8rYI6-*ZAQT;b1jN8*P->^ ze!aQDWgBg7K+)z#ya{fEn{f=>47cE|a0}dqx5I642i^&Hz+HGZ+y%qYZVrDOjD@jg z9Evsfz&++(w8z{B_nG@mcR$MkHV4du=zw{Mqd&}%Pk~3wqvkR5I67vYfG5n8=!E$n zI_2$8nWx#Efv4eFd=8$4=kW!29$v(k;6->DUxAn5Rr88#USqimuj2*qI`m(_4UT*v zya{h|o`v$(@@DIVa@GVDv48DW!IPw$lJ$&yS{(JKS`snRHaseOBPw*5x zgFnMF@EraE&%q1$E4%u z_#6J_$nU^E@DE3R7ygBRIr4k(ANlxPJGyZ+Vb8Nup{PT(z9KB(^CEdGlVFeU#CCY8mnz<{YO%S_|{XEHFA<(O&77EEKsH`9?F znAS>QrYFaT>8wO%266(J-b!L-BqxFytmLMfi3dr{OjbrKwVB!SzY=A(vRG-&tmM=% zvz6Y=MotT}S{cpkWaYPtngz*)VF9bSS%_Q|7PLy4g~`QX zA*-BO#PYup6|st170hDfaNQX!(gyA${KBrL8Gm37;cS4;np}f z&Ki%#Sre>@)+B2(nq*DEQ{fai4Nr&DpgY5wY0a``qgmD*ILDfc=2-LKJZnChXDzT6 zT8pg3Xpyx9N5Ca85-)|3a2Z9GQy?6!fGa344z7eNsWJhsvQ}GbC^ZpI!co>FmrWFX zqO7%WDx6M{brhHb*TeM`mg$x z+6Xt{18@`Ej1R)iFoxkts~YEm*o&WwpmBfHXiM?c38*I4(m82c3LOU zPV#QjNwkX`N0t9jtaXavan@-RN8U?1gZ7a3Q|T<)XPu+Oe(OBiPd-SxfDVu^TJ9yf z9JVe|;*xdQYj>Fvm#r)QcJL@Qu6l*8TGv>wd4;Z7*IBQ7g|1sStee&?s+@xV;oDxh z+myI%-SNuZq0~9+u2<-;b&vI)SLmK=-Dh*(YjodwU_G=R(dr_+gdf98@G^b^FT*R; zc}k6I@ELqYjqC6^d`^`c@P+l#dPS+5@D_dzZ^7I64ZIESQ0J}X-lNG|>m7VYjr;ID zd{31J@PqZy`b4RR@DctDAHm1?3w#WpP~VweafL5c08D6yRkCbN^HWOfRe z!Yh%2Iw|c`P=Yc}4Q1%VX`m14D2@L^1Ez&(Dd50#w&MbSmvnY|l)>9)piTxmBMg8E zaVD4$CPtYkkPK#qnW>QiW`S9}5?QFz#>#59=BMnec0b&P&y1{gJMYt>9iJCj?atn( zLpOF=ZMPdA3t8>%d>~}CdwM_f_vEu6tKFBM_fzo~%Z5_&7f;3N{}H98cUqVYW}|jG z=&zoQ${C=)Y(^?&hS^{?{@m<#4m&5xVF$uMI~NMHbHm)Ood@N%^P+s-KA)YRT>+RM z7Q}^ML0A|SvWuWXc2QW=E{2NQ#bI%~1S)QqM5VlaDZ4bgGO#o(i*vxT&|jw<1p=X4 zo~1kma>EL+f>))2T@h9C_LZnp$*v6Z!2-AnEC36kDikORtHP=jC=RQ^YF?FUc6C(4 z+t;8@4Z9{R1H-|y6h27F_Ww%DH>|hvdw?V;nTiDiahuYfh?GAQFyA$eYcedS*b_h#n*adfm zU0^qgbf-XF*aP;UKz-N~_M}Qf*vsy1hf=B$Y>fNB#;_Ug3!A~_6bYk1Fzg5WQJ^jC z5BpQ4y~{Gd9%v7uRR`D+4~89K2p$4MU{{I^r9clj3=X3}PdFS7r%EsA+9T|dlA59~{kF%;+r!=W2af&OgA!m(5t0LR(m?Fp0`2nXSba1b1fC&9sR2*Zck zlhIIn3d4ulQ_(Q8OPYp;lSfi%IvQcmpu|XfCK^c|O`3&9xjYD`%WO2pp2P5PdoBtm zk0Z@PW62Y!G#`z(7f@n?y%0?xPa-Ws6UmG1B~+OLC*ugOTm&T|>`1R%B&BB9OT9u% z?PaXXY}emtnY|n?_Zls?SJ*4lIpS zuVY>36Q|)lv)gz;EiwzjKG^<1dMd)vzY?RU<{0*#&Wm?ZlTHwxYgcfZ>Q8s zxC-xptKe$96Rw79sI!Y2YvFFVn;Pq2ER3bfdKhQ#vG-DH1B}M|pc~Dy5$}f^;UhDlhDp=#{sBMV(jnYj_jh#&6(lco)5K>G6Q@7QUs%Blr%!^Gdv< zPEPwhKVxTyWpPeEG~Q$Xr$a72FW$5IKOG9Qdk>4iB77da$Nr!Bi}6|Tp0zALn8)@9 z^q9Z)6ITBZ=&9{Kr}(`{pX|@*ll=vLvA?1( z_BZ&={*J!cKhRHa|I_}(?l=4e|KPvy5B!Jz+Jfr{&R<)Eq9Y-Z|EQ;g6(sxrgAM$q zUH{(4?>6x{y8ri_Km&iW`K2DvU&nO5P=SAL!)iI-s9^Di@r~CVe-+#D{}IpI$8&z# z@tpYZrybw~Aand^Job*n5mn8$v2s6M;I5W%yvp89uY)*ER z&B+0CI5|-cClCfYxlo{!+sWhPb@HLSPJUbf=7$AwAy^O=rbrPAq<}?XQ3|Aj#h_b^ zE@{{ncS<-VX_XeH!=+$4m=TwT8DVCMl%YTlSQeJ0Kp-p!%TXmaEbmlsDpD#B%!@0* zys!YS3=6xl8SdA*hVRfg5Qbxs4BTS zDF{^~*Q8Q&RKsaOiJDGJRFhnr)C$!i*QH8pRL2QscwMIrs!OgfEx0kF{vYJMDFBtrb<)T1c!L#LMRd9bn(h{p;QZ}t5>M2(~Y&8SE!rQowd7H zsJqj{>FM;MN-%7Vdwb=2Q=+#M3f(p=ZJqW`AFoj#r!Q+?uTWnnj5W+F6z24E`a1(C z)d_aS17T+vf(OA6*o8WSsnHD%fkUX#9S(&08hzntIGP$^a10znm3}bX8S9LrRDU=CkB0-`Ks*5sgo7wDkpe^DBshrzL*ZmN znJU8^cd|3Znd(fV)o|$I>ClBE@C-Ntj-<#;3XF!c;4BJ^fwSRks)WNi&Rl1nGat=! z7T|?&0bGO^!$oijTI@ui#ZDxQbe*Lr(pd(VIm^*9X9Zg6?N>Uh*sX@E;2InS*TA(X z%2|h^ob_UDSw%yWwuH#BS<*wqu=-{FEKbPu;Pk zuP)oK-p9jNJ}_dPU*5;VKX$RsKU;tTp9itn|1-bDCqXQ$!H;I66NfhP7vIF{{}FAb z_7)fi<0!us`m4uLdHeq=IHx6(QL|LVaQp%Ez zY(+M*u}lwX?l(?ylbic^zH?K@NzAx zxNqUU(m6>|mf)T;Iz>vBedMwaG|QoEH_S%zDoJY>mI9@@C61<#R_ zWx1gpZ>K#b%yC_9o+mGC=bAdbgAQ4qt7`U6I%OSPR=;=ACF|shTE3fZSr?Z=S?HmM zvhah8YW!Y$Wj$Qb@AuIs>*c)Ozn^|tAEllL3@{+;=Uj-DhCv2}1Dx>yVTd8&AZI;T z7-m>D#OY8HMi`+a{N$Mj5u=RCMtG_nA7e~5$`h^mIODQ09&6Jln2?S0NX*gmEjlp$ZuYHy|C-8HmaUshF6h4)VP~vmTF0jtJlWV zv&=cq@y3{Xj(O*K-r7>nv%ov&MHbyJ@ZPd|i6z-0A8f0aS(YvF$+kMS!U`WFfB9@& zy~?U^g)hd{YplsusWq-%XI-|&SL5mpHe~D68CP$zDcj(iarG8kvQ54lRd2H`+oHjy zdWRkABmZc$sV-2E?YJ(`WJ_IOH|AoOW@GAI_MG>K8&mJGPmA*b2k!Stm{uQhC_5l& zTz$lm?2xo^wSV~$Oxc+-+r<7z*Qlods@w$*;YBRdgov#s{nNEV6aY^#f- Rr;3FqqtA`2k8|pr=s(&lread(data); } +const void* rsi_AllocationGetType(Context *rsc, RsAllocation va) +{ + Allocation *a = static_cast(va); + a->getType()->incUserRef(); + + return a->getType(); +} + #endif //ANDROID_RS_BUILD_FOR_HOST } diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index 2a47ca4f9184d..d1346fc01dc8d 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -511,6 +511,9 @@ void FontState::issueDrawCommand() { ObjectBaseRef tmpV(mRSC->getVertex()); mRSC->setVertex(mRSC->getDefaultProgramVertex()); + ObjectBaseRef tmpR(mRSC->getRaster()); + mRSC->setRaster(mRSC->getDefaultProgramRaster()); + ObjectBaseRef tmpF(mRSC->getFragment()); mRSC->setFragment(mFontShaderF.get()); @@ -519,6 +522,7 @@ void FontState::issueDrawCommand() { if (!mRSC->setupCheck()) { mRSC->setVertex((ProgramVertex *)tmpV.get()); + mRSC->setRaster((ProgramRaster *)tmpR.get()); mRSC->setFragment((ProgramFragment *)tmpF.get()); mRSC->setFragmentStore((ProgramStore *)tmpPS.get()); return; @@ -538,6 +542,7 @@ void FontState::issueDrawCommand() { // Reset the state mRSC->setVertex((ProgramVertex *)tmpV.get()); + mRSC->setRaster((ProgramRaster *)tmpR.get()); mRSC->setFragment((ProgramFragment *)tmpF.get()); mRSC->setFragmentStore((ProgramStore *)tmpPS.get()); } diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp index d1b55814301f9..902657817936c 100644 --- a/libs/rs/rsMesh.cpp +++ b/libs/rs/rsMesh.cpp @@ -271,4 +271,45 @@ void rsi_MeshBindIndex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t primTy sm->updateGLPrimitives(); } +void rsi_MeshGetVertexBufferCount(Context *rsc, RsMesh mv, int32_t *numVtx) +{ + Mesh *sm = static_cast(mv); + *numVtx = sm->mVertexBufferCount; +} + +void rsi_MeshGetIndexCount(Context *rsc, RsMesh mv, int32_t *numIdx) +{ + Mesh *sm = static_cast(mv); + *numIdx = sm->mPrimitivesCount; +} + +void rsi_MeshGetVertices(Context *rsc, RsMesh mv, RsAllocation *vtxData, uint32_t vtxDataCount) +{ + Mesh *sm = static_cast(mv); + rsAssert(vtxDataCount == sm->mVertexBufferCount); + + for(uint32_t ct = 0; ct < vtxDataCount; ct ++) { + vtxData[ct] = sm->mVertexBuffers[ct].get(); + sm->mVertexBuffers[ct]->incUserRef(); + } +} + +void rsi_MeshGetIndices(Context *rsc, RsMesh mv, RsAllocation *va, uint32_t *primType, uint32_t idxDataCount) +{ + Mesh *sm = static_cast(mv); + rsAssert(idxDataCount == sm->mPrimitivesCount); + + for(uint32_t ct = 0; ct < idxDataCount; ct ++) { + va[ct] = sm->mPrimitives[ct]->mIndexBuffer.get(); + primType[ct] = sm->mPrimitives[ct]->mPrimitive; + if(sm->mPrimitives[ct]->mIndexBuffer.get()) { + sm->mPrimitives[ct]->mIndexBuffer->incUserRef(); + } + } + +} + + + + }} diff --git a/libs/rs/rsProgramRaster.cpp b/libs/rs/rsProgramRaster.cpp index 76638409aa09d..5b69370172944 100644 --- a/libs/rs/rsProgramRaster.cpp +++ b/libs/rs/rsProgramRaster.cpp @@ -42,6 +42,7 @@ ProgramRaster::ProgramRaster(Context *rsc, mLineSmooth = lineSmooth; mPointSprite = pointSprite; mLineWidth = 1.0f; + mCull = RS_CULL_BACK; } ProgramRaster::~ProgramRaster() @@ -51,14 +52,22 @@ ProgramRaster::~ProgramRaster() void ProgramRaster::setLineWidth(float s) { mLineWidth = s; + mDirty = true; +} + +void ProgramRaster::setCullMode(RsCullMode mode) +{ + mCull = mode; + mDirty = true; } void ProgramRaster::setupGL(const Context *rsc, ProgramRasterState *state) { - if (state->mLast.get() == this) { + if (state->mLast.get() == this && !mDirty) { return; } state->mLast.set(this); + mDirty = false; if (mPointSmooth) { glEnable(GL_POINT_SMOOTH); @@ -82,14 +91,43 @@ void ProgramRaster::setupGL(const Context *rsc, ProgramRasterState *state) } #endif //ANDROID_RS_BUILD_FOR_HOST } + + switch(mCull) { + case RS_CULL_BACK: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + break; + case RS_CULL_FRONT: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + break; + case RS_CULL_NONE: + glDisable(GL_CULL_FACE); + break; + } } void ProgramRaster::setupGL2(const Context *rsc, ProgramRasterState *state) { - if (state->mLast.get() == this) { + if (state->mLast.get() == this && !mDirty) { return; } state->mLast.set(this); + mDirty = false; + + switch(mCull) { + case RS_CULL_BACK: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + break; + case RS_CULL_FRONT: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + break; + case RS_CULL_NONE: + glDisable(GL_CULL_FACE); + break; + } } void ProgramRaster::serialize(OStream *stream) const @@ -126,7 +164,7 @@ void ProgramRasterState::deinit(Context *rsc) namespace android { namespace renderscript { -RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, RsElement in, RsElement out, +RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, bool pointSmooth, bool lineSmooth, bool pointSprite) @@ -145,6 +183,12 @@ void rsi_ProgramRasterSetLineWidth(Context * rsc, RsProgramRaster vpr, float s) pr->setLineWidth(s); } +void rsi_ProgramRasterSetCullMode(Context * rsc, RsProgramRaster vpr, RsCullMode mode) +{ + ProgramRaster *pr = static_cast(vpr); + pr->setCullMode(mode); +} + } } diff --git a/libs/rs/rsProgramRaster.h b/libs/rs/rsProgramRaster.h index ea78e7661f352..801ab2a9da2eb 100644 --- a/libs/rs/rsProgramRaster.h +++ b/libs/rs/rsProgramRaster.h @@ -41,12 +41,14 @@ public: static ProgramRaster *createFromStream(Context *rsc, IStream *stream); void setLineWidth(float w); + void setCullMode(RsCullMode mode); protected: bool mPointSmooth; bool mLineSmooth; bool mPointSprite; float mLineWidth; + RsCullMode mCull; }; class ProgramRasterState