3fd577309241ac458ac14e9cb0f8d7dacbbb5fc8
1 /*
2 * feTurbulence filter primitive renderer
3 *
4 * Authors:
5 * World Wide Web Consortium <http://www.w3.org/>
6 * Felipe Corrêa da Silva Sanches <felipe.sanches@gmail.com>
7 *
8 * This file has a considerable amount of code adapted from
9 * the W3C SVG filter specs, available at:
10 * http://www.w3.org/TR/SVG11/filters.html#feTurbulence
11 *
12 * W3C original code is licensed under the terms of
13 * the (GPL compatible) W3C® SOFTWARE NOTICE AND LICENSE:
14 * http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
15 *
16 * Copyright (C) 2007 authors
17 * Released under GNU GPL, read the file 'COPYING' for more information
18 */
20 #include "display/nr-arena-item.h"
21 #include "display/nr-filter.h"
22 #include "display/nr-filter-turbulence.h"
23 #include "display/nr-filter-units.h"
24 #include "display/nr-filter-utils.h"
25 #include "libnr/nr-rect-l.h"
26 #include "libnr/nr-blit.h"
27 #include <math.h>
29 namespace NR {
31 FilterTurbulence::FilterTurbulence()
32 : XbaseFrequency(0),
33 YbaseFrequency(0),
34 numOctaves(1),
35 seed(0),
36 updated(false),
37 updated_area(IPoint(), IPoint()),
38 pix(NULL),
39 fTileWidth(10), //guessed
40 fTileHeight(10), //guessed
41 fTileX(1), //guessed
42 fTileY(1) //guessed
43 {
44 }
46 FilterPrimitive * FilterTurbulence::create() {
47 return new FilterTurbulence();
48 }
50 FilterTurbulence::~FilterTurbulence()
51 {
52 if (pix) {
53 nr_pixblock_release(pix);
54 delete pix;
55 }
56 }
58 void FilterTurbulence::set_baseFrequency(int axis, double freq){
59 if (axis==0) XbaseFrequency=freq;
60 if (axis==1) YbaseFrequency=freq;
61 }
63 void FilterTurbulence::set_numOctaves(int num){
64 numOctaves=num;
65 }
67 void FilterTurbulence::set_seed(double s){
68 seed=s;
69 }
71 void FilterTurbulence::set_stitchTiles(bool st){
72 stitchTiles=st;
73 }
75 void FilterTurbulence::set_type(FilterTurbulenceType t){
76 type=t;
77 }
79 void FilterTurbulence::set_updated(bool u){
80 updated=u;
81 }
83 void FilterTurbulence::render_area(NRPixBlock *pix, IRect &full_area, FilterUnits const &units) {
84 const int bbox_x0 = full_area.min()[X];
85 const int bbox_y0 = full_area.min()[Y];
86 const int bbox_x1 = full_area.max()[X];
87 const int bbox_y1 = full_area.max()[Y];
89 Matrix unit_trans = units.get_matrix_primitiveunits2pb().inverse();
91 double point[2];
93 unsigned char *pb = NR_PIXBLOCK_PX(pix);
95 if (type==TURBULENCE_TURBULENCE){
96 for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){
97 int out_line = (y - pix->area.y0) * pix->rs;
98 point[1] = y * unit_trans[3] + unit_trans[5];
99 for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){
100 int out_pos = out_line + 4 * (x - pix->area.x0);
101 point[0] = x * unit_trans[0] + unit_trans[4];
102 pb[out_pos] = CLAMP_D_TO_U8( turbulence(0,point)*255 );
103 pb[out_pos + 1] = CLAMP_D_TO_U8( turbulence(1,point)*255 );
104 pb[out_pos + 2] = CLAMP_D_TO_U8( turbulence(2,point)*255 );
105 pb[out_pos + 3] = CLAMP_D_TO_U8( turbulence(3,point)*255 );
106 }
107 }
108 } else {
109 for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){
110 int out_line = (y - pix->area.y0) * pix->rs;
111 point[1] = y * unit_trans[3] + unit_trans[5];
112 for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){
113 int out_pos = out_line + 4 * (x - pix->area.x0);
114 point[0] = x * unit_trans[0] + unit_trans[4];
115 pb[out_pos] = CLAMP_D_TO_U8( ((turbulence(0,point)*255) +255)/2 );
116 pb[out_pos + 1] = CLAMP_D_TO_U8( ((turbulence(1,point)*255)+255)/2 );
117 pb[out_pos + 2] = CLAMP_D_TO_U8( ((turbulence(2,point)*255) +255)/2 );
118 pb[out_pos + 3] = CLAMP_D_TO_U8( ((turbulence(3,point)*255) +255)/2 );
119 }
120 }
121 }
123 pix->empty = FALSE;
124 }
126 void FilterTurbulence::update_pixbuffer(IRect &area, FilterUnits const &units) {
127 int bbox_x0 = area.min()[X];
128 int bbox_y0 = area.min()[Y];
129 int bbox_x1 = area.max()[X];
130 int bbox_y1 = area.max()[Y];
132 TurbulenceInit((long)seed);
134 if (!pix){
135 pix = new NRPixBlock;
136 nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
137 }
138 else if (bbox_x0 != pix->area.x0 || bbox_y0 != pix->area.y0 ||
139 bbox_x1 != pix->area.x1 || bbox_y1 != pix->area.y1)
140 {
141 /* TODO: release-setup cycle not actually needed, if pixblock
142 * width and height don't change */
143 nr_pixblock_release(pix);
144 nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
145 }
147 /* This limits pre-rendered turbulence to two megapixels. This is
148 * arbitary limit and could be something other, too.
149 * If bigger area is needed, visible area is rendered on demand. */
150 if (!pix || (pix->size != NR_PIXBLOCK_SIZE_TINY && pix->data.px == NULL) ||
151 ((bbox_x1 - bbox_x0) * (bbox_y1 - bbox_y0) > 2*1024*1024)) {
152 pix_data = NULL;
153 return;
154 }
156 render_area(pix, area, units);
158 pix_data = NR_PIXBLOCK_PX(pix);
160 updated=true;
161 updated_area = area;
162 }
164 int FilterTurbulence::render(FilterSlot &slot, FilterUnits const &units) {
165 IRect area = units.get_pixblock_filterarea_paraller();
166 // TODO: could be faster - updated_area only has to be same size as area
167 if (!updated || updated_area != area) update_pixbuffer(area, units);
169 NRPixBlock *in = slot.get(_input);
170 if (!in) {
171 g_warning("Missing source image for feTurbulence (in=%d)", _input);
172 return 1;
173 }
175 NRPixBlock *out = new NRPixBlock;
176 int x0 = in->area.x0, y0 = in->area.y0;
177 int x1 = in->area.x1, y1 = in->area.y1;
178 nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, x0, y0, x1, y1, true);
180 if (pix_data) {
181 /* If pre-rendered output of whole filter area exists, just copy it. */
182 nr_blit_pixblock_pixblock(out, pix);
183 } else {
184 /* No pre-rendered output, render the required area here. */
185 render_area(out, area, units);
186 }
188 out->empty = FALSE;
189 slot.set(_output, out);
190 return 0;
191 }
193 long FilterTurbulence::Turbulence_setup_seed(long lSeed)
194 {
195 if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1;
196 if (lSeed > RAND_m - 1) lSeed = RAND_m - 1;
197 return lSeed;
198 }
200 long FilterTurbulence::TurbulenceRandom(long lSeed)
201 {
202 long result;
203 result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q);
204 if (result <= 0) result += RAND_m;
205 return result;
206 }
208 void FilterTurbulence::TurbulenceInit(long lSeed)
209 {
210 double s;
211 int i, j, k;
212 lSeed = Turbulence_setup_seed(lSeed);
213 for(k = 0; k < 4; k++)
214 {
215 for(i = 0; i < BSize; i++)
216 {
217 uLatticeSelector[i] = i;
218 for (j = 0; j < 2; j++)
219 fGradient[k][i][j] = (double)(((lSeed = TurbulenceRandom(lSeed)) % (BSize + BSize)) - BSize) / BSize;
220 s = double(sqrt(fGradient[k][i][0] * fGradient[k][i][0] + fGradient[k][i][1] * fGradient[k][i][1]));
221 fGradient[k][i][0] /= s;
222 fGradient[k][i][1] /= s;
223 }
224 }
225 while(--i)
226 {
227 k = uLatticeSelector[i];
228 uLatticeSelector[i] = uLatticeSelector[j = (lSeed = TurbulenceRandom(lSeed)) % BSize];
229 uLatticeSelector[j] = k;
230 }
231 for(i = 0; i < BSize + 2; i++)
232 {
233 uLatticeSelector[BSize + i] = uLatticeSelector[i];
234 for(k = 0; k < 4; k++)
235 for(j = 0; j < 2; j++)
236 fGradient[k][BSize + i][j] = fGradient[k][i][j];
237 }
238 }
240 double FilterTurbulence::TurbulenceNoise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo)
241 {
242 int bx0, bx1, by0, by1, b00, b10, b01, b11;
243 double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
244 int i, j;
245 t = vec[0] + PerlinN;
246 bx0 = (int)t;
247 bx1 = bx0+1;
248 rx0 = t - (int)t;
249 rx1 = rx0 - 1.0f;
250 t = vec[1] + PerlinN;
251 by0 = (int)t;
252 by1 = by0+1;
253 ry0 = t - (int)t;
254 ry1 = ry0 - 1.0f;
255 // If stitching, adjust lattice points accordingly.
256 if(pStitchInfo != NULL)
257 {
258 if(bx0 >= pStitchInfo->nWrapX)
259 bx0 -= pStitchInfo->nWidth;
260 if(bx1 >= pStitchInfo->nWrapX)
261 bx1 -= pStitchInfo->nWidth;
262 if(by0 >= pStitchInfo->nWrapY)
263 by0 -= pStitchInfo->nHeight;
264 if(by1 >= pStitchInfo->nWrapY)
265 by1 -= pStitchInfo->nHeight;
266 }
267 bx0 &= BM;
268 bx1 &= BM;
269 by0 &= BM;
270 by1 &= BM;
271 i = uLatticeSelector[bx0];
272 j = uLatticeSelector[bx1];
273 b00 = uLatticeSelector[i + by0];
274 b10 = uLatticeSelector[j + by0];
275 b01 = uLatticeSelector[i + by1];
276 b11 = uLatticeSelector[j + by1];
277 sx = double(s_curve(rx0));
278 sy = double(s_curve(ry0));
279 q = fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1];
280 q = fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1];
281 a = turb_lerp(sx, u, v);
282 q = fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1];
283 q = fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1];
284 b = turb_lerp(sx, u, v);
285 return turb_lerp(sy, a, b);
286 }
288 double FilterTurbulence::turbulence(int nColorChannel, double *point)
289 {
290 StitchInfo stitch;
291 StitchInfo *pStitchInfo = NULL; // Not stitching when NULL.
292 // Adjust the base frequencies if necessary for stitching.
293 if(stitchTiles)
294 {
295 // When stitching tiled turbulence, the frequencies must be adjusted
296 // so that the tile borders will be continuous.
297 if(XbaseFrequency != 0.0)
298 {
299 double fLoFreq = double(floor(fTileWidth * XbaseFrequency)) / fTileWidth;
300 double fHiFreq = double(ceil(fTileWidth * XbaseFrequency)) / fTileWidth;
301 if(XbaseFrequency / fLoFreq < fHiFreq / XbaseFrequency)
302 XbaseFrequency = fLoFreq;
303 else
304 XbaseFrequency = fHiFreq;
305 }
306 if(YbaseFrequency != 0.0)
307 {
308 double fLoFreq = double(floor(fTileHeight * YbaseFrequency)) / fTileHeight;
309 double fHiFreq = double(ceil(fTileHeight * YbaseFrequency)) / fTileHeight;
310 if(YbaseFrequency / fLoFreq < fHiFreq / YbaseFrequency)
311 YbaseFrequency = fLoFreq;
312 else
313 YbaseFrequency = fHiFreq;
314 }
315 // Set up TurbulenceInitial stitch values.
316 pStitchInfo = &stitch;
317 stitch.nWidth = int(fTileWidth * XbaseFrequency + 0.5f);
318 stitch.nWrapX = int(fTileX * XbaseFrequency + PerlinN + stitch.nWidth);
319 stitch.nHeight = int(fTileHeight * YbaseFrequency + 0.5f);
320 stitch.nWrapY = int(fTileY * YbaseFrequency + PerlinN + stitch.nHeight);
321 }
322 double fSum = 0.0f;
323 double vec[2];
324 vec[0] = point[0] * XbaseFrequency;
325 vec[1] = point[1] * YbaseFrequency;
326 double ratio = 1;
327 for(int nOctave = 0; nOctave < numOctaves; nOctave++)
328 {
329 if(type==TURBULENCE_FRACTALNOISE)
330 fSum += double(TurbulenceNoise2(nColorChannel, vec, pStitchInfo) / ratio);
331 else
332 fSum += double(fabs(TurbulenceNoise2(nColorChannel, vec, pStitchInfo)) / ratio);
333 vec[0] *= 2;
334 vec[1] *= 2;
335 ratio *= 2;
336 if(pStitchInfo != NULL)
337 {
338 // Update stitch values. Subtracting PerlinN before the multiplication and
339 // adding it afterward simplifies to subtracting it once.
340 stitch.nWidth *= 2;
341 stitch.nWrapX = 2 * stitch.nWrapX - PerlinN;
342 stitch.nHeight *= 2;
343 stitch.nWrapY = 2 * stitch.nWrapY - PerlinN;
344 }
345 }
346 return fSum;
347 }
349 FilterTraits FilterTurbulence::get_input_traits() {
350 return TRAIT_PARALLER;
351 }
353 } /* namespace NR */
355 /*
356 Local Variables:
357 mode:c++
358 c-file-style:"stroustrup"
359 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
360 indent-tabs-mode:nil
361 fill-column:99
362 End:
363 */
364 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :