1 // Copyright (C) 2009, 2010, 2011, 2014, 2015, 2020 Ben Asselstine
2 //
3 //  This program is free software; you can redistribute it and/or modify
4 //  it under the terms of the GNU General Public License as published by
5 //  the Free Software Foundation; either version 3 of the License, or
6 //  (at your option) any later version.
7 //
8 //  This program is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //  GNU Library General Public License for more details.
12 //
13 //  You should have received a copy of the GNU General Public License
14 //  along with this program; if not, write to the Free Software
15 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 //  02110-1301, USA.
17 
18 #include <iostream>
19 #include "defs.h"
20 #include "PixMask.h"
21 #include <string.h>
22 #include <cairomm/cairomm.h>
23 #include <gdkmm.h>
24 #include "ucompose.hpp"
25 
26 
PixMask(Glib::RefPtr<Gdk::Pixbuf> pixbuf)27 PixMask::PixMask(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
28      : width(0), height(0), unscaled_width(0), unscaled_height(0)
29 {
30   pixmap = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, pixbuf->get_width(), pixbuf->get_height());
31   gc = Cairo::Context::create(pixmap);
32   Gdk::Cairo::set_source_pixbuf(gc, pixbuf, 0, 0);
33   gc->paint();
34   mask = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, pixbuf->get_width(), pixbuf->get_height());
35   unscaled_width = pixbuf->get_width();
36   unscaled_height = pixbuf->get_height();
37   width = unscaled_width;
38   height = unscaled_height;
39 }
40 
~PixMask()41 PixMask::~PixMask()
42 {
43   pixmap.clear();
44   mask.clear();
45   gc.clear();
46 }
47 
PixMask(Cairo::RefPtr<Cairo::Surface> p,Cairo::RefPtr<Cairo::Surface> m)48 PixMask::PixMask(Cairo::RefPtr<Cairo::Surface> p, Cairo::RefPtr<Cairo::Surface> m)
49     : width(0), height(0)
50 {
51   gc = Cairo::Context::create(p);
52   double x1, x2, y1, y2;
53   gc->get_clip_extents (x1, y1, x2, y2);
54   width = x2 - x1;
55   height = y2 - y1;
56 
57   pixmap = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, width, height);
58   if (p)
59     {
60       gc = Cairo::Context::create(pixmap);
61       gc->rectangle(0, 0, width, height);
62       gc->clip();
63       gc->save();
64       gc->set_source (p, 0, 0);
65       gc->rectangle (0, 0, width, height);
66       gc->clip();
67       gc->paint();
68       gc->restore();
69     }
70 
71   mask = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, width, height);
72   if (m)
73     {
74       Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(mask);
75       context->rectangle(0, 0, width, height);
76       context->clip();
77       context->save();
78       context->set_source (m, 0, 0);
79       context->rectangle (0, 0, width, height);
80       context->clip();
81       context->paint();
82       context->restore();
83     }
84   unscaled_width = width;
85   unscaled_height = height;
86 }
87 
PixMask(const PixMask & p)88 PixMask::PixMask(const PixMask&p)
89 {
90   width = p.width;
91   height = p.height;
92   unscaled_width = p.unscaled_width;
93   unscaled_height = p.unscaled_height;
94   pixmap = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, width, height);
95   if (p.pixmap)
96     {
97       gc = Cairo::Context::create(pixmap);
98       gc->rectangle(0, 0, width, height);
99       gc->clip();
100       gc->save();
101       gc->set_source (p.pixmap, 0, 0);
102       gc->rectangle (0, 0, width, height);
103       gc->clip();
104       gc->paint();
105       gc->restore();
106     }
107 
108   mask = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, width, height);
109   if (p.mask)
110     {
111       Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(mask);
112       context->rectangle(0, 0, width, height);
113       context->clip();
114       context->save();
115       context->set_source (p.mask, 0, 0);
116       context->rectangle (0, 0, width, height);
117       context->clip();
118       context->paint();
119       context->restore();
120     }
121 }
122 
PixMask(Glib::ustring filename,bool & broken)123 PixMask::PixMask(Glib::ustring filename, bool &broken)
124      : width(0), height(0)
125 {
126   if (Gtk::Main::instance() == NULL)
127     {
128       broken = true;
129       return;
130     }
131   Glib::RefPtr<Gdk::Pixbuf> pixbuf;
132   try
133     {
134       pixbuf = Gdk::Pixbuf::create_from_file(filename);
135     }
136   catch (const Glib::Exception &ex)
137     {
138       std::cerr << String::ucompose(_("Could not load image file `%1'."), filename) << std::endl;
139       broken = true;
140       return;
141     }
142   pixmap = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, pixbuf->get_width(), pixbuf->get_height());
143   gc = Cairo::Context::create(pixmap);
144   Gdk::Cairo::set_source_pixbuf(gc, pixbuf, 0, 0);
145   gc->paint();
146   mask = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, pixbuf->get_width(), pixbuf->get_height());
147   width = pixbuf->get_width();
148   height = pixbuf->get_height();
149   unscaled_width = width;
150   unscaled_height = height;
151 }
152 
create(Glib::ustring filename,bool & broken)153 PixMask* PixMask::create(Glib::ustring filename, bool &broken)
154 {
155   return new PixMask(filename, broken);
156 }
157 
create(Glib::RefPtr<Gdk::Pixbuf> pixbuf)158 PixMask* PixMask::create(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
159 {
160   return new PixMask(pixbuf);
161 }
162 
create(Cairo::RefPtr<Cairo::Surface> pixmap,Cairo::RefPtr<Cairo::Surface> mask)163 PixMask* PixMask::create(Cairo::RefPtr<Cairo::Surface> pixmap, Cairo::RefPtr<Cairo::Surface> mask)
164 {
165   return new PixMask(pixmap, mask);
166 }
167 
copy()168 PixMask* PixMask::copy()
169 {
170   return new PixMask(*this);
171 }
172 
blit_centered(Cairo::RefPtr<Cairo::Surface> dest,Vector<int> pos)173 void PixMask::blit_centered(Cairo::RefPtr<Cairo::Surface> dest, Vector<int> pos)
174 {
175   blit (dest, pos.x - (width/2), pos.y - (height/2));
176   return;
177 }
178 
blit(Cairo::RefPtr<Cairo::Surface> dest,Vector<int> pos)179 void PixMask::blit(Cairo::RefPtr<Cairo::Surface> dest, Vector<int> pos)
180 {
181   blit (dest, pos.x, pos.y);
182   return;
183 }
184 
blit(Cairo::RefPtr<Cairo::Surface> dest,int dest_x,int dest_y)185 void PixMask::blit(Cairo::RefPtr<Cairo::Surface> dest, int dest_x, int dest_y)
186 {
187   //Here we are the map tile, blitting ourselves to the buffer where other
188   //map tiles live.
189   Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(dest);
190   context->set_source (pixmap, dest_x, dest_y);
191   context->paint();
192 }
193 
blit(LwRectangle src,Cairo::RefPtr<Cairo::Surface> p,Vector<int> dest)194 void PixMask::blit(LwRectangle src, Cairo::RefPtr<Cairo::Surface> p, Vector<int> dest)
195 {
196   Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(p);
197   // Select the clipping rectangle
198   context->rectangle(dest.x, dest.y, src.w, src.h);
199 
200   context->clip();
201   context->save();
202   context->set_source (pixmap, dest.x-src.x, dest.y-src.y);
203   context->rectangle (0, 0, src.w, src.h);
204   context->clip();
205   context->paint();
206   context->restore();
207 }
208 
blit(Vector<int> tile,int ts,Cairo::RefPtr<Cairo::Surface> p,Vector<int> dest)209 void PixMask::blit(Vector<int> tile, int ts, Cairo::RefPtr<Cairo::Surface> p, Vector<int> dest)
210 {
211   Vector<int> src = tile * ts;
212   blit (LwRectangle(src.x, src.y, ts, ts), p, dest);
213 }
214 
scale(PixMask * & p,int xsize,int ysize,Gdk::InterpType interp)215 void PixMask::scale(PixMask*& p, int xsize, int ysize, Gdk::InterpType interp)
216 {
217   PixMask *scaled = p->scale(xsize, ysize, interp);
218   delete p;
219   p = scaled;
220   p->set_unscaled_width(p->get_unscaled_width());
221   p->set_unscaled_height(p->get_unscaled_height());
222   return;
223 }
224 
scale(int xsize,int ysize,Gdk::InterpType interp)225 PixMask * PixMask::scale(int xsize, int ysize, Gdk::InterpType interp)
226 {
227   Glib::RefPtr<Gdk::Pixbuf> pixbuf = to_pixbuf();
228   PixMask *pix = PixMask::create(pixbuf->scale_simple(xsize, ysize, interp));
229   pix->set_unscaled_width(get_unscaled_width());
230   pix->set_unscaled_height(get_unscaled_height());
231   pixbuf.reset();
232   return pix;
233 }
234 
to_pixbuf()235 Glib::RefPtr<Gdk::Pixbuf> PixMask::to_pixbuf()
236 {
237   Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(pixmap, 0, 0, width, height);
238   Glib::RefPtr<Gdk::Pixbuf> alphabuf = buf->add_alpha(true, 255, 87, 204);
239   return alphabuf;
240 }
241 
draw_pixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf,int src_x,int src_y,int dest_x,int dest_y,int w,int h)242 void PixMask::draw_pixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf, int src_x, int src_y, int dest_x, int dest_y, int w, int h)
243 {
244 
245   Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(pixmap);
246   // Select the clipping rectangle
247   context->rectangle(dest_x, dest_y, w, h);
248 
249   context->clip();
250   context->save();
251   PixMask *p = create(pixbuf);
252   context->set_source (p->get_pixmap(), src_x, src_y);
253   context->rectangle (src_x, src_y, w, h);
254   context->clip();
255   context->paint();
256   context->restore();
257   delete p;
258 }
259 
get_depth()260 int PixMask::get_depth()
261 {
262     return 32;
263 }
264 
get_dim() const265 Vector<int> PixMask::get_dim() const
266 {
267   return Vector<int>(width, height);
268 }
269 
get_unscaled_dim() const270 Vector<int> PixMask::get_unscaled_dim() const
271 {
272   return Vector<int>(unscaled_width, unscaled_height);
273 }
274