Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

png render problem #155

Open
173619070 opened this issue Feb 27, 2025 · 11 comments
Open

png render problem #155

173619070 opened this issue Feb 27, 2025 · 11 comments
Assignees
Labels

Comments

@173619070
Copy link

173619070 commented Feb 27, 2025

Image

Image

code:

const char* imgPath = "E:/vkvg/tests/data/miroir.png";
const char* imgPath2 = "E:/vkvg/tests/data/filled.png";

void paint () {
VkvgContext ctx = vkvg_create(surf);
vkvg_set_line_width(ctx, 1.0); // 设置线宽
vkvg_set_line_cap(ctx, VKVG_LINE_CAP_ROUND); // 设置线条端点样式
vkvg_set_line_join(ctx, VKVG_LINE_JOIN_ROUND); // 设置线条连接样式

VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);

vkvg_set_source_surface(ctx, imgSurf, 0, 0);
vkvg_paint(ctx);


vkvg_surface_destroy(imgSurf);

vkvg_destroy(ctx);

}
//----------
sample project: img_surf
configure code:
//-------------------------------
static bool paused = false;
static bool offscreen = false;
static bool threadAware = false;
static VkSampleCountFlags samples = VK_SAMPLE_COUNT_64_BIT;
static VkPhysicalDeviceType preferedPhysicalDeviceType = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
static vk_engine_t* e;
static char* saveToPng = NULL;

@173619070
Copy link
Author

[

Image

](url)

@jpbruyere
Copy link
Owner

jpbruyere commented Feb 28, 2025

Given the code you provided, I have no problem with that image. The first rendered image you provide seems to be clipped. Can you provide the code which clip the drawing?

Here is an example of an image painted in a circle with a stroked border:

Image

void imgTest2() {
    VkvgContext ctx = vkvg_create(surf);
    vkvg_set_line_width(ctx, 10.0);
    vkvg_set_line_cap(ctx, VKVG_LINE_CAP_ROUND);
    vkvg_set_line_join(ctx, VKVG_LINE_JOIN_ROUND);

    VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath3);
    vkvg_set_source_surface(ctx, imgSurf, 0, 0);

    float arcSize = 70.f;
    vkvg_translate(ctx, 20, 20);

    vkvg_arc(ctx, arcSize, arcSize, arcSize, 0, 2.f * M_PIF);
    vkvg_fill_preserve(ctx);
    vkvg_set_source_rgba(ctx,0.4f,0.4f,0.9f,1);

    vkvg_stroke(ctx);

    vkvg_surface_destroy(imgSurf);
    vkvg_destroy(ctx);
}

@jpbruyere jpbruyere self-assigned this Feb 28, 2025
@jpbruyere
Copy link
Owner

with the following code, I can reproduce the same result:

void imgTestClipped() {
    VkvgContext ctx = vkvg_create(surf);

    vkvg_set_line_width(ctx, 10.0); // 设置线宽
    vkvg_set_line_cap(ctx, VKVG_LINE_CAP_ROUND); // 设置线条端点样式
    vkvg_set_line_join(ctx, VKVG_LINE_JOIN_ROUND); // 设置线条连接样式
    vkvg_set_fill_rule(ctx, VKVG_FILL_RULE_EVEN_ODD); // 设置线条连接样式

    vkvg_set_source_rgba(ctx,0,0,0,1);
    vkvg_paint(ctx);//black background, or png will be transparent


    VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath3);
    vkvg_set_source_surface(ctx, imgSurf, 0, 0);

    float arcSize = 70.f;

    vkvg_arc(ctx, arcSize, arcSize, 71.f, 0, 2.f * M_PIF);
    vkvg_clip_preserve(ctx);

    vkvg_fill(ctx);

    vkvg_surface_destroy(imgSurf);
    vkvg_destroy(ctx);
}

Image

I'll make further analysis and come back to you with more info...

@173619070
Copy link
Author

with the following code, I can reproduce the same result:

void imgTestClipped() {
    VkvgContext ctx = vkvg_create(surf);

    vkvg_set_line_width(ctx, 10.0); // 设置线宽
    vkvg_set_line_cap(ctx, VKVG_LINE_CAP_ROUND); // 设置线条端点样式
    vkvg_set_line_join(ctx, VKVG_LINE_JOIN_ROUND); // 设置线条连接样式
    vkvg_set_fill_rule(ctx, VKVG_FILL_RULE_EVEN_ODD); // 设置线条连接样式

    vkvg_set_source_rgba(ctx,0,0,0,1);
    vkvg_paint(ctx);//black background, or png will be transparent


    VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath3);
    vkvg_set_source_surface(ctx, imgSurf, 0, 0);

    float arcSize = 70.f;

    vkvg_arc(ctx, arcSize, arcSize, 71.f, 0, 2.f * M_PIF);
    vkvg_clip_preserve(ctx);

    vkvg_fill(ctx);

    vkvg_surface_destroy(imgSurf);
    vkvg_destroy(ctx);
}

