<commit_before>/*
 * Copyright (C) 2006 <NAME> <<EMAIL>>
 * Copyright (C) 2011 <NAME> <<EMAIL>>
 * Copyright (c) 2011-2014, <NAME> <<EMAIL>>
 *
 * This file is part of libass.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <assert.h>
#include <ft2build.h>
#include FT_GLYPH_H
#include FT_OUTLINE_H

#include "ass_utils.h"
#include "ass_bitmap.h"
#include "ass_render.h"

#if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
#include "x86/be_blur.h"
#endif

static const unsigned base = 256;

struct ass_synth_priv {
    size_t tmp_allocated;
    void *tmp;

    int g_r;
    int g_w;

    double *g0;
    unsigned *g;
    unsigned *gt2;

    double radius;

    BEBlurFunc be_blur_func;
};

static bool generate_tables(ASS_SynthPriv *priv, double radius)
{
    double A = log(1.0 / base) / (radius * radius * 2);
    int mx, i;
    double volume_diff, volume_factor = 0;
    unsigned volume;

    if (radius < 0)
        return false;
    if (radius + 2.0 > INT_MAX / 2)
        radius = INT_MAX / 2;

    if (priv->radius == radius)
        return true;
    else
        priv->radius = radius;

    priv->g_r = ceil(radius);
    priv->g_w = 2 * priv->g_r + 1;

    if (priv->g_r) {
        priv->g0 = ass_realloc_array(priv->g0, priv->g_w, sizeof(double));
        priv->g = ass_realloc_array(priv->g, priv->g_w, sizeof(unsigned));
        priv->gt2 = ass_realloc_array(priv->gt2, priv->g_w, 256 * sizeof(unsigned));
        if (!priv->g || !priv->g0 || !priv->gt2) {
            free(priv->g0);
            free(priv->g);
            free(priv->gt2);
            return false;
        }
    }

    if (priv->g_r) {
        // exact gaussian curve
        for (i = 0; i < priv->g_w; ++i) {
            priv->g0[i] = exp(A * (i - priv->g_r) * (i - priv->g_r));
        }

        // integer gaussian curve with volume = 65536
        for (volume_diff = 10000000; volume_diff > 0.0000001;
             volume_diff *= 0.5) {
            volume_factor += volume_diff;
            volume = 0;
            for (i = 0; i < priv->g_w; ++i) {
                priv->g[i] = (unsigned) (priv->g0[i] * volume_factor + .5);
                volume += priv->g[i];
            }
            if (volume > 65536)
                volume_factor -= volume_diff;
        }
        volume = 0;
        for (i = 0; i < priv->g_w; ++i) {
            priv->g[i] = (unsigned) (priv->g0[i] * volume_factor + .5);
            volume += priv->g[i];
        }

        // gauss table:
        for (mx = 0; mx < priv->g_w; mx++) {
            for (i = 0; i < 256; i++) {
                priv->gt2[mx + i * priv->g_w] = i * priv->g[mx];
            }
        }
    }

    return true;
}

static bool resize_tmp(ASS_SynthPriv *priv, int w, int h)
{
    if (w >= INT_MAX || (w + 1) > SIZE_MAX / 2 / sizeof(unsigned) / FFMAX(h, 1))
        return false;
    size_t needed = sizeof(unsigned) * (w + 1) * h;
    if (priv->tmp && priv->tmp_allocated >= needed)
        return true;

    ass_aligned_free(priv->tmp);
    priv->tmp_allocated = FFMAX(needed, priv->tmp_allocated * 2);
    priv->tmp = ass_aligned_alloc(32, priv->tmp_allocated);
    return !!priv->tmp;
}

void ass_synth_blur(ASS_SynthPriv *priv_blur, int opaque_box, int be,
                    double blur_radius, Bitmap *bm_g, Bitmap *bm_o)
{
    if(blur_radius > 0.0 || be){
        if (bm_o && !resize_tmp(priv_blur, bm_o->w, bm_o->h))
            return;
        if ((!bm_o || opaque_box) && !resize_tmp(priv_blur, bm_g->w, bm_g->h))
            return;
    }

    // Apply box blur (multiple passes, if requested)
    if (be) {
        uint16_t* tmp = priv_blur->tmp;
        if (bm_o) {
            unsigned passes = be;
            unsigned w = bm_o->w;
            unsigned h = bm_o->h;
            unsigned stride = bm_o->stride;
            unsigned char *buf = bm_o->buffer;
            if(w && h){
                while(passes--){
                    memset(tmp, 0, stride * 2);
                    if(w < 16){
                        be_blur_c(buf, w, h, stride, tmp);
                    }else{
                        priv_blur->be_blur_func(buf, w, h, stride, tmp);
                    }
                }
            }
        }
        if (!bm_o || opaque_box) {
            unsigned passes = be;
            unsigned w = bm_g->w;
            unsigned h = bm_g->h;
            unsigned stride = bm_g->stride;
            unsigned char *buf = bm_g->buffer;
            if(w && h){
                while(passes--){
                    memset(tmp, 0, stride * 2);
                    priv_blur->be_blur_func(buf, w, h, stride, tmp);
                }
            }
        }
    }

    // Apply gaussian blur
    if (blur_radius > 0.0 && generate_tables(priv_blur, blur_radius)) {
        if (bm_o)
            ass_gauss_blur(bm_o->buffer, priv_blur->tmp,
                           bm_o->w, bm_o->h, bm_o->stride,
                           priv_blur->gt2, priv_blur->g_r,
                           priv_blur->g_w);
        if (!bm_o || opaque_box)
            ass_gauss_blur(bm_g->buffer, priv_blur->tmp,
                           bm_g->w, bm_g->h, bm_g->stride,
                           priv_blur->gt2, priv_blur->g_r,
                           priv_blur->g_w);
    }
}

ASS_SynthPriv *ass_synth_init(double radius)
{
    ASS_SynthPriv *priv = calloc(1, sizeof(ASS_SynthPriv));
    if (!priv || !generate_tables(priv, radius)) {
        free(priv);
        return NULL;
    }
    #if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
        int avx2 = has_avx2();
        #ifdef __x86_64__<commit_msg>Apply \be after \blur (like VSFilter)<commit_after>/*
 * Copyright (C) 2006 <NAME> <<EMAIL>>
 * Copyright (C) 2011 <NAME> <<EMAIL>>
 * Copyright (c) 2011-2014, <NAME> <<EMAIL>>
 *
 * This file is part of libass.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <assert.h>
#include <ft2build.h>
#include FT_GLYPH_H
#include FT_OUTLINE_H

#include "ass_utils.h"
#include "ass_bitmap.h"
#include "ass_render.h"

#if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
#include "x86/be_blur.h"
#endif

static const unsigned base = 256;

struct ass_synth_priv {
    size_t tmp_allocated;
    void *tmp;

    int g_r;
    int g_w;

    double *g0;
    unsigned *g;
    unsigned *gt2;

    double radius;

    BEBlurFunc be_blur_func;
};

static bool generate_tables(ASS_SynthPriv *priv, double radius)
{
    double A = log(1.0 / base) / (radius * radius * 2);
    int mx, i;
    double volume_diff, volume_factor = 0;
    unsigned volume;

    if (radius < 0)
        return false;
    if (radius + 2.0 > INT_MAX / 2)
        radius = INT_MAX / 2;

    if (priv->radius == radius)
        return true;
    else
        priv->radius = radius;

    priv->g_r = ceil(radius);
    priv->g_w = 2 * priv->g_r + 1;

    if (priv->g_r) {
        priv->g0 = ass_realloc_array(priv->g0, priv->g_w, sizeof(double));
        priv->g = ass_realloc_array(priv->g, priv->g_w, sizeof(unsigned));
        priv->gt2 = ass_realloc_array(priv->gt2, priv->g_w, 256 * sizeof(unsigned));
        if (!priv->g || !priv->g0 || !priv->gt2) {
            free(priv->g0);
            free(priv->g);
            free(priv->gt2);
            return false;
        }
    }

    if (priv->g_r) {
        // exact gaussian curve
        for (i = 0; i < priv->g_w; ++i) {
            priv->g0[i] = exp(A * (i - priv->g_r) * (i - priv->g_r));
        }

        // integer gaussian curve with volume = 65536
        for (volume_diff = 10000000; volume_diff > 0.0000001;
             volume_diff *= 0.5) {
            volume_factor += volume_diff;
            volume = 0;
            for (i = 0; i < priv->g_w; ++i) {
                priv->g[i] = (unsigned) (priv->g0[i] * volume_factor + .5);
                volume += priv->g[i];
            }
            if (volume > 65536)
                volume_factor -= volume_diff;
        }
        volume = 0;
        for (i = 0; i < priv->g_w; ++i) {
            priv->g[i] = (unsigned) (priv->g0[i] * volume_factor + .5);
            volume += priv->g[i];
        }

        // gauss table:
        for (mx = 0; mx < priv->g_w; mx++) {
            for (i = 0; i < 256; i++) {
                priv->gt2[mx + i * priv->g_w] = i * priv->g[mx];
            }
        }
    }

    return true;
}

static bool resize_tmp(ASS_SynthPriv *priv, int w, int h)
{
    if (w >= INT_MAX || (w + 1) > SIZE_MAX / 2 / sizeof(unsigned) / FFMAX(h, 1))
        return false;
    size_t needed = sizeof(unsigned) * (w + 1) * h;
    if (priv->tmp && priv->tmp_allocated >= needed)
        return true;

    ass_aligned_free(priv->tmp);
    priv->tmp_allocated = FFMAX(needed, priv->tmp_allocated * 2);
    priv->tmp = ass_aligned_alloc(32, priv->tmp_allocated);
    return !!priv->tmp;
}

void ass_synth_blur(ASS_SynthPriv *priv_blur, int opaque_box, int be,
                    double blur_radius, Bitmap *bm_g, Bitmap *bm_o)
{
    if(blur_radius > 0.0 || be){
        if (bm_o && !resize_tmp(priv_blur, bm_o->w, bm_o->h))
            return;
        if ((!bm_o || opaque_box) && !resize_tmp(priv_blur, bm_g->w, bm_g->h))
            return;
    }

    // Apply gaussian blur
    if (blur_radius > 0.0 && generate_tables(priv_blur, blur_radius)) {
        if (bm_o)
            ass_gauss_blur(bm_o->buffer, priv_blur->tmp,
                           bm_o->w, bm_o->h, bm_o->stride,
                           priv_blur->gt2, priv_blur->g_r,
                           priv_blur->g_w);
        if (!bm_o || opaque_box)
            ass_gauss_blur(bm_g->buffer, priv_blur->tmp,
                           bm_g->w, bm_g->h, bm_g->stride,
                           priv_blur->gt2, priv_blur->g_r,
                           priv_blur->g_w);
    }

    // Apply box blur (multiple passes, if requested)
    if (be) {
        uint16_t* tmp = priv_blur->tmp;
        if (bm_o) {
            unsigned passes = be;
            unsigned w = bm_o->w;
            unsigned h = bm_o->h;
            unsigned stride = bm_o->stride;
            unsigned char *buf = bm_o->buffer;
            if(w && h){
                while(passes--){
                    memset(tmp, 0, stride * 2);
                    if(w < 16){
                        be_blur_c(buf, w, h, stride, tmp);
                    }else{
                        priv_blur->be_blur_func(buf, w, h, stride, tmp);
                    }
                }
            }
        }
        if (!bm_o || opaque_box) {
            unsigned passes = be;
            unsigned w = bm_g->w;
            unsigned h = bm_g->h;
            unsigned stride = bm_g->stride;
            unsigned char *buf = bm_g->buffer;
            if(w && h){
                while(passes--){
                    memset(tmp, 0, stride * 2);
                    priv_blur->be_blur_func(buf, w, h, stride, tmp);
                }
            }
        }
    }
}

ASS_SynthPriv *ass_synth_init(double radius)
{
    ASS_SynthPriv *priv = calloc(1, sizeof(ASS_SynthPriv));
    if (!priv || !generate_tables(priv, radius)) {
        free(priv);
        return NULL;
    }
    #if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
        int avx2 = has_avx2();
        #ifdef __x86_64__
{'fraction_non_alphanumeric': 0.13241292751804204, 'fraction_numerical': 0.02070913084405397, 'mean_word_length': 1.848302055406613, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 203, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4290512', 'n_tokens_mistral': 4623, 'n_tokens_neox': 4251, 'n_words': 1112}