Skip to content

Commit de12f44

Browse files
committed
Optimizations
1 parent 6b8bc32 commit de12f44

File tree

5 files changed

+469
-311
lines changed

5 files changed

+469
-311
lines changed

lib/Models/Bin.vala

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ public class He.Bin : Gtk.Widget, Gtk.Buildable {
3232
private bool _content_color_override = false;
3333
private RGBColor? _content_source_color = null;
3434

35+
// Cache for expensive color scheme generation
36+
private RGBColor? _cached_source_color = null;
37+
private bool _cached_is_dark = false;
38+
private double _cached_contrast = -1.0;
39+
40+
// Static cache shared across all Bin instances for identical color configurations
41+
private static Gee.HashMap<string, string>? _css_cache = null;
42+
private static ContentScheme? _shared_content_scheme = null;
43+
private static StyleManager? _shared_style_manager = null;
44+
3545
private Gtk.Widget? _child;
3646
public Gtk.Widget child {
3747
get {
@@ -170,6 +180,7 @@ public class He.Bin : Gtk.Widget, Gtk.Buildable {
170180
if (!_content_color_override || _content_source_color == null) {
171181
remove_color_provider ();
172182
cached_css = null;
183+
_cached_source_color = null;
173184
notify_children_color_changed ();
174185
return;
175186
}
@@ -179,16 +190,30 @@ public class He.Bin : Gtk.Widget, Gtk.Buildable {
179190
bool is_dark = get_is_dark_theme ();
180191
double contrast = get_contrast_level ();
181192

193+
// Check if we can skip regeneration - color, theme, and contrast unchanged
194+
if (_cached_source_color != null &&
195+
colors_are_equal (_cached_source_color, source_argb) &&
196+
_cached_is_dark == is_dark &&
197+
Math.fabs (_cached_contrast - contrast) < 0.001) {
198+
// Nothing changed, skip expensive regeneration
199+
return;
200+
}
201+
182202
string css = build_content_css (source_argb, is_dark, contrast);
183203

184204
if (css == "") {
185205
remove_color_provider ();
186206
cached_css = null;
207+
_cached_source_color = null;
187208
notify_children_color_changed ();
188209
return;
189210
}
190211

191212
if (cached_css != null && cached_css == css) {
213+
// Update cache state even if CSS matches
214+
_cached_source_color = source_argb;
215+
_cached_is_dark = is_dark;
216+
_cached_contrast = contrast;
192217
return;
193218
}
194219

@@ -198,10 +223,21 @@ public class He.Bin : Gtk.Widget, Gtk.Buildable {
198223
apply_color_provider_recursive (this, (!) color_provider);
199224

200225
cached_css = css;
226+
_cached_source_color = source_argb;
227+
_cached_is_dark = is_dark;
228+
_cached_contrast = contrast;
201229
notify_children_color_changed ();
202230
queue_draw ();
203231
}
204232

233+
private bool colors_are_equal (RGBColor? a, RGBColor b) {
234+
if (a == null) return false;
235+
// Compare with small epsilon for floating point
236+
return Math.fabs (a.r - b.r) < 0.001 &&
237+
Math.fabs (a.g - b.g) < 0.001 &&
238+
Math.fabs (a.b - b.b) < 0.001;
239+
}
240+
205241
private void notify_children_color_changed () {
206242
notify_widget_tree_color_changed (this);
207243
}
@@ -288,15 +324,40 @@ public class He.Bin : Gtk.Widget, Gtk.Buildable {
288324
}
289325

290326
private string build_content_css (RGBColor source_color, bool is_dark, double contrast) {
291-
var source_hct = hct_from_int (rgb_to_argb_int (source_color));
327+
// Generate cache key from color + theme + contrast
328+
int argb = rgb_to_argb_int (source_color);
329+
string cache_key = "%d_%s_%.2f".printf (argb, is_dark ? "dark" : "light", contrast);
330+
331+
// Initialize static cache if needed
332+
if (_css_cache == null) {
333+
_css_cache = new Gee.HashMap<string, string> ();
334+
}
292335

293-
// Use ContentScheme to generate a full scheme from the source color
294-
var content_scheme = new ContentScheme ();
295-
var scheme = content_scheme.generate (source_hct, is_dark, contrast);
336+
// Check cache first
337+
if (_css_cache.has_key (cache_key)) {
338+
return _css_cache.get (cache_key);
339+
}
340+
341+
var source_hct = hct_from_int (argb);
342+
343+
// Reuse shared instances to avoid allocation overhead
344+
if (_shared_content_scheme == null) {
345+
_shared_content_scheme = new ContentScheme ();
346+
}
347+
if (_shared_style_manager == null) {
348+
_shared_style_manager = new StyleManager ();
349+
}
350+
351+
var scheme = _shared_content_scheme.generate (source_hct, is_dark, contrast);
352+
string css = _shared_style_manager.style_refresh (scheme);
353+
string result = extract_content_color_definitions (css);
354+
355+
// Cache the result (limit cache size to prevent memory bloat)
356+
if (_css_cache.size < 50) {
357+
_css_cache.set (cache_key, result);
358+
}
296359

297-
var manager = new StyleManager ();
298-
string css = manager.style_refresh (scheme);
299-
return extract_content_color_definitions (css);
360+
return result;
300361
}
301362

302363
private string extract_content_color_definitions (string css) {
@@ -412,4 +473,4 @@ public class He.Bin : Gtk.Widget, Gtk.Buildable {
412473

413474
this.unparent ();
414475
}
415-
}
476+
}

lib/Utils/Ensor/Ensor.vala

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,25 @@
1919
[CCode (gir_namespace = "He", gir_version = "1", cheader_filename = "libhelium-1.h")]
2020
namespace He.Ensor {
2121
// Maximum number of pixel samples to collect for quantization
22-
private const int MAX_SAMPLES = 128;
22+
// Reduced from 128 - fewer samples means faster quantization with minimal quality loss
23+
private const int MAX_SAMPLES = 64;
24+
25+
// Maximum colors for quantization - reduced from 128 for significant speedup
26+
// 32 colors is sufficient for accent extraction since we only need top 4
27+
private const int MAX_QUANTIZE_COLORS = 32;
2328

2429
private GLib.Array<int> accent_from_pixels (uint8[] pixels, bool alpha) {
2530
// First pass: collect filtered pixels
2631
int[] filtered_pixels = pixels_to_argb_array (pixels, alpha);
2732

28-
// Second pass: cluster to 128 colors using quantizer
33+
// Early exit if no valid pixels
34+
if (filtered_pixels.length == 0) {
35+
return new GLib.Array<int> ();
36+
}
37+
38+
// Second pass: cluster to reduced color count using quantizer
2939
var celebi = new He.QuantizerCelebi ();
30-
var result = celebi.quantize (filtered_pixels, 128);
40+
var result = celebi.quantize (filtered_pixels, MAX_QUANTIZE_COLORS);
3141

3242
var score = new He.Score ();
3343
return score.score (result, 4); // We only need 4.
@@ -44,14 +54,16 @@ namespace He.Ensor {
4454
int total_pixels = (int) pixels.length / factor;
4555

4656
// Calculate skip factor to sample approximately MAX_SAMPLES pixels
57+
// Use a larger skip to reduce processing time
4758
int skip = int.max (1, total_pixels / MAX_SAMPLES);
4859

4960
// Pre-calculate expected number of samples for array pre-allocation
50-
// This avoids O(n²) reallocation with dynamic array growth
5161
int expected_samples = (total_pixels + skip - 1) / skip;
5262

53-
// Use GLib.Array with pre-allocated capacity for efficient appending
54-
var list = new GLib.Array<int>.sized (false, false, sizeof (int), expected_samples);
63+
// Pre-allocate result array with estimated size
64+
// Using a simple dynamic array approach with capacity
65+
int[] temp_result = new int[expected_samples];
66+
int result_count = 0;
5567

5668
int i = 0;
5769
while (i < total_pixels) {
@@ -76,32 +88,38 @@ namespace He.Ensor {
7688
}
7789

7890
// Skip very dark pixels (likely shadows/borders)
79-
if (red < 8 && green < 8 && blue < 8) {
91+
// Slightly relaxed threshold for speed
92+
if (red < 10 && green < 10 && blue < 10) {
8093
i += skip;
8194
continue;
8295
}
8396

8497
// Skip very light pixels (likely highlights/glare)
85-
if (red > 247 && green > 247 && blue > 247) {
98+
// Slightly relaxed threshold for speed
99+
if (red > 245 && green > 245 && blue > 245) {
86100
i += skip;
87101
continue;
88102
}
89103

90104
int argb = argb_from_rgb_int (red, green, blue);
91-
list.append_val (argb);
105+
106+
// Append to result
107+
if (result_count < expected_samples) {
108+
temp_result[result_count] = argb;
109+
result_count++;
110+
}
92111

93112
i += skip;
94113
}
95114

96-
// Convert GLib.Array to int[] for the quantizer
97-
// This is a single copy at the end, much better than per-element reallocs
98-
if (list.length == 0) {
115+
// Return only the filled portion
116+
if (result_count == 0) {
99117
return new int[0];
100118
}
101119

102-
int[] result = new int[list.length];
103-
for (uint j = 0; j < list.length; j++) {
104-
result[j] = list.index (j);
120+
int[] result = new int[result_count];
121+
for (int j = 0; j < result_count; j++) {
122+
result[j] = temp_result[j];
105123
}
106124

107125
return result;

0 commit comments

Comments
 (0)