[shotwell] Convert color transformations to C



commit 1d7b21c77dad55845734b28425f31bb0023bd791
Author: Jens Georg <mail jensge org>
Date:   Sun Nov 5 18:59:23 2017 +0100

    Convert color transformations to C
    
    GCC has massive issues to optimize away the struct assignments Vala
    generates. clang seems to be fine with them, but we cannot require a C
    compiler.

 pipeline.am                  |    3 +-
 src/ColorTransformation.vala |  201 +++++-------------------------------------
 src/_transformation.c        |  180 +++++++++++++++++++++++++++++++++++++
 src/meson.build              |    3 +-
 4 files changed, 205 insertions(+), 182 deletions(-)
---
diff --git a/pipeline.am b/pipeline.am
index 670bff7..9f9969d 100644
--- a/pipeline.am
+++ b/pipeline.am
@@ -6,7 +6,8 @@ noinst_LTLIBRARIES += \
 
 src_libgraphics_processor_la_SOURCES = \
        src/ColorTransformation.vala \
-       src/util/KeyValueMap.vala
+       src/util/KeyValueMap.vala \
+       src/_transformation.c
 
 src_libgraphics_processor_la_VALAFLAGS = \
        $(COMMON_VALAFLAGS) \
diff --git a/src/ColorTransformation.vala b/src/ColorTransformation.vala
index b934443..a9f26c5 100644
--- a/src/ColorTransformation.vala
+++ b/src/ColorTransformation.vala
@@ -91,112 +91,9 @@ public struct HSVAnalyticPixel {
         this.light_value = rgb_lookup_table[light_value_quantized];
     }
 
