blob: 10d1e3e2c69a3a174ba094ca3b1adaad8a5fbe5e [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 Ossman3b347312017-04-28 11:33:28 +020031static CGImageRef create_image(const unsigned char* data,
32 int w, int h)
33{
34 CGColorSpaceRef lut;
35 CGDataProviderRef provider;
36
37 CGImageRef image;
38
39 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
40 if (!lut) {
41 lut = CGColorSpaceCreateDeviceRGB();
42 if (!lut)
43 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
44 }
45
46 provider = CGDataProviderCreateWithData(NULL, data,
47 w * h * 4, NULL);
48 if (!provider)
49 throw rdr::Exception("CGDataProviderCreateWithData");
50
51 image = CGImageCreate(w, h, 8, 32, w * 4, lut,
52 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,
53 provider, NULL, false, kCGRenderingIntentDefault);
54 CGColorSpaceRelease(lut);
55 CGDataProviderRelease(provider);
56 if (!image)
57 throw rdr::Exception("CGImageCreate");
58
59 return image;
60}
61
62static void render(CGContextRef gc,
63 const unsigned char* data,
Pierre Ossman455566e2017-02-10 16:37:52 +010064 CGBlendMode mode, CGFloat alpha,
Pierre Ossman3b347312017-04-28 11:33:28 +020065 int src_x, int src_y, int src_w, int src_h,
Pierre Ossman3d74d882017-01-02 19:49:52 +010066 int x, int y, int w, int h)
67{
68 CGRect rect;
Pierre Ossman3b347312017-04-28 11:33:28 +020069 CGImageRef image, subimage;
70
71 image = create_image(data, src_w, src_h);
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020072
73 rect.origin.x = src_x;
74 rect.origin.y = src_y;
75 rect.size.width = w;
76 rect.size.height = h;
77
78 subimage = CGImageCreateWithImageInRect(image, rect);
79 if (!subimage)
80 throw rdr::Exception("CGImageCreateImageWithImageInRect");
Pierre Ossman3d74d882017-01-02 19:49:52 +010081
82 CGContextSaveGState(gc);
83
Pierre Ossmande6a5802017-01-02 20:07:10 +010084 CGContextSetBlendMode(gc, mode);
Pierre Ossman455566e2017-02-10 16:37:52 +010085 CGContextSetAlpha(gc, alpha);
Pierre Ossmande6a5802017-01-02 20:07:10 +010086
Pierre Ossman3d74d882017-01-02 19:49:52 +010087 rect.origin.x = x;
88 rect.origin.y = y;
89 rect.size.width = w;
90 rect.size.height = h;
91
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020092 CGContextDrawImage(gc, rect, subimage);
Pierre Ossman3d74d882017-01-02 19:49:52 +010093
94 CGContextRestoreGState(gc);
Pierre Ossman2d0dc3a2017-04-27 13:28:49 +020095
96 CGImageRelease(subimage);
Pierre Ossman3b347312017-04-28 11:33:28 +020097 CGImageRelease(image);
Pierre Ossman3d74d882017-01-02 19:49:52 +010098}
99
Pierre Ossmande6a5802017-01-02 20:07:10 +0100100static CGContextRef make_bitmap(int width, int height, unsigned char* data)
101{
102 CGColorSpaceRef lut;
103 CGContextRef bitmap;
104
105 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
106 if (!lut) {
107 lut = CGColorSpaceCreateDeviceRGB();
108 if (!lut)
109 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
110 }
111
112 bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, lut,
113 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
114 CGColorSpaceRelease(lut);
115 if (!bitmap)
116 throw rdr::Exception("CGBitmapContextCreate");
117
118 return bitmap;
119}
120
Pierre Ossman403ac272017-01-02 17:00:41 +0100121void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
122{
123 unsigned char* out;
124 int x, y;
125
126 r = (unsigned)r * a / 255;
127 g = (unsigned)g * a / 255;
128 b = (unsigned)b * a / 255;
129
130 out = data;
131 for (y = 0;y < width();y++) {
132 for (x = 0;x < height();x++) {
133 *out++ = b;
134 *out++ = g;
135 *out++ = r;
136 *out++ = a;
137 }
138 }
139}
140
141void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
142{
Pierre Ossman403ac272017-01-02 17:00:41 +0100143 CGContextSaveGState(fl_gc);
144
145 // Reset the transformation matrix back to the default identity
146 // matrix as otherwise we get a massive performance hit
147 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
148
149 // macOS Coordinates are from bottom left, not top left
Pierre Ossman403ac272017-01-02 17:00:41 +0100150 y = Fl_Window::current()->h() - (y + h);
151
Pierre Ossman3b347312017-04-28 11:33:28 +0200152 render(fl_gc, data, kCGBlendModeCopy, 1.0,
153 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman403ac272017-01-02 17:00:41 +0100154
155 CGContextRestoreGState(fl_gc);
156}
157
Pierre Ossman3d74d882017-01-02 19:49:52 +0100158void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
159{
Pierre Ossman3d74d882017-01-02 19:49:52 +0100160 CGContextRef bitmap;
161
Pierre Ossmande6a5802017-01-02 20:07:10 +0100162 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100163
164 // macOS Coordinates are from bottom left, not top left
Pierre Ossman3d74d882017-01-02 19:49:52 +0100165 y = dst->height() - (y + h);
166
Pierre Ossman3b347312017-04-28 11:33:28 +0200167 render(bitmap, data, kCGBlendModeCopy, 1.0,
168 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100169
170 CGContextRelease(bitmap);
171}
172
Pierre Ossman455566e2017-02-10 16:37:52 +0100173void 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 +0100174{
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
Pierre Ossmande6a5802017-01-02 20:07:10 +0100182 y = Fl_Window::current()->h() - (y + h);
183
Pierre Ossman3b347312017-04-28 11:33:28 +0200184 render(fl_gc, data, kCGBlendModeNormal, (CGFloat)a/255.0,
185 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossmande6a5802017-01-02 20:07:10 +0100186
187 CGContextRestoreGState(fl_gc);
188}
189
Pierre Ossman455566e2017-02-10 16:37:52 +0100190void 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 +0100191{
192 CGContextRef bitmap;
193
194 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
195
196 // macOS Coordinates are from bottom left, not top left
Pierre Ossmande6a5802017-01-02 20:07:10 +0100197 y = dst->height() - (y + h);
198
Pierre Ossman3b347312017-04-28 11:33:28 +0200199 render(bitmap, data, kCGBlendModeNormal, (CGFloat)a/255.0,
200 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100201
202 CGContextRelease(bitmap);
203}
204
Pierre Ossman403ac272017-01-02 17:00:41 +0100205void Surface::alloc()
206{
Pierre Ossman403ac272017-01-02 17:00:41 +0100207 data = new unsigned char[width() * height() * 4];
Pierre Ossman403ac272017-01-02 17:00:41 +0100208}
209
210void Surface::dealloc()
211{
Pierre Ossman403ac272017-01-02 17:00:41 +0100212 delete [] data;
213}
214
215void Surface::update(const Fl_RGB_Image* image)
216{
217 int x, y;
218 const unsigned char* in;
219 unsigned char* out;
220
221 assert(image->w() == width());
222 assert(image->h() == height());
223
224 // Convert data and pre-multiply alpha
225 in = (const unsigned char*)image->data()[0];
226 out = data;
227 for (y = 0;y < image->h();y++) {
228 for (x = 0;x < image->w();x++) {
229 switch (image->d()) {
230 case 1:
231 *out++ = in[0];
232 *out++ = in[0];
233 *out++ = in[0];
234 *out++ = 0xff;
235 break;
236 case 2:
237 *out++ = (unsigned)in[0] * in[1] / 255;
238 *out++ = (unsigned)in[0] * in[1] / 255;
239 *out++ = (unsigned)in[0] * in[1] / 255;
240 *out++ = in[1];
241 break;
242 case 3:
243 *out++ = in[2];
244 *out++ = in[1];
245 *out++ = in[0];
246 *out++ = 0xff;
247 break;
248 case 4:
249 *out++ = (unsigned)in[2] * in[3] / 255;
250 *out++ = (unsigned)in[1] * in[3] / 255;
251 *out++ = (unsigned)in[0] * in[3] / 255;
252 *out++ = in[3];
253 break;
254 }
255 in += image->d();
256 }
257 if (image->ld() != 0)
258 in += image->ld() - image->w() * image->d();
259 }
260}