Image

I'll make further analysis and come back to you with more info...

@173619070
Copy link
Author

all code here from the file: vkvg/tests/img_surf.c, the problem still exists.

`#include "test.h"

const char* imgPath = "E:/vkvg/tests/data/miroir.png";
const char* imgPath2 = "E:/vkvg/tests/data/filled.png";

void paint () {
VkvgContext ctx = vkvg_create(surf);
//vkvg_set_line_width(ctx, 1.0); // 设置线宽
//vkvg_set_line_cap(ctx, VKVG_LINE_CAP_ROUND); // 设置线条端点样式
//vkvg_set_line_join(ctx, VKVG_LINE_JOIN_ROUND); // 设置线条连接样式

VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);

vkvg_set_source_surface(ctx, imgSurf, 0, 0);
vkvg_paint(ctx);


vkvg_surface_destroy(imgSurf);

vkvg_destroy(ctx);

}
void paint_offset () {
VkvgContext ctx = vkvg_create(surf);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);

vkvg_set_source_surface(ctx, imgSurf, 100, 100);
vkvg_paint(ctx);

vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);

}
void paint_with_scale(){
VkvgContext ctx = vkvg_create(surf);
vkvg_scale (ctx, 0.2f,0.2f);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 0, 0);

vkvg_paint(ctx);

vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);

}
void translate(){
VkvgContext ctx = vkvg_create(surf);
vkvg_translate (ctx, 150,50);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 0, 0);

vkvg_paint(ctx);

vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);

}
void offset_and_scale(){
VkvgContext ctx = vkvg_create(surf);
vkvg_scale (ctx, 0.2f,0.2f);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 100, 100);

vkvg_paint(ctx);

vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);

}

static float angle = 0;
void paint_with_rot(){
angle += 0.005;
VkvgContext ctx = vkvg_create(surf);
vkvg_clear(ctx);

vkvg_rotate (ctx, angle);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 0, 0);

vkvg_paint(ctx);

vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);

}
void offset_and_rot(){
angle += 0.005;
VkvgContext ctx = vkvg_create(surf);
vkvg_clear(ctx);

vkvg_rotate (ctx, angle);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 100, 100);

vkvg_paint(ctx);

vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);

}

void paint_pattern () {
VkvgContext ctx = vkvg_create(surf);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_set_source(ctx, pat);
vkvg_paint(ctx);
vkvg_pattern_destroy(pat);
vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);
}
void paint_patt_repeat () {
VkvgContext ctx = vkvg_create(surf);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_pattern_set_extend(pat,VKVG_EXTEND_REPEAT);
vkvg_set_source(ctx, pat);
vkvg_paint(ctx);
vkvg_pattern_destroy(pat);
vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);
}
void paint_patt_repeat_scalled () {
VkvgContext ctx = vkvg_create(surf);
vkvg_scale (ctx, 0.2f,0.2f);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_pattern_set_extend(pat,VKVG_EXTEND_REPEAT);
vkvg_set_source(ctx, pat);
vkvg_paint(ctx);
vkvg_pattern_destroy(pat);
vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);
}
void paint_patt_pad () {
VkvgContext ctx = vkvg_create(surf);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_pattern_set_extend(pat,VKVG_EXTEND_PAD);
vkvg_set_source(ctx, pat);
vkvg_paint(ctx);
vkvg_pattern_destroy(pat);
vkvg_surface_destroy(imgSurf);
vkvg_destroy(ctx);
}

void test(){
VkvgContext ctx = vkvg_create(surf);
vkvg_set_fill_rule(ctx,VKVG_FILL_RULE_EVEN_ODD);
VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);

vkvg_translate(ctx,200,200);
//vkvg_rotate(ctx,M_PI_4);

vkvg_set_line_width(ctx,20.f);
//vkvg_set_source_rgba(ctx,1,0,0,1);
vkvg_arc(ctx,200,200,200,0,2.f*M_PIF);
vkvg_new_sub_path(ctx);
vkvg_arc(ctx,200,200,100,0,2.f*M_PIF);

vkvg_set_source_surface(ctx, imgSurf, 0, 0);
vkvg_fill_preserve(ctx);
vkvg_set_source_rgba(ctx,0.2f,0.3f,0.8f,1);

vkvg_stroke(ctx);

vkvg_surface_destroy(imgSurf);

vkvg_destroy(ctx);

}

int main(int argc, char *argv[]) {
no_test_size = true;
PERFORM_TEST (paint, argc, argv);
//PERFORM_TEST (paint_offset, argc, argv);
//PERFORM_TEST (paint_with_scale, argc, argv);
//PERFORM_TEST (offset_and_scale, argc, argv);
PERFORM_TEST (translate, argc, argv);
PERFORM_TEST (paint_with_rot, argc, argv);
//PERFORM_TEST (offset_and_rot, argc, argv);
//PERFORM_TEST (paint_pattern, argc, argv);
//PERFORM_TEST (paint_patt_repeat, argc, argv);
//PERFORM_TEST (paint_patt_repeat_scalled, argc, argv);
//PERFORM_TEST (paint_patt_pad, argc, argv);
PERFORM_TEST (test, argc, argv);

return 0;

}
`

