Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Copyright 2011 Stefano Sabatini <stefano.sabatini-lala poste it> | |
3 | * Copyright 2012 Nicolas George <nicolas.george normalesup org> | |
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 | #include <string.h> | |
23 | ||
24 | #include "libavutil/avutil.h" | |
25 | #include "libavutil/colorspace.h" | |
26 | #include "libavutil/mem.h" | |
27 | #include "libavutil/pixdesc.h" | |
28 | #include "drawutils.h" | |
29 | #include "formats.h" | |
30 | ||
31 | enum { RED = 0, GREEN, BLUE, ALPHA }; | |
32 | ||
33 | int ff_fill_rgba_map(uint8_t *rgba_map, enum AVPixelFormat pix_fmt) | |
34 | { | |
35 | switch (pix_fmt) { | |
36 | case AV_PIX_FMT_0RGB: | |
37 | case AV_PIX_FMT_ARGB: rgba_map[ALPHA] = 0; rgba_map[RED ] = 1; rgba_map[GREEN] = 2; rgba_map[BLUE ] = 3; break; | |
38 | case AV_PIX_FMT_0BGR: | |
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: | |
44 | case AV_PIX_FMT_RGB0: | |
45 | case AV_PIX_FMT_RGBA: | |
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: | |
51 | case AV_PIX_FMT_BGRA: | |
52 | case AV_PIX_FMT_BGR0: | |
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); | |
58 | } | |
59 | return 0; | |
60 | } | |
61 | ||
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]) | |
65 | { | |
66 | uint8_t rgba_map[4] = {0}; | |
67 | int i; | |
68 | const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(pix_fmt); | |
69 | int hsub = pix_desc->log2_chroma_w; | |
70 | ||
71 | *is_packed_rgba = ff_fill_rgba_map(rgba_map, pix_fmt) >= 0; | |
72 | ||
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]; | |
77 | ||
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]); | |
81 | if (rgba_map_ptr) | |
82 | memcpy(rgba_map_ptr, rgba_map, sizeof(rgba_map[0]) * 4); | |
83 | } else { | |
84 | int plane; | |
85 | ||
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]; | |
90 | ||
91 | for (plane = 0; plane < 4; plane++) { | |
92 | int line_size; | |
93 | int hsub1 = (plane == 1 || plane == 2) ? hsub : 0; | |
94 | ||
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); | |
99 | } | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
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) | |
108 | { | |
109 | int i, plane; | |
110 | uint8_t *p; | |
111 | ||
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); | |
117 | ||
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]; | |
123 | } | |
124 | } | |
125 | } | |
126 | ||
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) | |
130 | { | |
131 | int i, plane; | |
132 | uint8_t *p; | |
133 | ||
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); | |
139 | ||
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]; | |
145 | } | |
146 | } | |
147 | } | |
148 | ||
149 | int ff_draw_init(FFDrawContext *draw, enum AVPixelFormat format, unsigned flags) | |
150 | { | |
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 }; | |
155 | ||
156 | if (!desc->name) | |
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++) { | |
161 | c = &desc->comp[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); | |
175 | } | |
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)); | |
179 | draw->desc = desc; | |
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); | |
188 | return 0; | |
189 | } | |
190 | ||
191 | void ff_draw_color(FFDrawContext *draw, FFDrawColor *color, const uint8_t rgba[4]) | |
192 | { | |
193 | unsigned i; | |
194 | uint8_t rgba_map[4]; | |
195 | ||
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]; | |
203 | } else { | |
204 | for (i = 0; i < 4; i++) | |
205 | color->comp[rgba_map[i]].u8[0] = rgba[i]; | |
206 | } | |
207 | } else if (draw->nb_planes == 3 || draw->nb_planes == 4) { | |
208 | /* assume YUV */ | |
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]; | |
216 | } else { | |
217 | av_log(NULL, AV_LOG_WARNING, | |
218 | "Color conversion not implemented for %s\n", draw->desc->name); | |
219 | memset(color, 128, sizeof(*color)); | |
220 | } | |
221 | } | |
222 | ||
223 | static uint8_t *pointer_at(FFDrawContext *draw, uint8_t *data[], int linesize[], | |
224 | int plane, int x, int y) | |
225 | { | |
226 | return data[plane] + | |
227 | (y >> draw->vsub[plane]) * linesize[plane] + | |
228 | (x >> draw->hsub[plane]) * draw->pixelstep[plane]; | |
229 | } | |
230 | ||
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, | |
235 | int w, int h) | |
236 | { | |
237 | int plane, y, wp, hp; | |
238 | uint8_t *p, *q; | |
239 | ||
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++) { | |
246 | memcpy(q, p, wp); | |
247 | p += src_linesize[plane]; | |
248 | q += dst_linesize[plane]; | |
249 | } | |
250 | } | |
251 | } | |
252 | ||
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) | |
256 | { | |
257 | int plane, x, y, wp, hp; | |
258 | uint8_t *p0, *p; | |
259 | ||
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]); | |
264 | if (!hp) | |
265 | return; | |
266 | p = p0; | |
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]; | |
271 | } | |
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++) { | |
276 | memcpy(p, p0, wp); | |
277 | p += dst_linesize[plane]; | |
278 | } | |
279 | } | |
280 | } | |
281 | ||
282 | /** | |
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. | |
286 | */ | |
287 | static void clip_interval(int wmax, int *x, int *w, int *dx) | |
288 | { | |
289 | if (dx) | |
290 | *dx = 0; | |
291 | if (*x < 0) { | |
292 | if (dx) | |
293 | *dx = -*x; | |
294 | *w += *x; | |
295 | *x = 0; | |
296 | } | |
297 | if (*x + *w > wmax) | |
298 | *w = wmax - *x; | |
299 | } | |
300 | ||
301 | /** | |
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. | |
305 | */ | |
306 | static void subsampling_bounds(int sub, int *x, int *w, int *start, int *end) | |
307 | { | |
308 | int mask = (1 << sub) - 1; | |
309 | ||
310 | *start = (-*x) & mask; | |
311 | *x += *start; | |
312 | *start = FFMIN(*start, *w); | |
313 | *w -= *start; | |
314 | *end = *w & mask; | |
315 | *w >>= sub; | |
316 | } | |
317 | ||
318 | static int component_used(FFDrawContext *draw, int plane, int comp) | |
319 | { | |
320 | return (draw->comp_mask[plane] >> comp) & 1; | |
321 | } | |
322 | ||
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) | |
328 | { | |
329 | unsigned asrc = alpha * src; | |
330 | unsigned tau = 0x1010101 - alpha; | |
331 | int x; | |
332 | ||
333 | if (left) { | |
334 | unsigned suba = (left * alpha) >> hsub; | |
335 | *dst = (*dst * (0x1010101 - suba) + src * suba) >> 24; | |
336 | dst += dx; | |
337 | } | |
338 | for (x = 0; x < w; x++) { | |
339 | *dst = (*dst * tau + asrc) >> 24; | |
340 | dst += dx; | |
341 | } | |
342 | if (right) { | |
343 | unsigned suba = (right * alpha) >> hsub; | |
344 | *dst = (*dst * (0x1010101 - suba) + src * suba) >> 24; | |
345 | } | |
346 | } | |
347 | ||
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) | |
352 | { | |
353 | unsigned alpha, nb_planes, nb_comp, plane, comp; | |
354 | int w_sub, h_sub, x_sub, y_sub, left, right, top, bottom, y; | |
355 | uint8_t *p0, *p; | |
356 | ||
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]) | |
361 | return; | |
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); | |
368 | w_sub = w; | |
369 | h_sub = h; | |
370 | x_sub = x0; | |
371 | y_sub = 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)) | |
376 | continue; | |
377 | p = p0 + comp; | |
378 | if (top) { | |
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]; | |
383 | } | |
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]; | |
389 | } | |
390 | if (bottom) | |
391 | blend_line(p, color->comp[plane].u8[comp], alpha >> 1, | |
392 | draw->pixelstep[plane], w_sub, | |
393 | draw->hsub[plane], left, right); | |
394 | } | |
395 | } | |
396 | } | |
397 | ||
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) | |
401 | { | |
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; | |
407 | ||
408 | for (y = 0; y < h; y++) { | |
409 | xm = xm0; | |
410 | for (x = 0; x < w; x++) { | |
411 | t += ((mask[xm >> xmshf] >> ((~xm & xmmod) << l2depth)) & mbits) | |
412 | * mmult; | |
413 | xm++; | |
414 | } | |
415 | mask += mask_linesize; | |
416 | } | |
417 | alpha = (t >> shift) * alpha; | |
418 | *dst = ((0x1010101 - alpha) * *dst + alpha * src) >> 24; | |
419 | } | |
420 | ||
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) | |
426 | { | |
427 | int x; | |
428 | ||
429 | if (left) { | |
430 | blend_pixel(dst, src, alpha, mask, mask_linesize, l2depth, | |
431 | left, hband, hsub + vsub, xm); | |
432 | dst += dst_delta; | |
433 | xm += left; | |
434 | } | |
435 | for (x = 0; x < w; x++) { | |
436 | blend_pixel(dst, src, alpha, mask, mask_linesize, l2depth, | |
437 | 1 << hsub, hband, hsub + vsub, xm); | |
438 | dst += dst_delta; | |
439 | xm += 1 << hsub; | |
440 | } | |
441 | if (right) | |
442 | blend_pixel(dst, src, alpha, mask, mask_linesize, l2depth, | |
443 | right, hband, hsub + vsub, xm); | |
444 | } | |
445 | ||
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) | |
450 | { | |
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; | |
453 | uint8_t *p0, *p, *m; | |
454 | ||
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]) | |
459 | return; | |
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); | |
467 | w_sub = mask_w; | |
468 | h_sub = mask_h; | |
469 | x_sub = x0; | |
470 | y_sub = 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)) | |
475 | continue; | |
476 | p = p0 + comp; | |
477 | m = mask; | |
478 | if (top) { | |
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; | |
486 | } | |
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]; | |
495 | } | |
496 | if (bottom) | |
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); | |
502 | } | |
503 | } | |
504 | } | |
505 | ||
506 | int ff_draw_round_to_sub(FFDrawContext *draw, int sub_dir, int round_dir, | |
507 | int value) | |
508 | { | |
509 | unsigned shift = sub_dir ? draw->vsub_max : draw->hsub_max; | |
510 | ||
511 | if (!shift) | |
512 | return value; | |
513 | if (round_dir >= 0) | |
514 | value += round_dir ? (1 << shift) - 1 : 1 << (shift - 1); | |
515 | return (value >> shift) << shift; | |
516 | } | |
517 | ||
518 | AVFilterFormats *ff_draw_supported_pixel_formats(unsigned flags) | |
519 | { | |
520 | enum AVPixelFormat i; | |
521 | FFDrawContext draw; | |
522 | AVFilterFormats *fmts = NULL; | |
523 | ||
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); | |
527 | return fmts; | |
528 | } | |
529 | ||
530 | #ifdef TEST | |
531 | ||
532 | #undef printf | |
533 | ||
534 | int main(void) | |
535 | { | |
536 | enum AVPixelFormat f; | |
537 | const AVPixFmtDescriptor *desc; | |
538 | FFDrawContext draw; | |
539 | FFDrawColor color; | |
540 | int r, i; | |
541 | ||
542 | for (f = 0; av_pix_fmt_desc_get(f); f++) { | |
543 | desc = av_pix_fmt_desc_get(f); | |
544 | if (!desc->name) | |
545 | continue; | |
546 | printf("Testing %s...%*s", desc->name, | |
547 | (int)(16 - strlen(desc->name)), ""); | |
548 | r = ff_draw_init(&draw, f, 0); | |
549 | if (r < 0) { | |
550 | char buf[128]; | |
551 | av_strerror(r, buf, sizeof(buf)); | |
552 | printf("no: %s\n", buf); | |
553 | continue; | |
554 | } | |
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) | |
558 | break; | |
559 | if (i == sizeof(color)) { | |
560 | printf("fallback color\n"); | |
561 | continue; | |
562 | } | |
563 | printf("ok\n"); | |
564 | } | |
565 | return 0; | |
566 | } | |
567 | ||
568 | #endif |