Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2aa115f authored by Nipun Kwatra's avatar Nipun Kwatra Committed by Android (Google) Code Review
Browse files

Merge "Adding YUVImage and YUVCanvas."

parents 74278c73 a435c138
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// YUVCanvas holds a reference to a YUVImage on which it can do various
// drawing operations. It provides various utility functions for filling,
// cropping, etc.


#ifndef YUV_CANVAS_H_

#define YUV_CANVAS_H_

#include <stdint.h>

namespace android {

class YUVImage;
class Rect;

class YUVCanvas {
public:

    // Constructor takes in reference to a yuvImage on which it can do
    // various drawing opreations.
    YUVCanvas(YUVImage &yuvImage);
    ~YUVCanvas();

    // Fills the entire image with the given YUV values.
    void FillYUV(uint8_t yValue, uint8_t uValue, uint8_t vValue);

    // Fills the rectangular region [startX,endX]x[startY,endY] with the given YUV values.
    void FillYUVRectangle(const Rect& rect,
            uint8_t yValue, uint8_t uValue, uint8_t vValue);

    // Copies the region [startX,endX]x[startY,endY] from srcImage into the
    // canvas' target image (mYUVImage) starting at
    // (destinationStartX,destinationStartY).
    // Note that undefined behavior may occur if srcImage is same as the canvas'
    // target image.
    void CopyImageRect(
            const Rect& srcRect,
            int32_t destStartX, int32_t destStartY,
            const YUVImage &srcImage);

private:
    YUVImage& mYUVImage;

    YUVCanvas(const YUVCanvas &);
    YUVCanvas &operator=(const YUVCanvas &);
};

}  // namespace android

#endif  // YUV_CANVAS_H_
+174 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// A container class to hold YUV data and provide various utilities,
// e.g. to set/get pixel values.
// Supported formats:
//  - YUV420 Planar
//  - YUV420 Semi Planar
//
//  Currently does not support variable strides.
//
//  Implementation: Two simple abstractions are done to simplify access
//  to YUV channels for different formats:
//  - initializeYUVPointers() sets up pointers (mYdata, mUdata, mVdata) to
//  point to the right start locations of the different channel data depending
//  on the format.
//  - getOffsets() returns the correct offset for the different channels
//  depending on the format.
//  Location of any pixel's YUV channels can then be easily computed using these.
//

#ifndef YUV_IMAGE_H_

#define YUV_IMAGE_H_

#include <stdint.h>
#include <cstring>

namespace android {

class Rect;

class YUVImage {
public:
    // Supported YUV formats
    enum YUVFormat {
        YUV420Planar,
        YUV420SemiPlanar
    };

    // Constructs an image with the given size, format. Also allocates and owns
    // the required memory.
    YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height);

    // Constructs an image with the given size, format. The memory is provided
    // by the caller and we don't own it.
    YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height, uint8_t *buffer);

    // Destructor to delete the memory if it owns it.
    ~YUVImage();

    // Returns the size of the buffer required to store the YUV data for the given
    // format and geometry. Useful when the caller wants to allocate the requisite
    // memory.
    static size_t bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height);

    int32_t width() {return mWidth;}
    int32_t height() {return mHeight;}

    // Get the pixel YUV value at pixel (x,y).
    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
    // Returns true if get was succesful and false otherwise.
    bool getPixelValue(int32_t x, int32_t y,
            uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const;

    // Set the pixel YUV value at pixel (x,y).
    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
    // Returns true if set was succesful and false otherwise.
    bool setPixelValue(int32_t x, int32_t y,
            uint8_t yValue, uint8_t uValue, uint8_t vValue);

    // Uses memcpy to copy an entire row of data
    static void fastCopyRectangle420Planar(
            const Rect& srcRect,
            int32_t destStartX, int32_t destStartY,
            const YUVImage &srcImage, YUVImage &destImage);

    // Uses memcpy to copy an entire row of data
    static void fastCopyRectangle420SemiPlanar(
            const Rect& srcRect,
            int32_t destStartX, int32_t destStartY,
            const YUVImage &srcImage, YUVImage &destImage);

    // Tries to use memcopy to copy entire rows of data.
    // Returns false if fast copy is not possible for the passed image formats.
    static bool fastCopyRectangle(
            const Rect& srcRect,
            int32_t destStartX, int32_t destStartY,
            const YUVImage &srcImage, YUVImage &destImage);

    // Convert the given YUV value to RGB.
    void yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
        uint8_t *r, uint8_t *g, uint8_t *b) const;

    // Write the image to a human readable PPM file.
    // Returns true if write was succesful and false otherwise.
    bool writeToPPM(const char *filename) const;

