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