<commit_before>#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}

#include "VapourSynth.h"
#include "VSHelper.h"

#include "common.h"


static const int64_t unused_colour = (int64_t)1 << 42;


typedef struct Subtitle {
    std::vector<AVPacket> packets;
    int start_frame;
    int end_frame; // Actually first frame where subtitle is not displayed.
} Subtitle;


typedef struct ImageFileData {
    std::string filter_name;

    VSNodeRef *clip;

    VSVideoInfo vi;

    VSFrameRef *blank_rgb;
    VSFrameRef *blank_alpha;

    const VSFrameRef *last_frame;
    int last_subtitle;

    std::vector<Subtitle> subtitles;

    std::vector<int64_t> palette;

    bool gray;

    AVCodecContext *avctx;
} ImageFileData;


static void VS_CC imageFileInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) {
    (void)in;
    (void)out;
    (void)core;

    ImageFileData *d = (ImageFileData *) * instanceData;

    vsapi->setVideoInfo(&d->vi, 1, node);
}


static int findSubtitleIndex(int frame, const std::vector<Subtitle> &subtitles) {
    for (size_t i = 0; i < subtitles.size(); i++)
        if (subtitles[i].start_frame <= frame && frame < subtitles[i].end_frame)
            return i;

    return -1;
}


static void makePaletteGray(uint32_t *palette) {
    for (int i = 0; i < AVPALETTE_COUNT; i++) {
        uint32_t a = palette[i] >> 24;
        uint32_t r = (palette[i] >> 16) & 0xff;
        uint32_t g = (palette[i] >> 8) & 0xff;
        uint32_t b = palette[i] & 0xff;

        g = (r + g + b) / 3;

        palette[i] = (a << 24) | (g << 16) | (g << 8) | g;
    }
}


static const VSFrameRef *VS_CC imageFileGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
    (void)frameData;

    ImageFileData *d = (ImageFileData *) *instanceData;

    if (activationReason == arInitial) {
        int subtitle_index = findSubtitleIndex(n, d->subtitles);
        if (subtitle_index == d->last_subtitle)
            return vsapi->cloneFrameRef(d->last_frame);

        VSFrameRef *rgb = vsapi->copyFrame(d->blank_rgb, core);
        VSFrameRef *alpha = vsapi->copyFrame(d->blank_alpha, core);

        if (subtitle_index > -1) {
            d->last_subtitle = subtitle_index;

            const Subtitle &sub = d->subtitles[subtitle_index];

            int got_subtitle = 0;

            AVSubtitle avsub;

            for (size_t i = 0; i < sub.packets.size(); i++) {
                AVPacket packet = sub.packets[i];

                if (avcodec_decode_subtitle2(d->avctx, &avsub, &got_subtitle, &packet) < 0) {
                    vsapi->setFilterError((d->filter_name + ": Failed to decode subtitle.").c_str(), frameCtx);

                    vsapi->freeFrame(rgb);
                    vsapi->freeFrame(alpha);

                    return nullptr;
                }

                if (got_subtitle && i < sub.packets.size() - 1) {
                    vsapi->setFilterError((d->filter_name + ": Got subtitle sooner than expected.").c_str(), frameCtx);

                    vsapi->freeFrame(rgb);
                    vsapi->freeFrame(alpha);

                    return nullptr;
                }
            }

            if (!got_subtitle) {
                vsapi->setFilterError((d->filter_name + ": Got no subtitle after decoding all the packets.").c_str(), frameCtx);

                vsapi->freeFrame(rgb);
                vsapi->freeFrame(alpha);

                return nullptr;
            }

            if (avsub.num_rects == 0) {
                vsapi->setFilterError((d->filter_name + ": Got subtitle with num_rects=0.").c_str(), frameCtx);

                vsapi->freeFrame(rgb);
                vsapi->freeFrame(alpha);

                return nullptr;
            }

            uint8_t *dst_a = vsapi->getWritePtr(alpha, 0);
            uint8_t *dst_r = vsapi->getWritePtr(rgb, 0);
            uint8_t *dst_g = vsapi->getWritePtr(rgb, 1);
            uint8_t *dst_b = vsapi->getWritePtr(rgb, 2);
            int stride = vsapi->getStride(rgb, 0);

            AVSubtitleRect *rect = avsub.rects[0];

#ifdef VS_HAVE_AVSUBTITLERECT_AVPICTURE
            uint8_t **rect_data = rect->pict.data;
            int *rect_linesize = rect->pict.linesize;
#else
            uint8_t **rect_data = rect->data;
            int *rect_linesize = rect->linesize;
#endif

            uint32_t palette[AVPALETTE_COUNT];
            memcpy(palette, rect_data[1], AVPALETTE_SIZE);
            for (size_t i = 0; i < d->palette.size(); i++)
                if (d->palette[i] != unused_colour)
                    palette[i] = d->palette[i];

            if (d->gray)
                makePaletteGray(palette);

            const uint8_t *input = rect_data[0];

            dst_a += rect->y * stride + rect->x;
            dst_r += rect->y * stride + rect->x;
            dst_g += rect->y * stride + rect->x;
            dst_b += rect->y * stride + rect->x;

            for (int y = 0; y < rect->h; y++) {
                for (int x = 0; x < rect->w; x++) {
                    uint32_t argb = palette[input[x]];

                    dst_a[x] = (argb >> 24) & 0xff;
                    dst_r[x] = (argb >> 16) & 0xff;
                    dst_g[x] = (argb >> 8) & 0xff;
                    dst_b[x] = argb & 0xff;
                }

                input += rect_linesize[0];
                dst_a += stride;
                dst_r += stride;
                dst_g += stride;
                dst_b += stride;
            }

            avsubtitle_free(&avsub);
        }


        VSMap *rgb_props = vsapi->getFramePropsRW(rgb);

        vsapi->propSetFrame(rgb_props, "_Alpha", alpha, paReplace);
        vsapi->freeFrame(alpha);

        if (subtitle_index > -1) {
            vsapi->freeFrame(d->last_frame);
            d->last_frame = vsapi->cloneFrameRef(rgb);
        }

        return rgb;
    }
