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

Commit a435c138 authored by Nipun Kwatra's avatar Nipun Kwatra
Browse files

Adding YUVImage and YUVCanvas.

YUVImage is a container class to hold YUV data and provide
various utilities, e.g. to set/get pixel values for different
YUV formats, fast copying routines, etc.
Currently supported YUV420 Planar and YUV420 Semi Planar.

YUVCanvas holds a reference to a YUVImage on which it can do various
drawing operations.

Change-Id: I052a57b7fbc834efe1626914f76c04c091996cac
parent 26d9b987
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