Сглаженная строка с C++ Builder VCL

Вопрос: Мне нужно обновить старое графическое математическое приложение Embarcadero VCL, добавив сглаженные линии. Итак, я написал на C++ алгоритм, указанный на странице: https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm.

Как правильно написать функцию «график» для рисования пикселя в точке (x, y) с яркостью «с», особенно на Embarcadero VCL.

Решение: Это решение стало возможным благодаря вкладу @Spektre (использование объединения для смешивания цветов в соответствии с некоторой яркостью). pC — указатель холста, funcColor — предполагаемый цвет линии и свойства класса Observer:

//Antialiased line:
void Observer::aaLine(int x0, int y0, int x1, int y1)
{
union {
        uint32_t dd;//The color value
        uint8_t  db[4];//To work on channels: {00.RR.GG.BB}
    } c, c0;//Line color, and background color

    //Color mixer, with calculations on each channel, because there is no
    //Alpha channel with VCL:
    auto plot = [&](int X, int Y, float brightness){
        c.dd  = funcColor;//Line color
        c0.dd = pC->Pixels[X][Y];//Background color
        //Find coefficients to simulate transparency, where there is not:
        //Front color is augmented when background is decreased:
        for(int i = 0; i < 3; ++i)
            c.db[i] = int(c.db[i] * brightness + c0.db[i] * (1 - brightness));
        //Output obtained by conversion:
        pC->Pixels[X][Y] = static_cast<TColor>(c.dd);
    };

    //Wu's algorithm:
    //Fractional part of x:
    auto fpart  = [](double x) { return x - floor(x); };
    auto rfpart = [&](double x) { return 1 - fpart(x); };

    bool steep = abs(y1 - y0) > abs(x1 - x0);//Means slope > 45 deg.

    if (steep) {
        std::swap(x0, y0);
        std::swap(x1, y1);
    }

    if ( x0 > x1 ) {
        std::swap(x0, x1);
        std::swap(y0, y1);
    }

    double  dx = x1 - x0, dy = y1 - y0, gradient = (dx == 0. ? 1. : dy/dx) ;

    //Handle first endpoint
    double xend = x0,
         yend  = y0 + gradient * (xend - x0),
         xgap  = rfpart(x0 + 0.5),
         xpxl1 = xend, // this will be used in the main loop
         ypxl1 = floor(yend);

    if ( steep ) {
        plot(ypxl1,   xpxl1, rfpart(yend) * xgap);
        plot(ypxl1+1, xpxl1,  fpart(yend) * xgap);
    }
    else {
        plot(xpxl1, ypxl1  , rfpart(yend) * xgap);
        plot(xpxl1, ypxl1+1,  fpart(yend) * xgap);
    }
    auto intery = yend + gradient; // first y-intersection for the main loop

    //Handle second endpoint
    xend = round(x1);
    yend = y1 + gradient * (xend - x1);
    xgap = fpart(x1 + 0.5);
    auto xpxl2 = xend, //this will be used in the main loop
         ypxl2 = floor(yend);

    if ( steep ){
        plot(ypxl2  , xpxl2, rfpart(yend) * xgap);
        plot(ypxl2+1, xpxl2,  fpart(yend) * xgap);
        //Main loop:
        for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
            plot(int(intery)  , x, rfpart(intery));
            plot(int(intery+1), x,  fpart(intery));
            intery += gradient;
        }
    }
    else {
        plot(xpxl2, ypxl2,  rfpart(yend) * xgap);
        plot(xpxl2, ypxl2+1, fpart(yend) * xgap);
        //Main loop:
        for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
            plot(x, int(intery),  rfpart(intery));
            plot(x, int(intery+1), fpart(intery));
            intery += gradient;
        }
    }    
}//Observer::aaLine.

Приведенный выше исходный код обновлен и работает для меня как решение. Изображение ниже взято из тестов: синие НЕ сглажены, а красные являются результатом решения, приведенного выше. Я доволен тем, что я хочу делать.

Здесь (neftali.clubdelphi.com/redimensionar-una-imagen-antialiasin‌​g) вы можете найти пример и объяснение сглаживания с помощью VCL (delphi). Если вы работаете с C++, вы можете адаптировать код. Это на испанском, но вы можете использовать Google Translate.