<commit_msg>sub.ImageFile: Render all the AVSubtitleRect found<commit_after>#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}

#include "VapourSynth.h"
#include "VSHelper.h"

#include "common.h"


static const int64_t unused_colour = (int64_t)1 << 42;


typedef struct Subtitle {
    std::vector<AVPacket> packets;
    int start_frame;
    int end_frame; // Actually first frame where subtitle is not displayed.
} Subtitle;


typedef struct ImageFileData {
    std::string filter_name;

    VSNodeRef *clip;

    VSVideoInfo vi;

    VSFrameRef *blank_rgb;
    VSFrameRef *blank_alpha;

    const VSFrameRef *last_frame;
    int last_subtitle;

    std::vector<Subtitle> subtitles;

    std::vector<int64_t> palette;

    bool gray;

    AVCodecContext *avctx;
} ImageFileData;


static void VS_CC imageFileInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) {
    (void)in;
    (void)out;
    (void)core;

    ImageFileData *d = (ImageFileData *) * instanceData;

    vsapi->setVideoInfo(&d->vi, 1, node);
}


static int findSubtitleIndex(int frame, const std::vector<Subtitle> &subtitles) {
    for (size_t i = 0; i < subtitles.size(); i++)
        if (subtitles[i].start_frame <= frame && frame < subtitles[i].end_frame)
            return i;

    return -1;
}


static void makePaletteGray(uint32_t *palette) {
    for (int i = 0; i < AVPALETTE_COUNT; i++) {
        uint32_t a = palette[i] >> 24;
        uint32_t r = (palette[i] >> 16) & 0xff;
        uint32_t g = (palette[i] >> 8) & 0xff;
        uint32_t b = palette[i] & 0xff;

        g = (r + g + b) / 3;

        palette[i] = (a << 24) | (g << 16) | (g << 8) | g;
    }
}


