+typedef struct ThreadData {
+ const uint8_t *srcrow;
+ uint8_t *dstrow;
+ int dst_linesize;
+ int src_linesize;
+
+ float coeff[4];
+
+ int h;
+
+ int imin[4];
+ int omin[4];
+} ThreadData;
+
+#define LOAD_COMMON \
+ ColorLevelsContext *s = ctx->priv; \
+ const ThreadData *td = arg; \
+ const int process_h = td->h; \
+ const int slice_start = (process_h * jobnr ) / nb_jobs; \
+ const int slice_end = (process_h * (jobnr+1)) / nb_jobs; \
+ const uint8_t *srcrow = td->srcrow; \
+ uint8_t *dstrow = td->dstrow; \
+ const int step = s->step;
+
+static int colorlevels_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+ LOAD_COMMON
+
+ for (int comp = 0; comp < s->nb_comp; comp++) {
+ const uint8_t offset = s->rgba_map[comp];
+ const int imin = td->imin[comp];
+ const int omin = td->omin[comp];
+ const float coeff = td->coeff[comp];
+
+ for (int y = slice_start; y < slice_end; y++) {
+ const uint8_t *src = srcrow + y * td->src_linesize;
+ uint8_t *dst = dstrow + y * td->dst_linesize;
+
+ for (int x = 0; x < s->linesize; x += step)
+ dst[x + offset] = av_clip_uint8((src[x + offset] - imin) * coeff + omin);
+ }
+ }
+
+ return 0;
+}
+
+static int colorlevels_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+ LOAD_COMMON
+
+ for (int comp = 0; comp < s->nb_comp; comp++) {
+ const uint8_t offset = s->rgba_map[comp];
+ const int imin = td->imin[comp];
+ const int omin = td->omin[comp];
+ const float coeff = td->coeff[comp];
+
+ for (int y = slice_start; y < slice_end; y++) {
+ const uint16_t *src = (const uint16_t *)(srcrow + y * td->src_linesize);
+ uint16_t *dst = (uint16_t *)(dstrow + y * td->dst_linesize);
+
+ for (int x = 0; x < s->linesize; x += step)
+ dst[x + offset] = av_clip_uint16((src[x + offset] - imin) * coeff + omin);
+ }
+ }
+
+ return 0;
+}
+