summaryrefslogtreecommitdiffstats
path: root/sub/draw_bmp.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-11-21 17:59:24 +0100
committerwm4 <wm4@nowhere>2012-11-21 19:56:59 +0100
commitea4332daf49c373c03612b7ed683f05383f843d6 (patch)
tree7a0c8dcd2cf78e6085d002aebc4537771a4aba3e /sub/draw_bmp.c
parent264b5124e702ecda1f9290b772ae7df68233acd3 (diff)
downloadmpv-ea4332daf49c373c03612b7ed683f05383f843d6.tar.bz2
mpv-ea4332daf49c373c03612b7ed683f05383f843d6.tar.xz
vo_xv: don't require frame stepping to remove OSD or subs
In order to improve performance, vo_xv didn't create a backup of the video frame before drawing OSD and subtitles during normal playback. It required the frontend to do frame stepping if it wanted to redraw the OSD, but no backup of the video frame was available. (Consider the following use case: enable the OSD permanently with --osd-level=3, then pause during playback and do something that shows an OSD message. The player will advance the video by one frame at the time the new OSD message is first drawn.) This also meant that taking a screenshot during playback with vo_xv would include OSD and subtitles in the resulting image. Fix this by always creating a backup before drawing OSD or subtitles. In order to avoid having to create a full copy of the whole image frame, introduce a complex scheme that tries to backup only the changed regions. It's unclear whether the additional complexity in draw_bmp.c for backing up only the changed areas of the frame is worth it. Possibly a simpler implementation would suffice, such as tracking only Y ranges of changed image data, or even just copying the full frame. vo_xv's get_screenshot() now always creates a copy in order not to modify the currently displayed frame.
Diffstat (limited to 'sub/draw_bmp.c')
-rw-r--r--sub/draw_bmp.c124
1 files changed, 124 insertions, 0 deletions
diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c
index e578bd0fcf..96d87eec0c 100644
--- a/sub/draw_bmp.c
+++ b/sub/draw_bmp.c
@@ -374,6 +374,7 @@ static void align_bbox(int xstep, int ystep, struct mp_rect *rc)
rc->y1 = FFALIGN(rc->y1, ystep);
}
+// Post condition, if true returned: rc is inside img
static bool align_bbox_for_swscale(struct mp_image *img, struct mp_rect *rc)
{
struct mp_rect img_rect = {0, 0, img->w, img->h};
@@ -527,4 +528,127 @@ void mp_draw_sub_bitmaps(struct mp_draw_sub_cache **cache, struct mp_image *dst,
}
}
+struct mp_draw_sub_backup
+{
+ bool valid;
+ struct mp_image *image; // backed up image parts
+ struct line_ext *lines[MP_MAX_PLANES]; // backup range for each line
+};
+
+struct line_ext {
+ int x0, x1; // x1 is exclusive
+};
+
+struct mp_draw_sub_backup *mp_draw_sub_backup_new(void)
+{
+ return talloc_zero(NULL, struct mp_draw_sub_backup);
+}
+
+// Signal that the full image is valid (nothing to backup).
+void mp_draw_sub_backup_reset(struct mp_draw_sub_backup *backup)
+{
+ backup->valid = true;
+ if (backup->image) {
+ for (int p = 0; p < MP_MAX_PLANES; p++) {
+ int h = backup->image->h;
+ for (int y = 0; y < h; y++) {
+ struct line_ext *ext = &backup->lines[p][y];
+ ext->x0 = ext->x1 = -1;
+ }
+ }
+ }
+}
+
+static void backup_realloc(struct mp_draw_sub_backup *backup,
+ struct mp_image *img)
+{
+ if (backup->image && backup->image->imgfmt == img->imgfmt
+ && backup->image->w == img->w && backup->image->h == img->h)
+ return;
+
+ talloc_free_children(backup);
+ backup->image = alloc_mpi(img->w, img->h, img->imgfmt);
+ talloc_steal(backup, backup->image);
+ for (int p = 0; p < MP_MAX_PLANES; p++) {
+ backup->lines[p] = talloc_array(backup, struct line_ext,
+ backup->image->h);
+ }
+ mp_draw_sub_backup_reset(backup);
+}
+
+static void copy_line(struct mp_image *dst, struct mp_image *src,
+ int p, int plane_y, int x0, int x1)
+{
+ int bits = MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(dst, p);
+ int xs = p ? dst->chroma_x_shift : 0;
+ memcpy(dst->planes[p] + plane_y * dst->stride[p] + (x0 >> xs) * bits / 8,
+ src->planes[p] + plane_y * src->stride[p] + (x0 >> xs) * bits / 8,
+ ((x1 - x0) >> xs) * bits / 8);
+}
+
+static void backup_rect(struct mp_draw_sub_backup *backup, struct mp_image *img,
+ int plane, struct mp_rect rc)
+{
+ if (!align_bbox_for_swscale(img, &rc))
+ return;
+ int ys = plane ? img->chroma_y_shift : 0;
+ int yp = ys ? ((1 << ys) - 1) : 0;
+ for (int y = (rc.y0 >> ys); y < ((rc.y1 + yp) >> ys); y++) {
+ struct line_ext *ext = &backup->lines[plane][y];
+ if (ext->x0 == -1) {
+ copy_line(backup->image, img, plane, y, rc.x0, rc.x1);
+ ext->x0 = rc.x0;
+ ext->x1 = rc.x1;
+ } else {
+ if (rc.x0 < ext->x0) {
+ copy_line(backup->image, img, plane, y, rc.x0, ext->x0);
+ ext->x0 = rc.x0;
+ }
+ if (ext->x1 < rc.x1) {
+ copy_line(backup->image, img, plane, y, ext->x1, rc.x1);
+ ext->x1 = rc.x1;
+ }
+ }
+ }
+}
+
+void mp_draw_sub_backup_add(struct mp_draw_sub_backup *backup,
+ struct mp_image *img, struct sub_bitmaps *sbs)
+{
+ backup_realloc(backup, img);
+
+ for (int p = 0; p < img->num_planes; p++) {
+ for (int i = 0; i < sbs->num_parts; ++i) {
+ struct sub_bitmap *sb = &sbs->parts[i];
+ struct mp_rect rc = {sb->x, sb->y, sb->x + sb->dw, sb->y + sb->dh};
+ backup_rect(backup, img, p, rc);
+ }
+ }
+}
+
+bool mp_draw_sub_backup_restore(struct mp_draw_sub_backup *backup,
+ struct mp_image *buffer)
+{
+ if (!backup->image || backup->image->imgfmt != buffer->imgfmt
+ || backup->image->w != buffer->w || backup->image->h != buffer->h
+ || !backup->valid)
+ {
+ backup->valid = false;
+ return false;
+ }
+ struct mp_image *img = backup->image;
+ for (int p = 0; p < img->num_planes; p++) {
+ int ys = p ? img->chroma_y_shift : 0;
+ int yp = ys ? ((1 << ys) - 1) : 0;
+ int p_h = ((img->h + yp) >> ys);
+ for (int y = 0; y < p_h; y++) {
+ struct line_ext *ext = &backup->lines[p][y];
+ if (ext->x0 < ext->x1) {
+ copy_line(buffer, img, p, y, ext->x0, ext->x1);
+ }
+ }
+ }
+ return true;
+}
+
// vim: ts=4 sw=4 et tw=80