private:
    // YUV Format of the image.
    YUVFormat mYUVFormat;

    int32_t mWidth;
    int32_t mHeight;

    // Pointer to the memory buffer.
    uint8_t *mBuffer;

    // Boolean telling whether we own the memory buffer.
    bool mOwnBuffer;

    // Pointer to start of the Y data plane.
    uint8_t *mYdata;

    // Pointer to start of the U data plane. Note that in case of interleaved formats like
    // YUV420 semiplanar, mUdata points to the start of the U data in the UV plane.
    uint8_t *mUdata;

    // Pointer to start of the V data plane. Note that in case of interleaved formats like
    // YUV420 semiplanar, mVdata points to the start of the V data in the UV plane.
    uint8_t *mVdata;

    // Initialize the pointers mYdata, mUdata, mVdata to point to the right locations for
    // the given format and geometry.
    // Returns true if initialize was succesful and false otherwise.
    bool initializeYUVPointers();

    // For the given pixel location, this returns the offset of the location of y, u and v
    // data from the corresponding base pointers -- mYdata, mUdata, mVdata.
    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
    // Returns true if getting offsets was succesful and false otherwise.
    bool getOffsets(int32_t x, int32_t y,
        int32_t *yOffset, int32_t *uOffset, int32_t *vOffset) const;

    // Returns the offset increments incurred in going from one data row to the next data row
    // for the YUV channels. Note that this corresponds to data rows and not pixel rows.
    // E.g. depending on formats, U/V channels may have only one data row corresponding
    // to two pixel rows.
    bool getOffsetIncrementsPerDataRow(
        int32_t *yDataOffsetIncrement,
        int32_t *uDataOffsetIncrement,
        int32_t *vDataOffsetIncrement) const;

    // Given the offset return the address of the corresponding channel's data.
    uint8_t* getYAddress(int32_t offset) const;
    uint8_t* getUAddress(int32_t offset) const;
    uint8_t* getVAddress(int32_t offset) const;

    // Given the pixel location, returns the address of the corresponding channel's data.
    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
    bool getYUVAddresses(int32_t x, int32_t y,
        uint8_t **yAddr, uint8_t **uAddr, uint8_t **vAddr) const;

    // Disallow implicit casting and copying.
    YUVImage(const YUVImage &);
    YUVImage &operator=(const YUVImage &);
};

}  // namespace android

#endif  // YUV_IMAGE_H_
+13 −0
Original line number Diff line number Diff line
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:=               \
        YUVImage.cpp            \
        YUVCanvas.cpp

LOCAL_SHARED_LIBRARIES :=       \
        libcutils

LOCAL_MODULE:= libstagefright_yuv

include $(BUILD_SHARED_LIBRARY)
+83 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_NDEBUG 0
#define LOG_TAG "YUVCanvas"

#include <media/stagefright/YUVCanvas.h>
#include <media/stagefright/YUVImage.h>
#include <ui/Rect.h>

namespace android {

YUVCanvas::YUVCanvas(YUVImage &yuvImage)
    : mYUVImage(yuvImage) {
}

YUVCanvas::~YUVCanvas() {
}

void YUVCanvas::FillYUV(uint8_t yValue, uint8_t uValue, uint8_t vValue) {
    for (int32_t y = 0; y < mYUVImage.height(); ++y) {
        for (int32_t x = 0; x < mYUVImage.width(); ++x) {
            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
        }
    }
}

void YUVCanvas::FillYUVRectangle(const Rect& rect,
        uint8_t yValue, uint8_t uValue, uint8_t vValue) {
    for (int32_t y = rect.top; y < rect.bottom; ++y) {
        for (int32_t x = rect.left; x < rect.right; ++x) {
            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
        }
    }
}

void YUVCanvas::CopyImageRect(
        const Rect& srcRect,
        int32_t destStartX, int32_t destStartY,
        const YUVImage &srcImage) {

    // Try fast copy first
    if (YUVImage::fastCopyRectangle(
                srcRect,
                destStartX, destStartY,
                srcImage, mYUVImage)) {
        return;
    }

    int32_t srcStartX = srcRect.left;
    int32_t srcStartY = srcRect.top;
    for (int32_t offsetY = 0; offsetY < srcRect.height(); ++offsetY) {
        for (int32_t offsetX = 0; offsetX < srcRect.width(); ++offsetX) {
            int32_t srcX = srcStartX + offsetX;
            int32_t srcY = srcStartY + offsetY;

            int32_t destX = destStartX + offsetX;
            int32_t destY = destStartY + offsetY;

            uint8_t yValue;
            uint8_t uValue;
            uint8_t vValue;

            srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, & vValue);
            mYUVImage.setPixelValue(destX, destY, yValue, uValue, vValue);
        }
    }
}

}  // namespace android
+404 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_NDEBUG 0
#define LOG_TAG "YUVImage"

