FifeGUI 0.2.0
A C++ GUI library designed for games.
backends/opengl/graphics.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2004 - 2008 Olof Naessén and Per Larsson
3// SPDX-FileCopyrightText: 2013 - 2026 Fifengine contributors
4
5#include <fifechan/backends/opengl/graphics.hpp>
6#include <fifechan/backends/opengl/image.hpp>
7#include <fifechan/exception.hpp>
8#include <fifechan/image.hpp>
9
10#if defined(_WIN32)
11 #include <windows.h>
12#endif
13
14#if defined(__APPLE__)
15 #include <OpenGL/gl.h>
16#else
17 #include <GL/gl.h>
18#endif
19
20namespace fcn::opengl
21{
23 {
24 setTargetPlane(640, 480);
25 }
26
27 Graphics::Graphics(int width, int height)
28 {
29 setTargetPlane(width, height);
30 }
31
32 Graphics::~Graphics() = default;
33
35 {
36 glPushAttrib(
37 GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_LIGHTING_BIT |
38 GL_LINE_BIT | GL_POINT_BIT | GL_POLYGON_BIT | GL_SCISSOR_BIT | GL_STENCIL_BUFFER_BIT | GL_TEXTURE_BIT |
39 GL_TRANSFORM_BIT);
40
41 glMatrixMode(GL_MODELVIEW);
42 glPushMatrix();
43 glLoadIdentity();
44
45 glMatrixMode(GL_TEXTURE);
46 glPushMatrix();
47 glLoadIdentity();
48
49 glMatrixMode(GL_PROJECTION);
50 glPushMatrix();
51 glLoadIdentity();
52
53 glOrtho(0.0, static_cast<double>(mWidth), static_cast<double>(mHeight), 0.0, -1.0, 1.0);
54
55 glDisable(GL_LIGHTING);
56 glDisable(GL_CULL_FACE);
57 glDisable(GL_DEPTH_TEST);
58 glDisable(GL_TEXTURE_2D);
59
60 glEnable(GL_SCISSOR_TEST);
61 glPointSize(1.0);
62 glLineWidth(1.0);
63
64 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
65
66 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
67
69 }
70
72 {
73 glMatrixMode(GL_MODELVIEW);
74 glPopMatrix();
75
76 glMatrixMode(GL_TEXTURE);
77 glPopMatrix();
78
79 glMatrixMode(GL_PROJECTION);
80 glPopMatrix();
81
82 glPopAttrib();
83
85 }
86
88 {
89 bool const result = fcn::Graphics::pushClipArea(area);
90
91 glScissor(
92 mClipStack.top().x,
93 mHeight - mClipStack.top().y - mClipStack.top().height,
94 mClipStack.top().width,
95 mClipStack.top().height);
96
97 return result;
98 }
99
101 {
103
104 if (mClipStack.empty()) {
105 return;
106 }
107
108 glScissor(
109 mClipStack.top().x,
110 mHeight - mClipStack.top().y - mClipStack.top().height,
111 mClipStack.top().width,
112 mClipStack.top().height);
113 }
114
115 void Graphics::setTargetPlane(int width, int height)
116 {
117 mWidth = width;
118 mHeight = height;
119 }
120
121 void Graphics::drawImage(fcn::Image const * image, int srcX, int srcY, int dstX, int dstY, int width, int height)
122 {
123 auto const * srcImage = dynamic_cast<OpenGLImage const *>(image);
124
125 if (srcImage == nullptr) {
126 throwException("Trying to draw an image of unknown format, must be an OpenGLImage.");
127 }
128
129 if (mClipStack.empty()) {
130 throwException(
131 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
132 }
133
134 ClipRectangle const & top = mClipStack.top();
135
136 dstX += top.xOffset;
137 dstY += top.yOffset;
138
139 // Find OpenGL texture coordinates
140 float const texX1 = srcX / static_cast<float>(srcImage->getTextureWidth());
141 float const texY1 = srcY / static_cast<float>(srcImage->getTextureHeight());
142 float const texX2 = (srcX + width) / static_cast<float>(srcImage->getTextureWidth());
143 float const texY2 = (srcY + height) / static_cast<float>(srcImage->getTextureHeight());
144
145 glBindTexture(GL_TEXTURE_2D, srcImage->getTextureHandle());
146
147 glEnable(GL_TEXTURE_2D);
148
149 GLboolean const blendWasEnabled = glIsEnabled(GL_BLEND);
150 if (blendWasEnabled == 0U) {
151 glEnable(GL_BLEND);
152 }
153
154 // Draw a textured quad -- the image
155 glBegin(GL_QUADS);
156 glTexCoord2f(texX1, texY1);
157 glVertex3i(dstX, dstY, 0);
158
159 glTexCoord2f(texX1, texY2);
160 glVertex3i(dstX, dstY + height, 0);
161
162 glTexCoord2f(texX2, texY2);
163 glVertex3i(dstX + width, dstY + height, 0);
164
165 glTexCoord2f(texX2, texY1);
166 glVertex3i(dstX + width, dstY, 0);
167 glEnd();
168 glDisable(GL_TEXTURE_2D);
169
170 if (blendWasEnabled == 0U) {
171 glDisable(GL_BLEND);
172 }
173 }
174
175 void Graphics::drawPoint(int x, int y)
176 {
177 if (mClipStack.empty()) {
178 throwException(
179 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
180 }
181
182 ClipRectangle const & top = mClipStack.top();
183
184 x += top.xOffset;
185 y += top.yOffset;
186
187 glBegin(GL_POINTS);
188 glVertex2i(x, y);
189 glEnd();
190 }
191
192 void Graphics::drawLine(int x1, int y1, int x2, int y2)
193 {
194 if (mClipStack.empty()) {
195 throwException(
196 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
197 }
198
199 ClipRectangle const & top = mClipStack.top();
200
201 x1 += top.xOffset;
202 y1 += top.yOffset;
203 x2 += top.xOffset;
204 y2 += top.yOffset;
205
206 glBegin(GL_LINES);
207 glVertex2f(x1 + 0.375F, y1 + 0.375F);
208 glVertex2f(x2 + 1.0F - 0.375F, y2 + 1.0F - 0.375F);
209 glEnd();
210
211 glBegin(GL_POINTS);
212 glVertex2f(x2 + 1.0F - 0.375F, y2 + 1.0F - 0.375F);
213 glEnd();
214
215 glBegin(GL_POINTS);
216 glVertex2f(x1 + 0.375F, y1 + 0.375F);
217 glEnd();
218 }
219
220 void Graphics::drawLine(int x1, int y1, int x2, int y2, unsigned int width)
221 {
222 // TODO(jakoch): Implement this function
223 }
224
225 void Graphics::drawPolyLine(PointVector const & points, unsigned int width)
226 {
227 // TODO(jakoch): Implement this function
228 }
229
230 void Graphics::drawBezier(PointVector const & points, int steps, unsigned int width)
231 {
232 // TODO(jakoch): Implement this function
233 }
234
235 void Graphics::drawRectangle(Rectangle const & rectangle)
236 {
237 if (mClipStack.empty()) {
238 throwException(
239 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
240 }
241
242 ClipRectangle const & top = mClipStack.top();
243
244 glBegin(GL_LINE_LOOP);
245 glVertex2f(rectangle.x + top.xOffset, rectangle.y + top.yOffset);
246 glVertex2f(rectangle.x + rectangle.width + top.xOffset - 1.0F, rectangle.y + top.yOffset + 0.375F);
247 glVertex2f(rectangle.x + rectangle.width + top.xOffset - 1.0F, rectangle.y + rectangle.height + top.yOffset);
248 glVertex2f(rectangle.x + top.xOffset, rectangle.y + rectangle.height + top.yOffset);
249 glEnd();
250 }
251
252 void Graphics::fillRectangle(Rectangle const & rectangle)
253 {
254 if (mClipStack.empty()) {
255 throwException(
256 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
257 }
258
259 ClipRectangle const & top = mClipStack.top();
260
261 glBegin(GL_QUADS);
262 glVertex2i(rectangle.x + top.xOffset, rectangle.y + top.yOffset);
263 glVertex2i(rectangle.x + rectangle.width + top.xOffset, rectangle.y + top.yOffset);
264 glVertex2i(rectangle.x + rectangle.width + top.xOffset, rectangle.y + rectangle.height + top.yOffset);
265 glVertex2i(rectangle.x + top.xOffset, rectangle.y + rectangle.height + top.yOffset);
266 glEnd();
267 }
268
269 void Graphics::drawCircle(Point const & p, unsigned int radius)
270 {
271 // TODO(jakoch): Implement this function
272 }
273
274 void Graphics::drawFillCircle(Point const & p, unsigned int radius)
275 {
276 // TODO(jakoch): Implement this function
277 }
278
279 void Graphics::drawCircleSegment(Point const & p, unsigned int radius, int sangle, int eangle)
280 {
281 // TODO(jakoch): Implement this function
282 }
283
284 void Graphics::drawFillCircleSegment(Point const & p, unsigned int radius, int sangle, int eangle)
285 {
286 // TODO(jakoch): Implement this method.
287 }
288
289 void Graphics::setColor(Color const & color)
290 {
291 mColor = color;
292 glColor4ub(
293 static_cast<GLubyte>(color.r),
294 static_cast<GLubyte>(color.g),
295 static_cast<GLubyte>(color.b),
296 static_cast<GLubyte>(color.a));
297
298 mAlpha = color.a != 255;
299
300 if (mAlpha) {
301 glEnable(GL_BLEND);
302 }
303 }
304
305 Color const & Graphics::getColor() const
306 {
307 return mColor;
308 }
309
311 {
312 return mWidth;
313 }
314
316 {
317 return mHeight;
318 }
319} // namespace fcn::opengl
A rectangle specifically used for clipping rendering regions.
int xOffset
Holds the x offset of the x coordinate.
int yOffset
Holds the y offset of the y coordinate.
Color.
Definition color.hpp:56
uint8_t a
Alpha color component (0-255).
Definition color.hpp:322
uint8_t b
Blue color component (0-255).
Definition color.hpp:319
uint8_t g
Green color component (0-255).
Definition color.hpp:316
uint8_t r
Red color component (0-255).
Definition color.hpp:313
virtual bool pushClipArea(Rectangle area)
Pushes a clip area onto the stack.
Definition graphics.cpp:18
std::stack< ClipRectangle > mClipStack
Holds the clip area stack.
Definition graphics.hpp:382
Abstract holder for image data.
Definition image.hpp:32
Represents a 2D coordinate (X, Y).
Definition point.hpp:29
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:20
int width
Holds the width of the rectangle.
int y
Holds the x coordinate of the rectangle.
int x
Holds the x coordinate of the rectangle.
int height
Holds the height of the rectangle.
void setColor(Color const &color) override
Sets the color to use when drawing.
void drawFillCircle(Point const &p, unsigned int radius) override
Draws a filled circle.
void drawCircleSegment(Point const &p, unsigned int radius, int sangle, int eangle) override
Draws a simple, non-filled circle segment with a one pixel width.
void _beginDraw() override
Initializes drawing.
void drawLine(int x1, int y1, int x2, int y2) override
Draws a line.
void drawCircle(Point const &p, unsigned int radius) override
Draws a simple, non-filled circle with a one pixel width.
bool mAlpha
Whether alpha blending is enabled.
void drawBezier(PointVector const &points, int steps, unsigned int width) override
Draws a bezier curve.
void drawRectangle(Rectangle const &rectangle) override
Draws a simple, non-filled rectangle with a one pixel width.
void drawFillCircleSegment(Point const &p, unsigned int radius, int sangle, int eangle) override
Draws a filled circle segment.
void popClipArea() override
Removes the top most clip area from the stack.
void drawPoint(int x, int y) override
Draws a single point/pixel.
Color mColor
Current drawing color.
void _endDraw() override
Deinitializes the drawing process.
void fillRectangle(Rectangle const &rectangle) override
Draws a filled rectangle.
void drawImage(fcn::Image const *image, int srcX, int srcY, int dstX, int dstY, int width, int height) override
Draws a part of an image.
int mWidth
Width of the logical target plane.
void drawPolyLine(PointVector const &points, unsigned int width) override
Draws lines between points with given width.
virtual void setTargetPlane(int width, int height)
Sets the target plane on where to draw.
virtual int getTargetPlaneWidth() const
Gets the target plane width.
int mHeight
Height of the logical target plane.
virtual int getTargetPlaneHeight() const
Gets the target plane height.
Color const & getColor() const override
Gets the color to use when drawing.
bool pushClipArea(Rectangle area) override
Pushes a clip area onto the stack.