blob: 668187bc30f84a21301457d52da2d870564cb1f4 [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
29#include "Surface.h"
30
Pierre Ossman455566e2017-02-10 16:37:52 +010031static void render(CGContextRef gc, CGImageRef image,
32 CGBlendMode mode, CGFloat alpha,
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020033 int src_x, int src_y,
Pierre Ossman3d74d882017-01-02 19:49:52 +010034 int x, int y, int w, int h)
35{
36 CGRect rect;
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020037 CGImageRef subimage;
38
39 rect.origin.x = src_x;
40 rect.origin.y = src_y;
41 rect.size.width = w;
42 rect.size.height = h;
43
44 subimage = CGImageCreateWithImageInRect(image, rect);
45 if (!subimage)
46 throw rdr::Exception("CGImageCreateImageWithImageInRect");
Pierre Ossman3d74d882017-01-02 19:49:52 +010047
48 CGContextSaveGState(gc);
49
Pierre Ossmande6a5802017-01-02 20:07:10 +010050 CGContextSetBlendMode(gc, mode);
Pierre Ossman455566e2017-02-10 16:37:52 +010051 CGContextSetAlpha(gc, alpha);
Pierre Ossmande6a5802017-01-02 20:07:10 +010052
Pierre Ossman3d74d882017-01-02 19:49:52 +010053 rect.origin.x = x;
54 rect.origin.y = y;
55 rect.size.width = w;
56 rect.size.height = h;
57
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020058 CGContextDrawImage(gc, rect, subimage);
Pierre Ossman3d74d882017-01-02 19:49:52 +010059
60 CGContextRestoreGState(gc);
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020061
62 CGImageRelease(subimage);
Pierre Ossman3d74d882017-01-02 19:49:52 +010063}
64
Pierre Ossmande6a5802017-01-02 20:07:10 +010065static CGContextRef make_bitmap(int width, int height, unsigned char* data)
66{
67 CGColorSpaceRef lut;
68 CGContextRef bitmap;
69
70 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
71 if (!lut) {
72 lut = CGColorSpaceCreateDeviceRGB();
73 if (!lut)
74 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
75 }
76
77 bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, lut,
78 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
79 CGColorSpaceRelease(lut);
80 if (!bitmap)
81 throw rdr::Exception("CGBitmapContextCreate");
82
83 return bitmap;
84}
85
Pierre Ossman403ac272017-01-02 17:00:41 +010086void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
87{
88 unsigned char* out;
89 int x, y;
90
91 r = (unsigned)r * a / 255;
92 g = (unsigned)g * a / 255;
93 b = (unsigned)b * a / 255;
94
95 out = data;
96 for (y = 0;y < width();y++) {
97 for (x = 0;x < height();x++) {
98 *out++ = b;
99 *out++ = g;
100 *out++ = r;
101 *out++ = a;
102 }
103 }
104}
105
106void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
107{
Pierre Ossman403ac272017-01-02 17:00:41 +0100108 CGContextSaveGState(fl_gc);
109
110 // Reset the transformation matrix back to the default identity
111 // matrix as otherwise we get a massive performance hit
112 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
113
114 // macOS Coordinates are from bottom left, not top left
Pierre Ossman403ac272017-01-02 17:00:41 +0100115 y = Fl_Window::current()->h() - (y + h);
116
Pierre Ossman455566e2017-02-10 16:37:52 +0100117 render(fl_gc, image, kCGBlendModeCopy, 1.0,
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +0200118 src_x, src_y, x, y, w, h);
Pierre Ossman403ac272017-01-02 17:00:41 +0100119
120 CGContextRestoreGState(fl_gc);
121}
122
Pierre Ossman3d74d882017-01-02 19:49:52 +0100123void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
124{
Pierre Ossman3d74d882017-01-02 19:49:52 +0100125 CGContextRef bitmap;
126
Pierre Ossmande6a5802017-01-02 20:07:10 +0100127 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100128
129 // macOS Coordinates are from bottom left, not top left
Pierre Ossman3d74d882017-01-02 19:49:52 +0100130 y = dst->height() - (y + h);
131
Pierre Ossman455566e2017-02-10 16:37:52 +0100132 render(bitmap, image, kCGBlendModeCopy, 1.0,
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +0200133 src_x, src_y, x, y, w, h);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100134
135 CGContextRelease(bitmap);
136}
137
Pierre Ossman455566e2017-02-10 16:37:52 +0100138void 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 +0100139{
140 CGContextSaveGState(fl_gc);
141
142 // Reset the transformation matrix back to the default identity
143 // matrix as otherwise we get a massive performance hit
144 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
145
146 // macOS Coordinates are from bottom left, not top left
Pierre Ossmande6a5802017-01-02 20:07:10 +0100147 y = Fl_Window::current()->h() - (y + h);
148
Pierre Ossman455566e2017-02-10 16:37:52 +0100149 render(fl_gc, image, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +0200150 src_x, src_y, x, y, w, h);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100151
152 CGContextRestoreGState(fl_gc);
153}
154
Pierre Ossman455566e2017-02-10 16:37:52 +0100155void 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 +0100156{
157 CGContextRef bitmap;
158
159 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
160
161 // macOS Coordinates are from bottom left, not top left
Pierre Ossmande6a5802017-01-02 20:07:10 +0100162 y = dst->height() - (y + h);
163
Pierre Ossman455566e2017-02-10 16:37:52 +0100164 render(bitmap, image, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +0200165 src_x, src_y, x, y, w, h);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100166
167 CGContextRelease(bitmap);
168}
169
Pierre Ossman403ac272017-01-02 17:00:41 +0100170void Surface::alloc()
171{
172 CGColorSpaceRef lut;
173 CGDataProviderRef provider;
174
175 data = new unsigned char[width() * height() * 4];
176
177 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
178 if (!lut) {
179 lut = CGColorSpaceCreateDeviceRGB();
180 if (!lut)
181 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
182 }
183
184 provider = CGDataProviderCreateWithData(NULL, data,
185 width() * height() * 4, NULL);
186 if (!provider)
187 throw rdr::Exception("CGDataProviderCreateWithData");
188
189 image = CGImageCreate(width(), height(), 8, 32, width() * 4, lut,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100190 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,
Pierre Ossman403ac272017-01-02 17:00:41 +0100191 provider, NULL, false, kCGRenderingIntentDefault);
192 CGColorSpaceRelease(lut);
193 CGDataProviderRelease(provider);
194 if (!image)
195 throw rdr::Exception("CGImageCreate");
196}
197
198void Surface::dealloc()
199{
200 CGImageRelease(image);
201 delete [] data;
202}
203
204void Surface::update(const Fl_RGB_Image* image)
205{
206 int x, y;
207 const unsigned char* in;
208 unsigned char* out;
209
210 assert(image->w() == width());
211 assert(image->h() == height());
212
213 // Convert data and pre-multiply alpha
214 in = (const unsigned char*)image->data()[0];
215 out = data;
216 for (y = 0;y < image->h();y++) {
217 for (x = 0;x < image->w();x++) {
218 switch (image->d()) {
219 case 1:
220 *out++ = in[0];
221 *out++ = in[0];
222 *out++ = in[0];
223 *out++ = 0xff;
224 break;
225 case 2:
226 *out++ = (unsigned)in[0] * in[1] / 255;
227 *out++ = (unsigned)in[0] * in[1] / 255;
228 *out++ = (unsigned)in[0] * in[1] / 255;
229 *out++ = in[1];
230 break;
231 case 3:
232 *out++ = in[2];
233 *out++ = in[1];
234 *out++ = in[0];
235 *out++ = 0xff;
236 break;
237 case 4:
238 *out++ = (unsigned)in[2] * in[3] / 255;
239 *out++ = (unsigned)in[1] * in[3] / 255;
240 *out++ = (unsigned)in[0] * in[3] / 255;
241 *out++ = in[3];
242 break;
243 }
244 in += image->d();
245 }
246 if (image->ld() != 0)
247 in += image->ld() - image->w() * image->d();
248 }
249}