From patchwork Tue Dec 11 20:40:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 11373 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 2C11744D53F for ; Tue, 11 Dec 2018 22:40:34 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 70F8668AB1D; Tue, 11 Dec 2018 22:40:24 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BABD168AB02 for ; Tue, 11 Dec 2018 22:40:18 +0200 (EET) Received: by mail-wr1-f43.google.com with SMTP id c14so15531186wrr.0 for ; Tue, 11 Dec 2018 12:40:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=+Joo2rE1RXh4vYZZJuKw2mFf7mYjmjsFM0Feg/AakVo=; b=tKeUQnhUBoW4abS7gdDLhYB8OSAT+5aI8YxI5Uu2V1fnB4DFLbnkOIjPDT951IXh1t F7K8BXh5gdQJFByKLzueiXriUE8l5JKWJTBKXDcv8KuiuRbuT4R95nck4ZkRBnQ1Rf4C Bq+lNslzjljFNAf5tSrrklAuXZIXfOkO5f9MJ+ftn6Qzv5gIq0bRvtuFyBA1kGi9FJdm /tedzWU6S4Ce/3ua/3oO7ibthR0jAFPGihrJFYnuhvzTwx83e7Bola0Z5NVmk9qd8/US zRbUGC/v9by5MXnieN90JxJYxrqckwcc3DyjfCPB1BnVgkbBbFAh3iHMUuTTmsjOkQP7 PVkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=+Joo2rE1RXh4vYZZJuKw2mFf7mYjmjsFM0Feg/AakVo=; b=tGAwnai7MtA986LtkB8srQWy7We2QNH8lYZknTqe1NJ1WiF9Aoaz5YnRqObbfR6qOh FCXd1B8cKiAKu/fk36uF+dd2oz6mFJIIe+rdVBRwwSTXHhIGxML+FfZQhdG+5B3psJWz pu+uIn3hQejLRbDe98+uOs0/TatX6OcIBWB9WZJ9stenca+UubqxSDLD7+bzyhqvs8yy MR12dunPp2A+GMjIOPrpTgBokqCD8XpI6X7ZLKQzAocJjqWzzD5yGcP7BOARGoq0YP4X kA07a/rkmSmqzGAQyba1vF7N28txa/utj5HxMcule05reAixA3AlFZ6dwfD4PPywvomr 8GKg== X-Gm-Message-State: AA+aEWYGELtlfpe/iTvuVS9GnmnBli6/MKYOAL9ro7mGYjGf+6NuffTD 1bHfcGAIPhTno/JRqS6EFoI+jkR+ X-Google-Smtp-Source: AFSGD/Vx7YlGrMh8hf6CDubvpJEQ0qYi/KZr5GgyKrVuo0XcZDO+19+gdUyUXFmzOvj4xWuoQ3TqDg== X-Received: by 2002:a5d:66c1:: with SMTP id k1mr14400388wrw.132.1544560829297; Tue, 11 Dec 2018 12:40:29 -0800 (PST) Received: from localhost.localdomain ([94.250.174.60]) by smtp.gmail.com with ESMTPSA id 14sm1506681wmv.36.2018.12.11.12.40.28 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 11 Dec 2018 12:40:28 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Tue, 11 Dec 2018 21:40:16 +0100 Message-Id: <20181211204016.31023-2-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181211204016.31023-1-onemda@gmail.com> References: <20181211204016.31023-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 2/2] avcodec/gif: add support for alpha X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Fixes #6813. Signed-off-by: Paul B Mahol --- libavcodec/gif.c | 168 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 144 insertions(+), 24 deletions(-) diff --git a/libavcodec/gif.c b/libavcodec/gif.c index ef04fabe86..798953b8c6 100644 --- a/libavcodec/gif.c +++ b/libavcodec/gif.c @@ -2,6 +2,8 @@ * Copyright (c) 2000 Fabrice Bellard * Copyright (c) 2002 Francois Revol * Copyright (c) 2006 Baptiste Coudurier + * Copyright (c) 2018 Byorn Roche + * Copyright (c) 2018 Paul B Mahol * * first version by Francois Revol * @@ -60,6 +62,27 @@ enum { GF_TRANSDIFF = 1<<1, }; +static int is_image_translucent(AVCodecContext *avctx, + const uint8_t *buf, const int linesize) +{ + GIFContext *s = avctx->priv_data; + int trans = s->transparent_index; + + if (trans < 0) + return 0; + + for (int y = 0; y < avctx->height; y++) { + for (int x = 0; x < avctx->width; x++) { + if (buf[x] == trans) { + return 1; + } + } + buf += linesize; + } + + return 0; +} + static int get_palette_transparency_index(const uint32_t *palette) { int transparent_color_index = -1; @@ -94,17 +117,91 @@ static int pick_palette_entry(const uint8_t *buf, int linesize, int w, int h) return -1; } -static int gif_image_write_image(AVCodecContext *avctx, - uint8_t **bytestream, uint8_t *end, - const uint32_t *palette, +static void gif_crop_translucent(AVCodecContext *avctx, const uint8_t *buf, const int linesize, - AVPacket *pkt) + int *width, int *height, + int *x_start, int *y_start) +{ + GIFContext *s = avctx->priv_data; + int trans = s->transparent_index; + + /* Crop image */ + if ((s->flags & GF_OFFSETTING) && trans >= 0) { + const int w = avctx->width; + const int h = avctx->height; + int x_end = w - 1, + y_end = h - 1; + + // crop top + while (*y_start < y_end) { + int is_trans = 1; + for (int i = 0; i < w; i++) { + if (buf[w * *y_start + i] != trans) { + is_trans = 0; + break; + } + } + + if (!is_trans) + break; + (*y_start)++; + } + + // crop bottom + while (y_end < h) { + int is_trans = 1; + for (int i = 0; i < w; i++) { + if (buf[w * y_end + i] != trans) { + is_trans = 0; + break; + } + } + if (!is_trans) + break; + y_end--; + } + + // crop left + while (*x_start < x_end) { + int is_trans = 1; + for (int i = *y_start; i < y_end; i++) { + if (buf[w * i + *x_start] != trans) { + is_trans = 0; + break; + } + } + if (!is_trans) + break; + (*x_start)++; + } + + // crop right + while (x_end < w) { + int is_trans = 1; + for (int i = *y_start; i < y_end; i++) { + if (buf[w * i + x_end] != trans) { + is_trans = 0; + break; + } + } + if (!is_trans) + break; + x_end--; + } + + *height = y_end + 1 - *y_start; + *width = x_end + 1 - *x_start; + av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n", + *width, *height, *x_start, *y_start, avctx->width, avctx->height); + } +} + +static void gif_crop_opaque(AVCodecContext *avctx, + const uint32_t *palette, + const uint8_t *buf, const int linesize, + int *width, int *height, int *x_start, int *y_start) { GIFContext *s = avctx->priv_data; - int len = 0, height = avctx->height, width = avctx->width, x, y; - int x_start = 0, y_start = 0, trans = s->transparent_index; - int bcid = -1, honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette; - const uint8_t *ptr; /* Crop image */ if ((s->flags & GF_OFFSETTING) && s->last_frame && !palette) { @@ -114,34 +211,34 @@ static int gif_image_write_image(AVCodecContext *avctx, y_end = avctx->height - 1; /* skip common lines */ - while (y_start < y_end) { - if (memcmp(ref + y_start*ref_linesize, buf + y_start*linesize, width)) + while (*y_start < y_end) { + if (memcmp(ref + *y_start*ref_linesize, buf + *y_start*linesize, *width)) break; - y_start++; + (*y_start)++; } - while (y_end > y_start) { - if (memcmp(ref + y_end*ref_linesize, buf + y_end*linesize, width)) + while (y_end > *y_start) { + if (memcmp(ref + y_end*ref_linesize, buf + y_end*linesize, *width)) break; y_end--; } - height = y_end + 1 - y_start; + *height = y_end + 1 - *y_start; /* skip common columns */ - while (x_start < x_end) { + while (*x_start < x_end) { int same_column = 1; - for (y = y_start; y <= y_end; y++) { - if (ref[y*ref_linesize + x_start] != buf[y*linesize + x_start]) { + for (int y = *y_start; y <= y_end; y++) { + if (ref[y*ref_linesize + *x_start] != buf[y*linesize + *x_start]) { same_column = 0; break; } } if (!same_column) break; - x_start++; + (*x_start)++; } - while (x_end > x_start) { + while (x_end > *x_start) { int same_column = 1; - for (y = y_start; y <= y_end; y++) { + for (int y = *y_start; y <= y_end; y++) { if (ref[y*ref_linesize + x_end] != buf[y*linesize + x_end]) { same_column = 0; break; @@ -151,10 +248,32 @@ static int gif_image_write_image(AVCodecContext *avctx, break; x_end--; } - width = x_end + 1 - x_start; + *width = x_end + 1 - *x_start; av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n", - width, height, x_start, y_start, avctx->width, avctx->height); + *width, *height, *x_start, *y_start, avctx->width, avctx->height); + } +} + +static int gif_image_write_image(AVCodecContext *avctx, + uint8_t **bytestream, uint8_t *end, + const uint32_t *palette, + const uint8_t *buf, const int linesize, + AVPacket *pkt) +{ + GIFContext *s = avctx->priv_data; + int disposal, len = 0, height = avctx->height, width = avctx->width, x, y; + int x_start = 0, y_start = 0, trans = s->transparent_index; + int bcid = -1, honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette; + const uint8_t *ptr; + + if (!s->is_first_frame && is_image_translucent(avctx, buf, linesize)) { + gif_crop_translucent(avctx, buf, linesize, &width, &height, &x_start, &y_start); + honor_transparency = 0; + disposal = GCE_DISPOSAL_BACKGROUND; + } else { + gif_crop_opaque(avctx, palette, buf, linesize, &width, &height, &x_start, &y_start); + disposal = GCE_DISPOSAL_INPLACE; } if (s->is_first_frame) { /* GIF header */ @@ -191,16 +310,17 @@ static int gif_image_write_image(AVCodecContext *avctx, if (trans < 0) // TODO, patch welcome av_log(avctx, AV_LOG_DEBUG, "No available color, can not use transparency\n"); } + if (trans < 0) honor_transparency = 0; - bcid = (honor_transparency && trans >= 0) ? trans : get_palette_transparency_index(palette); + bcid = honor_transparency || disposal == GCE_DISPOSAL_BACKGROUND ? trans : get_palette_transparency_index(palette); /* graphic control extension */ bytestream_put_byte(bytestream, GIF_EXTENSION_INTRODUCER); bytestream_put_byte(bytestream, GIF_GCE_EXT_LABEL); bytestream_put_byte(bytestream, 0x04); /* block size */ - bytestream_put_byte(bytestream, 1<<2 | (bcid >= 0)); + bytestream_put_byte(bytestream, disposal<<2 | (bcid >= 0)); bytestream_put_le16(bytestream, 5); // default delay bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid); bytestream_put_byte(bytestream, 0x00);