Files
Yajbir Singh f1b860b25c
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

728 lines
24 KiB
C++

/*
* (c) Copyright Ascensio System SIA 2010-2023
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include <iostream>
#include <math.h>
#include <vector>
#include "../agg-2.4/include/agg_color_rgba.h"
#include "../graphics/aggplustypes.h"
#include "../graphics/Matrix.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef SHADING_INFO
#define SHADING_INFO
namespace NSStructures
{
/**
*
* */
template <class ColorT = agg::rgba8>
class ColorFunction
/**
* Реализацию произвольной функции в рантайме я решил сделать как массив, тк так проще всего
* я еще не совсем понял как точно передается в пдфе функция, но такая реализация, позволяет пользователю
* выбрать любой способ.
*
* Пока все копируется, т.к. в большинсве случаев вектор 2кБ по размеру и проблем нету
* только если использовать двумерную функцию размер возрастает до МБ, но не хочется возится с укузателями
* ради этого, т.к. судя по всему случай исключительный(только 1 шейдинг требует такую функцию).
* Если надо будет, наверно можно будет переписать на юникптр.
*
* Есть возможность выставить обычную линейную интерполяцию, просто для тестирования
* + так реализуется градиент стандартный.
*
* Пока у меня конструкторы по умолчанию, чтото заполняют, для тестирования опятьже
* потом стоит все убрать, чтобы в кисти не таскать это все, когда оно не нужно,
* если не выделять память то там в сумме будет <100B гдето, не думаю, что это будет так много,
* чтобы писать отдельный интерфейс для кисти
*
* Плюс я вообще не знаю как обрабатывать, внештатные ситуации, в целом, можно вообще просто
* эксепшены кидать если что или ничего не делать.
*/
{
public:
ColorFunction() : RESOLUTION(0), x_domain_min(0.0f), x_domain_max(0.0f)
{
}
ColorFunction(size_t res, float xmin, float xmax) : RESOLUTION(res), x_domain_min(xmin), x_domain_max(xmax)
{
values = std::vector<std::vector<ColorT>>(1, std::vector<ColorT>(RESOLUTION));
for (size_t i = 0; i < RESOLUTION; i++)
{
unsigned int value = (unsigned int)(255 - (255 * ((float)i / RESOLUTION)));
values[0][i] = ColorT(value, value, value);
}
}
ColorFunction(size_t res, float xmin, float xmax, float ymin, float ymax) : RESOLUTION(res), x_domain_min(xmin), x_domain_max(xmax), y_domain_min(ymin), y_domain_max(ymax)
{
values = std::vector<std::vector<ColorT>>(RESOLUTION, std::vector<ColorT>(RESOLUTION));
for (size_t i = 0; i < RESOLUTION; i++)
{
for (size_t j = 0; j < RESOLUTION; j++)
{
unsigned int value = (unsigned int)(255 * sin(i * j * M_PI / RESOLUTION));
values[j][i] = ColorT(255, 0, 0, 255);
}
}
}
void set_x_min(float x_min)
{
x_domain_min = x_min;
}
void set_x_max(float x_max)
{
x_domain_max = x_max;
}
void set_y_min(float y_min)
{
y_domain_min = y_min;
}
void set_y_max(float y_max)
{
y_domain_max = y_max;
}
float get_x_min()
{
return x_domain_min;
}
float get_x_max()
{
return x_domain_max;
}
float get_y_min()
{
return y_domain_min;
}
float get_y_max()
{
return y_domain_max;
}
ColorT get_color(float x)
{
int index = get_x_index(x);
return values[0][index];
}
//used only in shading type 1
ColorT get_color(float x, float y)
{
int xi = get_x_index(x);
int yi = get_y_index(y);
//std::cout << x << ' ' << y << std::endl;
return values[yi][xi];
}
void set_color(float x, int r, int g, int b, int a)
{
int index = get_x_index(x); // pls dont set color out of bounds, it wont crush, but will work not as you max expected
values[0][index].r = r;
values[0][index].g = g;
values[0][index].b = b;
values[0][index].a = a;
}
void set_color(float x, float y, int r, int g, int b, int a)
{
int xindex = get_x_index(x);
int yindex = get_y_index(y);
values[yindex][xindex].r = r;
values[yindex][xindex].g = g;
values[yindex][xindex].b = b;
values[yindex][xindex].a = a;
}
void set_color(size_t xindex, int r, int g, int b, int a)
{
values[0][xindex].r = r;
values[0][xindex].g = g;
values[0][xindex].b = b;
values[0][xindex].a = a;
}
void set_color(size_t xindex, size_t yindex, int r, int g, int b, int a)
{
values[yindex][xindex].r = r;
values[yindex][xindex].g = g;
values[yindex][xindex].b = b;
values[yindex][xindex].a = a;
}
// position must be sorted by incr ub otherwise
// only for 1 in function
int set_linear_interpolation(const std::vector<uint32_t> &colors, const std::vector<float> &positions)
{
if (colors.size() != positions.size())
{
return -1; // error
}
std::vector<int> indexes;
for (float x : positions)
{
indexes.push_back(get_x_index(x));
}
for (int i = 0; i < colors.size(); i++)
{
values[0][indexes[i]].r = hex2r(colors[i]);
values[0][indexes[i]].g = hex2g(colors[i]);
values[0][indexes[i]].b = hex2b(colors[i]);
values[0][indexes[i]].a = hex2a(colors[i]);
}
for (int i = 0; i < positions.size() - 1; i++)
{
interpolate_indexes(indexes[i], indexes[i + 1]);
}
return 0;
}
size_t get_resolution() const
{
return RESOLUTION;
}
private:
size_t RESOLUTION;
std::vector<std::vector<ColorT>> values;
float x_domain_min, x_domain_max;
float y_domain_min, y_domain_max;
int get_x_index(float x)
{
int x_index = (int)(RESOLUTION - 1) * (x - x_domain_min) / (x_domain_max - x_domain_min);
if (x_index < 0)
return 0;
if (x_index > RESOLUTION - 1)
return RESOLUTION - 1;
return x_index;
}
int get_y_index(float y)
{
int y_index = (int)(RESOLUTION - 1) * (y - y_domain_min) / (y_domain_max - y_domain_min);
if (values.size() < RESOLUTION)
{
return 0;
}
if (y_index < 0)
return 0;
if (y_index > RESOLUTION - 1)
return RESOLUTION - 1;
return y_index;
}
/**
* Линейная интерполяция для построения цветовой функции.
*/
int interpolate_indexes(size_t first, size_t second, size_t line = 0)
{
size_t len = second - first;
ColorT f = values[line][first];
ColorT s = values[line][second];
for(size_t i = first + 1; i < second; i++) {
values[line][i].r = f.r * (1 - (float)(i - first) / len ) + s.r * ((float)(i - first) / len );
values[line][i].g = f.g * (1 - (float)(i - first) / len ) + s.g * ((float)(i - first) / len ); ;
values[line][i].b = f.b * (1 - (float)(i - first) / len ) + s.b * ((float)(i - first) / len ); ;
values[line][i].a = f.a * (1 - (float)(i - first) / len ) + s.a * ((float)(i - first) / len ); ;
}
return 0;
}
unsigned int hex2a(uint32_t c)
{
unsigned int a = (c >> 24) & 0xFF;
return a;
}
unsigned int hex2r(uint32_t c)
{
return (c >> 16) & 0xFF;
}
unsigned int hex2g(uint32_t c)
{
return (c >> 8) & 0xFF;
}
unsigned int hex2b(uint32_t c)
{
return c & 0xFF;
}
};
struct Point
{
Point():x(0),y(0){}
Point(const float& _x, const float& _y):x(_x),y(_y){}
Point(const int& _x, const int& _y):x((float)_x),y((float)_y){}
Point(const double& _x, const double& _y):x((float)_x),y((float)_y){}
float x, y;
Point& operator+=(const Point &a)
{
x += a.x;
y += a.y;
return *this;
}
/**
* Костыль от ошибок линковки. Чтобы время не терять пока что.
*/
friend Point operator*(const Point &a, float t)
{
return Point(a.x * t, a.y * t);
}
friend Point operator*(float t, const Point &a)
{
return Point(a.x * t, a.y * t);
}
friend Point operator+(const Point &a, const Point &b)
{
return Point(a.x + b.x, a.y + b.y);
}
friend Point operator-(const Point &a, const Point &b)
{
return Point(a.x - b.x, a.y - b.y);
}
};
/**
* Тут хранится информация спецефичная для рендера ПДФ.
*
* Взял новую реализацию преобразований, т.к. готовая была на даблах,
* а в такой точности смысла нету особо.
*
* Для шейдеров требуется поддерживать два способа вычисления (с параметром и без),
* поэтому требуется много дополнительной инфы.
*
* Так же шейдер будет получать, в качетве параметров, границы, тут я пока не решил, вообще
* можно оставить соблюдение границ, на откуп пользователю, т.к. все равно заполенение в конечном итоге будет
* выполняться с помощью рисования замкнутого пути и команды Fill
* */
struct ShadingInfo
{
public:
ShadingInfo() : shading_type(Parametric), f_type(UseNew), inv_map(6){}
enum ShadingType
{
FunctionOnly,
Parametric,
TriangleInterpolation,
CurveInterpolation,
TensorCurveInterpolation
} shading_type;
// if UseOld old function is called, look for IGraphicsRender.put_BrushGradientColors;
enum ColorFunctionType
{
UseOld, UseNew
} f_type;
ColorFunction<agg::rgba8> function;
// Обратное преобразование из картинки в цветовую функцию
std::vector<float> mapping;
std::vector<float> inv_map;
// Линейный градиент задается в pdf 2 точками
bool set_two_points;
Point point1, point2;
// triangle shading
std::vector<Point> triangle;
std::vector<agg::rgba8> triangle_colors;
std::vector<float> triangle_parameters;
/**
* Матрица 4 на 4 заполняется как в документации к пдф 7 тип
* Если выбран тип 6 то значения (1,2) (2,1) (1,1) (2,2)
* В массиве игнормруется и заполняются автоматически, следите за переданным типом градинта
* (Нумерация от нуля)
*
* Наверное напишу адаптор который переводит порядок точек из 6 типа в общий.
*/
agg::rgba8 fill_color;
std::vector<std::vector<Point>> patch;
std::vector<std::vector<agg::rgba8>> patch_colors; // 2 на 2 цвета по углам
std::vector<std::vector<float>> patch_parameters; // 2 на 2 параметра
};
// Containing additional info about gradient
struct GradientInfo
{
GradientInfo() : littleRadius(0.0f), largeRadius(1.0f),
centerX(0.0f), centerY(0.0f),
angle(0.0f),
discrete_step(0.0f),
reflected(false),
periods(0.5f), periodic(false),
xsize(1.0f), ysize(1.0f),
linstretch(1.0f), linoffset(0.0f),
continue_shading_f(false),
continue_shading_b(false),
luminocity(false)
{
}
void setAngleDegrees(float deg)
{
angle = deg / 180.f * (float)M_PI;
}
float getAngleDegrees() const
{
return angle / (float)M_PI * 180.f;
}
void setStepByNum(int n) // recommended to use
{
discrete_step = 1.0f / n;
}
bool checkLuminosity() const
{
if (shading.patch_colors.empty())
return false;
for (const auto& ar : shading.patch_colors)
for (const auto& c : ar)
if (c.r != c.g || c.g != c.b)
return false;
return true;
}
bool colorEqual(const agg::rgba8& c1, const agg::rgba8& c2) const
{
return c1.r == c2.r &&
c1.g == c2.g &&
c1.b == c2.b &&
c1.a == c2.a;
}
bool isOneColor() const
{
switch (shading.shading_type)
{
case ShadingInfo::FunctionOnly:
case ShadingInfo::Parametric:
return false;
case ShadingInfo::TriangleInterpolation:
return colorEqual(shading.triangle_colors[0], shading.triangle_colors[1]) &&
colorEqual(shading.triangle_colors[1], shading.triangle_colors[2]);
case ShadingInfo::CurveInterpolation:
case ShadingInfo::TensorCurveInterpolation:
return colorEqual(shading.patch_colors[0][0], shading.patch_colors[0][1]) &&
colorEqual(shading.patch_colors[0][1], shading.patch_colors[1][0]) &&
colorEqual(shading.patch_colors[1][0], shading.patch_colors[1][1]);
default:
return false;
}
}
agg::rgba8 getFillColor() const
{
if (!shading.triangle_colors.empty())
return shading.triangle_colors[0];
if (!shading.patch_colors.empty())
return shading.patch_colors[0][0];
return agg::rgba8(0, 0, 0);
}
void setFillColor(const agg::rgba8& color)
{
shading.fill_color = color;
luminocity = true;
}
void transform(const Aggplus::CMatrix& matrix)
{
// shading transform
auto& point1 = shading.point1;
auto& point2 = shading.point2;
double point1_x = static_cast<double>(point1.x);
double point1_y = static_cast<double>(point1.y);
double point2_x = static_cast<double>(point2.x);
double point2_y = static_cast<double>(point2.y);
matrix.TransformPoint(point1_x, point1_y);
matrix.TransformPoint(point2_x, point2_y);
point1.x = static_cast<float>(point1_x);
point1.y = static_cast<float>(point1_y);
point2.x = static_cast<float>(point2_x);
point2.y = static_cast<float>(point2_y);
// triangle transform
for (auto& p : shading.triangle)
{
double triangle_x = static_cast<double>(p.x);
double triangle_y = static_cast<double>(p.y);
matrix.TransformPoint(triangle_x, triangle_y);
p.x = static_cast<float>(triangle_x);
p.y = static_cast<float>(triangle_y);
}
// domains transform
double x_domain_min = static_cast<double>(shading.function.get_x_min());
double y_domain_min = static_cast<double>(shading.function.get_y_min());
double x_domain_max = static_cast<double>(shading.function.get_x_max());
double y_domain_max = static_cast<double>(shading.function.get_y_max());
matrix.TransformPoint(x_domain_min, y_domain_min);
matrix.TransformPoint(x_domain_max, y_domain_max);
shading.function.set_x_min(static_cast<float>(x_domain_min));
shading.function.set_y_min(static_cast<float>(y_domain_min));
shading.function.set_x_max(static_cast<float>(x_domain_max));
shading.function.set_y_max(static_cast<float>(y_domain_max));
// center transform
double center_x = static_cast<double>(centerX);
double center_y = static_cast<double>(centerY);
matrix.TransformPoint(center_x, center_y);
double p0_x = static_cast<double>(p0.x);
double p0_y = static_cast<double>(p0.y);
double p1_x = static_cast<double>(p1.x);
double p1_y = static_cast<double>(p1.y);
matrix.TransformPoint(p0_x, p0_y);
matrix.TransformPoint(p1_x, p1_y);
p0.x = static_cast<float>(p0_x);
p0.y = static_cast<float>(p0_y);
p1.x = static_cast<float>(p1_x);
p1.y = static_cast<float>(p1_y);
for (size_t i = 0; i < shading.patch.size(); ++i)
{
for (size_t j = 0; j < shading.patch[i].size(); ++j)
{
double patch_x = static_cast<double>(shading.patch[i][j].x);
double patch_y = static_cast<double>(shading.patch[i][j].y);
matrix.TransformPoint(patch_x, patch_y);
shading.patch[i][j].x = static_cast<float>(patch_x);
shading.patch[i][j].y = static_cast<float>(patch_y);
}
}
// sizes scale
double sqrt_det = sqrt(fabs(matrix.Determinant()));
r0 *= sqrt_det;
r1 *= sqrt_det;
}
Point p0, p1;
float r0, r1;
float littleRadius, largeRadius;
float centerX, centerY; // used in radial, diamond and conical gradient - offset relative to figure center
float angle; // used in linear and conical gradient (rad)
float discrete_step; // used to make discrete gradient. <= 0 to make continuous
float xsize, ysize; // stretch image; can be negative to reflect relative to other axis; cannot be zero
bool reflected; // 1234567 -> 1357531 works kind of like this
bool periodic;
float periods; // number of periods (best with to colours, works as saw function in color space)
float linstretch; // stretch linear gradient, can be negative (eq angle = 180) can not be zero
float linoffset; // offset relative to image size
float continue_shading_b, continue_shading_f;
bool luminocity;
ShadingInfo shading;
};
/**
* Создает объект класса GradientInfo по заданным параметрам.
*
* Цветовую функцию надо заполнять вручную
*/
class GInfoConstructor {
public:
static GradientInfo get_functional(float xmin, float xmax, float ymin, float ymax,
std::vector<float> mapping)
{
GradientInfo ginfo;
ginfo.shading.function = ColorFunction<agg::rgba8>(256, xmin, xmax, ymin, ymax);
ginfo.shading.f_type = ShadingInfo::UseNew;
ginfo.shading.shading_type = ShadingInfo::FunctionOnly;
ginfo.shading.mapping = mapping;
return ginfo;
}
static GradientInfo get_linear(const Point &p1, const Point &p2, float t0 = 0.0f, float t1 = 1.0f,
bool continue_shading_b = false, bool continue_shading_f = false)
{
GradientInfo ginfo;
ginfo.shading.f_type = ShadingInfo::UseNew;
ginfo.shading.shading_type = ShadingInfo::Parametric;
ginfo.continue_shading_f = continue_shading_f;
ginfo.continue_shading_b = continue_shading_b;
ginfo.shading.function = ColorFunction<agg::rgba8>(256, t0, t1);
ginfo.shading.set_two_points = true;
ginfo.shading.point1 = p1;
ginfo.shading.point2 = p2;
return ginfo;
}
static GradientInfo get_radial(const Point &c0, const Point &c1, float r0, float r1,
float t0 = 0.0f, float t1 = 1.0f,
bool continue_shading_b = false, bool continue_shading_f = false)
{
GradientInfo ginfo;
ginfo.shading.f_type = ShadingInfo::UseNew;
ginfo.shading.shading_type = ShadingInfo::Parametric;
ginfo.continue_shading_f = continue_shading_f;
ginfo.continue_shading_b = continue_shading_b;
ginfo.shading.function = ColorFunction<agg::rgba8>(256, t0, t1);
ginfo.p0 = c0;
ginfo.p1 = c1;
ginfo.r0 = r0;
ginfo.r1 = r1;
return ginfo;
}
static GradientInfo get_triangle(const std::vector<Point> &points,
const std::vector<agg::rgba8> &colors,
const std::vector<float> &params,
bool parametric,
float t0 = 0.0f, float t1 = 1.0f)
{
GradientInfo ginfo;
ginfo.shading.triangle = points;
ginfo.shading.shading_type = ShadingInfo::Parametric;
ginfo.shading.function = ColorFunction<agg::rgba8>(256, t0, t1);
ginfo.continue_shading_f = false;
ginfo.continue_shading_b = false;
if (parametric)
{
ginfo.shading.triangle_parameters = params;
ginfo.shading.shading_type = ShadingInfo::Parametric;
}
else
{
ginfo.shading.triangle_colors = colors;
ginfo.shading.shading_type = ShadingInfo::TriangleInterpolation;
}
return ginfo;
}
/**
* Набор из 12 точек для построения границ в порядке указанном в стандарте,
* Порядок цветов или параметров как в стандарте.
*/
static GradientInfo get_curve(const std::vector<Point> &curve_points,
const std::vector<float> &curve_parametrs,
const std::vector<agg::rgba8> &curve_colors,
bool parametric,
float t0 = 0.0f, float t1 = 1.0f)
{
GradientInfo ginfo;
ginfo.shading.patch.resize(4, std::vector<Point>(4));
ginfo.shading.patch[0][0] = curve_points[0];
ginfo.shading.patch[0][1] = curve_points[1];
ginfo.shading.patch[0][2] = curve_points[2];
ginfo.shading.patch[0][3] = curve_points[3];
ginfo.shading.patch[1][3] = curve_points[4];
ginfo.shading.patch[2][3] = curve_points[5];
ginfo.shading.patch[3][3] = curve_points[6];
ginfo.shading.patch[3][2] = curve_points[7];
ginfo.shading.patch[3][1] = curve_points[8];
ginfo.shading.patch[3][0] = curve_points[9];
ginfo.shading.patch[2][0] = curve_points[10];
ginfo.shading.patch[1][0] = curve_points[11];
ginfo.shading.f_type = ShadingInfo::UseNew;
ginfo.shading.function = ColorFunction<agg::rgba8>(256, t0, t1);
ginfo.continue_shading_f = false;
ginfo.continue_shading_b = false;
if (parametric)
{
ginfo.shading.patch_parameters.resize(2, std::vector<float>(2));
ginfo.shading.patch_parameters[0][0] = curve_parametrs[0];
ginfo.shading.patch_parameters[0][1] = curve_parametrs[1];
ginfo.shading.patch_parameters[1][0] = curve_parametrs[3];
ginfo.shading.patch_parameters[1][1] = curve_parametrs[2];
ginfo.shading.shading_type = ShadingInfo::Parametric;
}
else
{
ginfo.shading.patch_colors.resize(2, std::vector<agg::rgba8>(2));
ginfo.shading.patch_colors[0][0] = curve_colors[0];
ginfo.shading.patch_colors[0][1] = curve_colors[1];
ginfo.shading.patch_colors[1][0] = curve_colors[3];
ginfo.shading.patch_colors[1][1] = curve_colors[2];
ginfo.shading.shading_type = ShadingInfo::CurveInterpolation;
}
return ginfo;
}
static GradientInfo get_tensor_curve(const std::vector<std::vector<Point>> &curve_poits,
const std::vector<std::vector<float>> &curve_parametrs,
const std::vector<std::vector<agg::rgba8>> &curve_colors,
bool parametric,
bool luminosity = false,
float t0 = 0.0f, float t1 = 1.0f)
{
GradientInfo ginfo;
ginfo.shading.patch = curve_poits;
ginfo.shading.f_type = ShadingInfo::UseNew;
ginfo.shading.function = ColorFunction<agg::rgba8>(256, t0, t1);
ginfo.luminocity = luminosity;
ginfo.continue_shading_f = false;
ginfo.continue_shading_b = false;
if (parametric)
{
ginfo.shading.patch_parameters = curve_parametrs;
ginfo.shading.shading_type = ShadingInfo::Parametric;
}
else
{
ginfo.shading.patch_colors = curve_colors;
ginfo.shading.shading_type = ShadingInfo::TensorCurveInterpolation;
}
return ginfo;
}
};
}
#endif