1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18#include "rsCpuIntrinsic.h"
19#include "rsCpuIntrinsicInlines.h"
20
21#ifdef RS_COMPATIBILITY_LIB
22#include "rsCompatibilityLib.h"
23#endif
24
25#ifndef RS_COMPATIBILITY_LIB
26#include "hardware/gralloc.h"
27#endif
28
29namespace android {
30namespace renderscript {
31
32
33class RsdCpuScriptIntrinsicYuvToRGB : public RsdCpuScriptIntrinsic {
34public:
35    void populateScript(Script *) override;
36    void invokeFreeChildren() override;
37
38    void setGlobalObj(uint32_t slot, ObjectBase *data) override;
39
40    ~RsdCpuScriptIntrinsicYuvToRGB() override;
41    RsdCpuScriptIntrinsicYuvToRGB(RsdCpuReferenceImpl *ctx, const Script *s, const Element *e);
42
43protected:
44    ObjectBaseRef<Allocation> alloc;
45
46    static void kernel(const RsExpandKernelDriverInfo *info,
47                       uint32_t xstart, uint32_t xend,
48                       uint32_t outstep);
49};
50
51
52void RsdCpuScriptIntrinsicYuvToRGB::setGlobalObj(uint32_t slot, ObjectBase *data) {
53    rsAssert(slot == 0);
54    alloc.set(static_cast<Allocation *>(data));
55}
56
57
58
59
60static uchar4 rsYuvToRGBA_uchar4(uchar y, uchar u, uchar v) {
61    short Y = ((short)y) - 16;
62    short U = ((short)u) - 128;
63    short V = ((short)v) - 128;
64
65    short4 p;
66    p.x = (Y * 298 + V * 409 + 128) >> 8;
67    p.y = (Y * 298 - U * 100 - V * 208 + 128) >> 8;
68    p.z = (Y * 298 + U * 516 + 128) >> 8;
69    p.w = 255;
70    if(p.x < 0) {
71        p.x = 0;
72    }
73    if(p.x > 255) {
74        p.x = 255;
75    }
76    if(p.y < 0) {
77        p.y = 0;
78    }
79    if(p.y > 255) {
80        p.y = 255;
81    }
82    if(p.z < 0) {
83        p.z = 0;
84    }
85    if(p.z > 255) {
86        p.z = 255;
87    }
88
89    return (uchar4){static_cast<uchar>(p.x), static_cast<uchar>(p.y),
90                    static_cast<uchar>(p.z), static_cast<uchar>(p.w)};
91}
92
93
94extern "C" void rsdIntrinsicYuv_K(void *dst, const uchar *Y, const uchar *uv, uint32_t xstart, size_t xend);
95extern "C" void rsdIntrinsicYuvR_K(void *dst, const uchar *Y, const uchar *uv, uint32_t xstart, size_t xend);
96extern "C" void rsdIntrinsicYuv2_K(void *dst, const uchar *Y, const uchar *u, const uchar *v, size_t xstart, size_t xend);
97
98void RsdCpuScriptIntrinsicYuvToRGB::kernel(const RsExpandKernelDriverInfo *info,
99                                           uint32_t xstart, uint32_t xend,
100                                           uint32_t outstep) {
101    RsdCpuScriptIntrinsicYuvToRGB *cp = (RsdCpuScriptIntrinsicYuvToRGB *)info->usr;
102    if (!cp->alloc.get()) {
103        ALOGE("YuvToRGB executed without input, skipping");
104        return;
105    }
106    const uchar *pinY = (const uchar *)cp->alloc->mHal.drvState.lod[0].mallocPtr;
107    if (pinY == nullptr) {
108        ALOGE("YuvToRGB executed without data, skipping");
109        return;
110    }
111
112    size_t strideY = cp->alloc->mHal.drvState.lod[0].stride;
113
114    // calculate correct stride in legacy case
115    if (cp->alloc->mHal.drvState.lod[0].dimY == 0) {
116        strideY = info->dim.x;
117    }
118    const uchar *Y = pinY + (info->current.y * strideY);
119
120    uchar4 *out = (uchar4 *)info->outPtr[0] + xstart;
121    uint32_t x1 = xstart;
122    uint32_t x2 = xend;
123
124    size_t cstep = cp->alloc->mHal.drvState.yuv.step;
125
126    const uchar *pinU = (const uchar *)cp->alloc->mHal.drvState.lod[1].mallocPtr;
127    const size_t strideU = cp->alloc->mHal.drvState.lod[1].stride;
128    const uchar *u = pinU + ((info->current.y >> 1) * strideU);
129
130    const uchar *pinV = (const uchar *)cp->alloc->mHal.drvState.lod[2].mallocPtr;
131    const size_t strideV = cp->alloc->mHal.drvState.lod[2].stride;
132    const uchar *v = pinV + ((info->current.y >> 1) * strideV);
133
134    //ALOGE("pinY, %p, Y, %p, info->current.y, %d, strideY, %d", pinY, Y, info->current.y, strideY);
135    //ALOGE("pinU, %p, U, %p, info->current.y, %d, strideU, %d", pinU, u, info->current.y, strideU);
136    //ALOGE("pinV, %p, V, %p, info->current.y, %d, strideV, %d", pinV, v, info->current.y, strideV);
137    //ALOGE("dimX, %d, dimY, %d", cp->alloc->mHal.drvState.lod[0].dimX, cp->alloc->mHal.drvState.lod[0].dimY);
138    //ALOGE("info->dim.x, %d, info->dim.y, %d", info->dim.x, info->dim.y);
139
140    if (pinU == nullptr) {
141        // Legacy yuv support didn't fill in uv
142        v = ((uint8_t *)cp->alloc->mHal.drvState.lod[0].mallocPtr) +
143            (strideY * info->dim.y) +
144            ((info->current.y >> 1) * strideY);
145        u = v + 1;
146        cstep = 2;
147    }
148
149    /* If we start on an odd pixel then deal with it here and bump things along
150     * so that subsequent code can carry on with even-odd pairing assumptions.
151     */
152    if((x1 & 1) && (x2 > x1)) {
153        int cx = (x1 >> 1) * cstep;
154        *out = rsYuvToRGBA_uchar4(Y[x1], u[cx], v[cx]);
155        out++;
156        x1++;
157    }
158
159#if defined(ARCH_ARM_USE_INTRINSICS)
160    if((x2 > x1) && gArchUseSIMD) {
161        int32_t len = x2 - x1;
162        if (cstep == 1) {
163            rsdIntrinsicYuv2_K(info->outPtr[0], Y, u, v, x1, x2);
164            x1 += len;
165            out += len;
166        } else if (cstep == 2) {
167            // Check for proper interleave
168            intptr_t ipu = (intptr_t)u;
169            intptr_t ipv = (intptr_t)v;
170
171            if (ipu == (ipv + 1)) {
172                rsdIntrinsicYuv_K(info->outPtr[0], Y, v, x1, x2);
173                x1 += len;
174                out += len;
175            } else if (ipu == (ipv - 1)) {
176                rsdIntrinsicYuvR_K(info->outPtr[0], Y, u, x1, x2);
177                x1 += len;
178                out += len;
179            }
180        }
181    }
182#endif
183
184    if(x2 > x1) {
185       // ALOGE("y %i  %i  %i", info->current.y, x1, x2);
186        while(x1 < x2) {
187            int cx = (x1 >> 1) * cstep;
188            *out = rsYuvToRGBA_uchar4(Y[x1], u[cx], v[cx]);
189            out++;
190            x1++;
191            *out = rsYuvToRGBA_uchar4(Y[x1], u[cx], v[cx]);
192            out++;
193            x1++;
194        }
195    }
196
197}
198
199RsdCpuScriptIntrinsicYuvToRGB::RsdCpuScriptIntrinsicYuvToRGB(
200            RsdCpuReferenceImpl *ctx, const Script *s, const Element *e)
201            : RsdCpuScriptIntrinsic(ctx, s, e, RS_SCRIPT_INTRINSIC_ID_YUV_TO_RGB) {
202
203    mRootPtr = &kernel;
204}
205
206RsdCpuScriptIntrinsicYuvToRGB::~RsdCpuScriptIntrinsicYuvToRGB() {
207}
208
209void RsdCpuScriptIntrinsicYuvToRGB::populateScript(Script *s) {
210    s->mHal.info.exportedVariableCount = 1;
211}
212
213void RsdCpuScriptIntrinsicYuvToRGB::invokeFreeChildren() {
214    alloc.clear();
215}
216
217RsdCpuScriptImpl * rsdIntrinsic_YuvToRGB(RsdCpuReferenceImpl *ctx,
218                                         const Script *s, const Element *e) {
219    return new RsdCpuScriptIntrinsicYuvToRGB(ctx, s, e);
220}
221
222} // namespace renderscript
223} // namespace android
224