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