#include <media/stagefright/YUVImage.h>
#include <ui/Rect.h>
#include <media/stagefright/MediaDebug.h>

namespace android {

YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height) {
    mYUVFormat = yuvFormat;
    mWidth = width;
    mHeight = height;

    size_t numberOfBytes = bufferSize(yuvFormat, width, height);
    uint8_t *buffer = new uint8_t[numberOfBytes];
    mBuffer = buffer;
    mOwnBuffer = true;

    initializeYUVPointers();
}

YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height, uint8_t *buffer) {
    mYUVFormat = yuvFormat;
    mWidth = width;
    mHeight = height;
    mBuffer = buffer;
    mOwnBuffer = false;

    initializeYUVPointers();
}

//static
size_t YUVImage::bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height) {
    int32_t numberOfPixels = width*height;
    size_t numberOfBytes = 0;
    if (yuvFormat == YUV420Planar || yuvFormat == YUV420SemiPlanar) {
        // Y takes numberOfPixels bytes and U/V take numberOfPixels/4 bytes each.
        numberOfBytes = (size_t)(numberOfPixels + (numberOfPixels >> 1));
    } else {
        LOGE("Format not supported");
    }
    return numberOfBytes;
}

bool YUVImage::initializeYUVPointers() {
    int32_t numberOfPixels = mWidth * mHeight;

    if (mYUVFormat == YUV420Planar) {
        mYdata = (uint8_t *)mBuffer;
        mUdata = mYdata + numberOfPixels;
        mVdata = mUdata + (numberOfPixels >> 2);
    } else if (mYUVFormat == YUV420SemiPlanar) {
        // U and V channels are interleaved as VUVUVU.
        // So V data starts at the end of Y channel and
        // U data starts right after V's start.
        mYdata = (uint8_t *)mBuffer;
        mVdata = mYdata + numberOfPixels;
        mUdata = mVdata + 1;
    } else {
        LOGE("Format not supported");
        return false;
    }
    return true;
}

YUVImage::~YUVImage() {
    if (mOwnBuffer) delete[] mBuffer;
}

bool YUVImage::getOffsets(int32_t x, int32_t y,
        int32_t *yOffset, int32_t *uOffset, int32_t *vOffset) const {
    *yOffset = y*mWidth + x;

    int32_t uvOffset = (y >> 1) * (mWidth >> 1) + (x >> 1);
    if (mYUVFormat == YUV420Planar) {
        *uOffset = uvOffset;
        *vOffset = uvOffset;
    } else if (mYUVFormat == YUV420SemiPlanar) {
        // Since U and V channels are interleaved, offsets need
        // to be doubled.
        *uOffset = 2*uvOffset;
        *vOffset = 2*uvOffset;
    } else {
        LOGE("Format not supported");
        return false;
    }

    return true;
}

bool YUVImage::getOffsetIncrementsPerDataRow(
        int32_t *yDataOffsetIncrement,
        int32_t *uDataOffsetIncrement,
        int32_t *vDataOffsetIncrement) const {
    *yDataOffsetIncrement = mWidth;

    int32_t uvDataOffsetIncrement = mWidth >> 1;

    if (mYUVFormat == YUV420Planar) {
        *uDataOffsetIncrement = uvDataOffsetIncrement;
        *vDataOffsetIncrement = uvDataOffsetIncrement;
    } else if (mYUVFormat == YUV420SemiPlanar) {
        // Since U and V channels are interleaved, offsets need
        // to be doubled.
        *uDataOffsetIncrement = 2*uvDataOffsetIncrement;
        *vDataOffsetIncrement = 2*uvDataOffsetIncrement;
    } else {
        LOGE("Format not supported");
        return false;
    }

    return true;
}

uint8_t* YUVImage::getYAddress(int32_t offset) const {
    return mYdata + offset;
}

uint8_t* YUVImage::getUAddress(int32_t offset) const {
    return mUdata + offset;
}

