diff --git a/libswscale/Makefile b/libswscale/Makefile index e70e358637..a9f9e03b25 100644 --- a/libswscale/Makefile +++ b/libswscale/Makefile @@ -18,6 +18,7 @@ OBJS = alphablend.o \ slice.o \ hscale.o \ vscale.o \ + gamma.o \ OBJS-$(CONFIG_SHARED) += log2_tab.o diff --git a/libswscale/gamma.c b/libswscale/gamma.c new file mode 100644 index 0000000000..d7470cb1c9 --- /dev/null +++ b/libswscale/gamma.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 Pedro Arthur + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "swscale_internal.h" + +typedef struct GammaContext +{ + uint16_t *table; +} GammaContext; + +// gamma_convert expects 16 bit rgb format +// it writes directly in src slice thus it must be modifiable (done through cascade context) +static int gamma_convert(SwsContext *c, SwsFilterDescriptor *desc, int sliceY, int sliceH) +{ + GammaContext *instance = desc->instance; + uint16_t *table = instance->table; + int srcW = desc->src->width; + + int i; + for (i = 0; i < sliceH; ++i) { + uint8_t ** src = desc->src->plane[0].line; + int src_pos = sliceY+i - desc->src->plane[0].sliceY; + + uint16_t *src1 = (uint16_t*)*(src+src_pos); + int j; + for (j = 0; j < srcW; ++j) { + uint16_t r = AV_RL16(src1 + j*4 + 0); + uint16_t g = AV_RL16(src1 + j*4 + 1); + uint16_t b = AV_RL16(src1 + j*4 + 2); + + AV_WL16(src1 + j*4 + 0, table[r]); + AV_WL16(src1 + j*4 + 1, table[g]); + AV_WL16(src1 + j*4 + 2, table[b]); + } + + } + return sliceH; +} + + +int ff_init_gamma_convert(SwsFilterDescriptor *desc, SwsSlice * src, uint16_t *table) +{ + GammaContext *li = av_malloc(sizeof(GammaContext)); + if (!li) + return AVERROR(ENOMEM); + li->table = table; + + desc->instance = li; + desc->src = src; + desc->dst = NULL; + desc->process = &gamma_convert; + + return 0; +} + diff --git a/libswscale/slice.c b/libswscale/slice.c index 44376728a0..94841e5776 100644 --- a/libswscale/slice.c +++ b/libswscale/slice.c @@ -217,6 +217,7 @@ int ff_init_filters(SwsContext * c) int num_vdesc = isPlanarYUV(c->dstFormat) && !isGray(c->dstFormat) ? 2 : 1; int need_lum_conv = c->lumToYV12 || c->readLumPlanar || c->alpToYV12 || c->readAlpPlanar; int need_chr_conv = c->chrToYV12 || c->readChrPlanar; + int need_gamma = c->is_internal_gamma; int srcIdx, dstIdx; int dst_stride = FFALIGN(c->dstW * sizeof(int16_t) + 66, 16); @@ -230,9 +231,9 @@ int ff_init_filters(SwsContext * c) num_cdesc = need_chr_conv ? 2 : 1; c->numSlice = FFMAX(num_ydesc, num_cdesc) + 2; - c->numDesc = num_ydesc + num_cdesc + num_vdesc; - c->descIndex[0] = num_ydesc; - c->descIndex[1] = num_ydesc + num_cdesc; + c->numDesc = num_ydesc + num_cdesc + num_vdesc + (need_gamma ? 2 : 0); + c->descIndex[0] = num_ydesc + (need_gamma ? 1 : 0); + c->descIndex[1] = num_ydesc + num_cdesc + (need_gamma ? 1 : 0); @@ -267,6 +268,12 @@ int ff_init_filters(SwsContext * c) srcIdx = 0; dstIdx = 1; + if (need_gamma) { + res = ff_init_gamma_convert(c->desc + index, c->slice + srcIdx, c->inv_gamma); + if (res < 0) goto cleanup; + ++index; + } + if (need_lum_conv) { res = ff_init_desc_fmt_convert(&c->desc[index], &c->slice[srcIdx], &c->slice[dstIdx], pal); if (res < 0) goto cleanup; @@ -309,6 +316,12 @@ int ff_init_filters(SwsContext * c) if (res < 0) goto cleanup; } + ++index; + if (need_gamma) { + res = ff_init_gamma_convert(c->desc + index, c->slice + dstIdx, c->gamma); + if (res < 0) goto cleanup; + } + return 0; cleanup: diff --git a/libswscale/swscale.c b/libswscale/swscale.c index 4008210abc..e9e4f622f5 100644 --- a/libswscale/swscale.c +++ b/libswscale/swscale.c @@ -379,7 +379,7 @@ static int swscale(SwsContext *c, const uint8_t *src[], int chrBufIndex = c->chrBufIndex; int lastInLumBuf = c->lastInLumBuf; int lastInChrBuf = c->lastInChrBuf; -// int perform_gamma = c->is_internal_gamma; + int perform_gamma = c->is_internal_gamma; #ifdef NEW_FILTER int lumStart = 0; @@ -392,9 +392,10 @@ static int swscale(SwsContext *c, const uint8_t *src[], SwsSlice *hout_slice = &c->slice[c->numSlice-2]; SwsSlice *vout_slice = &c->slice[c->numSlice-1]; SwsFilterDescriptor *desc = c->desc; -#endif + int hasLumHoles = 1; int hasChrHoles = 1; +#endif #ifndef NEW_FILTER if (!usePal(c->srcFormat)) { @@ -612,8 +613,8 @@ static int swscale(SwsContext *c, const uint8_t *src[], av_assert0(lastInLumBuf + 1 - srcSliceY < srcSliceH); av_assert0(lastInLumBuf + 1 - srcSliceY >= 0); - //if (perform_gamma) - // gamma_convert((uint8_t **)src1, srcW, c->inv_gamma); + if (perform_gamma) + gamma_convert((uint8_t **)src1, srcW, c->inv_gamma); hyscale(c, lumPixBuf[lumBufIndex], dstW, src1, srcW, lumXInc, hLumFilter, hLumFilterPos, hLumFilterSize, @@ -783,9 +784,9 @@ static int swscale(SwsContext *c, const uint8_t *src[], chrUSrcPtr, chrVSrcPtr, vChrFilterSize, alpSrcPtr, dest, dstW, dstY); } + if (perform_gamma) + gamma_convert(dest, dstW, c->gamma); #endif - //if (perform_gamma) - // gamma_convert(dest, dstW, c->gamma); } } if (isPlanar(dstFormat) && isALPHA(dstFormat) && !alpPixBuf) { diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h index 0d4ce331f4..608cc3e6e9 100644 --- a/libswscale/swscale_internal.h +++ b/libswscale/swscale_internal.h @@ -1028,6 +1028,9 @@ int ff_free_filters(SwsContext *c); */ int ff_rotate_slice(SwsSlice *s, int lum, int chr); +/// initializes gamma conversion descriptor +int ff_init_gamma_convert(SwsFilterDescriptor *desc, SwsSlice * src, uint16_t *table); + /// initializes lum pixel format conversion descriptor int ff_init_desc_fmt_convert(SwsFilterDescriptor *desc, SwsSlice * src, SwsSlice *dst, uint32_t *pal); diff --git a/libswscale/utils.c b/libswscale/utils.c index 4664de42e3..0c4b4d7977 100644 --- a/libswscale/utils.c +++ b/libswscale/utils.c @@ -1419,6 +1419,15 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, if (!c2->gamma || !c2->inv_gamma) return AVERROR(ENOMEM); + // is_internal_flag is set after creating the context + // to properly create the gamma convert FilterDescriptor + // we have to re-initialize it + ff_free_filters(c2); + if (ff_init_filters(c2) < 0) { + sws_freeContext(c2); + return -1; + } + c->cascaded_context[2] = NULL; if (dstFormat != tmpFmt) { ret = av_image_alloc(c->cascaded1_tmp, c->cascaded1_tmpStride, diff --git a/libswscale/vscale.c b/libswscale/vscale.c index 18d5ad15f4..3d6e81a70f 100644 --- a/libswscale/vscale.c +++ b/libswscale/vscale.c @@ -229,7 +229,7 @@ void ff_init_vscale_pfn(SwsContext *c, { VScalerContext *lumCtx = NULL; VScalerContext *chrCtx = NULL; - int idx = c->numDesc - 1; + int idx = c->numDesc - (c->is_internal_gamma ? 2 : 1); if (isPlanarYUV(c->dstFormat) || (isGray(c->dstFormat) && !isALPHA(c->dstFormat))) { if (!isGray(c->dstFormat)) {