1 /*
2  * imagesource_convolution.cpp - Applies a convolution filter to an image.
3  *
4  * Supports Greyscale, RGB and CMYK data
5  * Doesn't (yet) support random access
6  *
7  * Copyright (c) 2008 by Alastair M. Robinson
8  * Distributed under the terms of the GNU General Public License -
9  * see the file named "COPYING" for more details.
10  *
11  */
12 
13 #include <iostream>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <math.h>
17 
18 #include "imagesource_convolution.h"
19 
20 using namespace std;
21 
22 #ifndef M_PI
23 #define M_PI    3.14159265358979323846
24 #endif
25 
26 // The row cache is just a simplistic ring-buffer type cache which handles
27 // the details of tracking several rows of "support" data.
28 
29 class ISConvolution_RowCache
30 {
31 	public:
32 	ISConvolution_RowCache(ImageSource_Convolution *source);
33 	~ISConvolution_RowCache();
34 	float *GetRow(int row);
35 	private:
36 	ImageSource_Convolution *source;
37 	float *cache;
38 	int cachewidth,cachehoffset;
39 	int bufferrows;
40 	int currentrow;
41 };
42 
43 
~ISConvolution_RowCache()44 ISConvolution_RowCache::~ISConvolution_RowCache()
45 {
46 	if(cache)
47 		free(cache);
48 }
49 
50 
ISConvolution_RowCache(ImageSource_Convolution * source)51 ISConvolution_RowCache::ISConvolution_RowCache(ImageSource_Convolution *source)
52 	: source(source), currentrow(-1)
53 {
54 	cachewidth=source->width+source->hextra*2;
55 	cachehoffset=source->hextra;
56 	bufferrows=source->vextra*2+1;
57 	cache=(float *)malloc(sizeof(float)*source->samplesperpixel*cachewidth*bufferrows);
58 }
59 
60 
GetRow(int row)61 inline float *ISConvolution_RowCache::GetRow(int row)
62 {
63 	if(row<0)
64 		row=0;
65 	if(row>=source->source->height)
66 		row=source->source->height-1;
67 	int crow=row%(source->vextra*2+1);
68 	float *rowptr=cache+crow*source->samplesperpixel*cachewidth;
69 	if(row>currentrow)
70 	{
71 		currentrow=row;
72 		ISDataType *src=source->source->GetRow(row);
73 		for(int x=0;x<cachewidth;++x)
74 		{
75 			int sx=x-cachehoffset;
76 			if(sx<0) sx=0;
77 			if(sx>=source->width) sx=source->width-1;
78 			for(int s=0;s<source->samplesperpixel;++s)
79 			{
80 				float a=src[sx*source->samplesperpixel+s];
81 				rowptr[x*source->samplesperpixel+s]=a;
82 			}
83 		}
84 	}
85 	return(rowptr+cachehoffset*source->samplesperpixel);
86 }
87 
88 
~ImageSource_Convolution()89 ImageSource_Convolution::~ImageSource_Convolution()
90 {
91 	if(tmprows)
92 		free(tmprows);
93 	if(cache)
94 		delete cache;
95 	if(source)
96 		delete source;
97 }
98 
99 
GetRow(int row)100 ISDataType *ImageSource_Convolution::GetRow(int row)
101 {
102 	if(row==currentrow)
103 		return(rowbuffer);
104 
105 	int kw=kernel->GetWidth();
106 	int kh=kernel->GetHeight();
107 
108 	for(int r=0;r<kh;++r)
109 	{
110 		tmprows[r]=cache->GetRow(row+(r-vextra));
111 	}
112 
113 	for(int x=0;x<width;++x)
114 	{
115 		float t[5]={0.0,0.0,0.0,0.0,0.0};
116 		for(int ky=0;ky<kh;++ky)
117 		{
118 			for(int kx=0;kx<kw;++kx)
119 			{
120 				for(int s=0;s<samplesperpixel;++s)
121 				{
122 					t[s]+=kernel->Kernel(kx,ky) * tmprows[ky][(x+(kx-hextra))*samplesperpixel+s];
123 				}
124 			}
125 		}
126 		for(int s=0;s<samplesperpixel;++s)
127 		{
128 			float a=t[s];
129 			if(a<0.0) a=0.0;
130 			if(a>IS_SAMPLEMAX) a=IS_SAMPLEMAX;
131 			rowbuffer[x*samplesperpixel+s]=ISDataType(a);
132 		}
133 	}
134 
135 	currentrow=row;
136 
137 	return(rowbuffer);
138 }
139 
140 
ImageSource_Convolution(struct ImageSource * source,ConvKernel * kernel)141 ImageSource_Convolution::ImageSource_Convolution(struct ImageSource *source,ConvKernel *kernel)
142 	: ImageSource(source), source(source), kernel(kernel)
143 {
144 	hextra=kernel->GetWidth()/2;
145 	vextra=kernel->GetHeight()/2;
146 	cache=new ISConvolution_RowCache(this);
147 	tmprows=(float **)malloc(sizeof(float *)*kernel->GetHeight());
148 	MakeRowBuffer();
149 	randomaccess=false;
150 }
151