1 /* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2 * levels.c
3 * Copyright (C) 2009 Maksim Golovkin (m4ks1k@gmail.com)
4 * This file is a Frei0r plugin.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <stdlib.h>
22 #include <assert.h>
23 #include <math.h>
24 #include <stdio.h>
25
26 #include "frei0r.h"
27 #include "frei0r_math.h"
28
29 enum ChannelChoice
30 {
31 CHANNEL_RED,
32 CHANNEL_GREEN,
33 CHANNEL_BLUE,
34 CHANNEL_LUMA,
35 };
36
37 enum HistogramPosChoice
38 {
39 POS_TOP_LEFT,
40 POS_TOP_RIGHT,
41 POS_BOTTOM_LEFT,
42 POS_BOTTOM_RIGHT,
43 };
44
45 enum ParamIndex
46 {
47 PARAM_CHANNEL,
48 PARAM_INPUT_MIN,
49 PARAM_INPUT_MAX,
50 PARAM_GAMMA,
51 PARAM_OUTPUT_MIN,
52 PARAM_OUTPUT_MAX,
53 PARAM_SHOW_HISTOGRAM,
54 PARAM_HISTOGRAM_POS,
55
56 PARAMETER_COUNT // last one.
57 };
58
59 typedef struct levels_instance
60 {
61 unsigned int width;
62 unsigned int height;
63 double inputMin; // 0 - 1
64 double inputMax; // 0 - 1
65 double outputMin; // 0 - 1
66 double outputMax; // 0 - 1
67 double gamma;
68 enum ChannelChoice channel;
69 char showHistogram;
70 enum HistogramPosChoice histogramPosition;
71 } levels_instance_t;
72
f0r_init()73 int f0r_init()
74 {
75 return 1;
76 }
77
f0r_deinit()78 void f0r_deinit()
79 { /* no initialization required */ }
80
f0r_get_plugin_info(f0r_plugin_info_t * levels_instance_t)81 void f0r_get_plugin_info(f0r_plugin_info_t* levels_instance_t)
82 {
83 levels_instance_t->name = "Levels";
84 levels_instance_t->author = "Maksim Golovkin";
85 levels_instance_t->plugin_type = F0R_PLUGIN_TYPE_FILTER;
86 levels_instance_t->color_model = F0R_COLOR_MODEL_RGBA8888;
87 levels_instance_t->frei0r_version = FREI0R_MAJOR_VERSION;
88 levels_instance_t->major_version = 0;
89 levels_instance_t->minor_version = 4;
90 levels_instance_t->num_params = PARAMETER_COUNT;
91 levels_instance_t->explanation = "Adjust luminance or color channel intensity";
92 }
93
f0r_get_param_info(f0r_param_info_t * info,int param_index)94 void f0r_get_param_info(f0r_param_info_t* info, int param_index)
95 {
96 switch(param_index)
97 {
98 case PARAM_CHANNEL:
99 info->name = "Channel";
100 info->type = F0R_PARAM_DOUBLE;
101 info->explanation = "Channel to adjust levels. "
102 "0%=R, 10%=G, 20%=B, 30%=Luma";
103 break;
104 case PARAM_INPUT_MIN:
105 info->name = "Input black level";
106 info->type = F0R_PARAM_DOUBLE;
107 info->explanation = "Input black level";
108 break;
109 case PARAM_INPUT_MAX:
110 info->name = "Input white level";
111 info->type = F0R_PARAM_DOUBLE;
112 info->explanation = "Input white level";
113 break;
114 case PARAM_GAMMA:
115 info->name = "Gamma";
116 info->type = F0R_PARAM_DOUBLE;
117 info->explanation = "Gamma";
118 break;
119 case PARAM_OUTPUT_MIN:
120 info->name = "Black output";
121 info->type = F0R_PARAM_DOUBLE;
122 info->explanation = "Black output";
123 break;
124 case PARAM_OUTPUT_MAX:
125 info->name = "White output";
126 info->type = F0R_PARAM_DOUBLE;
127 info->explanation = "White output";
128 break;
129 case PARAM_SHOW_HISTOGRAM:
130 info->name = "Show histogram";
131 info->type = F0R_PARAM_BOOL;
132 info->explanation = "Show histogram";
133 break;
134 case PARAM_HISTOGRAM_POS:
135 info->name = "Histogram position";
136 info->type = F0R_PARAM_DOUBLE;
137 info->explanation = "Histogram position. 0%=TL, 10%=TR, 20%=BL, 30%=BR";
138 break;
139 }
140 }
141
f0r_construct(unsigned int width,unsigned int height)142 f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
143 {
144 levels_instance_t* inst = (levels_instance_t*)calloc(1, sizeof(*inst));
145 inst->width = width; inst->height = height;
146 inst->inputMin = 0;
147 inst->inputMax = 1;
148 inst->outputMin = 0;
149 inst->outputMax = 1;
150 inst->gamma = 1;
151 inst->channel = CHANNEL_LUMA;
152 inst->showHistogram = 1;
153 inst->histogramPosition = POS_BOTTOM_RIGHT;
154 return (f0r_instance_t)inst;
155 }
156
f0r_destruct(f0r_instance_t instance)157 void f0r_destruct(f0r_instance_t instance)
158 {
159 free(instance);
160 }
161
f0r_set_param_value(f0r_instance_t instance,f0r_param_t param,int param_index)162 void f0r_set_param_value(f0r_instance_t instance,
163 f0r_param_t param, int param_index)
164 {
165 assert(instance);
166 levels_instance_t* inst = (levels_instance_t*)instance;
167
168 switch(param_index)
169 {
170 case PARAM_CHANNEL:
171 inst->channel = (enum ChannelChoice)
172 CLAMP(floor(*((f0r_param_double *)param) * 10),
173 CHANNEL_RED, CHANNEL_LUMA);
174 break;
175 case PARAM_INPUT_MIN:
176 inst->inputMin = *((f0r_param_double *)param);
177 break;
178 case PARAM_INPUT_MAX:
179 inst->inputMax = *((f0r_param_double *)param);
180 break;
181 case PARAM_GAMMA:
182 inst->gamma = *((f0r_param_double *)param) * 4;
183 break;
184 case PARAM_OUTPUT_MIN:
185 inst->outputMin = *((f0r_param_double *)param);
186 break;
187 case PARAM_OUTPUT_MAX:
188 inst->outputMax = *((f0r_param_double *)param);
189 break;
190 case PARAM_SHOW_HISTOGRAM:
191 inst->showHistogram = *((f0r_param_bool *)param);
192 break;
193 case PARAM_HISTOGRAM_POS:
194 inst->histogramPosition = (enum HistogramPosChoice)
195 CLAMP(floor(*((f0r_param_double *)param) * 10),
196 POS_TOP_LEFT, POS_BOTTOM_RIGHT);
197 break;
198 }
199 }
200
f0r_get_param_value(f0r_instance_t instance,f0r_param_t param,int param_index)201 void f0r_get_param_value(f0r_instance_t instance,
202 f0r_param_t param, int param_index)
203 {
204 assert(instance);
205 levels_instance_t* inst = (levels_instance_t*)instance;
206
207 switch(param_index)
208 {
209 case PARAM_CHANNEL:
210 *((f0r_param_double *)param) = inst->channel / 10.;
211 break;
212 case PARAM_INPUT_MIN:
213 *((f0r_param_double *)param) = inst->inputMin;
214 break;
215 case PARAM_INPUT_MAX:
216 *((f0r_param_double *)param) = inst->inputMax;
217 break;
218 case PARAM_GAMMA:
219 *((f0r_param_double *)param) = inst->gamma / 4;
220 break;
221 case PARAM_OUTPUT_MIN:
222 *((f0r_param_double *)param) = inst->outputMin;
223 break;
224 case PARAM_OUTPUT_MAX:
225 *((f0r_param_double *)param) = inst->outputMax;
226 break;
227 case PARAM_SHOW_HISTOGRAM:
228 *((f0r_param_bool *)param) = inst->showHistogram;
229 break;
230 case PARAM_HISTOGRAM_POS:
231 *((f0r_param_double *)param) = inst->histogramPosition / 10.;
232 break;
233 }
234 }
235
f0r_update(f0r_instance_t instance,double time,const uint32_t * inframe,uint32_t * outframe)236 void f0r_update(f0r_instance_t instance, double time,
237 const uint32_t* inframe, uint32_t* outframe)
238 {
239 assert(instance);
240 levels_instance_t* inst = (levels_instance_t*)instance;
241 unsigned int len = inst->width * inst->height;
242 unsigned int maxHisto = 0;
243
244 unsigned char* dst = (unsigned char*)outframe;
245 const unsigned char* src = (unsigned char*)inframe;
246 int r, g, b;
247
248 double levels[256];
249 unsigned int map[256];
250
251 double inScale = inst->inputMax != inst->inputMin?inst->inputMax - inst->inputMin:1;
252 double exp = inst->gamma == 0?1:1/inst->gamma;
253 double outScale = inst->outputMax - inst->outputMin;
254
255 for(int i = 0; i < 256; i++) {
256 double v = i / 255. - inst->inputMin;
257 if (v < 0.0) {
258 v = 0.0;
259 }
260 double w = pow(v / inScale, exp) * outScale + inst->outputMin;
261 map[i] = CLAMP0255(lrintf(w * 255.0));
262 }
263
264 if (inst->showHistogram)
265 for(int i = 0; i < 256; i++)
266 levels[i] = 0;
267
268 while (len--)
269 {
270 r = *src++;
271 g = *src++;
272 b = *src++;
273
274 if (inst->showHistogram) {
275 int intensity =
276 inst->channel == CHANNEL_RED?r:
277 inst->channel == CHANNEL_GREEN?g:
278 inst->channel == CHANNEL_BLUE?b:
279 CLAMP0255(b * .114 + g * .587 + r * .299);
280 int index = CLAMP0255(intensity);
281 levels[index]++;
282 if (levels[index] > maxHisto)
283 maxHisto = levels[index];
284 }
285
286 switch (inst->channel) {
287 case CHANNEL_RED:
288 *dst++ = map[r];
289 *dst++ = g;
290 *dst++ = b;
291 break;
292 case CHANNEL_GREEN:
293 *dst++ = r;
294 *dst++ = map[g];
295 *dst++ = b;
296 break;
297 case CHANNEL_BLUE:
298 *dst++ = r;
299 *dst++ = g;
300 *dst++ = map[b];
301 break;
302 case CHANNEL_LUMA:
303 *dst++ = map[r];
304 *dst++ = map[g];
305 *dst++ = map[b];
306 break;
307 }
308
309 *dst++ = *src++; // copy alpha
310 }
311 if (inst->showHistogram) {
312 dst = (unsigned char *)outframe;
313 src = (unsigned char *)inframe;
314 int thirdY = inst->height / 3;
315 int thirdX = inst->width / 3;
316 int barHeight = inst->height / 27;
317 int histoHeight = thirdY - barHeight * 3;
318 int xOffset = 0;
319 int yOffset = 0;
320 if (inst->histogramPosition == POS_BOTTOM_RIGHT || inst->histogramPosition == POS_BOTTOM_LEFT)
321 yOffset += 2 * thirdY;
322 if (inst->histogramPosition == POS_BOTTOM_RIGHT || inst->histogramPosition == POS_TOP_RIGHT)
323 xOffset += 2 * thirdX;
324 for(int y = 0; y < histoHeight; y++) {
325 double pointValue = (double)(histoHeight - y) / histoHeight;
326 for(int x = 0; x < thirdX; x++) {
327 int offset = ((y + yOffset) * inst->width + x + xOffset) * 4;
328 int drawPoint = pointValue < (double)levels[CLAMP0255(x * 255 / thirdX)] / maxHisto;
329 dst[offset] = drawPoint?(CHANNEL_RED != inst->channel || CHANNEL_LUMA == inst->channel?0:255):127 + src[offset]/2;
330 dst[offset + 1] = drawPoint?(CHANNEL_GREEN != inst->channel || CHANNEL_LUMA == inst->channel?0:255):127 + src[offset + 1]/2;
331 dst[offset + 2] = drawPoint?(CHANNEL_BLUE != inst->channel || CHANNEL_LUMA == inst->channel?0:255):127 + src[offset + 2]/2;
332 dst[offset + 3] = src[offset + 3];
333 }
334 }
335 int posInMin = inst->inputMin * thirdX;
336 int posInMax = inst->inputMax * thirdX;
337 int posOutMin = inst->outputMin * thirdX;
338 int posOutMax = inst->outputMax * thirdX;
339 int posGamma = posInMin + (posInMax - posInMin) * pow(inst->gamma, .5) *.5;
340 int color[3];
341 color[0] = CHANNEL_RED == inst->channel || CHANNEL_LUMA == inst->channel?255:0;
342 color[1] = CHANNEL_GREEN == inst->channel || CHANNEL_LUMA == inst->channel?255:0;
343 color[2] = CHANNEL_BLUE == inst->channel || CHANNEL_LUMA == inst->channel?255:0;
344 int midColor[3];
345 midColor[0] = color[0]>>1;
346 midColor[1] = color[1]>>1;
347 midColor[2] = color[2]>>1;
348 for(int y = histoHeight; y < thirdY - barHeight * 2; y++) {
349 int offsettedY = (y + yOffset) * inst->width;
350 int offsettedYlower = (y + yOffset + barHeight * 2) * inst->width;
351 for(int x = 0; x < thirdX; x++) {
352 int offset = (offsettedY + x + xOffset) * 4;
353 dst[offset] = 127 + dst[offset]/2;
354 dst[offset + 1] = 127 + dst[offset + 1]/2;
355 dst[offset + 2] = 127 + dst[offset + 2]/2;
356 offset = (offsettedYlower + x + xOffset) * 4;
357 dst[offset] = 127 + dst[offset]/2;
358 dst[offset + 1] = 127 + dst[offset + 1]/2;
359 dst[offset + 2] = 127 + dst[offset + 2]/2;
360 }
361 int delta = (y - histoHeight)/2;
362
363 for(int x = -delta; x < delta; x++) {
364 int xInMin = x + posInMin;
365 int xInMax = x + posInMax;
366 int xOutMin = x + posOutMin;
367 int xOutMax = x + posOutMax;
368 int xGamma = x + posGamma;
369 if (xInMin >= 0 && xInMin < thirdX) {
370 int offset = (offsettedY + xInMin + xOffset) * 4;
371 dst[offset] = 0;
372 dst[offset + 1] = 0;
373 dst[offset + 2] = 0;
374 }
375 if (xInMax >= 0 && xInMax < thirdX) {
376 int offset = (offsettedY + xInMax + xOffset) * 4;
377 dst[offset] = color[0];
378 dst[offset + 1] = color[1];
379 dst[offset + 2] = color[2];
380 }
381 if (xGamma >= 0 && xGamma < thirdX) {
382 int offset = (offsettedY + xGamma + xOffset) * 4;
383 dst[offset] = midColor[0];
384 dst[offset + 1] = midColor[1];
385 dst[offset + 2] = midColor[2];
386 }
387 if (xOutMin >= 0 && xOutMin < thirdX) {
388 int offset = (offsettedYlower + xOutMin + xOffset) * 4;
389 dst[offset] = 0;
390 dst[offset + 1] = 0;
391 dst[offset + 2] = 0;
392 }
393 if (xOutMax >= 0 && xOutMax < thirdX) {
394 int offset = (offsettedYlower + xOutMax + xOffset) * 4;
395 dst[offset] = color[0];
396 dst[offset + 1] = color[1];
397 dst[offset + 2] = color[2];
398 }
399 }
400 }
401 for(int y = thirdY - barHeight * 2; y < thirdY - barHeight; y++) {
402 for(int x = 0; x < thirdX; x++) {
403 int offset = ((y + yOffset) * inst->width + x + xOffset) * 4;
404 int pointValue = CLAMP0255(x * 255 / thirdX);
405 dst[offset] = inst->channel == CHANNEL_RED || inst->channel == CHANNEL_LUMA?pointValue:0;
406 dst[offset + 1] = inst->channel == CHANNEL_GREEN || inst->channel == CHANNEL_LUMA?pointValue:0;
407 dst[offset + 2] = inst->channel == CHANNEL_BLUE || inst->channel == CHANNEL_LUMA?pointValue:0;
408 }
409 }
410 }
411 }
412