1 /* Copyright 2016 Pierre Ossman for Cendio AB
2 *
3 * This 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 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software 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 General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19 #include <assert.h>
20
21 #include <ApplicationServices/ApplicationServices.h>
22
23 #include <FL/Fl_RGB_Image.H>
24 #include <FL/Fl_Window.H>
25 #include <FL/x.H>
26
27 #include <rdr/Exception.h>
28
29 #include "cocoa.h"
30 #include "Surface.h"
31
32 static CGColorSpaceRef srgb = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
33
create_image(CGColorSpaceRef lut,const unsigned char * data,int w,int h,bool skip_alpha)34 static CGImageRef create_image(CGColorSpaceRef lut,
35 const unsigned char* data,
36 int w, int h, bool skip_alpha)
37 {
38 CGDataProviderRef provider;
39 CGImageAlphaInfo alpha;
40
41 CGImageRef image;
42
43 provider = CGDataProviderCreateWithData(NULL, data,
44 w * h * 4, NULL);
45 if (!provider)
46 throw rdr::Exception("CGDataProviderCreateWithData");
47
48 // FIXME: This causes a performance hit, but is necessary to avoid
49 // artifacts in the edges of the window
50 if (skip_alpha)
51 alpha = kCGImageAlphaNoneSkipFirst;
52 else
53 alpha = kCGImageAlphaPremultipliedFirst;
54
55 image = CGImageCreate(w, h, 8, 32, w * 4, lut,
56 alpha | kCGBitmapByteOrder32Little,
57 provider, NULL, false, kCGRenderingIntentDefault);
58 CGDataProviderRelease(provider);
59 if (!image)
60 throw rdr::Exception("CGImageCreate");
61
62 return image;
63 }
64
render(CGContextRef gc,CGColorSpaceRef lut,const unsigned char * data,CGBlendMode mode,CGFloat alpha,int src_x,int src_y,int src_w,int src_h,int x,int y,int w,int h)65 static void render(CGContextRef gc, CGColorSpaceRef lut,
66 const unsigned char* data,
67 CGBlendMode mode, CGFloat alpha,
68 int src_x, int src_y, int src_w, int src_h,
69 int x, int y, int w, int h)
70 {
71 CGRect rect;
72 CGImageRef image, subimage;
73
74 image = create_image(lut, data, src_w, src_h, mode == kCGBlendModeCopy);
75
76 rect.origin.x = src_x;
77 rect.origin.y = src_y;
78 rect.size.width = w;
79 rect.size.height = h;
80
81 subimage = CGImageCreateWithImageInRect(image, rect);
82 if (!subimage)
83 throw rdr::Exception("CGImageCreateImageWithImageInRect");
84
85 CGContextSaveGState(gc);
86
87 CGContextSetBlendMode(gc, mode);
88 CGContextSetAlpha(gc, alpha);
89
90 rect.origin.x = x;
91 rect.origin.y = y;
92 rect.size.width = w;
93 rect.size.height = h;
94
95 CGContextDrawImage(gc, rect, subimage);
96
97 CGContextRestoreGState(gc);
98
99 CGImageRelease(subimage);
100 CGImageRelease(image);
101 }
102
make_bitmap(int width,int height,unsigned char * data)103 static CGContextRef make_bitmap(int width, int height, unsigned char* data)
104 {
105 CGContextRef bitmap;
106
107 bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, srgb,
108 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
109 if (!bitmap)
110 throw rdr::Exception("CGBitmapContextCreate");
111
112 return bitmap;
113 }
114
clear(unsigned char r,unsigned char g,unsigned char b,unsigned char a)115 void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
116 {
117 unsigned char* out;
118 int x, y;
119
120 r = (unsigned)r * a / 255;
121 g = (unsigned)g * a / 255;
122 b = (unsigned)b * a / 255;
123
124 out = data;
125 for (y = 0;y < width();y++) {
126 for (x = 0;x < height();x++) {
127 *out++ = b;
128 *out++ = g;
129 *out++ = r;
130 *out++ = a;
131 }
132 }
133 }
134
draw(int src_x,int src_y,int x,int y,int w,int h)135 void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
136 {
137 CGColorSpaceRef lut;
138
139 CGContextSaveGState(fl_gc);
140
141 // Reset the transformation matrix back to the default identity
142 // matrix as otherwise we get a massive performance hit
143 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
144
145 // macOS Coordinates are from bottom left, not top left
146 y = Fl_Window::current()->h() - (y + h);
147
148 lut = cocoa_win_color_space(Fl_Window::current());
149 render(fl_gc, lut, data, kCGBlendModeCopy, 1.0,
150 src_x, src_y, width(), height(), x, y, w, h);
151 CGColorSpaceRelease(lut);
152
153 CGContextRestoreGState(fl_gc);
154 }
155
draw(Surface * dst,int src_x,int src_y,int x,int y,int w,int h)156 void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
157 {
158 CGContextRef bitmap;
159
160 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
161
162 // macOS Coordinates are from bottom left, not top left
163 y = dst->height() - (y + h);
164
165 render(bitmap, srgb, data, kCGBlendModeCopy, 1.0,
166 src_x, src_y, width(), height(), x, y, w, h);
167
168 CGContextRelease(bitmap);
169 }
170
blend(int src_x,int src_y,int x,int y,int w,int h,int a)171 void Surface::blend(int src_x, int src_y, int x, int y, int w, int h, int a)
172 {
173 CGColorSpaceRef lut;
174
175 CGContextSaveGState(fl_gc);
176
177 // Reset the transformation matrix back to the default identity
178 // matrix as otherwise we get a massive performance hit
179 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
180
181 // macOS Coordinates are from bottom left, not top left
182 y = Fl_Window::current()->h() - (y + h);
183
184 lut = cocoa_win_color_space(Fl_Window::current());
185 render(fl_gc, lut, data, kCGBlendModeNormal, (CGFloat)a/255.0,
186 src_x, src_y, width(), height(), x, y, w, h);
187 CGColorSpaceRelease(lut);
188
189 CGContextRestoreGState(fl_gc);
190 }
191
blend(Surface * dst,int src_x,int src_y,int x,int y,int w,int h,int a)192 void Surface::blend(Surface* dst, int src_x, int src_y, int x, int y, int w, int h, int a)
193 {
194 CGContextRef bitmap;
195
196 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
197
198 // macOS Coordinates are from bottom left, not top left
199 y = dst->height() - (y + h);
200
201 render(bitmap, srgb, data, kCGBlendModeNormal, (CGFloat)a/255.0,
202 src_x, src_y, width(), height(), x, y, w, h);
203
204 CGContextRelease(bitmap);
205 }
206
alloc()207 void Surface::alloc()
208 {
209 data = new unsigned char[width() * height() * 4];
210 }
211
dealloc()212 void Surface::dealloc()
213 {
214 delete [] data;
215 }
216
update(const Fl_RGB_Image * image)217 void Surface::update(const Fl_RGB_Image* image)
218 {
219 int x, y;
220 const unsigned char* in;
221 unsigned char* out;
222
223 assert(image->w() == width());
224 assert(image->h() == height());
225
226 // Convert data and pre-multiply alpha
227 in = (const unsigned char*)image->data()[0];
228 out = data;
229 for (y = 0;y < image->h();y++) {
230 for (x = 0;x < image->w();x++) {
231 switch (image->d()) {
232 case 1:
233 *out++ = in[0];
234 *out++ = in[0];
235 *out++ = in[0];
236 *out++ = 0xff;
237 break;
238 case 2:
239 *out++ = (unsigned)in[0] * in[1] / 255;
240 *out++ = (unsigned)in[0] * in[1] / 255;
241 *out++ = (unsigned)in[0] * in[1] / 255;
242 *out++ = in[1];
243 break;
244 case 3:
245 *out++ = in[2];
246 *out++ = in[1];
247 *out++ = in[0];
248 *out++ = 0xff;
249 break;
250 case 4:
251 *out++ = (unsigned)in[2] * in[3] / 255;
252 *out++ = (unsigned)in[1] * in[3] / 255;
253 *out++ = (unsigned)in[0] * in[3] / 255;
254 *out++ = in[3];
255 break;
256 }
257 in += image->d();
258 }
259 if (image->ld() != 0)
260 in += image->ld() - image->w() * image->d();
261 }
262 }
263