context_rect.cpp
3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <kandinsky/context.h>
#include <assert.h>
KDRect KDContext::absoluteFillRect(KDRect rect) {
return rect.translatedBy(m_origin).intersectedWith(m_clippingRect);
}
void KDContext::fillRect(KDRect rect, KDColor color) {
KDRect absoluteRect = absoluteFillRect(rect);
if (absoluteRect.isEmpty()) {
return;
}
pushRectUniform(absoluteRect, color);
}
/* Note: we support the case where workingBuffer IS equal to pixels */
void KDContext::fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer) {
KDRect absoluteRect = absoluteFillRect(rect);
if (absoluteRect.isEmpty()) {
return;
}
/* Caution:
* The absoluteRect may have a SMALLER size than the original rect because it
* has been clipped. Therefore we cannot assume that the mask can be read as a
* continuous area. */
if (absoluteRect.width() == rect.width() && absoluteRect.height() == rect.height()) {
pushRect(absoluteRect, pixels);
return;
}
KDCoordinate startingI = m_clippingRect.x() - rect.translatedBy(m_origin).x();
KDCoordinate startingJ = m_clippingRect.y() - rect.translatedBy(m_origin).y();
startingI = startingI > 0 ? startingI : 0;
startingJ = startingJ > 0 ? startingJ : 0;
/* If the rect has indeed been clipped, we only want to push the correct
* discontinuous extract of pixels. We want also to minimize calls to pushRect
* (time consuming). If a working buffer is available, we can fill it by
* concatenating extracted rows of 'pixels' to call pushRect only once on the
* absoluteRect. However, if we do not have a working buffer, we push row by
* row extracts of 'pixels' calling pushRect multiple times. */
if (workingBuffer == nullptr) {
for (KDCoordinate j=0; j<absoluteRect.height(); j++) {
KDRect absoluteRow = KDRect(absoluteRect.x(), absoluteRect.y()+j, absoluteRect.width(), 1);
KDColor * rowPixels = (KDColor *)pixels+startingI+rect.width()*(startingJ+j);
pushRect(absoluteRow, rowPixels);
}
} else {
for (KDCoordinate j=0; j<absoluteRect.height(); j++) {
for (KDCoordinate i=0; i<absoluteRect.width(); i++) {
workingBuffer[i+absoluteRect.width()*j] = pixels[startingI+i+rect.width()*(startingJ+j)];
}
}
pushRect(absoluteRect, workingBuffer);
}
}
// Mask's size must be rect.size
// WorkingBuffer, same deal
// TODO: should we avoid pullRect by giving a 'memory' working buffer?
void KDContext::blendRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer) {
KDRect absoluteRect = absoluteFillRect(rect);
/* Caution:
* The absoluteRect may have a SMALLER size than the original rect because it
* has been clipped. Therefore we cannot assume that the mask can be read as a
* continuous area. */
pullRect(absoluteRect, workingBuffer);
KDCoordinate startingI = m_clippingRect.x() - rect.translatedBy(m_origin).x();
KDCoordinate startingJ = m_clippingRect.y() - rect.translatedBy(m_origin).y();
startingI = startingI > 0 ? startingI : 0;
startingJ = startingJ > 0 ? startingJ : 0;
for (KDCoordinate j=0; j<absoluteRect.height(); j++) {
for (KDCoordinate i=0; i<absoluteRect.width(); i++) {
KDColor * currentPixelAdress = workingBuffer + i + absoluteRect.width()*j;
const uint8_t * currentMaskAddress = mask + i + startingI + rect.width()*(j + startingJ);
*currentPixelAdress = KDColor::blend(*currentPixelAdress, color, *currentMaskAddress);
//*currentPixelAdress = KDColorBlend(*currentPixelAdress, color, *currentMaskAddress);
}
}
pushRect(absoluteRect, workingBuffer);
}
void KDContext::strokeRect(KDRect rect, KDColor color) {
fillRect(KDRect(rect.origin(), rect.width(), 1), color);
fillRect(KDRect(KDPoint(rect.x(), rect.bottom()), rect.width(), 1), color);
fillRect(KDRect(rect.origin(), 1, rect.height()), color);
fillRect(KDRect(KDPoint(rect.right(), rect.y()), 1, rect.height()), color);
}