OSDN Git Service

イニシャルコミット
[marathon/Aorta.git] / FloatImage.cpp
1 // This code is in the public domain -- castanyo@yahoo.es
2
3 //#include <nvcore/Containers.h>
4 //#include <nvcore/Ptr.h>
5
6 //#include <nvmath/Color.h>
7
8 #include <memory>
9 #include <algorithm>
10 #include <vector>
11 #include "FloatImage.h"
12 #include "Filter.h"
13 //#include "Image.h"
14
15 #include <math.h>
16 #include <stdlib.h>
17
18 static int iround(float f)
19 {
20         return int(f);
21 }
22
23 static int ifloor(float f)
24 {
25         return int(floor(f));
26 }
27
28 static float frac(float f)
29 {
30         return f - floor(f);
31 }
32
33 static int mirror(int x, int w)
34 {
35         x = abs(x);
36         while (x >= w) {
37                 x = 2 * w - x - 2;
38         }
39         return x;
40 }
41
42
43 /// Ctor.
44 FloatImage::FloatImage() : m_width(0), m_height(0), 
45         m_componentNum(0), m_count(0), m_mem(NULL)
46 {
47 }
48
49 /*/// Ctor. Init from image.
50 FloatImage::FloatImage(const Image * img) : m_width(0), m_height(0), 
51         m_componentNum(0), m_count(0), m_mem(NULL)
52 {
53         initFrom(img);
54 }
55 */
56
57 /// Dtor.
58 FloatImage::~FloatImage()
59 {
60         free();
61 }
62
63 /*
64 /// Init the floating point image from a regular image.
65 void FloatImage::initFrom(const Image * img)
66 {
67         nvCheck(img != NULL);
68         
69         allocate(4, img->width(), img->height());
70         
71         float * red_channel = channel(0);
72         float * green_channel = channel(1);
73         float * blue_channel = channel(2);
74         float * alpha_channel = channel(3);
75         
76         const uint count = m_width * m_height;
77         for(uint i = 0; i < count; i++) {
78                 Color32 pixel = img->pixel(i);
79                 red_channel[i] = float(pixel.r) / 255.0f;
80                 green_channel[i] = float(pixel.g) / 255.0f;
81                 blue_channel[i] = float(pixel.b) / 255.0f;
82                 alpha_channel[i] = float(pixel.a) / 255.0f;
83         }
84 }
85 */
86
87  #if 0
88 /// Convert the floating point image to a regular image.
89 Image * FloatImage::createImage(uint base_component/*= 0*/, uint num/*= 4*/) const
90 {
91 //      nvCheck(num <= 4);
92 //      nvCheck(base_component + num <= m_componentNum);
93         
94         AutoPtr<Image> img(new Image());
95         img->allocate(m_width, m_height);
96         
97         const uint size = m_width * m_height;
98         for(uint i = 0; i < size; i++) {
99                 
100                 uint c;
101                 uint8 rgba[4]= {0, 0, 0, 0xff};
102
103                 for(c = 0; c < num; c++) {
104                         float f = m_mem[size * (base_component + c) + i];
105                         rgba[c] = nv::clamp(int(255.0f * f), 0, 255);
106                 }
107
108                 img->pixel(i) = Color32(rgba[0], rgba[1], rgba[2], rgba[3]);
109         }
110         
111         return img.release();
112 }
113
114
115 /// Convert the floating point image to a regular image. Correct gamma of rgb, but not alpha.
116 Image * FloatImage::createImageGammaCorrect(float gamma/*= 2.2f*/) const
117 {
118 //      nvCheck(m_componentNum == 4);
119         
120         AutoPtr<Image> img(new Image());
121         img->allocate(m_width, m_height);
122         
123         const float * rChannel = this->channel(0);
124         const float * gChannel = this->channel(1);
125         const float * bChannel = this->channel(2);
126         const float * aChannel = this->channel(3);
127
128         const uint size = m_width * m_height;
129         for(uint i = 0; i < size; i++)
130         {
131                 const uint8 r = nv::clamp(int(255.0f * pow(rChannel[i], 1.0f/gamma)), 0, 255);
132                 const uint8 g = nv::clamp(int(255.0f * pow(gChannel[i], 1.0f/gamma)), 0, 255);
133                 const uint8 b = nv::clamp(int(255.0f * pow(bChannel[i], 1.0f/gamma)), 0, 255);
134                 const uint8 a = nv::clamp(int(255.0f * aChannel[i]), 0, 255);
135
136                 img->pixel(i) = Color32(r, g, b, a);
137         }
138         
139         return img.release();
140 }
141 #endif
142
143 /// Allocate a 2d float image of the given format and the given extents.
144 void FloatImage::allocate(uint c, uint w, uint h)
145 {
146 //      nvCheck(m_mem == NULL);
147         m_width = w;
148         m_height = h;
149         m_componentNum = c;
150         m_count = w * h * c;
151 //      m_mem = reinterpret_cast<float *>(nv::mem::malloc(m_count * sizeof(float)));
152         m_mem = reinterpret_cast<float *>(malloc(m_count * sizeof(float)));
153 }
154
155 /// Free the image, but don't clear the members.
156 void FloatImage::free()
157 {
158 //      nvCheck(m_mem != NULL);
159 //      nv::mem::free( reinterpret_cast<void *>(m_mem) );
160         ::free(m_mem);
161         m_mem = NULL;
162 }
163
164 void FloatImage::clear(float f/*=0.0f*/)
165 {
166         for(uint i = 0; i < m_count; i++) {
167                 m_mem[i] = f;
168         }
169 }
170
171 #if 0
172 void FloatImage::normalize(uint base_component)
173 {
174 //      nvCheck(base_component + 3 <= m_componentNum);
175         
176         float * xChannel = this->channel(base_component + 0);
177         float * yChannel = this->channel(base_component + 1);
178         float * zChannel = this->channel(base_component + 2);
179
180         const uint size = m_width * m_height;
181         for(uint i = 0; i < size; i++) {
182                 
183                 Vector3 normal(xChannel[i], yChannel[i], zChannel[i]);
184                 normal = normalizeSafe(normal, Vector3(zero), 0.0f);
185                 
186                 xChannel[i] = normal.x();
187                 yChannel[i] = normal.y();
188                 zChannel[i] = normal.z();
189         }
190 }
191 #endif
192
193 void FloatImage::packNormals(uint base_component)
194 {
195         scaleBias(base_component, 3, 0.5f, 1.0f);
196 }
197
198 void FloatImage::expandNormals(uint base_component)
199 {
200         scaleBias(base_component, 3, 2, -0.5);
201 }
202
203 void FloatImage::scaleBias(uint base_component, uint num, float scale, float bias)
204 {
205         const uint size = m_width * m_height;
206         
207         for(uint c = 0; c < num; c++) {
208                 float * ptr = this->channel(base_component + c);
209                 
210                 for(uint i = 0; i < size; i++) {
211                         ptr[i] = scale * (ptr[i] + bias);
212                 }
213         }
214 }
215
216 /// Clamp the elements of the image.
217 void FloatImage::clamp(float low, float high)
218 {
219         for(uint i = 0; i < m_count; i++) {
220                 m_mem[i] = ::clamp(m_mem[i], low, high);
221         }
222 }
223
224 /// From gamma to linear space.
225 void FloatImage::toLinear(uint base_component, uint num, float gamma /*= 2.2f*/)
226 {
227         exponentiate(base_component, num, gamma);
228 }
229
230 /// From linear to gamma space.
231 void FloatImage::toGamma(uint base_component, uint num, float gamma /*= 2.2f*/)
232 {
233         exponentiate(base_component, num, 1.0f/gamma);
234 }
235
236 /// Exponentiate the elements of the image.
237 void FloatImage::exponentiate(uint base_component, uint num, float power)
238 {
239         const uint size = m_width * m_height;
240
241         for(uint c = 0; c < num; c++) {
242                 float * ptr = this->channel(base_component + c);
243                 
244                 for(uint i = 0; i < size; i++) {
245                         ptr[i] = pow(ptr[i], power);
246                 }
247         }
248 }
249
250 float FloatImage::sampleNearest(const float x, const float y, const int c, const WrapMode wm) const
251 {
252         if( wm == WrapMode_Clamp ) return sampleNearestClamp(x, y, c);
253         else if( wm == WrapMode_Repeat ) return sampleNearestRepeat(x, y, c);
254         else /*if( wm == WrapMode_Mirror )*/ return sampleNearestMirror(x, y, c);
255 }
256
257 float FloatImage::sampleLinear(const float x, const float y, const int c, const WrapMode wm) const
258 {
259         if( wm == WrapMode_Clamp ) return sampleLinearClamp(x, y, c);
260         else if( wm == WrapMode_Repeat ) return sampleLinearRepeat(x, y, c);
261         else /*if( wm == WrapMode_Mirror )*/ return sampleLinearMirror(x, y, c);
262 }
263
264 float FloatImage::sampleNearestClamp(const float x, const float y, const int c) const
265 {
266         int ix = ::clamp(iround(x * m_width), 0, (int) m_width-1);
267         int iy = ::clamp(iround(y * m_height), 0, (int) m_height-1);
268         return pixel(ix, iy, c);
269 }
270
271 float FloatImage::sampleNearestRepeat(const float x, const float y, const int c) const
272 {
273         int ix = iround(frac(x) * m_width);
274         int iy = iround(frac(y) * m_height);
275         return pixel(ix, iy, c);
276 }
277
278 float FloatImage::sampleNearestMirror(const float x, const float y, const int c) const
279 {
280         int ix = mirror(iround(x * m_width), m_width);
281         int iy = mirror(iround(y * m_height), m_height);
282         return pixel(ix, iy, c);
283 }
284
285 float FloatImage::sampleLinearClamp(float x, float y, const int c) const
286 {
287         const int w = m_width;
288         const int h = m_height;
289         
290         x *= w;
291         y *= h;
292         
293         const float fracX = frac(x);
294         const float fracY = frac(y);
295         
296         const int ix0 = ::clamp(ifloor(x), 0, w-1);
297         const int iy0 = ::clamp(ifloor(y), 0, h-1);
298         const int ix1 = ::clamp(ifloor(x)+1, 0, w-1);
299         const int iy1 = ::clamp(ifloor(y)+1, 0, h-1);
300
301         float f1 = pixel(ix0, iy0, c);
302         float f2 = pixel(ix1, iy0, c);
303         float f3 = pixel(ix0, iy1, c);
304         float f4 = pixel(ix1, iy1, c);
305         
306         float i1 = lerp(f1, f2, fracX);
307         float i2 = lerp(f3, f4, fracX);
308
309         return lerp(i1, i2, fracY);
310 }
311
312 float FloatImage::sampleLinearRepeat(float x, float y, int c) const
313 {
314         const int w = m_width;
315         const int h = m_height;
316         
317         const float fracX = frac(x * w);
318         const float fracY = frac(y * h);
319         
320         int ix0 = ifloor(frac(x) * w);
321         int iy0 = ifloor(frac(y) * h);
322         int ix1 = ifloor(frac(x + 1.0f/w) * w);
323         int iy1 = ifloor(frac(y + 1.0f/h) * h);
324         
325         float f1 = pixel(ix0, iy0, c);
326         float f2 = pixel(ix1, iy0, c);
327         float f3 = pixel(ix0, iy1, c);
328         float f4 = pixel(ix1, iy1, c);
329         
330         float i1 = lerp(f1, f2, fracX);
331         float i2 = lerp(f3, f4, fracX);
332
333         return lerp(i1, i2, fracY);
334 }
335
336 float FloatImage::sampleLinearMirror(float x, float y, int c) const
337 {
338         const int w = m_width;
339         const int h = m_height;
340
341         x *= w;
342         y *= h;
343
344         const float fracX = frac(x);
345         const float fracY = frac(y);
346
347         int ix0 = mirror(iround(x), w);
348         int iy0 = mirror(iround(y), h);
349         int ix1 = mirror(iround(x) + 1, w);
350         int iy1 = mirror(iround(y) + 1, h);
351
352         float f1 = pixel(ix0, iy0, c);
353         float f2 = pixel(ix1, iy0, c);
354         float f3 = pixel(ix0, iy1, c);
355         float f4 = pixel(ix1, iy1, c);
356         
357         float i1 = lerp(f1, f2, fracX);
358         float i2 = lerp(f3, f4, fracX);
359
360         return lerp(i1, i2, fracY);
361 }
362
363
364 /// Fast downsampling using box filter. 
365 ///
366 /// The extents of the image are divided by two and rounded down.
367 ///
368 /// When the size of the image is odd, this uses a polyphase box filter as explained in:
369 /// http://developer.nvidia.com/object/np2_mipmapping.html
370 ///
371 FloatImage * FloatImage::fastDownSample() const
372 {
373 //      nvDebugCheck(m_width != 1 || m_height != 1);
374         
375         std::auto_ptr<FloatImage> dst_image( new FloatImage() );
376 //      AutoPtr<FloatImage> dst_image( new FloatImage() );
377
378         const uint w = std::max((unsigned int) 1, m_width / 2);
379         const uint h = std::max((unsigned int) 1, m_height / 2);
380         dst_image->allocate(m_componentNum, w, h);
381
382         // 1D box filter.
383         if (m_width == 1 || m_height == 1)
384         {
385                 const uint n = w * h;
386                 
387                 if (n & 1)
388                 {
389                         const float scale = 1.0f / (2 * n + 1);
390                         
391                         for(uint c = 0; c < m_componentNum; c++)
392                         {
393                                 const float * src = this->channel(c);
394                                 float * dst = dst_image->channel(c);
395                                 
396                                 for(uint x = 0; x < n; x++)
397                                 {
398                                         const float w0 = float(n - x);
399                                         const float w1 = float(n - 0);
400                                         const float w2 = float(1 + x);
401                                         
402                                         *dst++ = scale * (w0 * src[0] + w1 * src[1] + w2 * src[2]);
403                                         src += 2;
404                                 }
405                         }
406                 }
407                 else
408                 {
409                         for(uint c = 0; c < m_componentNum; c++)
410                         {
411                                 const float * src = this->channel(c);
412                                 float * dst = dst_image->channel(c);
413                                 
414                                 for(uint x = 0; x < n; x++)
415                                 {
416                                         *dst = 0.5f * (src[0] + src[1]);
417                                         dst++;
418                                         src += 2;
419                                 }
420                         }
421                 }
422         }
423         
424         // Regular box filter.
425         else if ((m_width & 1) == 0 && (m_height & 1) == 0)
426         {
427                 for(uint c = 0; c < m_componentNum; c++)
428                 {
429                         const float * src = this->channel(c);
430                         float * dst = dst_image->channel(c);
431                         
432                         for(uint y = 0; y < h; y++)
433                         {
434                                 for(uint x = 0; x < w; x++)
435                                 {
436                                         *dst = 0.25f * (src[0] + src[1] + src[m_width] + src[m_width + 1]);
437                                         dst++;
438                                         src += 2;
439                                 }
440                                 
441                                 src += m_width;
442                         }
443                 }
444         }
445         
446         // Polyphase filters.
447         else if (m_width & 1 && m_height & 1)
448         {
449 //              nvDebugCheck(m_width == 2 * w + 1);
450 //              nvDebugCheck(m_height == 2 * h + 1);
451                 
452                 const float scale = 1.0f / (m_width * m_height);
453                 
454                 for(uint c = 0; c < m_componentNum; c++)
455                 {
456                         const float * src = this->channel(c);
457                         float * dst = dst_image->channel(c);
458                         
459                         for(uint y = 0; y < h; y++)
460                         {
461                                 const float v0 = float(h - y);
462                                 const float v1 = float(h - 0);
463                                 const float v2 = float(1 + y);
464                                 
465                                 for (uint x = 0; x < w; x++)
466                                 {
467                                         const float w0 = float(w - x);
468                                         const float w1 = float(w - 0);
469                                         const float w2 = float(1 + x);
470                                         
471                                         float f = 0.0f;
472                                         f += v0 * (w0 * src[0 * m_width + 2 * x] + w1 * src[0 * m_width + 2 * x + 1] + w2 * src[0 * m_width + 2 * x + 2]);
473                                         f += v1 * (w0 * src[1 * m_width + 2 * x] + w1 * src[1 * m_width + 2 * x + 1] + w2 * src[0 * m_width + 2 * x + 2]);
474                                         f += v2 * (w0 * src[2 * m_width + 2 * x] + w1 * src[2 * m_width + 2 * x + 1] + w2 * src[0 * m_width + 2 * x + 2]);
475                                         
476                                         *dst = f * scale;
477                                         dst++;
478                                 }
479                                 
480                                 src += 2 * m_width;
481                         }
482                 }
483         }
484         else if (m_width & 1)
485         {
486 //              nvDebugCheck(m_width == 2 * w + 1);
487                 const float scale = 1.0f / (2 * m_width);
488                 
489                 for(uint c = 0; c < m_componentNum; c++)
490                 {
491                         const float * src = this->channel(c);
492                         float * dst = dst_image->channel(c);
493                         
494                         for(uint y = 0; y < h; y++)
495                         {
496                                 for (uint x = 0; x < w; x++)
497                                 {
498                                         const float w0 = float(w - x);
499                                         const float w1 = float(w - 0);
500                                         const float w2 = float(1 + x);
501                                         
502                                         float f = 0.0f;
503                                         f += w0 * (src[2 * x + 0] + src[m_width + 2 * x + 0]);
504                                         f += w1 * (src[2 * x + 1] + src[m_width + 2 * x + 1]);
505                                         f += w2 * (src[2 * x + 2] + src[m_width + 2 * x + 2]);
506                                         
507                                         *dst = f * scale;
508                                         dst++;
509                                 }
510                                 
511                                 src += 2 * m_width;
512                         }
513                 }
514         }
515         else if (m_height & 1)
516         {
517 //              nvDebugCheck(m_height == 2 * h + 1);
518                 
519                 const float scale = 1.0f / (2 * m_height);
520                 
521                 for(uint c = 0; c < m_componentNum; c++)
522                 {
523                         const float * src = this->channel(c);
524                         float * dst = dst_image->channel(c);
525                         
526                         for(uint y = 0; y < h; y++)
527                         {
528                                 const float v0 = float(h - y);
529                                 const float v1 = float(h - 0);
530                                 const float v2 = float(1 + y);
531                                 
532                                 for (uint x = 0; x < w; x++)
533                                 {
534                                         float f = 0.0f;
535                                         f += v0 * (src[0 * m_width + 2 * x] + src[0 * m_width + 2 * x + 1]);
536                                         f += v1 * (src[1 * m_width + 2 * x] + src[1 * m_width + 2 * x + 1]);
537                                         f += v2 * (src[2 * m_width + 2 * x] + src[2 * m_width + 2 * x + 1]);
538                                         
539                                         *dst = f * scale;
540                                         dst++;
541                                 }
542                                 
543                                 src += 2 * m_width;
544                         }
545                 }
546         }
547         
548         return dst_image.release();
549 }
550
551 /*
552 /// Downsample applying a 1D kernel separately in each dimension.
553 FloatImage * FloatImage::downSample(const Kernel1 & kernel, WrapMode wm) const
554 {
555         const uint w = max(1, m_width / 2);
556         const uint h = max(1, m_height / 2);
557         
558         return downSample(kernel, w, h, wm);
559 }
560
561
562 /// Downsample applying a 1D kernel separately in each dimension.
563 FloatImage * FloatImage::downSample(const Kernel1 & kernel, uint w, uint h, WrapMode wm) const
564 {
565         nvCheck(!(kernel.windowSize() & 1));    // Make sure that kernel m_width is even.
566
567         AutoPtr<FloatImage> tmp_image( new FloatImage() );
568         tmp_image->allocate(m_componentNum, w, m_height);
569         
570         AutoPtr<FloatImage> dst_image( new FloatImage() );      
571         dst_image->allocate(m_componentNum, w, h);
572         
573         const float xscale = float(m_width) / float(w);
574         const float yscale = float(m_height) / float(h);
575         
576         for(uint c = 0; c < m_componentNum; c++) {
577                 float * tmp_channel = tmp_image->channel(c);
578                 
579                 for(uint y = 0; y < m_height; y++) {
580                         for(uint x = 0; x < w; x++) {
581                                 
582                                 float sum = this->applyKernelHorizontal(&kernel, uint(x*xscale), y, c, wm);
583                                 
584                                 const uint tmp_index = tmp_image->index(x, y);
585                                 tmp_channel[tmp_index] = sum;
586                         }
587                 }
588                 
589                 float * dst_channel = dst_image->channel(c);
590                 
591                 for(uint y = 0; y < h; y++) {
592                         for(uint x = 0; x < w; x++) {
593                                 
594                                 float sum = tmp_image->applyKernelVertical(&kernel, uint(x*xscale), uint(y*yscale), c, wm);
595                                 
596                                 const uint dst_index = dst_image->index(x, y);
597                                 dst_channel[dst_index] = sum;
598                         }
599                 }
600         }
601         
602         return dst_image.release();
603 }
604 */
605
606 /// Downsample applying a 1D kernel separately in each dimension.
607 FloatImage * FloatImage::downSample(const Filter & filter, WrapMode wm) const
608 {
609         const uint w = std::max((unsigned int) 1, m_width / 2);
610         const uint h = std::max((unsigned int) 1, m_height / 2);
611
612         return downSample(filter, w, h, wm);
613 }
614
615
616 /// Downsample applying a 1D kernel separately in each dimension.
617 FloatImage * FloatImage::downSample(const Filter & filter, uint w, uint h, WrapMode wm) const
618 {
619         // @@ Use monophase filters when frac(m_width / w) == 0
620
621 //      AutoPtr<FloatImage> tmp_image( new FloatImage() );
622 //      AutoPtr<FloatImage> dst_image( new FloatImage() );      
623         std::auto_ptr<FloatImage> tmp_image(new FloatImage() );
624         std::auto_ptr<FloatImage> dst_image(new FloatImage() );
625         
626         PolyphaseKernel xkernel(filter, m_width, w, 32);
627         PolyphaseKernel ykernel(filter, m_height, h, 32);
628         
629         // @@ Select fastest filtering order:
630         //if (w * m_height <= h * m_width)
631         {
632                 tmp_image->allocate(m_componentNum, w, m_height);
633                 dst_image->allocate(m_componentNum, w, h);
634                 
635 //              Array<float> tmp_column(h);
636                 std::vector<float> tmp_column(h);
637                 tmp_column.resize(h);
638                 
639                 for (uint c = 0; c < m_componentNum; c++)
640                 {
641                         float * tmp_channel = tmp_image->channel(c);
642                         
643                         for (uint y = 0; y < m_height; y++) {
644                                 this->applyKernelHorizontal(xkernel, y, c, wm, tmp_channel + y * w);
645                         }
646                         
647                         float * dst_channel = dst_image->channel(c);
648                         
649                         for (uint x = 0; x < w; x++) {
650                                 tmp_image->applyKernelVertical(ykernel, x, c, wm, &tmp_column[0]);
651                                 
652                                 for (uint y = 0; y < h; y++) {
653                                         dst_channel[y * w + x] = tmp_column[y];
654                                 }
655                         }
656                 }
657         }
658         /*else
659         {
660                 tmp_image->allocate(m_componentNum, m_width, h);
661                 dst_image->allocate(m_componentNum, w, h);
662                 
663                 Array<float> tmp_column(h);
664                 tmp_column.resize(h);
665                 
666                 for (uint c = 0; c < m_componentNum; c++)
667                 {
668                         float * tmp_channel = tmp_image->channel(c);
669
670                         for (uint x = 0; x < w; x++) {
671                                 tmp_image->applyKernelVertical(ykernel, x, c, wm, tmp_column.unsecureBuffer());
672                                 
673                                 for (uint y = 0; y < h; y++) {
674                                         tmp_channel[y * w + x] = tmp_column[y];
675                                 }
676                         }
677
678                         float * dst_channel = dst_image->channel(c);
679
680                         for (uint y = 0; y < m_height; y++) {
681                                 this->applyKernelHorizontal(xkernel, y, c, wm, dst_channel + y * w);
682                         }
683                 }
684         }*/
685         
686         return dst_image.release();
687 }
688
689
690
691 /// Apply 2D kernel at the given coordinates and return result.
692 float FloatImage::applyKernel(const Kernel2 * k, int x, int y, int c, WrapMode wm) const
693 {
694 //      nvDebugCheck(k != NULL);
695         
696         const uint kernelWindow = k->windowSize();
697         const int kernelOffset = int(kernelWindow / 2) - 1;
698         
699         const float * channel = this->channel(c);
700
701         float sum = 0.0f;
702         for (uint i = 0; i < kernelWindow; i++)
703         {
704                 const int src_y = int(y + i) - kernelOffset;
705                 
706                 for (uint e = 0; e < kernelWindow; e++)
707                 {
708                         const int src_x = int(x + e) - kernelOffset;
709                         
710                         int idx = this->index(src_x, src_y, wm);
711                         
712                         sum += k->valueAt(e, i) * channel[idx];
713                 }
714         }
715         
716         return sum;
717 }
718
719
720 /// Apply 1D vertical kernel at the given coordinates and return result.
721 float FloatImage::applyKernelVertical(const Kernel1 * k, int x, int y, int c, WrapMode wm) const
722 {
723 //      nvDebugCheck(k != NULL);
724         
725         const uint kernelWindow = k->windowSize();
726         const int kernelOffset = int(kernelWindow / 2) - 1;
727         
728         const float * channel = this->channel(c);
729
730         float sum = 0.0f;
731         for (uint i = 0; i < kernelWindow; i++)
732         {
733                 const int src_y = int(y + i) - kernelOffset;
734                 const int idx = this->index(x, src_y, wm);
735                 
736                 sum += k->valueAt(i) * channel[idx];
737         }
738         
739         return sum;
740 }
741
742 /// Apply 1D horizontal kernel at the given coordinates and return result.
743 float FloatImage::applyKernelHorizontal(const Kernel1 * k, int x, int y, int c, WrapMode wm) const
744 {
745 //      nvDebugCheck(k != NULL);
746         
747         const uint kernelWindow = k->windowSize();
748         const int kernelOffset = int(kernelWindow / 2) - 1;
749         
750         const float * channel = this->channel(c);
751
752         float sum = 0.0f;
753         for (uint e = 0; e < kernelWindow; e++)
754         {
755                 const int src_x = int(x + e) - kernelOffset;
756                 const int idx = this->index(src_x, y, wm);
757                 
758                 sum += k->valueAt(e) * channel[idx];
759         }
760         
761         return sum;
762 }
763
764
765 /// Apply 1D vertical kernel at the given coordinates and return result.
766 void FloatImage::applyKernelVertical(const PolyphaseKernel & k, int x, int c, WrapMode wm, float * output) const
767 {
768         const uint length = k.length();
769         const float scale = float(length) / float(m_height);
770         const float iscale = 1.0f / scale;
771
772         const float width = k.width();
773         const int windowSize = k.windowSize();
774
775         const float * channel = this->channel(c);
776
777         for (uint i = 0; i < length; i++)
778         {
779                 const float center = (0.5f + i) * iscale;
780                 
781                 const int left = (int)floorf(center - width);
782                 const int right = (int)ceilf(center + width);
783 //              nvCheck(right - left <= windowSize);
784                 
785                 float sum = 0;
786                 for (int j = 0; j < windowSize; ++j)
787                 {
788                         const int idx = this->index(x, j+left, wm);
789                         
790                         sum += k.valueAt(i, j) * channel[idx];
791                 }
792                 
793                 output[i] = sum;
794         }
795 }
796
797 /// Apply 1D horizontal kernel at the given coordinates and return result.
798 void FloatImage::applyKernelHorizontal(const PolyphaseKernel & k, int y, int c, WrapMode wm, float * output) const
799 {
800         const uint length = k.length();
801         const float scale = float(length) / float(m_width);
802         const float iscale = 1.0f / scale;
803
804         const float width = k.width();
805         const int windowSize = k.windowSize();
806
807         const float * channel = this->channel(c);
808
809         for (uint i = 0; i < length; i++)
810         {
811                 const float center = (0.5f + i) * iscale;
812                 
813                 const int left = (int)floorf(center - width);
814                 const int right = (int)ceilf(center + width);
815 //              nvDebugCheck(right - left <= windowSize);
816                 
817                 float sum = 0;
818                 for (int j = 0; j < windowSize; ++j)
819                 {
820                         const int idx = this->index(left + j, y, wm);
821                         
822                         sum += k.valueAt(i, j) * channel[idx];
823                 }
824                 
825                 output[i] = sum;
826         }
827 }
828