Skip to content

Spectrum mixing speed up by direct mixing colours that are close enough #182

@ChengduLittleA

Description

@ChengduLittleA

Here I have a patch:

diff --git a/brushmodes.c b/brushmodes.c
index f0c68b6..eede454 100644
--- a/brushmodes.c
+++ b/brushmodes.c
@@ -70,6 +70,12 @@ void draw_dab_pixels_BlendMode_Normal (uint16_t * mask,
   }
 };
 
+inline int color_direct_mixable(uint16_t r, uint16_t g, uint16_t b, uint16_t* rgba){
+  return ((rgba[0]==r || r/abs(rgba[0]-r)>50)&&
+          (rgba[1]==g || g/abs(rgba[1]-g)>50)&&
+          (rgba[2]==b || b/abs(rgba[2]-b)>50));
+}
+
 void draw_dab_pixels_BlendMode_Normal_Paint (uint16_t * mask,
                                        uint16_t * rgba,
                                        uint16_t color_r,
@@ -91,7 +97,7 @@ void draw_dab_pixels_BlendMode_Normal_Paint (uint16_t * mask,
       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
       // optimization- if background has 0 alpha we can just do normal additive
       // blending since there is nothing to mix with.
-      if (rgba[3] <= 0) {
+      if (rgba[3] <= 0 || opacity>30000 || color_direct_mixable(color_r, color_g, color_b, rgba)) {
         rgba[3] = opa_a + opa_b * rgba[3] / (1<<15);
         rgba[0] = (opa_a*color_r + opa_b*rgba[0])/(1<<15);
         rgba[1] = (opa_a*color_g + opa_b*rgba[1])/(1<<15);
@@ -351,6 +357,16 @@ void draw_dab_pixels_BlendMode_Normal_and_Eraser_Paint (uint16_t * mask,
     for (; mask[0]; mask++, rgba+=4) {
       const uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
       const uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
+
+    if (rgba[3] <= 0 || opacity>30000 || color_direct_mixable(color_r, color_g, color_b, rgba)) {
+      const uint32_t opa_a1 = opa_a * color_a / (1<<15);
+      rgba[3] = opa_a1 + opa_b * rgba[3] / (1<<15);
+      rgba[0] = (opa_a1*color_r + opa_b*rgba[0])/(1<<15);
+      rgba[1] = (opa_a1*color_g + opa_b*rgba[1])/(1<<15);
+      rgba[2] = (opa_a1*color_b + opa_b*rgba[2])/(1<<15);
+      continue;
+    }
+
       const uint32_t opa_a2 = opa_a * color_a / (1<<15); // erase-adjusted alpha
       const uint32_t opa_out = opa_a2 + opa_b * rgba[3] / (1<<15);
 
@@ -458,7 +474,7 @@ void draw_dab_pixels_BlendMode_LockAlpha_Paint (uint16_t * mask,
       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
       opa_a *= rgba[3];
       opa_a /= (1<<15);
-      if (rgba[3] <= 0) {
+      if (rgba[3] <= 0 || opacity>30000 || color_direct_mixable(color_r, color_g, color_b, rgba)) {
         rgba[0] = (opa_a*color_r + opa_b*rgba[0])/(1<<15);
         rgba[1] = (opa_a*color_g + opa_b*rgba[1])/(1<<15);
         rgba[2] = (opa_a*color_b + opa_b*rgba[2])/(1<<15);

Because most brushes are dabbed repeatedly around the same spot, so there are a lot overlapping, so if colours are close enough, we don't really need to do spectrum conversion, which greatly speeds up large brushes.

Results are still visually correct:

图片


My original description which I thought it was a bug: (wrong, don't look at it)

Because spectrum mixing is ridiculously slow even with fully opaque brushes, I am looking into the code and found the spectrum switch seems to be controlled by "alpha underneath" instead of "input alpha"? Is this a typo error or am I not understanding the algorithm correctly?

brushmodes.c line 375:

      if (spectral_factor && rgba[3] != 0) { // apparently it should be "color_a!=65535" here?
        // Convert straightened tile pixel color to a spectral
        float spectral_b[10] = {0};
        rgb_to_spectral(
          (float)rgba[0] / rgba[3],
          (float)rgba[1] / rgba[3],
          (float)rgba[2] / rgba[3],
          spectral_b
          );

I'm not sure I understood the code correctly, but I think for opaque brushes there should not be any spectrum mixing? Or if it's a different mindset I'd appreciate a more in-depth explanation.

Thanks and have a great day!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions