1 
2 
3 #include "stdfx.h"
4 #include "tfxparam.h"
5 #include "tparamset.h"
6 #include "tparamuiconcept.h"
7 
8 class RadialBlurFx final : public TStandardRasterFx {
9   FX_PLUGIN_DECLARATION(RadialBlurFx)
10 
11   TRasterFxPort m_input;
12   TPointParamP m_point;
13   TDoubleParamP m_radius;
14   TDoubleParamP m_blur;
15 
16 public:
RadialBlurFx()17   RadialBlurFx() : m_point(TPointD(0.0, 0.0)), m_radius(0.0), m_blur(5.0) {
18     m_point->getX()->setMeasureName("fxLength");
19     m_point->getY()->setMeasureName("fxLength");
20     m_radius->setMeasureName("fxLength");
21     bindParam(this, "point", m_point);
22     bindParam(this, "radius", m_radius);
23     bindParam(this, "blur", m_blur);
24     addInputPort("Source", m_input);
25     m_radius->setValueRange(0, (std::numeric_limits<double>::max)());
26     m_blur->setValueRange(0, (std::numeric_limits<double>::max)());
27   }
28 
~RadialBlurFx()29   ~RadialBlurFx(){};
30 
getMaxBraid(const TRectD & bBox,double frame,const TAffine & aff=TAffine ())31   int getMaxBraid(const TRectD &bBox, double frame,
32                   const TAffine &aff = TAffine()) {
33     double scale  = sqrt(fabs(aff.det()));
34     TPointD point = aff * m_point->getValue(frame);
35     double radius = m_radius->getValue(frame) * scale;
36     double blur   = m_blur->getValue(frame);
37 
38     double intensity = blur * M_PI_180;
39 
40     TPointD p1 = bBox.getP00() - point;
41     TPointD p2 = bBox.getP01() - point;
42     TPointD p3 = bBox.getP10() - point;
43     TPointD p4 = bBox.getP11() - point;
44     double d1  = p1.x * p1.x + p1.y * p1.y;
45     double d2  = p2.x * p2.x + p2.y * p2.y;
46     double d3  = p3.x * p3.x + p3.y * p3.y;
47     double d4  = p4.x * p4.x + p4.y * p4.y;
48 
49     double maxD = std::max(std::max(std::max(d3, d4), d2), d1);
50     return tround(std::max(sqrt(maxD) - radius, 0.0)) * intensity;
51   }
52 
53   void enlarge(const TRectD &bbox, TRectD &requestedGeom,
54                const TRenderSettings &ri, double frame);
55 
doGetBBox(double frame,TRectD & bBox,const TRenderSettings & info)56   bool doGetBBox(double frame, TRectD &bBox,
57                  const TRenderSettings &info) override {
58     if (m_input.isConnected()) {
59       m_input->doGetBBox(frame, bBox, info);
60       bBox = bBox.enlarge(getMaxBraid(bBox, frame));
61       return true;
62     } else {
63       bBox = TRectD();
64       return false;
65     }
66   }
67 
68   void transform(double frame, int port, const TRectD &rectOnOutput,
69                  const TRenderSettings &infoOnOutput, TRectD &rectOnInput,
70                  TRenderSettings &infoOnInput) override;
71 
72   void doCompute(TTile &tile, double frame, const TRenderSettings &) override;
73 
74   int getMemoryRequirement(const TRectD &rect, double frame,
75                            const TRenderSettings &info) override;
canHandle(const TRenderSettings & info,double frame)76   bool canHandle(const TRenderSettings &info, double frame) override {
77     if (info.m_isSwatch) return true;
78 
79     return m_blur->getValue(frame) == 0 ? true
80                                         : isAlmostIsotropic(info.m_affine);
81   }
82 
getParamUIs(TParamUIConcept * & concepts,int & length)83   void getParamUIs(TParamUIConcept *&concepts, int &length) override {
84     concepts = new TParamUIConcept[length = 2];
85 
86     concepts[0].m_type  = TParamUIConcept::POINT;
87     concepts[0].m_label = "Center";
88     concepts[0].m_params.push_back(m_point);
89 
90     concepts[1].m_type  = TParamUIConcept::RADIUS;
91     concepts[1].m_label = "Radius";
92     concepts[1].m_params.push_back(m_radius);
93     concepts[1].m_params.push_back(m_point);
94   }
95 };
96 
97 //------------------------------------------------------------------------------
98 template <typename PIXEL, typename CHANNEL_TYPE, int MAX_CHANNEL_VALUE>
doRadialBlur(const TRasterPT<PIXEL> rout,const TRasterPT<PIXEL> rin,double blur,double radius,TPointD point)99 void doRadialBlur(const TRasterPT<PIXEL> rout, const TRasterPT<PIXEL> rin,
100                   double blur, double radius, TPointD point) {
101   /*-センター位置-*/
102   int dx = (int)point.x;
103   int dy = (int)point.y;
104   int i, j;
105   /*- 出力サイズ -*/
106   int lx = rout->getLx();
107   int ly = rout->getLy();
108 
109   PIXEL *src_buf, *dst_buf;
110 
111   /*- チャンネル最大値(bppにより異なる) -*/
112   double CROP_VAL         = (double)MAX_CHANNEL_VALUE;
113   CHANNEL_TYPE U_CROP_VAL = MAX_CHANNEL_VALUE;
114 
115   double intensity = blur * M_PI_180;
116 
117   /*-出力サイズの画面の中の、ブラーのセンター位置-*/
118   int cx = lx / 2 + dx;
119   int cy = ly / 2 + dy;
120   rin->lock();
121   rout->lock();
122   for (i = 0; i < ly; i++) {
123     src_buf = rin->pixels(i);
124     dst_buf = rout->pixels(i);
125     for (j = 0; j < lx; j++, src_buf++, dst_buf++) {
126       double valr = 0, valg = 0, valb = 0, valm = 0;
127       double sinangle = 0, cosangle = 0;
128       double angle = 0, dist, rangeinv = 0;
129       int ii, vx, vy;
130       int shiftx, shifty, range = 0, rangehalf;
131       /*- ブラー中心→現在のピクセルへのベクトル -*/
132       vx   = (int)(j - cx);
133       vy   = (int)(i - cy);
134       dist = sqrt((double)(vx * vx + vy * vy));
135       /*- ブラーのかかる大きさ。(距離-radius)に比例 -*/
136       range = (int)((dist - radius) * intensity);
137       /*- ブラーが少しでもかかる場合 -*/
138       if (range >= 1 && (dist - radius) > 0) {
139         rangehalf = range / 2;
140         /*- ブラーの角度 -*/
141         angle    = atan2((double)vy, (double)vx);
142         cosangle = cos(angle);
143         if (vx)
144           sinangle = cosangle * (vy / (float)vx);
145         else
146           sinangle = sin(angle);
147         for (ii = 0; ii <= range; ii++) {
148           shiftx = (int)((ii - rangehalf) * cosangle);
149           shifty = (int)((ii - rangehalf) * sinangle);
150           /*- 画面外にはみだす条件 -*/
151           if ((j + shiftx) < 0) continue;    // shiftx=-j;
152           if ((j + shiftx) >= lx) continue;  // shiftx=lx-j-1;
153           if ((i + shifty) < 0) continue;    // shifty=-i;
154           if ((i + shifty) >= ly) continue;  // shifty=ly-i-1;
155           valr += rin->pixels(i + shifty)[j + shiftx].r;
156           valg += rin->pixels(i + shifty)[j + shiftx].g;
157           valb += rin->pixels(i + shifty)[j + shiftx].b;
158           valm += rin->pixels(i + shifty)[j + shiftx].m;
159         }
160         rangeinv = 1.0 / (range + 1);
161         valr *= rangeinv;
162         valg *= rangeinv;
163         valb *= rangeinv;
164         valm *= rangeinv;
165         dst_buf->r = (valr > CROP_VAL) ? U_CROP_VAL
166                                        : ((valr < 0) ? 0 : (CHANNEL_TYPE)valr);
167         dst_buf->g = (valg > CROP_VAL) ? U_CROP_VAL
168                                        : ((valg < 0) ? 0 : (CHANNEL_TYPE)valg);
169         dst_buf->b = (valb > CROP_VAL) ? U_CROP_VAL
170                                        : ((valb < 0) ? 0 : (CHANNEL_TYPE)valb);
171         dst_buf->m = (valm > CROP_VAL) ? U_CROP_VAL
172                                        : ((valm < 0) ? 0 : (CHANNEL_TYPE)valm);
173       } else {
174         *(dst_buf) = *(src_buf);
175       }
176     }
177   }
178 
179   rin->unlock();
180   rout->unlock();
181 }
182 
183 //------------------------------------------------------------------------------
184 
185 //! Calculates the geometry we need for this node computation, given
186 //! the known input data (bbox) and the requested output (requestedGeom).
enlarge(const TRectD & bbox,TRectD & requestedGeom,const TRenderSettings & ri,double frame)187 void RadialBlurFx::enlarge(const TRectD &bbox, TRectD &requestedGeom,
188                            const TRenderSettings &ri, double frame) {
189   TRectD enlargedBbox(bbox);
190   TRectD enlargedGeom(requestedGeom);
191   TPointD originalP00(requestedGeom.getP00());
192 
193   double maxRange = getMaxBraid(enlargedBbox, frame, ri.m_affine);
194 
195   /*- 最低でも1pixel追加する -*/
196   maxRange = std::max(maxRange, 1.0);
197 
198   enlargedBbox = enlargedBbox.enlarge(maxRange);
199   enlargedGeom = enlargedGeom.enlarge(maxRange);
200 
201   // We are to find out the geometry that is useful for the fx computation.
202   // There are some rules to follow:
203   //  a) First, the interesting output we can generate is bounded by both
204   //     the requestedRect and the blurred bbox (i.e. enlarged by the blur
205   //     radius).
206   //  b) Pixels contributing to any output are necessarily part of bbox - and
207   //  only
208   //     those which are blurrable into the requestedRect are useful to us
209   //     (i.e. pixels contained in its enlargement by the blur radius).
210 
211   requestedGeom = (enlargedGeom * bbox) + (enlargedBbox * requestedGeom);
212 
213   // Finally, make sure that the result is coherent with the original P00
214   requestedGeom -= originalP00;
215   requestedGeom.x0 = tfloor(requestedGeom.x0);
216   requestedGeom.y0 = tfloor(requestedGeom.y0);
217   requestedGeom.x1 = tceil(requestedGeom.x1);
218   requestedGeom.y1 = tceil(requestedGeom.y1);
219   requestedGeom += originalP00;
220 }
221 
222 //------------------------------------------------------------------------------
223 
transform(double frame,int port,const TRectD & rectOnOutput,const TRenderSettings & infoOnOutput,TRectD & rectOnInput,TRenderSettings & infoOnInput)224 void RadialBlurFx::transform(double frame, int port, const TRectD &rectOnOutput,
225                              const TRenderSettings &infoOnOutput,
226                              TRectD &rectOnInput,
227                              TRenderSettings &infoOnInput) {
228   TRectD rectOut(rectOnOutput);
229 
230   if (canHandle(infoOnOutput, frame))
231     infoOnInput = infoOnOutput;
232   else {
233     infoOnInput          = infoOnOutput;
234     infoOnInput.m_affine = TAffine();  // because the affine does not commute
235     rectOut              = infoOnOutput.m_affine.inv() * rectOut;
236   }
237 
238   TRectD bbox;
239   m_input->getBBox(frame, bbox, infoOnInput);
240   if (rectOnInput == TConsts::infiniteRectD) bbox = rectOut;
241 
242   rectOnInput = rectOut;
243   enlarge(bbox, rectOnInput, infoOnInput, frame);
244 }
245 
246 //------------------------------------------------------------------------------
247 
doCompute(TTile & tile,double frame,const TRenderSettings & ri)248 void RadialBlurFx::doCompute(TTile &tile, double frame,
249                              const TRenderSettings &ri) {
250   if (!m_input.isConnected()) return;
251 
252   double scale  = sqrt(fabs(ri.m_affine.det()));
253   TPointD point = ri.m_affine * m_point->getValue(frame);
254   double radius = m_radius->getValue(frame) * scale;
255   double blur   = m_blur->getValue(frame);
256 
257   TRectD tileRect = convert(tile.getRaster()->getBounds()) + tile.m_pos;
258 
259   TRectD bBox;
260   m_input->getBBox(frame, bBox, ri);
261   if (bBox.isEmpty()) return;
262 
263   if (bBox == TConsts::infiniteRectD) bBox = tileRect;
264 
265   enlarge(bBox, tileRect, ri, frame);
266 
267   TPointD tileRectCenter = (tileRect.getP00() + tileRect.getP11()) * 0.5;
268   point -= tileRectCenter;
269 
270   int rasInLx = tileRect.getLx();
271   int rasInLy = tileRect.getLy();
272 
273   TRaster32P raster32 = tile.getRaster();
274   TRaster64P raster64 = tile.getRaster();
275 
276   TPoint offset = convert(tile.m_pos - tileRect.getP00());
277   TTile tileIn;
278   if (raster32) {
279     m_input->allocateAndCompute(tileIn, tileRect.getP00(),
280                                 TDimension(rasInLx, rasInLy), raster32, frame,
281                                 ri);
282     TRaster32P rin = tileIn.getRaster();
283     TRaster32P app = raster32->create(rasInLx, rasInLy);
284     doRadialBlur<TPixel32, UCHAR, 255>(app, rin, blur, radius, point);
285     raster32->copy(app, -offset);
286   } else if (raster64) {
287     TRaster64P raster64 = tile.getRaster();
288     m_input->allocateAndCompute(tileIn, tileRect.getP00(),
289                                 TDimension(rasInLx, rasInLy), raster64, frame,
290                                 ri);
291     TRaster64P rin = tileIn.getRaster();
292     TRaster64P app = raster64->create(rasInLx, rasInLy);
293     doRadialBlur<TPixel64, USHORT, 65535>(app, rin, blur, radius, point);
294     raster64->copy(app, -offset);
295   } else
296     throw TException("Brightness&Contrast: unsupported Pixel Type");
297 }
298 
299 //------------------------------------------------------------------
300 
getMemoryRequirement(const TRectD & rect,double frame,const TRenderSettings & info)301 int RadialBlurFx::getMemoryRequirement(const TRectD &rect, double frame,
302                                        const TRenderSettings &info) {
303   double scale  = sqrt(fabs(info.m_affine.det()));
304   TPointD point = info.m_affine * m_point->getValue(frame);
305   double blur   = m_blur->getValue(frame);
306 
307   TRectD bBox;
308   m_input->getBBox(frame, bBox, info);
309   if (bBox.isEmpty()) return 0;
310 
311   if (bBox == TConsts::infiniteRectD) bBox = rect;
312 
313   TRectD tileRect(rect);
314   enlarge(bBox, tileRect, info, frame);
315 
316   return TRasterFx::memorySize(tileRect.enlarge(blur), info.m_bpp);
317 }
318 
319 //------------------------------------------------------------------
320 
321 FX_PLUGIN_IDENTIFIER(RadialBlurFx, "radialBlurFx")
322