1 /**
2 * File name: RkCairoGraphicsBackend.cpp
3 * Project: Redkite (A small GUI toolkit)
4 *
5 * Copyright (C) 2019 Iurie Nistor (http://geontime.com)
6 *
7 * This file is part of Redkite.
8 *
9 * Redkite is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "RkCairoGraphicsBackend.h"
25 #include "RkCanvas.h"
26 #include "RkCanvasInfo.h"
27 #include "RkLog.h"
28
29 #include <math.h>
30
RkCairoGraphicsBackend(RkCanvas * canvas)31 RkCairoGraphicsBackend::RkCairoGraphicsBackend(RkCanvas *canvas)
32 : cairoContext{cairo_create(canvas->getCanvasInfo()->cairo_surface)}
33 {
34 cairo_set_font_size(context(), 10);
35 cairo_set_line_width (context(), 1);
36 }
37
context() const38 cairo_t* RkCairoGraphicsBackend::context() const
39 {
40 return cairoContext;
41 }
42
~RkCairoGraphicsBackend()43 RkCairoGraphicsBackend::~RkCairoGraphicsBackend()
44 {
45 cairo_destroy(context());
46 }
47
drawText(const std::string & text,int x,int y)48 void RkCairoGraphicsBackend::drawText(const std::string &text, int x, int y)
49 {
50 cairo_move_to(context(), x, y);
51 cairo_show_text(context(), text.c_str());
52 }
53
drawImage(const std::string & file,int x,int y)54 void RkCairoGraphicsBackend::drawImage(const std::string &file, int x, int y)
55 {
56 auto image = cairo_image_surface_create_from_png(file.c_str());
57 cairo_set_source_surface(context(), image, x, y);
58 cairo_paint(context());
59 cairo_surface_destroy(image);
60 }
61
drawImage(const RkImage & image,int x,int y)62 void RkCairoGraphicsBackend::drawImage(const RkImage &image, int x, int y)
63 {
64 cairo_set_source_surface(context(),
65 image.getCanvasInfo()->cairo_surface,
66 x, y);
67 cairo_paint(context());
68 }
69
drawEllipse(const RkPoint & p,int width,int height)70 void RkCairoGraphicsBackend::drawEllipse(const RkPoint& p, int width, int height)
71 {
72 if (width == height) {
73 cairo_move_to(context(), p.x() + width / 2, p.y());
74 cairo_arc(context(), p.x(), p.y(), width / 2, 0, 2 * M_PI);
75 cairo_stroke(context());
76 } else {
77 // TODO: implemented ellipse.
78 RK_LOG_ERROR("ellipse not implemented yet");
79 }
80 }
81
drawLine(const RkPoint & p1,const RkPoint & p2)82 void RkCairoGraphicsBackend::drawLine(const RkPoint &p1, const RkPoint &p2)
83 {
84 cairo_move_to(context(), p1.x() + 0.5, p1.y() + 0.5);
85 cairo_line_to(context(), p2.x() + 0.5, p2.y() + 0.5);
86 cairo_stroke(context());
87 }
88
drawRect(const RkRect & rect)89 void RkCairoGraphicsBackend::drawRect(const RkRect &rect)
90 {
91 cairo_rectangle(context(),
92 rect.left() + 0.5,
93 rect.top() + 0.5,
94 rect.width(),
95 rect.height());
96 cairo_stroke(context());
97 }
98
setPen(const RkPen & pen)99 void RkCairoGraphicsBackend::setPen(const RkPen &pen)
100 {
101 cairo_set_line_width(context(), pen.width());
102 cairo_set_source_rgba(context(),
103 static_cast<double>(pen.color().red()) / 255,
104 static_cast<double>(pen.color().green()) / 255,
105 static_cast<double>(pen.color().blue()) / 255,
106 static_cast<double>(pen.color().alpha()) / 255);
107 double dashLine[] = {12, 8};
108 double dotLine[] = {1, 2};
109 switch (pen.style())
110 {
111 case RkPen::PenStyle::DashLine:
112 cairo_set_dash(context(), dashLine, 2, 0);
113 break;
114 case RkPen::PenStyle::DotLine:
115 cairo_set_dash(context(), dotLine, 2, 0);
116 break;
117 case RkPen::PenStyle::NoLine:
118 case RkPen::PenStyle::SolidLine:
119 default:
120 cairo_set_dash(context(), nullptr, 0, 0);
121 break;
122 }
123 }
124
setFont(const RkFont & font)125 void RkCairoGraphicsBackend::setFont(const RkFont &font)
126 {
127 cairo_set_font_size(context(), font.size());
128 cairo_font_slant_t slant;
129 switch (font.style())
130 {
131 case RkFont::Style::Normal:
132 slant = CAIRO_FONT_SLANT_NORMAL;
133 break;
134 case RkFont::Style::Italic:
135 slant = CAIRO_FONT_SLANT_ITALIC;
136 break;
137 case RkFont::Style::Oblique:
138 slant = CAIRO_FONT_SLANT_OBLIQUE;
139 break;
140 default:
141 slant = CAIRO_FONT_SLANT_NORMAL;
142 }
143
144 cairo_font_weight_t weight;
145 switch (font.weight())
146 {
147 case RkFont::Weight::Normal:
148 weight = CAIRO_FONT_WEIGHT_NORMAL;
149 break;
150 case RkFont::Weight::Bold:
151 weight = CAIRO_FONT_WEIGHT_BOLD;
152 break;
153 default:
154 weight = CAIRO_FONT_WEIGHT_NORMAL;
155 }
156
157 auto face = cairo_toy_font_face_create(font.family().c_str(), slant, weight);
158 cairo_set_font_face(context(), face);
159 cairo_font_face_destroy(face);
160 cairo_set_font_size(context(), font.size());
161 }
162
drawPolyLine(const std::vector<RkPoint> & points)163 void RkCairoGraphicsBackend::drawPolyLine(const std::vector<RkPoint> &points)
164 {
165 bool first = true;
166 RkPoint currPoint;
167 for (const auto &point: points) {
168 if (first) {
169 cairo_move_to(context(), point.x() + 0.5, point.y() + 0.5);
170 currPoint = point;
171 first = false;
172 } else if (currPoint != point) {
173 cairo_rel_line_to(context(), point.x() - currPoint.x(),
174 point.y() - currPoint.y());
175 currPoint = point;
176 }
177 }
178 cairo_stroke(context());
179 }
180
fillRect(const RkRect & rect,const RkColor & color)181 void RkCairoGraphicsBackend::fillRect(const RkRect &rect, const RkColor &color)
182 {
183 cairo_rectangle(context(), rect.left(), rect.top(), rect.width(), rect.height());
184 cairo_set_source_rgba(context(),
185 static_cast<double>(color.red()) / 255,
186 static_cast<double>(color.green()) / 255,
187 static_cast<double>(color.blue()) / 255,
188 static_cast<double>(color.alpha()) / 255);
189 cairo_fill(context());
190 }
191
applyAlpha(int alpha)192 void RkCairoGraphicsBackend::applyAlpha(int alpha)
193 {
194 cairo_paint_with_alpha(context(), (float) alpha / 255);
195 }
196
translate(const RkPoint & offset)197 void RkCairoGraphicsBackend::translate(const RkPoint &offset)
198 {
199 cairo_translate(context(), offset.x(), offset.y());
200 }
201
rotate(rk_real angle)202 void RkCairoGraphicsBackend::rotate(rk_real angle)
203 {
204 cairo_rotate(context(), angle);
205 }
206
getTextWidth(const std::string & text) const207 int RkCairoGraphicsBackend::getTextWidth(const std::string &text) const
208 {
209 if (text.empty())
210 return 0;
211
212 cairo_text_extents_t extents;
213 cairo_text_extents (context(), text.data(), &extents);
214 return extents.x_advance;
215 }
216