FifeGUI 0.3.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// Corresponding header include
6#include <fifechan/backends/opengl/graphics.hpp>
7
8// Platform config include
9#include "fifechan/platform.hpp"
10
11#ifdef _WIN32
12 #include <windows.h>
13#endif
14
15#ifdef __APPLE__
16 #include <OpenGL/gl.h>
17#else
18 #include <GL/gl.h>
19#endif
20
21// Project headers (subdirs before local)
22#include <fifechan/backends/opengl/image.hpp>
23#include <fifechan/exception.hpp>
24#include <fifechan/image.hpp>
25
26namespace fcn::opengl
27{
29 {
30 }
31
32 Graphics::Graphics(int width, int height) : mWidth(width), mHeight(height)
33 {
34 }
35
36 void Graphics::setTargetPlane(int width, int height)
37 {
38 setTargetPlaneImpl(width, height);
39 }
40
41 Graphics::~Graphics() = default;
42
44 {
45 glPushAttrib(
46 GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_LIGHTING_BIT |
47 GL_LINE_BIT | GL_POINT_BIT | GL_POLYGON_BIT | GL_SCISSOR_BIT | GL_STENCIL_BUFFER_BIT | GL_TEXTURE_BIT |
48 GL_TRANSFORM_BIT);
49
50 glMatrixMode(GL_MODELVIEW);
51 glPushMatrix();
52 glLoadIdentity();
53
54 glMatrixMode(GL_TEXTURE);
55 glPushMatrix();
56 glLoadIdentity();
57
58 glMatrixMode(GL_PROJECTION);
59 glPushMatrix();
60 glLoadIdentity();
61
62 glOrtho(0.0, static_cast<double>(mWidth), static_cast<double>(mHeight), 0.0, -1.0, 1.0);
63
64 glDisable(GL_LIGHTING);
65 glDisable(GL_CULL_FACE);
66 glDisable(GL_DEPTH_TEST);
67 glDisable(GL_TEXTURE_2D);
68
69 glEnable(GL_SCISSOR_TEST);
70 glPointSize(1.0);
71 glLineWidth(1.0);
72
73 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
74
75 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
76
78 }
79
81 {
82 glMatrixMode(GL_MODELVIEW);
83 glPopMatrix();
84
85 glMatrixMode(GL_TEXTURE);
86 glPopMatrix();
87
88 glMatrixMode(GL_PROJECTION);
89 glPopMatrix();
90
91 glPopAttrib();
92
94 }
95
97 {
98 bool const result = fcn::Graphics::pushClipArea(area);
99
100 glScissor(
101 mClipStack.top().x,
102 mHeight - mClipStack.top().y - mClipStack.top().height,
103 mClipStack.top().width,
104 mClipStack.top().height);
105
106 return result;
107 }
108
110 {
112
113 if (mClipStack.empty()) {
114 return;
115 }
116
117 glScissor(
118 mClipStack.top().x,
119 mHeight - mClipStack.top().y - mClipStack.top().height,
120 mClipStack.top().width,
121 mClipStack.top().height);
122 }
123
124 void Graphics::setTargetPlaneImpl(int width, int height)
125 {
126 mWidth = width;
127 mHeight = height;
128 }
129
130 void Graphics::drawImage(fcn::Image const * image, int srcX, int srcY, int dstX, int dstY, int width, int height)
131 {
132 auto const * srcImage = dynamic_cast<OpenGLImage const *>(image);
133
134 if (srcImage == nullptr) {
135 throwException("Trying to draw an image of unknown format, must be an OpenGLImage.");
136 }
137
138 if (mClipStack.empty()) {
140 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
141 }
142
143 ClipRectangle const & top = mClipStack.top();
144
145 dstX += top.xOffset;
146 dstY += top.yOffset;
147
148 // Find OpenGL texture coordinates
149 float const texX1 = srcX / static_cast<float>(srcImage->getTextureWidth());
150 float const texY1 = srcY / static_cast<float>(srcImage->getTextureHeight());
151 float const texX2 = (srcX + width) / static_cast<float>(srcImage->getTextureWidth());
152 float const texY2 = (srcY + height) / static_cast<float>(srcImage->getTextureHeight());
153
154 glBindTexture(GL_TEXTURE_2D, srcImage->getTextureHandle());
155
156 glEnable(GL_TEXTURE_2D);
157
158 GLboolean const blendWasEnabled = glIsEnabled(GL_BLEND);
159 if (blendWasEnabled == 0U) {
160 glEnable(GL_BLEND);
161 }
162
163 // Draw a textured quad -- the image
164 glBegin(GL_QUADS);
165 glTexCoord2f(texX1, texY1);
166 glVertex3i(dstX, dstY, 0);
167
168 glTexCoord2f(texX1, texY2);
169 glVertex3i(dstX, dstY + height, 0);
170
171 glTexCoord2f(texX2, texY2);
172 glVertex3i(dstX + width, dstY + height, 0);
173
174 glTexCoord2f(texX2, texY1);
175 glVertex3i(dstX + width, dstY, 0);
176 glEnd();
177 glDisable(GL_TEXTURE_2D);
178
179 if (blendWasEnabled == 0U) {
180 glDisable(GL_BLEND);
181 }
182 }
183
184 void Graphics::drawPoint(int x, int y)
185 {
186 if (mClipStack.empty()) {
188 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
189 }
190
191 ClipRectangle const & top = mClipStack.top();
192
193 x += top.xOffset;
194 y += top.yOffset;
195
196 glBegin(GL_POINTS);
197 glVertex2i(x, y);
198 glEnd();
199 }
200
201 void Graphics::drawLine(int x1, int y1, int x2, int y2)
202 {
203 if (mClipStack.empty()) {
205 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
206 }
207
208 ClipRectangle const & top = mClipStack.top();
209
210 x1 += top.xOffset;
211 y1 += top.yOffset;
212 x2 += top.xOffset;
213 y2 += top.yOffset;
214
215 glBegin(GL_LINES);
216 glVertex2f(x1 + 0.375F, y1 + 0.375F);
217 glVertex2f(x2 + 1.0F - 0.375F, y2 + 1.0F - 0.375F);
218 glEnd();
219
220 glBegin(GL_POINTS);
221 glVertex2f(x2 + 1.0F - 0.375F, y2 + 1.0F - 0.375F);
222 glEnd();
223
224 glBegin(GL_POINTS);
225 glVertex2f(x1 + 0.375F, y1 + 0.375F);
226 glEnd();
227 }
228
229 void Graphics::drawLine(int x1, int y1, int x2, int y2, unsigned int width)
230 {
231 // TODO(jakoch): Implement this function
232 }
233
234 void Graphics::drawPolyLine(PointVector const & points, unsigned int width)
235 {
236 // TODO(jakoch): Implement this function
237 }
238
239 void Graphics::drawBezier(PointVector const & points, int steps, unsigned int width)
240 {
241 // TODO(jakoch): Implement this function
242 }
243
244 void Graphics::drawRectangle(Rectangle const & rectangle)
245 {
246 if (mClipStack.empty()) {
248 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
249 }
250
251 ClipRectangle const & top = mClipStack.top();
252
253 glBegin(GL_LINE_LOOP);
254 glVertex2f(rectangle.x + top.xOffset, rectangle.y + top.yOffset);
255 glVertex2f(rectangle.x + rectangle.width + top.xOffset - 1.0F, rectangle.y + top.yOffset + 0.375F);
256 glVertex2f(rectangle.x + rectangle.width + top.xOffset - 1.0F, rectangle.y + rectangle.height + top.yOffset);
257 glVertex2f(rectangle.x + top.xOffset, rectangle.y + rectangle.height + top.yOffset);
258 glEnd();
259 }
260
261 void Graphics::fillRectangle(Rectangle const & rectangle)
262 {
263 if (mClipStack.empty()) {
265 "The clip stack is empty, perhaps you called a draw function outside of _beginDraw() and _endDraw()?");
266 }
267
268 ClipRectangle const & top = mClipStack.top();
269
270 glBegin(GL_QUADS);
271 glVertex2i(rectangle.x + top.xOffset, rectangle.y + top.yOffset);
272 glVertex2i(rectangle.x + rectangle.width + top.xOffset, rectangle.y + top.yOffset);
273 glVertex2i(rectangle.x + rectangle.width + top.xOffset, rectangle.y + rectangle.height + top.yOffset);
274 glVertex2i(rectangle.x + top.xOffset, rectangle.y + rectangle.height + top.yOffset);
275 glEnd();
276 }
277
278 void Graphics::drawCircle(Point const & p, unsigned int radius)
279 {
280 // TODO(jakoch): Implement this function
281 }
282
283 void Graphics::drawFillCircle(Point const & p, unsigned int radius)
284 {
285 // TODO(jakoch): Implement this function
286 }
287
288 void Graphics::drawCircleSegment(Point const & p, unsigned int radius, int sangle, int eangle)
289 {
290 // TODO(jakoch): Implement this function
291 }
292
293 void Graphics::drawFillCircleSegment(Point const & p, unsigned int radius, int sangle, int eangle)
294 {
295 // TODO(jakoch): Implement this method.
296 }
297
298 void Graphics::setColor(Color const & color)
299 {
300 mColor = color;
301 glColor4ub(
302 static_cast<GLubyte>(color.r),
303 static_cast<GLubyte>(color.g),
304 static_cast<GLubyte>(color.b),
305 static_cast<GLubyte>(color.a));
306
307 mAlpha = color.a != 255;
308
309 if (mAlpha) {
310 glEnable(GL_BLEND);
311 }
312 }
313
314 Color const & Graphics::getColor() const
315 {
316 return mColor;
317 }
318
320 {
321 return mWidth;
322 }
323
325 {
326 return mHeight;
327 }
328} // 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:58
uint8_t a
Alpha color component (0-255).
Definition color.hpp:350
uint8_t b
Blue color component (0-255).
Definition color.hpp:347
uint8_t g
Green color component (0-255).
Definition color.hpp:344
uint8_t r
Red color component (0-255).
Definition color.hpp:341
virtual void popClipArea()
Removes the top most clip area from the stack.
Definition graphics.cpp:65
virtual bool pushClipArea(Rectangle area)
Pushes a clip area onto the stack.
Definition graphics.cpp:25
std::stack< ClipRectangle > mClipStack
Holds the clip area stack.
Definition graphics.hpp:421
Abstract holder for image data.
Definition image.hpp:34
Represents a 2D coordinate (X, Y).
Definition point.hpp:34
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:22
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.
void setTargetPlane(int width, int height)
Sets the target plane on where to draw.
virtual int getTargetPlaneWidth() const
Gets the target plane width.
virtual void setTargetPlaneImpl(int width, int height)
Backend hook that actually applies the logical target plane size.
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.
std::vector< Point > PointVector
A list of points.
Definition point.hpp:331
void throwException(std::string const &message, std::source_location location=std::source_location::current())
Throw an Exception capturing the current source location.
fcn::opengl::Image OpenGLImage
Alias for the OpenGL image implementation.