uint8_t* YUVImage::getVAddress(int32_t offset) const {
    return mVdata + offset;
}

bool YUVImage::getYUVAddresses(int32_t x, int32_t y,
        uint8_t **yAddr, uint8_t **uAddr, uint8_t **vAddr) const {
    int32_t yOffset;
    int32_t uOffset;
    int32_t vOffset;
    if (!getOffsets(x, y, &yOffset, &uOffset, &vOffset)) return false;

    *yAddr = getYAddress(yOffset);
    *uAddr = getUAddress(uOffset);
    *vAddr = getVAddress(vOffset);

    return true;
}

bool YUVImage::getPixelValue(int32_t x, int32_t y,
        uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const {
    uint8_t *yAddr;
    uint8_t *uAddr;
    uint8_t *vAddr;
    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;

    *yPtr = *yAddr;
    *uPtr = *uAddr;
    *vPtr = *vAddr;

    return true;
}

bool YUVImage::setPixelValue(int32_t x, int32_t y,
        uint8_t yValue, uint8_t uValue, uint8_t vValue) {
    uint8_t *yAddr;
    uint8_t *uAddr;
    uint8_t *vAddr;
    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;

    *yAddr = yValue;
    *uAddr = uValue;
    *vAddr = vValue;

    return true;
}

void YUVImage::fastCopyRectangle420Planar(
        const Rect& srcRect,
        int32_t destStartX, int32_t destStartY,
        const YUVImage &srcImage, YUVImage &destImage) {
    CHECK(srcImage.mYUVFormat == YUV420Planar);
    CHECK(destImage.mYUVFormat == YUV420Planar);

    int32_t srcStartX = srcRect.left;
    int32_t srcStartY = srcRect.top;
    int32_t width = srcRect.width();
    int32_t height = srcRect.height();

    // Get source and destination start addresses
    uint8_t *ySrcAddrBase;
    uint8_t *uSrcAddrBase;
    uint8_t *vSrcAddrBase;
    srcImage.getYUVAddresses(srcStartX, srcStartY,
            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);

    uint8_t *yDestAddrBase;
    uint8_t *uDestAddrBase;
    uint8_t *vDestAddrBase;
    destImage.getYUVAddresses(destStartX, destStartY,
            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);

    // Get source and destination offset increments incurred in going
    // from one data row to next.
    int32_t ySrcOffsetIncrement;
    int32_t uSrcOffsetIncrement;
    int32_t vSrcOffsetIncrement;
    srcImage.getOffsetIncrementsPerDataRow(
            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);

    int32_t yDestOffsetIncrement;
    int32_t uDestOffsetIncrement;
    int32_t vDestOffsetIncrement;
    destImage.getOffsetIncrementsPerDataRow(
            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);

    // Copy Y
    {
        size_t numberOfYBytesPerRow = (size_t) width;
        uint8_t *ySrcAddr = ySrcAddrBase;
        uint8_t *yDestAddr = yDestAddrBase;
        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);

            ySrcAddr += ySrcOffsetIncrement;
            yDestAddr += yDestOffsetIncrement;
        }
    }

    // Copy U
    {
        size_t numberOfUBytesPerRow = (size_t) (width >> 1);
        uint8_t *uSrcAddr = uSrcAddrBase;
        uint8_t *uDestAddr = uDestAddrBase;
        // Every other row has an entry for U/V channel values. Hence only
        // go half the height.
        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
            memcpy(uDestAddr, uSrcAddr, numberOfUBytesPerRow);

            uSrcAddr += uSrcOffsetIncrement;
            uDestAddr += uDestOffsetIncrement;
        }
    }

    // Copy V
    {
        size_t numberOfVBytesPerRow = (size_t) (width >> 1);
        uint8_t *vSrcAddr = vSrcAddrBase;
        uint8_t *vDestAddr = vDestAddrBase;
        // Every other pixel row has a U/V data row. Hence only go half the height.
        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
            memcpy(vDestAddr, vSrcAddr, numberOfVBytesPerRow);

            vSrcAddr += vSrcOffsetIncrement;
            vDestAddr += vDestOffsetIncrement;
        }
    }
}

