2 * Copyright 2011 Stefano Sabatini <stefano.sabatini-lala poste it>
3 * Copyright 2012 Nicolas George <nicolas.george normalesup org>
5 * This file is part of FFmpeg.
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.
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.
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
24 #include "libavutil/avutil.h"
25 #include "libavutil/colorspace.h"
26 #include "libavutil/mem.h"
27 #include "libavutil/pixdesc.h"
28 #include "drawutils.h"
31 enum { RED
= 0, GREEN
, BLUE
, ALPHA
};
33 int ff_fill_rgba_map(uint8_t *rgba_map
, enum AVPixelFormat pix_fmt
)
37 case AV_PIX_FMT_ARGB
: rgba_map
[ALPHA
] = 0; rgba_map
[RED
] = 1; rgba_map
[GREEN
] = 2; rgba_map
[BLUE
] = 3; break;
39 case AV_PIX_FMT_ABGR
: rgba_map
[ALPHA
] = 0; rgba_map
[BLUE
] = 1; rgba_map
[GREEN
] = 2; rgba_map
[RED
] = 3; break;
40 case AV_PIX_FMT_RGB48LE
:
41 case AV_PIX_FMT_RGB48BE
:
42 case AV_PIX_FMT_RGBA64BE
:
43 case AV_PIX_FMT_RGBA64LE
:
46 case AV_PIX_FMT_RGB24
: rgba_map
[RED
] = 0; rgba_map
[GREEN
] = 1; rgba_map
[BLUE
] = 2; rgba_map
[ALPHA
] = 3; break;
47 case AV_PIX_FMT_BGR48LE
:
48 case AV_PIX_FMT_BGR48BE
:
49 case AV_PIX_FMT_BGRA64BE
:
50 case AV_PIX_FMT_BGRA64LE
:
53 case AV_PIX_FMT_BGR24
: rgba_map
[BLUE
] = 0; rgba_map
[GREEN
] = 1; rgba_map
[RED
] = 2; rgba_map
[ALPHA
] = 3; break;
54 case AV_PIX_FMT_GBRAP
:
55 case AV_PIX_FMT_GBRP
: rgba_map
[GREEN
] = 0; rgba_map
[BLUE
] = 1; rgba_map
[RED
] = 2; rgba_map
[ALPHA
] = 3; break;
56 default: /* unsupported */
57 return AVERROR(EINVAL
);
62 int ff_fill_line_with_color(uint8_t *line
[4], int pixel_step
[4], int w
, uint8_t dst_color
[4],
63 enum AVPixelFormat pix_fmt
, uint8_t rgba_color
[4],
64 int *is_packed_rgba
, uint8_t rgba_map_ptr
[4])
66 uint8_t rgba_map
[4] = {0};
68 const AVPixFmtDescriptor
*pix_desc
= av_pix_fmt_desc_get(pix_fmt
);
69 int hsub
= pix_desc
->log2_chroma_w
;
71 *is_packed_rgba
= ff_fill_rgba_map(rgba_map
, pix_fmt
) >= 0;
73 if (*is_packed_rgba
) {
74 pixel_step
[0] = (av_get_bits_per_pixel(pix_desc
))>>3;
75 for (i
= 0; i
< 4; i
++)
76 dst_color
[rgba_map
[i
]] = rgba_color
[i
];
78 line
[0] = av_malloc(w
* pixel_step
[0]);
79 for (i
= 0; i
< w
; i
++)
80 memcpy(line
[0] + i
* pixel_step
[0], dst_color
, pixel_step
[0]);
82 memcpy(rgba_map_ptr
, rgba_map
, sizeof(rgba_map
[0]) * 4);
86 dst_color
[0] = RGB_TO_Y_CCIR(rgba_color
[0], rgba_color
[1], rgba_color
[2]);
87 dst_color
[1] = RGB_TO_U_CCIR(rgba_color
[0], rgba_color
[1], rgba_color
[2], 0);
88 dst_color
[2] = RGB_TO_V_CCIR(rgba_color
[0], rgba_color
[1], rgba_color
[2], 0);
89 dst_color
[3] = rgba_color
[3];
91 for (plane
= 0; plane
< 4; plane
++) {
93 int hsub1
= (plane
== 1 || plane
== 2) ? hsub
: 0;
95 pixel_step
[plane
] = 1;
96 line_size
= FF_CEIL_RSHIFT(w
, hsub1
) * pixel_step
[plane
];
97 line
[plane
] = av_malloc(line_size
);
98 memset(line
[plane
], dst_color
[plane
], line_size
);
105 void ff_draw_rectangle(uint8_t *dst
[4], int dst_linesize
[4],
106 uint8_t *src
[4], int pixelstep
[4],
107 int hsub
, int vsub
, int x
, int y
, int w
, int h
)
112 for (plane
= 0; plane
< 4 && dst
[plane
]; plane
++) {
113 int hsub1
= plane
== 1 || plane
== 2 ? hsub
: 0;
114 int vsub1
= plane
== 1 || plane
== 2 ? vsub
: 0;
115 int width
= FF_CEIL_RSHIFT(w
, hsub1
);
116 int height
= FF_CEIL_RSHIFT(h
, vsub1
);
118 p
= dst
[plane
] + (y
>> vsub1
) * dst_linesize
[plane
];
119 for (i
= 0; i
< height
; i
++) {
120 memcpy(p
+ (x
>> hsub1
) * pixelstep
[plane
],
121 src
[plane
], width
* pixelstep
[plane
]);
122 p
+= dst_linesize
[plane
];
127 void ff_copy_rectangle(uint8_t *dst
[4], int dst_linesize
[4],
128 uint8_t *src
[4], int src_linesize
[4], int pixelstep
[4],
129 int hsub
, int vsub
, int x
, int y
, int y2
, int w
, int h
)
134 for (plane
= 0; plane
< 4 && dst
[plane
]; plane
++) {
135 int hsub1
= plane
== 1 || plane
== 2 ? hsub
: 0;
136 int vsub1
= plane
== 1 || plane
== 2 ? vsub
: 0;
137 int width
= FF_CEIL_RSHIFT(w
, hsub1
);
138 int height
= FF_CEIL_RSHIFT(h
, vsub1
);
140 p
= dst
[plane
] + (y
>> vsub1
) * dst_linesize
[plane
];
141 for (i
= 0; i
< height
; i
++) {
142 memcpy(p
+ (x
>> hsub1
) * pixelstep
[plane
],
143 src
[plane
] + src_linesize
[plane
]*(i
+(y2
>>vsub1
)), width
* pixelstep
[plane
]);
144 p
+= dst_linesize
[plane
];
149 int ff_draw_init(FFDrawContext
*draw
, enum AVPixelFormat format
, unsigned flags
)
151 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(format
);
152 const AVComponentDescriptor
*c
;
153 unsigned i
, nb_planes
= 0;
154 int pixelstep
[MAX_PLANES
] = { 0 };
157 return AVERROR(EINVAL
);
158 if (desc
->flags
& ~(AV_PIX_FMT_FLAG_PLANAR
| AV_PIX_FMT_FLAG_RGB
| AV_PIX_FMT_FLAG_PSEUDOPAL
| AV_PIX_FMT_FLAG_ALPHA
))
159 return AVERROR(ENOSYS
);
160 for (i
= 0; i
< desc
->nb_components
; i
++) {
162 /* for now, only 8-bits formats */
163 if (c
->depth_minus1
!= 8 - 1)
164 return AVERROR(ENOSYS
);
165 if (c
->plane
>= MAX_PLANES
)
166 return AVERROR(ENOSYS
);
167 /* strange interleaving */
168 if (pixelstep
[c
->plane
] != 0 &&
169 pixelstep
[c
->plane
] != c
->step_minus1
+ 1)
170 return AVERROR(ENOSYS
);
171 pixelstep
[c
->plane
] = c
->step_minus1
+ 1;
172 if (pixelstep
[c
->plane
] >= 8)
173 return AVERROR(ENOSYS
);
174 nb_planes
= FFMAX(nb_planes
, c
->plane
+ 1);
176 if ((desc
->log2_chroma_w
|| desc
->log2_chroma_h
) && nb_planes
< 3)
177 return AVERROR(ENOSYS
); /* exclude NV12 and NV21 */
178 memset(draw
, 0, sizeof(*draw
));
180 draw
->format
= format
;
181 draw
->nb_planes
= nb_planes
;
182 memcpy(draw
->pixelstep
, pixelstep
, sizeof(draw
->pixelstep
));
183 draw
->hsub
[1] = draw
->hsub
[2] = draw
->hsub_max
= desc
->log2_chroma_w
;
184 draw
->vsub
[1] = draw
->vsub
[2] = draw
->vsub_max
= desc
->log2_chroma_h
;
185 for (i
= 0; i
< ((desc
->nb_components
- 1) | 1); i
++)
186 draw
->comp_mask
[desc
->comp
[i
].plane
] |=
187 1 << (desc
->comp
[i
].offset_plus1
- 1);
191 void ff_draw_color(FFDrawContext
*draw
, FFDrawColor
*color
, const uint8_t rgba
[4])
196 if (rgba
!= color
->rgba
)
197 memcpy(color
->rgba
, rgba
, sizeof(color
->rgba
));
198 if ((draw
->desc
->flags
& AV_PIX_FMT_FLAG_RGB
) &&
199 ff_fill_rgba_map(rgba_map
, draw
->format
) >= 0) {
200 if (draw
->nb_planes
== 1) {
201 for (i
= 0; i
< 4; i
++)
202 color
->comp
[0].u8
[rgba_map
[i
]] = rgba
[i
];
204 for (i
= 0; i
< 4; i
++)
205 color
->comp
[rgba_map
[i
]].u8
[0] = rgba
[i
];
207 } else if (draw
->nb_planes
== 3 || draw
->nb_planes
== 4) {
209 color
->comp
[0].u8
[0] = RGB_TO_Y_CCIR(rgba
[0], rgba
[1], rgba
[2]);
210 color
->comp
[1].u8
[0] = RGB_TO_U_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
211 color
->comp
[2].u8
[0] = RGB_TO_V_CCIR(rgba
[0], rgba
[1], rgba
[2], 0);
212 color
->comp
[3].u8
[0] = rgba
[3];
213 } else if (draw
->format
== AV_PIX_FMT_GRAY8
|| draw
->format
== AV_PIX_FMT_GRAY8A
) {
214 color
->comp
[0].u8
[0] = RGB_TO_Y_CCIR(rgba
[0], rgba
[1], rgba
[2]);
215 color
->comp
[1].u8
[0] = rgba
[3];
217 av_log(NULL
, AV_LOG_WARNING
,
218 "Color conversion not implemented for %s\n", draw
->desc
->name
);
219 memset(color
, 128, sizeof(*color
));
223 static uint8_t *pointer_at(FFDrawContext
*draw
, uint8_t *data
[], int linesize
[],
224 int plane
, int x
, int y
)
227 (y
>> draw
->vsub
[plane
]) * linesize
[plane
] +
228 (x
>> draw
->hsub
[plane
]) * draw
->pixelstep
[plane
];
231 void ff_copy_rectangle2(FFDrawContext
*draw
,
232 uint8_t *dst
[], int dst_linesize
[],
233 uint8_t *src
[], int src_linesize
[],
234 int dst_x
, int dst_y
, int src_x
, int src_y
,
237 int plane
, y
, wp
, hp
;
240 for (plane
= 0; plane
< draw
->nb_planes
; plane
++) {
241 p
= pointer_at(draw
, src
, src_linesize
, plane
, src_x
, src_y
);
242 q
= pointer_at(draw
, dst
, dst_linesize
, plane
, dst_x
, dst_y
);
243 wp
= FF_CEIL_RSHIFT(w
, draw
->hsub
[plane
]) * draw
->pixelstep
[plane
];
244 hp
= FF_CEIL_RSHIFT(h
, draw
->vsub
[plane
]);
245 for (y
= 0; y
< hp
; y
++) {
247 p
+= src_linesize
[plane
];
248 q
+= dst_linesize
[plane
];
253 void ff_fill_rectangle(FFDrawContext
*draw
, FFDrawColor
*color
,
254 uint8_t *dst
[], int dst_linesize
[],
255 int dst_x
, int dst_y
, int w
, int h
)
257 int plane
, x
, y
, wp
, hp
;
260 for (plane
= 0; plane
< draw
->nb_planes
; plane
++) {
261 p0
= pointer_at(draw
, dst
, dst_linesize
, plane
, dst_x
, dst_y
);
262 wp
= FF_CEIL_RSHIFT(w
, draw
->hsub
[plane
]);
263 hp
= FF_CEIL_RSHIFT(h
, draw
->vsub
[plane
]);
267 /* copy first line from color */
268 for (x
= 0; x
< wp
; x
++) {
269 memcpy(p
, color
->comp
[plane
].u8
, draw
->pixelstep
[plane
]);
270 p
+= draw
->pixelstep
[plane
];
272 wp
*= draw
->pixelstep
[plane
];
273 /* copy next lines from first line */
274 p
= p0
+ dst_linesize
[plane
];
275 for (y
= 1; y
< hp
; y
++) {
277 p
+= dst_linesize
[plane
];
283 * Clip interval [x; x+w[ within [0; wmax[.
284 * The resulting w may be negative if the final interval is empty.
285 * dx, if not null, return the difference between in and out value of x.
287 static void clip_interval(int wmax
, int *x
, int *w
, int *dx
)
302 * Decompose w pixels starting at x
303 * into start + (w starting at x) + end
304 * with x and w aligned on multiples of 1<<sub.
306 static void subsampling_bounds(int sub
, int *x
, int *w
, int *start
, int *end
)
308 int mask
= (1 << sub
) - 1;
310 *start
= (-*x
) & mask
;
312 *start
= FFMIN(*start
, *w
);
318 static int component_used(FFDrawContext
*draw
, int plane
, int comp
)
320 return (draw
->comp_mask
[plane
] >> comp
) & 1;
323 /* If alpha is in the [ 0 ; 0x1010101 ] range,
324 then alpha * value is in the [ 0 ; 0xFFFFFFFF ] range,
325 and >> 24 gives a correct rounding. */
326 static void blend_line(uint8_t *dst
, unsigned src
, unsigned alpha
,
327 int dx
, int w
, unsigned hsub
, int left
, int right
)
329 unsigned asrc
= alpha
* src
;
330 unsigned tau
= 0x1010101 - alpha
;
334 unsigned suba
= (left
* alpha
) >> hsub
;
335 *dst
= (*dst
* (0x1010101 - suba
) + src
* suba
) >> 24;
338 for (x
= 0; x
< w
; x
++) {
339 *dst
= (*dst
* tau
+ asrc
) >> 24;
343 unsigned suba
= (right
* alpha
) >> hsub
;
344 *dst
= (*dst
* (0x1010101 - suba
) + src
* suba
) >> 24;
348 void ff_blend_rectangle(FFDrawContext
*draw
, FFDrawColor
*color
,
349 uint8_t *dst
[], int dst_linesize
[],
350 int dst_w
, int dst_h
,
351 int x0
, int y0
, int w
, int h
)
353 unsigned alpha
, nb_planes
, nb_comp
, plane
, comp
;
354 int w_sub
, h_sub
, x_sub
, y_sub
, left
, right
, top
, bottom
, y
;
357 /* TODO optimize if alpha = 0xFF */
358 clip_interval(dst_w
, &x0
, &w
, NULL
);
359 clip_interval(dst_h
, &y0
, &h
, NULL
);
360 if (w
<= 0 || h
<= 0 || !color
->rgba
[3])
362 /* 0x10203 * alpha + 2 is in the [ 2 ; 0x1010101 - 2 ] range */
363 alpha
= 0x10203 * color
->rgba
[3] + 0x2;
364 nb_planes
= (draw
->nb_planes
- 1) | 1; /* eliminate alpha */
365 for (plane
= 0; plane
< nb_planes
; plane
++) {
366 nb_comp
= draw
->pixelstep
[plane
];
367 p0
= pointer_at(draw
, dst
, dst_linesize
, plane
, x0
, y0
);
372 subsampling_bounds(draw
->hsub
[plane
], &x_sub
, &w_sub
, &left
, &right
);
373 subsampling_bounds(draw
->vsub
[plane
], &y_sub
, &h_sub
, &top
, &bottom
);
374 for (comp
= 0; comp
< nb_comp
; comp
++) {
375 if (!component_used(draw
, plane
, comp
))
379 blend_line(p
, color
->comp
[plane
].u8
[comp
], alpha
>> 1,
380 draw
->pixelstep
[plane
], w_sub
,
381 draw
->hsub
[plane
], left
, right
);
382 p
+= dst_linesize
[plane
];
384 for (y
= 0; y
< h_sub
; y
++) {
385 blend_line(p
, color
->comp
[plane
].u8
[comp
], alpha
,
386 draw
->pixelstep
[plane
], w_sub
,
387 draw
->hsub
[plane
], left
, right
);
388 p
+= dst_linesize
[plane
];
391 blend_line(p
, color
->comp
[plane
].u8
[comp
], alpha
>> 1,
392 draw
->pixelstep
[plane
], w_sub
,
393 draw
->hsub
[plane
], left
, right
);
398 static void blend_pixel(uint8_t *dst
, unsigned src
, unsigned alpha
,
399 uint8_t *mask
, int mask_linesize
, int l2depth
,
400 unsigned w
, unsigned h
, unsigned shift
, unsigned xm0
)
402 unsigned xm
, x
, y
, t
= 0;
403 unsigned xmshf
= 3 - l2depth
;
404 unsigned xmmod
= 7 >> l2depth
;
405 unsigned mbits
= (1 << (1 << l2depth
)) - 1;
406 unsigned mmult
= 255 / mbits
;
408 for (y
= 0; y
< h
; y
++) {
410 for (x
= 0; x
< w
; x
++) {
411 t
+= ((mask
[xm
>> xmshf
] >> ((~xm
& xmmod
) << l2depth
)) & mbits
)
415 mask
+= mask_linesize
;
417 alpha
= (t
>> shift
) * alpha
;
418 *dst
= ((0x1010101 - alpha
) * *dst
+ alpha
* src
) >> 24;
421 static void blend_line_hv(uint8_t *dst
, int dst_delta
,
422 unsigned src
, unsigned alpha
,
423 uint8_t *mask
, int mask_linesize
, int l2depth
, int w
,
424 unsigned hsub
, unsigned vsub
,
425 int xm
, int left
, int right
, int hband
)
430 blend_pixel(dst
, src
, alpha
, mask
, mask_linesize
, l2depth
,
431 left
, hband
, hsub
+ vsub
, xm
);
435 for (x
= 0; x
< w
; x
++) {
436 blend_pixel(dst
, src
, alpha
, mask
, mask_linesize
, l2depth
,
437 1 << hsub
, hband
, hsub
+ vsub
, xm
);
442 blend_pixel(dst
, src
, alpha
, mask
, mask_linesize
, l2depth
,
443 right
, hband
, hsub
+ vsub
, xm
);
446 void ff_blend_mask(FFDrawContext
*draw
, FFDrawColor
*color
,
447 uint8_t *dst
[], int dst_linesize
[], int dst_w
, int dst_h
,
448 uint8_t *mask
, int mask_linesize
, int mask_w
, int mask_h
,
449 int l2depth
, unsigned endianness
, int x0
, int y0
)
451 unsigned alpha
, nb_planes
, nb_comp
, plane
, comp
;
452 int xm0
, ym0
, w_sub
, h_sub
, x_sub
, y_sub
, left
, right
, top
, bottom
, y
;
455 clip_interval(dst_w
, &x0
, &mask_w
, &xm0
);
456 clip_interval(dst_h
, &y0
, &mask_h
, &ym0
);
457 mask
+= ym0
* mask_linesize
;
458 if (mask_w
<= 0 || mask_h
<= 0 || !color
->rgba
[3])
460 /* alpha is in the [ 0 ; 0x10203 ] range,
461 alpha * mask is in the [ 0 ; 0x1010101 - 4 ] range */
462 alpha
= (0x10307 * color
->rgba
[3] + 0x3) >> 8;
463 nb_planes
= (draw
->nb_planes
- 1) | 1; /* eliminate alpha */
464 for (plane
= 0; plane
< nb_planes
; plane
++) {
465 nb_comp
= draw
->pixelstep
[plane
];
466 p0
= pointer_at(draw
, dst
, dst_linesize
, plane
, x0
, y0
);
471 subsampling_bounds(draw
->hsub
[plane
], &x_sub
, &w_sub
, &left
, &right
);
472 subsampling_bounds(draw
->vsub
[plane
], &y_sub
, &h_sub
, &top
, &bottom
);
473 for (comp
= 0; comp
< nb_comp
; comp
++) {
474 if (!component_used(draw
, plane
, comp
))
479 blend_line_hv(p
, draw
->pixelstep
[plane
],
480 color
->comp
[plane
].u8
[comp
], alpha
,
481 m
, mask_linesize
, l2depth
, w_sub
,
482 draw
->hsub
[plane
], draw
->vsub
[plane
],
483 xm0
, left
, right
, top
);
484 p
+= dst_linesize
[plane
];
485 m
+= top
* mask_linesize
;
487 for (y
= 0; y
< h_sub
; y
++) {
488 blend_line_hv(p
, draw
->pixelstep
[plane
],
489 color
->comp
[plane
].u8
[comp
], alpha
,
490 m
, mask_linesize
, l2depth
, w_sub
,
491 draw
->hsub
[plane
], draw
->vsub
[plane
],
492 xm0
, left
, right
, 1 << draw
->vsub
[plane
]);
493 p
+= dst_linesize
[plane
];
494 m
+= mask_linesize
<< draw
->vsub
[plane
];
497 blend_line_hv(p
, draw
->pixelstep
[plane
],
498 color
->comp
[plane
].u8
[comp
], alpha
,
499 m
, mask_linesize
, l2depth
, w_sub
,
500 draw
->hsub
[plane
], draw
->vsub
[plane
],
501 xm0
, left
, right
, bottom
);
506 int ff_draw_round_to_sub(FFDrawContext
*draw
, int sub_dir
, int round_dir
,
509 unsigned shift
= sub_dir
? draw
->vsub_max
: draw
->hsub_max
;
514 value
+= round_dir
? (1 << shift
) - 1 : 1 << (shift
- 1);
515 return (value
>> shift
) << shift
;
518 AVFilterFormats
*ff_draw_supported_pixel_formats(unsigned flags
)
520 enum AVPixelFormat i
;
522 AVFilterFormats
*fmts
= NULL
;
524 for (i
= 0; av_pix_fmt_desc_get(i
); i
++)
525 if (ff_draw_init(&draw
, i
, flags
) >= 0)
526 ff_add_format(&fmts
, i
);
536 enum AVPixelFormat f
;
537 const AVPixFmtDescriptor
*desc
;
542 for (f
= 0; av_pix_fmt_desc_get(f
); f
++) {
543 desc
= av_pix_fmt_desc_get(f
);
546 printf("Testing %s...%*s", desc
->name
,
547 (int)(16 - strlen(desc
->name
)), "");
548 r
= ff_draw_init(&draw
, f
, 0);
551 av_strerror(r
, buf
, sizeof(buf
));
552 printf("no: %s\n", buf
);
555 ff_draw_color(&draw
, &color
, (uint8_t[]) { 1, 0, 0, 1 });
556 for (i
= 0; i
< sizeof(color
); i
++)
557 if (((uint8_t *)&color
)[i
] != 128)
559 if (i
== sizeof(color
)) {
560 printf("fallback color\n");