@jpbruyere
Copy link
Owner

Trying with compilation option -DVKVG_PREMULT_ALPHA=false (disabling alpha pre-multiplication, the white border disappears.
The original image has color information (blue) when alpha is going down to 0, so it has not pre-multiplied alpha pixels encoded.
vkvg image loading has no alpha premult handling for now, so provided image must have pixels already encoded with pre mult alpha.

It would surely be a good improvement to provide alpha premult handling for image loading api.

The quick solution for now is to load an image with pre multiplied alpha encoding.

@jpbruyere
Copy link
Owner

Usually, when image has alpha-premultiplied pixels, there is a loss of information. So when writing images to disk, it is better to have it without alpha premultiplication.

@jpbruyere
Copy link
Owner

@ #156 task created.

@173619070 173619070 reopened this Mar 1, 2025
@173619070
Copy link
Author

I want to draw multiple pictures. I've enabled the VK_BLEND_FACTOR_SRC_ALPHA & VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA. These pictures together form an image. Later, I want to draw some text or circles at certain positions on this image. However, after I finish drawing the text, the image part disappears. How can I achieve the desired effect using your functions?

VkPipelineColorBlendAttachmentState blendAttachmentState =
{ .colorWriteMask = 0x0, .blendEnable = VK_TRUE,
  .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
  .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
  .colorBlendOp = VK_BLEND_OP_ADD,
  .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
  .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
  .alphaBlendOp = VK_BLEND_OP_ADD,
.....................

}

//--------------------------------------
const char* imgPath = "E:/vkvg/tests/data/miroir.png";
const char* imgPath2 = "E:/vkvg/tests/data/filled.png";

void paint () {

VkvgSurface surface = vkvg_surface_create(device, 800, 600);
VkvgContext ctx = vkvg_create(surface);


VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgSurface imgSurf2 = vkvg_surface_create_from_image(device, imgPath2);
vkvg_set_operator(ctx, VKVG_OPERATOR_OVER);

vkvg_set_source_surface(ctx, imgSurf, 0, 0);
vkvg_paint(ctx);
vkvg_set_operator(ctx, VKVG_OPERATOR_OVER);
//vkvg_set_source_rgba(ctx, 1.0, 1.0, 1.0, 0.4);
vkvg_set_source_surface(ctx, imgSurf2, 40, 40);
vkvg_paint(ctx);

vkvg_arc(ctx, 200, 200, 41.f, 0, M_PIF * 2);
vkvg_fill_preserve(ctx);


//vkvg_set_source_rgba(ctx, 1.0f, 0.0f, 0.0f, 0.9f);
//vkvg_set_line_width(ctx, 1);

vkvg_surface_destroy(imgSurf);
vkvg_surface_destroy(imgSurf2);




vkvg_destroy(ctx);

}

@173619070
Copy link
Author

Below is my OpenGL code. I want to migrate the functionality of this code to Vulkan.
`
#ifndef GLWINDOW_H
#define GLWINDOW_H

#include
#include
#include
#include
#include
#include
#include

class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
GLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {

}

protected:
void initializeGL() override {
initializeOpenGLFunctions();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    // 编译着色器程序
    shaderProgram = new QOpenGLShaderProgram(this);
    shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex_shader.glsl");
    shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment_shader.glsl");
    shaderProgram->link();

    // 加载纹理
    textures[0] = new QOpenGLTexture(QImage(":/images/borderCOMFORT_L.png").mirrored());
    textures[1] = new QOpenGLTexture(QImage(":/images/apertureCOMFORT_L.png").mirrored());

    textures[3] = new QOpenGLTexture(QImage(":/images/speedCOMFORT.png").mirrored());
    textures[2] = new QOpenGLTexture(QImage(":/images/speedBarCOMFORT.png").mirrored());


    // 获取图片的原始大小
    for (int i = 0; i < 4; ++i) {
        textures[i]->setMinificationFilter(QOpenGLTexture::Linear);
        textures[i]->setMagnificationFilter(QOpenGLTexture::Linear);
        textures[i]->generateMipMaps();
        textures[i]->create();
        imageSizes[i] = QSize(textures[i]->width(),textures[i]->height());
    }

    // 设置顶点数据和纹理坐标
    setupVertices();
}

void resizeGL(int w, int h) override {
    glViewport(0, 0, w, h);
}

void paintGL() override {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    // 使用着色器程序
    shaderProgram->bind();

    // 设置正交投影矩阵
    QMatrix4x4 projection;
    projection.ortho(0, 1920, 0, 720, -1, 1); // 从左到右:0 -> 1920,从下到上:720 -> 0
    shaderProgram->setUniformValue("projection", projection);

    // 绘制四张图片
    for (int i = 0; i < 4; ++i) {
        textures[i]->bind(i); // 绑定纹理
        shaderProgram->setUniformValue("texture1", i); // 设置纹理单元

        // 绑定 VAO 并绘制
        glBindVertexArray(VAOs[i]);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        glBindVertexArray(0);
    }

    shaderProgram->release();
}

private:
void setupVertices() {
// 图片的绘制起点
QVector2D positions[4] = {
QVector2D(98, 37), // 图片1
QVector2D(119, 33), // 图片2

        QVector2D(144, 65),   // 图片4
        QVector2D(151, 50)  // 图片3speedCOMFORT
    };

    // 创建 VAO 和 VBO
    glGenVertexArrays(4, VAOs);
    glGenBuffers(4, VBOs);

    for (int i = 0; i < 4; ++i) {
        // 计算图片的绘制大小(保持原始比例)
        float aspectRatio = static_cast<float>(imageSizes[i].width()) / imageSizes[i].height();
        float width = imageSizes[i].width();
        float height = imageSizes[i].height();

        // 如果图片宽度超过窗口宽度,按比例缩小
        if (positions[i].x() + width > 1920) {
            width = 1920 - positions[i].x();
            height = width / aspectRatio;
        }

        // 如果图片高度超过窗口高度,按比例缩小
        if (positions[i].y() + height > 720) {
            height = 720 - positions[i].y();
            width = height * aspectRatio;
        }

        // 顶点数据:四个顶点(位置 + 纹理坐标)
        GLfloat vertices[16] = {
            positions[i].x(), positions[i].y(), 0.0f, 0.0f,
            positions[i].x() + width, positions[i].y(), 1.0f, 0.0f,
            positions[i].x(), positions[i].y() + height, 0.0f, 1.0f,
            positions[i].x() + width, positions[i].y() + height, 1.0f, 1.0f
        };

        glBindVertexArray(VAOs[i]);

        glBindBuffer(GL_ARRAY_BUFFER, VBOs[i]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

        // 设置顶点属性指针
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
        glEnableVertexAttribArray(0);

        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
        glEnableVertexAttribArray(1);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }
}

QOpenGLShaderProgram *shaderProgram;
QOpenGLTexture *textures[4];
QSize imageSizes[4]; // 图片的原始大小
GLuint VAOs[4], VBOs[4];

};

#endif // `

@jpbruyere
Copy link
Owner

jpbruyere commented Mar 1, 2025

I'm deeply focused on another project of mine, sorry but I will take some time to reply, I will try to give you the big picture before Tuesday.
The basic principle is that once the vkvg_context is destroyed, the surface is guaranteed to have the expected drawings ready. Also, vkvg Surfaces may be created from existing one.
On the vulkan side, the clearing of the target surface is done through vulkan renderpass configuration and starting. Be aware also that when working with multi-buffered swapchain, drawings are not copied from one image to the others, so you have to consider the swapchain image as a blanc one each time you acquire one.
I'll come back to you when I have the time to review the actual implementation in vkvg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants