2
* Copyright (c) 2008,2009 Lukáš Tvrdý <lukast.dev@gmail.com>
3
* Copyright (c) 2010 José Luis Vergara <pentalis@gmail.com>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
#include "kis_hatching_paintop.h"
21
#include "kis_hatching_paintop_settings.h"
27
#include <KoColorSpace.h>
29
#include <kis_image.h>
30
#include <kis_debug.h>
32
#include <kis_global.h>
33
#include <kis_paint_device.h>
34
#include <kis_painter.h>
35
#include <kis_types.h>
36
#include <kis_paintop.h>
37
#include <kis_brush_based_paintop.h>
38
#include <kis_paint_information.h>
40
#include <kis_pressure_opacity_option.h>
42
#include <KoColorSpaceRegistry.h>
44
KisHatchingPaintOp::KisHatchingPaintOp(const KisHatchingPaintOpSettings *settings, KisPainter * painter, KisImageWSP image)
45
: KisBrushBasedPaintOp(settings, painter)
48
m_settings = new KisHatchingPaintOpSettings();
49
settings->initializeTwin(m_settings);
51
m_hatchingBrush = new HatchingBrush(m_settings);
53
m_crosshatchingOption.readOptionSetting(settings);
54
m_separationOption.readOptionSetting(settings);
55
m_thicknessOption.readOptionSetting(settings);
56
m_opacityOption.readOptionSetting(settings);
57
m_sizeOption.readOptionSetting(settings);
58
m_crosshatchingOption.sensor()->reset();
59
m_separationOption.sensor()->reset();
60
m_thicknessOption.sensor()->reset();
61
m_opacityOption.sensor()->reset();
62
m_sizeOption.sensor()->reset();
65
KisHatchingPaintOp::~KisHatchingPaintOp()
67
delete m_hatchingBrush;
70
double KisHatchingPaintOp::paintAt(const KisPaintInformation& info)
72
//------START SIMPLE ERROR CATCHING-------
73
if (!painter()->device()) return 1;
75
m_hatchedDab = new KisPaintDevice(painter()->device()->colorSpace());
77
m_hatchedDab->clear();
79
//Simple convenience renaming, I'm thinking of removing these inherited quirks
80
KisBrushSP brush = m_brush;
81
KisPaintDeviceSP device = painter()->device();
83
//Macro to catch errors
86
//----------SIMPLE error catching code, maybe it's not even needed------
88
if (!brush->canPaintFor(info)) return 1;
90
//SENSOR-depending settings
91
m_settings->crosshatchingsensorvalue = m_crosshatchingOption.apply(info);
92
m_settings->separationsensorvalue = m_separationOption.apply(info);
93
m_settings->thicknesssensorvalue = KisPaintOp::scaleForPressure(m_thicknessOption.apply(info));
95
double scale = KisPaintOp::scaleForPressure(m_sizeOption.apply(info));
96
if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return 1.0;
98
setCurrentScale(scale);
100
quint8 origOpacity = m_opacityOption.apply(painter(), info);
102
//-----------POSITIONING code----------
103
QPointF hotSpot = brush->hotSpot(scale, scale);
104
QPointF pt = info.pos() - hotSpot;
107
double xFraction, yFraction;
109
splitCoordinate(pt.x(), &x, &xFraction);
110
splitCoordinate(pt.y(), &y, &yFraction);
112
if (!m_settings->subpixelprecision) {
116
//--------END POSITIONING CODE-----------
118
//DECLARING EMPTY pixel-only paint device, note that it is a smart pointer
119
KisFixedPaintDeviceSP maskDab = 0;
121
/*--------copypasted from SmudgeOp-------
122
---This IF-ELSE block is used to turn the mask created in the BrushTip dialogue
123
into a beautiful SELECTION MASK (it's an opacity multiplier), intended to give
124
the brush a "brush feel" (soft borders, round shape) despite it comes from a
125
simple, ugly, hatched rectangle.*/
126
if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) {
127
maskDab = brush->paintDevice(device->colorSpace(), scale, 0.0, info, xFraction, yFraction);
128
maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8());
130
maskDab = cachedDab();
131
KoColor color = painter()->paintColor();
132
color.convertTo(maskDab->colorSpace());
133
brush->mask(maskDab, color, scale, scale, 0.0, info, xFraction, yFraction);
134
maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8());
137
/*-----Convenient renaming for the limits of the maskDab, this will be used
138
to hatch a dab of just the right size------*/
139
qint32 sw = maskDab->bounds().width();
140
qint32 sh = maskDab->bounds().height();
142
//------This If_block pre-fills the future m_hatchedDab with a pretty backgroundColor
143
if (m_settings->opaquebackground) {
144
KoColor aersh = painter()->backgroundColor();
145
m_hatchedDab->fill(0, 0, (sw-1), (sh-1), aersh.data()); //this plus yellow background = french fry brush
148
// Trick for moire pattern to look better
149
bool donotbasehatch = false;
151
/* If block describing how to stack hatching passes to generate
152
crosshatching according to user specifications */
153
if (m_settings->enabledcurvecrosshatching) {
154
if (m_settings->perpendicular) {
155
if (m_settings->crosshatchingsensorvalue > 0.5)
156
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor());
158
else if (m_settings->minusthenplus) {
159
if (m_settings->crosshatchingsensorvalue > 0.33)
160
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor());
161
if (m_settings->crosshatchingsensorvalue > 0.67)
162
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor());
164
else if (m_settings->plusthenminus) {
165
if (m_settings->crosshatchingsensorvalue > 0.33)
166
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor());
167
if (m_settings->crosshatchingsensorvalue > 0.67)
168
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor());
170
else if (m_settings->moirepattern) {
171
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->crosshatchingsensorvalue)*180), painter()->paintColor());
172
donotbasehatch = true;
176
if (m_settings->perpendicular) {
177
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor());
179
else if (m_settings->minusthenplus) {
180
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor());
181
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor());
183
else if (m_settings->plusthenminus) {
184
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor());
185
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor());
187
else if (m_settings->moirepattern) {
188
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-10), painter()->paintColor());
193
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, m_settings->angle, painter()->paintColor());
195
// The most important line, the one that paints to the screen.
196
painter()->bitBltWithFixedSelection(x, y, m_hatchedDab, maskDab, sw, sh);
197
painter()->setOpacity(origOpacity);
199
/*-----It took me very long to realize the importance of this line, this is
200
the line that makes all brushes be slow, even if they're small, yay!-------*/
201
return spacing(scale);
204
double KisHatchingPaintOp::spinAngle(double spin)
206
double tempangle = m_settings->angle + spin;
212
tempangle = fabs(fmod(tempangle, 180));
214
if ((tempangle >= 0) && (tempangle <= 90))
215
return factor * tempangle;
216
else if ((tempangle > 90) && (tempangle <= 180))
217
return factor * -(180 - tempangle);
219
return 0; // this should never be executed except if NAN