1/*
2 * Copyright (C) 2010 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#define LOG_NDEBUG 0
18#define LOG_TAG "YUVImage"
19
20#include <media/stagefright/foundation/ADebug.h>
21#include <media/stagefright/YUVImage.h>
22#include <ui/Rect.h>
23
24namespace android {
25
26YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height) {
27    mYUVFormat = yuvFormat;
28    mWidth = width;
29    mHeight = height;
30
31    size_t numberOfBytes = bufferSize(yuvFormat, width, height);
32    uint8_t *buffer = new uint8_t[numberOfBytes];
33    mBuffer = buffer;
34    mOwnBuffer = true;
35
36    initializeYUVPointers();
37}
38
39YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height, uint8_t *buffer) {
40    mYUVFormat = yuvFormat;
41    mWidth = width;
42    mHeight = height;
43    mBuffer = buffer;
44    mOwnBuffer = false;
45
46    initializeYUVPointers();
47}
48
49//static
50size_t YUVImage::bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height) {
51    int32_t numberOfPixels = width*height;
52    size_t numberOfBytes = 0;
53    if (yuvFormat == YUV420Planar || yuvFormat == YUV420SemiPlanar) {
54        // Y takes numberOfPixels bytes and U/V take numberOfPixels/4 bytes each.
55        numberOfBytes = (size_t)(numberOfPixels + (numberOfPixels >> 1));
56    } else {
57        ALOGE("Format not supported");
58    }
59    return numberOfBytes;
60}
61
62bool YUVImage::initializeYUVPointers() {
63    int32_t numberOfPixels = mWidth * mHeight;
64
65    if (mYUVFormat == YUV420Planar) {
66        mYdata = (uint8_t *)mBuffer;
67        mUdata = mYdata + numberOfPixels;
68        mVdata = mUdata + (numberOfPixels >> 2);
69    } else if (mYUVFormat == YUV420SemiPlanar) {
70        // U and V channels are interleaved as VUVUVU.
71        // So V data starts at the end of Y channel and
72        // U data starts right after V's start.
73        mYdata = (uint8_t *)mBuffer;
74        mVdata = mYdata + numberOfPixels;
75        mUdata = mVdata + 1;
76    } else {
77        ALOGE("Format not supported");
78        return false;
79    }
80    return true;
81}
82
83YUVImage::~YUVImage() {
84    if (mOwnBuffer) delete[] mBuffer;
85}
86
87bool YUVImage::getOffsets(int32_t x, int32_t y,
88        int32_t *yOffset, int32_t *uOffset, int32_t *vOffset) const {
89    *yOffset = y*mWidth + x;
90
91    int32_t uvOffset = (y >> 1) * (mWidth >> 1) + (x >> 1);
92    if (mYUVFormat == YUV420Planar) {
93        *uOffset = uvOffset;
94        *vOffset = uvOffset;
95    } else if (mYUVFormat == YUV420SemiPlanar) {
96        // Since U and V channels are interleaved, offsets need
97        // to be doubled.
98        *uOffset = 2*uvOffset;
99        *vOffset = 2*uvOffset;
100    } else {
101        ALOGE("Format not supported");
102        return false;
103    }
104
105    return true;
106}
107
108bool YUVImage::getOffsetIncrementsPerDataRow(
109        int32_t *yDataOffsetIncrement,
110        int32_t *uDataOffsetIncrement,
111        int32_t *vDataOffsetIncrement) const {
112    *yDataOffsetIncrement = mWidth;
113
114    int32_t uvDataOffsetIncrement = mWidth >> 1;
115
116    if (mYUVFormat == YUV420Planar) {
117        *uDataOffsetIncrement = uvDataOffsetIncrement;
118        *vDataOffsetIncrement = uvDataOffsetIncrement;
119    } else if (mYUVFormat == YUV420SemiPlanar) {
120        // Since U and V channels are interleaved, offsets need
121        // to be doubled.
122        *uDataOffsetIncrement = 2*uvDataOffsetIncrement;
123        *vDataOffsetIncrement = 2*uvDataOffsetIncrement;
124    } else {
125        ALOGE("Format not supported");
126        return false;
127    }
128
129    return true;
130}
131
132uint8_t* YUVImage::getYAddress(int32_t offset) const {
133    return mYdata + offset;
134}
135
136uint8_t* YUVImage::getUAddress(int32_t offset) const {
137    return mUdata + offset;
138}
139
140uint8_t* YUVImage::getVAddress(int32_t offset) const {
141    return mVdata + offset;
142}
143
144bool YUVImage::getYUVAddresses(int32_t x, int32_t y,
145        uint8_t **yAddr, uint8_t **uAddr, uint8_t **vAddr) const {
146    int32_t yOffset;
147    int32_t uOffset;
148    int32_t vOffset;
149    if (!getOffsets(x, y, &yOffset, &uOffset, &vOffset)) return false;
150
151    *yAddr = getYAddress(yOffset);
152    *uAddr = getUAddress(uOffset);
153    *vAddr = getVAddress(vOffset);
154
155    return true;
156}
157
158bool YUVImage::validPixel(int32_t x, int32_t y) const {
159    return (x >= 0 && x < mWidth &&
160            y >= 0 && y < mHeight);
161}
162
163bool YUVImage::getPixelValue(int32_t x, int32_t y,
164        uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const {
165    CHECK(validPixel(x, y));
166
167    uint8_t *yAddr;
168    uint8_t *uAddr;
169    uint8_t *vAddr;
170    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
171
172    *yPtr = *yAddr;
173    *uPtr = *uAddr;
174    *vPtr = *vAddr;
175
176    return true;
177}
178
179bool YUVImage::setPixelValue(int32_t x, int32_t y,
180        uint8_t yValue, uint8_t uValue, uint8_t vValue) {
181    CHECK(validPixel(x, y));
182
183    uint8_t *yAddr;
184    uint8_t *uAddr;
185    uint8_t *vAddr;
186    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
187
188    *yAddr = yValue;
189    *uAddr = uValue;
190    *vAddr = vValue;
191
192    return true;
193}
194
195void YUVImage::fastCopyRectangle420Planar(
196        const Rect& srcRect,
197        int32_t destStartX, int32_t destStartY,
198        const YUVImage &srcImage, YUVImage &destImage) {
199    CHECK(srcImage.mYUVFormat == YUV420Planar);
200    CHECK(destImage.mYUVFormat == YUV420Planar);
201
202    int32_t srcStartX = srcRect.left;
203    int32_t srcStartY = srcRect.top;
204    int32_t width = srcRect.width();
205    int32_t height = srcRect.height();
206
207    // Get source and destination start addresses
208    uint8_t *ySrcAddrBase;
209    uint8_t *uSrcAddrBase;
210    uint8_t *vSrcAddrBase;
211    srcImage.getYUVAddresses(srcStartX, srcStartY,
212            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
213
214    uint8_t *yDestAddrBase;
215    uint8_t *uDestAddrBase;
216    uint8_t *vDestAddrBase;
217    destImage.getYUVAddresses(destStartX, destStartY,
218            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
219
220    // Get source and destination offset increments incurred in going
221    // from one data row to next.
222    int32_t ySrcOffsetIncrement;
223    int32_t uSrcOffsetIncrement;
224    int32_t vSrcOffsetIncrement;
225    srcImage.getOffsetIncrementsPerDataRow(
226            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
227
228    int32_t yDestOffsetIncrement;
229    int32_t uDestOffsetIncrement = 0;
230    int32_t vDestOffsetIncrement = 0;
231    destImage.getOffsetIncrementsPerDataRow(
232            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
233
234    // Copy Y
235    {
236        size_t numberOfYBytesPerRow = (size_t) width;
237        uint8_t *ySrcAddr = ySrcAddrBase;
238        uint8_t *yDestAddr = yDestAddrBase;
239        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
240            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
241
242            ySrcAddr += ySrcOffsetIncrement;
243            yDestAddr += yDestOffsetIncrement;
244        }
245    }
246
247    // Copy U
248    {
249        size_t numberOfUBytesPerRow = (size_t) (width >> 1);
250        uint8_t *uSrcAddr = uSrcAddrBase;
251        uint8_t *uDestAddr = uDestAddrBase;
252        // Every other row has an entry for U/V channel values. Hence only
253        // go half the height.
254        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
255            memcpy(uDestAddr, uSrcAddr, numberOfUBytesPerRow);
256
257            uSrcAddr += uSrcOffsetIncrement;
258            uDestAddr += uDestOffsetIncrement;
259        }
260    }
261
262    // Copy V
263    {
264        size_t numberOfVBytesPerRow = (size_t) (width >> 1);
265        uint8_t *vSrcAddr = vSrcAddrBase;
266        uint8_t *vDestAddr = vDestAddrBase;
267        // Every other pixel row has a U/V data row. Hence only go half the height.
268        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
269            memcpy(vDestAddr, vSrcAddr, numberOfVBytesPerRow);
270
271            vSrcAddr += vSrcOffsetIncrement;
272            vDestAddr += vDestOffsetIncrement;
273        }
274    }
275}
276
277void YUVImage::fastCopyRectangle420SemiPlanar(
278        const Rect& srcRect,
279        int32_t destStartX, int32_t destStartY,
280        const YUVImage &srcImage, YUVImage &destImage) {
281    CHECK(srcImage.mYUVFormat == YUV420SemiPlanar);
282    CHECK(destImage.mYUVFormat == YUV420SemiPlanar);
283
284    int32_t srcStartX = srcRect.left;
285    int32_t srcStartY = srcRect.top;
286    int32_t width = srcRect.width();
287    int32_t height = srcRect.height();
288
289    // Get source and destination start addresses
290    uint8_t *ySrcAddrBase;
291    uint8_t *uSrcAddrBase;
292    uint8_t *vSrcAddrBase;
293    srcImage.getYUVAddresses(srcStartX, srcStartY,
294            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
295
296    uint8_t *yDestAddrBase;
297    uint8_t *uDestAddrBase;
298    uint8_t *vDestAddrBase;
299    destImage.getYUVAddresses(destStartX, destStartY,
300            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
301
302    // Get source and destination offset increments incurred in going
303    // from one data row to next.
304    int32_t ySrcOffsetIncrement;
305    int32_t uSrcOffsetIncrement;
306    int32_t vSrcOffsetIncrement;
307    srcImage.getOffsetIncrementsPerDataRow(
308            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
309
310    int32_t yDestOffsetIncrement;
311    int32_t uDestOffsetIncrement;
312    int32_t vDestOffsetIncrement = 0;
313    destImage.getOffsetIncrementsPerDataRow(
314            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
315
316    // Copy Y
317    {
318        size_t numberOfYBytesPerRow = (size_t) width;
319        uint8_t *ySrcAddr = ySrcAddrBase;
320        uint8_t *yDestAddr = yDestAddrBase;
321        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
322            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
323
324            ySrcAddr = ySrcAddr + ySrcOffsetIncrement;
325            yDestAddr = yDestAddr + yDestOffsetIncrement;
326        }
327    }
328
329    // Copy UV
330    {
331        // UV are interleaved. So number of UV bytes per row is 2*(width/2).
332        size_t numberOfUVBytesPerRow = (size_t) width;
333        uint8_t *vSrcAddr = vSrcAddrBase;
334        uint8_t *vDestAddr = vDestAddrBase;
335        // Every other pixel row has a U/V data row. Hence only go half the height.
336        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
337            memcpy(vDestAddr, vSrcAddr, numberOfUVBytesPerRow);
338
339            vSrcAddr += vSrcOffsetIncrement;
340            vDestAddr += vDestOffsetIncrement;
341        }
342    }
343}
344
345// static
346bool YUVImage::fastCopyRectangle(
347        const Rect& srcRect,
348        int32_t destStartX, int32_t destStartY,
349        const YUVImage &srcImage, YUVImage &destImage) {
350    if (srcImage.mYUVFormat == destImage.mYUVFormat) {
351        if (srcImage.mYUVFormat == YUV420Planar) {
352            fastCopyRectangle420Planar(
353                    srcRect,
354                    destStartX, destStartY,
355                    srcImage, destImage);
356        } else if (srcImage.mYUVFormat == YUV420SemiPlanar) {
357            fastCopyRectangle420SemiPlanar(
358                    srcRect,
359                    destStartX, destStartY,
360                    srcImage, destImage);
361        }
362        return true;
363    }
364    return false;
365}
366
367uint8_t clamp(uint8_t v, uint8_t minValue, uint8_t maxValue) {
368    CHECK(maxValue >= minValue);
369
370    if (v < minValue) return minValue;
371    else if (v > maxValue) return maxValue;
372    else return v;
373}
374
375void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
376        uint8_t *r, uint8_t *g, uint8_t *b) const {
377    int rTmp = yValue + (1.370705 * (vValue-128));
378    int gTmp = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128));
379    int bTmp = yValue + (1.732446 * (uValue-128));
380
381    *r = clamp(rTmp, 0, 255);
382    *g = clamp(gTmp, 0, 255);
383    *b = clamp(bTmp, 0, 255);
384}
385
386bool YUVImage::writeToPPM(const char *filename) const {
387    FILE *fp = fopen(filename, "w");
388    if (fp == NULL) {
389        return false;
390    }
391    fprintf(fp, "P3\n");
392    fprintf(fp, "%d %d\n", mWidth, mHeight);
393    fprintf(fp, "255\n");
394    for (int32_t y = 0; y < mHeight; ++y) {
395        for (int32_t x = 0; x < mWidth; ++x) {
396            uint8_t yValue = 0u;
397            uint8_t uValue = 0u;
398            uint8_t vValue = 0u;
399            getPixelValue(x, y, &yValue, &uValue, & vValue);
400
401            uint8_t rValue;
402            uint8_t gValue;
403            uint8_t bValue;
404            yuv2rgb(yValue, uValue, vValue, &rValue, &gValue, &bValue);
405
406            fprintf(fp, "%d %d %d\n", (int32_t)rValue, (int32_t)gValue, (int32_t)bValue);
407        }
408    }
409    fclose(fp);
410    return true;
411}
412
413}  // namespace android
414