blob: 3b92e5d0072e11f77b5ed35a142a8afeaa71af7b [file] [log] [blame]
Andreas Huber53a76bd2009-10-06 16:20:44 -07001/*
2 * Copyright (C) 2009 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#include <media/stagefright/ColorConverter.h>
18#include <media/stagefright/MediaDebug.h>
Andreas Huberf341eb52011-01-06 11:26:54 -080019#include <media/stagefright/MediaErrors.h>
Andreas Huber53a76bd2009-10-06 16:20:44 -070020
21namespace android {
22
23static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
24
25ColorConverter::ColorConverter(
26 OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to)
27 : mSrcFormat(from),
28 mDstFormat(to),
29 mClip(NULL) {
30}
31
32ColorConverter::~ColorConverter() {
33 delete[] mClip;
34 mClip = NULL;
35}
36
37bool ColorConverter::isValid() const {
38 if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
39 return false;
40 }
41
42 switch (mSrcFormat) {
43 case OMX_COLOR_FormatYUV420Planar:
44 case OMX_COLOR_FormatCbYCrY:
45 case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
Andreas Huberc543a212010-06-30 10:32:20 -070046 case OMX_COLOR_FormatYUV420SemiPlanar:
Andreas Huber53a76bd2009-10-06 16:20:44 -070047 return true;
48
49 default:
50 return false;
51 }
52}
53
Andreas Huber1bb0ffd2010-11-22 13:06:35 -080054ColorConverter::BitmapParams::BitmapParams(
55 void *bits,
Andreas Huber53a76bd2009-10-06 16:20:44 -070056 size_t width, size_t height,
Andreas Huber1bb0ffd2010-11-22 13:06:35 -080057 size_t cropLeft, size_t cropTop,
58 size_t cropRight, size_t cropBottom)
59 : mBits(bits),
60 mWidth(width),
61 mHeight(height),
62 mCropLeft(cropLeft),
63 mCropTop(cropTop),
64 mCropRight(cropRight),
65 mCropBottom(cropBottom) {
66}
67
68size_t ColorConverter::BitmapParams::cropWidth() const {
69 return mCropRight - mCropLeft + 1;
70}
71
72size_t ColorConverter::BitmapParams::cropHeight() const {
73 return mCropBottom - mCropTop + 1;
74}
75
Andreas Huberf341eb52011-01-06 11:26:54 -080076status_t ColorConverter::convert(
Andreas Huber1bb0ffd2010-11-22 13:06:35 -080077 const void *srcBits,
78 size_t srcWidth, size_t srcHeight,
79 size_t srcCropLeft, size_t srcCropTop,
80 size_t srcCropRight, size_t srcCropBottom,
81 void *dstBits,
82 size_t dstWidth, size_t dstHeight,
83 size_t dstCropLeft, size_t dstCropTop,
84 size_t dstCropRight, size_t dstCropBottom) {
Andreas Huberf341eb52011-01-06 11:26:54 -080085 if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
86 return ERROR_UNSUPPORTED;
87 }
Andreas Huber53a76bd2009-10-06 16:20:44 -070088
Andreas Huber1bb0ffd2010-11-22 13:06:35 -080089 BitmapParams src(
90 const_cast<void *>(srcBits),
91 srcWidth, srcHeight,
92 srcCropLeft, srcCropTop, srcCropRight, srcCropBottom);
93
94 BitmapParams dst(
95 dstBits,
96 dstWidth, dstHeight,
97 dstCropLeft, dstCropTop, dstCropRight, dstCropBottom);
98
Andreas Huberf341eb52011-01-06 11:26:54 -080099 status_t err;
100
Andreas Huber53a76bd2009-10-06 16:20:44 -0700101 switch (mSrcFormat) {
102 case OMX_COLOR_FormatYUV420Planar:
Andreas Huberf341eb52011-01-06 11:26:54 -0800103 err = convertYUV420Planar(src, dst);
Andreas Huber53a76bd2009-10-06 16:20:44 -0700104 break;
105
106 case OMX_COLOR_FormatCbYCrY:
Andreas Huberf341eb52011-01-06 11:26:54 -0800107 err = convertCbYCrY(src, dst);
Andreas Huber53a76bd2009-10-06 16:20:44 -0700108 break;
109
110 case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
Andreas Huberf341eb52011-01-06 11:26:54 -0800111 err = convertQCOMYUV420SemiPlanar(src, dst);
Andreas Huber53a76bd2009-10-06 16:20:44 -0700112 break;
113
Andreas Huberc543a212010-06-30 10:32:20 -0700114 case OMX_COLOR_FormatYUV420SemiPlanar:
Andreas Huberf341eb52011-01-06 11:26:54 -0800115 err = convertYUV420SemiPlanar(src, dst);
Andreas Huberc543a212010-06-30 10:32:20 -0700116 break;
117
Andreas Huber53a76bd2009-10-06 16:20:44 -0700118 default:
119 {
120 CHECK(!"Should not be here. Unknown color conversion.");
121 break;
122 }
123 }
Andreas Huberf341eb52011-01-06 11:26:54 -0800124
125 return err;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700126}
127
Andreas Huberf341eb52011-01-06 11:26:54 -0800128status_t ColorConverter::convertCbYCrY(
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800129 const BitmapParams &src, const BitmapParams &dst) {
130 // XXX Untested
Andreas Huber53a76bd2009-10-06 16:20:44 -0700131
132 uint8_t *kAdjustedClip = initClip();
133
Andreas Huberf341eb52011-01-06 11:26:54 -0800134 if (!((src.mCropLeft & 1) == 0
135 && src.cropWidth() == dst.cropWidth()
136 && src.cropHeight() == dst.cropHeight())) {
137 return ERROR_UNSUPPORTED;
138 }
Andreas Huber53a76bd2009-10-06 16:20:44 -0700139
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800140 uint32_t *dst_ptr = (uint32_t *)dst.mBits
141 + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700142
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800143 const uint8_t *src_ptr = (const uint8_t *)src.mBits
144 + (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
145
146 for (size_t y = 0; y < src.cropHeight(); ++y) {
147 for (size_t x = 0; x < src.cropWidth(); x += 2) {
148 signed y1 = (signed)src_ptr[2 * x + 1] - 16;
149 signed y2 = (signed)src_ptr[2 * x + 3] - 16;
150 signed u = (signed)src_ptr[2 * x] - 128;
151 signed v = (signed)src_ptr[2 * x + 2] - 128;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700152
153 signed u_b = u * 517;
154 signed u_g = -u * 100;
155 signed v_g = -v * 208;
156 signed v_r = v * 409;
157
158 signed tmp1 = y1 * 298;
159 signed b1 = (tmp1 + u_b) / 256;
160 signed g1 = (tmp1 + v_g + u_g) / 256;
161 signed r1 = (tmp1 + v_r) / 256;
162
163 signed tmp2 = y2 * 298;
164 signed b2 = (tmp2 + u_b) / 256;
165 signed g2 = (tmp2 + v_g + u_g) / 256;
166 signed r2 = (tmp2 + v_r) / 256;
167
168 uint32_t rgb1 =
169 ((kAdjustedClip[r1] >> 3) << 11)
170 | ((kAdjustedClip[g1] >> 2) << 5)
171 | (kAdjustedClip[b1] >> 3);
172
173 uint32_t rgb2 =
174 ((kAdjustedClip[r2] >> 3) << 11)
175 | ((kAdjustedClip[g2] >> 2) << 5)
176 | (kAdjustedClip[b2] >> 3);
177
178 dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
179 }
180
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800181 src_ptr += src.mWidth * 2;
182 dst_ptr += dst.mWidth / 2;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700183 }
Andreas Huberf341eb52011-01-06 11:26:54 -0800184
185 return OK;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700186}
187
Andreas Huberf341eb52011-01-06 11:26:54 -0800188status_t ColorConverter::convertYUV420Planar(
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800189 const BitmapParams &src, const BitmapParams &dst) {
James Dong94738e42011-02-11 13:04:32 -0800190 if (!((dst.mWidth & 1) == 0
Andreas Huberf341eb52011-01-06 11:26:54 -0800191 && (src.mCropLeft & 1) == 0
192 && src.cropWidth() == dst.cropWidth()
193 && src.cropHeight() == dst.cropHeight())) {
194 return ERROR_UNSUPPORTED;
195 }
Andreas Huber53a76bd2009-10-06 16:20:44 -0700196
Andreas Huberf341eb52011-01-06 11:26:54 -0800197 uint8_t *kAdjustedClip = initClip();
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800198
199 uint32_t *dst_ptr = (uint32_t *)dst.mBits
200 + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
201
202 const uint8_t *src_y =
203 (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700204
205 const uint8_t *src_u =
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800206 (const uint8_t *)src_y + src.mWidth * src.mHeight
207 + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700208
209 const uint8_t *src_v =
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800210 src_u + (src.mWidth / 2) * (src.mHeight / 2);
Andreas Huber53a76bd2009-10-06 16:20:44 -0700211
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800212 for (size_t y = 0; y < src.cropHeight(); ++y) {
213 for (size_t x = 0; x < src.cropWidth(); x += 2) {
Andreas Huber53a76bd2009-10-06 16:20:44 -0700214 // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
215 // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
216 // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
217
218 // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
219 // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
220 // R = .................. + 409/256 * (V - 128)
221
222 // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
223 // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
224 // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
225
226 // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
227 // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
228 // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
229
230 // clip range -278 .. 535
231
232 signed y1 = (signed)src_y[x] - 16;
233 signed y2 = (signed)src_y[x + 1] - 16;
234
235 signed u = (signed)src_u[x / 2] - 128;
236 signed v = (signed)src_v[x / 2] - 128;
237
238 signed u_b = u * 517;
239 signed u_g = -u * 100;
240 signed v_g = -v * 208;
241 signed v_r = v * 409;
242
243 signed tmp1 = y1 * 298;
244 signed b1 = (tmp1 + u_b) / 256;
245 signed g1 = (tmp1 + v_g + u_g) / 256;
246 signed r1 = (tmp1 + v_r) / 256;
247
248 signed tmp2 = y2 * 298;
249 signed b2 = (tmp2 + u_b) / 256;
250 signed g2 = (tmp2 + v_g + u_g) / 256;
251 signed r2 = (tmp2 + v_r) / 256;
252
253 uint32_t rgb1 =
254 ((kAdjustedClip[r1] >> 3) << 11)
255 | ((kAdjustedClip[g1] >> 2) << 5)
256 | (kAdjustedClip[b1] >> 3);
257
258 uint32_t rgb2 =
259 ((kAdjustedClip[r2] >> 3) << 11)
260 | ((kAdjustedClip[g2] >> 2) << 5)
261 | (kAdjustedClip[b2] >> 3);
262
263 dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
264 }
265
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800266 src_y += src.mWidth;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700267
268 if (y & 1) {
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800269 src_u += src.mWidth / 2;
270 src_v += src.mWidth / 2;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700271 }
272
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800273 dst_ptr += dst.mWidth / 2;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700274 }
Andreas Huberf341eb52011-01-06 11:26:54 -0800275
276 return OK;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700277}
278
Andreas Huberf341eb52011-01-06 11:26:54 -0800279status_t ColorConverter::convertQCOMYUV420SemiPlanar(
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800280 const BitmapParams &src, const BitmapParams &dst) {
Andreas Huber53a76bd2009-10-06 16:20:44 -0700281 uint8_t *kAdjustedClip = initClip();
282
Andreas Huberf341eb52011-01-06 11:26:54 -0800283 if (!((dst.mWidth & 3) == 0
284 && (src.mCropLeft & 1) == 0
285 && src.cropWidth() == dst.cropWidth()
286 && src.cropHeight() == dst.cropHeight())) {
287 return ERROR_UNSUPPORTED;
288 }
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800289
290 uint32_t *dst_ptr = (uint32_t *)dst.mBits
291 + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
292
293 const uint8_t *src_y =
294 (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700295
296 const uint8_t *src_u =
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800297 (const uint8_t *)src_y + src.mWidth * src.mHeight
298 + src.mCropTop * src.mWidth + src.mCropLeft;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700299
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800300 for (size_t y = 0; y < src.cropHeight(); ++y) {
301 for (size_t x = 0; x < src.cropWidth(); x += 2) {
Andreas Huber53a76bd2009-10-06 16:20:44 -0700302 signed y1 = (signed)src_y[x] - 16;
303 signed y2 = (signed)src_y[x + 1] - 16;
304
305 signed u = (signed)src_u[x & ~1] - 128;
306 signed v = (signed)src_u[(x & ~1) + 1] - 128;
307
308 signed u_b = u * 517;
309 signed u_g = -u * 100;
310 signed v_g = -v * 208;
311 signed v_r = v * 409;
312
313 signed tmp1 = y1 * 298;
314 signed b1 = (tmp1 + u_b) / 256;
315 signed g1 = (tmp1 + v_g + u_g) / 256;
316 signed r1 = (tmp1 + v_r) / 256;
317
318 signed tmp2 = y2 * 298;
319 signed b2 = (tmp2 + u_b) / 256;
320 signed g2 = (tmp2 + v_g + u_g) / 256;
321 signed r2 = (tmp2 + v_r) / 256;
322
323 uint32_t rgb1 =
324 ((kAdjustedClip[b1] >> 3) << 11)
325 | ((kAdjustedClip[g1] >> 2) << 5)
326 | (kAdjustedClip[r1] >> 3);
327
328 uint32_t rgb2 =
329 ((kAdjustedClip[b2] >> 3) << 11)
330 | ((kAdjustedClip[g2] >> 2) << 5)
331 | (kAdjustedClip[r2] >> 3);
332
333 dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
334 }
335
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800336 src_y += src.mWidth;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700337
338 if (y & 1) {
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800339 src_u += src.mWidth;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700340 }
341
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800342 dst_ptr += dst.mWidth / 2;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700343 }
Andreas Huberf341eb52011-01-06 11:26:54 -0800344
345 return OK;
Andreas Huber53a76bd2009-10-06 16:20:44 -0700346}
347
Andreas Huberf341eb52011-01-06 11:26:54 -0800348status_t ColorConverter::convertYUV420SemiPlanar(
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800349 const BitmapParams &src, const BitmapParams &dst) {
350 // XXX Untested
Andreas Huberc543a212010-06-30 10:32:20 -0700351
352 uint8_t *kAdjustedClip = initClip();
353
Andreas Huberf341eb52011-01-06 11:26:54 -0800354 if (!((dst.mWidth & 3) == 0
355 && (src.mCropLeft & 1) == 0
356 && src.cropWidth() == dst.cropWidth()
357 && src.cropHeight() == dst.cropHeight())) {
358 return ERROR_UNSUPPORTED;
359 }
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800360
361 uint32_t *dst_ptr = (uint32_t *)dst.mBits
362 + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
363
364 const uint8_t *src_y =
365 (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
Andreas Huberc543a212010-06-30 10:32:20 -0700366
367 const uint8_t *src_u =
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800368 (const uint8_t *)src_y + src.mWidth * src.mHeight
369 + src.mCropTop * src.mWidth + src.mCropLeft;
Andreas Huberc543a212010-06-30 10:32:20 -0700370
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800371 for (size_t y = 0; y < src.cropHeight(); ++y) {
372 for (size_t x = 0; x < src.cropWidth(); x += 2) {
Andreas Huberc543a212010-06-30 10:32:20 -0700373 signed y1 = (signed)src_y[x] - 16;
374 signed y2 = (signed)src_y[x + 1] - 16;
375
376 signed v = (signed)src_u[x & ~1] - 128;
377 signed u = (signed)src_u[(x & ~1) + 1] - 128;
378
379 signed u_b = u * 517;
380 signed u_g = -u * 100;
381 signed v_g = -v * 208;
382 signed v_r = v * 409;
383
384 signed tmp1 = y1 * 298;
385 signed b1 = (tmp1 + u_b) / 256;
386 signed g1 = (tmp1 + v_g + u_g) / 256;
387 signed r1 = (tmp1 + v_r) / 256;
388
389 signed tmp2 = y2 * 298;
390 signed b2 = (tmp2 + u_b) / 256;
391 signed g2 = (tmp2 + v_g + u_g) / 256;
392 signed r2 = (tmp2 + v_r) / 256;
393
394 uint32_t rgb1 =
395 ((kAdjustedClip[b1] >> 3) << 11)
396 | ((kAdjustedClip[g1] >> 2) << 5)
397 | (kAdjustedClip[r1] >> 3);
398
399 uint32_t rgb2 =
400 ((kAdjustedClip[b2] >> 3) << 11)
401 | ((kAdjustedClip[g2] >> 2) << 5)
402 | (kAdjustedClip[r2] >> 3);
403
404 dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
405 }
406
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800407 src_y += src.mWidth;
Andreas Huberc543a212010-06-30 10:32:20 -0700408
409 if (y & 1) {
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800410 src_u += src.mWidth;
Andreas Huberc543a212010-06-30 10:32:20 -0700411 }
412
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800413 dst_ptr += dst.mWidth / 2;
Andreas Huberc543a212010-06-30 10:32:20 -0700414 }
Andreas Huberf341eb52011-01-06 11:26:54 -0800415
416 return OK;
Andreas Huberc543a212010-06-30 10:32:20 -0700417}
418
Andreas Huber53a76bd2009-10-06 16:20:44 -0700419uint8_t *ColorConverter::initClip() {
420 static const signed kClipMin = -278;
421 static const signed kClipMax = 535;
422
423 if (mClip == NULL) {
424 mClip = new uint8_t[kClipMax - kClipMin + 1];
425
426 for (signed i = kClipMin; i <= kClipMax; ++i) {
427 mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
428 }
429 }
430
431 return &mClip[-kClipMin];
432}
433
434} // namespace android