| /** |
| * Copyright (C) 2025 Niklas Haas |
| * |
| * 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 "libavutil/avassert.h" |
| #include "libavutil/mem.h" |
| #include "libavutil/rational.h" |
| |
| #include "ops_chain.h" |
| |
| SwsOpChain *ff_sws_op_chain_alloc(void) |
| { |
| return av_mallocz(sizeof(SwsOpChain)); |
| } |
| |
| void ff_sws_op_chain_free_cb(void *ptr) |
| { |
| if (!ptr) |
| return; |
| |
| SwsOpChain *chain = ptr; |
| for (int i = 0; i < chain->num_impl + 1; i++) { |
| if (chain->free[i]) |
| chain->free[i](&chain->impl[i].priv); |
| } |
| |
| av_free(chain); |
| } |
| |
| int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, |
| void (*free)(SwsOpPriv *), const SwsOpPriv *priv) |
| { |
| const int idx = chain->num_impl; |
| if (idx == SWS_MAX_OPS) |
| return AVERROR(EINVAL); |
| |
| av_assert1(func); |
| chain->impl[idx].cont = func; |
| chain->impl[idx + 1].priv = *priv; |
| chain->free[idx + 1] = free; |
| chain->num_impl++; |
| return 0; |
| } |
| |
| #define q2pixel(type, q) ((q).den ? (type) (q).num / (q).den : 0) |
| |
| #if ARCH_AARCH64 |
| int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out) |
| { |
| const SwsOp *op = params->op; |
| const AVRational64 factor = op->scale.factor; |
| switch (op->type) { |
| case SWS_PIXEL_U8: out->priv.u8[0] = q2pixel(uint8_t, factor); break; |
| case SWS_PIXEL_U16: out->priv.u16[0] = q2pixel(uint16_t, factor); break; |
| case SWS_PIXEL_U32: out->priv.u32[0] = q2pixel(uint32_t, factor); break; |
| case SWS_PIXEL_F32: out->priv.f32[0] = q2pixel(float, factor); break; |
| default: return AVERROR(EINVAL); |
| } |
| |
| return 0; |
| } |
| |
| int ff_sws_setup_clamp(const SwsImplParams *params, SwsImplResult *out) |
| { |
| const SwsOp *op = params->op; |
| for (int i = 0; i < 4; i++) { |
| const AVRational64 limit = op->clamp.limit[i]; |
| switch (op->type) { |
| case SWS_PIXEL_U8: out->priv.u8[i] = q2pixel(uint8_t, limit); break; |
| case SWS_PIXEL_U16: out->priv.u16[i] = q2pixel(uint16_t, limit); break; |
| case SWS_PIXEL_U32: out->priv.u32[i] = q2pixel(uint32_t, limit); break; |
| case SWS_PIXEL_F32: out->priv.f32[i] = q2pixel(float, limit); break; |
| default: return AVERROR(EINVAL); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int ff_sws_setup_clear(const SwsImplParams *params, SwsImplResult *out) |
| { |
| const SwsOp *op = params->op; |
| for (int i = 0; i < 4; i++) { |
| const AVRational64 value = op->clear.value[i]; |
| if (!value.den) |
| continue; |
| switch (op->type) { |
| case SWS_PIXEL_U8: out->priv.u8[i] = q2pixel(uint8_t, value); break; |
| case SWS_PIXEL_U16: out->priv.u16[i] = q2pixel(uint16_t, value); break; |
| case SWS_PIXEL_U32: out->priv.u32[i] = q2pixel(uint32_t, value); break; |
| case SWS_PIXEL_F32: out->priv.f32[i] = q2pixel(float, value); break; |
| default: return AVERROR(EINVAL); |
| } |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| int ff_sws_uop_lookup(SwsContext *ctx, const SwsUOpTable *const tables[], |
| int num_tables, const SwsUOp *uop, const int block_size, |
| SwsOpChain *chain) |
| { |
| const unsigned cpu_flags = av_get_cpu_flags(); |
| const SwsUOpEntry *match = NULL; |
| int ret; |
| |
| SwsImplParams params = { |
| .ctx = ctx, |
| .uop = uop |
| }; |
| |
| for (int n = 0; !match && n < num_tables; n++) { |
| const SwsUOpTable *table = params.table = tables[n]; |
| if (table->block_size && table->block_size != block_size || |
| table->cpu_flags & ~cpu_flags) |
| continue; |
| |
| for (int i = 0; table->entries[i]; i++) { |
| const SwsUOpEntry *entry = table->entries[i]; |
| const SwsUOp entry_uop = { |
| .uop = entry->uop, |
| .type = entry->type, |
| .mask = entry->mask, |
| .par = entry->par, |
| }; |
| |
| if (ff_sws_uop_cmp(uop, &entry_uop) != 0) |
| continue; |
| if (entry->check && !entry->check(¶ms)) |
| continue; |
| |
| match = entry; |
| break; |
| } |
| } |
| |
| if (!match) { |
| char name[64]; |
| ff_sws_uop_name(uop, name); |
| av_log(ctx, AV_LOG_DEBUG, "No implementation found for: %s\n", name); |
| return AVERROR(ENOTSUP); |
| } |
| |
| SwsImplResult res = {0}; |
| if (match->setup) { |
| ret = match->setup(¶ms, &res); |
| if (ret < 0) |
| return ret; |
| } |
| |
| ret = ff_sws_op_chain_append(chain, res.func ? res.func : match->func, |
| res.free, &res.priv); |
| if (ret < 0) { |
| if (res.free) |
| res.free(&res.priv); |
| return ret; |
| } |
| |
| for (int i = 0; i < 4; i++) { |
| chain->over_read[i] = FFMAX(chain->over_read[i], res.over_read[i]); |
| chain->over_write[i] = FFMAX(chain->over_write[i], res.over_write[i]); |
| } |
| |
| chain->cpu_flags |= params.table->cpu_flags; |
| return 0; |
| } |
| |
| int ff_sws_setup_scalar(const SwsImplParams *params, SwsImplResult *out) |
| { |
| const SwsUOp *uop = params->uop; |
| const SwsPixel scalar = uop->data.scalar; |
| switch (uop->type) { |
| case SWS_PIXEL_U8: out->priv.u8[0] = scalar.u8; break; |
| case SWS_PIXEL_U16: out->priv.u16[0] = scalar.u16; break; |
| case SWS_PIXEL_U32: out->priv.u32[0] = scalar.u32; break; |
| case SWS_PIXEL_F32: out->priv.f32[0] = scalar.f32; break; |
| default: return AVERROR(EINVAL); |
| } |
| |
| return 0; |
| } |
| |
| int ff_sws_setup_vec4(const SwsImplParams *params, SwsImplResult *out) |
| { |
| const SwsUOp *uop = params->uop; |
| for (int i = 0; i < 4; i++) { |
| const SwsPixel vi = uop->data.vec4[i]; |
| switch (uop->type) { |
| case SWS_PIXEL_U8: out->priv.u8[i] = vi.u8; break; |
| case SWS_PIXEL_U16: out->priv.u16[i] = vi.u16; break; |
| case SWS_PIXEL_U32: out->priv.u32[i] = vi.u32; break; |
| case SWS_PIXEL_F32: out->priv.f32[i] = vi.f32; break; |
| default: return AVERROR(EINVAL); |
| } |
| } |
| |
| return 0; |
| } |