paulnicholson-acrylic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ begin
2
+ require 'yaml'
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |s|
5
+ s.name = "acrylic"
6
+ # s.executables = "acrylic"
7
+ s.summary = "Photoshop for cool people."
8
+ # s.email = ""
9
+ s.homepage = "http://github.com/dotjerky/acrylic"
10
+ s.description = "A set of image manipulation tools built on top of Cairo"
11
+ s.authors = ["Austin Taylor", "Paul Nicholson"]
12
+ s.files = FileList["[A-Z]*", "{bin,lib,test}/**/*", "ext/**/*.{c,rb}"]
13
+ s.autorequire = "acrylic"
14
+ s.extensions = FileList["ext/**/extconf.rb"]
15
+ s.add_dependency('activesupport')
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
20
+
21
+ require 'rake'
22
+ require 'rake/testtask'
23
+ require 'rake/rdoctask'
24
+
25
+ Rake::TestTask.new(:default) do |t|
26
+ t.libs << "test"
27
+ t.pattern = 'test/**/*_test.rb'
28
+ end
data/TODO ADDED
@@ -0,0 +1,19 @@
1
+ drawing:
2
+ - width controls (calligraphy)
3
+ - mirroring
4
+
5
+ surface:
6
+ - bump maps
7
+ - clouds (fractal-controlled noise)
8
+ - noise
9
+ - squiggle (how do you do this?)
10
+ - brushed metal (horizontally blurred noise?)
11
+ - wood (is this worth trying to generate? squiggled brushed metal?)
12
+ - leather (squiggled and blurred hexagons?)
13
+
14
+ factoring:
15
+ - tools for image compositing/slicing (capture_image, etc)
16
+ - layer stuff
17
+ - permutations (variations)
18
+ - better syntax for parameter variations
19
+ - better manipulation of paths (rounding, etc)
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 1
3
+ :patch: 0
4
+ :major: 0
@@ -0,0 +1,11 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ have_header 'stdlib.h'
5
+ have_header 'ruby.h'
6
+ have_header 'intern.h'
7
+ have_library 'cairo'
8
+ find_header 'cairo.h', "#{Config::expand(CONFIG['includedir'])}/cairo"
9
+ find_header 'rb_cairo.h', Config::expand(CONFIG['sitearchdir'])
10
+
11
+ create_makefile('native_image_surface_extensions')
@@ -0,0 +1,344 @@
1
+ #include "ruby.h"
2
+ #include <stdlib.h>
3
+ #include <cairo.h>
4
+ #include <rb_cairo.h>
5
+ #include <intern.h>
6
+
7
+ int zero[1] = {1};
8
+ int one[3] = {1, 2, 1};
9
+ int two[5] = {1, 4, 6, 4, 1};
10
+ int three[7] = {1, 6, 15, 20, 15, 6, 1};
11
+ int four[9] = {1, 8, 28, 56, 70, 56, 28, 8, 1};
12
+ int five[11] = {1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1};
13
+ int six[13] = {1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1};
14
+ int seven[15] = {1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1};
15
+ int eight[17] = {1, 16, 120, 560, 1820, 4368, 8008, 11440, 12870, 11440, 8008, 4368, 1820, 560, 120, 16, 1};
16
+ int nine[19] = {1, 18, 153, 816, 3060, 8568, 18564, 31824, 43758, 48620, 43758, 31824, 18564, 8568, 3060, 816, 153, 18, 1};
17
+ int ten[21] = {1, 20, 190, 1140, 4845, 15504, 38760, 77520, 125970, 167960, 184756, 167960, 125970, 77520, 38760, 15504, 4845, 1140, 190, 20, 1};
18
+ int *triangle[11] = {zero, one, two, three, four, five, six, seven, eight, nine, ten};
19
+ int sums[11] = {1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576};
20
+
21
+ // cr.blur(radius)
22
+ static VALUE method_blur(VALUE self, VALUE _radius) {
23
+ int radius = FIX2INT(_radius);
24
+
25
+ cairo_surface_t *surface = RVAL2CRSURFACE(self);
26
+ unsigned char *data = cairo_image_surface_get_data(surface);
27
+ int width = cairo_image_surface_get_width(surface);
28
+ int height = cairo_image_surface_get_height(surface);
29
+ int stride = cairo_image_surface_get_stride(surface);
30
+ int length = height*stride;
31
+
32
+ cairo_surface_t *tmp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
33
+ unsigned char *tmp_data = cairo_image_surface_get_data(tmp_surface);
34
+
35
+ unsigned char *color;
36
+ double sumr, sumg, sumb, suma;
37
+ int gauss_w = radius*2 + 1;
38
+ int *mask = triangle[radius];
39
+ int gauss_sum = sums[radius];
40
+ int i, j, k, x, y;
41
+ for (i = 0; i < height; i++) {
42
+ for (j = 0; j < width; j++) {
43
+ sumr = sumg = sumb = suma = 0;
44
+ for (k = 0; k < gauss_w; k++) {
45
+ y = i-radius+k;
46
+ if (y > height || y < 0) continue;
47
+ color = data + y*stride + j*4;
48
+ if (color < data || color > data + length) continue;
49
+ suma += color[0]*mask[k];
50
+ sumr += color[1]*mask[k];
51
+ sumg += color[2]*mask[k];
52
+ sumb += color[3]*mask[k];
53
+ }
54
+ color = tmp_data + i*stride + j*4;
55
+ color[0] = suma/gauss_sum;
56
+ color[1] = sumr/gauss_sum;
57
+ color[2] = sumg/gauss_sum;
58
+ color[3] = sumb/gauss_sum;
59
+ }
60
+ }
61
+ for (i = 0; i < height; i++) {
62
+ for (j = 0; j < width; j++) {
63
+ sumr = sumg = sumb = suma = 0;
64
+ for (k = 0; k < gauss_w; k++) {
65
+ int x = j-radius+k;
66
+ if (x > width || x < 0) continue;
67
+ color = tmp_data + i*stride + x*4;
68
+ if (color < tmp_data || color > tmp_data + length) continue;
69
+ suma += color[0]*mask[k];
70
+ sumr += color[1]*mask[k];
71
+ sumg += color[2]*mask[k];
72
+ sumb += color[3]*mask[k];
73
+ }
74
+ color = data + i*stride + j*4;
75
+ color[0] = suma/gauss_sum;
76
+ color[1] = sumr/gauss_sum;
77
+ color[2] = sumg/gauss_sum;
78
+ color[3] = sumb/gauss_sum;
79
+ }
80
+ }
81
+ return Qnil;
82
+ }
83
+
84
+ // cr.vertical_blur(radius)
85
+ static VALUE method_vertical_blur(VALUE self, VALUE _radius) {
86
+ int radius = FIX2INT(_radius);
87
+
88
+ cairo_surface_t *surface = RVAL2CRSURFACE(self);
89
+ unsigned char *data = cairo_image_surface_get_data(surface);
90
+ int width = cairo_image_surface_get_width(surface);
91
+ int height = cairo_image_surface_get_height(surface);
92
+ int stride = cairo_image_surface_get_stride(surface);
93
+ int length = height*stride;
94
+
95
+ cairo_surface_t *tmp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
96
+ unsigned char *tmp_data = cairo_image_surface_get_data(tmp_surface);
97
+
98
+ unsigned char *color;
99
+ unsigned char *color2;
100
+ double sumr, sumg, sumb, suma;
101
+ int gauss_w = radius*2 + 1;
102
+ int *mask = triangle[radius];
103
+ int gauss_sum = sums[radius];
104
+ int i, j, k, x, y;
105
+ for (i = 0; i < height; i++) {
106
+ for (j = 0; j < width; j++) {
107
+ color = data + i*stride + j*4;
108
+ if (color < data || color > data + length) continue;
109
+ color2 = tmp_data + i*stride + j*4;
110
+ color2[0] = color[0];
111
+ color2[1] = color[1];
112
+ color2[2] = color[2];
113
+ color2[3] = color[3];
114
+ }
115
+ }
116
+ for (i = 0; i < height; i++) {
117
+ for (j = 0; j < width; j++) {
118
+ sumr = sumg = sumb = suma = 0;
119
+ for (k = 0; k < gauss_w; k++) {
120
+ y = i-radius+k;
121
+ if (y > height || y < 0) continue;
122
+ color = data + y*stride + j*4;
123
+ if (color < data || color > data + length) continue;
124
+ suma += color[0]*mask[k];
125
+ sumr += color[1]*mask[k];
126
+ sumg += color[2]*mask[k];
127
+ sumb += color[3]*mask[k];
128
+ }
129
+ color = data + i*stride + j*4;
130
+ color[0] = suma/gauss_sum;
131
+ color[1] = sumr/gauss_sum;
132
+ color[2] = sumg/gauss_sum;
133
+ color[3] = sumb/gauss_sum;
134
+ }
135
+ }
136
+ return Qnil;
137
+ }
138
+
139
+ // cr.horizontal_blur(radius)
140
+ static VALUE method_horizontal_blur(VALUE self, VALUE _radius) {
141
+ int radius = FIX2INT(_radius);
142
+
143
+ cairo_surface_t *surface = RVAL2CRSURFACE(self);
144
+ unsigned char *data = cairo_image_surface_get_data(surface);
145
+ int width = cairo_image_surface_get_width(surface);
146
+ int height = cairo_image_surface_get_height(surface);
147
+ int stride = cairo_image_surface_get_stride(surface);
148
+ int length = height*stride;
149
+
150
+ cairo_surface_t *tmp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
151
+ unsigned char *tmp_data = cairo_image_surface_get_data(tmp_surface);
152
+
153
+ unsigned char *color;
154
+ unsigned char *color2;
155
+ double sumr, sumg, sumb, suma;
156
+ int gauss_w = radius*2 + 1;
157
+ int *mask = triangle[radius];
158
+ int gauss_sum = sums[radius];
159
+ int i, j, k, x, y;
160
+ for (i = 0; i < height; i++) {
161
+ for (j = 0; j < width; j++) {
162
+ color = data + i*stride + j*4;
163
+ if (color < data || color > data + length) continue;
164
+ color2 = tmp_data + i*stride + j*4;
165
+ color2[0] = color[0];
166
+ color2[1] = color[1];
167
+ color2[2] = color[2];
168
+ color2[3] = color[3];
169
+ }
170
+ }
171
+ for (i = 0; i < height; i++) {
172
+ for (j = 0; j < width; j++) {
173
+ sumr = sumg = sumb = suma = 0;
174
+ for (k = 0; k < gauss_w; k++) {
175
+ int x = j-radius+k;
176
+ if (x > width || x < 0) continue;
177
+ color = tmp_data + i*stride + x*4;
178
+ if (color < tmp_data || color > tmp_data + length) continue;
179
+ suma += color[0]*mask[k];
180
+ sumr += color[1]*mask[k];
181
+ sumg += color[2]*mask[k];
182
+ sumb += color[3]*mask[k];
183
+ }
184
+ color = data + i*stride + j*4;
185
+ color[0] = suma/gauss_sum;
186
+ color[1] = sumr/gauss_sum;
187
+ color[2] = sumg/gauss_sum;
188
+ color[3] = sumb/gauss_sum;
189
+ }
190
+ }
191
+ return Qnil;
192
+ }
193
+
194
+ int abs(int x) {
195
+ return x < 0 ? -x : x;
196
+ }
197
+
198
+ // cr.bump_map(height_map, light_x, light_y, light_radius, specular_radius, normal_coefficient=1.0)
199
+ static VALUE method_bump_map(int argc, VALUE *argv, VALUE self) {
200
+ VALUE height_map;
201
+ VALUE _light_x;
202
+ VALUE _light_y;
203
+ VALUE _light_radius;
204
+ VALUE _specular_radius;
205
+ VALUE _normal_coefficient;
206
+
207
+ rb_scan_args(argc, argv, "51", &height_map, &_light_x, &_light_y, &_light_radius, &_specular_radius, &_normal_coefficient);
208
+ if(NIL_P(_normal_coefficient)) _normal_coefficient = DBL2NUM(1.0);
209
+
210
+ int light_x = FIX2INT(rb_Integer(_light_x));
211
+ int light_y = FIX2INT(rb_Integer(_light_y));
212
+ int light_radius = FIX2INT(rb_Integer(_light_radius));
213
+ int specular_radius = FIX2INT(rb_Integer(_specular_radius));
214
+ double normal_coefficient = NUM2DBL(rb_Float(_normal_coefficient));
215
+
216
+ cairo_surface_t *surface = RVAL2CRSURFACE(self);
217
+ unsigned int *data = (unsigned int *) cairo_image_surface_get_data(surface);
218
+ int width = cairo_image_surface_get_width(surface);
219
+ int height = cairo_image_surface_get_height(surface);
220
+ int stride = cairo_image_surface_get_stride(surface);
221
+ int length = height * stride;
222
+
223
+ cairo_surface_t *height_surface = RVAL2CRSURFACE(height_map);
224
+ unsigned char *height_data = cairo_image_surface_get_data(height_surface);
225
+
226
+ int x, y;
227
+ for (y = 0; y < height; y++) {
228
+ for (x = 0; x < width; x++) {
229
+ int xnext = y * stride + (x + 1) * 4 - 1;
230
+ int xprev = y * stride + (x - 1) * 4 - 1;
231
+ int xn = (xnext > length || xprev < 0) ? 0 : (height_data[xnext] - height_data[xprev]);
232
+ int ynext = (y + 1) * stride + x * 4 - 1;
233
+ int yprev = (y - 1) * stride + x * 4 - 1;
234
+ int yn = (ynext > length || yprev < 0) ? 0 : (height_data[ynext] - height_data[yprev]);
235
+ int ex = abs(xn*normal_coefficient - x + light_x);
236
+ int ey = abs(yn*normal_coefficient - y + light_y);
237
+
238
+ if (ex > light_radius - 1) ex = light_radius - 1;
239
+ if (ey > light_radius - 1) ey = light_radius - 1;
240
+
241
+ unsigned int color;
242
+ int magnitude = (int) sqrtf((float)ex*ex+ey*ey);
243
+ if (magnitude < specular_radius) {
244
+ int alpha = 255 - magnitude*255/specular_radius;
245
+ if (alpha > 255) alpha = 255;
246
+ if (alpha < 0) alpha = 0;
247
+ color = (alpha << 24) + (alpha << 16) + (alpha << 8) + alpha;
248
+ } else {
249
+ int alpha = (magnitude - specular_radius)*255/(light_radius - specular_radius);
250
+ if (alpha > 255) alpha = 255;
251
+ if (alpha < 0) alpha = 0;
252
+ color = 0xFF000000 & (alpha << 24);
253
+ }
254
+ data[y * width + x] = color;
255
+ }
256
+ }
257
+ return Qnil;
258
+ }
259
+
260
+ // cr.downsample(scale)
261
+ static VALUE method_downsample(VALUE self, VALUE _scale) {
262
+ int scale = FIX2INT(_scale);
263
+
264
+ cairo_surface_t *surface = RVAL2CRSURFACE(self);
265
+ unsigned int *data = (unsigned int *) cairo_image_surface_get_data(surface);
266
+ int width = cairo_image_surface_get_width(surface);
267
+ int height = cairo_image_surface_get_height(surface);
268
+
269
+ int w = width / scale;
270
+ int h = height / scale;
271
+ cairo_surface_t *output_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
272
+ unsigned int *output_data = (unsigned int *) cairo_image_surface_get_data(output_surface);
273
+ int denominator = scale * scale;
274
+
275
+ unsigned char * pixel;
276
+ int i, j, k, l;
277
+ int r, g, b, a;
278
+
279
+ for (i=0; i<w+1; i++) {
280
+ for (j=0; j<h+1; j++) {
281
+ r = g = b = a = 0;
282
+ for (k=0; k<scale; k++) {
283
+ for (l=0; l<scale; l++) {
284
+ pixel = (unsigned char *) (data + (j*scale + l)*width + i*scale + k);
285
+ if (pixel < (unsigned char *) data || pixel > (unsigned char *) (data + width * height)) continue;
286
+ r += pixel[0];
287
+ g += pixel[1];
288
+ b += pixel[2];
289
+ a += pixel[3];
290
+ }
291
+ }
292
+ pixel = (unsigned char *) (output_data + j*w + i);
293
+ if (pixel < (unsigned char *) output_data || pixel > (unsigned char *) (output_data + h*w)) continue;
294
+ pixel[0] = r/denominator;
295
+ pixel[1] = g/denominator;
296
+ pixel[2] = b/denominator;
297
+ pixel[3] = a/denominator;
298
+ }
299
+ }
300
+ return (CRSURFACE2RVAL(output_surface));
301
+ }
302
+
303
+ // cr.render_noise
304
+ static VALUE method_render_noise(VALUE self) {
305
+ cairo_surface_t *surface = RVAL2CRSURFACE(self);
306
+ unsigned char *data = cairo_image_surface_get_data(surface);
307
+ int height = cairo_image_surface_get_height(surface);
308
+ int stride = cairo_image_surface_get_stride(surface);
309
+ int length = height * stride;
310
+
311
+ unsigned char * pixel;
312
+ unsigned char x;
313
+ for (pixel = data; pixel < data + length; pixel++) {
314
+ pixel[0] = (unsigned char) rand() % 255;
315
+ }
316
+ return Qnil;
317
+ }
318
+
319
+ // cr.get_values(x, y)
320
+ static VALUE method_get_values(VALUE self, VALUE _x, VALUE _y) {
321
+ int x = FIX2INT(_x);
322
+ int y = FIX2INT(_y);
323
+
324
+ cairo_surface_t *surface = RVAL2CRSURFACE(self);
325
+ unsigned char *data = cairo_image_surface_get_data(surface);
326
+ int width = cairo_image_surface_get_width(surface);
327
+ int height = cairo_image_surface_get_height(surface);
328
+ if (x > width || y > height || x < 0 || y < 0) return Qnil;
329
+ unsigned int *pixel = (unsigned int *) (data + (y*width + x)*4);
330
+ return UINT2NUM(*pixel);
331
+ }
332
+
333
+ void Init_native_image_surface_extensions() {
334
+ VALUE cKlass = rb_cObject;
335
+ cKlass = rb_const_get(cKlass,rb_intern("Cairo"));
336
+ cKlass = rb_const_get(cKlass,rb_intern("ImageSurface"));
337
+ rb_define_method(cKlass, "blur", (VALUE(*)(ANYARGS))method_blur, 1);
338
+ rb_define_method(cKlass, "vertical_blur", (VALUE(*)(ANYARGS))method_vertical_blur, 1);
339
+ rb_define_method(cKlass, "horizontal_blur", (VALUE(*)(ANYARGS))method_horizontal_blur, 1);
340
+ rb_define_method(cKlass, "bump_map", (VALUE(*)(ANYARGS))method_bump_map, 5);
341
+ rb_define_method(cKlass, "downsample", (VALUE(*)(ANYARGS))method_downsample, 1);
342
+ rb_define_method(cKlass, "render_noise", (VALUE(*)(ANYARGS))method_render_noise, 0);
343
+ rb_define_method(cKlass, "get_values", (VALUE(*)(ANYARGS))method_get_values, 2);
344
+ }
data/lib/acrylic.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'cairo'
4
+ require 'image_surface_extensions'
5
+
6
+ module Acrylic; end
@@ -0,0 +1,121 @@
1
+ require 'generator'
2
+ class BorderTools < Generator
3
+ attr_accessor :size
4
+
5
+ def self.preview(image=:border, *args)
6
+ super
7
+ end
8
+
9
+ def self.suite(prefix, *args)
10
+ super
11
+ self.new.write_css(prefix)
12
+ end
13
+
14
+ def draw(image, *args)
15
+ @args = args
16
+ super
17
+ end
18
+
19
+ def set_source
20
+ unless @source
21
+ draw_border(*(@args || []))
22
+ @border = @surface
23
+ @source = Cairo::SurfacePattern.new(@border)
24
+ end
25
+ end
26
+
27
+ def sass(class_name)
28
+ %{
29
+ table.#{class_name}
30
+ border-collapse: collapse
31
+ padding: 0
32
+ margin: 0
33
+ .#{class_name}
34
+ &.tl, &.tr, &.bl, &.br, &.tc, &.bc
35
+ height: #{size}px !important
36
+ padding: 0 !important
37
+ border: 0
38
+ margin: 0
39
+ &.tl, &.bl, &.tr, &.br, &.cl, &.cr
40
+ width: #{size}px !important
41
+ padding: 0 !important
42
+ border: 0
43
+ margin: 0
44
+ &.tl
45
+ background: url(/images/#{class_name}_tl.png)
46
+ &.tr
47
+ background: url(/images/#{class_name}_tr.png)
48
+ &.bl
49
+ background: url(/images/#{class_name}_bl.png)
50
+ &.br
51
+ background: url(/images/#{class_name}_br.png)
52
+ &.cl
53
+ background: url(/images/#{class_name}_cl.png)
54
+ &.tc
55
+ background: url(/images/#{class_name}_tc.png)
56
+ &.cr
57
+ background: url(/images/#{class_name}_cr.png)
58
+ &.bc
59
+ background: url(/images/#{class_name}_bc.png)
60
+ &.content
61
+ background-color: #{background_color.to_css}
62
+ }
63
+ end
64
+
65
+ def write_css(name)
66
+ set_source
67
+ path = File.join(File.dirname(__FILE__), "../public/stylesheets/sass/#{name}.sass")
68
+ File.open(path, 'w') {|f| f << sass(name)}
69
+ end
70
+
71
+ def background_color
72
+ set_source
73
+ @border.get_pixel(@border.width/2, @border.height/2)
74
+ end
75
+
76
+ def self.inherited(c)
77
+ c.image :tl do
78
+ slice(size, size, 0, 0)
79
+ end
80
+
81
+ c.image :tc do
82
+ slice(1, size, size + 1, 0)
83
+ end
84
+
85
+ c.image :tr do
86
+ slice(size, size, @border.width - size, 0)
87
+ end
88
+
89
+ c.image :cl do
90
+ slice(size, 1, 0, size + 1)
91
+ end
92
+
93
+ c.image :cr do
94
+ slice(size, 1, @border.width - size, size + 1)
95
+ end
96
+
97
+ c.image :bl do
98
+ slice(size, size, 0, @border.height - size)
99
+ end
100
+
101
+ c.image :bc do
102
+ slice(1, size, size + 1, @border.height - size)
103
+ end
104
+
105
+ c.image :br do
106
+ slice(size, size, @border.width - size, @border.height - size)
107
+ end
108
+
109
+ c.image :border, :suite => false do
110
+ draw_border
111
+ end
112
+ end
113
+
114
+ def slice(sizeX, sizeY, offsetX, offsetY)
115
+ set_source
116
+ dimensions sizeX, sizeY
117
+ cr.set_source(@source)
118
+ cr.source.matrix = Cairo::Matrix.identity.translate(offsetX, offsetY)
119
+ cr.paint
120
+ end
121
+ end