chalk

kis_brush.cpp
1/*
2 * Copyright (c) 1999 Matthias Elter <me@kde.org>
3 * Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
4 * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
5 * Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
6 * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#ifdef HAVE_SYS_TYPES_H
28#include <sys/types.h>
29#endif
30
31#include <netinet/in.h>
32#include <limits.h>
33#include <stdlib.h>
34#include <cfloat>
35
36#include <tqfile.h>
37#include <tqimage.h>
38#include <tqpoint.h>
39#include <tqvaluevector.h>
40
41#include <kdebug.h>
42#include <tdelocale.h>
43
44#include <kis_meta_registry.h>
45#include "kis_paint_device.h"
46#include "kis_global.h"
47#include "kis_brush.h"
48#include "kis_alpha_mask.h"
49#include "kis_colorspace_factory_registry.h"
50#include "kis_iterators_pixel.h"
51#include "kis_image.h"
52
53
54namespace {
55 struct GimpBrushV1Header {
56 TQ_UINT32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
57 TQ_UINT32 version; /* brush file version # */
58 TQ_UINT32 width; /* width of brush */
59 TQ_UINT32 height; /* height of brush */
60 TQ_UINT32 bytes; /* depth of brush in bytes */
61 };
62
64 struct GimpBrushHeader {
65 TQ_UINT32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
66 TQ_UINT32 version; /* brush file version # */
67 TQ_UINT32 width; /* width of brush */
68 TQ_UINT32 height; /* height of brush */
69 TQ_UINT32 bytes; /* depth of brush in bytes */
70
71 /* The following are only defined in version 2 */
72 TQ_UINT32 magic_number; /* GIMP brush magic number */
73 TQ_UINT32 spacing; /* brush spacing as % of width & height, 0 - 1000 */
74 };
75
76 // Needed, or the GIMP won't open it!
77 TQ_UINT32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0);
78}
79
80#define DEFAULT_SPACING 0.25
81#define MAXIMUM_SCALE 2
82
83KisBrush::KisBrush(const TQString& filename) : super(filename)
84{
85 m_brushType = INVALID;
86 m_ownData = true;
87 m_useColorAsMask = false;
88 m_hasColor = false;
89 m_spacing = DEFAULT_SPACING;
90 m_boundary = 0;
91}
92
93KisBrush::KisBrush(const TQString& filename,
94 const TQByteArray& data,
95 TQ_UINT32 & dataPos) : super(filename)
96{
97 m_brushType = INVALID;
98 m_ownData = false;
99 m_useColorAsMask = false;
100 m_hasColor = false;
101 m_spacing = DEFAULT_SPACING;
102 m_boundary = 0;
103
104 m_data.setRawData(data.data() + dataPos, data.size() - dataPos);
105 init();
106 m_data.resetRawData(data.data() + dataPos, data.size() - dataPos);
107 dataPos += m_header_size + (width() * height() * m_bytes);
108}
109
110KisBrush::KisBrush(KisPaintDevice* image, int x, int y, int w, int h)
111 : super(TQString(""))
112{
113 m_brushType = INVALID;
114 m_ownData = true;
115 m_useColorAsMask = false;
116 m_hasColor = true;
117 m_spacing = DEFAULT_SPACING;
118 m_boundary = 0;
119
120 initFromPaintDev(image, x, y, w, h);
121}
122
123KisBrush::KisBrush(const TQImage& image, const TQString& name)
124 : super(TQString(""))
125{
126 m_ownData = false;
127 m_useColorAsMask = false;
128 m_hasColor = true;
129 m_spacing = DEFAULT_SPACING;
130 m_boundary = 0;
131
132 setImage(image);
133 setName(name);
134 setBrushType(IMAGE);
135}
136
137
138KisBrush::~KisBrush()
139{
140 m_scaledBrushes.clear();
141 delete m_boundary;
142}
143
144bool KisBrush::load()
145{
146 if (m_ownData) {
147 TQFile file(filename());
148 file.open(IO_ReadOnly);
149 m_data = file.readAll();
150 file.close();
151 }
152 return init();
153}
154
155bool KisBrush::init()
156{
157 GimpBrushHeader bh;
158
159 if (sizeof(GimpBrushHeader) > m_data.size()) {
160 return false;
161 }
162
163 memcpy(&bh, &m_data[0], sizeof(GimpBrushHeader));
164 bh.header_size = ntohl(bh.header_size);
165 m_header_size = bh.header_size;
166
167 bh.version = ntohl(bh.version);
168 m_version = bh.version;
169
170 bh.width = ntohl(bh.width);
171 bh.height = ntohl(bh.height);
172
173 bh.bytes = ntohl(bh.bytes);
174 m_bytes = bh.bytes;
175
176 bh.magic_number = ntohl(bh.magic_number);
177 m_magic_number = bh.magic_number;
178
179 if (bh.version == 1) {
180 // No spacing in version 1 files so use Gimp default
181 bh.spacing = static_cast<int>(DEFAULT_SPACING * 100);
182 }
183 else {
184 bh.spacing = ntohl(bh.spacing);
185
186 if (bh.spacing > 1000) {
187 return false;
188 }
189 }
190
191 setSpacing(bh.spacing / 100.0);
192
193 if (bh.header_size > m_data.size() || bh.header_size == 0) {
194 return false;
195 }
196
197 TQString name;
198
199 if (bh.version == 1) {
200 // Version 1 has no magic number or spacing, so the name
201 // is at a different offset. Character encoding is undefined.
202 const char *text = &m_data[sizeof(GimpBrushV1Header)];
203 name = TQString::fromAscii(text, bh.header_size - sizeof(GimpBrushV1Header));
204 } else {
205 // ### Version = 3->cinepaint; may be float16 data!
206 // Version >=2: UTF-8 encoding is used
207 name = TQString::fromUtf8(&m_data[sizeof(GimpBrushHeader)],
208 bh.header_size - sizeof(GimpBrushHeader));
209 }
210
211 setName(i18n(name.ascii())); // Ascii? And what with real UTF-8 chars?
212
213 if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) {
214 return false;
215 }
216
217 TQ_INT32 k = bh.header_size;
218
219 if (bh.bytes == 1) {
220 // Grayscale
221
222 if (static_cast<TQ_UINT32>(k + bh.width * bh.height) > m_data.size()) {
223 return false;
224 }
225
226 m_brushType = MASK;
227 m_hasColor = false;
228
229 for (TQ_UINT32 y = 0; y < bh.height; y++) {
230 for (TQ_UINT32 x = 0; x < bh.width; x++, k++) {
231 TQ_INT32 val = 255 - static_cast<uchar>(m_data[k]);
232 m_img.setPixel(x, y, tqRgb(val, val, val));
233 }
234 }
235 } else if (bh.bytes == 4) {
236 // RGBA
237
238 if (static_cast<TQ_UINT32>(k + (bh.width * bh.height * 4)) > m_data.size()) {
239 return false;
240 }
241
242 m_brushType = IMAGE;
243 m_img.setAlphaBuffer(true);
244 m_hasColor = true;
245
246 for (TQ_UINT32 y = 0; y < bh.height; y++) {
247 for (TQ_UINT32 x = 0; x < bh.width; x++, k += 4) {
248 m_img.setPixel(x, y, tqRgba(m_data[k],
249 m_data[k+1],
250 m_data[k+2],
251 m_data[k+3]));
252 }
253 }
254 } else {
255 return false;
256 }
257
258 setWidth(m_img.width());
259 setHeight(m_img.height());
260 //createScaledBrushes();
261 if (m_ownData) {
262 m_data.resize(0); // Save some memory, we're using enough of it as it is.
263 }
264
265
266 if (m_img.width() == 0 || m_img.height() == 0)
267 setValid(false);
268 else
269 setValid(true);
270
271 return true;
272}
273
274bool KisBrush::initFromPaintDev(KisPaintDevice* image, int x, int y, int w, int h) {
275 // Forcefully convert to RGBA8
276 // XXX profile and exposure?
277 setImage(image->convertToTQImage(0, x, y, w, h));
278 setName(image->name());
279
280 m_brushType = IMAGE;
281 m_hasColor = true;
282
283 return true;
284}
285
286bool KisBrush::save()
287{
288 TQFile file(filename());
289 file.open(IO_WriteOnly | IO_Truncate);
290 bool ok = saveToDevice(&file);
291 file.close();
292 return ok;
293}
294
295bool KisBrush::saveToDevice(TQIODevice* dev) const
296{
297 GimpBrushHeader bh;
298 TQCString utf8Name = name().utf8(); // Names in v2 brushes are in UTF-8
299 char const* name = utf8Name.data();
300 int nameLength = tqstrlen(name);
301 int wrote;
302
303 bh.header_size = htonl(sizeof(GimpBrushHeader) + nameLength);
304 bh.version = htonl(2); // Only RGBA8 data needed atm, no cinepaint stuff
305 bh.width = htonl(width());
306 bh.height = htonl(height());
307 // Hardcoded, 4 bytes RGBA or 1 byte GREY
308 if (!hasColor())
309 bh.bytes = htonl(1);
310 else
311 bh.bytes = htonl(4);
312 bh.magic_number = htonl(GimpV2BrushMagic);
313 bh.spacing = htonl(static_cast<TQ_UINT32>(spacing() * 100.0));
314
315 // Write header: first bh, then the name
316 TQByteArray bytes;
317 bytes.setRawData(reinterpret_cast<char*>(&bh), sizeof(GimpBrushHeader));
318 wrote = dev->writeBlock(bytes);
319 bytes.resetRawData(reinterpret_cast<char*>(&bh), sizeof(GimpBrushHeader));
320
321 if (wrote == -1)
322 return false;
323
324 wrote = dev->writeBlock(name, nameLength); // No +1 for the trailing NULL it seems...
325 if (wrote == -1)
326 return false;
327
328 int k = 0;
329
330 if (!hasColor()) {
331 bytes.resize(width() * height());
332 for (TQ_INT32 y = 0; y < height(); y++) {
333 for (TQ_INT32 x = 0; x < width(); x++) {
334 TQRgb c = m_img.pixel(x, y);
335 bytes[k++] = static_cast<char>(255 - tqRed(c)); // red == blue == green
336 }
337 }
338 } else {
339 bytes.resize(width() * height() * 4);
340 for (TQ_INT32 y = 0; y < height(); y++) {
341 for (TQ_INT32 x = 0; x < width(); x++) {
342 // order for gimp brushes, v2 is: RGBA
343 TQRgb pixel = m_img.pixel(x,y);
344 bytes[k++] = static_cast<char>(tqRed(pixel));
345 bytes[k++] = static_cast<char>(tqGreen(pixel));
346 bytes[k++] = static_cast<char>(tqBlue(pixel));
347 bytes[k++] = static_cast<char>(tqAlpha(pixel));
348 }
349 }
350 }
351
352 wrote = dev->writeBlock(bytes);
353 if (wrote == -1)
354 return false;
355
356 return true;
357}
358
359TQImage KisBrush::img()
360{
361 TQImage image = m_img;
362
363 if (hasColor() && useColorAsMask()) {
364 image.detach();
365
366 for (int x = 0; x < image.width(); x++) {
367 for (int y = 0; y < image.height(); y++) {
368 TQRgb c = image.pixel(x, y);
369 int a = (tqGray(c) * tqAlpha(c)) / 255;
370 image.setPixel(x, y, tqRgba(a, 0, a, a));
371 }
372 }
373 }
374
375 return image;
376}
377
378KisAlphaMaskSP KisBrush::mask(const KisPaintInformation& info, double subPixelX, double subPixelY) const
379{
380 if (m_scaledBrushes.isEmpty()) {
381 createScaledBrushes();
382 }
383
384 double scale = scaleForPressure(info.pressure);
385
386 const ScaledBrush *aboveBrush = 0;
387 const ScaledBrush *belowBrush = 0;
388
389 findScaledBrushes(scale, &aboveBrush, &belowBrush);
390 Q_ASSERT(aboveBrush != 0);
391
392 KisAlphaMaskSP outputMask = 0;
393
394 if (belowBrush != 0) {
395 // We're in between two masks. Interpolate between them.
396
397 KisAlphaMaskSP scaledAboveMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY);
398 KisAlphaMaskSP scaledBelowMask = scaleMask(belowBrush, scale, subPixelX, subPixelY);
399
400 double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale());
401
402 outputMask = KisAlphaMask::interpolate(scaledBelowMask, scaledAboveMask, t);
403 } else {
404 if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) {
405 // Exact match.
406 outputMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY);
407 } else {
408 // We are smaller than the smallest mask, which is always 1x1.
409 double s = scale / aboveBrush->scale();
410 outputMask = scaleSinglePixelMask(s, aboveBrush->mask()->alphaAt(0, 0), subPixelX, subPixelY);
411 }
412 }
413
414 return outputMask;
415}
416
417KisPaintDeviceSP KisBrush::image(KisColorSpace * /*colorSpace*/, const KisPaintInformation& info, double subPixelX, double subPixelY) const
418{
419 if (m_scaledBrushes.isEmpty()) {
420 createScaledBrushes();
421 }
422
423 double scale = scaleForPressure(info.pressure);
424
425 const ScaledBrush *aboveBrush = 0;
426 const ScaledBrush *belowBrush = 0;
427
428 findScaledBrushes(scale, &aboveBrush, &belowBrush);
429 Q_ASSERT(aboveBrush != 0);
430
431 TQImage outputImage;
432
433 if (belowBrush != 0) {
434 // We're in between two brushes. Interpolate between them.
435
436 TQImage scaledAboveImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY);
437 TQImage scaledBelowImage = scaleImage(belowBrush, scale, subPixelX, subPixelY);
438
439 double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale());
440
441 outputImage = interpolate(scaledBelowImage, scaledAboveImage, t);
442 } else {
443 if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) {
444 // Exact match.
445 outputImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY);
446 } else {
447 // We are smaller than the smallest brush, which is always 1x1.
448 double s = scale / aboveBrush->scale();
449 outputImage = scaleSinglePixelImage(s, aboveBrush->image().pixel(0, 0), subPixelX, subPixelY);
450 }
451 }
452
453 int outputWidth = outputImage.width();
454 int outputHeight = outputImage.height();
455
456 KisPaintDevice *layer = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "brush");
457
458 TQ_CHECK_PTR(layer);
459
460 for (int y = 0; y < outputHeight; y++) {
461 KisHLineIterator iter = layer->createHLineIterator( 0, y, outputWidth, true);
462 for (int x = 0; x < outputWidth; x++) {
463 TQ_UINT8 * p = iter.rawData();
464
465 TQRgb pixel = outputImage.pixel(x, y);
466 int red = tqRed(pixel);
467 int green = tqGreen(pixel);
468 int blue = tqBlue(pixel);
469 int alpha = tqAlpha(pixel);
470
471 // Scaled images are in pre-multiplied alpha form so
472 // divide by alpha.
473 // channel order is BGRA
474 if (alpha != 0) {
475 p[2] = (red * 255) / alpha;
476 p[1] = (green * 255) / alpha;
477 p[0] = (blue * 255) / alpha;
478 p[3] = alpha;
479 }
480
481 ++iter;
482 }
483 }
484
485 return layer;
486}
487
488void KisBrush::setHotSpot(KisPoint pt)
489{
490 double x = pt.x();
491 double y = pt.y();
492
493 if (x < 0)
494 x = 0;
495 else if (x >= width())
496 x = width() - 1;
497
498 if (y < 0)
499 y = 0;
500 else if (y >= height())
501 y = height() - 1;
502
503 m_hotSpot = KisPoint(x, y);
504}
505
506KisPoint KisBrush::hotSpot(const KisPaintInformation& info) const
507{
508 double scale = scaleForPressure(info.pressure);
509 double w = width() * scale;
510 double h = height() * scale;
511
512 // The smallest brush we can produce is a single pixel.
513 if (w < 1) {
514 w = 1;
515 }
516
517 if (h < 1) {
518 h = 1;
519 }
520
521 // XXX: This should take m_hotSpot into account, though it
522 // isn't specified by gimp brushes so it would default to the centre
523 // anyway.
524 KisPoint p(w / 2, h / 2);
525 return p;
526}
527
528enumBrushType KisBrush::brushType() const
529{
530 if (m_brushType == IMAGE && useColorAsMask()) {
531 return MASK;
532 }
533 else {
534 return m_brushType;
535 }
536}
537
538bool KisBrush::hasColor() const
539{
540 return m_hasColor;
541}
542
543void KisBrush::createScaledBrushes() const
544{
545 if (!m_scaledBrushes.isEmpty())
546 m_scaledBrushes.clear();
547
548 // Construct a series of brushes where each one's dimensions are
549 // half the size of the previous one.
550 int width = m_img.width() * MAXIMUM_SCALE;
551 int height = m_img.height() * MAXIMUM_SCALE;
552
553 TQImage scaledImage;
554
555 while (true) {
556
557 if (width >= m_img.width() && height >= m_img.height()) {
558 scaledImage = scaleImage(m_img, width, height);
559 }
560 else {
561 // Scale down the previous image once we're below 1:1.
562 scaledImage = scaleImage(scaledImage, width, height);
563 }
564
565 KisAlphaMaskSP scaledMask = new KisAlphaMask(scaledImage, hasColor());
566 TQ_CHECK_PTR(scaledMask);
567
568 double xScale = static_cast<double>(width) / m_img.width();
569 double yScale = static_cast<double>(height) / m_img.height();
570 double scale = xScale;
571
572 m_scaledBrushes.append(ScaledBrush(scaledMask, hasColor() ? scaledImage : TQImage(), scale, xScale, yScale));
573
574 if (width == 1 && height == 1) {
575 break;
576 }
577
578 // Round up so that we never have to scale an image by less than 1/2.
579 width = (width + 1) / 2;
580 height = (height + 1) / 2;
581
582 }
583
584}
585
586double KisBrush::xSpacing(double pressure) const
587{
588 return width() * scaleForPressure(pressure) * m_spacing;
589}
590
591double KisBrush::ySpacing(double pressure) const
592{
593 return height() * scaleForPressure(pressure) * m_spacing;
594}
595
596double KisBrush::scaleForPressure(double pressure)
597{
598 double scale = pressure / PRESSURE_DEFAULT;
599
600 if (scale < 0) {
601 scale = 0;
602 }
603
604 if (scale > MAXIMUM_SCALE) {
605 scale = MAXIMUM_SCALE;
606 }
607
608 return scale;
609}
610
611TQ_INT32 KisBrush::maskWidth(const KisPaintInformation& info) const
612{
613 // Add one for sub-pixel shift
614 return static_cast<TQ_INT32>(ceil(width() * scaleForPressure(info.pressure)) + 1);
615}
616
617TQ_INT32 KisBrush::maskHeight(const KisPaintInformation& info) const
618{
619 // Add one for sub-pixel shift
620 return static_cast<TQ_INT32>(ceil(height() * scaleForPressure(info.pressure)) + 1);
621}
622
623KisAlphaMaskSP KisBrush::scaleMask(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const
624{
625 // Add one pixel for sub-pixel shifting
626 int dstWidth = static_cast<int>(ceil(scale * width())) + 1;
627 int dstHeight = static_cast<int>(ceil(scale * height())) + 1;
628
629 KisAlphaMaskSP dstMask = new KisAlphaMask(dstWidth, dstHeight);
630 TQ_CHECK_PTR(dstMask);
631
632 KisAlphaMaskSP srcMask = srcBrush->mask();
633
634 // Compute scales to map the scaled brush onto the required scale.
635 double xScale = srcBrush->xScale() / scale;
636 double yScale = srcBrush->yScale() / scale;
637
638 int srcWidth = srcMask->width();
639 int srcHeight = srcMask->height();
640
641 for (int dstY = 0; dstY < dstHeight; dstY++) {
642 for (int dstX = 0; dstX < dstWidth; dstX++) {
643
644 double srcX = (dstX - subPixelX + 0.5) * xScale;
645 double srcY = (dstY - subPixelY + 0.5) * yScale;
646
647 srcX -= 0.5;
648 srcY -= 0.5;
649
650 int leftX = static_cast<int>(srcX);
651
652 if (srcX < 0) {
653 leftX--;
654 }
655
656 double xInterp = srcX - leftX;
657
658 int topY = static_cast<int>(srcY);
659
660 if (srcY < 0) {
661 topY--;
662 }
663
664 double yInterp = srcY - topY;
665
666 TQ_UINT8 topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX, topY) : OPACITY_TRANSPARENT;
667 TQ_UINT8 bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX, topY + 1) : OPACITY_TRANSPARENT;
668 TQ_UINT8 topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX + 1, topY) : OPACITY_TRANSPARENT;
669 TQ_UINT8 bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX + 1, topY + 1) : OPACITY_TRANSPARENT;
670
671 double a = 1 - xInterp;
672 double b = 1 - yInterp;
673
674 // Bi-linear interpolation
675 int d = static_cast<int>(a * b * topLeft
676 + a * (1 - b) * bottomLeft
677 + (1 - a) * b * topRight
678 + (1 - a) * (1 - b) * bottomRight + 0.5);
679
680 if (d < OPACITY_TRANSPARENT) {
681 d = OPACITY_TRANSPARENT;
682 }
683 else
684 if (d > OPACITY_OPAQUE) {
685 d = OPACITY_OPAQUE;
686 }
687
688 dstMask->setAlphaAt(dstX, dstY, static_cast<TQ_UINT8>(d));
689 }
690 }
691
692 return dstMask;
693}
694
695TQImage KisBrush::scaleImage(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const
696{
697 // Add one pixel for sub-pixel shifting
698 int dstWidth = static_cast<int>(ceil(scale * width())) + 1;
699 int dstHeight = static_cast<int>(ceil(scale * height())) + 1;
700
701 TQImage dstImage(dstWidth, dstHeight, 32);
702 dstImage.setAlphaBuffer(true);
703
704 const TQImage srcImage = srcBrush->image();
705
706 // Compute scales to map the scaled brush onto the required scale.
707 double xScale = srcBrush->xScale() / scale;
708 double yScale = srcBrush->yScale() / scale;
709
710 int srcWidth = srcImage.width();
711 int srcHeight = srcImage.height();
712
713 for (int dstY = 0; dstY < dstHeight; dstY++) {
714 for (int dstX = 0; dstX < dstWidth; dstX++) {
715
716 double srcX = (dstX - subPixelX + 0.5) * xScale;
717 double srcY = (dstY - subPixelY + 0.5) * yScale;
718
719 srcX -= 0.5;
720 srcY -= 0.5;
721
722 int leftX = static_cast<int>(srcX);
723
724 if (srcX < 0) {
725 leftX--;
726 }
727
728 double xInterp = srcX - leftX;
729
730 int topY = static_cast<int>(srcY);
731
732 if (srcY < 0) {
733 topY--;
734 }
735
736 double yInterp = srcY - topY;
737
738 TQRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : tqRgba(0, 0, 0, 0);
739 TQRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : tqRgba(0, 0, 0, 0);
740 TQRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : tqRgba(0, 0, 0, 0);
741 TQRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : tqRgba(0, 0, 0, 0);
742
743 double a = 1 - xInterp;
744 double b = 1 - yInterp;
745
746 // Bi-linear interpolation. Image is pre-multiplied by alpha.
747 int red = static_cast<int>(a * b * tqRed(topLeft)
748 + a * (1 - b) * tqRed(bottomLeft)
749 + (1 - a) * b * tqRed(topRight)
750 + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5);
751 int green = static_cast<int>(a * b * tqGreen(topLeft)
752 + a * (1 - b) * tqGreen(bottomLeft)
753 + (1 - a) * b * tqGreen(topRight)
754 + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5);
755 int blue = static_cast<int>(a * b * tqBlue(topLeft)
756 + a * (1 - b) * tqBlue(bottomLeft)
757 + (1 - a) * b * tqBlue(topRight)
758 + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5);
759 int alpha = static_cast<int>(a * b * tqAlpha(topLeft)
760 + a * (1 - b) * tqAlpha(bottomLeft)
761 + (1 - a) * b * tqAlpha(topRight)
762 + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5);
763
764 if (red < 0) {
765 red = 0;
766 }
767 else
768 if (red > 255) {
769 red = 255;
770 }
771
772 if (green < 0) {
773 green = 0;
774 }
775 else
776 if (green > 255) {
777 green = 255;
778 }
779
780 if (blue < 0) {
781 blue = 0;
782 }
783 else
784 if (blue > 255) {
785 blue = 255;
786 }
787
788 if (alpha < 0) {
789 alpha = 0;
790 }
791 else
792 if (alpha > 255) {
793 alpha = 255;
794 }
795
796 dstImage.setPixel(dstX, dstY, tqRgba(red, green, blue, alpha));
797 }
798 }
799
800 return dstImage;
801}
802
803TQImage KisBrush::scaleImage(const TQImage& srcImage, int width, int height)
804{
805 TQImage scaledImage;
806 //TQString filename;
807
808 int srcWidth = srcImage.width();
809 int srcHeight = srcImage.height();
810
811 double xScale = static_cast<double>(srcWidth) / width;
812 double yScale = static_cast<double>(srcHeight) / height;
813
814 if (xScale > 2 + DBL_EPSILON || yScale > 2 + DBL_EPSILON || xScale < 1 - DBL_EPSILON || yScale < 1 - DBL_EPSILON) {
815 // smoothScale gives better results when scaling an image up
816 // or scaling it to less than half size.
817 scaledImage = srcImage.smoothScale(width, height);
818
819 //filename = TQString("smoothScale_%1x%2.png").arg(width).arg(height);
820 }
821 else {
822 scaledImage.create(width, height, 32);
823 scaledImage.setAlphaBuffer(srcImage.hasAlphaBuffer());
824
825 for (int dstY = 0; dstY < height; dstY++) {
826 for (int dstX = 0; dstX < width; dstX++) {
827
828 double srcX = (dstX + 0.5) * xScale;
829 double srcY = (dstY + 0.5) * yScale;
830
831 srcX -= 0.5;
832 srcY -= 0.5;
833
834 int leftX = static_cast<int>(srcX);
835
836 if (srcX < 0) {
837 leftX--;
838 }
839
840 double xInterp = srcX - leftX;
841
842 int topY = static_cast<int>(srcY);
843
844 if (srcY < 0) {
845 topY--;
846 }
847
848 double yInterp = srcY - topY;
849
850 TQRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : tqRgba(0, 0, 0, 0);
851 TQRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : tqRgba(0, 0, 0, 0);
852 TQRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : tqRgba(0, 0, 0, 0);
853 TQRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : tqRgba(0, 0, 0, 0);
854
855 double a = 1 - xInterp;
856 double b = 1 - yInterp;
857
858 int red;
859 int green;
860 int blue;
861 int alpha;
862
863 if (srcImage.hasAlphaBuffer()) {
864 red = static_cast<int>(a * b * tqRed(topLeft) * tqAlpha(topLeft)
865 + a * (1 - b) * tqRed(bottomLeft) * tqAlpha(bottomLeft)
866 + (1 - a) * b * tqRed(topRight) * tqAlpha(topRight)
867 + (1 - a) * (1 - b) * tqRed(bottomRight) * tqAlpha(bottomRight) + 0.5);
868 green = static_cast<int>(a * b * tqGreen(topLeft) * tqAlpha(topLeft)
869 + a * (1 - b) * tqGreen(bottomLeft) * tqAlpha(bottomLeft)
870 + (1 - a) * b * tqGreen(topRight) * tqAlpha(topRight)
871 + (1 - a) * (1 - b) * tqGreen(bottomRight) * tqAlpha(bottomRight) + 0.5);
872 blue = static_cast<int>(a * b * tqBlue(topLeft) * tqAlpha(topLeft)
873 + a * (1 - b) * tqBlue(bottomLeft) * tqAlpha(bottomLeft)
874 + (1 - a) * b * tqBlue(topRight) * tqAlpha(topRight)
875 + (1 - a) * (1 - b) * tqBlue(bottomRight) * tqAlpha(bottomRight) + 0.5);
876 alpha = static_cast<int>(a * b * tqAlpha(topLeft)
877 + a * (1 - b) * tqAlpha(bottomLeft)
878 + (1 - a) * b * tqAlpha(topRight)
879 + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5);
880
881 if (alpha != 0) {
882 red /= alpha;
883 green /= alpha;
884 blue /= alpha;
885 }
886 }
887 else {
888 red = static_cast<int>(a * b * tqRed(topLeft)
889 + a * (1 - b) * tqRed(bottomLeft)
890 + (1 - a) * b * tqRed(topRight)
891 + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5);
892 green = static_cast<int>(a * b * tqGreen(topLeft)
893 + a * (1 - b) * tqGreen(bottomLeft)
894 + (1 - a) * b * tqGreen(topRight)
895 + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5);
896 blue = static_cast<int>(a * b * tqBlue(topLeft)
897 + a * (1 - b) * tqBlue(bottomLeft)
898 + (1 - a) * b * tqBlue(topRight)
899 + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5);
900 alpha = 255;
901 }
902
903 if (red < 0) {
904 red = 0;
905 }
906 else
907 if (red > 255) {
908 red = 255;
909 }
910
911 if (green < 0) {
912 green = 0;
913 }
914 else
915 if (green > 255) {
916 green = 255;
917 }
918
919 if (blue < 0) {
920 blue = 0;
921 }
922 else
923 if (blue > 255) {
924 blue = 255;
925 }
926
927 if (alpha < 0) {
928 alpha = 0;
929 }
930 else
931 if (alpha > 255) {
932 alpha = 255;
933 }
934
935 scaledImage.setPixel(dstX, dstY, tqRgba(red, green, blue, alpha));
936 }
937 }
938
939 //filename = TQString("bilinear_%1x%2.png").arg(width).arg(height);
940 }
941
942 //scaledImage.save(filename, "PNG");
943
944 return scaledImage;
945}
946
947void KisBrush::findScaledBrushes(double scale, const ScaledBrush **aboveBrush, const ScaledBrush **belowBrush) const
948{
949 uint current = 0;
950
951 while (true) {
952 *aboveBrush = &(m_scaledBrushes[current]);
953
954 if (fabs((*aboveBrush)->scale() - scale) < DBL_EPSILON) {
955 // Scale matches exactly
956 break;
957 }
958
959 if (current == m_scaledBrushes.count() - 1) {
960 // This is the last one
961 break;
962 }
963
964 if (scale > m_scaledBrushes[current + 1].scale() + DBL_EPSILON) {
965 // We fit in between the two.
966 *belowBrush = &(m_scaledBrushes[current + 1]);
967 break;
968 }
969
970 current++;
971 }
972}
973
974KisAlphaMaskSP KisBrush::scaleSinglePixelMask(double scale, TQ_UINT8 maskValue, double subPixelX, double subPixelY)
975{
976 int srcWidth = 1;
977 int srcHeight = 1;
978 int dstWidth = 2;
979 int dstHeight = 2;
980 KisAlphaMaskSP outputMask = new KisAlphaMask(dstWidth, dstHeight);
981 TQ_CHECK_PTR(outputMask);
982
983 double a = subPixelX;
984 double b = subPixelY;
985
986 for (int y = 0; y < dstHeight; y++) {
987 for (int x = 0; x < dstWidth; x++) {
988
989 TQ_UINT8 topLeft = (x > 0 && y > 0) ? maskValue : OPACITY_TRANSPARENT;
990 TQ_UINT8 bottomLeft = (x > 0 && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT;
991 TQ_UINT8 topRight = (x < srcWidth && y > 0) ? maskValue : OPACITY_TRANSPARENT;
992 TQ_UINT8 bottomRight = (x < srcWidth && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT;
993
994 // Bi-linear interpolation
995 int d = static_cast<int>(a * b * topLeft
996 + a * (1 - b) * bottomLeft
997 + (1 - a) * b * topRight
998 + (1 - a) * (1 - b) * bottomRight + 0.5);
999
1000 // Multiply by the square of the scale because a 0.5x0.5 pixel
1001 // has 0.25 the value of the 1x1.
1002 d = static_cast<int>(d * scale * scale + 0.5);
1003
1004 if (d < OPACITY_TRANSPARENT) {
1005 d = OPACITY_TRANSPARENT;
1006 }
1007 else
1008 if (d > OPACITY_OPAQUE) {
1009 d = OPACITY_OPAQUE;
1010 }
1011
1012 outputMask->setAlphaAt(x, y, static_cast<TQ_UINT8>(d));
1013 }
1014 }
1015
1016 return outputMask;
1017}
1018
1019TQImage KisBrush::scaleSinglePixelImage(double scale, TQRgb pixel, double subPixelX, double subPixelY)
1020{
1021 int srcWidth = 1;
1022 int srcHeight = 1;
1023 int dstWidth = 2;
1024 int dstHeight = 2;
1025
1026 TQImage outputImage(dstWidth, dstHeight, 32);
1027 outputImage.setAlphaBuffer(true);
1028
1029 double a = subPixelX;
1030 double b = subPixelY;
1031
1032 for (int y = 0; y < dstHeight; y++) {
1033 for (int x = 0; x < dstWidth; x++) {
1034
1035 TQRgb topLeft = (x > 0 && y > 0) ? pixel : tqRgba(0, 0, 0, 0);
1036 TQRgb bottomLeft = (x > 0 && y < srcHeight) ? pixel : tqRgba(0, 0, 0, 0);
1037 TQRgb topRight = (x < srcWidth && y > 0) ? pixel : tqRgba(0, 0, 0, 0);
1038 TQRgb bottomRight = (x < srcWidth && y < srcHeight) ? pixel : tqRgba(0, 0, 0, 0);
1039
1040 // Bi-linear interpolation. Images are in pre-multiplied form.
1041 int red = static_cast<int>(a * b * tqRed(topLeft)
1042 + a * (1 - b) * tqRed(bottomLeft)
1043 + (1 - a) * b * tqRed(topRight)
1044 + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5);
1045 int green = static_cast<int>(a * b * tqGreen(topLeft)
1046 + a * (1 - b) * tqGreen(bottomLeft)
1047 + (1 - a) * b * tqGreen(topRight)
1048 + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5);
1049 int blue = static_cast<int>(a * b * tqBlue(topLeft)
1050 + a * (1 - b) * tqBlue(bottomLeft)
1051 + (1 - a) * b * tqBlue(topRight)
1052 + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5);
1053 int alpha = static_cast<int>(a * b * tqAlpha(topLeft)
1054 + a * (1 - b) * tqAlpha(bottomLeft)
1055 + (1 - a) * b * tqAlpha(topRight)
1056 + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5);
1057
1058 // Multiply by the square of the scale because a 0.5x0.5 pixel
1059 // has 0.25 the value of the 1x1.
1060 alpha = static_cast<int>(alpha * scale * scale + 0.5);
1061
1062 // Apply to the colour channels too since we are
1063 // storing pre-multiplied by alpha.
1064 red = static_cast<int>(red * scale * scale + 0.5);
1065 green = static_cast<int>(green * scale * scale + 0.5);
1066 blue = static_cast<int>(blue * scale * scale + 0.5);
1067
1068 if (red < 0) {
1069 red = 0;
1070 }
1071 else
1072 if (red > 255) {
1073 red = 255;
1074 }
1075
1076 if (green < 0) {
1077 green = 0;
1078 }
1079 else
1080 if (green > 255) {
1081 green = 255;
1082 }
1083
1084 if (blue < 0) {
1085 blue = 0;
1086 }
1087 else
1088 if (blue > 255) {
1089 blue = 255;
1090 }
1091
1092 if (alpha < 0) {
1093 alpha = 0;
1094 }
1095 else
1096 if (alpha > 255) {
1097 alpha = 255;
1098 }
1099
1100 outputImage.setPixel(x, y, tqRgba(red, green, blue, alpha));
1101 }
1102 }
1103
1104 return outputImage;
1105}
1106
1107TQImage KisBrush::interpolate(const TQImage& image1, const TQImage& image2, double t)
1108{
1109 Q_ASSERT((image1.width() == image2.width()) && (image1.height() == image2.height()));
1110 Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
1111
1112 int width = image1.width();
1113 int height = image1.height();
1114
1115 TQImage outputImage(width, height, 32);
1116 outputImage.setAlphaBuffer(true);
1117
1118 for (int x = 0; x < width; x++) {
1119 for (int y = 0; y < height; y++) {
1120 TQRgb image1pixel = image1.pixel(x, y);
1121 TQRgb image2pixel = image2.pixel(x, y);
1122
1123 // Images are in pre-multiplied alpha format.
1124 int red = static_cast<int>((1 - t) * tqRed(image1pixel) + t * tqRed(image2pixel) + 0.5);
1125 int green = static_cast<int>((1 - t) * tqGreen(image1pixel) + t * tqGreen(image2pixel) + 0.5);
1126 int blue = static_cast<int>((1 - t) * tqBlue(image1pixel) + t * tqBlue(image2pixel) + 0.5);
1127 int alpha = static_cast<int>((1 - t) * tqAlpha(image1pixel) + t * tqAlpha(image2pixel) + 0.5);
1128
1129 if (red < 0) {
1130 red = 0;
1131 }
1132 else
1133 if (red > 255) {
1134 red = 255;
1135 }
1136
1137 if (green < 0) {
1138 green = 0;
1139 }
1140 else
1141 if (green > 255) {
1142 green = 255;
1143 }
1144
1145 if (blue < 0) {
1146 blue = 0;
1147 }
1148 else
1149 if (blue > 255) {
1150 blue = 255;
1151 }
1152
1153 if (alpha < 0) {
1154 alpha = 0;
1155 }
1156 else
1157 if (alpha > 255) {
1158 alpha = 255;
1159 }
1160
1161 outputImage.setPixel(x, y, tqRgba(red, green, blue, alpha));
1162 }
1163 }
1164
1165 return outputImage;
1166}
1167
1168KisBrush::ScaledBrush::ScaledBrush()
1169{
1170 m_mask = 0;
1171 m_image = TQImage();
1172 m_scale = 1;
1173 m_xScale = 1;
1174 m_yScale = 1;
1175}
1176
1177KisBrush::ScaledBrush::ScaledBrush(KisAlphaMaskSP scaledMask, const TQImage& scaledImage, double scale, double xScale, double yScale)
1178{
1179 m_mask = scaledMask;
1180 m_image = scaledImage;
1181 m_scale = scale;
1182 m_xScale = xScale;
1183 m_yScale = yScale;
1184
1185 if (!m_image.isNull()) {
1186 // Convert image to pre-multiplied by alpha.
1187
1188 m_image.detach();
1189
1190 for (int y = 0; y < m_image.height(); y++) {
1191 for (int x = 0; x < m_image.width(); x++) {
1192
1193 TQRgb pixel = m_image.pixel(x, y);
1194
1195 int red = tqRed(pixel);
1196 int green = tqGreen(pixel);
1197 int blue = tqBlue(pixel);
1198 int alpha = tqAlpha(pixel);
1199
1200 red = (red * alpha) / 255;
1201 green = (green * alpha) / 255;
1202 blue = (blue * alpha) / 255;
1203
1204 m_image.setPixel(x, y, tqRgba(red, green, blue, alpha));
1205 }
1206 }
1207 }
1208}
1209
1210void KisBrush::setImage(const TQImage& img)
1211{
1212 m_img = img;
1213 m_img.detach();
1214
1215 setWidth(img.width());
1216 setHeight(img.height());
1217
1218 m_scaledBrushes.clear();
1219
1220 setValid(true);
1221}
1222
1223TQ_INT32 KisBrush::width() const
1224{
1225 return m_width;
1226}
1227
1228void KisBrush::setWidth(TQ_INT32 w)
1229{
1230 m_width = w;
1231}
1232
1233TQ_INT32 KisBrush::height() const
1234{
1235 return m_height;
1236}
1237
1238void KisBrush::setHeight(TQ_INT32 h)
1239{
1240 m_height = h;
1241}
1242
1243/*TQImage KisBrush::outline(double pressure) {
1244 KisLayerSP layer = image(KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""),
1245 KisPaintInformation(pressure));
1246 KisBoundary bounds(layer.data());
1247 int w = maskWidth(pressure);
1248 int h = maskHeight(pressure);
1249
1250 bounds.generateBoundary(w, h);
1251 TQPixmap pix(bounds.pixmap(w, h));
1252 TQImage result;
1253 result = pix;
1254 return result;
1255}*/
1256
1257void KisBrush::generateBoundary() {
1258 KisPaintDeviceSP dev;
1259 int w = maskWidth(KisPaintInformation());
1260 int h = maskHeight(KisPaintInformation());
1261
1262 if (brushType() == IMAGE || brushType() == PIPE_IMAGE) {
1263 dev = image(KisMetaRegistry::instance()->csRegistry() ->getColorSpace(KisID("RGBA",""),""), KisPaintInformation());
1264 } else {
1265 KisAlphaMaskSP amask = mask(KisPaintInformation());
1266 KisColorSpace* cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),"");
1267 dev = new KisPaintDevice(cs, "tmp for generateBoundary");
1268 for (int y = 0; y < h; y++) {
1269 KisHLineIteratorPixel it = dev->createHLineIterator(0, y, w, true);
1270 int x = 0;
1271
1272 while(!it.isDone()) {
1273 cs->setAlpha(it.rawData(), amask->alphaAt(x++, y), 1);
1274 ++it;
1275 }
1276 }
1277 }
1278
1279 m_boundary = new KisBoundary(dev);
1280 m_boundary->generateBoundary(w, h);
1281}
1282
1283KisBoundary KisBrush::boundary() {
1284 if (!m_boundary)
1285 generateBoundary();
1286 return *m_boundary;
1287}
1288
1289void KisBrush::makeMaskImage() {
1290 if (!hasColor())
1291 return;
1292
1293 TQImage img;
1294 img.create(width(), height(), 32);
1295
1296 if (m_img.width() == img.width() && m_img.height() == img.height()) {
1297 for (int x = 0; x < width(); x++) {
1298 for (int y = 0; y < height(); y++) {
1299 TQRgb c = m_img.pixel(x, y);
1300 int a = (tqGray(c) * tqAlpha(c)) / 255; // tqGray(black) = 0
1301 img.setPixel(x, y, tqRgba(a, a, a, 255));
1302 }
1303 }
1304
1305 m_img = img;
1306 }
1307
1308 m_brushType = MASK;
1309 m_hasColor = false;
1310 m_useColorAsMask = false;
1311 delete m_boundary;
1312 m_boundary = 0;
1313 m_scaledBrushes.clear();
1314}
1315
1316KisBrush* KisBrush::clone() const {
1317 KisBrush* c = new KisBrush("");
1318 c->m_spacing = m_spacing;
1319 c->m_useColorAsMask = m_useColorAsMask;
1320 c->m_hasColor = m_useColorAsMask;
1321 c->m_img = m_img;
1322 c->m_width = m_width;
1323 c->m_height = m_height;
1324 c->m_ownData = false;
1325 c->m_hotSpot = m_hotSpot;
1326 c->m_brushType = m_brushType;
1327 c->setValid(true);
1328
1329 return c;
1330}
1331
1332#include "kis_brush.moc"
1333
KisColorSpace * getColorSpace(const KisID &csID, const TQString &profileName)
Return the colorspace + profile as named, or NULL if impossible combination.
A colorspace is the definition of a certain color model in Chalk.
virtual void setAlpha(TQ_UINT8 *pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const =0
Set the alpha channel of the given run of pixels to the given value.
A paint device contains the actual pixel data and offers methods to read and write pixels.
KisHLineIteratorPixel createHLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable)
This function return an iterator which points to the first pixel of a horizontal line.
This class keeps information that can be used in the painting process, for example by brushes.
Definition kis_paintop.h:50
A double-based point class that can return it's coordinates approximated to integers.
Definition kis_point.h:28
virtual TQImage img()=0
Returns a TQImage representing this resource.
virtual bool save()=0
Save this resource asynchronously.
virtual bool load()=0
Load this resource.