blob: 3313ae3bd5ef546b57e45bed7d9d569324fbbcb9 [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 Ossman3b347312017-04-28 11:33:28 +020036 int w, int h)
37{
Pierre Ossman3b347312017-04-28 11:33:28 +020038 CGDataProviderRef provider;
39
40 CGImageRef image;
41
Pierre Ossman3b347312017-04-28 11:33:28 +020042 provider = CGDataProviderCreateWithData(NULL, data,
43 w * h * 4, NULL);
44 if (!provider)
45 throw rdr::Exception("CGDataProviderCreateWithData");
46
47 image = CGImageCreate(w, h, 8, 32, w * 4, lut,
48 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,
49 provider, NULL, false, kCGRenderingIntentDefault);
Pierre Ossman3b347312017-04-28 11:33:28 +020050 CGDataProviderRelease(provider);
51 if (!image)
52 throw rdr::Exception("CGImageCreate");
53
54 return image;
55}
56
Pierre Ossman1669a2d2017-04-28 11:37:12 +020057static void render(CGContextRef gc, CGColorSpaceRef lut,
Pierre Ossman3b347312017-04-28 11:33:28 +020058 const unsigned char* data,
Pierre Ossman455566e2017-02-10 16:37:52 +010059 CGBlendMode mode, CGFloat alpha,
Pierre Ossman3b347312017-04-28 11:33:28 +020060 int src_x, int src_y, int src_w, int src_h,
Pierre Ossman3d74d882017-01-02 19:49:52 +010061 int x, int y, int w, int h)
62{
63 CGRect rect;
Pierre Ossman3b347312017-04-28 11:33:28 +020064 CGImageRef image, subimage;
65
Pierre Ossman1669a2d2017-04-28 11:37:12 +020066 image = create_image(lut, data, src_w, src_h);
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020067
68 rect.origin.x = src_x;
69 rect.origin.y = src_y;
70 rect.size.width = w;
71 rect.size.height = h;
72
73 subimage = CGImageCreateWithImageInRect(image, rect);
74 if (!subimage)
75 throw rdr::Exception("CGImageCreateImageWithImageInRect");
Pierre Ossman3d74d882017-01-02 19:49:52 +010076
77 CGContextSaveGState(gc);
78
Pierre Ossmande6a5802017-01-02 20:07:10 +010079 CGContextSetBlendMode(gc, mode);
Pierre Ossman455566e2017-02-10 16:37:52 +010080 CGContextSetAlpha(gc, alpha);
Pierre Ossmande6a5802017-01-02 20:07:10 +010081
Pierre Ossman3d74d882017-01-02 19:49:52 +010082 rect.origin.x = x;
83 rect.origin.y = y;
84 rect.size.width = w;
85 rect.size.height = h;
86
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020087 CGContextDrawImage(gc, rect, subimage);
Pierre Ossman3d74d882017-01-02 19:49:52 +010088
89 CGContextRestoreGState(gc);
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020090
91 CGImageRelease(subimage);
Pierre Ossman3b347312017-04-28 11:33:28 +020092 CGImageRelease(image);
Pierre Ossman3d74d882017-01-02 19:49:52 +010093}
94
Pierre Ossmande6a5802017-01-02 20:07:10 +010095static CGContextRef make_bitmap(int width, int height, unsigned char* data)
96{
Pierre Ossmande6a5802017-01-02 20:07:10 +010097 CGContextRef bitmap;
98
Pierre Ossman1669a2d2017-04-28 11:37:12 +020099 bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, srgb,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100100 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100101 if (!bitmap)
102 throw rdr::Exception("CGBitmapContextCreate");
103
104 return bitmap;
105}
106
Pierre Ossman403ac272017-01-02 17:00:41 +0100107void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
108{
109 unsigned char* out;
110 int x, y;
111
112 r = (unsigned)r * a / 255;
113 g = (unsigned)g * a / 255;
114 b = (unsigned)b * a / 255;
115
116 out = data;
117 for (y = 0;y < width();y++) {
118 for (x = 0;x < height();x++) {
119 *out++ = b;
120 *out++ = g;
121 *out++ = r;
122 *out++ = a;
123 }
124 }
125}
126
127void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
128{
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200129 CGColorSpaceRef lut;
130
Pierre Ossman403ac272017-01-02 17:00:41 +0100131 CGContextSaveGState(fl_gc);
132
133 // Reset the transformation matrix back to the default identity
134 // matrix as otherwise we get a massive performance hit
135 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
136
137 // macOS Coordinates are from bottom left, not top left
Pierre Ossman403ac272017-01-02 17:00:41 +0100138 y = Fl_Window::current()->h() - (y + h);
139
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200140 lut = cocoa_win_color_space(Fl_Window::current());
141 render(fl_gc, lut, data, kCGBlendModeCopy, 1.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200142 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200143 CGColorSpaceRelease(lut);
Pierre Ossman403ac272017-01-02 17:00:41 +0100144
145 CGContextRestoreGState(fl_gc);
146}
147
Pierre Ossman3d74d882017-01-02 19:49:52 +0100148void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
149{
Pierre Ossman3d74d882017-01-02 19:49:52 +0100150 CGContextRef bitmap;
151
Pierre Ossmande6a5802017-01-02 20:07:10 +0100152 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100153
154 // macOS Coordinates are from bottom left, not top left
Pierre Ossman3d74d882017-01-02 19:49:52 +0100155 y = dst->height() - (y + h);
156
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200157 render(bitmap, srgb, data, kCGBlendModeCopy, 1.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200158 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100159
160 CGContextRelease(bitmap);
161}
162
Pierre Ossman455566e2017-02-10 16:37:52 +0100163void 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 +0100164{
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200165 CGColorSpaceRef lut;
166
Pierre Ossmande6a5802017-01-02 20:07:10 +0100167 CGContextSaveGState(fl_gc);
168
169 // Reset the transformation matrix back to the default identity
170 // matrix as otherwise we get a massive performance hit
171 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
172
173 // macOS Coordinates are from bottom left, not top left
Pierre Ossmande6a5802017-01-02 20:07:10 +0100174 y = Fl_Window::current()->h() - (y + h);
175
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200176 lut = cocoa_win_color_space(Fl_Window::current());
177 render(fl_gc, lut, data, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200178 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200179 CGColorSpaceRelease(lut);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100180
181 CGContextRestoreGState(fl_gc);
182}
183
Pierre Ossman455566e2017-02-10 16:37:52 +0100184void 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 +0100185{
186 CGContextRef bitmap;
187
188 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
189
190 // macOS Coordinates are from bottom left, not top left
Pierre Ossmande6a5802017-01-02 20:07:10 +0100191 y = dst->height() - (y + h);
192
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200193 render(bitmap, srgb, data, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossman3b347312017-04-28 11:33:28 +0200194 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100195
196 CGContextRelease(bitmap);
197}
198
Pierre Ossman403ac272017-01-02 17:00:41 +0100199void Surface::alloc()
200{
Pierre Ossman403ac272017-01-02 17:00:41 +0100201 data = new unsigned char[width() * height() * 4];
Pierre Ossman403ac272017-01-02 17:00:41 +0100202}
203
204void Surface::dealloc()
205{
Pierre Ossman403ac272017-01-02 17:00:41 +0100206 delete [] data;
207}
208
209void Surface::update(const Fl_RGB_Image* image)
210{
211 int x, y;
212 const unsigned char* in;
213 unsigned char* out;
214
215 assert(image->w() == width());
216 assert(image->h() == height());
217
218 // Convert data and pre-multiply alpha
219 in = (const unsigned char*)image->data()[0];
220 out = data;
221 for (y = 0;y < image->h();y++) {
222 for (x = 0;x < image->w();x++) {
223 switch (image->d()) {
224 case 1:
225 *out++ = in[0];
226 *out++ = in[0];
227 *out++ = in[0];
228 *out++ = 0xff;
229 break;
230 case 2:
231 *out++ = (unsigned)in[0] * in[1] / 255;
232 *out++ = (unsigned)in[0] * in[1] / 255;
233 *out++ = (unsigned)in[0] * in[1] / 255;
234 *out++ = in[1];
235 break;
236 case 3:
237 *out++ = in[2];
238 *out++ = in[1];
239 *out++ = in[0];
240 *out++ = 0xff;
241 break;
242 case 4:
243 *out++ = (unsigned)in[2] * in[3] / 255;
244 *out++ = (unsigned)in[1] * in[3] / 255;
245 *out++ = (unsigned)in[0] * in[3] / 255;
246 *out++ = in[3];
247 break;
248 }
249 in += image->d();
250 }
251 if (image->ld() != 0)
252 in += image->ld() - image->w() * image->d();
253 }
254}