<commit_before>#include "core/image_draw.h"

#include "core/numeric.h"
#include "core/assert.h"
#include "core/range.h"
#include "core/fonts.h"
#include "core/utf8.h"
#include "core/rgb_blend.h"

#include <utility>


namespace euphoria::core
{
    Recti
    WholeImage(const Image& image)
    {
        return Recti::FromTopLeftWidthHeight(
                image.GetHeight(), 0, image.GetWidth(), image.GetHeight());
    }

    void
    Clear(Image* image, const Rgbi& color)
    {
        ASSERT(image);
        return DrawRect(image, color, WholeImage(*image));
    }

    void
    DrawRect(Image* image, const Rgbi& color, const Recti& rect)
    {
        ASSERT(image);
        const int left   = rect.TopLeft().x;
        const int right  = rect.TopRight().x;
        const int top    = rect.TopLeft().y;
        const int bottom = rect.BottomLeft().y;
        // ASSERTX(left >= 0, left);
        // ASSERTX(bottom >= 0, bottom);
        for(int y = bottom; y < top; ++y)
        {
            if(y < 0 || y >= image->GetHeight())
                continue;
            for(int x = left; x < right; ++x)
            {
                if(x < 0 || x >= image->GetWidth())
                    continue;
                image->SetPixel(x, y, color);
            }
        }
    }


    void
    DrawSquare(Image* image, const Rgbi& color, int x, int y, int size)
    {
        ASSERT(image);
        // is the +1 right?
        DrawRect(
                image,
                color,
                Recti::FromTopLeftWidthHeight(y + 1, x, size, size));
    }

    void
    DrawCircle(
            Image*       image,
            const Rgb&   color,
            const vec2i& center,
            float        radius,
            float        softness,
            float        inner)
    {
        ASSERT(image);
        const int left = Max(0, Floori(center.x - radius - softness));
        const int right
                = Min(image->GetWidth(), Ceili(center.x + radius + softness));
        const int top = Max(0, Floori(center.y - radius - softness));
        const int bottom
                = Min(image->GetHeight(), Ceili(center.y + radius + softness));

        // color modes
        // nothing INNER-SOFTNESS fade INNER full RADIUS fade RADIUS+SOFTNESS nothing

        for(int y = top; y < bottom; ++y)
        {
            for(int x = left; x < right; ++x)
            {
                // todo: use length squared!
                const float sq = vec2f::FromTo(
                                         vec2f {static_cast<float>(x),
                                                static_cast<float>(y)},
                                         center.StaticCast<float>())
                                         .GetLength();
                bool  blend        = false;
                float blend_factor = 1.0f;

                const auto a = MakeRange(inner - softness, inner);
                const auto b = MakeRange(radius, radius + softness);

                if(IsWithin(a, sq))
                {
                    blend_factor = To01(a, sq);
                    blend        = true;
                }
                else if(IsWithin(b, sq))
                {
                    blend_factor = 1.0f - To01(b, sq);
                    blend        = true;
                }
                else if(IsWithin(MakeRange(inner, radius), sq))
                {
                    // full color
                }
                else
                {
                    // outside
                    continue;
                }

                const Rgb paint_color = blend ? RgbTransform::Transform(
                                                rgb(image->GetPixel(x, y)),
                                                blend_factor,
                                                color)
                                              : color;

                image->SetPixel(x, y, Rgbi {paint_color});
            }
        }
    }

    void
    DrawLineFast(
            Image*       image,
            const Rgbi&  color,
            const vec2i& from,
            const vec2i& to)
    {
        ASSERT(image);
        // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

        const int x0 = from.x;
        const int y0 = from.y;
        const int x1 = to.x;
        const int y1 = to.y;

        int         deltax = x1 - x0;
        int         deltay = y1 - y0;
        const float deltaerr
                = deltax == 0 ? -1 : Abs(deltay / static_cast<float>(deltax));
        float error = deltaerr - 0.5f;
        int   y     = y0;
        for(int x = x0; x < x1; x += 1)
        {
            const bool valid_x
                    = IsWithinInclusivei(0, x, image->GetWidth() - 1);
            const bool valid_y
                    = IsWithinInclusivei(0, y, image->GetHeight() - 1);
            if(valid_x && valid_y)
            {
                image->SetPixel(x, y, color);
            }

            if(deltaerr > 0)
            {
                error = error + deltaerr;
                if(error >= 0.5)
                {
                    if(deltay > 0)
                    {
                        y = y + 1;
                    }
                    else
                    {
                        y = y - 1;
                    }
                }
                error = error - 1.0;
            }
        }
    }

    int
    Round(float x)
    {
        return Floori(x + 0.5f);
    }

    // fractional part of x
    float
    GetFractionalPart(float x)
    {
        return x - Floor(x);
    }

    float
    GetOneMinusFractionalPart(float x)
    {
        return 1.0f - GetFractionalPart(x);
    }

