blob: fe2d722b9e9587f2cd02a30ec33cce04c3015129 [file] [log] [blame]
Pierre Ossman403ac272017-01-02 17:00:41 +01001/* 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
Pierre Ossman1669a2d2017-04-28 11:37:12 +020029#include "cocoa.h"
Pierre Ossman403ac272017-01-02 17:00:41 +010030#include "Surface.h"
31
Pierre Ossman1669a2d2017-04-28 11:37:12 +020032static CGColorSpaceRef srgb = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
33
34static CGImageRef create_image(CGColorSpaceRef lut,
35 const unsigned char* data,
Pierre Ossmana88991b2017-04-28 11:40:02 +020036 int w, int h, bool skip_alpha)
Pierre Ossman3b347312017-04-28 11:33:28 +020037{
Pierre Ossman3b347312017-04-28 11:33:28 +020038 CGDataProviderRef provider;
Pierre Ossmana88991b2017-04-28 11:40:02 +020039 CGImageAlphaInfo alpha;
Pierre Ossman3b347312017-04-28 11:33:28 +020040
41 CGImageRef image;
42
Pierre Ossman3b347312017-04-28 11:33:28 +020043 provider = CGDataProviderCreateWithData(NULL, data,
44 w * h * 4, NULL);
45 if (!provider)
46 throw rdr::Exception("CGDataProviderCreateWithData");
47
Pierre Ossmana88991b2017-04-28 11:40:02 +020048 // 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
Pierre Ossman3b347312017-04-28 11:33:28 +020055 image = CGImageCreate(w, h, 8, 32, w * 4, lut,
Pierre Ossmana88991b2017-04-28 11:40:02 +020056 alpha | kCGBitmapByteOrder32Little,
Pierre Ossman3b347312017-04-28 11:33:28 +020057 provider, NULL, false, kCGRenderingIntentDefault);
Pierre Ossman3b347312017-04-28 11:33:28 +020058 CGDataProviderRelease(provider);
59 if (!image)
60 throw rdr::Exception("CGImageCreate");
61
62 return image;
63}
64
Pierre Ossman1669a2d2017-04-28 11:37:12 +020065static void render(CGContextRef gc, CGColorSpaceRef lut,
Pierre Ossman3b347312017-04-28 11:33:28 +020066 const unsigned char* data,
Pierre Ossman455566e2017-02-10 16:37:52 +010067 CGBlendMode mode, CGFloat alpha,
Pierre Ossman3b347312017-04-28 11:33:28 +020068 int src_x, int src_y, int src_w, int src_h,
Pierre Ossman3d74d882017-01-02 19:49:52 +010069 int x, int y, int w, int h)
70{
71 CGRect rect;
Pierre Ossman3b347312017-04-28 11:33:28 +020072 CGImageRef image, subimage;
73
Pierre Ossmana88991b2017-04-28 11:40:02 +020074 image = create_image(lut, data, src_w, src_h, mode == kCGBlendModeCopy);
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020075
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");
Pierre Ossman3d74d882017-01-02 19:49:52 +010084
85 CGContextSaveGState(gc);
86
Pierre Ossmande6a5802017-01-02 20:07:10 +010087 CGContextSetBlendMode(gc, mode);
Pierre Ossman455566e2017-02-10 16:37:52 +010088 CGContextSetAlpha(gc, alpha);
Pierre Ossmande6a5802017-01-02 20:07:10 +010089
Pierre Ossman3d74d882017-01-02 19:49:52 +010090 rect.origin.x = x;
91 rect.origin.y = y;
92 rect.size.width = w;
93 rect.size.height = h;
94
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020095 CGContextDrawImage(gc, rect, subimage);
Pierre Ossman3d74d882017-01-02 19:49:52 +010096
97 CGContextRestoreGState(gc);
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020098
99 CGImageRelease(subimage);
Pierre Ossman3b347312017-04-28 11:33:28 +0200100 CGImageRelease(image);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100101}
102
Pierre Ossmande6a5802017-01-02 20:07:10 +0100103static CGContextRef make_bitmap(int width, int height, unsigned char* data)
104{
Pierre Ossmande6a5802017-01-02 20:07:10 +0100105 CGContextRef bitmap;
106
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200107 bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, srgb,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100108 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100109 if (!bitmap)
110 throw rdr::Exception("CGBitmapContextCreate");
111
112 return bitmap;
113}
114
Pierre Ossman403ac272017-01-02 17:00:41 +0100115void 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
135void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
136{
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200137 CGColorSpaceRef lut;
138
Pierre Ossman403ac272017-01-02 17:00:41 +0100139 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
Pierre Ossman403ac272017-01-02 17:00:41 +0100146 y = Fl_Window::current()->h() - (y + h);
147
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200148 lut = cocoa_win_color_space(Fl_Window::current());
149 render(fl_gc, lut, data, kCGBlendModeCopy, 1.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200150 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200151 CGColorSpaceRelease(lut);
Pierre Ossman403ac272017-01-02 17:00:41 +0100152
153 CGContextRestoreGState(fl_gc);
154}
155
Pierre Ossman3d74d882017-01-02 19:49:52 +0100156void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
157{
Pierre Ossman3d74d882017-01-02 19:49:52 +0100158 CGContextRef bitmap;
159
Pierre Ossmande6a5802017-01-02 20:07:10 +0100160 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100161
162 // macOS Coordinates are from bottom left, not top left
Pierre Ossman3d74d882017-01-02 19:49:52 +0100163 y = dst->height() - (y + h);
164
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200165 render(bitmap, srgb, data, kCGBlendModeCopy, 1.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200166 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100167
168 CGContextRelease(bitmap);
169}
170
Pierre Ossman455566e2017-02-10 16:37:52 +0100171void Surface::blend(int src_x, int src_y, int x, int y, int w, int h, int a)
Pierre Ossmande6a5802017-01-02 20:07:10 +0100172{
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200173 CGColorSpaceRef lut;
174
Pierre Ossmande6a5802017-01-02 20:07:10 +0100175 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
Pierre Ossmande6a5802017-01-02 20:07:10 +0100182 y = Fl_Window::current()->h() - (y + h);
183
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200184 lut = cocoa_win_color_space(Fl_Window::current());
185 render(fl_gc, lut, data, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200186 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200187 CGColorSpaceRelease(lut);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100188
189 CGContextRestoreGState(fl_gc);
190}
191
Pierre Ossman455566e2017-02-10 16:37:52 +0100192void Surface::blend(Surface* dst, int src_x, int src_y, int x, int y, int w, int h, int a)
Pierre Ossmande6a5802017-01-02 20:07:10 +0100193{
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
Pierre Ossmande6a5802017-01-02 20:07:10 +0100199 y = dst->height() - (y + h);
200
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200201 render(bitmap, srgb, data, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200202 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100203
204 CGContextRelease(bitmap);
205}
206
Pierre Ossman403ac272017-01-02 17:00:41 +0100207void Surface::alloc()
208{
Pierre Ossman403ac272017-01-02 17:00:41 +0100209 data = new unsigned char[width() * height() * 4];
Pierre Ossman403ac272017-01-02 17:00:41 +0100210}
211
212void Surface::dealloc()
213{
Pierre Ossman403ac272017-01-02 17:00:41 +0100214 delete [] data;
215}
216
217void 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}