void YUVImage::fastCopyRectangle420SemiPlanar(
        const Rect& srcRect,
        int32_t destStartX, int32_t destStartY,
        const YUVImage &srcImage, YUVImage &destImage) {
    CHECK(srcImage.mYUVFormat == YUV420SemiPlanar);
    CHECK(destImage.mYUVFormat == YUV420SemiPlanar);

    int32_t srcStartX = srcRect.left;
    int32_t srcStartY = srcRect.top;
    int32_t width = srcRect.width();
    int32_t height = srcRect.height();

    // Get source and destination start addresses
    uint8_t *ySrcAddrBase;
    uint8_t *uSrcAddrBase;
    uint8_t *vSrcAddrBase;
    srcImage.getYUVAddresses(srcStartX, srcStartY,
            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);

    uint8_t *yDestAddrBase;
    uint8_t *uDestAddrBase;
    uint8_t *vDestAddrBase;
    destImage.getYUVAddresses(destStartX, destStartY,
            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);

    // Get source and destination offset increments incurred in going
    // from one data row to next.
    int32_t ySrcOffsetIncrement;
    int32_t uSrcOffsetIncrement;
    int32_t vSrcOffsetIncrement;
    srcImage.getOffsetIncrementsPerDataRow(
            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);

    int32_t yDestOffsetIncrement;
    int32_t uDestOffsetIncrement;
    int32_t vDestOffsetIncrement;
    destImage.getOffsetIncrementsPerDataRow(
            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);

    // Copy Y
    {
        size_t numberOfYBytesPerRow = (size_t) width;
        uint8_t *ySrcAddr = ySrcAddrBase;
        uint8_t *yDestAddr = yDestAddrBase;
        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);

            ySrcAddr = ySrcAddr + ySrcOffsetIncrement;
            yDestAddr = yDestAddr + yDestOffsetIncrement;
        }
    }

    // Copy UV
    {
        // UV are interleaved. So number of UV bytes per row is 2*(width/2).
        size_t numberOfUVBytesPerRow = (size_t) width;
        uint8_t *vSrcAddr = vSrcAddrBase;
        uint8_t *vDestAddr = vDestAddrBase;
        // Every other pixel row has a U/V data row. Hence only go half the height.
        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
            memcpy(vDestAddr, vSrcAddr, numberOfUVBytesPerRow);

            vSrcAddr += vSrcOffsetIncrement;
            vDestAddr += vDestOffsetIncrement;
        }
    }
}

// static
bool YUVImage::fastCopyRectangle(
        const Rect& srcRect,
        int32_t destStartX, int32_t destStartY,
        const YUVImage &srcImage, YUVImage &destImage) {
    if (srcImage.mYUVFormat == destImage.mYUVFormat) {
        if (srcImage.mYUVFormat == YUV420Planar) {
            fastCopyRectangle420Planar(
                    srcRect,
                    destStartX, destStartY,
                    srcImage, destImage);
        } else if (srcImage.mYUVFormat == YUV420SemiPlanar) {
            fastCopyRectangle420SemiPlanar(
                    srcRect,
                    destStartX, destStartY,
                    srcImage, destImage);
        }
        return true;
    }
    return false;
}

uint8_t clamp(uint8_t v, uint8_t minValue, uint8_t maxValue) {
    CHECK(maxValue >= minValue);

    if (v < minValue) return minValue;
    else if (v > maxValue) return maxValue;
    else return v;
}

void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
        uint8_t *r, uint8_t *g, uint8_t *b) const {
    *r = yValue + (1.370705 * (vValue-128));
    *g = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128));
    *b = yValue + (1.732446 * (uValue-128));

    *r = clamp(*r, 0, 255);
    *g = clamp(*g, 0, 255);
    *b = clamp(*b, 0, 255);
}

bool YUVImage::writeToPPM(const char *filename) const {
    FILE *fp = fopen(filename, "w");
    if (fp == NULL) {
        return false;
    }
    fprintf(fp, "P3\n");
    fprintf(fp, "%d %d\n", mWidth, mHeight);
    fprintf(fp, "255\n");
    for (int32_t y = 0; y < mHeight; ++y) {
        for (int32_t x = 0; x < mWidth; ++x) {
            uint8_t yValue;
            uint8_t uValue;
            uint8_t vValue;
            getPixelValue(x, y, &yValue, &uValue, & vValue);

            uint8_t rValue;
            uint8_t gValue;
            uint8_t bValue;
            yuv2rgb(yValue, uValue, vValue, &rValue, &gValue, &bValue);

            fprintf(fp, "%d %d %d\n", (int32_t)rValue, (int32_t)gValue, (int32_t)bValue);
        }
    }
    fclose(fp);
    return true;
}

}  // namespace android