-    public HSVAnalyticPixel.from_rgb(RGBAnalyticPixel p) {
-        float max_component = float.max(float.max(p.red, p.green), p.blue);
-        float min_component = float.min(float.min(p.red, p.green), p.blue);
+    public extern HSVAnalyticPixel.from_rgb(RGBAnalyticPixel p);
 
-        light_value = max_component;
-        var delta = max_component - min_component;
-        saturation = (max_component != 0.0f) ? ((delta) / max_component) : 0.0f;
-
-        if (saturation == 0.0f) {
-            hue = 0.0f; /* hue is undefined in the zero saturation case */
-        } else {
-            if (p.red == max_component) {
-                hue = (p.green - p.blue) / delta;
-            } else if (p.green == max_component) {
-                hue = 2.0f + ((p.blue - p.red) / delta);
-            } else if (p.blue == max_component) {
-                hue = 4.0f + ((p.red - p.green) / delta);
-            }
-
-            hue *= 60.0f;
-            if (hue < 0.0f)
-                hue += 360.0f;
-
-            hue /= 360.0f; /* normalize hue */
-        }
-
-        hue = hue.clamp(0.0f, 1.0f);
-        saturation = saturation.clamp(0.0f, 1.0f);
-        light_value = light_value.clamp(0.0f, 1.0f);
-    }
-
-    public RGBAnalyticPixel to_rgb() {
-        RGBAnalyticPixel result = RGBAnalyticPixel();
-
-        if (saturation == 0.0f) {
-            result.red = light_value;
-            result.green = light_value;
-            result.blue = light_value;
-        } else {
-            float hue_denorm = hue * 360.0f;
-            if (hue_denorm == 360.0f)
-                hue_denorm = 0.0f;
-
-            float hue_hexant = hue_denorm / 60.0f;
-
-            int hexant_i_part = (int) hue_hexant;
-
-            float hexant_f_part = hue_hexant - ((float) hexant_i_part);
-
-            /* the p, q, and t quantities from section 13.3 of Foley, et. al. */
-            float p = light_value * (1.0f - saturation);
-            float q = light_value * (1.0f - (saturation * hexant_f_part));
-            float t = light_value * (1.0f - (saturation * (1.0f - hexant_f_part)));
-            switch (hexant_i_part) {
-                /* the (r, g, b) components of the output pixel are computed
-                   from the light_value, p, q, and t quantities differently
-                   depending on which "hexant" (1/6 of a full rotation) of the
-                   HSV color cone the hue lies in. For example, if the hue lies
-                   in the yellow hexant, the dominant channels in the output
-                   are red and green, so we map relatively more of the light_value
-                   into these colors than if, say, the hue were to lie in the
-                   cyan hexant. See chapter 13 of Foley, et. al. for more
-                   information. */
-                case 0:
-                    result.red = light_value;
-                    result.green = t;
-                    result.blue = p;
-                break;
-
-                case 1:
-                    result.red = q;
-                    result.green = light_value;
-                    result.blue = p;
-                break;
-
-                case 2:
-                    result.red = p;
-                    result.green = light_value;
-                    result.blue = t;
-                break;
-
-                case 3:
-                    result.red = p;
-                    result.green = q;
-                    result.blue = light_value;
-                break;
-
-                case 4:
-                    result.red = t;
-                    result.green = p;
-                    result.blue = light_value;
-                break;
-
-                case 5:
-                    result.red = light_value;
-                    result.green = p;
-                    result.blue = q;
-                break;
-
-                default:
-                    error("bad color hexant in HSV-to-RGB conversion");
-            }
-        }
-
-        return result;
-    }
+    public extern RGBAnalyticPixel to_rgb();
 
     public bool equals(ref HSVAnalyticPixel rhs) {
         return ((hue == rhs.hue) && (saturation == rhs.saturation) &&
@@ -559,27 +456,7 @@ public class RGBTransformation : PixelTransformation {
         return (transform_pixel_rgb(p.to_rgb())).to_hsv();
     }
 
-    public override RGBAnalyticPixel transform_pixel_rgb(RGBAnalyticPixel p) {
-        float red_out = (p.red * matrix_entries[0]) +
-            (p.green * matrix_entries[1]) +
-            (p.blue * matrix_entries[2]) +
-            matrix_entries[3];
-        red_out = red_out.clamp(0.0f, 1.0f);
-
-        float green_out = (p.red * matrix_entries[4]) +
-            (p.green * matrix_entries[5]) +
-            (p.blue * matrix_entries[6]) +
-            matrix_entries[7];
-        green_out = green_out.clamp(0.0f, 1.0f);
-
-        float blue_out = (p.red * matrix_entries[8]) +
-            (p.green * matrix_entries[9]) +
-            (p.blue * matrix_entries[10]) +
-            matrix_entries[11];
-        blue_out = blue_out.clamp(0.0f, 1.0f);
-        
-        return RGBAnalyticPixel.from_components(red_out, green_out, blue_out);
-    }
+    public extern override RGBAnalyticPixel transform_pixel_rgb(RGBAnalyticPixel p);
 
     public override bool is_identity() {
         return identity;
@@ -625,6 +502,18 @@ public abstract class HSVTransformation : PixelTransformation {
             this.remap_table[i] = hsv_trans.remap_table[idx].clamp (0.0f, 1.0f);
         }
     }
+
+    public override HSVAnalyticPixel transform_pixel_hsv(HSVAnalyticPixel pixel) {
+        int remap_index = (int)(pixel.light_value * 255.0f);
+
+        HSVAnalyticPixel result = pixel;
+        result.light_value = remap_table[remap_index];
+
+        result.light_value = result.light_value.clamp(0.0f, 1.0f);
+
+        return result;
+    }
+
 }
 
 public class TintTransformation : RGBTransformation {
@@ -793,9 +682,9 @@ public class ContrastTransformation : RGBTransformation {
 public class PixelTransformer {
     private Gee.ArrayList<PixelTransformation> transformations =
         new Gee.ArrayList<PixelTransformation>();
-    private PixelTransformation[] optimized_transformations = null;
-    private int optimized_slots_used = 0;
-    
+    public PixelTransformation[] optimized_transformations = null;
+    public int optimized_slots_used = 0;
+
     public PixelTransformer() {
     }
     
@@ -835,33 +724,7 @@ public class PixelTransformer {
         }
     }
     
-    private RGBAnalyticPixel apply_transformations(RGBAnalyticPixel p) {
-        PixelFormat current_format = PixelFormat.RGB;
-        RGBAnalyticPixel p_rgb = p;
-        HSVAnalyticPixel p_hsv = HSVAnalyticPixel();
-
-        for (int i = 0; i < optimized_slots_used; i++) {
-            PixelTransformation trans = optimized_transformations[i];
-            if (trans.get_preferred_format() == PixelFormat.RGB) {
-                if (current_format == PixelFormat.HSV) {
-                    p_rgb = p_hsv.to_rgb();
-                    current_format = PixelFormat.RGB;
-                }
-                p_rgb = trans.transform_pixel_rgb(p_rgb);
-            } else {
-                if (current_format == PixelFormat.RGB) {
-                    p_hsv = p_rgb.to_hsv();
-                    current_format = PixelFormat.HSV;
-                }
-                p_hsv = trans.transform_pixel_hsv(p_hsv);
-            }
-        }
-
-        if (current_format == PixelFormat.HSV)
-            p_rgb = p_hsv.to_rgb();
-
-        return p_rgb;
-    }
+    private extern RGBAnalyticPixel apply_transformations(RGBAnalyticPixel p);
 
     /* NOTE: this method allows the same transformation to be added multiple
              times. There's nothing wrong with this behavior as of today,
@@ -971,6 +834,7 @@ public class PixelTransformer {
             }
         }
     }
+
 }
 
 public class RGBHistogram {
@@ -1347,17 +1211,6 @@ public class ExpansionTransformation : HSVTransformation {
             remap_table[i] = 1.0f;
     }
 
-    public override HSVAnalyticPixel transform_pixel_hsv(HSVAnalyticPixel pixel) {
-        int remap_index = (int)(pixel.light_value * 255.0f);
-
-        HSVAnalyticPixel result = pixel;
-        result.light_value = remap_table[remap_index];
-
-        result.light_value = result.light_value.clamp(0.0f, 1.0f);
-
-        return result;
-    }
-
     public override string to_string() {
         return "{ %d, %d }".printf(low_kink, high_kink);
     }
@@ -1407,12 +1260,6 @@ public class ShadowDetailTransformation : HSVTransformation {
         }
     }
 
-    public override HSVAnalyticPixel transform_pixel_hsv(HSVAnalyticPixel pixel) {
-        HSVAnalyticPixel result = pixel;
-        result.light_value = (remap_table[(int)(pixel.light_value * 255.0f)]).clamp(0.0f, 1.0f);
-        return result;
-    }
-
     public override PixelTransformation copy() {
         return new ShadowDetailTransformation(intensity);
     }
@@ -1479,12 +1326,6 @@ public class HighlightDetailTransformation : HSVTransformation {
         }
     }
 
-    public override HSVAnalyticPixel transform_pixel_hsv(HSVAnalyticPixel pixel) {
-        HSVAnalyticPixel result = pixel;
-        result.light_value = (remap_table[(int)(pixel.light_value * 255.0f)]).clamp(0.0f, 1.0f);
-        return result;
-    }
-
     public override PixelTransformation copy() {
         return new HighlightDetailTransformation(intensity);
     }
@@ -1570,7 +1411,7 @@ public PixelTransformationBundle create_auto_enhance_adjustments(Gdk.Pixbuf pixb
 }
 }
 
-const float rgb_lookup_table[] = {
+public const float rgb_lookup_table[] = {
       0.0f/255.0f,   1.0f/255.0f,   2.0f/255.0f,   3.0f/255.0f,   4.0f/255.0f,
       5.0f/255.0f,   6.0f/255.0f,   7.0f/255.0f,   8.0f/255.0f,   9.0f/255.0f,
      10.0f/255.0f,  11.0f/255.0f,  12.0f/255.0f,  13.0f/255.0f,  14.0f/255.0f,
diff --git a/src/_transformation.c b/src/_transformation.c
new file mode 100644
index 0000000..d3bd760
--- /dev/null
+++ b/src/_transformation.c
@@ -0,0 +1,180 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2017 Jens Georg <mail jensge org>
+ *
+ * This software is licensed under the GNU LGPL (version 2.1 or later).
+ * See the COPYING file in this distribution.
+ */
+
+#include "shotwell-graphics-processor.h"
+
+inline void _pixel_transformer_apply_transformations (PixelTransformer* self, RGBAnalyticPixel* p, 
RGBAnalyticPixel* result) {
+    PixelFormat current_format = PIXEL_FORMAT_RGB;
+    RGBAnalyticPixel p_rgb = {p->red, p->green, p->blue };
+    HSVAnalyticPixel p_hsv = {0.0f, 0.0f, 0.0f};
+    gint i = 0;
+
+    for (i = 0; i < self->optimized_slots_used; i++) {
+        PixelTransformation* trans = NULL;
+        PixelFormat preferred_format;
+
+        trans = self->optimized_transformations[i];
+        preferred_format = pixel_transformation_get_preferred_format (trans);
+        if (preferred_format == PIXEL_FORMAT_RGB) {
+            RGBAnalyticPixel _tmp14_ = {0};
+            if (current_format == PIXEL_FORMAT_HSV) {
+                hsv_analytic_pixel_to_rgb (&p_hsv, &p_rgb);
+                current_format = PIXEL_FORMAT_RGB;
+            }
+            pixel_transformation_transform_pixel_rgb (trans, &p_rgb, &_tmp14_);
+            p_rgb.red =_tmp14_.red;
+            p_rgb.green =_tmp14_.green;
+            p_rgb.blue =_tmp14_.blue;
+        } else {
+            HSVAnalyticPixel _tmp19_ = {0};
+            if (current_format == PIXEL_FORMAT_RGB) {
+                rgb_analytic_pixel_to_hsv (&p_rgb, &p_hsv);
+                current_format = PIXEL_FORMAT_HSV;
+            }
+            pixel_transformation_transform_pixel_hsv (trans, &p_hsv, &_tmp19_);
+            p_hsv.hue = _tmp19_.hue;
+            p_hsv.saturation = _tmp19_.saturation;
+            p_hsv.light_value = _tmp19_.light_value;
+        }
+    }
+
+    if (current_format == PIXEL_FORMAT_HSV) {
+        hsv_analytic_pixel_to_rgb (&p_hsv, &p_rgb);
+    }
+
+    result->red = p_rgb.red;
+    result->green = p_rgb.green;
+    result->blue = p_rgb.blue;
+}
+
+void pixel_transformer_apply_transformations (PixelTransformer* self, RGBAnalyticPixel* p, RGBAnalyticPixel* 
result) {
+    _pixel_transformer_apply_transformations (self, p, result);
+}
+
+void pixel_transformer_apply_transformation (PixelTransformer* self,
+                                             guint row,
+                                             gint rowstride,
+                                             gint rowbytes,
+                                             gint n_channels,
+                                             guchar* source_pixels, int source_pixels_length1,
+                                             guchar* dest_pixels, int dest_pixels_length1) {
+    guint row_start_index = row * rowstride;
+    guint row_end_index = row_start_index + rowbytes;
+    guint i = 0;
+
+    for (i = row_start_index; i < row_end_index; i += n_channels) {
+        RGBAnalyticPixel current_pixel = { rgb_lookup_table[source_pixels[i]],
+                                           rgb_lookup_table[source_pixels[i+1]],
+                                           rgb_lookup_table[source_pixels[i+2]] };
+        RGBAnalyticPixel transformed_pixel = { 0.0f, 0.0f, 0.0f };
+        _pixel_transformer_apply_transformations (self, &current_pixel, &transformed_pixel);
+        dest_pixels[i] = (guchar) (transformed_pixel.red * 255.0f);
+        dest_pixels[i+1] = (guchar) (transformed_pixel.green * 255.0f);
+        dest_pixels[i+2] = (guchar) (transformed_pixel.blue * 255.0f);
+    }
+}
+
+void hsv_analytic_pixel_to_rgb (HSVAnalyticPixel *self, RGBAnalyticPixel* result) {
+    if (self->saturation == 0.0f) {
+        result->red = self->light_value;
+        result->green = self->light_value;
+        result->blue = self->light_value;
+
+        return;
+    }
+
+    float hue_denorm = self->hue * 360.0f;
+    if (hue_denorm == 360.0f)
+        hue_denorm = 0.0f;
+
+    float hue_hexant = hue_denorm / 60.0f;
+    int hexant_i_part = (int) hue_hexant;
+    float hexant_f_part = hue_hexant - ((float) hexant_i_part);
+
+    float p = self->light_value * (1.0f - self->saturation);
+    float q = self->light_value * (1.0f - (self->saturation * hexant_f_part));
+    float t = self->light_value * (1.0f - (self->saturation * (1.0f - hexant_f_part)));
+
+    switch (hexant_i_part) {
+        case 0:
+            result->red = self->light_value; result->green = t; result->blue = p;
+        break;
+        case 1:
+            result->red = q; result->green = self->light_value; result->blue = p;
+        break;
+        case 2:
+            result->red = p; result->green = self->light_value; result->blue = t;
+        break;
+        case 3:
+            result->red = p; result->green = q; result->blue = self->light_value;
+        break;
+        case 4:
+            result->red = t; result->green = p; result->blue = self->light_value;
+        break;
+        case 5:
+            result->red = self->light_value; result->green = p; result->blue = q;
+        break;
+        default:
+            g_assert_not_reached();
+    }
+}
+
+void hsv_analytic_pixel_init_from_rgb (HSVAnalyticPixel *self, RGBAnalyticPixel* p) {
+    gfloat max_component = MAX(MAX(p->red, p->green), p->blue);
+    gfloat min_component = MIN(MIN(p->red, p->green), p->blue);
+
+    self->light_value = max_component;
+    gfloat delta = max_component - min_component;
+    self->saturation = (max_component != 0.0f) ? ((delta) / max_component) : 0.0f;
+    if (self->saturation == 0.0f) {
+        self->hue = 0.0f;
+
+        return;
+    }
+
+    if (p->red == max_component) {
+        self->hue = (p->green - p->blue) / delta;
+    } else if (p->green == max_component) {
+        self->hue = 2.0f + ((p->blue - p->red) / delta);
+    } else if (p->blue == max_component) {
+        self->hue = 4.0f + ((p->red - p->green) / delta);
+    }
+
+    self->hue *= 60.0f;
+    if (self->hue < 0.0f) {
+        self->hue += 360.0f;
+    }
+
+    self->hue /= 360.0f;
+    self->hue = CLAMP(self->hue, 0.0f, 1.0f);
+    self->saturation = CLAMP(self->saturation, 0.0f, 1.0f);
+    self->light_value = CLAMP(self->light_value, 0.0f, 1.0f);
+}
+
+void rgb_transformation_real_transform_pixel_rgb (PixelTransformation* base, RGBAnalyticPixel* p, 
RGBAnalyticPixel* result) {
+    RGBTransformation *self = RGB_TRANSFORMATION(base);
+    result->red = CLAMP(p->red * self->matrix_entries[0] +
+                        p->green * self->matrix_entries[1] +
+                        p->blue * self->matrix_entries[2] +
+                                  self->matrix_entries[3], 0.0f, 1.0f);
+    result->green = CLAMP(p->red * self->matrix_entries[4] +
+                          p->green * self->matrix_entries[5] +
+                          p->blue * self->matrix_entries[6] +
+                                    self->matrix_entries[7], 0.0f, 1.0f);
+    result->blue = CLAMP(p->red * self->matrix_entries[8] +
+                         p->green * self->matrix_entries[9] +
+                         p->blue * self->matrix_entries[10] +
+                                   self->matrix_entries[11], 0.0f, 1.0f);
+}
+
+
+void hsv_transformation_real_transform_pixel_hsv (PixelTransformation* base, HSVAnalyticPixel* pixel, 
HSVAnalyticPixel* result) {
+    HSVTransformation *self = HSV_TRANSFORMATION(base);
+    result->hue = pixel->hue;
+    result->saturation = pixel->saturation;
+    result->light_value = CLAMP(self->remap_table[(int) (pixel->light_value * 255.0f)], 0.0f, 1.0f);
+}
diff --git a/src/meson.build b/src/meson.build
index d42a153..d094a1f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -2,7 +2,8 @@ subdir('plugins')
 
 sw_graphics_processor = static_library('shotwell-graphics-processor',
                ['ColorTransformation.vala',
-                'util/KeyValueMap.vala'],
+                'util/KeyValueMap.vala',
+                '_transformation.c'],
                vala_header : 'shotwell-graphics-processor.h',
                vala_vapi : 'shotwell-graphics-processor.vapi',
                dependencies : [gio, gee, gdk],


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]