static const VSFrameRef *VS_CC imageFileGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
    (void)frameData;

    ImageFileData *d = (ImageFileData *) *instanceData;

    if (activationReason == arInitial) {
        int subtitle_index = findSubtitleIndex(n, d->subtitles);
        if (subtitle_index == d->last_subtitle)
            return vsapi->cloneFrameRef(d->last_frame);

        VSFrameRef *rgb = vsapi->copyFrame(d->blank_rgb, core);
        VSFrameRef *alpha = vsapi->copyFrame(d->blank_alpha, core);

        if (subtitle_index > -1) {
            d->last_subtitle = subtitle_index;

            const Subtitle &sub = d->subtitles[subtitle_index];

            int got_subtitle = 0;

            AVSubtitle avsub;

            for (size_t i = 0; i < sub.packets.size(); i++) {
                AVPacket packet = sub.packets[i];

                if (avcodec_decode_subtitle2(d->avctx, &avsub, &got_subtitle, &packet) < 0) {
                    vsapi->setFilterError((d->filter_name + ": Failed to decode subtitle.").c_str(), frameCtx);

                    vsapi->freeFrame(rgb);
                    vsapi->freeFrame(alpha);

                    return nullptr;
                }

                if (got_subtitle && i < sub.packets.size() - 1) {
                    vsapi->setFilterError((d->filter_name + ": Got subtitle sooner than expected.").c_str(), frameCtx);

                    vsapi->freeFrame(rgb);
                    vsapi->freeFrame(alpha);

                    return nullptr;
                }
            }

            if (!got_subtitle) {
                vsapi->setFilterError((d->filter_name + ": Got no subtitle after decoding all the packets.").c_str(), frameCtx);

                vsapi->freeFrame(rgb);
                vsapi->freeFrame(alpha);

                return nullptr;
            }

            if (avsub.num_rects == 0) {
                vsapi->setFilterError((d->filter_name + ": Got subtitle with num_rects=0.").c_str(), frameCtx);

                vsapi->freeFrame(rgb);
                vsapi->freeFrame(alpha);

                return nullptr;
            }

            for (unsigned r = 0; r < avsub.num_rects; r++) {
                AVSubtitleRect *rect = avsub.rects[r];

                if (rect->w <= 0 || rect->h <= 0 || rect->type != SUBTITLE_BITMAP)
                    continue;

#ifdef VS_HAVE_AVSUBTITLERECT_AVPICTURE
                uint8_t **rect_data = rect->pict.data;
                int *rect_linesize = rect->pict.linesize;
#else
                uint8_t **rect_data = rect->data;
                int *rect_linesize = rect->linesize;
#endif

                uint32_t palette[AVPALETTE_COUNT];
                memcpy(palette, rect_data[1], AVPALETTE_SIZE);
                for (size_t i = 0; i < d->palette.size(); i++)
                    if (d->palette[i] != unused_colour)
                        palette[i] = d->palette[i];

                if (d->gray)
                    makePaletteGray(palette);

                const uint8_t *input = rect_data[0];

                uint8_t *dst_a = vsapi->getWritePtr(alpha, 0);
                uint8_t *dst_r = vsapi->getWritePtr(rgb, 0);
                uint8_t *dst_g = vsapi->getWritePtr(rgb, 1);
                uint8_t *dst_b = vsapi->getWritePtr(rgb, 2);
                int stride = vsapi->getStride(rgb, 0);

                dst_a += rect->y * stride + rect->x;
                dst_r += rect->y * stride + rect->x;
                dst_g += rect->y * stride + rect->x;
                dst_b += rect->y * stride + rect->x;

                for (int y = 0; y < rect->h; y++) {
                    for (int x = 0; x < rect->w; x++) {
                        uint32_t argb = palette[input[x]];

                        dst_a[x] = (argb >> 24) & 0xff;
                        dst_r[x] = (argb >> 16) & 0xff;
                        dst_g[x] = (argb >> 8) & 0xff;
                        dst_b[x] = argb & 0xff;
                    }

                    input += rect_linesize[0];
                    dst_a += stride;
                    dst_r += stride;
                    dst_g += stride;
                    dst_b += stride;
                }
            }

            avsubtitle_free(&avsub);
        }


        VSMap *rgb_props = vsapi->getFramePropsRW(rgb);

        vsapi->propSetFrame(rgb_props, "_Alpha", alpha, paReplace);
        vsapi->freeFrame(alpha);

        if (subtitle_index > -1) {
            vsapi->freeFrame(d->last_frame);
            d->last_frame = vsapi->cloneFrameRef(rgb);
        }

        return rgb;
    }

{'fraction_non_alphanumeric': 0.13035174279802642, 'fraction_numerical': 0.012573611332166163, 'mean_word_length': 1.8691780821917807, 'pattern_counts': {'":': 8, '<': 66, '<?xml version=': 0, '>': 176, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, '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': '6934085', 'n_tokens_mistral': 4385, 'n_tokens_neox': 4007, 'n_words': 886}