1/*
2    PPCanvasView_ColorRampToolOverlay.m
3
4    Copyright 2013-2018 Josh Freeman
5    http://www.twilightedge.com
6
7    This file is part of PikoPixel for Mac OS X and GNUstep.
8    PikoPixel is a graphical application for drawing & editing pixel-art images.
9
10    PikoPixel is free software: you can redistribute it and/or modify it under
11    the terms of the GNU Affero General Public License as published by the
12    Free Software Foundation, either version 3 of the License, or (at your
13    option) any later version approved for PikoPixel by its copyright holder (or
14    an authorized proxy).
15
16    PikoPixel is distributed in the hope that it will be useful, but WITHOUT ANY
17    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18    FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19    details.
20
21    You should have received a copy of the GNU Affero General Public License
22    along with this program. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25#import "PPCanvasView.h"
26
27#import "NSBitmapImageRep_PPUtilities.h"
28#import "NSBezierPath_PPUtilities.h"
29#import "NSColor_PPUtilities.h"
30#import "PPGeometry.h"
31
32
33#define kMinZoomFactorToDrawOverlay             5
34
35#define kMinZoomFactorToDrawWideOutlines        14
36#define kMinZoomFactorToDrawXMarks              8
37
38
39#define kOverlayPathLineWidth_WideOutline       (2.0)
40#define kOverlayPathLineWidth_NarrowOutline     (0.0)
41
42#define kOverlayPathLineWidth_XMarkLine         (0.0)
43#define kOverlayPathLineWidth_XMarkHalo         (2.0)
44
45
46#define kUIColor_ColorRampToolOverlay_Outline                           \
47            [NSColor ppDiagonalLinePatternColorWithLineWidth: 2.0f      \
48                                color1: [NSColor blackColor]            \
49                                color2: [NSColor whiteColor]]
50
51#define kUIColor_ColorRampToolOverlay_XMarkLine                         \
52            [NSColor ppSRGBColorWithWhite: 0.6 alpha: 0.7]
53
54#define kUIColor_ColorRampToolOverlay_XMarkHalo                         \
55            [NSColor ppSRGBColorWithWhite: 0.85 alpha: 0.7]
56
57
58static NSColor *gOverlayColor_Outline = nil, *gOverlayColor_XMarkLine = nil,
59                *gOverlayColor_XMarkHalo = nil;
60
61
62@implementation PPCanvasView (ColorRampToolOverlay)
63
64+ (void) initializeColorRampToolOverlay
65{
66    gOverlayColor_Outline = [kUIColor_ColorRampToolOverlay_Outline retain];
67
68    gOverlayColor_XMarkLine = [kUIColor_ColorRampToolOverlay_XMarkLine retain];
69
70    gOverlayColor_XMarkHalo = [kUIColor_ColorRampToolOverlay_XMarkHalo retain];
71}
72
73- (bool) initColorRampToolOverlayMembers
74{
75    _colorRampToolOverlayPath_Outline = [[NSBezierPath bezierPath] retain];
76    _colorRampToolOverlayPath_XMarks = [[NSBezierPath bezierPath] retain];
77
78    if (!_colorRampToolOverlayPath_Outline || !_colorRampToolOverlayPath_XMarks)
79    {
80        goto ERROR;
81    }
82
83    return YES;
84
85ERROR:
86    return NO;
87}
88
89- (void) deallocColorRampToolOverlayMembers
90{
91    [_colorRampToolOverlayPath_Outline release];
92    _colorRampToolOverlayPath_Outline = nil;
93
94    [_colorRampToolOverlayPath_XMarks release];
95    _colorRampToolOverlayPath_XMarks = nil;
96}
97
98- (void) setColorRampToolOverlayToMask: (NSBitmapImageRep *) maskBitmap
99            maskBounds: (NSRect) maskBounds
100{
101    NSAffineTransform *canvasViewTransform;
102    NSRect outlinePathDisplayBounds, displayClippingBounds;
103    CGFloat outlinePathLineWidth, outlinePathLineHalfWidth, canvasDisplayOutsetAmount;
104
105    [self clearColorRampToolOverlay];
106
107    if (![maskBitmap ppIsMaskBitmap])
108    {
109        goto ERROR;
110    }
111
112    maskBounds = NSIntersectionRect(PPGeometry_PixelBoundsCoveredByRect(maskBounds),
113                                    [maskBitmap ppFrameInPixels]);
114
115    if (NSIsEmptyRect(maskBounds))
116    {
117        goto ERROR;
118    }
119
120    if (_zoomFactor < kMinZoomFactorToDrawOverlay)
121    {
122        return;
123    }
124
125    canvasViewTransform = [NSAffineTransform transform];
126
127    if (!canvasViewTransform)
128    {
129        goto ERROR;
130    }
131
132    [canvasViewTransform translateXBy: _canvasDrawingOffset.x + 0.5f
133                            yBy: _canvasDrawingOffset.y - 0.5f];
134
135    [canvasViewTransform scaleBy: _zoomFactor];
136
137
138    if (_hasSelectionOutline)
139    {
140        [_colorRampToolOverlayPath_XMarks
141                                    ppAppendXMarksForUnmaskedPixelsInMaskBitmap: maskBitmap
142                                    inBounds: maskBounds];
143
144        [_colorRampToolOverlayPath_XMarks transformUsingAffineTransform: canvasViewTransform];
145    }
146
147    [_colorRampToolOverlayPath_Outline appendBezierPathWithRect: maskBounds];
148
149    if (maskBounds.size.width > 1.0)
150    {
151        [_colorRampToolOverlayPath_Outline
152                                        ppAppendPixelColumnSeparatorLinesInBounds: maskBounds];
153    }
154    else if (maskBounds.size.height > 1.0)
155    {
156        [_colorRampToolOverlayPath_Outline ppAppendPixelRowSeparatorLinesInBounds: maskBounds];
157    }
158
159    [_colorRampToolOverlayPath_Outline transformUsingAffineTransform: canvasViewTransform];
160
161    outlinePathLineWidth =
162        (_zoomFactor >= kMinZoomFactorToDrawWideOutlines) ?
163            kOverlayPathLineWidth_WideOutline : kOverlayPathLineWidth_NarrowOutline;
164
165    outlinePathLineHalfWidth = outlinePathLineWidth / 2.0f;
166
167    [_colorRampToolOverlayPath_Outline setLineWidth: outlinePathLineWidth];
168
169    outlinePathDisplayBounds = [_colorRampToolOverlayPath_Outline bounds];
170
171    if (outlinePathLineHalfWidth > 0)
172    {
173        // outset outlinePathDisplayBounds to account for path's linewidth
174        outlinePathDisplayBounds = NSInsetRect(outlinePathDisplayBounds,
175                                                -outlinePathLineHalfWidth,
176                                                -outlinePathLineHalfWidth);
177    }
178
179    // allow the display bounds to extend past the canvas edges to account for the linewidth
180
181    canvasDisplayOutsetAmount = 1.0f + outlinePathLineHalfWidth;
182
183    displayClippingBounds = NSInsetRect(_offsetZoomedVisibleCanvasBounds,
184                                        -canvasDisplayOutsetAmount,
185                                        -canvasDisplayOutsetAmount);
186
187    _colorRampToolOverlayDisplayBounds =
188        PPGeometry_PixelBoundsCoveredByRect(
189                        NSIntersectionRect(outlinePathDisplayBounds, displayClippingBounds));
190
191    if (!NSIsEmptyRect(_colorRampToolOverlayDisplayBounds))
192    {
193        _shouldDisplayColorRampToolOverlay = YES;
194
195        [self setNeedsDisplayInRect: _colorRampToolOverlayDisplayBounds];
196    }
197
198    return;
199
200ERROR:
201    return;
202}
203
204- (void) clearColorRampToolOverlay
205{
206    [_colorRampToolOverlayPath_Outline removeAllPoints];
207    [_colorRampToolOverlayPath_XMarks removeAllPoints];
208
209    if (_shouldDisplayColorRampToolOverlay)
210    {
211        [self setNeedsDisplayInRect: _colorRampToolOverlayDisplayBounds];
212    }
213
214    _shouldDisplayColorRampToolOverlay = NO;
215    _colorRampToolOverlayDisplayBounds = NSZeroRect;
216}
217
218- (void) drawColorRampToolOverlay
219{
220    if (!_shouldDisplayColorRampToolOverlay)
221        return;
222
223    if (![_colorRampToolOverlayPath_XMarks isEmpty]
224        && (_zoomFactor >= kMinZoomFactorToDrawXMarks))
225    {
226        [gOverlayColor_XMarkHalo set];
227        [_colorRampToolOverlayPath_XMarks setLineWidth: kOverlayPathLineWidth_XMarkHalo];
228        [_colorRampToolOverlayPath_XMarks stroke];
229
230        [gOverlayColor_XMarkLine set];
231        [_colorRampToolOverlayPath_XMarks setLineWidth: kOverlayPathLineWidth_XMarkLine];
232        [_colorRampToolOverlayPath_XMarks stroke];
233    }
234
235    [gOverlayColor_Outline set];
236    [_colorRampToolOverlayPath_Outline stroke];
237}
238
239@end
240