1 /*
2 Drawpile - a collaborative drawing program.
3
4 Copyright (C) 2008-2019 Calle Laakkonen
5
6 Drawpile 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 3 of the License, or
9 (at your option) any later version.
10
11 Drawpile 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 Drawpile. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "layerstack.h"
21 #include "layerstackobserver.h"
22 #include "layer.h"
23 #include "tile.h"
24 #include "brushmask.h"
25 #include "point.h"
26 #include "blendmodes.h"
27 #include "rasterop.h"
28 #include "concurrent.h"
29
30 #include <QPainter>
31 #include <QImage>
32 #include <QDataStream>
33
34 #define OBSERVERS(notification) for(auto *observer : owner->observers()) observer->notification
35
36 namespace paintcore {
37
38 namespace {
39
40 //! Sample colors at layer edges and return the most frequent color
_sampleEdgeColors(const Layer * layer,bool top,bool right,bool bottom,bool left)41 QColor _sampleEdgeColors(const Layer *layer, bool top, bool right, bool bottom, bool left)
42 {
43 const int STEP = 22;
44 QHash<QRgb, int> colorfreq;
45 // Sample top & bottom edges
46 for(int x=0;x<layer->width();x+=STEP) {
47 int count;
48 QRgb c;
49 if(top) {
50 c = layer->pixelAt(x, 0);
51 count = colorfreq[c];
52 ++count;
53 colorfreq[c] = count;
54 }
55
56 if(bottom) {
57 c = layer->pixelAt(x, layer->height()-1);
58 count = colorfreq[c];
59 ++count;
60 colorfreq[c] = count;
61 }
62 }
63
64 // Sample left & right edges
65 for(int y=0;y<layer->height();y+=STEP) {
66 int count;
67 QRgb c;
68 if(left) {
69 c = layer->pixelAt(0, y);
70 count = colorfreq[c];
71 ++count;
72 colorfreq[c] = count;
73 }
74
75 if(right) {
76 c = layer->pixelAt(layer->width()-1, y);
77 count = colorfreq[c];
78 ++count;
79 colorfreq[c] = count;
80 }
81 }
82
83 // Return the most frequent color
84 QRgb color=0;
85 int freq=0;
86 QHashIterator<QRgb, int> i(colorfreq);
87 while(i.hasNext()) {
88 i.next();
89 int value = i.value();
90 // In the unlikely case of two colors being equally frequent, pick the
91 // smaller one just for consistency. Otherwise the random ordering of
92 // QHash might lead to users getting different results from this.
93 if(value > freq || (value == freq && i.key() < color)) {
94 freq = i.value();
95 color = i.key();
96 }
97 }
98
99 return QColor::fromRgba(qUnpremultiply(color));
100 }
101
102 }
103
104 /**
105 * Construct a layer initialized to a solid color
106 * @param owner the stack to which this layer belongs to
107 * @param id layer ID
108 * @param color layer color
109 * @parma size layer size
110 */
Layer(int id,const QString & title,const QColor & color,const QSize & size)111 Layer::Layer(int id, const QString& title, const QColor& color, const QSize& size)
112 : m_info({id, title}),
113 m_width(size.width()),
114 m_height(size.height()),
115 m_xtiles(Tile::roundTiles(size.width())),
116 m_ytiles(Tile::roundTiles(size.height()))
117 {
118 m_tiles = QVector<Tile>(
119 m_xtiles * m_ytiles,
120 color.alpha() > 0 ? Tile(color) : Tile()
121 );
122 }
123
Layer(int id,const QSize & size)124 Layer::Layer(int id, const QSize &size)
125 : Layer(id, QString(), Qt::transparent, size)
126 {
127 // sublayers are used for indirect drawing and previews
128 }
129
Layer(const QVector<Tile> & tiles,const QSize & size,const LayerInfo & info,const QList<Layer * > sublayers)130 Layer::Layer(const QVector<Tile> &tiles, const QSize &size, const LayerInfo &info, const QList<Layer*> sublayers)
131 : m_info(info),
132 m_tiles(tiles),
133 m_sublayers(sublayers),
134 m_width(size.width()),
135 m_height(size.height()),
136 m_xtiles(Tile::roundTiles(size.width())),
137 m_ytiles(Tile::roundTiles(size.height()))
138 {
139 if(m_xtiles * m_ytiles != m_tiles.size()) {
140 qWarning("Layer constructor: tile vector size mismatch!");
141 m_tiles.resize(m_xtiles * m_ytiles);
142 }
143 }
144
Layer(const Layer & layer)145 Layer::Layer(const Layer &layer)
146 : m_info(layer.m_info),
147 m_changeBounds(layer.m_changeBounds),
148 m_tiles(layer.m_tiles),
149 m_width(layer.m_width), m_height(layer.m_height),
150 m_xtiles(layer.m_xtiles), m_ytiles(layer.m_ytiles)
151 {
152 // Hidden sublayers are deleted layers, kept around only as a performance
153 // optimization. No need to copy them.
154 for(const Layer *sl : layer.sublayers()) {
155 if(!sl->isHidden())
156 m_sublayers.append(new Layer(*sl));
157 }
158 }
159
~Layer()160 Layer::~Layer() {
161 for(Layer *sl : m_sublayers)
162 delete sl;
163 }
164
toImage() const165 QImage Layer::toImage() const {
166 QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
167 int i=0;
168 for(int y=0;y<m_ytiles;++y) {
169 for(int x=0;x<m_xtiles;++x,++i)
170 m_tiles[i].copyToImage(image, x*Tile::SIZE, y*Tile::SIZE);
171 }
172 return image;
173 }
174
toCroppedImage(int * xOffset,int * yOffset) const175 QImage Layer::toCroppedImage(int *xOffset, int *yOffset) const
176 {
177 int top=m_ytiles, bottom=0;
178 int left=m_xtiles, right=0;
179
180 // Find bounding rectangle of non-blank tiles
181 for(int y=0;y<m_ytiles;++y) {
182 for(int x=0;x<m_xtiles;++x) {
183 const Tile &t = m_tiles.at(y*m_xtiles + x);
184 if(!t.isBlank()) {
185 if(x<left)
186 left=x;
187 if(x>right)
188 right=x;
189 if(y<top)
190 top=y;
191 if(y>bottom)
192 bottom=y;
193 }
194 }
195 }
196
197 if(top==m_ytiles) {
198 // Entire layer appears to be blank
199 return QImage();
200 }
201
202 // Copy tiles to image
203 QImage image((right-left+1)*Tile::SIZE, (bottom-top+1)*Tile::SIZE, QImage::Format_ARGB32_Premultiplied);
204 for(int y=top;y<=bottom;++y) {
205 for(int x=left;x<=right;++x) {
206 m_tiles.at(y*m_xtiles+x).copyToImage(image, (x-left)*Tile::SIZE, (y-top)*Tile::SIZE);
207 }
208 }
209
210 if(xOffset)
211 *xOffset = left * Tile::SIZE;
212 if(yOffset)
213 *yOffset = top * Tile::SIZE;
214
215 // TODO pixel perfect cropping
216 return image;
217 }
218
219 /**
220 * @param x
221 * @param y
222 * @return invalid color if x or y is outside image boundaries
223 */
colorAt(int x,int y,int dia) const224 QColor Layer::colorAt(int x, int y, int dia) const
225 {
226 if(x<0 || y<0 || x>=m_width || y>=m_height)
227 return QColor();
228
229 if(dia<2) {
230 const quint32 c = pixelAt(x, y);
231 if(c==0)
232 return QColor();
233
234 return QColor::fromRgb(qUnpremultiply(c));
235
236 } else {
237 return getDabColor(makeColorSamplingStamp(dia/2, QPoint(x,y)));
238 }
239 }
240
pixelAt(int x,int y) const241 QRgb Layer::pixelAt(int x, int y) const
242 {
243 if(x<0 || y<0 || x>=m_width || y>=m_height)
244 return 0;
245
246 const int yindex = y/Tile::SIZE;
247 const int xindex = x/Tile::SIZE;
248
249 return tile(xindex, yindex).pixel(x-xindex*Tile::SIZE, y-yindex*Tile::SIZE);
250 }
251
252 /**
253 * Return a temporary layer with the original image padded and composited with the
254 * content of this layer.
255 *
256 * @param xpos target image position
257 * @param ypos target image position
258 * @param original the image to pad
259 * @param mode compositing mode
260 * @param contextId the ID to assign as the "last edited by" tag
261 */
padImageToTileBoundary(int xpos,int ypos,const QImage & original,BlendMode::Mode mode,int contextId) const262 Layer Layer::padImageToTileBoundary(int xpos, int ypos, const QImage &original, BlendMode::Mode mode, int contextId) const
263 {
264 const int x0 = Tile::roundDown(xpos);
265 const int x1 = qMin(m_width, Tile::roundUp(xpos+original.width()));
266 const int y0 = Tile::roundDown(ypos);
267 const int y1 = qMin(m_height, Tile::roundUp(ypos+original.height()));
268
269 const int w = x1 - x0;
270 const int h = y1 - y0;
271
272 // Pad the image to tile boundaries
273 QImage image;
274 if(image.width() == w && image.height() == h) {
275 image = original;
276
277 } else {
278 image = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
279 QPainter painter(&image);
280
281 if(mode == BlendMode::MODE_REPLACE) {
282 // Replace mode is special: we must copy the original pixels
283 // and do the composition with QPainter, since layer merge
284 // can't distinguish the padding from image transparency
285 for(int y=0;y<h;y+=Tile::SIZE) {
286 for(int x=0;x<w;x+=Tile::SIZE) {
287 tile((x0+x) / Tile::SIZE, (y0+y) / Tile::SIZE).copyToImage(image, x, y);
288 }
289 }
290 painter.setCompositionMode(QPainter::CompositionMode_Source);
291
292 } else {
293 image.fill(0);
294 }
295
296 painter.drawImage(xpos-x0, ypos-y0, original);
297 #if 0 /* debugging aid */
298 painter.setPen(Qt::red);
299 painter.drawLine(0, 0, w-1, 0);
300 painter.drawLine(0, 0, 0, h-1);
301 painter.drawLine(w-1, 0, w-1, h-1);
302 painter.drawLine(0, h-1, w-1, h-1);
303 #endif
304 }
305
306 // Create scratch layers and composite
307 Layer scratch(0, QString(), Qt::transparent, QSize(w,h));
308 Layer imglayer = scratch;
309
310 // Copy image pixels to image layer
311 for(int y=0;y<h;y+=Tile::SIZE) {
312 for(int x=0;x<w;x+=Tile::SIZE) {
313 imglayer.rtile(x/Tile::SIZE, y/Tile::SIZE) = Tile(image, x, y, contextId);
314 }
315 }
316
317 // In replace mode, compositing was already done with QPainter
318 if(mode == BlendMode::MODE_REPLACE) {
319 return imglayer;
320 }
321
322 // Copy tiles from current layer to scratch layer
323 for(int y=0;y<h;y+=Tile::SIZE) {
324 for(int x=0;x<w;x+=Tile::SIZE) {
325 scratch.rtile(x/Tile::SIZE, y/Tile::SIZE) = tile((x+x0)/Tile::SIZE, (y+y0)/Tile::SIZE);
326 }
327 }
328
329 // Merge image using standard layer compositing ops
330 EditableLayer(&imglayer, nullptr, contextId).setBlend(mode);
331 EditableLayer(&scratch, nullptr, contextId).merge(&imglayer);
332
333 return scratch;
334 }
335
336 /**
337 * @brief Get a weighted average of the layer's color, using the given brush mask as the weight
338 *
339 * @param stamp
340 * @return color average
341 */
getDabColor(const BrushStamp & stamp) const342 QColor Layer::getDabColor(const BrushStamp &stamp) const
343 {
344 // This is very much like directDab, instead we only read pixel values
345 const uchar *weights = stamp.mask.data();
346
347 // The mask can overlap multiple tiles
348 const int dia = stamp.mask.diameter();
349 const int bottom = qMin(stamp.top + dia, m_height);
350 const int right = qMin(stamp.left + dia, m_width);
351
352 int y = qMax(0, stamp.top);
353 int yb = stamp.top<0?-stamp.top:0; // y in relation to brush origin
354 const int x0 = qMax(0, stamp.left);
355 const int xb0 = stamp.left<0?-stamp.left:0;
356
357 qreal weight=0, red=0, green=0, blue=0, alpha=0;
358
359 // collect weighted color sums
360 while(y<bottom) {
361 const int yindex = y / Tile::SIZE;
362 const int yt = y - yindex * Tile::SIZE;
363 const int hb = yt+dia-yb < Tile::SIZE ? dia-yb : Tile::SIZE-yt;
364 int x = x0;
365 int xb = xb0; // x in relation to brush origin
366 while(x<right) {
367 const int xindex = x / Tile::SIZE;
368 const int xt = x - xindex * Tile::SIZE;
369 const int wb = xt+dia-xb < Tile::SIZE ? dia-xb : Tile::SIZE-xt;
370 const int i = m_xtiles * yindex + xindex;
371
372 std::array<quint32, 5> avg = m_tiles.at(i).weightedAverage(weights + yb * dia + xb, xt, yt, wb, hb, dia-wb);
373 weight += avg[0];
374 red += avg[1];
375 green += avg[2];
376 blue += avg[3];
377 alpha += avg[4];
378
379 x = (xindex+1) * Tile::SIZE;
380 xb = xb + wb;
381 }
382 y = (yindex+1) * Tile::SIZE;
383 yb = yb + hb;
384 }
385
386 // There must be at least some alpha for the results to make sense
387 if(alpha < stamp.mask.diameter()*stamp.mask.diameter() * 30)
388 return QColor();
389
390 // Calculate final average
391 red /= weight;
392 green /= weight;
393 blue /= weight;
394 alpha /= weight;
395
396 // Unpremultiply
397 red = qMin(1.0, red/alpha);
398 green = qMin(1.0, green/alpha);
399 blue = qMin(1.0, blue/alpha);
400
401 return QColor::fromRgbF(red, green, blue, alpha);
402 }
403
404 /**
405 * Free all tiles that are completely transparent
406 */
optimize()407 void Layer::optimize()
408 {
409 // Optimize tile memory usage
410 for(int i=0;i<m_tiles.size();++i) {
411 if(!m_tiles[i].isNull() && m_tiles[i].isBlank())
412 m_tiles[i] = Tile();
413 }
414
415 // Delete unused sublayers
416 QMutableListIterator<Layer*> li(m_sublayers);
417 while(li.hasNext()) {
418 Layer *sl = li.next();
419 if(sl->isHidden()) {
420 delete sl;
421 li.remove();
422 }
423 }
424 }
425
getSubLayer(int id,BlendMode::Mode blendmode,uchar opacity)426 Layer *Layer::getSubLayer(int id, BlendMode::Mode blendmode, uchar opacity)
427 {
428 Q_ASSERT(id != 0);
429
430 // See if the sublayer exists already
431 for(Layer *sl : m_sublayers)
432 if(sl->id() == id) {
433 if(sl->isHidden()) {
434 // Hidden, reset properties
435 sl->m_tiles.fill(Tile());
436 sl->m_info.opacity = opacity;
437 sl->m_info.blend = blendmode;
438 sl->m_info.hidden = false;
439 sl->m_changeBounds = QRect();
440 }
441 return sl;
442 }
443
444 // Okay, try recycling a sublayer
445 for(Layer *sl : m_sublayers) {
446 if(sl->isHidden()) {
447 // Set these flags directly to avoid markDirty call.
448 // We know the layer is invisible at this point
449 sl->m_tiles.fill(Tile());
450 sl->m_info.id = id;
451 sl->m_info.opacity = opacity;
452 sl->m_info.blend = blendmode;
453 sl->m_info.hidden = false;
454 return sl;
455 }
456 }
457
458 // No available sublayers, create a new one
459 Layer *sl = new Layer(id, QSize(m_width, m_height));
460 sl->m_info.opacity = opacity;
461 sl->m_info.blend = blendmode;
462 m_sublayers.append(sl);
463 return sl;
464 }
465
getVisibleSublayer(int id) const466 const Layer *Layer::getVisibleSublayer(int id) const
467 {
468 for(const Layer *sl : m_sublayers) {
469 if(sl->id() == id && !sl->isHidden())
470 return sl;
471 }
472 return nullptr;
473 }
474
resize(int top,int right,int bottom,int left)475 void EditableLayer::resize(int top, int right, int bottom, int left)
476 {
477 Q_ASSERT(d);
478
479 // Minimize amount of data that needs to be copied
480 d->optimize();
481
482 // Resize sublayers
483 for(Layer *sl : d->m_sublayers)
484 EditableLayer(sl, nullptr, contextId).resize(top, right, bottom, left);
485
486 // Calculate new size
487 int width = left + d->m_width + right;
488 int height = top + d->m_height + bottom;
489
490 int xtiles = Tile::roundTiles(width);
491 int ytiles = Tile::roundTiles(height);
492 QVector<Tile> tiles(xtiles * ytiles);
493
494 // if there is no old content, resizing is simple
495 bool hascontent = false;
496 for(int i=0;i<d->m_tiles.size();++i) {
497 if(!d->m_tiles.at(i).isBlank()) {
498 hascontent = true;
499 break;
500 }
501 }
502 if(!hascontent) {
503 d->m_width = width;
504 d->m_height = height;
505 d->m_xtiles = xtiles;
506 d->m_ytiles = ytiles;
507 d->m_tiles = tiles;
508 return;
509 }
510
511 // Sample colors around the layer edges to determine fill color
512 // for the new tiles
513 Tile bgtile;
514 {
515 QColor bgcolor = _sampleEdgeColors(d, top>0, right>0, bottom>0, left>0);
516 if(bgcolor.alpha()>0)
517 bgtile = Tile(bgcolor);
518 }
519
520 if((left % Tile::SIZE) || (top % Tile::SIZE)) {
521 // If top/left adjustment is not divisble by tile size,
522 // we need to move the layer content
523
524 QImage oldcontent = d->toImage();
525
526 d->m_width = width;
527 d->m_height = height;
528 d->m_xtiles = xtiles;
529 d->m_ytiles = ytiles;
530 d->m_tiles = tiles;
531 if(left<0 || top<0) {
532 int cropx = 0;
533 if(left<0) {
534 cropx = -left;
535 left = 0;
536 }
537 int cropy = 0;
538 if(top<0) {
539 cropy = -top;
540 top = 0;
541 }
542 oldcontent = oldcontent.copy(cropx, cropy, oldcontent.width()-cropx, oldcontent.height()-cropy);
543 }
544
545 d->m_tiles.fill(bgtile);
546
547 // temporarily set the hidden flag, because markDirty must not
548 // be called during a resize operation.
549 const bool hidden = d->m_info.hidden;
550 d->m_info.hidden = true;
551 putImage(left, top, oldcontent, BlendMode::MODE_REPLACE);
552 d->m_info.hidden = hidden;
553
554 } else {
555 // top/left offset is aligned at tile boundary:
556 // existing tile content can be reused
557
558 const int firstrow = Tile::roundTiles(-top);
559 const int firstcol = Tile::roundTiles(-left);
560
561 int oldy = firstrow;
562 for(int y=0;y<ytiles;++y,++oldy) {
563 int oldx = firstcol;
564 const int oldyy = d->m_xtiles * oldy;
565 const int yy = xtiles * y;
566 for(int x=0;x<xtiles;++x,++oldx) {
567 const int i = yy + x;
568
569 if(oldy<0 || oldy>=d->m_ytiles || oldx<0 || oldx>=d->m_xtiles) {
570 tiles[i] = bgtile;
571
572 } else {
573 const int oldi = oldyy + oldx;
574 tiles[i] = d->m_tiles.at(oldi);
575 }
576 }
577 }
578
579 d->m_width = width;
580 d->m_height = height;
581 d->m_xtiles = xtiles;
582 d->m_ytiles = ytiles;
583 d->m_tiles = tiles;
584 }
585 }
586
587 /**
588 * @param opacity
589 */
setOpacity(int opacity)590 void EditableLayer::setOpacity(int opacity)
591 {
592 Q_ASSERT(d);
593 Q_ASSERT(opacity>=0 && opacity<256);
594 if(d->m_info.opacity != opacity) {
595 d->m_info.opacity = opacity;
596 markOpaqueDirty(true);
597 }
598 }
599
setBlend(BlendMode::Mode blend)600 void EditableLayer::setBlend(BlendMode::Mode blend)
601 {
602 Q_ASSERT(d);
603 if(d->m_info.blend != blend) {
604 d->m_info.blend = blend;
605 markOpaqueDirty();
606 }
607 }
608
setCensored(bool censor)609 void EditableLayer::setCensored(bool censor)
610 {
611 Q_ASSERT(d);
612 if(d->m_info.censored != censor) {
613 d->m_info.censored = censor;
614 markOpaqueDirty(true);
615 }
616 }
617
setFixed(bool fixed)618 void EditableLayer::setFixed(bool fixed)
619 {
620 Q_ASSERT(d);
621 if(d->m_info.fixed != fixed) {
622 d->m_info.fixed = fixed;
623 markOpaqueDirty(true);
624 }
625 }
626
627 /**
628 * @param hide new status
629 */
setHidden(bool hide)630 void EditableLayer::setHidden(bool hide)
631 {
632 Q_ASSERT(d);
633 if(d->m_info.hidden != hide) {
634 d->m_info.hidden = hide;
635 markOpaqueDirty(true);
636 }
637 }
638
639 /**
640 * @param x x coordinate
641 * @param y y coordinate
642 * @param image the image to draw
643 * @param mode blending/compositing mode (see protocol::PutImage)
644 */
putImage(int x,int y,QImage image,BlendMode::Mode mode)645 void EditableLayer::putImage(int x, int y, QImage image, BlendMode::Mode mode)
646 {
647 Q_ASSERT(d);
648 Q_ASSERT(image.format() == QImage::Format_ARGB32_Premultiplied);
649
650 // Check if the image is completely outside the layer
651 if(x >= d->m_width || y >= d->m_height || x+image.width() < 0 || y+image.height() < 0)
652 return;
653
654 // Crop image if x or y are negative
655 if(x<0 || y<0) {
656 const int xCrop = x<0 ? -x : 0;
657 const int yCrop = y<0 ? -y : 0;
658
659 image = image.copy(xCrop, yCrop, image.width()-xCrop, image.height()-yCrop);
660 x = qMax(0, x);
661 y = qMax(0, y);
662 }
663
664 const int x0 = Tile::roundDown(x);
665 const int y0 = Tile::roundDown(y);
666
667 const Layer imageLayer = d->padImageToTileBoundary(x, y, image, mode, contextId);
668
669 // Replace this layer's tiles with the scratch tiles
670 const int tx0 = x0 / Tile::SIZE;
671 const int ty0 = y0 / Tile::SIZE;
672 const int tx1 = qMin((x0 + imageLayer.width() - 1) / Tile::SIZE, d->m_xtiles-1);
673 const int ty1 = qMin((y0 + imageLayer.height() - 1) / Tile::SIZE, d->m_ytiles-1);
674
675 for(int ty=ty0;ty<=ty1;++ty) {
676 for(int tx=tx0;tx<=tx1;++tx) {
677 d->rtile(tx, ty) = imageLayer.tile(tx-tx0, ty-ty0);
678 }
679 }
680
681 if(owner && d->isVisible())
682 OBSERVERS(markDirty(QRect(x, y, image.width(), image.height())));
683 }
684
putTile(int col,int row,int repeat,const Tile & tile,int sublayer)685 void EditableLayer::putTile(int col, int row, int repeat, const Tile &tile, int sublayer)
686 {
687 Q_ASSERT(d);
688 if(col<0 || col >= d->m_xtiles || row<0 || row >= d->m_ytiles)
689 return;
690
691 if(sublayer != 0) {
692 // LayerAttributes command can be used to set the sublayer's blendmode
693 // and opacity before calling putTile, if necessary.
694 getEditableSubLayer(sublayer, BlendMode::MODE_NORMAL, 255).putTile(col, row, repeat, tile, 0);
695 return;
696 }
697
698 int i=row*d->m_xtiles+col;
699 const int end = qMin(i+repeat, d->m_tiles.size()-1);
700 for(;i<=end;++i) {
701 d->m_tiles[i] = tile;
702 if(owner && d->isVisible())
703 OBSERVERS(markDirty(i));
704 }
705 }
706
fillRect(const QRect & rectangle,const QColor & color,BlendMode::Mode blendmode)707 void EditableLayer::fillRect(const QRect &rectangle, const QColor &color, BlendMode::Mode blendmode)
708 {
709 const QRect canvas(0, 0, d->m_width, d->m_height);
710
711 if(rectangle.contains(canvas) && (blendmode==BlendMode::MODE_REPLACE || (blendmode==BlendMode::MODE_NORMAL && color.alpha() == 255))) {
712 // Special case: overwrite whole layer
713 d->m_tiles.fill(Tile(color));
714
715 } else {
716 // The usual case: only a portion of the layer is filled or pixel blending is needed
717 QRect rect = rectangle.intersected(canvas);
718
719 uchar mask[Tile::LENGTH];
720 if(blendmode==255)
721 memset(mask, 0xff, Tile::LENGTH);
722 else
723 memset(mask, color.alpha(), Tile::LENGTH);
724
725 const int size = Tile::SIZE;
726 const int bottom = rect.y() + rect.height();
727 const int right = rect.x() + rect.width();
728
729 const int tx0 = rect.x() / size;
730 const int tx1 = (right-1) / size;
731 const int ty0 = rect.y() / size;
732 const int ty1 = (bottom-1) / size;
733
734 bool canIncrOpacity;
735 if(blendmode==255 && color.alpha()==0)
736 canIncrOpacity = false;
737 else
738 canIncrOpacity = findBlendMode(blendmode).flags.testFlag(BlendMode::IncrOpacity);
739
740 for(int ty=ty0;ty<=ty1;++ty) {
741 for(int tx=tx0;tx<=tx1;++tx) {
742 int left = qMax(tx * size, rect.x()) - tx*size;
743 int top = qMax(ty * size, rect.y()) - ty*size;
744 int w = qMin((tx+1)*size, right) - tx*size - left;
745 int h = qMin((ty+1)*size, bottom) - ty*size - top;
746
747 Tile &t = d->m_tiles[ty*d->m_xtiles+tx];
748 t.setLastEditedBy(contextId);
749
750 if(!t.isNull() || canIncrOpacity)
751 t.composite(blendmode, mask, color, left, top, w, h, 0);
752 }
753 }
754 }
755
756 if(owner && d->isVisible())
757 OBSERVERS(markDirty(rectangle));
758 }
759
putBrushStamp(const BrushStamp & bs,const QColor & color,BlendMode::Mode blendmode)760 void EditableLayer::putBrushStamp(const BrushStamp &bs, const QColor &color, BlendMode::Mode blendmode)
761 {
762 Q_ASSERT(d);
763 const int top=bs.top, left=bs.left;
764 const int dia = bs.mask.diameter();
765 const int bottom = qMin(top + dia, d->m_height);
766 const int right = qMin(left + dia, d->m_width);
767
768 if(left+dia<=0 || top+dia<=0 || left>=d->m_width || top>=d->m_height)
769 return;
770
771 // Composite the brush mask onto the layer
772 const uchar *values = bs.mask.data();
773
774 // A single dab can (and often does) span multiple tiles.
775 int y = top<0?0:top;
776 int yb = top<0?-top:0; // y in relation to brush origin
777 const int x0 = left<0?0:left;
778 const int xb0 = left<0?-left:0;
779 while(y<bottom) {
780 const int yindex = y / Tile::SIZE;
781 const int yt = y - yindex * Tile::SIZE;
782 const int hb = yt+dia-yb < Tile::SIZE ? dia-yb : Tile::SIZE-yt;
783 int x = x0;
784 int xb = xb0; // x in relation to brush origin
785 while(x<right) {
786 const int xindex = x / Tile::SIZE;
787 const int xt = x - xindex * Tile::SIZE;
788 const int wb = xt+dia-xb < Tile::SIZE ? dia-xb : Tile::SIZE-xt;
789 const int i = d->m_xtiles * yindex + xindex;
790 d->m_tiles[i].composite(
791 blendmode,
792 values + yb * dia + xb,
793 color,
794 xt, yt,
795 wb, hb,
796 dia-wb
797 );
798 d->m_tiles[i].setLastEditedBy(contextId);
799
800 x = (xindex+1) * Tile::SIZE;
801 xb = xb + wb;
802 }
803 y = (yindex+1) * Tile::SIZE;
804 yb = yb + hb;
805 }
806
807 if(owner && d->isVisible())
808 OBSERVERS(markDirty(QRect(left, top, right-left, bottom-top)));
809 }
810
811 /**
812 * @brief Merge another layer to this layer
813 *
814 * Both layer must be the same size
815 *
816 * @param layer the source layer
817 */
merge(const Layer * layer)818 void EditableLayer::merge(const Layer *layer)
819 {
820 Q_ASSERT(d);
821 Q_ASSERT(layer);
822 Q_ASSERT(layer->m_xtiles == d->m_xtiles);
823 Q_ASSERT(layer->m_ytiles == d->m_ytiles);
824
825 // Gather a list of non-null source tiles to merge
826 QList<int> mergeidx;
827 for(int i=0;i<d->m_tiles.size();++i) {
828 if(!layer->m_tiles.at(i).isNull())
829 mergeidx.append(i);
830 }
831
832 // Detach tile vector explicitly to make sure concurrent modifications
833 // are all done to the same vector
834 d->m_tiles.detach();
835
836 // Merge tiles
837 concurrentForEach<int>(mergeidx, [this, layer](int idx) {
838 d->m_tiles[idx].merge(layer->m_tiles.at(idx), layer->opacity(), layer->blendmode());
839 });
840
841 // Merging a layer does not cause an immediate visual change, so we don't
842 // mark the area as dirty here.
843 }
844
makeBlank()845 void EditableLayer::makeBlank()
846 {
847 Q_ASSERT(d);
848 d->m_tiles.fill(Tile());
849
850 if(owner && d->isVisible())
851 OBSERVERS(markDirty());
852 }
853
854 /**
855 * This is used to end an indirect stroke.
856 * If a sublayer with the given ID does not exist, this function does nothing.
857 * @param id
858 */
mergeSublayer(int id)859 void EditableLayer::mergeSublayer(int id)
860 {
861 Q_ASSERT(d);
862 for(Layer *sl : d->m_sublayers) {
863 if(sl->id() == id) {
864 if(!sl->isHidden()) {
865 merge(sl);
866 // Set hidden flag directly to avoid markDirty call.
867 // The merge should cause no visual change.
868 sl->m_info.hidden = true;
869 }
870 return;
871 }
872 }
873 }
874
mergeAllSublayers()875 void EditableLayer::mergeAllSublayers()
876 {
877 Q_ASSERT(d);
878 for(Layer *sl : d->m_sublayers) {
879 if(sl->id() > 0) {
880 if(!sl->isHidden()) {
881 merge(sl);
882 // Set hidden flag directly to avoid markDirty call.
883 // The merge should cause no visual change.
884 sl->m_info.hidden = true;
885 }
886 return;
887 }
888 }
889 }
890
891 /**
892 * @brief This is used to remove temporary sublayers
893 *
894 * The layer isn't actually deleted, but is just marked as hidden for recycling.
895 * Call optimize() to clean up removed sublayers.
896 * @param id
897 */
removeSublayer(int id)898 void EditableLayer::removeSublayer(int id)
899 {
900 Q_ASSERT(d);
901 for(Layer *sl : d->m_sublayers) {
902 if(sl->id() == id) {
903 EditableLayer(sl, owner, contextId).setHidden(true);
904 return;
905 }
906 }
907 }
908
removePreviews()909 void EditableLayer::removePreviews()
910 {
911 Q_ASSERT(d);
912 for(Layer *sl : d->m_sublayers) {
913 if(sl->id() < 0)
914 EditableLayer(sl, owner, contextId).setHidden(true);
915 }
916 }
917
markOpaqueDirty(bool forceVisible)918 void EditableLayer::markOpaqueDirty(bool forceVisible)
919 {
920 if(!owner || !(forceVisible || d->isVisible()))
921 return;
922
923 for(int i=0;i<d->m_tiles.size();++i) {
924 if(!d->m_tiles.at(i).isNull())
925 OBSERVERS(markDirty(i));
926 }
927 }
928
929 }
930
931