summaryrefslogtreecommitdiffstats
path: root/video/mp_image.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/mp_image.c')
-rw-r--r--video/mp_image.c129
1 files changed, 104 insertions, 25 deletions
diff --git a/video/mp_image.c b/video/mp_image.c
index e5c6d957f7..81d2b7e04f 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -23,6 +23,7 @@
#include <libavutil/common.h>
#include <libavutil/bswap.h>
#include <libavutil/hwcontext.h>
+#include <libavutil/intreadwrite.h>
#include <libavutil/rational.h>
#include <libavcodec/avcodec.h>
#include <libavutil/mastering_display_metadata.h>
@@ -553,8 +554,54 @@ void mp_image_crop_rc(struct mp_image *img, struct mp_rect rc)
mp_image_crop(img, rc.x0, rc.y0, rc.x1, rc.y1);
}
+// Repeatedly write count patterns of src[0..src_size] to p.
+static void memset_pattern(void *p, size_t count, uint8_t *src, size_t src_size)
+{
+ assert(src_size >= 1);
+
+ if (src_size == 1) {
+ memset(p, src[0], count);
+ } else if (src_size == 2) { // >8 bit YUV => common, be slightly less naive
+ uint16_t val;
+ memcpy(&val, src, 2);
+ uint16_t *p16 = p;
+ while (count--)
+ *p16++ = val;
+ } else {
+ while (count--) {
+ memcpy(p, src, src_size);
+ p = (char *)p + src_size;
+ }
+ }
+}
+
+static bool endian_swap_bytes(void *d, size_t bytes, size_t word_size)
+{
+ if (word_size != 2 && word_size != 4)
+ return false;
+
+ size_t num_words = bytes / word_size;
+ uint8_t *ud = d;
+
+ switch (word_size) {
+ case 2:
+ for (size_t x = 0; x < num_words; x++)
+ AV_WL16(ud + x * 2, AV_RB16(ud + x * 2));
+ break;
+ case 4:
+ for (size_t x = 0; x < num_words; x++)
+ AV_WL32(ud + x * 2, AV_RB32(ud + x * 2));
+ break;
+ default:
+ assert(0);
+ }
+
+ return true;
+}
+
// Bottom/right border is allowed not to be aligned, but it might implicitly
// overwrite pixel data until the alignment (align_x/align_y) is reached.
+// Alpha is cleared to 0 (fully transparent).
void mp_image_clear(struct mp_image *img, int x0, int y0, int x1, int y1)
{
assert(x0 >= 0 && y0 >= 0);
@@ -564,37 +611,69 @@ void mp_image_clear(struct mp_image *img, int x0, int y0, int x1, int y1)
assert(!(y0 & (img->fmt.align_y - 1)));
struct mp_image area = *img;
+ struct mp_imgfmt_desc *fmt = &area.fmt;
mp_image_crop(&area, x0, y0, x1, y1);
- enum mp_component_type ctype = mp_imgfmt_get_component_type(img->imgfmt);
- uint32_t plane_clear[MP_MAX_PLANES] = {0};
-
- if (ctype != MP_COMPONENT_TYPE_FLOAT) {
- if (area.imgfmt == IMGFMT_UYVY) {
- plane_clear[0] = av_le2ne16(0x0080);
- } else if (area.fmt.flags & MP_IMGFLAG_YUV_NV) {
- plane_clear[1] = 0x8080;
- } else if (area.fmt.flags & MP_IMGFLAG_YUV_P) {
- struct mp_regular_imgfmt desc = {0};
- mp_get_regular_imgfmt(&desc, img->imgfmt);
- int depth = desc.component_size * 8 + MPMIN(0, desc.component_pad);
- uint16_t chroma_clear = (1 << depth) / 2;
- if (!(area.fmt.flags & MP_IMGFLAG_NE))
- chroma_clear = av_bswap16(chroma_clear);
- if (area.num_planes > 2)
- plane_clear[1] = plane_clear[2] = chroma_clear;
+ // "Black" color for each plane.
+ uint8_t plane_clear[MP_MAX_PLANES][8] = {0};
+ int plane_size[MP_MAX_PLANES] = {0};
+ int misery = 1; // pixel group width
+
+ // YUV integer chroma needs special consideration, and technically luma is
+ // usually not 0 either.
+ if ((fmt->flags & (MP_IMGFLAG_HAS_COMPS | MP_IMGFLAG_PACKED_SS_YUV)) &&
+ (fmt->flags & MP_IMGFLAG_TYPE_MASK) == MP_IMGFLAG_TYPE_UINT &&
+ (fmt->flags & MP_IMGFLAG_COLOR_MASK) == MP_IMGFLAG_COLOR_YUV)
+ {
+ uint64_t plane_clear_i[MP_MAX_PLANES] = {0};
+
+ // Need to handle "multiple" pixels with packed YUV.
+ uint8_t luma_offsets[4] = {0};
+ if (fmt->flags & MP_IMGFLAG_PACKED_SS_YUV) {
+ misery = fmt->align_x;
+ if (misery <= MP_ARRAY_SIZE(luma_offsets)) // ignore if out of bounds
+ mp_imgfmt_get_packed_yuv_locations(fmt->id, luma_offsets);
+ }
+
+ for (int c = 0; c < 4; c++) {
+ struct mp_imgfmt_comp_desc *cd = &fmt->comps[c];
+ int plane_bits = fmt->bpp[cd->plane] * misery;
+ if (plane_bits <= 64 && plane_bits % 8u == 0 && cd->size) {
+ plane_size[cd->plane] = plane_bits / 8u;
+ int depth = cd->size + MPMIN(cd->pad, 0);
+ double m, o;
+ mp_get_csp_uint_mul(area.params.color.space,
+ area.params.color.levels,
+ depth, c + 1, &m, &o);
+ uint64_t val = MPCLAMP(lrint((0 - o) / m), 0, 1ull << depth);
+ plane_clear_i[cd->plane] |= val << cd->offset;
+ for (int x = 1; x < (c ? 0 : misery); x++)
+ plane_clear_i[cd->plane] |= val << luma_offsets[x];
+ }
+ }
+
+ for (int p = 0; p < MP_MAX_PLANES; p++) {
+ if (!plane_clear_i[p])
+ plane_size[p] = 0;
+ memcpy(&plane_clear[p][0], &plane_clear_i[p], 8); // endian dependent
+
+ if (fmt->endian_shift) {
+ endian_swap_bytes(&plane_clear[p][0], plane_size[p],
+ 1 << fmt->endian_shift);
+ }
}
}
for (int p = 0; p < area.num_planes; p++) {
- int bpp = area.fmt.bpp[p];
- int bytes = mp_image_plane_bytes(&area, p, 0, area.w);
- if (bpp <= 8 || bpp > 16) {
- memset_pic(area.planes[p], plane_clear[p], bytes,
- mp_image_plane_h(&area, p), area.stride[p]);
- } else {
- memset16_pic(area.planes[p], plane_clear[p], (bytes + 1) / 2,
- mp_image_plane_h(&area, p), area.stride[p]);
+ int p_h = mp_image_plane_h(&area, p);
+ int p_w = mp_image_plane_w(&area, p);
+ for (int y = 0; y < p_h; y++) {
+ void *ptr = area.planes[p] + (ptrdiff_t)area.stride[p] * y;
+ if (plane_size[p] && plane_clear[p]) {
+ memset_pattern(ptr, p_w / misery, plane_clear[p], plane_size[p]);
+ } else {
+ memset(ptr, 0, mp_image_plane_bytes(&area, p, 0, area.w));
+ }
}
}
}