    void
    Plot(int x, int y, float brightness, const Rgb& color, Image* image)
    {
        ASSERT(image);

        const bool valid_x = IsWithinInclusivei(0, x, image->GetWidth() - 1);
        const bool valid_y = IsWithinInclusivei(0, y, image->GetHeight() - 1);
        if(valid_x && valid_y)
        {<commit_msg>fixed fast line drawing<commit_after>#include "core/image_draw.h"

#include "core/numeric.h"
#include "core/assert.h"
#include "core/range.h"
#include "core/fonts.h"
#include "core/utf8.h"
#include "core/rgb_blend.h"

#include "core/log.h"

#include <utility>


namespace euphoria::core
{
    Recti
    WholeImage(const Image& image)
    {
        return Recti::FromTopLeftWidthHeight(
                image.GetHeight(), 0, image.GetWidth(), image.GetHeight());
    }

    void
    Clear(Image* image, const Rgbi& color)
    {
        ASSERT(image);
        return DrawRect(image, color, WholeImage(*image));
    }

    void
    DrawRect(Image* image, const Rgbi& color, const Recti& rect)
    {
        ASSERT(image);
        const int left   = rect.TopLeft().x;
        const int right  = rect.TopRight().x;
        const int top    = rect.TopLeft().y;
        const int bottom = rect.BottomLeft().y;
        // ASSERTX(left >= 0, left);
        // ASSERTX(bottom >= 0, bottom);
        for(int y = bottom; y < top; ++y)
        {
            if(y < 0 || y >= image->GetHeight())
                continue;
            for(int x = left; x < right; ++x)
            {
                if(x < 0 || x >= image->GetWidth())
                    continue;
                image->SetPixel(x, y, color);
            }
        }
    }


    void
    DrawSquare(Image* image, const Rgbi& color, int x, int y, int size)
    {
        ASSERT(image);
        // is the +1 right?
        DrawRect(
                image,
                color,
                Recti::FromTopLeftWidthHeight(y + 1, x, size, size));
    }

    void
    DrawCircle(
            Image*       image,
            const Rgb&   color,
            const vec2i& center,
            float        radius,
            float        softness,
            float        inner)
    {
        ASSERT(image);
        const int left = Max(0, Floori(center.x - radius - softness));
        const int right
                = Min(image->GetWidth(), Ceili(center.x + radius + softness));
        const int top = Max(0, Floori(center.y - radius - softness));
        const int bottom
                = Min(image->GetHeight(), Ceili(center.y + radius + softness));

        // color modes
        // nothing INNER-SOFTNESS fade INNER full RADIUS fade RADIUS+SOFTNESS nothing

        for(int y = top; y < bottom; ++y)
        {
            for(int x = left; x < right; ++x)
            {
                // todo: use length squared!
                const float sq = vec2f::FromTo(
                                         vec2f {static_cast<float>(x),
                                                static_cast<float>(y)},
                                         center.StaticCast<float>())
                                         .GetLength();
                bool  blend        = false;
                float blend_factor = 1.0f;

                const auto a = MakeRange(inner - softness, inner);
                const auto b = MakeRange(radius, radius + softness);

                if(IsWithin(a, sq))
                {
                    blend_factor = To01(a, sq);
                    blend        = true;
                }
                else if(IsWithin(b, sq))
                {
                    blend_factor = 1.0f - To01(b, sq);
                    blend        = true;
                }
                else if(IsWithin(MakeRange(inner, radius), sq))
                {
                    // full color
                }
                else
                {
                    // outside
                    continue;
                }

                const Rgb paint_color = blend ? RgbTransform::Transform(
                                                rgb(image->GetPixel(x, y)),
                                                blend_factor,
                                                color)
                                              : color;

                image->SetPixel(x, y, Rgbi {paint_color});
            }
        }
    }

    void
    DrawLineFast(
            Image*       image,
            const Rgbi&  color,
            const vec2i& from,
            const vec2i& to)
    {
        ASSERT(image);
        // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

        auto plot = [&](int x, int y)
        {
            image->SetPixel(x, y, color);
        };

        auto plot_line_low = [&](int x0, int y0, int x1, int y1)
        {
          auto dx = x1 - x0;
          auto dy = y1 - y0;
          auto yi = 1;
          if(dy < 0)
          {
            yi = -1;
            dy = -dy;
          }
          auto D = 2*dy - dx;
          auto y = y0;

          for(int x=x0; x<x1; x+=1)
          {
            plot(x,y);
            if(D > 0)
            {
               y = y + yi;
               D = D - 2*dx;
            }
            D = D + 2*dy;
          }
        };

        auto plot_line_high = [&](int x0, int y0, int x1, int y1)
        {
          int dx = x1 - x0;
          int dy = y1 - y0;
          int xi = 1;
          if(dx < 0)
          {
            xi = -1;
            dx = -dx;
          }
          int D = 2*dx - dy;
          int x = x0;

          for(int y=y0; y<y1; y+=1)
          {
            plot(x,y);
            if(D > 0)
            {
               x = x + xi;
               D = D - 2*dy;
            }
            D = D + 2*dx;
          }
        };

        const auto x0 = from.x;
        const auto y0 = from.y;
        const auto x1 = to.x;
        const auto y1 = to.y;
        if(abs(y1 - y0) < abs(x1 - x0))
        {
            if(x0 > x1) { plot_line_low(x1, y1, x0, y0); }
            else        { plot_line_low(x0, y0, x1, y1); }
        }
        else
        {
            if(y0 > y1) { plot_line_high(x1, y1, x0, y0); }
            else        { plot_line_high(x0, y0, x1, y1); }
        }
    }

    int
    Round(float x)
    {
        return Floori(x + 0.5f);
    }

    // fractional part of x
    float
    GetFractionalPart(float x)
    {
        return x - Floor(x);
    }

    float
    GetOneMinusFractionalPart(float x)
    {
        return 1.0f - GetFractionalPart(x);
    }

    void
    Plot(int x, int y, float brightness, const Rgb& color, Image* image)
    {
        ASSERT(image);

        const bool valid_x = IsWithinInclusivei(0, x, image->GetWidth() - 1);
        const bool valid_y = IsWithinInclusivei(0, y, image->GetHeight() - 1);
        if(valid_x && valid_y)
        {
{'fraction_non_alphanumeric': 0.10961538461538461, 'fraction_numerical': 0.01282051282051282, 'mean_word_length': 1.0511092851273625, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 49, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4467447', 'n_tokens_mistral': 3791, 'n_tokens_neox': 3576, 'n_words': 891}