Home | History | Annotate | Download | only in yuv
      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/YUVImage.h>
     21 #include <ui/Rect.h>
     22 #include <media/stagefright/MediaDebug.h>
     23 
     24 namespace android {
     25 
     26 YUVImage::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 
     39 YUVImage::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
     50 size_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         LOGE("Format not supported");
     58     }
     59     return numberOfBytes;
     60 }
     61 
     62 bool 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         LOGE("Format not supported");
     78         return false;
     79     }
     80     return true;
     81 }
     82 
     83 YUVImage::~YUVImage() {
     84     if (mOwnBuffer) delete[] mBuffer;
     85 }
     86 
     87 bool 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         LOGE("Format not supported");
    102         return false;
    103     }
    104 
    105     return true;
    106 }
    107 
    108 bool 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         LOGE("Format not supported");
    126         return false;
    127     }
    128 
    129     return true;
    130 }
    131 
    132 uint8_t* YUVImage::getYAddress(int32_t offset) const {
    133     return mYdata + offset;
    134 }
    135 
    136 uint8_t* YUVImage::getUAddress(int32_t offset) const {
    137     return mUdata + offset;
    138 }
    139 
    140 uint8_t* YUVImage::getVAddress(int32_t offset) const {
    141     return mVdata + offset;
    142 }
    143 
    144 bool 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 
    158 bool YUVImage::validPixel(int32_t x, int32_t y) const {
    159     return (x >= 0 && x < mWidth &&
    160             y >= 0 && y < mHeight);
    161 }
    162 
    163 bool 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 
    179 bool 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 
    195 void 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;
    230     int32_t vDestOffsetIncrement;
    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 
    277 void 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;
    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
    346 bool 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 
    367 uint8_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 
    375 void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
    376         uint8_t *r, uint8_t *g, uint8_t *b) const {
    377     *r = yValue + (1.370705 * (vValue-128));
    378     *g = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128));
    379     *b = yValue + (1.732446 * (uValue-128));
    380 
    381     *r = clamp(*r, 0, 255);
    382     *g = clamp(*g, 0, 255);
    383     *b = clamp(*b, 0, 255);
    384 }
    385 
    386 bool 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;
    397             uint8_t uValue;
    398             uint8_t vValue;
    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