Merge "Update RenderScript C++ API"
diff --git a/cpp/Allocation.cpp b/cpp/Allocation.cpp
index c4d62b5..8530440 100644
--- a/cpp/Allocation.cpp
+++ b/cpp/Allocation.cpp
@@ -63,7 +63,7 @@
 
     mType = t;
     mUsage = usage;
-
+    mAutoPadding = false;
     if (t != nullptr) {
         updateCacheInfo(t);
     }
@@ -71,6 +71,13 @@
 }
 
 
+void Allocation::validateIsInt64() {
+    RsDataType dt = mType->getElement()->getDataType();
+    if ((dt == RS_TYPE_SIGNED_64) || (dt == RS_TYPE_UNSIGNED_64)) {
+        return;
+    }
+    ALOGE("64 bit integer source does not match allocation type %i", dt);
+}
 
 void Allocation::validateIsInt32() {
     RsDataType dt = mType->getElement()->getDataType();
@@ -104,6 +111,14 @@
     ALOGE("32 bit float source does not match allocation type %i", dt);
 }
 
+void Allocation::validateIsFloat64() {
+    RsDataType dt = mType->getElement()->getDataType();
+    if (dt == RS_TYPE_FLOAT_64) {
+        return;
+    }
+    ALOGE("64 bit float source does not match allocation type %i", dt);
+}
+
 void Allocation::validateIsObject() {
     RsDataType dt = mType->getElement()->getDataType();
     if ((dt == RS_TYPE_ELEMENT) ||
@@ -150,6 +165,7 @@
 }
 
 void Allocation::ioSendOutput() {
+//TODO: Also make it able to use for compatlib.
 #ifndef RS_COMPATIBILITY_LIB
     if ((mUsage & RS_ALLOCATION_USAGE_IO_OUTPUT) == 0) {
         mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Can only send buffer if IO_OUTPUT usage specified.");
@@ -162,7 +178,7 @@
 void Allocation::ioGetInput() {
 #ifndef RS_COMPATIBILITY_LIB
     if ((mUsage & RS_ALLOCATION_USAGE_IO_INPUT) == 0) {
-        mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Can only send buffer if IO_OUTPUT usage specified.");
+        mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Can only get buffer if IO_INPUT usage specified.");
         return;
     }
     tryDispatch(mRS, RS::dispatch->AllocationIoReceive(mRS->getContext(), getID()));
@@ -191,6 +207,33 @@
     return p;
 }
 
+// ---------------------------------------------------------------------------
+//Functions needed for autopadding & unpadding
+static void copyWithPadding(void* ptr, const void* srcPtr, int mSize, int count) {
+    int sizeBytesPad = mSize * 4;
+    int sizeBytes = mSize * 3;
+    uint8_t *dst = static_cast<uint8_t *>(ptr);
+    const uint8_t *src = static_cast<const uint8_t *>(srcPtr);
+    for (int i = 0; i < count; i++) {
+        memcpy(dst, src, sizeBytes);
+        dst += sizeBytesPad;
+        src += sizeBytes;
+    }
+}
+
+static void copyWithUnPadding(void* ptr, const void* srcPtr, int mSize, int count) {
+    int sizeBytesPad = mSize * 4;
+    int sizeBytes = mSize * 3;
+    uint8_t *dst = static_cast<uint8_t *>(ptr);
+    const uint8_t *src = static_cast<const uint8_t *>(srcPtr);
+    for (int i = 0; i < count; i++) {
+        memcpy(dst, src, sizeBytes);
+        dst += sizeBytes;
+        src += sizeBytesPad;
+    }
+}
+// ---------------------------------------------------------------------------
+
 void Allocation::copy1DRangeFrom(uint32_t off, size_t count, const void *data) {
 
     if(count < 1) {
@@ -202,9 +245,17 @@
         mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Invalid copy specified");
         return;
     }
-
-    tryDispatch(mRS, RS::dispatch->Allocation1DData(mRS->getContext(), getIDSafe(), off, mSelectedLOD,
-                                                    count, data, count * mType->getElement()->getSizeBytes()));
+    if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) {
+        size_t eSize = mType->getElement()->getSizeBytes();
+        void *ptr = malloc(eSize * count);
+        copyWithPadding(ptr, data, eSize / 4, count);
+        tryDispatch(mRS, RS::dispatch->Allocation1DData(mRS->getContext(), getIDSafe(), off, mSelectedLOD,
+                                                        count, ptr, count * mType->getElement()->getSizeBytes()));
+        free(ptr);
+    } else {
+        tryDispatch(mRS, RS::dispatch->Allocation1DData(mRS->getContext(), getIDSafe(), off, mSelectedLOD,
+                                                        count, data, count * mType->getElement()->getSizeBytes()));
+    }
 }
 
 void Allocation::copy1DRangeTo(uint32_t off, size_t count, void *data) {
@@ -217,9 +268,17 @@
         mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Invalid copy specified");
         return;
     }
-
-    tryDispatch(mRS, RS::dispatch->Allocation1DRead(mRS->getContext(), getIDSafe(), off, mSelectedLOD,
-                                                    count, data, count * mType->getElement()->getSizeBytes()));
+    if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) {
+        size_t eSize = mType->getElement()->getSizeBytes();
+        void *ptr = malloc(eSize * count);
+        tryDispatch(mRS, RS::dispatch->Allocation1DRead(mRS->getContext(), getIDSafe(), off, mSelectedLOD,
+                                                        count, ptr, count * mType->getElement()->getSizeBytes()));
+        copyWithUnPadding(data, ptr, eSize / 4, count);
+        free(ptr);
+    } else {
+        tryDispatch(mRS, RS::dispatch->Allocation1DRead(mRS->getContext(), getIDSafe(), off, mSelectedLOD,
+                                                        count, data, count * mType->getElement()->getSizeBytes()));
+    }
 }
 
 void Allocation::copy1DRangeFrom(uint32_t off, size_t count, sp<const Allocation> data,
@@ -253,10 +312,21 @@
 void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
                                  const void *data) {
     validate2DRange(xoff, yoff, w, h);
-    tryDispatch(mRS, RS::dispatch->Allocation2DData(mRS->getContext(), getIDSafe(), xoff,
-                                                    yoff, mSelectedLOD, mSelectedFace,
-                                                    w, h, data, w * h * mType->getElement()->getSizeBytes(),
-                                                    w * mType->getElement()->getSizeBytes()));
+    if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) {
+        size_t eSize = mType->getElement()->getSizeBytes();
+        void *ptr = malloc(eSize * w * h);
+        copyWithPadding(ptr, data, eSize / 4, w * h);
+        tryDispatch(mRS, RS::dispatch->Allocation2DData(mRS->getContext(), getIDSafe(), xoff,
+                                                        yoff, mSelectedLOD, mSelectedFace,
+                                                        w, h, ptr, w * h * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+        free(ptr);
+    } else {
+        tryDispatch(mRS, RS::dispatch->Allocation2DData(mRS->getContext(), getIDSafe(), xoff,
+                                                        yoff, mSelectedLOD, mSelectedFace,
+                                                        w, h, data, w * h * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+    }
 }
 
 void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
@@ -271,10 +341,21 @@
 void Allocation::copy2DRangeTo(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
                                void* data) {
     validate2DRange(xoff, yoff, w, h);
-    tryDispatch(mRS, RS::dispatch->Allocation2DRead(mRS->getContext(), getIDSafe(), xoff, yoff,
-                                                    mSelectedLOD, mSelectedFace, w, h, data,
-                                                    w * h * mType->getElement()->getSizeBytes(),
-                                                    w * mType->getElement()->getSizeBytes()));
+    if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) {
+        size_t eSize = mType->getElement()->getSizeBytes();
+        void *ptr = malloc(eSize * w * h);
+        tryDispatch(mRS, RS::dispatch->Allocation2DRead(mRS->getContext(), getIDSafe(), xoff, yoff,
+                                                        mSelectedLOD, mSelectedFace, w, h, ptr,
+                                                        w * h * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+        copyWithUnPadding(data, ptr, eSize / 4, w * h);
+        free(ptr);
+    } else {
+        tryDispatch(mRS, RS::dispatch->Allocation2DRead(mRS->getContext(), getIDSafe(), xoff, yoff,
+                                                        mSelectedLOD, mSelectedFace, w, h, data,
+                                                        w * h * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+    }
 }
 
 void Allocation::copy2DStridedFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
@@ -315,20 +396,50 @@
 void Allocation::copy3DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w,
                                  uint32_t h, uint32_t d, const void* data) {
     validate3DRange(xoff, yoff, zoff, w, h, d);
-    tryDispatch(mRS, RS::dispatch->Allocation3DData(mRS->getContext(), getIDSafe(), xoff, yoff, zoff,
-                                                    mSelectedLOD, w, h, d, data,
-                                                    w * h * d * mType->getElement()->getSizeBytes(),
-                                                    w * mType->getElement()->getSizeBytes()));
+    if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) {
+        size_t eSize = mType->getElement()->getSizeBytes();
+        void *ptr = malloc(eSize * w * h * d);
+        copyWithPadding(ptr, data, eSize / 4, w * h * d);
+        tryDispatch(mRS, RS::dispatch->Allocation3DData(mRS->getContext(), getIDSafe(), xoff, yoff, zoff,
+                                                        mSelectedLOD, w, h, d, ptr,
+                                                        w * h * d * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+        free(ptr);
+    } else {
+        tryDispatch(mRS, RS::dispatch->Allocation3DData(mRS->getContext(), getIDSafe(), xoff, yoff, zoff,
+                                                        mSelectedLOD, w, h, d, data,
+                                                        w * h * d * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+    }
 }
 
 void Allocation::copy3DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w, uint32_t h, uint32_t d,
                                  sp<const Allocation> data, uint32_t dataXoff, uint32_t dataYoff, uint32_t dataZoff) {
-    validate3DRange(xoff, yoff, zoff, dataXoff, dataYoff, dataZoff);
+    validate3DRange(xoff, yoff, zoff, w, h, d);
     tryDispatch(mRS, RS::dispatch->AllocationCopy3DRange(mRS->getContext(), getIDSafe(), xoff, yoff, zoff,
                                                          mSelectedLOD, w, h, d, data->getIDSafe(),
                                                          dataXoff, dataYoff, dataZoff, data->mSelectedLOD));
 }
 
+void Allocation::copy3DRangeTo(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w,
+                                 uint32_t h, uint32_t d, void* data) {
+    validate3DRange(xoff, yoff, zoff, w, h, d);
+    if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) {
+        size_t eSize = mType->getElement()->getSizeBytes();
+        void *ptr = malloc(eSize * w * h * d);
+        tryDispatch(mRS, RS::dispatch->Allocation3DRead(mRS->getContext(), getIDSafe(), xoff, yoff, zoff,
+                                                        mSelectedLOD, w, h, d, ptr,
+                                                        w * h * d * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+        copyWithUnPadding(data, ptr, eSize / 4, w * h * d);
+        free(ptr);
+    } else {
+        tryDispatch(mRS, RS::dispatch->Allocation3DRead(mRS->getContext(), getIDSafe(), xoff, yoff, zoff,
+                                                        mSelectedLOD, w, h, d, data,
+                                                        w * h * d * mType->getElement()->getSizeBytes(),
+                                                        w * mType->getElement()->getSizeBytes()));
+    }
+}
 
 sp<Allocation> Allocation::createTyped(sp<RS> rs, sp<const Type> type,
                                     RsAllocationMipmapControl mipmaps, uint32_t usage) {
diff --git a/cpp/RenderScript.cpp b/cpp/RenderScript.cpp
index de22dea..2a04109 100644
--- a/cpp/RenderScript.cpp
+++ b/cpp/RenderScript.cpp
@@ -80,14 +80,14 @@
 
 // this will only open API 19+ libRS
 // because that's when we changed libRS to extern "C" entry points
-static bool loadSO(const char* filename) {
+static bool loadSO(const char* filename, int targetApi) {
     void* handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
     if (handle == nullptr) {
         ALOGV("couldn't dlopen %s, %s", filename, dlerror());
         return false;
     }
 
-    if (loadSymbols(handle, *RS::dispatch) == false) {
+    if (loadSymbols(handle, *RS::dispatch, targetApi) == false) {
         ALOGV("%s init failed!", filename);
         return false;
     }
@@ -119,10 +119,10 @@
     // attempt to load libRS, load libRSSupport on failure
     // if property is set, proceed directly to libRSSupport
     if (getProp("debug.rs.forcecompat") == 0) {
-        usingNative = loadSO("libRS.so");
+        usingNative = loadSO("libRS.so", targetApi);
     }
     if (usingNative == false) {
-        if (loadSO("libRSSupport.so") == false) {
+        if (loadSO("libRSSupport.so", targetApi) == false) {
             ALOGE("Failed to load libRS.so and libRSSupport.so");
             goto error;
         }
diff --git a/cpp/Sampler.cpp b/cpp/Sampler.cpp
index 500651f..62cea87 100644
--- a/cpp/Sampler.cpp
+++ b/cpp/Sampler.cpp
@@ -25,11 +25,22 @@
 {
     RsSamplerValue mMin = RS_SAMPLER_INVALID;
     RsSamplerValue mMag = RS_SAMPLER_INVALID;
-    RsSamplerValue mWrapS = RS_SAMPLER_INVALID;;
-    RsSamplerValue mWrapT = RS_SAMPLER_INVALID;;
+    RsSamplerValue mWrapS = RS_SAMPLER_INVALID;
+    RsSamplerValue mWrapT = RS_SAMPLER_INVALID;
     float mAniso = 0.f;
 }
 
+Sampler::Sampler(sp<RS> rs, void* id, RsSamplerValue min, RsSamplerValue mag,
+                 RsSamplerValue wrapS, RsSamplerValue wrapT, float anisotropy):
+    BaseObj(id, rs)
+{
+    RsSamplerValue mMin = min;
+    RsSamplerValue mMag = mag;
+    RsSamplerValue mWrapS = wrapS;
+    RsSamplerValue mWrapT = wrapT;
+    float mAniso = anisotropy;
+}
+
 RsSamplerValue Sampler::getMinification() {
     return mMin;
 }
@@ -50,10 +61,12 @@
     return mAniso;
 }
 
-sp<Sampler> Sampler::create(sp<RS> rs, RsSamplerValue min, RsSamplerValue mag, RsSamplerValue wrapS, RsSamplerValue wrapT, float anisotropy) {
+sp<Sampler> Sampler::create(sp<RS> rs, RsSamplerValue min, RsSamplerValue mag,
+                            RsSamplerValue wrapS, RsSamplerValue wrapT, float anisotropy) {
     // we aren't supporting wrapR in C++ API atm, so always pass wrap for that
-    void* id = RS::dispatch->SamplerCreate(rs->getContext(), min, mag, wrapS, wrapT, RS_SAMPLER_WRAP, anisotropy);
-    return new Sampler(rs, id);
+    void* id = RS::dispatch->SamplerCreate(rs->getContext(), min, mag, wrapS, wrapT,
+                                           RS_SAMPLER_WRAP, anisotropy);
+    return new Sampler(rs, id, min, mag, wrapS, wrapT, anisotropy);
 }
 
 #define CREATE_SAMPLER(N, MIN, MAG, WRAPS, WRAPT) sp<const Sampler> Sampler::N(sp<RS> rs) { \
diff --git a/cpp/ScriptIntrinsics.cpp b/cpp/ScriptIntrinsics.cpp
index e40d1a0..54ce465 100644
--- a/cpp/ScriptIntrinsics.cpp
+++ b/cpp/ScriptIntrinsics.cpp
@@ -24,7 +24,8 @@
 
 ScriptIntrinsic::ScriptIntrinsic(sp<RS> rs, int id, sp<const Element> e)
     : Script(nullptr, rs) {
-    mID = createDispatch(rs, RS::dispatch->ScriptIntrinsicCreate(rs->getContext(), id, e->getID()));
+    mID = createDispatch(rs, RS::dispatch->ScriptIntrinsicCreate(rs->getContext(), id,
+                         e != nullptr ? e->getID() : 0));
     mElement = e;
 }
 
@@ -569,6 +570,43 @@
 
 }
 
+sp<ScriptIntrinsicResize> ScriptIntrinsicResize::create(sp<RS> rs) {
+    return new ScriptIntrinsicResize(rs, nullptr);
+}
+
+ScriptIntrinsicResize::ScriptIntrinsicResize(sp<RS> rs, sp<const Element> e)
+    : ScriptIntrinsic(rs, RS_SCRIPT_INTRINSIC_ID_RESIZE, e) {
+
+}
+void ScriptIntrinsicResize::forEach_bicubic(sp<Allocation> aout) {
+    if (aout == mInput) {
+        mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Resize Input and Ouput cannot be the same");
+    }
+
+    if (!(mInput->getType()->getElement()->isCompatible(aout->getType()->getElement()))) {
+        mRS->throwError(RS_ERROR_INVALID_ELEMENT, "Resize forEach element mismatch");
+        return;
+    }
+    Script::forEach(0, nullptr, aout, nullptr, 0);
+}
+void ScriptIntrinsicResize::setInput(sp<Allocation> ain) {
+    if (!(ain->getType()->getElement()->isCompatible(Element::U8(mRS))) &&
+        !(ain->getType()->getElement()->isCompatible(Element::U8_2(mRS))) &&
+        !(ain->getType()->getElement()->isCompatible(Element::U8_3(mRS))) &&
+        !(ain->getType()->getElement()->isCompatible(Element::U8_4(mRS))) &&
+        !(ain->getType()->getElement()->isCompatible(Element::F32(mRS))) &&
+        !(ain->getType()->getElement()->isCompatible(Element::F32_2(mRS))) &&
+        !(ain->getType()->getElement()->isCompatible(Element::F32_3(mRS))) &&
+        !(ain->getType()->getElement()->isCompatible(Element::F32_4(mRS)))) {
+        mRS->throwError(RS_ERROR_INVALID_ELEMENT, "Invalid element for Resize Input");
+        return;
+    }
+
+    mInput = ain;
+    Script::setVar(0, ain);
+}
+
+
 sp<ScriptIntrinsicYuvToRGB> ScriptIntrinsicYuvToRGB::create(sp<RS> rs, sp<const Element> e) {
     if (!(e->isCompatible(Element::U8_4(rs)))) {
         rs->throwError(RS_ERROR_INVALID_ELEMENT, "Invalid element for YuvToRGB");
diff --git a/cpp/rsCppStructs.h b/cpp/rsCppStructs.h
index cd8f39a..14166f0 100644
--- a/cpp/rsCppStructs.h
+++ b/cpp/rsCppStructs.h
@@ -305,6 +305,7 @@
     bool mConstrainedZ;
     bool mReadAllowed;
     bool mWriteAllowed;
+    bool mAutoPadding;
     uint32_t mSelectedY;
     uint32_t mSelectedZ;
     uint32_t mSelectedLOD;
@@ -320,10 +321,12 @@
 
     Allocation(void *id, sp<RS> rs, sp<const Type> t, uint32_t usage);
 
+    void validateIsInt64();
     void validateIsInt32();
     void validateIsInt16();
     void validateIsInt8();
     void validateIsFloat32();
+    void validateIsFloat64();
     void validateIsObject();
 
     virtual void updateFromNative();
@@ -343,6 +346,16 @@
     }
 
     /**
+     * Enable/Disable AutoPadding for Vec3 elements.
+     *
+     * @param useAutoPadding True: enable AutoPadding; flase: disable AutoPadding
+     *
+     */
+    void setAutoPadding(bool useAutoPadding) {
+        mAutoPadding = useAutoPadding;
+    }
+
+    /**
      * Propagate changes from one usage of the Allocation to other usages of the Allocation.
      * @param[in] srcLocation source location with changes to propagate elsewhere
      */
@@ -505,6 +518,20 @@
                          uint32_t dataXoff, uint32_t dataYoff, uint32_t dataZoff);
 
     /**
+     * Copy a 3D region in this Allocation into an array. The
+     * array is assumed to be tightly packed.
+     * @param[in] xoff X offset of region to update in this Allocation
+     * @param[in] yoff Y offset of region to update in this Allocation
+     * @param[in] zoff Z offset of region to update in this Allocation
+     * @param[in] w Width of region to update
+     * @param[in] h Height of region to update
+     * @param[in] d Depth of region to update
+     * @param[in] data Array from which to copy
+     */
+    void copy3DRangeTo(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w,
+                         uint32_t h, uint32_t d, void* data);
+
+    /**
      * Creates an Allocation for use by scripts with a given Type.
      * @param[in] rs Context to which the Allocation will belong
      * @param[in] type Type of the Allocation
@@ -1836,6 +1863,38 @@
 };
 
 /**
+ * Intrinsic for performing a resize of a 2D allocation.
+ */
+class ScriptIntrinsicResize : public ScriptIntrinsic {
+ private:
+    sp<Allocation> mInput;
+    ScriptIntrinsicResize(sp<RS> rs, sp<const Element> e);
+ public:
+    /**
+     * Supported Element types are U8_4. Default lookup table is identity.
+     * @param[in] rs RenderScript context
+     * @param[in] e Element
+     * @return new ScriptIntrinsic
+     */
+    static sp<ScriptIntrinsicResize> create(sp<RS> rs);
+
+    /**
+     * Resize copy the input allocation to the output specified. The
+     * Allocation is rescaled if necessary using bi-cubic
+     * interpolation.
+     * @param[in] ain input Allocation
+     * @param[in] aout output Allocation
+     */
+    void forEach_bicubic(sp<Allocation> aout);
+
+    /**
+     * Set the input of the resize.
+     * @param[in] lut new lookup table
+     */
+    void setInput(sp<Allocation> ain);
+};
+
+/**
  * Intrinsic for converting an Android YUV buffer to RGB.
  *
  * The input allocation should be supplied in a supported YUV format
@@ -1887,6 +1946,8 @@
  class Sampler : public BaseObj {
  private:
     Sampler(sp<RS> rs, void* id);
+    Sampler(sp<RS> rs, void* id, RsSamplerValue min, RsSamplerValue mag,
+            RsSamplerValue wrapS, RsSamplerValue wrapT, float anisotropy);
     RsSamplerValue mMin;
     RsSamplerValue mMag;
     RsSamplerValue mWrapS;