1/* 2 * Copyright (c) 2013 Paul B Mahol 3 * 4 * This file is part of FFmpeg. 5 * 6 * FFmpeg is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * FFmpeg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with FFmpeg; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include "libavutil/opt.h" 22#include "avfilter.h" 23#include "drawutils.h" 24#include "formats.h" 25#include "internal.h" 26#include "video.h" 27 28#define R 0 29#define G 1 30#define B 2 31#define A 3 32 33typedef struct { 34 const AVClass *class; 35 double rr, rg, rb, ra; 36 double gr, gg, gb, ga; 37 double br, bg, bb, ba; 38 double ar, ag, ab, aa; 39 40 int *lut[4][4]; 41 42 int *buffer; 43 44 uint8_t rgba_map[4]; 45} ColorChannelMixerContext; 46 47#define OFFSET(x) offsetof(ColorChannelMixerContext, x) 48#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM 49static const AVOption colorchannelmixer_options[] = { 50 { "rr", "set the red gain for the red channel", OFFSET(rr), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS }, 51 { "rg", "set the green gain for the red channel", OFFSET(rg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 52 { "rb", "set the blue gain for the red channel", OFFSET(rb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 53 { "ra", "set the alpha gain for the red channel", OFFSET(ra), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 54 { "gr", "set the red gain for the green channel", OFFSET(gr), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 55 { "gg", "set the green gain for the green channel", OFFSET(gg), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS }, 56 { "gb", "set the blue gain for the green channel", OFFSET(gb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 57 { "ga", "set the alpha gain for the green channel", OFFSET(ga), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 58 { "br", "set the red gain for the blue channel", OFFSET(br), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 59 { "bg", "set the green gain for the blue channel", OFFSET(bg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 60 { "bb", "set the blue gain for the blue channel", OFFSET(bb), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS }, 61 { "ba", "set the alpha gain for the blue channel", OFFSET(ba), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 62 { "ar", "set the red gain for the alpha channel", OFFSET(ar), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 63 { "ag", "set the green gain for the alpha channel", OFFSET(ag), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 64 { "ab", "set the blue gain for the alpha channel", OFFSET(ab), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, 65 { "aa", "set the alpha gain for the alpha channel", OFFSET(aa), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS }, 66 { NULL } 67}; 68 69AVFILTER_DEFINE_CLASS(colorchannelmixer); 70 71static int query_formats(AVFilterContext *ctx) 72{ 73 static const enum AVPixelFormat pix_fmts[] = { 74 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, 75 AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, 76 AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, 77 AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, 78 AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0, 79 AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48, 80 AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64, 81 AV_PIX_FMT_NONE 82 }; 83 84 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); 85 return 0; 86} 87 88static int config_output(AVFilterLink *outlink) 89{ 90 AVFilterContext *ctx = outlink->src; 91 ColorChannelMixerContext *cm = ctx->priv; 92 int i, j, size, *buffer; 93 94 ff_fill_rgba_map(cm->rgba_map, outlink->format); 95 96 switch (outlink->format) { 97 case AV_PIX_FMT_RGB48: 98 case AV_PIX_FMT_BGR48: 99 case AV_PIX_FMT_RGBA64: 100 case AV_PIX_FMT_BGRA64: 101 size = 65536; 102 break; 103 default: 104 size = 256; 105 } 106 107 cm->buffer = buffer = av_malloc(16 * size * sizeof(*cm->buffer)); 108 if (!cm->buffer) 109 return AVERROR(ENOMEM); 110 111 for (i = 0; i < 4; i++) 112 for (j = 0; j < 4; j++, buffer += size) 113 cm->lut[i][j] = buffer; 114 115 for (i = 0; i < size; i++) { 116 cm->lut[R][R][i] = round(i * cm->rr); 117 cm->lut[R][G][i] = round(i * cm->rg); 118 cm->lut[R][B][i] = round(i * cm->rb); 119 cm->lut[R][A][i] = round(i * cm->ra); 120 121 cm->lut[G][R][i] = round(i * cm->gr); 122 cm->lut[G][G][i] = round(i * cm->gg); 123 cm->lut[G][B][i] = round(i * cm->gb); 124 cm->lut[G][A][i] = round(i * cm->ga); 125 126 cm->lut[B][R][i] = round(i * cm->br); 127 cm->lut[B][G][i] = round(i * cm->bg); 128 cm->lut[B][B][i] = round(i * cm->bb); 129 cm->lut[B][A][i] = round(i * cm->ba); 130 131 cm->lut[A][R][i] = round(i * cm->ar); 132 cm->lut[A][G][i] = round(i * cm->ag); 133 cm->lut[A][B][i] = round(i * cm->ab); 134 cm->lut[A][A][i] = round(i * cm->aa); 135 } 136 137 return 0; 138} 139 140static int filter_frame(AVFilterLink *inlink, AVFrame *in) 141{ 142 AVFilterContext *ctx = inlink->dst; 143 ColorChannelMixerContext *cm = ctx->priv; 144 AVFilterLink *outlink = ctx->outputs[0]; 145 const uint8_t roffset = cm->rgba_map[R]; 146 const uint8_t goffset = cm->rgba_map[G]; 147 const uint8_t boffset = cm->rgba_map[B]; 148 const uint8_t aoffset = cm->rgba_map[A]; 149 const uint8_t *srcrow = in->data[0]; 150 uint8_t *dstrow; 151 AVFrame *out; 152 int i, j; 153 154 if (av_frame_is_writable(in)) { 155 out = in; 156 } else { 157 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 158 if (!out) { 159 av_frame_free(&in); 160 return AVERROR(ENOMEM); 161 } 162 av_frame_copy_props(out, in); 163 } 164 165 dstrow = out->data[0]; 166 switch (outlink->format) { 167 case AV_PIX_FMT_BGR24: 168 case AV_PIX_FMT_RGB24: 169 for (i = 0; i < outlink->h; i++) { 170 const uint8_t *src = srcrow; 171 uint8_t *dst = dstrow; 172 173 for (j = 0; j < outlink->w * 3; j += 3) { 174 const uint8_t rin = src[j + roffset]; 175 const uint8_t gin = src[j + goffset]; 176 const uint8_t bin = src[j + boffset]; 177 178 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] + 179 cm->lut[R][G][gin] + 180 cm->lut[R][B][bin]); 181 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] + 182 cm->lut[G][G][gin] + 183 cm->lut[G][B][bin]); 184 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] + 185 cm->lut[B][G][gin] + 186 cm->lut[B][B][bin]); 187 } 188 189 srcrow += in->linesize[0]; 190 dstrow += out->linesize[0]; 191 } 192 break; 193 case AV_PIX_FMT_0BGR: 194 case AV_PIX_FMT_0RGB: 195 case AV_PIX_FMT_BGR0: 196 case AV_PIX_FMT_RGB0: 197 for (i = 0; i < outlink->h; i++) { 198 const uint8_t *src = srcrow; 199 uint8_t *dst = dstrow; 200 201 for (j = 0; j < outlink->w * 4; j += 4) { 202 const uint8_t rin = src[j + roffset]; 203 const uint8_t gin = src[j + goffset]; 204 const uint8_t bin = src[j + boffset]; 205 206 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] + 207 cm->lut[R][G][gin] + 208 cm->lut[R][B][bin]); 209 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] + 210 cm->lut[G][G][gin] + 211 cm->lut[G][B][bin]); 212 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] + 213 cm->lut[B][G][gin] + 214 cm->lut[B][B][bin]); 215 if (in != out) 216 dst[j + aoffset] = 0; 217 } 218 219 srcrow += in->linesize[0]; 220 dstrow += out->linesize[0]; 221 } 222 break; 223 case AV_PIX_FMT_ABGR: 224 case AV_PIX_FMT_ARGB: 225 case AV_PIX_FMT_BGRA: 226 case AV_PIX_FMT_RGBA: 227 for (i = 0; i < outlink->h; i++) { 228 const uint8_t *src = srcrow; 229 uint8_t *dst = dstrow; 230 231 for (j = 0; j < outlink->w * 4; j += 4) { 232 const uint8_t rin = src[j + roffset]; 233 const uint8_t gin = src[j + goffset]; 234 const uint8_t bin = src[j + boffset]; 235 const uint8_t ain = src[j + aoffset]; 236 237 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] + 238 cm->lut[R][G][gin] + 239 cm->lut[R][B][bin] + 240 cm->lut[R][A][ain]); 241 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] + 242 cm->lut[G][G][gin] + 243 cm->lut[G][B][bin] + 244 cm->lut[G][A][ain]); 245 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] + 246 cm->lut[B][G][gin] + 247 cm->lut[B][B][bin] + 248 cm->lut[B][A][ain]); 249 dst[j + aoffset] = av_clip_uint8(cm->lut[A][R][rin] + 250 cm->lut[A][G][gin] + 251 cm->lut[A][B][bin] + 252 cm->lut[A][A][ain]); 253 } 254 255 srcrow += in->linesize[0]; 256 dstrow += out->linesize[0]; 257 } 258 break; 259 case AV_PIX_FMT_BGR48: 260 case AV_PIX_FMT_RGB48: 261 for (i = 0; i < outlink->h; i++) { 262 const uint16_t *src = (const uint16_t *)srcrow; 263 uint16_t *dst = (uint16_t *)dstrow; 264 265 for (j = 0; j < outlink->w * 3; j += 3) { 266 const uint16_t rin = src[j + roffset]; 267 const uint16_t gin = src[j + goffset]; 268 const uint16_t bin = src[j + boffset]; 269 270 dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] + 271 cm->lut[R][G][gin] + 272 cm->lut[R][B][bin]); 273 dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] + 274 cm->lut[G][G][gin] + 275 cm->lut[G][B][bin]); 276 dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] + 277 cm->lut[B][G][gin] + 278 cm->lut[B][B][bin]); 279 } 280 281 srcrow += in->linesize[0]; 282 dstrow += out->linesize[0]; 283 } 284 break; 285 case AV_PIX_FMT_BGRA64: 286 case AV_PIX_FMT_RGBA64: 287 for (i = 0; i < outlink->h; i++) { 288 const uint16_t *src = (const uint16_t *)srcrow; 289 uint16_t *dst = (uint16_t *)dstrow; 290 291 for (j = 0; j < outlink->w * 4; j += 4) { 292 const uint16_t rin = src[j + roffset]; 293 const uint16_t gin = src[j + goffset]; 294 const uint16_t bin = src[j + boffset]; 295 const uint16_t ain = src[j + aoffset]; 296 297 dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] + 298 cm->lut[R][G][gin] + 299 cm->lut[R][B][bin] + 300 cm->lut[R][A][ain]); 301 dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] + 302 cm->lut[G][G][gin] + 303 cm->lut[G][B][bin] + 304 cm->lut[G][A][ain]); 305 dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] + 306 cm->lut[B][G][gin] + 307 cm->lut[B][B][bin] + 308 cm->lut[B][A][ain]); 309 dst[j + aoffset] = av_clip_uint16(cm->lut[A][R][rin] + 310 cm->lut[A][G][gin] + 311 cm->lut[A][B][bin] + 312 cm->lut[A][A][ain]); 313 } 314 315 srcrow += in->linesize[0]; 316 dstrow += out->linesize[0]; 317 } 318 } 319 320 if (in != out) 321 av_frame_free(&in); 322 return ff_filter_frame(ctx->outputs[0], out); 323} 324 325static av_cold void uninit(AVFilterContext *ctx) 326{ 327 ColorChannelMixerContext *cm = ctx->priv; 328 329 av_freep(&cm->buffer); 330} 331 332static const AVFilterPad colorchannelmixer_inputs[] = { 333 { 334 .name = "default", 335 .type = AVMEDIA_TYPE_VIDEO, 336 .filter_frame = filter_frame, 337 }, 338 { NULL } 339}; 340 341static const AVFilterPad colorchannelmixer_outputs[] = { 342 { 343 .name = "default", 344 .type = AVMEDIA_TYPE_VIDEO, 345 .config_props = config_output, 346 }, 347 { NULL } 348}; 349 350AVFilter ff_vf_colorchannelmixer = { 351 .name = "colorchannelmixer", 352 .description = NULL_IF_CONFIG_SMALL("Adjust colors by mixing color channels."), 353 .priv_size = sizeof(ColorChannelMixerContext), 354 .priv_class = &colorchannelmixer_class, 355 .uninit = uninit, 356 .query_formats = query_formats, 357 .inputs = colorchannelmixer_inputs, 358 .outputs = colorchannelmixer_outputs, 359 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, 360}; 361