kaleidoscope 1.4.0
 
Loading...
Searching...
No Matches
kaleidoscope.c
Go to the documentation of this file.
1#include "kaleidoscope.h"
2#include "kaleidoscope-config.h"
3
4#ifdef WIN32
5#define _USE_MATH_DEFINES
6#endif
7#include <assert.h>
8#include <math.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13void getKaleidoscopeVersion(int *major, int *minor, int *patch)
14{
15 if (major && minor && patch)
16 {
17 *major = PROJECT_MAJOR_VERSION;
18 *minor = PROJECT_MINOR_VERSION;
19 *patch = PROJECT_PATCH_VERSION;
20 }
21}
22
24{
25 static char info[sizeof(PROJECT_VERSION)];
26 strncpy(info, PROJECT_VERSION, sizeof(PROJECT_VERSION));
27 return info;
28}
29
31{
32 int offset = 0;
33 static char info[125];
34
35 strncpy(info, PROJECT_VERSION, sizeof(PROJECT_VERSION));
36 offset += sizeof(PROJECT_VERSION);
37 memset(&info[offset - 1], 32, 1);
38 strncpy(&info[offset], COMPILER_NAME, sizeof(COMPILER_NAME));
39 offset += sizeof(COMPILER_NAME);
40 memset(&info[offset - 1], 32, 1);
41 strncpy(&info[offset], COMPILER_VERSION, sizeof(COMPILER_VERSION));
42 offset += sizeof(COMPILER_VERSION);
43 memset(&info[offset - 1], 32, 1);
44 strncpy(&info[offset], BUILD_TYPE, sizeof(BUILD_TYPE));
45 offset += sizeof(BUILD_TYPE);
46 memset(&info[offset - 1], 32, 1);
47 strncpy(&info[offset], PROJECT_BUILD_DATE, sizeof(PROJECT_BUILD_DATE));
48 offset += sizeof(PROJECT_BUILD_DATE);
49 memset(&info[offset - 1], 32, 1);
50 strncpy(&info[offset], PROJECT_BUILD_TIME, sizeof(PROJECT_BUILD_TIME));
51
52 return info;
53}
54
55static int compare(const void *lhsPtr, const void *rhsPtr)
56{
57 const TransformationInfo *lhs = (const TransformationInfo *)lhsPtr;
58 const TransformationInfo *rhs = (const TransformationInfo *)rhsPtr;
59 return (int)(lhs->dstOffset - rhs->dstOffset);
60}
61
62void interpolate(TransformationInfo *dataOut, const TransformationInfo *dataIn, int width, int height)
63{
64 // Very simple implementation of nearest neighbour interpolation
65 for (int idx = 1; idx < height - 1; ++idx)
66 {
67 int heightOffset = idx * width;
68 for (int jdx = 1; jdx < width - 1; ++jdx)
69 {
70 const TransformationInfo *ptrIn = &dataIn[heightOffset + jdx];
71 TransformationInfo *ptrOut = &dataOut[heightOffset + jdx];
72 if (!(ptrIn->dstLocation.x) && !(ptrIn->dstLocation.y))
73 {
74 ptrOut->dstLocation.x = jdx;
75 ptrOut->dstLocation.y = idx;
76 if (((ptrIn - 1)->dstLocation.x) || ((ptrIn - 1)->dstLocation.y)) // Left
77 {
78 ptrOut->srcLocation.x = (ptrIn - 1)->srcLocation.x + 1;
79 ptrOut->srcLocation.y = (ptrIn - 1)->srcLocation.y;
80 }
81 else if (((ptrIn + 1)->dstLocation.x) || ((ptrIn + 1)->dstLocation.y)) // Right
82 {
83 ptrOut->srcLocation.x = (ptrIn + 1)->srcLocation.x - 1;
84 ptrOut->srcLocation.y = (ptrIn + 1)->srcLocation.y;
85 }
86 else if (((ptrIn - width)->dstLocation.x) || ((ptrIn - width)->dstLocation.y)) // Top
87 {
88 ptrOut->srcLocation.x = (ptrIn - width)->srcLocation.x;
89 ptrOut->srcLocation.y = (ptrIn - width)->srcLocation.y - 1;
90 }
91 else if (((ptrIn + width)->dstLocation.x) || ((ptrIn + width)->dstLocation.y)) // Bottom
92 {
93 ptrOut->srcLocation.x = (ptrIn + width)->srcLocation.x;
94 ptrOut->srcLocation.y = (ptrIn + width)->srcLocation.y + 1;
95 }
96 else if (((ptrIn - width - 1)->dstLocation.x) || ((ptrIn - width - 1)->dstLocation.y)) // Top-Left
97 {
98 ptrOut->srcLocation.x = (ptrIn - width - 1)->srcLocation.x - 1;
99 ptrOut->srcLocation.y = (ptrIn - width - 1)->srcLocation.y - 1;
100 }
101 else if (((ptrIn - width + 1)->dstLocation.x) || ((ptrIn - width + 1)->dstLocation.y)) // Top-Right
102 {
103 ptrOut->srcLocation.x = (ptrIn - width + 1)->srcLocation.x + 1;
104 ptrOut->srcLocation.y = (ptrIn - width + 1)->srcLocation.y - 1;
105 }
106 else if (((ptrIn + width - 1)->dstLocation.x) || ((ptrIn + width - 1)->dstLocation.y)) // Bottom-Left
107 {
108 ptrOut->srcLocation.x = (ptrIn + width - 1)->srcLocation.x - 1;
109 ptrOut->srcLocation.y = (ptrIn + width - 1)->srcLocation.y - 1;
110 }
111 else if (((ptrIn + width + 1)->dstLocation.x) || ((ptrIn + width + 1)->dstLocation.y)) // Bottom-Right
112 {
113 ptrOut->srcLocation.x = (ptrIn + width + 1)->srcLocation.x + 1;
114 ptrOut->srcLocation.y = (ptrIn + width + 1)->srcLocation.y + 1;
115 }
116 else
117 {
118 memset(ptrOut, 0, sizeof(TransformationInfo));
119 }
120 }
121 else
122 {
123 *ptrOut = *ptrIn;
124 }
125 }
126 }
127}
128
129void rotatePoints(TransformationInfo *outData, const TransformationInfo *orgData, int width, int height, double angle)
130{
131 double cosVal = cos(angle * M_PI / 180);
132 double sinVal = sin(angle * M_PI / 180);
133
134 for (int idx = 0; idx < width * height; ++idx)
135 {
136 if (orgData[idx].dstLocation.x || orgData[idx].dstLocation.y)
137 {
138 int newX = (int)round(orgData[idx].dstLocation.x * cosVal + orgData[idx].dstLocation.y * sinVal);
139 int newY = (int)round(orgData[idx].dstLocation.y * cosVal - orgData[idx].dstLocation.x * sinVal);
140
141 // Fix origin to top left again
142 newX += (width / 2);
143 newY += (height / 2);
144
145 if (newX <= width && newX >= 0 && newY <= height && newY >= 0)
146 {
147 outData[newY * width + newX].srcLocation = orgData[idx].srcLocation;
148 outData[newY * width + newX].dstLocation.x = newX;
149 outData[newY * width + newX].dstLocation.y = newY;
150 }
151 }
152 }
153}
154
155void sliceTriangle(TransformationInfo *transformPtr, int width, int height, int n, double scaleDown)
156{
157 // Variables
158 const double topAngle = 360.0 / n;
159 const double tanVal = tan(topAngle / 2.0 * M_PI / 180.0); // tan(topAngle / 2) in radians
160 const int triangleHeight = (int)fmin(round(width / (2.0 * tanVal)), height - 1);
161 const int heightStart = (height - triangleHeight) / 2;
162 const int heightEnd = (height + triangleHeight) / 2;
163 const int scaleDownOffset = (int)(height * scaleDown / 2);
164
165 // Ensure limits within image
166 assert(heightStart >= 0);
167 assert(heightStart <= height);
168 assert(heightEnd >= 0);
169 assert(heightEnd <= height);
170
171 for (int idx = heightStart; idx < heightEnd; ++idx)
172 {
173 const int currentBaseLength = (int)((idx - heightStart) * tanVal);
174
175 const int widthStart = (width / 2 - currentBaseLength);
176 const int widthEnd = (width / 2 + currentBaseLength);
177
178 // Ensure limits within image
179 if (widthStart < 0 || widthStart > width || widthEnd < 0 || widthEnd > width)
180 {
181 continue;
182 }
183
184 TransformationInfo *ptr = &transformPtr[idx * width];
185 for (int jdx = widthStart; jdx <= widthEnd; ++jdx)
186 {
187 ptr[jdx].srcLocation.x = jdx;
188 ptr[jdx].srcLocation.y = idx;
189
190 // Calculate coordinates respect to center to prepare rotating
191 ptr[jdx].dstLocation.x = (int)((jdx - width / 2) * scaleDown);
192 ptr[jdx].dstLocation.y = (int)((idx - heightStart - height / 2) * scaleDown + scaleDownOffset);
193 }
194 }
195}
196
197int initKaleidoscope(KaleidoscopeHandle *handler, int n, int width, int height, int nComponents, double scaleDown)
198{
199 int jdx;
200
201 int retval = EXIT_FAILURE;
202 const int nPixels = width * height;
203 TransformationInfo *buffPtr1 = NULL;
204 TransformationInfo *buffPtr2 = NULL;
205
206 // Check parameters
207 if (handler == NULL || n <= 2 || width <= 0 || height <= 0 || nComponents <= 0 || scaleDown <= 0.0 ||
208 scaleDown >= 1.0)
209 {
210 return EXIT_FAILURE;
211 }
212
213 assert(handler);
214 assert(n > 2);
215 assert(width > 0);
216 assert(height > 0);
217 assert(nComponents > 0);
218 assert(scaleDown > 0.0);
219 assert(scaleDown < 1.0);
220
221 handler->width = width;
222 handler->height = height;
223 handler->nComponents = (unsigned char)nComponents;
224
225 buffPtr1 = (TransformationInfo *)calloc(nPixels, sizeof(TransformationInfo));
226 buffPtr2 = (TransformationInfo *)calloc(nPixels, sizeof(TransformationInfo));
227 if (!buffPtr1 || !buffPtr2)
228 {
229 goto cleanup;
230 }
231
232 sliceTriangle(buffPtr1, width, height, n, scaleDown);
233
234 // Rotate all points and fix origin to left top
235 for (int idx = 0; idx < n; ++idx)
236 {
237 double rotationAngle = idx * (360.0 / n);
238 rotatePoints(buffPtr2, buffPtr1, width, height, rotationAngle);
239 }
240
241 // Fill rotation artifacts
242 memset(buffPtr1, 0, sizeof(TransformationInfo) * width * height);
243 interpolate(buffPtr1, buffPtr2, width, height);
244
245 // Remove zeros and set to points for handler
246 handler->nPoints = 0;
247 for (int idx = 0; idx < nPixels; ++idx)
248 {
249 const TransformationInfo *ptr = &buffPtr1[idx];
250 if (!(ptr->srcLocation.x) || !(ptr->srcLocation.y))
251 {
252 continue;
253 }
254
255 buffPtr1[handler->nPoints] = *ptr;
256 buffPtr1[handler->nPoints].srcOffset =
257 ptr->srcLocation.x * nComponents + ptr->srcLocation.y * width * nComponents;
258 buffPtr1[handler->nPoints].dstOffset =
259 ptr->dstLocation.x * nComponents + ptr->dstLocation.y * width * nComponents;
260 ++(handler->nPoints);
261 }
262
263 // Sort
264 qsort(buffPtr1, handler->nPoints, sizeof(TransformationInfo), compare);
265
266 // Deduplicate
267 jdx = 0;
268 for (int idx = 1; idx < handler->nPoints; ++idx)
269 {
270 if (compare(&buffPtr1[jdx], &buffPtr1[idx]))
271 {
272 buffPtr1[jdx] = buffPtr1[idx];
273 ++jdx;
274 }
275 }
276 handler->nPoints = jdx;
277
278 handler->pTransferFunc = (TransformationInfo *)malloc(handler->nPoints * sizeof(TransformationInfo));
279 memcpy(handler->pTransferFunc, buffPtr1, handler->nPoints * sizeof(TransformationInfo));
280 retval = EXIT_SUCCESS;
281
282cleanup:
283 free(buffPtr1);
284 free(buffPtr2);
285
286 if (retval == EXIT_FAILURE)
287 {
288 free(handler->pTransferFunc);
289 }
290
291 return retval;
292}
293
294void processKaleidoscope(const KaleidoscopeHandle *handler, double k, const unsigned char *imgIn, unsigned char *imgOut)
295{
296 long long idx;
297 const long long nComponents = handler->nComponents;
298 const long long nPixels = (long long)handler->width * handler->height * handler->nComponents;
299
300 const unsigned char *srcPtr = imgIn;
301 unsigned char *destPtr = imgOut;
302 const TransformationInfo *ptrTransform = &(handler->pTransferFunc[0]);
303
304 for (idx = 0; idx < nPixels; ++idx, ++destPtr, ++srcPtr) // Dim image
305 {
306 *destPtr = (unsigned char)((*srcPtr) * k);
307 }
308 for (idx = 0; idx < handler->nPoints; ++idx, ++ptrTransform) // Merge
309 {
310 memcpy(&(imgOut[ptrTransform->dstOffset]), &(imgIn[ptrTransform->srcOffset]), nComponents);
311 }
312}
313
315{
316 if (handler)
317 free(handler->pTransferFunc);
318}
void getKaleidoscopeVersion(int *major, int *minor, int *patch)
Get the Kaleidoscope Library version as integer.
void processKaleidoscope(const KaleidoscopeHandle *handler, double k, const unsigned char *imgIn, unsigned char *imgOut)
Applies kaleidoscope effect to image.
char * getKaleidoscopeLibraryInfo()
Get the Kaleidoscope Library info as string.
void sliceTriangle(TransformationInfo *transformPtr, int width, int height, int n, double scaleDown)
Slices a suitable triangle from image.
static int compare(const void *lhsPtr, const void *rhsPtr)
int initKaleidoscope(KaleidoscopeHandle *handler, int n, int width, int height, int nComponents, double scaleDown)
Initializes kaleidoscope handler.
char * getKaleidoscopeVersionString()
Get the Kaleidoscope Library version as string.
void interpolate(TransformationInfo *dataOut, const TransformationInfo *dataIn, int width, int height)
A simple interpolation function. Internal use only.
void rotatePoints(TransformationInfo *outData, const TransformationInfo *orgData, int width, int height, double angle)
Rotates the coordinates of sliced triangle. Internal use only.
void deInitKaleidoscope(KaleidoscopeHandle *handler)
Deinitializes kaleidoscope handler.
Struct for kaleidoscope effect generator.
int height
Image height.
int width
Image width.
long long nPoints
Total number of points of transfer function.
struct TransformationInfo_t * pTransferFunc
Transformation info.
unsigned char nComponents
Number of components (eg 3 for RGB)
Data struct for transformation information.
unsigned long long srcOffset
Offset from source image.
unsigned long long dstOffset
Offset from destination image.
Point2D srcLocation
Location from source image.
Point2D dstLocation
Location to destination image.