[shotwell] Convert color transformations to C
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell] Convert color transformations to C
- Date: Thu, 9 Nov 2017 13:02:11 +0000 (UTC)
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, ¤t_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]