rsCpuIntrinsicBlur.cpp revision 2913f381a554c28abb44f49eddd1ee4c68a72578
1d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams/* 2d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * Copyright (C) 2012 The Android Open Source Project 3d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * 4d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * Licensed under the Apache License, Version 2.0 (the "License"); 5d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * you may not use this file except in compliance with the License. 6d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * You may obtain a copy of the License at 7d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * 8d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * http://www.apache.org/licenses/LICENSE-2.0 9d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * 10d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * Unless required by applicable law or agreed to in writing, software 11d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * distributed under the License is distributed on an "AS IS" BASIS, 12d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * See the License for the specific language governing permissions and 14d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams * limitations under the License. 15d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams */ 16d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 17709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#include "rsCpuIntrinsic.h" 18709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#include "rsCpuIntrinsicInlines.h" 19d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 20d85e283087ecd00760a0d8d0c9d8482cda845efcJason Samsusing namespace android; 21d85e283087ecd00760a0d8d0c9d8482cda845efcJason Samsusing namespace android::renderscript; 22d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 23709a0978ae141198018ca9769f8d96292a8928e6Jason Samsnamespace android { 24709a0978ae141198018ca9769f8d96292a8928e6Jason Samsnamespace renderscript { 25709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 26709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 27709a0978ae141198018ca9769f8d96292a8928e6Jason Samsclass RsdCpuScriptIntrinsicBlur : public RsdCpuScriptIntrinsic { 28709a0978ae141198018ca9769f8d96292a8928e6Jason Samspublic: 29709a0978ae141198018ca9769f8d96292a8928e6Jason Sams virtual void populateScript(Script *); 30709a0978ae141198018ca9769f8d96292a8928e6Jason Sams virtual void invokeFreeChildren(); 31709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 32709a0978ae141198018ca9769f8d96292a8928e6Jason Sams virtual void setGlobalVar(uint32_t slot, const void *data, size_t dataLength); 33709a0978ae141198018ca9769f8d96292a8928e6Jason Sams virtual void setGlobalObj(uint32_t slot, ObjectBase *data); 34709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 35709a0978ae141198018ca9769f8d96292a8928e6Jason Sams virtual ~RsdCpuScriptIntrinsicBlur(); 36c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams RsdCpuScriptIntrinsicBlur(RsdCpuReferenceImpl *ctx, const Script *s, const Element *e); 37709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 38709a0978ae141198018ca9769f8d96292a8928e6Jason Samsprotected: 39c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams float mFp[104]; 40c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams short mIp[104]; 41c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams void **mScratch; 42c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams size_t *mScratchSize; 43c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams float mRadius; 44c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams int mIradius; 45c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams ObjectBaseRef<Allocation> mAlloc; 46709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 47c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams static void kernelU4(const RsForEachStubParamStruct *p, 48c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t xstart, uint32_t xend, 49c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t instep, uint32_t outstep); 50c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams static void kernelU1(const RsForEachStubParamStruct *p, 51c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t xstart, uint32_t xend, 52c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t instep, uint32_t outstep); 53709a0978ae141198018ca9769f8d96292a8928e6Jason Sams void ComputeGaussianWeights(); 54d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams}; 55d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 56709a0978ae141198018ca9769f8d96292a8928e6Jason Sams} 57709a0978ae141198018ca9769f8d96292a8928e6Jason Sams} 58709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 59709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 60709a0978ae141198018ca9769f8d96292a8928e6Jason Samsvoid RsdCpuScriptIntrinsicBlur::ComputeGaussianWeights() { 61c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams memset(mFp, 0, sizeof(mFp)); 62c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams memset(mIp, 0, sizeof(mIp)); 637079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams 64d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // Compute gaussian weights for the blur 65d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // e is the euler's number 66d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float e = 2.718281828459045f; 67d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float pi = 3.1415926535897932f; 68d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 69d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // x is of the form [-radius .. 0 .. radius] 70d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // and sigma varies with radius. 71d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // Based on some experimental radius values and sigma's 72d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // we approximately fit sigma = f(radius) as 73d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // sigma = radius * 0.4 + 0.6 74d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // The larger the radius gets, the more our gaussian blur 75d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // will resemble a box blur since with large sigma 76d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // the gaussian curve begins to lose its shape 77c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams float sigma = 0.4f * mRadius + 0.6f; 78d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 79d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // Now compute the coefficients. We will store some redundant values to save 80d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams // some math during the blur calculations precompute some values 81d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float coeff1 = 1.0f / (sqrtf(2.0f * pi) * sigma); 82d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float coeff2 = - 1.0f / (2.0f * sigma * sigma); 83d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 84d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float normalizeFactor = 0.0f; 85d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float floatR = 0.0f; 86d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams int r; 87c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mIradius = (float)ceil(mRadius) + 0.5f; 88c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams for (r = -mIradius; r <= mIradius; r ++) { 89d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams floatR = (float)r; 90c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mFp[r + mIradius] = coeff1 * powf(e, floatR * floatR * coeff2); 91c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams normalizeFactor += mFp[r + mIradius]; 92d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams } 93d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 94d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams //Now we need to normalize the weights because all our coefficients need to add up to one 95d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams normalizeFactor = 1.0f / normalizeFactor; 96c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams for (r = -mIradius; r <= mIradius; r ++) { 97c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mFp[r + mIradius] *= normalizeFactor; 98c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mIp[r + mIradius] = (short)(mIp[r + mIradius] * 32768); 99d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams } 100d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams} 101d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 102709a0978ae141198018ca9769f8d96292a8928e6Jason Samsvoid RsdCpuScriptIntrinsicBlur::setGlobalObj(uint32_t slot, ObjectBase *data) { 103d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams rsAssert(slot == 1); 104c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mAlloc.set(static_cast<Allocation *>(data)); 105d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams} 106d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 107709a0978ae141198018ca9769f8d96292a8928e6Jason Samsvoid RsdCpuScriptIntrinsicBlur::setGlobalVar(uint32_t slot, const void *data, size_t dataLength) { 108d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams rsAssert(slot == 0); 109c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mRadius = ((const float *)data)[0]; 110709a0978ae141198018ca9769f8d96292a8928e6Jason Sams ComputeGaussianWeights(); 111d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams} 112d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 113d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 114d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 115c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsstatic void OneVU4(const RsForEachStubParamStruct *p, float4 *out, int32_t x, int32_t y, 116c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const uchar *ptrIn, int iStride, const float* gPtr, int iradius) { 117d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 118d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams const uchar *pi = ptrIn + x*4; 119d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 120d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float4 blurredPixel = 0; 121d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams for (int r = -iradius; r <= iradius; r ++) { 122d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams int validY = rsMax((y + r), 0); 123d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams validY = rsMin(validY, (int)(p->dimY - 1)); 124d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams const uchar4 *pvy = (const uchar4 *)&pi[validY * iStride]; 125d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float4 pf = convert_float4(pvy[0]); 126d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams blurredPixel += pf * gPtr[0]; 127d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams gPtr++; 128d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams } 129d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 130d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams out->xyzw = blurredPixel; 131d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams} 132d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 133c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsstatic void OneVU1(const RsForEachStubParamStruct *p, float *out, int32_t x, int32_t y, 134c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const uchar *ptrIn, int iStride, const float* gPtr, int iradius) { 135e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams 136c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const uchar *pi = ptrIn + x; 137c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 138c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float blurredPixel = 0; 139c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams for (int r = -iradius; r <= iradius; r ++) { 140c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams int validY = rsMax((y + r), 0); 141c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams validY = rsMin(validY, (int)(p->dimY - 1)); 142c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float pf = (float)pi[validY * iStride]; 143c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams blurredPixel += pf * gPtr[0]; 144c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams gPtr++; 145c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 146c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 147c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams out[0] = blurredPixel; 148c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams} 149c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 150c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsextern "C" void rsdIntrinsicBlurVFU4_K(void *dst, const void *pin, int stride, const void *gptr, int rct, int x1, int ct); 151c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsextern "C" void rsdIntrinsicBlurHFU4_K(void *dst, const void *pin, const void *gptr, int rct, int x1, int ct); 1527079cd8ddb0874d431446aea305b8c3b10e4f0baJason Samsextern "C" void rsdIntrinsicBlurHFU1_K(void *dst, const void *pin, const void *gptr, int rct, int x1, int ct); 153c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 154c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsstatic void OneVFU4(float4 *out, 155c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const uchar *ptrIn, int iStride, const float* gPtr, int ct, 156c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams int x1, int x2) { 157e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams 158e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams#if defined(ARCH_ARM_HAVE_NEON) 1592207ab7e0f2d28382fe61ff002ddd58c4fa3fb99Jason Sams { 1602207ab7e0f2d28382fe61ff002ddd58c4fa3fb99Jason Sams int t = (x2 - x1); 1612207ab7e0f2d28382fe61ff002ddd58c4fa3fb99Jason Sams t &= ~1; 1622207ab7e0f2d28382fe61ff002ddd58c4fa3fb99Jason Sams if(t) { 163c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams rsdIntrinsicBlurVFU4_K(out, ptrIn, iStride, gPtr, ct, x1, x1 + t); 1642207ab7e0f2d28382fe61ff002ddd58c4fa3fb99Jason Sams } 1652207ab7e0f2d28382fe61ff002ddd58c4fa3fb99Jason Sams x1 += t; 1662207ab7e0f2d28382fe61ff002ddd58c4fa3fb99Jason Sams } 167e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams#endif 168e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams 169e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams while(x2 > x1) { 1702e5ef66f7d88e2f85bbc4f97edf1ed0b6edbaac7Tim Murray const uchar *pi = ptrIn; 171e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams float4 blurredPixel = 0; 172e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams const float* gp = gPtr; 173e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams 174e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams for (int r = 0; r < ct; r++) { 175e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams float4 pf = convert_float4(((const uchar4 *)pi)[0]); 176e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams blurredPixel += pf * gp[0]; 177e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams pi += iStride; 178e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams gp++; 179e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams } 180e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams out->xyzw = blurredPixel; 181e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams x1++; 182e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams out++; 1837079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams ptrIn++; 184e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams } 185e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams} 186e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams 187c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsstatic void OneVFU1(float *out, 188c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams const uchar *ptrIn, int iStride, const float* gPtr, int ct, int x1, int x2) { 189c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 190c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams int len = x2 - x1; 191c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams 192c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams while((x2 > x1) && (((int)ptrIn) & 0x3)) { 1937079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams const uchar *pi = ptrIn; 1947079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams float blurredPixel = 0; 1957079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams const float* gp = gPtr; 1967079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams 1977079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams for (int r = 0; r < ct; r++) { 1987079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams float pf = (float)pi[0]; 1997079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams blurredPixel += pf * gp[0]; 2007079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams pi += iStride; 2017079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams gp++; 2027079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams } 2037079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams out[0] = blurredPixel; 204c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams x1++; 2057079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams out++; 2067079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams ptrIn++; 2077079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams } 2087079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams 209c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams#if defined(ARCH_ARM_HAVE_NEON) 210c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams { 211c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams int t = (x2 - x1) >> 2; 212c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams t &= ~1; 213c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams if(t) { 2147079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams rsdIntrinsicBlurVFU4_K(out, ptrIn, iStride, gPtr, ct, 0, t << 2); 2157079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams len -= t << 2; 2167079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams ptrIn += t << 2; 2177079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams out += t << 2; 218c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 219c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 220c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams#endif 221c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 222c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams while(len) { 223c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const uchar *pi = ptrIn; 224c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float blurredPixel = 0; 225c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const float* gp = gPtr; 226c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 227c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams for (int r = 0; r < ct; r++) { 228c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float pf = (float)pi[0]; 229c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams blurredPixel += pf * gp[0]; 230c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams pi += iStride; 231c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams gp++; 232c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 233c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams out[0] = blurredPixel; 234c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams len--; 235c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams out++; 2367079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams ptrIn++; 237c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 238c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams} 239c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 240c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsstatic void OneHU4(const RsForEachStubParamStruct *p, uchar4 *out, int32_t x, 241c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const float4 *ptrIn, const float* gPtr, int iradius) { 242d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 243d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float4 blurredPixel = 0; 244d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams for (int r = -iradius; r <= iradius; r ++) { 245d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams int validX = rsMax((x + r), 0); 246d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams validX = rsMin(validX, (int)(p->dimX - 1)); 247d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float4 pf = ptrIn[validX]; 248d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams blurredPixel += pf * gPtr[0]; 249d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams gPtr++; 250d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams } 251d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 252d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams out->xyzw = convert_uchar4(blurredPixel); 253d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams} 254d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 255c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsstatic void OneHU1(const RsForEachStubParamStruct *p, uchar *out, int32_t x, 256c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const float *ptrIn, const float* gPtr, int iradius) { 257c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 258c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float blurredPixel = 0; 259c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams for (int r = -iradius; r <= iradius; r ++) { 260c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams int validX = rsMax((x + r), 0); 261c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams validX = rsMin(validX, (int)(p->dimX - 1)); 262c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float pf = ptrIn[validX]; 263c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams blurredPixel += pf * gPtr[0]; 264c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams gPtr++; 265c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 266c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 267c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams out[0] = (uchar)blurredPixel; 268c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams} 269c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 270d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 271c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsvoid RsdCpuScriptIntrinsicBlur::kernelU4(const RsForEachStubParamStruct *p, 272c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t xstart, uint32_t xend, 273c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t instep, uint32_t outstep) { 274c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams 2752913f381a554c28abb44f49eddd1ee4c68a72578Stephen Hines float4 stackbuf[2048]; 2762913f381a554c28abb44f49eddd1ee4c68a72578Stephen Hines float4 *buf = &stackbuf[0]; 277709a0978ae141198018ca9769f8d96292a8928e6Jason Sams RsdCpuScriptIntrinsicBlur *cp = (RsdCpuScriptIntrinsicBlur *)p->usr; 278c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if (!cp->mAlloc.get()) { 279b801b949e286275b5d19a33135235ba68d3a19a9Jason Sams ALOGE("Blur executed without input, skipping"); 280b801b949e286275b5d19a33135235ba68d3a19a9Jason Sams return; 281b801b949e286275b5d19a33135235ba68d3a19a9Jason Sams } 282c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams const uchar *pin = (const uchar *)cp->mAlloc->mHal.drvState.lod[0].mallocPtr; 283c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams const size_t stride = cp->mAlloc->mHal.drvState.lod[0].stride; 284d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 285d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams uchar4 *out = (uchar4 *)p->out; 286d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams uint32_t x1 = xstart; 287d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams uint32_t x2 = xend; 288d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 289c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if (p->dimX > 2048) { 290c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if ((p->dimX > cp->mScratchSize[p->lid]) || !cp->mScratch[p->lid]) { 291c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams cp->mScratch[p->lid] = realloc(cp->mScratch[p->lid], p->dimX * 16); 292c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams cp->mScratchSize[p->lid] = p->dimX; 293c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams } 2942913f381a554c28abb44f49eddd1ee4c68a72578Stephen Hines buf = (float4 *)cp->mScratch[p->lid]; 295c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams } 296d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams float4 *fout = (float4 *)buf; 297e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams int y = p->y; 298c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if ((y > cp->mIradius) && (y < ((int)p->dimY - cp->mIradius))) { 299c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams const uchar *pi = pin + (y - cp->mIradius) * stride; 300c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams OneVFU4(fout, pi, stride, cp->mFp, cp->mIradius * 2 + 1, x1, x2); 301e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams } else { 302e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams while(x2 > x1) { 303c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams OneVU4(p, fout, x1, y, pin, stride, cp->mFp, cp->mIradius); 304e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams fout++; 305e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams x1++; 306e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams } 307d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams } 308d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 309d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams x1 = xstart; 310c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams while ((x1 < (uint32_t)cp->mIradius) && (x1 < x2)) { 3112913f381a554c28abb44f49eddd1ee4c68a72578Stephen Hines OneHU4(p, out, x1, buf, cp->mFp, cp->mIradius); 312e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams out++; 313e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams x1++; 314e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams } 315e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams#if defined(ARCH_ARM_HAVE_NEON) 316c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if ((x1 + cp->mIradius) < x2) { 3172913f381a554c28abb44f49eddd1ee4c68a72578Stephen Hines rsdIntrinsicBlurHFU4_K(out, buf - cp->mIradius, cp->mFp, 318c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams cp->mIradius * 2 + 1, x1, x2 - cp->mIradius); 319c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams out += (x2 - cp->mIradius) - x1; 320c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams x1 = x2 - cp->mIradius; 321e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams } 322e78e514f3f209e594767e8ebc64f5df4be5b0b41Jason Sams#endif 323d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams while(x2 > x1) { 3242913f381a554c28abb44f49eddd1ee4c68a72578Stephen Hines OneHU4(p, out, x1, buf, cp->mFp, cp->mIradius); 325d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams out++; 326d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams x1++; 327d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams } 328d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams} 329d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 330c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Samsvoid RsdCpuScriptIntrinsicBlur::kernelU1(const RsForEachStubParamStruct *p, 331c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t xstart, uint32_t xend, 332c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t instep, uint32_t outstep) { 333c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float buf[4 * 2048]; 334c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams RsdCpuScriptIntrinsicBlur *cp = (RsdCpuScriptIntrinsicBlur *)p->usr; 335c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if (!cp->mAlloc.get()) { 336c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams ALOGE("Blur executed without input, skipping"); 337c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams return; 338c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 339c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams const uchar *pin = (const uchar *)cp->mAlloc->mHal.drvState.lod[0].mallocPtr; 340c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams const size_t stride = cp->mAlloc->mHal.drvState.lod[0].stride; 341709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 342c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uchar *out = (uchar *)p->out; 343c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t x1 = xstart; 344c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams uint32_t x2 = xend; 345c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 346c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams float *fout = (float *)buf; 347c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams int y = p->y; 348c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if ((y > cp->mIradius) && (y < ((int)p->dimY - cp->mIradius))) { 349c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams const uchar *pi = pin + (y - cp->mIradius) * stride; 350c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams OneVFU1(fout, pi, stride, cp->mFp, cp->mIradius * 2 + 1, x1, x2); 351c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } else { 352c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams while(x2 > x1) { 353c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams OneVU1(p, fout, x1, y, pin, stride, cp->mFp, cp->mIradius); 354c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams fout++; 355c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams x1++; 356c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 357c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 358c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 359c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams x1 = xstart; 3607079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams while ((x1 < x2) && 361c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams ((x1 < (uint32_t)cp->mIradius) || (((int)out) & 0x3))) { 362c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams OneHU1(p, out, x1, buf, cp->mFp, cp->mIradius); 363c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams out++; 364c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams x1++; 365c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 3667079cd8ddb0874d431446aea305b8c3b10e4f0baJason Sams#if defined(ARCH_ARM_HAVE_NEON) 367c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if ((x1 + cp->mIradius) < x2) { 368c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams uint32_t len = x2 - (x1 + cp->mIradius); 369c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams len &= ~3; 370c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams rsdIntrinsicBlurHFU1_K(out, ((float *)buf) - cp->mIradius, cp->mFp, 371c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams cp->mIradius * 2 + 1, x1, x1 + len); 372c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams out += len; 373c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams x1 += len; 374c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 375c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams#endif 376c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams while(x2 > x1) { 377c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams OneHU1(p, out, x1, buf, cp->mFp, cp->mIradius); 378c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams out++; 379c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams x1++; 380c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 381c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams} 382c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 383c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason SamsRsdCpuScriptIntrinsicBlur::RsdCpuScriptIntrinsicBlur(RsdCpuReferenceImpl *ctx, 384c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams const Script *s, const Element *e) 385c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams : RsdCpuScriptIntrinsic(ctx, s, e, RS_SCRIPT_INTRINSIC_ID_BLUR) { 386c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams 387c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams mRootPtr = NULL; 388c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams if (e->getType() == RS_TYPE_UNSIGNED_8) { 389c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams switch (e->getVectorSize()) { 390c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams case 1: 391c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams mRootPtr = &kernelU1; 392c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams break; 393c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams case 4: 394c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams mRootPtr = &kernelU4; 395c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams break; 396c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 397c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams } 398c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams rsAssert(mRootPtr); 399c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mRadius = 5; 400c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams 401c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mScratch = new void *[mCtx->getThreadCount()]; 402c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mScratchSize = new size_t[mCtx->getThreadCount()]; 403c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams 404709a0978ae141198018ca9769f8d96292a8928e6Jason Sams ComputeGaussianWeights(); 405709a0978ae141198018ca9769f8d96292a8928e6Jason Sams} 406709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 407709a0978ae141198018ca9769f8d96292a8928e6Jason SamsRsdCpuScriptIntrinsicBlur::~RsdCpuScriptIntrinsicBlur() { 408c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams uint32_t threads = mCtx->getThreadCount(); 409c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if (mScratch) { 410c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams for (size_t i = 0; i < threads; i++) { 411c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if (mScratch[i]) { 412c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams free(mScratch[i]); 413c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams } 414c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams } 415c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams delete []mScratch; 416c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams } 417c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams if (mScratchSize) { 418c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams delete []mScratchSize; 419c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams } 420709a0978ae141198018ca9769f8d96292a8928e6Jason Sams} 421709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 422709a0978ae141198018ca9769f8d96292a8928e6Jason Samsvoid RsdCpuScriptIntrinsicBlur::populateScript(Script *s) { 423709a0978ae141198018ca9769f8d96292a8928e6Jason Sams s->mHal.info.exportedVariableCount = 2; 424709a0978ae141198018ca9769f8d96292a8928e6Jason Sams} 425709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 426709a0978ae141198018ca9769f8d96292a8928e6Jason Samsvoid RsdCpuScriptIntrinsicBlur::invokeFreeChildren() { 427c44d6706868749abe37780fc28b2cc627ddcf269Jason Sams mAlloc.clear(); 428709a0978ae141198018ca9769f8d96292a8928e6Jason Sams} 429709a0978ae141198018ca9769f8d96292a8928e6Jason Sams 430d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 431c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason SamsRsdCpuScriptImpl * rsdIntrinsic_Blur(RsdCpuReferenceImpl *ctx, const Script *s, const Element *e) { 432d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 433c905efd76fdcc1b8846b229bf7d991d185a7b4b7Jason Sams return new RsdCpuScriptIntrinsicBlur(ctx, s, e); 434d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams} 435d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 436d85e283087ecd00760a0d8d0c9d8482cda845efcJason Sams 437