1/* 2 * Misc image conversion routines 3 * Copyright (c) 2001, 2002, 2003 Fabrice Bellard 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * misc image conversion routines 25 */ 26 27/* TODO: 28 * - write 'ffimg' program to test all the image related stuff 29 * - move all api to slice based system 30 * - integrate deinterlacing, postprocessing and scaling in the conversion process 31 */ 32 33#include "avcodec.h" 34#include "imgconvert.h" 35#include "internal.h" 36#include "mathops.h" 37#include "libavutil/avassert.h" 38#include "libavutil/colorspace.h" 39#include "libavutil/common.h" 40#include "libavutil/pixdesc.h" 41#include "libavutil/imgutils.h" 42 43#if HAVE_MMX_EXTERNAL 44#define deinterlace_line_inplace ff_deinterlace_line_inplace_mmx 45#define deinterlace_line ff_deinterlace_line_mmx 46#else 47#define deinterlace_line_inplace deinterlace_line_inplace_c 48#define deinterlace_line deinterlace_line_c 49#endif 50 51void avcodec_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, int *h_shift, int *v_shift) 52{ 53 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); 54 av_assert0(desc); 55 *h_shift = desc->log2_chroma_w; 56 *v_shift = desc->log2_chroma_h; 57} 58 59int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, 60 enum AVPixelFormat src_pix_fmt, 61 int has_alpha) 62{ 63 return av_get_pix_fmt_loss(dst_pix_fmt, src_pix_fmt, has_alpha); 64} 65 66enum AVPixelFormat avcodec_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, 67 enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr) 68{ 69 return av_find_best_pix_fmt_of_2(dst_pix_fmt1, dst_pix_fmt2, src_pix_fmt, has_alpha, loss_ptr); 70} 71 72#if AV_HAVE_INCOMPATIBLE_LIBAV_ABI 73enum AVPixelFormat avcodec_find_best_pix_fmt2(const enum AVPixelFormat *pix_fmt_list, 74 enum AVPixelFormat src_pix_fmt, 75 int has_alpha, int *loss_ptr){ 76 return avcodec_find_best_pix_fmt_of_list(pix_fmt_list, src_pix_fmt, has_alpha, loss_ptr); 77} 78#else 79enum AVPixelFormat avcodec_find_best_pix_fmt2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, 80 enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr) 81{ 82 return avcodec_find_best_pix_fmt_of_2(dst_pix_fmt1, dst_pix_fmt2, src_pix_fmt, has_alpha, loss_ptr); 83} 84#endif 85 86enum AVPixelFormat avcodec_find_best_pix_fmt_of_list(const enum AVPixelFormat *pix_fmt_list, 87 enum AVPixelFormat src_pix_fmt, 88 int has_alpha, int *loss_ptr){ 89 int i; 90 91 enum AVPixelFormat best = AV_PIX_FMT_NONE; 92 93 for(i=0; pix_fmt_list[i] != AV_PIX_FMT_NONE; i++) 94 best = avcodec_find_best_pix_fmt_of_2(best, pix_fmt_list[i], src_pix_fmt, has_alpha, loss_ptr); 95 96 return best; 97} 98 99/* 2x2 -> 1x1 */ 100void ff_shrink22(uint8_t *dst, int dst_wrap, 101 const uint8_t *src, int src_wrap, 102 int width, int height) 103{ 104 int w; 105 const uint8_t *s1, *s2; 106 uint8_t *d; 107 108 for(;height > 0; height--) { 109 s1 = src; 110 s2 = s1 + src_wrap; 111 d = dst; 112 for(w = width;w >= 4; w-=4) { 113 d[0] = (s1[0] + s1[1] + s2[0] + s2[1] + 2) >> 2; 114 d[1] = (s1[2] + s1[3] + s2[2] + s2[3] + 2) >> 2; 115 d[2] = (s1[4] + s1[5] + s2[4] + s2[5] + 2) >> 2; 116 d[3] = (s1[6] + s1[7] + s2[6] + s2[7] + 2) >> 2; 117 s1 += 8; 118 s2 += 8; 119 d += 4; 120 } 121 for(;w > 0; w--) { 122 d[0] = (s1[0] + s1[1] + s2[0] + s2[1] + 2) >> 2; 123 s1 += 2; 124 s2 += 2; 125 d++; 126 } 127 src += 2 * src_wrap; 128 dst += dst_wrap; 129 } 130} 131 132/* 4x4 -> 1x1 */ 133void ff_shrink44(uint8_t *dst, int dst_wrap, 134 const uint8_t *src, int src_wrap, 135 int width, int height) 136{ 137 int w; 138 const uint8_t *s1, *s2, *s3, *s4; 139 uint8_t *d; 140 141 for(;height > 0; height--) { 142 s1 = src; 143 s2 = s1 + src_wrap; 144 s3 = s2 + src_wrap; 145 s4 = s3 + src_wrap; 146 d = dst; 147 for(w = width;w > 0; w--) { 148 d[0] = (s1[0] + s1[1] + s1[2] + s1[3] + 149 s2[0] + s2[1] + s2[2] + s2[3] + 150 s3[0] + s3[1] + s3[2] + s3[3] + 151 s4[0] + s4[1] + s4[2] + s4[3] + 8) >> 4; 152 s1 += 4; 153 s2 += 4; 154 s3 += 4; 155 s4 += 4; 156 d++; 157 } 158 src += 4 * src_wrap; 159 dst += dst_wrap; 160 } 161} 162 163/* 8x8 -> 1x1 */ 164void ff_shrink88(uint8_t *dst, int dst_wrap, 165 const uint8_t *src, int src_wrap, 166 int width, int height) 167{ 168 int w, i; 169 170 for(;height > 0; height--) { 171 for(w = width;w > 0; w--) { 172 int tmp=0; 173 for(i=0; i<8; i++){ 174 tmp += src[0] + src[1] + src[2] + src[3] + src[4] + src[5] + src[6] + src[7]; 175 src += src_wrap; 176 } 177 *(dst++) = (tmp + 32)>>6; 178 src += 8 - 8*src_wrap; 179 } 180 src += 8*src_wrap - 8*width; 181 dst += dst_wrap - width; 182 } 183} 184 185/* return true if yuv planar */ 186static inline int is_yuv_planar(const AVPixFmtDescriptor *desc) 187{ 188 int i; 189 int planes[4] = { 0 }; 190 191 if ( desc->flags & AV_PIX_FMT_FLAG_RGB 192 || !(desc->flags & AV_PIX_FMT_FLAG_PLANAR)) 193 return 0; 194 195 /* set the used planes */ 196 for (i = 0; i < desc->nb_components; i++) 197 planes[desc->comp[i].plane] = 1; 198 199 /* if there is an unused plane, the format is not planar */ 200 for (i = 0; i < desc->nb_components; i++) 201 if (!planes[i]) 202 return 0; 203 return 1; 204} 205 206int av_picture_crop(AVPicture *dst, const AVPicture *src, 207 enum AVPixelFormat pix_fmt, int top_band, int left_band) 208{ 209 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); 210 int y_shift; 211 int x_shift; 212 213 if (pix_fmt < 0 || pix_fmt >= AV_PIX_FMT_NB) 214 return -1; 215 216 y_shift = desc->log2_chroma_h; 217 x_shift = desc->log2_chroma_w; 218 219 if (is_yuv_planar(desc)) { 220 dst->data[0] = src->data[0] + (top_band * src->linesize[0]) + left_band; 221 dst->data[1] = src->data[1] + ((top_band >> y_shift) * src->linesize[1]) + (left_band >> x_shift); 222 dst->data[2] = src->data[2] + ((top_band >> y_shift) * src->linesize[2]) + (left_band >> x_shift); 223 } else{ 224 if(top_band % (1<<y_shift) || left_band % (1<<x_shift)) 225 return -1; 226 if(left_band) //FIXME add support for this too 227 return -1; 228 dst->data[0] = src->data[0] + (top_band * src->linesize[0]) + left_band; 229 } 230 231 dst->linesize[0] = src->linesize[0]; 232 dst->linesize[1] = src->linesize[1]; 233 dst->linesize[2] = src->linesize[2]; 234 return 0; 235} 236 237int av_picture_pad(AVPicture *dst, const AVPicture *src, int height, int width, 238 enum AVPixelFormat pix_fmt, int padtop, int padbottom, int padleft, int padright, 239 int *color) 240{ 241 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); 242 uint8_t *optr; 243 int y_shift; 244 int x_shift; 245 int yheight; 246 int i, y; 247 248 if (pix_fmt < 0 || pix_fmt >= AV_PIX_FMT_NB || 249 !is_yuv_planar(desc)) return -1; 250 251 for (i = 0; i < 3; i++) { 252 x_shift = i ? desc->log2_chroma_w : 0; 253 y_shift = i ? desc->log2_chroma_h : 0; 254 255 if (padtop || padleft) { 256 memset(dst->data[i], color[i], 257 dst->linesize[i] * (padtop >> y_shift) + (padleft >> x_shift)); 258 } 259 260 if (padleft || padright) { 261 optr = dst->data[i] + dst->linesize[i] * (padtop >> y_shift) + 262 (dst->linesize[i] - (padright >> x_shift)); 263 yheight = (height - 1 - (padtop + padbottom)) >> y_shift; 264 for (y = 0; y < yheight; y++) { 265 memset(optr, color[i], (padleft + padright) >> x_shift); 266 optr += dst->linesize[i]; 267 } 268 } 269 270 if (src) { /* first line */ 271 uint8_t *iptr = src->data[i]; 272 optr = dst->data[i] + dst->linesize[i] * (padtop >> y_shift) + 273 (padleft >> x_shift); 274 memcpy(optr, iptr, (width - padleft - padright) >> x_shift); 275 iptr += src->linesize[i]; 276 optr = dst->data[i] + dst->linesize[i] * (padtop >> y_shift) + 277 (dst->linesize[i] - (padright >> x_shift)); 278 yheight = (height - 1 - (padtop + padbottom)) >> y_shift; 279 for (y = 0; y < yheight; y++) { 280 memset(optr, color[i], (padleft + padright) >> x_shift); 281 memcpy(optr + ((padleft + padright) >> x_shift), iptr, 282 (width - padleft - padright) >> x_shift); 283 iptr += src->linesize[i]; 284 optr += dst->linesize[i]; 285 } 286 } 287 288 if (padbottom || padright) { 289 optr = dst->data[i] + dst->linesize[i] * 290 ((height - padbottom) >> y_shift) - (padright >> x_shift); 291 memset(optr, color[i],dst->linesize[i] * 292 (padbottom >> y_shift) + (padright >> x_shift)); 293 } 294 } 295 return 0; 296} 297 298#if FF_API_DEINTERLACE 299 300#if !HAVE_MMX_EXTERNAL 301/* filter parameters: [-1 4 2 4 -1] // 8 */ 302static void deinterlace_line_c(uint8_t *dst, 303 const uint8_t *lum_m4, const uint8_t *lum_m3, 304 const uint8_t *lum_m2, const uint8_t *lum_m1, 305 const uint8_t *lum, 306 int size) 307{ 308 const uint8_t *cm = ff_crop_tab + MAX_NEG_CROP; 309 int sum; 310 311 for(;size > 0;size--) { 312 sum = -lum_m4[0]; 313 sum += lum_m3[0] << 2; 314 sum += lum_m2[0] << 1; 315 sum += lum_m1[0] << 2; 316 sum += -lum[0]; 317 dst[0] = cm[(sum + 4) >> 3]; 318 lum_m4++; 319 lum_m3++; 320 lum_m2++; 321 lum_m1++; 322 lum++; 323 dst++; 324 } 325} 326 327static void deinterlace_line_inplace_c(uint8_t *lum_m4, uint8_t *lum_m3, 328 uint8_t *lum_m2, uint8_t *lum_m1, 329 uint8_t *lum, int size) 330{ 331 const uint8_t *cm = ff_crop_tab + MAX_NEG_CROP; 332 int sum; 333 334 for(;size > 0;size--) { 335 sum = -lum_m4[0]; 336 sum += lum_m3[0] << 2; 337 sum += lum_m2[0] << 1; 338 lum_m4[0]=lum_m2[0]; 339 sum += lum_m1[0] << 2; 340 sum += -lum[0]; 341 lum_m2[0] = cm[(sum + 4) >> 3]; 342 lum_m4++; 343 lum_m3++; 344 lum_m2++; 345 lum_m1++; 346 lum++; 347 } 348} 349#endif /* !HAVE_MMX_EXTERNAL */ 350 351/* deinterlacing : 2 temporal taps, 3 spatial taps linear filter. The 352 top field is copied as is, but the bottom field is deinterlaced 353 against the top field. */ 354static void deinterlace_bottom_field(uint8_t *dst, int dst_wrap, 355 const uint8_t *src1, int src_wrap, 356 int width, int height) 357{ 358 const uint8_t *src_m2, *src_m1, *src_0, *src_p1, *src_p2; 359 int y; 360 361 src_m2 = src1; 362 src_m1 = src1; 363 src_0=&src_m1[src_wrap]; 364 src_p1=&src_0[src_wrap]; 365 src_p2=&src_p1[src_wrap]; 366 for(y=0;y<(height-2);y+=2) { 367 memcpy(dst,src_m1,width); 368 dst += dst_wrap; 369 deinterlace_line(dst,src_m2,src_m1,src_0,src_p1,src_p2,width); 370 src_m2 = src_0; 371 src_m1 = src_p1; 372 src_0 = src_p2; 373 src_p1 += 2*src_wrap; 374 src_p2 += 2*src_wrap; 375 dst += dst_wrap; 376 } 377 memcpy(dst,src_m1,width); 378 dst += dst_wrap; 379 /* do last line */ 380 deinterlace_line(dst,src_m2,src_m1,src_0,src_0,src_0,width); 381} 382 383static void deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap, 384 int width, int height) 385{ 386 uint8_t *src_m1, *src_0, *src_p1, *src_p2; 387 int y; 388 uint8_t *buf; 389 buf = av_malloc(width); 390 391 src_m1 = src1; 392 memcpy(buf,src_m1,width); 393 src_0=&src_m1[src_wrap]; 394 src_p1=&src_0[src_wrap]; 395 src_p2=&src_p1[src_wrap]; 396 for(y=0;y<(height-2);y+=2) { 397 deinterlace_line_inplace(buf,src_m1,src_0,src_p1,src_p2,width); 398 src_m1 = src_p1; 399 src_0 = src_p2; 400 src_p1 += 2*src_wrap; 401 src_p2 += 2*src_wrap; 402 } 403 /* do last line */ 404 deinterlace_line_inplace(buf,src_m1,src_0,src_0,src_0,width); 405 av_free(buf); 406} 407 408int avpicture_deinterlace(AVPicture *dst, const AVPicture *src, 409 enum AVPixelFormat pix_fmt, int width, int height) 410{ 411 int i; 412 413 if (pix_fmt != AV_PIX_FMT_YUV420P && 414 pix_fmt != AV_PIX_FMT_YUVJ420P && 415 pix_fmt != AV_PIX_FMT_YUV422P && 416 pix_fmt != AV_PIX_FMT_YUVJ422P && 417 pix_fmt != AV_PIX_FMT_YUV444P && 418 pix_fmt != AV_PIX_FMT_YUV411P && 419 pix_fmt != AV_PIX_FMT_GRAY8) 420 return -1; 421 if ((width & 3) != 0 || (height & 3) != 0) 422 return -1; 423 424 for(i=0;i<3;i++) { 425 if (i == 1) { 426 switch(pix_fmt) { 427 case AV_PIX_FMT_YUVJ420P: 428 case AV_PIX_FMT_YUV420P: 429 width >>= 1; 430 height >>= 1; 431 break; 432 case AV_PIX_FMT_YUV422P: 433 case AV_PIX_FMT_YUVJ422P: 434 width >>= 1; 435 break; 436 case AV_PIX_FMT_YUV411P: 437 width >>= 2; 438 break; 439 default: 440 break; 441 } 442 if (pix_fmt == AV_PIX_FMT_GRAY8) { 443 break; 444 } 445 } 446 if (src == dst) { 447 deinterlace_bottom_field_inplace(dst->data[i], dst->linesize[i], 448 width, height); 449 } else { 450 deinterlace_bottom_field(dst->data[i],dst->linesize[i], 451 src->data[i], src->linesize[i], 452 width, height); 453 } 454 } 455 emms_c(); 456 return 0; 457} 458 459#endif /* FF_API_DEINTERLACE */ 460 461#ifdef TEST 462 463int main(void){ 464 int i; 465 int err=0; 466 int skip = 0; 467 468 for (i=0; i<AV_PIX_FMT_NB*2; i++) { 469 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i); 470 if(!desc || !desc->name) { 471 skip ++; 472 continue; 473 } 474 if (skip) { 475 av_log(NULL, AV_LOG_INFO, "%3d unused pixel format values\n", skip); 476 skip = 0; 477 } 478 av_log(NULL, AV_LOG_INFO, "pix fmt %s yuv_plan:%d avg_bpp:%d\n", desc->name, is_yuv_planar(desc), av_get_padded_bits_per_pixel(desc)); 479 if ((!(desc->flags & AV_PIX_FMT_FLAG_ALPHA)) != (desc->nb_components != 2 && desc->nb_components != 4)) { 480 av_log(NULL, AV_LOG_ERROR, "Alpha flag mismatch\n"); 481 err = 1; 482 } 483 } 484 return err; 485} 486 487#endif 488