kaleidoscope 1.4.2
 
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 if (handler->nPoints <= 0) // Prevent allocation of zero points
279 {
280 goto cleanup;
281 }
282
283 handler->pTransferFunc = (TransformationInfo *)malloc(handler->nPoints * sizeof(TransformationInfo));
284 memcpy(handler->pTransferFunc, buffPtr1, handler->nPoints * sizeof(TransformationInfo));
285 retval = EXIT_SUCCESS;
286
287cleanup:
288 free(buffPtr1);
289 free(buffPtr2);
290
291 if (retval == EXIT_FAILURE)
292 {
293 free(handler->pTransferFunc);
294 }
295
296 return retval;
297}
298
299void processKaleidoscope(const KaleidoscopeHandle *handler, double k, const unsigned char *imgIn, unsigned char *imgOut)
300{
301 long long idx;
302 const long long nComponents = handler->nComponents;
303 const long long nPixels = (long long)handler->width * handler->height * handler->nComponents;
304
305 const unsigned char *srcPtr = imgIn;
306 unsigned char *destPtr = imgOut;
307 const TransformationInfo *ptrTransform = &(handler->pTransferFunc[0]);
308
309 for (idx = 0; idx < nPixels; ++idx, ++destPtr, ++srcPtr) // Dim image
310 {
311 *destPtr = (unsigned char)((*srcPtr) * k);
312 }
313 for (idx = 0; idx < handler->nPoints; ++idx, ++ptrTransform) // Merge
314 {
315 memcpy(&(imgOut[ptrTransform->dstOffset]), &(imgIn[ptrTransform->srcOffset]), nComponents);
316 }
317}
318
320{
321 if (handler)
322 free(handler->pTransferFunc);
323}
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.