1 /*
2  * imagesource_unsharpmask.cpp - Applies an Unsharp Mask 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_unsharpmask.h"
19 #include "convkernel_unsharpmask.h"
20 #include "convkernel_gaussian_1D.h"
21 
22 using namespace std;
23 
24 // The row cache is just a simplistic ring-buffer type cache which handles
25 // the details of tracking several rows of "support" data.
26 
27 class ISUnsharpMask_RowCache
28 {
29 	public:
30 	ISUnsharpMask_RowCache(ImageSource_UnsharpMask *source);
31 	~ISUnsharpMask_RowCache();
32 	ISDataType *GetRawRow(int row);
33 	float *GetConvRow(int row);
34 	private:
35 	ImageSource_UnsharpMask *source;
36 	float *convcache;
37 	ISDataType *rawcache;
38 	int cachewidth,cachehoffset;
39 	int bufferrows;
40 	int rawcurrentrow;
41 	int convcurrentrow;
42 };
43 
44 
~ISUnsharpMask_RowCache()45 ISUnsharpMask_RowCache::~ISUnsharpMask_RowCache()
46 {
47 	if(convcache)
48 		free(convcache);
49 	if(rawcache)
50 		free(rawcache);
51 }
52 
53 
ISUnsharpMask_RowCache(ImageSource_UnsharpMask * source)54 ISUnsharpMask_RowCache::ISUnsharpMask_RowCache(ImageSource_UnsharpMask *source)
55 	: source(source), rawcurrentrow(-1), convcurrentrow(-1)
56 {
57 	cachewidth=source->width+source->hextra*2;
58 	cachehoffset=source->hextra;
59 	bufferrows=source->vextra*2+1;
60 	convcache=(float *)malloc(sizeof(float)*source->samplesperpixel*cachewidth*bufferrows);
61 	rawcache=(ISDataType *)malloc(sizeof(float)*source->samplesperpixel*source->width*bufferrows);
62 }
63 
64 
GetConvRow(int row)65 inline float *ISUnsharpMask_RowCache::GetConvRow(int row)
66 {
67 	if(row<0)
68 		row=0;
69 	if(row>=source->source->height)
70 		row=source->source->height-1;
71 	int crow=row%(source->vextra*2+1);
72 	float *rowptr=convcache+crow*source->samplesperpixel*cachewidth;
73 	if(row>convcurrentrow)
74 	{
75 		convcurrentrow=row;
76 		ISDataType *src=GetRawRow(row);
77 
78 		// Convolve the temp row into the cache;
79 		for(int x=0;x<cachewidth;++x)
80 		{
81 			float t[5]={0.0,0.0,0.0,0.0,0.0};
82 			for(int kx=0;kx<source->kernel->GetWidth();++kx)
83 			{
84 				int sx=x+kx-cachehoffset*2;
85 				if(sx<0) sx=0;
86 				if(sx>source->width) sx=source->width-1;
87 				for(int s=0;s<source->samplesperpixel;++s)
88 				{
89 					t[s]+=source->kernel->Kernel(kx,0)*src[sx*source->samplesperpixel+s];
90 				}
91 			}
92 			for(int s=0;s<source->samplesperpixel;++s)
93 			{
94 				rowptr[x*source->samplesperpixel+s]=t[s];
95 			}
96 		}
97 	}
98 	return(rowptr+cachehoffset*source->samplesperpixel);
99 }
100 
101 
GetRawRow(int row)102 inline ISDataType *ISUnsharpMask_RowCache::GetRawRow(int row)
103 {
104 	if(row<0)
105 		row=0;
106 	if(row>=source->source->height)
107 		row=source->source->height-1;
108 	int crow=row%(source->vextra*2+1);
109 	ISDataType *rowptr=rawcache+crow*source->samplesperpixel*source->width;
110 	if(row>rawcurrentrow)
111 	{
112 		rawcurrentrow=row;
113 		ISDataType *src=source->source->GetRow(row);
114 
115 		// Store the row to be cached in a temporary buffer...
116 		for(int s=0;s<source->width*source->samplesperpixel;++s)
117 		{
118 			rowptr[s]=src[s];
119 		}
120 	}
121 	return(rowptr);
122 }
123 
124 
~ImageSource_UnsharpMask()125 ImageSource_UnsharpMask::~ImageSource_UnsharpMask()
126 {
127 	if(tmprows)
128 		free(tmprows);
129 	if(cache)
130 		delete cache;
131 	if(source)
132 		delete source;
133 	if(kernel)
134 		delete kernel;
135 }
136 
137 
GetRow(int row)138 ISDataType *ImageSource_UnsharpMask::GetRow(int row)
139 {
140 	if(row==currentrow)
141 		return(rowbuffer);
142 
143 	int kh=kernel->GetWidth();	// Using a 1D kernel!
144 
145 	for(int r=0;r<kh;++r)
146 	{
147 		tmprows[r]=cache->GetConvRow(row+(r-vextra));
148 	}
149 	ISDataType *srcrow=cache->GetRawRow(row);
150 	for(int x=0;x<width;++x)
151 	{
152 		float t[5]={0.0,0.0,0.0,0.0,0.0};
153 		for(int ky=0;ky<kh;++ky)
154 		{
155 			for(int s=0;s<samplesperpixel;++s)
156 			{
157 				t[s]+=kernel->Kernel(ky,0) * tmprows[ky][x*samplesperpixel+s];
158 			}
159 		}
160 		for(int s=0;s<samplesperpixel;++s)
161 		{
162 //			float out=t[s];
163 //			float out=tmprows[vextra][x*samplesperpixel+s];
164 			float out=(1+amount)*srcrow[x*samplesperpixel+s]-amount*t[s];
165 			float d=srcrow[x*samplesperpixel+s]-out;
166 			if((d*d)<threshold)
167 			{
168 				out=srcrow[x*samplesperpixel+s];
169 			}
170 			if(out<0.0) out=0.0;
171 			if(out>IS_SAMPLEMAX) out=IS_SAMPLEMAX;
172 			rowbuffer[x*samplesperpixel+s]=ISDataType(out);
173 		}
174 	}
175 
176 	currentrow=row;
177 
178 	return(rowbuffer);
179 }
180 
181 
ImageSource_UnsharpMask(struct ImageSource * source,float radius,float amount,float threshold)182 ImageSource_UnsharpMask::ImageSource_UnsharpMask(struct ImageSource *source,float radius,float amount,float threshold)
183 	: ImageSource(source), source(source), kernel(NULL), amount(amount), threshold(threshold*threshold)
184 {
185 	kernel=new ConvKernel_Gaussian_1D(radius);
186 	kernel->Normalize();
187 	hextra=kernel->GetWidth()/2;
188 	vextra=hextra;
189 	cache=new ISUnsharpMask_RowCache(this);
190 	tmprows=(float **)malloc(sizeof(float *)*kernel->GetWidth());
191 	MakeRowBuffer();
192 	randomaccess=false;
193 }
194