Germán Estévez -Neftalí- 05.12.2022 11:07

Это скорее проблема отсутствующих пикселей при уменьшении изображений, а не сглаживание при отрисовке линии? Но в любом случае это определенно интересно, потому что мы также намерены создавать миниатюры изображений, сделанных приложением. Так что это полезно для создания миниатюр приложений.

Hugo 05.12.2022 16:58

Значения 8-битного кода RGB обычно нелинейны по отношению к яркости.

Wyck 15.12.2022 16:24

traduce, вероятно, не то слово, которое вы имели в виду.

Wyck 15.12.2022 16:27

@Вик. Ну... английский не мой родной язык, так что вы предлагаете вместо этого? Спасибо

Hugo 15.12.2022 16:34

Я не уверен, что вы имели в виду. Ты хотел сказать "уменьшить"? Похоже, вы хотите нарисовать сглаженную линию на произвольном фоне, но вы хотите, чтобы линия была определенного цвета, а не просто черного на белом или белого на черном, но вы хотели бы иметь возможность указать конкретный цвет для линии и его смешивание с фоном соответствующим образом, но образец, с которым вы работаете, не поддерживает эту функцию, и у вас есть какие-то неудовлетворительные результаты с вашим кодом. Это правильно?

Wyck 15.12.2022 16:45

Возможно, лучше было бы использовать слова «представлять», «переводить» или «выражать»? (или другой? Извините, это довольно непросто для меня, поэтому я отредактирую для этого). В остальном, что вы объясняете, я согласен: решение не идеально, я полагаю, но VCL также (намного), и этого решения для меня достаточно: это не графическое приложение, это математическое приложение. Решение быстрое, простое для понимания и ремонтопригодное. Улучшение качества приложения благодаря этому простому решению ОГРОМНОЕ, и я полностью доволен.

Hugo 15.12.2022 17:35

Пост Q&A переписан с учетом замечаний читателей.

Hugo 15.12.2022 20:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
112
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, что ваша проблема заключается в этом:

auto plot = [&](double X, double Y, double brighness){
    pC->Pixels[X][Y] = brightness; };

Если я правильно понимаю, pC - это какая-то цель TCanvas ... у этого есть 2 основные проблемы:

  1. pC->Pixels[X][Y] = brightness; будет обрабатывать brightness как цвет в соответствии с выбранным режимом (например, копирование, xor,... или что-то еще), а не как яркость.

    Я бы использовал форму альфа-смешивания, когда вы берете исходный цвет рендеринга (или фона) и желаемый цвет визуализируемой линии и смешиваете его с brightness в качестве параметра:

     TColor c0=pC->Pixels[X][Y],c0=color of your line;
    
     // here mix colors c = (c0*(1.0-brightness)) + (c1*brightness)
     // however you need to do this according to selected pixelformat of you graphic object and color channel wise...
    
     pC->Pixels[X][Y]=c;
    

    Остерегайтесь, что прозрачность VCL не использует альфа-параметр, он просто непрозрачен или нет... Для получения дополнительной информации о микшировании см. Аналогично:

    особенно обратите внимание на:

     union
             {
             DWORD dd;
             BYTE db[4];
             } c,c0;
    

    поскольку TColor в любом случае 32-битный...

  2. скорость pC->Pixels[X][Y] в VCL (или любом API на основе GDI) в лучшем случае жалкая

    если вы обрабатываете много пикселей, вам следует рассмотреть возможность использования ScanLine[Y] из Graphics::TBitmap ... и рендеринга в растровое изображение в качестве резервного буфера. Это обычно улучшает скорость от ~ 1000 до ~ 10000 раз. для получения дополнительной информации см.:

В вашем коде: ``` // здесь смешиваем цвета c = (c0*(1.0-brightness)) + (c1*brightness) ``` Если c0 — это запрошенный цвет линии, что такое c1? Спасибо за полный ответ выше.

Hugo 05.12.2022 16:50

@Hugo c0 - это цвет, который уже отображается под вашей линией (фон или что-то еще, если вы уже что-то там визуализировали), а c1 - желаемый цвет вашей линии ...

Spektre 05.12.2022 17:09

Другие вопросы по теме