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
This commit is contained in:
Nipun Kwatra
2010-07-27 22:13:04 -07:00
parent 26d9b9870e
commit a435c1389a
5 changed files with 741 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
/*
* 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_

View File

@@ -0,0 +1,174 @@
/*
* 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_

View File

@@ -0,0 +1,13 @@
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)

View File

@@ -0,0 +1,83 @@
/*
* 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

View File

@@ -0,0 +1,404 @@
/*
* 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