2
* feTurbulence filter primitive renderer
5
* World Wide Web Consortium <http://www.w3.org/>
6
* Felipe Corrêa da Silva Sanches <felipe.sanches@gmail.com>
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
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
16
* Copyright (C) 2007 authors
17
* Released under GNU GPL version 2 (or later), read the file 'COPYING' for more information
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"
32
FilterTurbulence::FilterTurbulence()
38
updated_area(NR::IPoint(), NR::IPoint()),
40
fTileWidth(10), //guessed
41
fTileHeight(10), //guessed
47
FilterPrimitive * FilterTurbulence::create() {
48
return new FilterTurbulence();
51
FilterTurbulence::~FilterTurbulence()
54
nr_pixblock_release(pix);
59
void FilterTurbulence::set_baseFrequency(int axis, double freq){
60
if (axis==0) XbaseFrequency=freq;
61
if (axis==1) YbaseFrequency=freq;
64
void FilterTurbulence::set_numOctaves(int num){
68
void FilterTurbulence::set_seed(double s){
72
void FilterTurbulence::set_stitchTiles(bool st){
76
void FilterTurbulence::set_type(FilterTurbulenceType t){
80
void FilterTurbulence::set_updated(bool u){
84
void FilterTurbulence::render_area(NRPixBlock *pix, NR::IRect &full_area, FilterUnits const &units) {
85
const int bbox_x0 = full_area.min()[NR::X];
86
const int bbox_y0 = full_area.min()[NR::Y];
87
const int bbox_x1 = full_area.max()[NR::X];
88
const int bbox_y1 = full_area.max()[NR::Y];
90
Geom::Matrix unit_trans = units.get_matrix_primitiveunits2pb().inverse();
94
unsigned char *pb = NR_PIXBLOCK_PX(pix);
96
if (type==TURBULENCE_TURBULENCE){
97
for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){
98
int out_line = (y - pix->area.y0) * pix->rs;
99
point[1] = y * unit_trans[3] + unit_trans[5];
100
for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){
101
int out_pos = out_line + 4 * (x - pix->area.x0);
102
point[0] = x * unit_trans[0] + unit_trans[4];
103
pb[out_pos] = CLAMP_D_TO_U8( turbulence(0,point)*255 ); // CLAMP includes rounding!
104
pb[out_pos + 1] = CLAMP_D_TO_U8( turbulence(1,point)*255 );
105
pb[out_pos + 2] = CLAMP_D_TO_U8( turbulence(2,point)*255 );
106
pb[out_pos + 3] = CLAMP_D_TO_U8( turbulence(3,point)*255 );
110
for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){
111
int out_line = (y - pix->area.y0) * pix->rs;
112
point[1] = y * unit_trans[3] + unit_trans[5];
113
for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){
114
int out_pos = out_line + 4 * (x - pix->area.x0);
115
point[0] = x * unit_trans[0] + unit_trans[4];
116
pb[out_pos] = CLAMP_D_TO_U8( ((turbulence(0,point)*255) +255)/2 );
117
pb[out_pos + 1] = CLAMP_D_TO_U8( ((turbulence(1,point)*255)+255)/2 );
118
pb[out_pos + 2] = CLAMP_D_TO_U8( ((turbulence(2,point)*255) +255)/2 );
119
pb[out_pos + 3] = CLAMP_D_TO_U8( ((turbulence(3,point)*255) +255)/2 );
127
void FilterTurbulence::update_pixbuffer(NR::IRect &area, FilterUnits const &units) {
128
int bbox_x0 = area.min()[NR::X];
129
int bbox_y0 = area.min()[NR::Y];
130
int bbox_x1 = area.max()[NR::X];
131
int bbox_y1 = area.max()[NR::Y];
133
TurbulenceInit((long)seed);
136
pix = new NRPixBlock;
137
nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
139
else if (bbox_x0 != pix->area.x0 || bbox_y0 != pix->area.y0 ||
140
bbox_x1 != pix->area.x1 || bbox_y1 != pix->area.y1)
142
/* TODO: release-setup cycle not actually needed, if pixblock
143
* width and height don't change */
144
nr_pixblock_release(pix);
145
nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
148
/* This limits pre-rendered turbulence to two megapixels. This is
149
* arbitary limit and could be something other, too.
150
* If bigger area is needed, visible area is rendered on demand. */
151
if (!pix || (pix->size != NR_PIXBLOCK_SIZE_TINY && pix->data.px == NULL) ||
152
((bbox_x1 - bbox_x0) * (bbox_y1 - bbox_y0) > 2*1024*1024)) {
157
render_area(pix, area, units);
159
pix_data = NR_PIXBLOCK_PX(pix);
165
int FilterTurbulence::render(FilterSlot &slot, FilterUnits const &units) {
166
NR::IRect area = units.get_pixblock_filterarea_paraller();
167
// TODO: could be faster - updated_area only has to be same size as area
168
if (!updated || updated_area != area) update_pixbuffer(area, units);
170
NRPixBlock *in = slot.get(_input);
172
g_warning("Missing source image for feTurbulence (in=%d)", _input);
176
NRPixBlock *out = new NRPixBlock;
177
int x0 = in->area.x0, y0 = in->area.y0;
178
int x1 = in->area.x1, y1 = in->area.y1;
179
nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, x0, y0, x1, y1, true);
182
/* If pre-rendered output of whole filter area exists, just copy it. */
183
nr_blit_pixblock_pixblock(out, pix);
185
/* No pre-rendered output, render the required area here. */
186
render_area(out, area, units);
190
slot.set(_output, out);
194
long FilterTurbulence::Turbulence_setup_seed(long lSeed)
196
if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1;
197
if (lSeed > RAND_m - 1) lSeed = RAND_m - 1;
201
long FilterTurbulence::TurbulenceRandom(long lSeed)
204
result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q);
205
if (result <= 0) result += RAND_m;
209
void FilterTurbulence::TurbulenceInit(long lSeed)
213
lSeed = Turbulence_setup_seed(lSeed);
214
for(k = 0; k < 4; k++)
216
for(i = 0; i < BSize; i++)
218
uLatticeSelector[i] = i;
219
for (j = 0; j < 2; j++)
220
fGradient[k][i][j] = (double)(((lSeed = TurbulenceRandom(lSeed)) % (BSize + BSize)) - BSize) / BSize;
221
s = double(sqrt(fGradient[k][i][0] * fGradient[k][i][0] + fGradient[k][i][1] * fGradient[k][i][1]));
222
fGradient[k][i][0] /= s;
223
fGradient[k][i][1] /= s;
228
k = uLatticeSelector[i];
229
uLatticeSelector[i] = uLatticeSelector[j = (lSeed = TurbulenceRandom(lSeed)) % BSize];
230
uLatticeSelector[j] = k;
232
for(i = 0; i < BSize + 2; i++)
234
uLatticeSelector[BSize + i] = uLatticeSelector[i];
235
for(k = 0; k < 4; k++)
236
for(j = 0; j < 2; j++)
237
fGradient[k][BSize + i][j] = fGradient[k][i][j];
241
double FilterTurbulence::TurbulenceNoise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo)
243
int bx0, bx1, by0, by1, b00, b10, b01, b11;
244
double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
246
t = vec[0] + PerlinN;
251
t = vec[1] + PerlinN;
256
// If stitching, adjust lattice points accordingly.
257
if(pStitchInfo != NULL)
259
if(bx0 >= pStitchInfo->nWrapX)
260
bx0 -= pStitchInfo->nWidth;
261
if(bx1 >= pStitchInfo->nWrapX)
262
bx1 -= pStitchInfo->nWidth;
263
if(by0 >= pStitchInfo->nWrapY)
264
by0 -= pStitchInfo->nHeight;
265
if(by1 >= pStitchInfo->nWrapY)
266
by1 -= pStitchInfo->nHeight;
272
i = uLatticeSelector[bx0];
273
j = uLatticeSelector[bx1];
274
b00 = uLatticeSelector[i + by0];
275
b10 = uLatticeSelector[j + by0];
276
b01 = uLatticeSelector[i + by1];
277
b11 = uLatticeSelector[j + by1];
278
sx = double(s_curve(rx0));
279
sy = double(s_curve(ry0));
280
q = fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1];
281
q = fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1];
282
a = turb_lerp(sx, u, v);
283
q = fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1];
284
q = fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1];
285
b = turb_lerp(sx, u, v);
286
return turb_lerp(sy, a, b);
289
double FilterTurbulence::turbulence(int nColorChannel, double *point)
292
StitchInfo *pStitchInfo = NULL; // Not stitching when NULL.
293
// Adjust the base frequencies if necessary for stitching.
296
// When stitching tiled turbulence, the frequencies must be adjusted
297
// so that the tile borders will be continuous.
298
if(XbaseFrequency != 0.0)
300
double fLoFreq = double(floor(fTileWidth * XbaseFrequency)) / fTileWidth;
301
double fHiFreq = double(ceil(fTileWidth * XbaseFrequency)) / fTileWidth;
302
if(XbaseFrequency / fLoFreq < fHiFreq / XbaseFrequency)
303
XbaseFrequency = fLoFreq;
305
XbaseFrequency = fHiFreq;
307
if(YbaseFrequency != 0.0)
309
double fLoFreq = double(floor(fTileHeight * YbaseFrequency)) / fTileHeight;
310
double fHiFreq = double(ceil(fTileHeight * YbaseFrequency)) / fTileHeight;
311
if(YbaseFrequency / fLoFreq < fHiFreq / YbaseFrequency)
312
YbaseFrequency = fLoFreq;
314
YbaseFrequency = fHiFreq;
316
// Set up TurbulenceInitial stitch values.
317
pStitchInfo = &stitch;
318
stitch.nWidth = int(fTileWidth * XbaseFrequency + 0.5f);
319
stitch.nWrapX = int(fTileX * XbaseFrequency + PerlinN + stitch.nWidth);
320
stitch.nHeight = int(fTileHeight * YbaseFrequency + 0.5f);
321
stitch.nWrapY = int(fTileY * YbaseFrequency + PerlinN + stitch.nHeight);
325
vec[0] = point[0] * XbaseFrequency;
326
vec[1] = point[1] * YbaseFrequency;
328
for(int nOctave = 0; nOctave < numOctaves; nOctave++)
330
if(type==TURBULENCE_FRACTALNOISE)
331
fSum += double(TurbulenceNoise2(nColorChannel, vec, pStitchInfo) / ratio);
333
fSum += double(fabs(TurbulenceNoise2(nColorChannel, vec, pStitchInfo)) / ratio);
337
if(pStitchInfo != NULL)
339
// Update stitch values. Subtracting PerlinN before the multiplication and
340
// adding it afterward simplifies to subtracting it once.
342
stitch.nWrapX = 2 * stitch.nWrapX - PerlinN;
344
stitch.nWrapY = 2 * stitch.nWrapY - PerlinN;
350
FilterTraits FilterTurbulence::get_input_traits() {
351
return TRAIT_PARALLER;
354
} /* namespace Filters */
355
} /* namespace Inkscape */
360
c-file-style:"stroustrup"
361
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
366
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :