[go: up one dir, main page]

Menu

[r49]: / lpi_draw2d.cpp  Maximize  Restore  History

Download this file

264 lines (237 with data), 9.6 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
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/*
Copyright (c) 2005-2007 Lode Vandevenne
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Lode Vandevenne nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "lpi_draw2d.h"
#include <vector>
namespace lpi
{
//Draw a line from (x1, y1) to (x2, y2) on a buffer where the color is 4 unsigned chars (with bresenham)
void drawLine(unsigned char* buffer, int buffer_w, int buffer_h, int x1, int y1, int x2, int y2, const ColorRGB& color, int clipx1, int clipy1, int clipx2, int clipy2)
{
//don't clip if the user gives no clipping area by making clipx2 smaller than clipx1
bool clip = (clipx2 >= clipx1);
//clip if some point is outside the clipping area
if(clip)
if(x1 < clipx1 || x1 >= clipx2 || x2 < clipx1 || x2 >= clipx2 || y1 < clipy1 || y1 >= clipy2 || y2 < clipy1 || y2 >= clipy2)
{
int x3, y3, x4, y4;
if(!clipLine(x1, y1, x2, y2, x3, y3, x4, y4, clipx1, clipy1, clipx2, clipy2)) return;
x1 = x3;
y1 = y3;
x2 = x4;
y2 = y4;
}
//draw the line with bresenham
int deltax = abs(x2 - x1); // The difference between the x's
int deltay = abs(y2 - y1); // The difference between the y's
int x = x1; // Start x off at the first pixel
int y = y1; // Start y off at the first pixel
int xinc1, xinc2, yinc1, yinc2, den, num, numadd, numpixels, curpixel;
if (x2 >= x1) // The x-values are increasing
{
xinc1 = 1;
xinc2 = 1;
}
else // The x-values are decreasing
{
xinc1 = -1;
xinc2 = -1;
}
if (y2 >= y1) // The y-values are increasing
{
yinc1 = 1;
yinc2 = 1;
}
else // The y-values are decreasing
{
yinc1 = -1;
yinc2 = -1;
}
if (deltax >= deltay) // There is at least one x-value for every y-value
{
xinc1 = 0; // Don't change the x when numerator >= denominator
yinc2 = 0; // Don't change the y for every iteration
den = deltax;
num = deltax / 2;
numadd = deltay;
numpixels = deltax; // There are more x-values than y-values
}
else // There is at least one y-value for every x-value
{
xinc2 = 0; // Don't change the x for every iteration
yinc1 = 0; // Don't change the y when numerator >= denominator
den = deltay;
num = deltay / 2;
numadd = deltax;
numpixels = deltay; // There are more y-values than x-values
}
for (curpixel = 0; curpixel <= numpixels; curpixel++)
{
pset(buffer, buffer_w, buffer_h, x, y, color); // Draw the current pixel
num += numadd; // Increase the numerator by the top of the fraction
if (num >= den) // Check if numerator >= denominator
{
num -= den; // Calculate the new numerator value
x += xinc1; // Change the x as appropriate
y += yinc1; // Change the y as appropriate
}
x += xinc2; // Change the x as appropriate
y += yinc2; // Change the y as appropriate
}
}
//Fast horizontal line from (x1,y) to (x2,y), with rgb color
void horLine(unsigned char* buffer, int buffer_w, int buffer_h, int y, int x1, int x2, const ColorRGB& color)
{
if(x2 < x1) std::swap(x1, x2); //swap x1 and x2 because x1 must be the leftmost endpoint
if(x2 < 0 || x1 > buffer_w - 1 || y < 0 || y > buffer_h - 1) return; //no single point of the line is on screen
for(int x = x1; x <= x2; x++)
{
int bufferPos = 4 * buffer_w * y + 4 * x;
buffer[bufferPos + 0] = color.r;
buffer[bufferPos + 1] = color.g;
buffer[bufferPos + 2] = color.b;
buffer[bufferPos + 3] = color.a;
}
}
//Fast vertical line from (x,y1) to (x,y2), with rgb color
void verLine(unsigned char* buffer, int buffer_w, int buffer_h, int x, int y1, int y2, const ColorRGB& color)
{
if(y2 < y1) std::swap(y1, y2); //swap y1 and y2 because y1 must be the topmost endpoint
if(x < 0 || x > buffer_w - 1 || y2 < 0 || y1 > buffer_h - 1) return;
for(int y = y1; y <= y2; y++)
{
int bufferPos = 4 * buffer_w * y + 4 * x;
buffer[bufferPos + 0] = color.r;
buffer[bufferPos + 1] = color.g;
buffer[bufferPos + 2] = color.b;
buffer[bufferPos + 3] = color.a;
}
}
//Filled bresenham circle with center at (xc,yc) with radius and red green blue color
void drawDisk(unsigned char* buffer, int buffer_w, int buffer_h, int xc, int yc, int radius, const ColorRGB& color)
{
if(xc + radius < 0 || xc - radius >= buffer_w || yc + radius < 0 || yc - radius >= buffer_h) return; //every single pixel outside screen, so don't waste time on it
int x = 0;
int y = radius;
int p = 3 - (radius << 1);
int a, b, c, d, e, f, g, h;
int pb = xc - radius, pd = xc - radius; //previous values: to avoid drawing horizontal lines multiple times
while (x <= y)
{
// write data
a = xc + x;
b = yc + y;
c = xc - x;
d = yc - y;
e = xc + y;
f = yc + x;
g = xc - y;
h = yc - x;
if(b != pb) horLine(buffer, buffer_w, buffer_h, b, a, c, color);
if(d != pd) horLine(buffer, buffer_w, buffer_h, d, a, c, color);
if(f != b) horLine(buffer, buffer_w, buffer_h, f, e, g, color);
if(h != d && h != f) horLine(buffer, buffer_w, buffer_h, h, e, g, color);
pb = b;
pd = d;
if(p < 0) p += (x++ << 2) + 6;
else p += ((x++ - y--) << 2) + 10;
}
}
//Set a pixel on a buffer where the color is 4 unsigned chars (w and h are the width and height of the buffer)
void pset(unsigned char* buffer, int buffer_w, int buffer_h, int x, int y, const ColorRGB& color)
{
if(x < 0 || y < 0 || x >= buffer_w || y >= buffer_h) return;
int bufferPos = 4 * buffer_w * y + 4 * x;
buffer[bufferPos + 0] = color.r;
buffer[bufferPos + 1] = color.g;
buffer[bufferPos + 2] = color.b;
buffer[bufferPos + 3] = color.a;
}
//Functions for clipping a 2D line to the screen, which is the rectangle (0,0)-(w,h)
//This is the Cohen-Sutherland Clipping Algorithm
//Each of 9 regions gets an outcode, based on if it's at the top, bottom, left or right of the screen
// 1001 1000 1010 9 8 10
// 0001 0000 0010 1 0 2
// 0101 0100 0110 5 4 6
//int findregion returns which of the 9 regions a point is in, void clipline does the actual clipping
int findRegion(int x, int y, int left, int top, int right, int bottom)
{
int code=0;
if(y >= bottom)
code |= 1; //top
else if( y < top)
code |= 2; //bottom
if(x >= right)
code |= 4; //right
else if ( x < left)
code |= 8; //left
return(code);
}
bool clipLine(int x1, int y1, int x2, int y2, int & x3, int & y3, int & x4, int & y4, int left, int top, int right, int bottom)
{
int code1, code2, codeout;
bool accept = 0, done = 0;
code1 = findRegion(x1, y1, left, top, right, bottom); //the region outcodes for the endpoints
code2 = findRegion(x2, y2, left, top, right, bottom);
do //In theory, this can never end up in an infinite loop, it'll always come in one of the trivial cases eventually
{
if(!(code1 | code2)) accept = done = 1; //accept because both endpoints are in screen or on the border, trivial accept
else if(code1 & code2) done = 1; //the line isn't visible on screen, trivial reject
else //if no trivial reject or accept, continue the loop
{
int x, y;
codeout = code1 ? code1 : code2;
if(codeout & 1) //top
{
x = x1 + (x2 - x1) * (bottom - y1) / (y2 - y1);
y = bottom - 1;
}
else if(codeout & 2) //bottom
{
x = x1 + (x2 - x1) * (top - y1) / (y2 - y1);
y = top;
}
else if(codeout & 4) //right
{
y = y1 + (y2 - y1) * (right - x1) / (x2 - x1);
x = right - 1;
}
else //left
{
y = y1 + (y2 - y1) * (left - x1) / (x2 - x1);
x = left;
}
if(codeout == code1) //first endpoint was clipped
{
x1 = x; y1 = y;
code1 = findRegion(x1, y1, left, top, right, bottom);
}
else //second endpoint was clipped
{
x2 = x; y2 = y;
code2 = findRegion(x2, y2, left, top, right, bottom);
}
}
}
while(done == 0);
if(accept)
{
x3 = x1;
x4 = x2;
y3 = y1;
y4 = y2;
return 1;
}
else
{
x3 = x4 = y3 = y4 = 0;
return 0;
}
}
} //end of namespace lpi