SurfaceTexture.cpp revision c5f94d8a4779050125145396ca83fbc862c7ed6b
168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis/*
268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * Copyright (C) 2010 The Android Open Source Project
368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis *
468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * Licensed under the Apache License, Version 2.0 (the "License");
568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * you may not use this file except in compliance with the License.
668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * You may obtain a copy of the License at
768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis *
868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis *      http://www.apache.org/licenses/LICENSE-2.0
968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis *
1068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * Unless required by applicable law or agreed to in writing, software
1168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * distributed under the License is distributed on an "AS IS" BASIS,
1268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * See the License for the specific language governing permissions and
1468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis * limitations under the License.
1568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis */
1668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
1768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#define LOG_TAG "SurfaceTexture"
187dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis//#define LOG_NDEBUG 0
1968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
2068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#define GL_GLEXT_PROTOTYPES
2168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#define EGL_EGLEXT_PROTOTYPES
2268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
2368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <EGL/egl.h>
2468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <EGL/eglext.h>
2568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <GLES2/gl2.h>
2668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <GLES2/gl2ext.h>
2768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
2868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <gui/SurfaceTexture.h>
2968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
3068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <surfaceflinger/ISurfaceComposer.h>
3168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <surfaceflinger/SurfaceComposerClient.h>
32f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis#include <surfaceflinger/IGraphicBufferAlloc.h>
3368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
3468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis#include <utils/Log.h>
3568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
3668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennisnamespace android {
3768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
38b598fb90727be45e926a11abefc319819a733540Jamie Gennis// Transform matrices
39b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic float mtxIdentity[16] = {
40b598fb90727be45e926a11abefc319819a733540Jamie Gennis    1, 0, 0, 0,
41b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 1, 0, 0,
42b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 0, 1, 0,
43b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 0, 0, 1,
44b598fb90727be45e926a11abefc319819a733540Jamie Gennis};
45b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic float mtxFlipH[16] = {
46b598fb90727be45e926a11abefc319819a733540Jamie Gennis    -1, 0, 0, 0,
47b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 1, 0, 0,
48b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 0, 1, 0,
49b598fb90727be45e926a11abefc319819a733540Jamie Gennis    1, 0, 0, 1,
50b598fb90727be45e926a11abefc319819a733540Jamie Gennis};
51b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic float mtxFlipV[16] = {
52b598fb90727be45e926a11abefc319819a733540Jamie Gennis    1, 0, 0, 0,
53b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, -1, 0, 0,
54b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 0, 1, 0,
55b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 1, 0, 1,
56b598fb90727be45e926a11abefc319819a733540Jamie Gennis};
57b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic float mtxRot90[16] = {
58b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 1, 0, 0,
59b598fb90727be45e926a11abefc319819a733540Jamie Gennis    -1, 0, 0, 0,
60b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 0, 1, 0,
61b598fb90727be45e926a11abefc319819a733540Jamie Gennis    1, 0, 0, 1,
62b598fb90727be45e926a11abefc319819a733540Jamie Gennis};
63b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic float mtxRot180[16] = {
64b598fb90727be45e926a11abefc319819a733540Jamie Gennis    -1, 0, 0, 0,
65b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, -1, 0, 0,
66b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 0, 1, 0,
67b598fb90727be45e926a11abefc319819a733540Jamie Gennis    1, 1, 0, 1,
68b598fb90727be45e926a11abefc319819a733540Jamie Gennis};
69b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic float mtxRot270[16] = {
70b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, -1, 0, 0,
71b598fb90727be45e926a11abefc319819a733540Jamie Gennis    1, 0, 0, 0,
72b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 0, 1, 0,
73b598fb90727be45e926a11abefc319819a733540Jamie Gennis    0, 1, 0, 1,
74b598fb90727be45e926a11abefc319819a733540Jamie Gennis};
75b598fb90727be45e926a11abefc319819a733540Jamie Gennis
76b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic void mtxMul(float out[16], const float a[16], const float b[16]);
77b598fb90727be45e926a11abefc319819a733540Jamie Gennis
7868e4a7ac849b681b1fb769857fc04f64262480c4Jamie GennisSurfaceTexture::SurfaceTexture(GLuint tex) :
79c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mBufferCount(MIN_BUFFER_SLOTS),
80c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mCurrentTexture(INVALID_BUFFER_SLOT),
81c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mCurrentTransform(0),
82c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mCurrentTimestamp(0),
83c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mLastQueued(INVALID_BUFFER_SLOT),
84c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mLastQueuedTransform(0),
85c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mLastQueuedTimestamp(0),
86c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mNextTransform(0),
87c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mTexName(tex) {
887dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::SurfaceTexture");
89fd804f31a36c31661859b53bbee1bb408462ddcaJamie Gennis    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
90fd804f31a36c31661859b53bbee1bb408462ddcaJamie Gennis        mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
91fd804f31a36c31661859b53bbee1bb408462ddcaJamie Gennis        mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
92fd804f31a36c31661859b53bbee1bb408462ddcaJamie Gennis        mSlots[i].mOwnedByClient = false;
93fd804f31a36c31661859b53bbee1bb408462ddcaJamie Gennis    }
94f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
95f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
9668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
9768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
9868e4a7ac849b681b1fb769857fc04f64262480c4Jamie GennisSurfaceTexture::~SurfaceTexture() {
997dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::~SurfaceTexture");
10068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    freeAllBuffers();
10168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
10268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
10368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennisstatus_t SurfaceTexture::setBufferCount(int bufferCount) {
1047dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::setBufferCount");
10596dcc978430f0daf6d73fee96a01779ed537a0ceJamie Gennis
10696dcc978430f0daf6d73fee96a01779ed537a0ceJamie Gennis    if (bufferCount < MIN_BUFFER_SLOTS) {
10796dcc978430f0daf6d73fee96a01779ed537a0ceJamie Gennis        return BAD_VALUE;
10896dcc978430f0daf6d73fee96a01779ed537a0ceJamie Gennis    }
10996dcc978430f0daf6d73fee96a01779ed537a0ceJamie Gennis
11068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
11168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    freeAllBuffers();
11268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    mBufferCount = bufferCount;
113d369dc42506ec003f1839bb9e27edada411324b5Jamie Gennis    mCurrentTexture = INVALID_BUFFER_SLOT;
114d369dc42506ec003f1839bb9e27edada411324b5Jamie Gennis    mLastQueued = INVALID_BUFFER_SLOT;
11568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return OK;
11668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
11768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
11868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennissp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
11968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
1207dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::requestBuffer");
12168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
12268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    if (buf < 0 || mBufferCount <= buf) {
12368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("requestBuffer: slot index out of range [0, %d]: %d",
12468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis                mBufferCount, buf);
12568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        return 0;
12668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
12768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    usage |= GraphicBuffer::USAGE_HW_TEXTURE;
128f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    sp<GraphicBuffer> graphicBuffer(
129f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis            mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
13068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    if (graphicBuffer == 0) {
13168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
13268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    } else {
13368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        mSlots[buf].mGraphicBuffer = graphicBuffer;
13468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
13568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
13668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
13768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
13868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        }
139f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        mAllocdBuffers.add(graphicBuffer);
14068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
14168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return graphicBuffer;
14268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
14368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
14468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennisstatus_t SurfaceTexture::dequeueBuffer(int *buf) {
1457dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::dequeueBuffer");
14668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
14768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    int found = INVALID_BUFFER_SLOT;
14868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    for (int i = 0; i < mBufferCount; i++) {
149a7eacc148adec1ee26636a0c727ceefa9e012ba6Jamie Gennis        if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) {
15068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            mSlots[i].mOwnedByClient = true;
15168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            found = i;
15268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            break;
15368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        }
15468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
15568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    if (found == INVALID_BUFFER_SLOT) {
15668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        return -EBUSY;
15768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
15868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    *buf = found;
15968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return OK;
16068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
16168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
162c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvalastatus_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
1637dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::queueBuffer");
16468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
16568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    if (buf < 0 || mBufferCount <= buf) {
16668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("queueBuffer: slot index out of range [0, %d]: %d",
16768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis                mBufferCount, buf);
16868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        return -EINVAL;
16968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    } else if (!mSlots[buf].mOwnedByClient) {
17068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("queueBuffer: slot %d is not owned by the client", buf);
17168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        return -EINVAL;
17268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    } else if (mSlots[buf].mGraphicBuffer == 0) {
17368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("queueBuffer: slot %d was enqueued without requesting a buffer",
17468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis                buf);
17568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        return -EINVAL;
17668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
17768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    mSlots[buf].mOwnedByClient = false;
17868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    mLastQueued = buf;
179b598fb90727be45e926a11abefc319819a733540Jamie Gennis    mLastQueuedCrop = mNextCrop;
180b598fb90727be45e926a11abefc319819a733540Jamie Gennis    mLastQueuedTransform = mNextTransform;
181c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    mLastQueuedTimestamp = timestamp;
182376590d668e22a918439877b55faf075427b13f3Jamie Gennis    if (mFrameAvailableListener != 0) {
183376590d668e22a918439877b55faf075427b13f3Jamie Gennis        mFrameAvailableListener->onFrameAvailable();
184376590d668e22a918439877b55faf075427b13f3Jamie Gennis    }
18568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return OK;
18668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
18768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
18868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennisvoid SurfaceTexture::cancelBuffer(int buf) {
1897dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::cancelBuffer");
19068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
19168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    if (buf < 0 || mBufferCount <= buf) {
19268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount,
19368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis                buf);
19468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        return;
19568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    } else if (!mSlots[buf].mOwnedByClient) {
19668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("cancelBuffer: slot %d is not owned by the client", buf);
19768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        return;
19868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
19968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    mSlots[buf].mOwnedByClient = false;
20068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
20168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
202b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatus_t SurfaceTexture::setCrop(const Rect& crop) {
2037dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::setCrop");
20468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
205b598fb90727be45e926a11abefc319819a733540Jamie Gennis    mNextCrop = crop;
20668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return OK;
20768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
20868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
20968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennisstatus_t SurfaceTexture::setTransform(uint32_t transform) {
2107dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::setTransform");
21168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
212b598fb90727be45e926a11abefc319819a733540Jamie Gennis    mNextTransform = transform;
21368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return OK;
21468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
21568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
21668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennisstatus_t SurfaceTexture::updateTexImage() {
2177dc00d5eb27de41f93a7e232b3cd374c84eb77d1Jamie Gennis    LOGV("SurfaceTexture::updateTexImage");
21868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    Mutex::Autolock lock(mMutex);
21968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
22068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    // We always bind the texture even if we don't update its contents.
22168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName);
22268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
22368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
22468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    // so this check will fail until a buffer gets queued.
22568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    if (mCurrentTexture != mLastQueued) {
226b598fb90727be45e926a11abefc319819a733540Jamie Gennis        // Update the GL texture object.
227f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        EGLImageKHR image = mSlots[mLastQueued].mEglImage;
22868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        if (image == EGL_NO_IMAGE_KHR) {
22968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            EGLDisplay dpy = eglGetCurrentDisplay();
230f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis            sp<GraphicBuffer> graphicBuffer = mSlots[mLastQueued].mGraphicBuffer;
23168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            image = createImage(dpy, graphicBuffer);
232f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis            mSlots[mLastQueued].mEglImage = image;
233f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis            mSlots[mLastQueued].mEglDisplay = dpy;
23468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        }
23579d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis
23679d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis        GLint error;
23779d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis        while ((error = glGetError()) != GL_NO_ERROR) {
23879d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis            LOGE("GL error cleared before updating SurfaceTexture: %#04x", error);
23979d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis        }
24068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
24179d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis        bool failed = false;
24279d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis        while ((error = glGetError()) != GL_NO_ERROR) {
24368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            LOGE("error binding external texture image %p (slot %d): %#04x",
244f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis                    image, mLastQueued, error);
24579d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis            failed = true;
24679d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis        }
24779d01fe8232c67a18aeb8784c2b8783358ec4e44Jamie Gennis        if (failed) {
24868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            return -EINVAL;
24968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        }
250f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis
251f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        // Update the SurfaceTexture state.
252f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        mCurrentTexture = mLastQueued;
253f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
254f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        mCurrentCrop = mLastQueuedCrop;
255f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        mCurrentTransform = mLastQueuedTransform;
256c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala        mCurrentTimestamp = mLastQueuedTimestamp;
25768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
25868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return OK;
25968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
26068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
261b598fb90727be45e926a11abefc319819a733540Jamie Gennisvoid SurfaceTexture::getTransformMatrix(float mtx[16]) {
262c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    LOGV("SurfaceTexture::getTransformMatrix");
263b598fb90727be45e926a11abefc319819a733540Jamie Gennis    Mutex::Autolock lock(mMutex);
264b598fb90727be45e926a11abefc319819a733540Jamie Gennis
2650fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    float xform[16];
2660fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    for (int i = 0; i < 16; i++) {
2670fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        xform[i] = mtxIdentity[i];
2680fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    }
2690fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
2700fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        float result[16];
2710fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        mtxMul(result, xform, mtxFlipH);
2720fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        for (int i = 0; i < 16; i++) {
2730fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis            xform[i] = result[i];
2740fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        }
2750fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    }
2760fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
2770fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        float result[16];
2780fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        mtxMul(result, xform, mtxFlipV);
2790fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        for (int i = 0; i < 16; i++) {
2800fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis            xform[i] = result[i];
2810fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        }
2820fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    }
2830fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
2840fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        float result[16];
2850fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        mtxMul(result, xform, mtxRot90);
2860fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        for (int i = 0; i < 16; i++) {
2870fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis            xform[i] = result[i];
2880fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        }
289b598fb90727be45e926a11abefc319819a733540Jamie Gennis    }
290b598fb90727be45e926a11abefc319819a733540Jamie Gennis
291b598fb90727be45e926a11abefc319819a733540Jamie Gennis    sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer);
2920fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    float tx, ty, sx, sy;
2930fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    if (!mCurrentCrop.isEmpty()) {
294f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // In order to prevent bilinear sampling at the of the crop rectangle we
295f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // may need to shrink it by 2 texels in each direction.  Normally this
296f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // would just need to take 1/2 a texel off each end, but because the
297f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // chroma channels will likely be subsampled we need to chop off a whole
298f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // texel.  This will cause artifacts if someone does nearest sampling
299f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // with 1:1 pixel:texel ratio, but it's impossible to simultaneously
300f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // accomodate the bilinear and nearest sampling uses.
301f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        //
302f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // If nearest sampling turns out to be a desirable usage of these
303f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // textures then we could add the ability to switch a SurfaceTexture to
304f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // nearest-mode.  Preferably, however, the image producers (video
305f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // decoder, camera, etc.) would simply not use a crop rectangle (or at
306f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // least not tell the framework about it) so that the GPU can do the
307f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        // correct edge behavior.
308f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        int xshrink = 0, yshrink = 0;
309f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        if (mCurrentCrop.left > 0) {
310f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            tx = float(mCurrentCrop.left + 1) / float(buf->getWidth());
311f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            xshrink++;
312f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        } else {
313f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            tx = 0.0f;
314f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        }
315f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        if (mCurrentCrop.right < buf->getWidth()) {
316f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            xshrink++;
317f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        }
318f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        if (mCurrentCrop.bottom < buf->getHeight()) {
319f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
320f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis                    float(buf->getHeight());
321f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            yshrink++;
322f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        } else {
323f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            ty = 0.0f;
324f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        }
325f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        if (mCurrentCrop.top > 0) {
326f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis            yshrink++;
327f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        }
328f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        sx = float(mCurrentCrop.width() - xshrink) / float(buf->getWidth());
329f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        sy = float(mCurrentCrop.height() - yshrink) / float(buf->getHeight());
3300fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    } else {
3310fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        tx = 0.0f;
3320fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        ty = 0.0f;
3330fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        sx = 1.0f;
3340fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        sy = 1.0f;
3350fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    }
336b598fb90727be45e926a11abefc319819a733540Jamie Gennis    float crop[16] = {
3370fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        sx, 0, 0, 0,
3380fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis        0, sy, 0, 0,
339b598fb90727be45e926a11abefc319819a733540Jamie Gennis        0, 0, 1, 0,
340f3cedb602197a6255e3a7b005cc368577d75bf3aJamie Gennis        tx, ty, 0, 1,
341b598fb90727be45e926a11abefc319819a733540Jamie Gennis    };
342b598fb90727be45e926a11abefc319819a733540Jamie Gennis
3430fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    float mtxBeforeFlipV[16];
3440fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    mtxMul(mtxBeforeFlipV, crop, xform);
3450fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis
3460fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    // SurfaceFlinger expects the top of its window textures to be at a Y
3470fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    // coordinate of 0, so SurfaceTexture must behave the same way.  We don't
3480fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    // want to expose this to applications, however, so we must add an
3490fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    // additional vertical flip to the transform after all the other transforms.
3500fb736c0937d9d65001e0176d90e1011226594bfJamie Gennis    mtxMul(mtx, mtxFlipV, mtxBeforeFlipV);
351b598fb90727be45e926a11abefc319819a733540Jamie Gennis}
352b598fb90727be45e926a11abefc319819a733540Jamie Gennis
353c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvalansecs_t SurfaceTexture::getTimestamp() {
354c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    LOGV("SurfaceTexture::getTimestamp");
355c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    Mutex::Autolock lock(mMutex);
356c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala    return mCurrentTimestamp;
357c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala}
358c5f94d8a4779050125145396ca83fbc862c7ed6bEino-Ville Talvala
359376590d668e22a918439877b55faf075427b13f3Jamie Gennisvoid SurfaceTexture::setFrameAvailableListener(
360376590d668e22a918439877b55faf075427b13f3Jamie Gennis        const sp<FrameAvailableListener>& l) {
361376590d668e22a918439877b55faf075427b13f3Jamie Gennis    LOGV("SurfaceTexture::setFrameAvailableListener");
362376590d668e22a918439877b55faf075427b13f3Jamie Gennis    Mutex::Autolock lock(mMutex);
363376590d668e22a918439877b55faf075427b13f3Jamie Gennis    mFrameAvailableListener = l;
364376590d668e22a918439877b55faf075427b13f3Jamie Gennis}
365376590d668e22a918439877b55faf075427b13f3Jamie Gennis
36683bac216a7ba8493a7916e40b2555e73c3a5cc1aJamie Gennissp<IBinder> SurfaceTexture::getAllocator() {
36783bac216a7ba8493a7916e40b2555e73c3a5cc1aJamie Gennis    LOGV("SurfaceTexture::getAllocator");
36883bac216a7ba8493a7916e40b2555e73c3a5cc1aJamie Gennis    return mGraphicBufferAlloc->asBinder();
36983bac216a7ba8493a7916e40b2555e73c3a5cc1aJamie Gennis}
37083bac216a7ba8493a7916e40b2555e73c3a5cc1aJamie Gennis
37168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennisvoid SurfaceTexture::freeAllBuffers() {
37268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
37368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        mSlots[i].mGraphicBuffer = 0;
37468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        mSlots[i].mOwnedByClient = false;
37568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
37668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
37768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
37868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
37968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        }
38068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
381f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis
382f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    int exceptBuf = -1;
383f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    for (size_t i = 0; i < mAllocdBuffers.size(); i++) {
384f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        if (mAllocdBuffers[i] == mCurrentTextureBuf) {
385f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis            exceptBuf = i;
386f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis            break;
387f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        }
388f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    }
389f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    mAllocdBuffers.clear();
390f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    if (exceptBuf >= 0) {
391f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis        mAllocdBuffers.add(mCurrentTextureBuf);
392f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    }
393f7acf162f8d682c6ebc9af41ca76795b79509193Jamie Gennis    mGraphicBufferAlloc->freeAllGraphicBuffersExcept(exceptBuf);
39468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
39568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
39668e4a7ac849b681b1fb769857fc04f64262480c4Jamie GennisEGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
39768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        const sp<GraphicBuffer>& graphicBuffer) {
39868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
39968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    EGLint attrs[] = {
40068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE,
40168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        EGL_NONE,
40268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    };
40368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
40468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis            EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
40568e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    EGLint error = eglGetError();
40668e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    if (error != EGL_SUCCESS) {
40768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("error creating EGLImage: %#x", error);
40868e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    } else if (image == EGL_NO_IMAGE_KHR) {
40968e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis        LOGE("no error reported, but no image was returned by "
41068e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis                "eglCreateImageKHR");
41168e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    }
41268e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis    return image;
41368e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}
41468e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis
415b598fb90727be45e926a11abefc319819a733540Jamie Gennisstatic void mtxMul(float out[16], const float a[16], const float b[16]) {
416b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
417b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];
418b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3];
419b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3];
420b598fb90727be45e926a11abefc319819a733540Jamie Gennis
421b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7];
422b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7];
423b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7];
424b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7];
425b598fb90727be45e926a11abefc319819a733540Jamie Gennis
426b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11];
427b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11];
428b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11];
429b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11];
430b598fb90727be45e926a11abefc319819a733540Jamie Gennis
431b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15];
432b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15];
433b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15];
434b598fb90727be45e926a11abefc319819a733540Jamie Gennis    out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];
435b598fb90727be45e926a11abefc319819a733540Jamie Gennis}
436b598fb90727be45e926a11abefc319819a733540Jamie Gennis
43768e4a7ac849b681b1fb769857fc04f64262480c4Jamie Gennis}; // namespace android
438