memononen / nanovg

Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.
zlib License
5.13k stars 768 forks source link

Paint Color for Image Pattern in nvgFillPaint #450

Open xeekworx opened 6 years ago

xeekworx commented 6 years ago

I needed to modulate the color of an image to be a color of my choice and couldn't find a way to do it with nanovg. For example I have an image of white text that when rendered I want to be any color I choose (eg. green). I would have though setting nvgFillColor would have done the job, but it changes my paint to a solid fill without the image pattern. So then after reading a lot of code I modified the innerColor field of my NVGpaint object with the color I wanted and it worked. There just isn't a way to do this with nvg calls that I'm aware of.

In summary: I would propose adding a NVGcolor parameter to nvgImagePattern or maybe nvgFillPaint so that I could achieve some color modulation. For the time being I'm modifying the innerColor field of my NVGpaint object.

xeekworx commented 6 years ago

The reason using nvgFillColor fails to change the color of my image pattern is because of nvg__setPaintColor

static void nvg__setPaintColor(NVGpaint* p, NVGcolor color)
{
    memset(p, 0, sizeof(*p)); // The image is no longer set after this
        // ...
xeekworx commented 6 years ago

I would offer a pull request but I'm unsure what the author really wants here. If I were to guess, for consistency with other parts of the api in painting, then nvgImagePattern needs an additional parameter for the color. For example the nvgXxxGradient calls have color parameters.

xeekworx commented 6 years ago

Adding another parameter would break older projects using nanovg (I must have been thinking in C++ with defaults). To avoid that either a new function would be added (eg. nvgImagePatternWithColor) or nvgFillColor could be made to just modify the inner color of NVGpaint.

xeekworx commented 6 years ago

You can see I'm doing it here by cheating a bit. I achieve color modulation by setting the paint_img.innerColor:

void renderer_nanovg::on_draw_image(
    uintptr_t source_img,
    const int32_t source_x, const int32_t source_y,
    const int32_t source_w, const int32_t source_h,
    const int32_t x, const int32_t y,
    const int32_t width, const int32_t height,
    const color& modulation,
    const rotation direction
) const
{
    if (m_nvgcontext) {
        NVGcontext * vg = reinterpret_cast<NVGcontext *>(m_nvgcontext);

        float angle = 0.f;
        int destX = x, destY = y;
        switch (direction) {
        case rotation::right90degrees:
            destX += height;
            angle = (90.f * NVG_PI) / 180; // Radians
            break;
        case rotation::left90degrees:
            destY += width;
            angle = (-90.f * NVG_PI) / 180; // Radians
            break;
        }

        nvgSave(vg);
        nvgTranslate(vg, (float)destX, (float)destY);

        float ax = (float)width / (float)source_w;
        float ay = (float)height / (float)source_h;
        int original_w, original_h;
        nvgImageSize(vg, static_cast<int>(source_img), &original_w, &original_h);
        NVGpaint paint_img = nvgImagePattern(
            vg, (float)(-source_x * ax), (float)(-source_y * ay), (float)original_w*ax, (float)original_h*ay, 0.f, static_cast<int>(source_img), 1.f
        );

        nvgRotate(vg, angle);

        nvgBeginPath(vg);
        nvgRect(vg, 0.f, 0.f, (float)width, (float)height);
        paint_img.innerColor = nvgRGBA(
            modulation.r,
            modulation.g,
            modulation.b,
            modulation.a);
        nvgFillPaint(vg, paint_img);
        nvgFill(vg);

        nvgRestore(vg);
    }
}