1// 2// PRCurvesView.m 3// PRICE 4// 5// Created by Riccardo Mottola on 7 August 2011. 6// Copyright 2011-2014 Riccardo Mottola. All rights reserved. 7// 8// This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 9// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 11#import "PRCurvesView.h" 12#import "PRCCurves.h" 13 14 15@implementation PRCurvesView 16 17- (id)initWithFrame:(NSRect)frame 18 { 19 self = [super initWithFrame:frame]; 20 if (self) 21 { 22 hasColor = NO; 23 24 pathLumi = [[PRCurvesPath alloc] init]; 25 pathR = [[PRCurvesPath alloc] init]; 26 pathG = [[PRCurvesPath alloc] init]; 27 pathB = [[PRCurvesPath alloc] init]; 28 29 pathHLumi = [[NSBezierPath alloc] init]; 30 pathHR = [[NSBezierPath alloc] init]; 31 pathHG = [[NSBezierPath alloc] init]; 32 pathHB = [[NSBezierPath alloc] init]; 33 34 pathHLumiTr = [[NSBezierPath alloc] init]; 35 pathHRTr = [[NSBezierPath alloc] init]; 36 pathHGTr = [[NSBezierPath alloc] init]; 37 pathHBTr = [[NSBezierPath alloc] init]; 38 39 [self initPathLumi]; 40 [self initPathR]; 41 [self initPathG]; 42 [self initPathB]; 43 44 histogramScale = 0.5; 45 } 46 return self; 47} 48 49- (void)initPath:(PRCurvesPath *)p 50{ 51 [p removeAllPoints]; 52 53 [p moveToPoint: NSMakePoint(0, 0)]; 54 [p curveToPoint: NSMakePoint(255, 255) controlPoint1: NSMakePoint(50, 0) controlPoint2: NSMakePoint(205, 255)]; 55} 56 57- (void)initPathLumi 58{ 59 [self initPath: pathLumi]; 60} 61 62- (void)initPathR 63{ 64 [self initPath: pathR]; 65} 66 67- (void)initPathG 68{ 69 [self initPath: pathG]; 70} 71 72- (void)initPathB 73{ 74 [self initPath: pathB]; 75} 76 77 78- (void)dealloc 79{ 80 [pathLumi release]; 81 [pathR release]; 82 [pathG release]; 83 [pathB release]; 84 85 [pathHLumi release]; 86 [pathHR release]; 87 [pathHG release]; 88 [pathHB release]; 89 90 [pathHLumiTr release]; 91 [pathHRTr release]; 92 [pathHGTr release]; 93 [pathHBTr release]; 94 95 [super dealloc]; 96} 97 98-(BOOL)hasColor 99{ 100 return hasColor; 101} 102 103-(void)setFilterController: (PRCCurves *)aFilterController; 104{ 105 controller = aFilterController; 106} 107 108-(void)setBlackPoint: (int)value 109{ 110 PRCurvesPath *path; 111 NSBezierPathElement pe; 112 NSPoint cp[3]; 113 float diff; 114 115 path = pathLumi; 116 pe = [path elementAtIndex: 0 associatedPoints: cp]; 117 diff = cp[0].x - (float)value; 118 if ((pe == NSMoveToBezierPathElement) || (pe == NSLineToBezierPathElement)) 119 { 120 /* if we have a straight line, we move the next control point too to preserve the curve */ 121 NSBezierPathElement pe2; 122 NSPoint cp2[3]; 123 124 if ([path elementCount] > 0) 125 { 126 cp[0].x = (float)value; 127 pe2 = [path elementAtIndex: 1 associatedPoints: cp2]; 128 if (pe2 == NSCurveToBezierPathElement) 129 { 130 NSLog(@"old cp2-0.x: %f", cp2[0].x); 131 cp2[0].x = cp2[0].x - diff; 132 NSLog(@"new cp2-0.x: %f", cp2[0].x); 133 [path setAssociatedPoints: cp2 atIndex: 1]; 134 } 135 } 136 [path setAssociatedPoints: cp atIndex: 0]; 137 } 138} 139 140-(void)setWhitePoint: (int)value 141{ 142 PRCurvesPath *path; 143 NSBezierPathElement pe; 144 NSPoint cp[3]; 145 146 path = pathLumi; 147 pe = [path elementAtIndex: [path elementCount]-1 associatedPoints: cp]; 148 if (pe == NSMoveToBezierPathElement) 149 cp[0].x = value; 150 else if (pe == NSLineToBezierPathElement) 151 cp[0].x = value; 152 else if (pe == NSCurveToBezierPathElement) 153 { 154 float diff; 155 156 diff = cp[2].x - (float)value; 157 cp[2].x = value; 158 cp[1].x = cp[1].x - diff; 159 //cp[2].x = cp[2].x - diff; 160 } 161 [path setAssociatedPoints: cp atIndex: [path elementCount]-1]; 162} 163 164 165-(PRCurvesPath *)luminancePath 166{ 167 return pathLumi; 168} 169 170-(PRCurvesPath *)redPath 171{ 172 return pathR; 173} 174 175-(PRCurvesPath *)greenPath 176{ 177 return pathG; 178} 179 180-(PRCurvesPath *)bluePath 181{ 182 return pathB; 183} 184 185- (void)drawRect:(NSRect)rect 186{ 187 [[NSColor whiteColor] set]; 188 [NSBezierPath fillRect: [self bounds]]; 189 190 if (hasColor) 191 { 192 [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:1.0] set]; 193 [pathHR stroke]; 194 [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:0.7] set]; 195 [pathHR fill]; 196 [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:1.0] set]; 197 [pathHRTr stroke]; 198 [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:0.7] set]; 199 [pathHRTr fill]; 200 // [[NSColor redColor] set]; 201 // [pathR stroke]; 202 203 [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:1.0] set]; 204 [pathHG stroke]; 205 [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:0.7] set]; 206 [pathHG fill]; 207 [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:1.0] set]; 208 [pathHGTr stroke]; 209 [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:0.7] set]; 210 [pathHGTr fill]; 211 // [[NSColor greenColor] set]; 212 // [pathG stroke]; 213 214 [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:1.0 alpha:1.0] set]; 215 [pathHB stroke]; 216 [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:1.0 alpha:0.7] set]; 217 [pathHB fill]; 218 [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:1.0 alpha:1.0] set]; 219 [pathHBTr stroke]; 220 [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:1.0 alpha:0.7] set]; 221 [pathHBTr fill]; 222 // [[NSColor blueColor] set]; 223 // [pathB stroke]; 224 // FIXME: temporary, until we have full RGB support 225 [[NSColor blackColor] set]; 226 [pathLumi stroke]; 227 } 228 else 229 { 230 [[NSColor colorWithDeviceWhite:0.5 alpha:1.0] set]; 231 [pathHLumi stroke]; 232 [[NSColor colorWithDeviceWhite:0.9 alpha:1.0] set]; 233 [pathHLumi fill]; 234 235 [[NSColor colorWithDeviceWhite:0.5 alpha:1.0] set]; 236 [pathHLumiTr stroke]; 237 [[NSColor colorWithDeviceWhite:0.9 alpha:1.0] set]; 238 [pathHLumiTr fill]; 239 240 [[NSColor blackColor] set]; 241 [pathLumi stroke]; 242 } 243 244 245 246 /* boundaries */ 247 [[NSColor blackColor] set]; 248 [NSBezierPath strokeRect: [self bounds]]; 249} 250 251-(void)calculateHistogram: (PRImage *)image; 252{ 253 NSBitmapImageRep *theImageRep; 254 unsigned char *theData; 255 NSInteger w, h; 256 NSInteger x, y; 257 NSInteger i; 258 NSInteger srcBytesPerPixel; 259 NSInteger srcBytesPerRow; 260 float maxHisto; 261 unsigned viewHeight; 262 float histoScale; 263 264 265 /* get source image representation and associated information */ 266 theImageRep = [image bitmapRep]; 267 268 w = [theImageRep pixelsWide]; 269 h = [theImageRep pixelsHigh]; 270 pixNum = h * w; 271 srcBytesPerRow = [theImageRep bytesPerRow]; 272 srcBytesPerPixel = [theImageRep bitsPerPixel] / 8; 273 hasColor = [image hasColor]; 274 275 theData = [theImageRep bitmapData]; 276 277 if (hasColor) 278 { 279 /* calculate the histogram */ 280 for (i = 0; i <= UCHAR_MAX; i++) 281 histogramDenormR[i] = histogramDenormG[i] = histogramDenormB[i] = 0; 282 for (y = 0; y < h; y++) 283 for (x = 0; x < w; x++) 284 { 285 histogramDenormR[theData[y*srcBytesPerRow + x*srcBytesPerPixel]]++; 286 histogramDenormG[theData[y*srcBytesPerRow + x*srcBytesPerPixel + 1]]++; 287 histogramDenormB[theData[y*srcBytesPerRow + x*srcBytesPerPixel + 2]]++; 288 } 289 290 /* normalize histogram */ 291 /* calculate the maximum luminance as maxHisto */ 292 maxHisto = 0; 293 for (i = 0; i <= UCHAR_MAX; i++) 294 { 295 histogramR[i] = (float)histogramDenormR[i] / (float)pixNum; 296 histogramG[i] = (float)histogramDenormG[i] / (float)pixNum; 297 histogramB[i] = (float)histogramDenormB[i] / (float)pixNum; 298 if (histogramR[i] > maxHisto) 299 maxHisto = histogramR[i]; 300 if (histogramG[i] > maxHisto) 301 maxHisto = histogramG[i]; 302 if (histogramB[i] > maxHisto) 303 maxHisto = histogramB[i]; 304 } 305 } 306 else 307 { 308 /* calculate the histogram */ 309 for (i = 0; i <= UCHAR_MAX; i++) 310 histogramDenormL[i] = 0; 311 for (y = 0; y < h; y++) 312 for (x = 0; x < w; x++) 313 histogramDenormL[theData[y*srcBytesPerRow + x*srcBytesPerPixel]]++; 314 315 /* normalize histogram */ 316 maxHisto = 0; 317 for (i = 0; i <= UCHAR_MAX; i++) 318 { 319 histogramL[i] = (float)histogramDenormL[i] / (float)pixNum; 320 if (histogramL[i] > maxHisto) 321 maxHisto = histogramL[i]; 322 } 323 } 324 325 viewHeight = 256; 326 NSLog(@"maxHisto L : %f for %u", maxHisto, pixNum); 327 histoScale = (histogramScale * viewHeight) / maxHisto; 328 if (hasColor) 329 { 330 [pathHR removeAllPoints]; 331 [pathHG removeAllPoints]; 332 [pathHB removeAllPoints]; 333 [pathHR moveToPoint: NSMakePoint(0, 0)]; 334 [pathHR moveToPoint: NSMakePoint(0, 0)]; 335 [pathHG moveToPoint: NSMakePoint(0, 0)]; 336 [pathHB moveToPoint: NSMakePoint(0, 0)]; 337 for (i = 0; i <= UCHAR_MAX; i++) 338 { 339 [pathHR lineToPoint: NSMakePoint(i, histogramR[i] * histoScale)]; 340 [pathHG lineToPoint: NSMakePoint(i, histogramG[i] * histoScale)]; 341 [pathHB lineToPoint: NSMakePoint(i, histogramB[i] * histoScale)]; 342 } 343 [pathHR lineToPoint: NSMakePoint(viewHeight, 0)]; 344 [pathHG lineToPoint: NSMakePoint(viewHeight, 0)]; 345 [pathHB lineToPoint: NSMakePoint(viewHeight, 0)]; 346 } 347 else 348 { 349 [pathHLumi removeAllPoints]; 350 [pathHLumi moveToPoint: NSMakePoint(0, 0)]; 351 for (i = 0; i <= UCHAR_MAX; i++) 352 { 353 [pathHLumi lineToPoint: NSMakePoint(i, histogramL[i] * histoScale)]; 354 } 355 [pathHLumi lineToPoint: NSMakePoint(viewHeight, 0)]; 356 } 357 358 NSLog(@"curves histogram initing ended"); 359} 360 361-(void)calculateFunctions 362{ 363 unsigned i; 364 NSArray *curveL; 365 366 curveL = [pathLumi values]; 367 for (i = 0; i <= UCHAR_MAX; i++) 368 funL[i] = floor([[curveL objectAtIndex: i] floatValue]); 369} 370 371-(void)calculateTransformedHistograms 372{ 373 unsigned long int histogramDenormLTr[UCHAR_MAX+1]; /* not normalized pixel count for each level */ 374 unsigned long int histogramDenormRTr[UCHAR_MAX+1]; /* not normalized pixel count for each level */ 375 unsigned long int histogramDenormGTr[UCHAR_MAX+1]; /* not normalized pixel count for each level */ 376 unsigned long int histogramDenormBTr[UCHAR_MAX+1]; /* not normalized pixel count for each level */ 377 unsigned i; 378 float maxHisto; 379 unsigned viewHeight; 380 float histoScale; 381 382 [self calculateFunctions]; 383 384 /* zero histogram */ 385 for (i = 0; i <= UCHAR_MAX; i++) 386 { 387 histogramDenormLTr[i] = 0; 388 histogramDenormRTr[i] = 0; 389 histogramDenormGTr[i] = 0; 390 histogramDenormBTr[i] = 0; 391 } 392 393 /* derive new histogram */ 394 for (i = 0; i <= UCHAR_MAX; i++) 395 { 396 histogramDenormLTr[funL[i]] += histogramDenormL[i]; 397 histogramDenormRTr[funL[i]] += histogramDenormR[i]; 398 histogramDenormGTr[funL[i]] += histogramDenormG[i]; 399 histogramDenormBTr[funL[i]] += histogramDenormB[i]; 400 } 401 402 /* normalize histogram */ 403 if (hasColor) 404 { 405 maxHisto = 0; 406 for (i = 0; i <= UCHAR_MAX; i++) 407 { 408 histogramRTr[i] = (float)histogramDenormRTr[i] / (float)pixNum; 409 histogramGTr[i] = (float)histogramDenormGTr[i] / (float)pixNum; 410 histogramBTr[i] = (float)histogramDenormBTr[i] / (float)pixNum; 411 if (histogramRTr[i] > maxHisto) 412 maxHisto = histogramRTr[i]; 413 if (histogramGTr[i] > maxHisto) 414 maxHisto = histogramGTr[i]; 415 if (histogramBTr[i] > maxHisto) 416 maxHisto = histogramBTr[i]; 417 } 418 } 419 else 420 { 421 maxHisto = 0; 422 for (i = 0; i <= UCHAR_MAX; i++) 423 { 424 histogramLTr[i] = (float)histogramDenormLTr[i] / (float)pixNum; 425 if (histogramLTr[i] > maxHisto) 426 maxHisto = histogramLTr[i]; 427 } 428 } 429 430 viewHeight = 256; 431 NSLog(@"transformed maxHisto L : %f for %u", maxHisto, pixNum); 432 histoScale = (histogramScale * viewHeight) / maxHisto; 433 [pathHLumiTr removeAllPoints]; 434 [pathHLumiTr moveToPoint: NSMakePoint(0, viewHeight)]; 435 for (i = 0; i <= UCHAR_MAX; i++) 436 { 437 [pathHLumiTr lineToPoint: NSMakePoint(i, viewHeight-(histogramLTr[i] * histoScale))]; 438 } 439 [pathHLumiTr lineToPoint: NSMakePoint(viewHeight, viewHeight)]; 440 441 [pathHRTr removeAllPoints]; 442 [pathHGTr removeAllPoints]; 443 [pathHBTr removeAllPoints]; 444 [pathHRTr moveToPoint: NSMakePoint(0, viewHeight)]; 445 [pathHGTr moveToPoint: NSMakePoint(0, viewHeight)]; 446 [pathHBTr moveToPoint: NSMakePoint(0, viewHeight)]; 447 for (i = 0; i <= UCHAR_MAX; i++) 448 { 449 [pathHRTr lineToPoint: NSMakePoint(i, viewHeight - (histogramRTr[i] * histoScale))]; 450 [pathHGTr lineToPoint: NSMakePoint(i, viewHeight - (histogramGTr[i] * histoScale))]; 451 [pathHBTr lineToPoint: NSMakePoint(i, viewHeight - (histogramBTr[i] * histoScale))]; 452 } 453 [pathHRTr lineToPoint: NSMakePoint(viewHeight, viewHeight)]; 454 [pathHGTr lineToPoint: NSMakePoint(viewHeight, viewHeight)]; 455 [pathHBTr lineToPoint: NSMakePoint(viewHeight, viewHeight)]; 456} 457 458- (void)moveControlPoint:(NSPoint)cp ofCurve:(PRCurvesPath *)curveP 459{ 460 NSEvent *nextEvent; 461 NSPoint oldP; 462 463 oldP = cp; 464 nextEvent = [[self window] nextEventMatchingMask: 465 NSLeftMouseUpMask | NSLeftMouseDraggedMask]; 466 if([nextEvent type] == NSLeftMouseDragged) 467 { 468 NSPoint p; 469 470 do 471 { 472 p = [nextEvent locationInWindow]; 473 p = [self convertPoint:p fromView:nil]; 474 475 /* bounds checking */ 476 if (p.x < 0) 477 p.x = 0; 478 if (p.x > [self frame].size.width-1) 479 p.x = [self frame].size.width-1; 480 if (p.y < 0) 481 p.y = 0; 482 if (p.y > [self frame].size.height-1) 483 p.y = [self frame].size.height-1; 484 485 [curveP move:oldP toPoint:p]; 486 [controller recalculateCurves:self]; 487 [controller setBlackPointValue: (int)floor([curveP blackPoint].x)]; 488 [controller setWhitePointValue: (int)floor([curveP whitePoint].x)]; 489 [self setNeedsDisplay:YES]; 490 491 oldP = p; 492 nextEvent = [[self window] nextEventMatchingMask: 493 NSLeftMouseUpMask | NSLeftMouseDraggedMask]; 494 495 } 496 while([nextEvent type] != NSLeftMouseUp); 497 } 498 [controller parametersChanged:self]; 499} 500 501- (void)mouseDown:(NSEvent *)theEvent 502{ 503 NSPoint p; 504 unsigned count = [theEvent clickCount]; 505 506 p = [theEvent locationInWindow]; 507 p = [self convertPoint: p fromView: nil]; 508 509 if (count == 1) 510 { 511 if ([pathLumi pointOnControlHandle:p]) 512 { 513 NSLog(@"Luminance path hit"); 514 [self moveControlPoint:p ofCurve:pathLumi]; 515 } 516 else if ([pathR pointOnControlHandle:p]) 517 { 518 NSLog(@"Red path hit"); 519 [self moveControlPoint:p ofCurve:pathR]; 520 } 521 else if ([pathG pointOnControlHandle:p]) 522 { 523 NSLog(@"Green path hit"); 524 [self moveControlPoint:p ofCurve:pathG]; 525 } 526 else if ([pathB pointOnControlHandle:p]) 527 { 528 NSLog(@"Blue path hit"); 529 [self moveControlPoint:p ofCurve:pathB]; 530 } 531 } 532 else 533 { 534 NSLog(@"click count: %d", count); 535 } 536} 537 538@end 539