blob: 2dfaa477cff1c6d4e86e27b9a75f3b754d7680d0 [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 Ossman3d74d882017-01-02 19:49:52 +010033 int src_x, int src_y, int src_w, int src_h,
34 int x, int y, int w, int h)
35{
36 CGRect rect;
37
38 CGContextSaveGState(gc);
39
Pierre Ossmande6a5802017-01-02 20:07:10 +010040 CGContextSetBlendMode(gc, mode);
Pierre Ossman455566e2017-02-10 16:37:52 +010041 CGContextSetAlpha(gc, alpha);
Pierre Ossmande6a5802017-01-02 20:07:10 +010042
Pierre Ossman3d74d882017-01-02 19:49:52 +010043 // We have to use clipping to partially display an image
44 rect.origin.x = x;
45 rect.origin.y = y;
46 rect.size.width = w;
47 rect.size.height = h;
48
49 CGContextClipToRect(gc, rect);
50
51 rect.origin.x = x - src_x;
52 rect.origin.y = y - src_y;
53 rect.size.width = src_w;
54 rect.size.height = src_h;
55
56 CGContextDrawImage(gc, rect, image);
57
58 CGContextRestoreGState(gc);
59}
60
Pierre Ossmande6a5802017-01-02 20:07:10 +010061static CGContextRef make_bitmap(int width, int height, unsigned char* data)
62{
63 CGColorSpaceRef lut;
64 CGContextRef bitmap;
65
66 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
67 if (!lut) {
68 lut = CGColorSpaceCreateDeviceRGB();
69 if (!lut)
70 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
71 }
72
73 bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, lut,
74 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
75 CGColorSpaceRelease(lut);
76 if (!bitmap)
77 throw rdr::Exception("CGBitmapContextCreate");
78
79 return bitmap;
80}
81
Pierre Ossman403ac272017-01-02 17:00:41 +010082void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
83{
84 unsigned char* out;
85 int x, y;
86
87 r = (unsigned)r * a / 255;
88 g = (unsigned)g * a / 255;
89 b = (unsigned)b * a / 255;
90
91 out = data;
92 for (y = 0;y < width();y++) {
93 for (x = 0;x < height();x++) {
94 *out++ = b;
95 *out++ = g;
96 *out++ = r;
97 *out++ = a;
98 }
99 }
100}
101
102void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
103{
Pierre Ossman403ac272017-01-02 17:00:41 +0100104 CGContextSaveGState(fl_gc);
105
106 // Reset the transformation matrix back to the default identity
107 // matrix as otherwise we get a massive performance hit
108 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
109
110 // macOS Coordinates are from bottom left, not top left
111 src_y = height() - (src_y + h);
112 y = Fl_Window::current()->h() - (y + h);
113
Pierre Ossman455566e2017-02-10 16:37:52 +0100114 render(fl_gc, image, kCGBlendModeCopy, 1.0,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100115 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman403ac272017-01-02 17:00:41 +0100116
117 CGContextRestoreGState(fl_gc);
118}
119
Pierre Ossman3d74d882017-01-02 19:49:52 +0100120void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
121{
Pierre Ossman3d74d882017-01-02 19:49:52 +0100122 CGContextRef bitmap;
123
Pierre Ossmande6a5802017-01-02 20:07:10 +0100124 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100125
126 // macOS Coordinates are from bottom left, not top left
127 src_y = height() - (src_y + h);
128 y = dst->height() - (y + h);
129
Pierre Ossman455566e2017-02-10 16:37:52 +0100130 render(bitmap, image, kCGBlendModeCopy, 1.0,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100131 src_x, src_y, width(), height(), x, y, w, h);
132
133 CGContextRelease(bitmap);
134}
135
Pierre Ossman455566e2017-02-10 16:37:52 +0100136void 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 +0100137{
138 CGContextSaveGState(fl_gc);
139
140 // Reset the transformation matrix back to the default identity
141 // matrix as otherwise we get a massive performance hit
142 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
143
144 // macOS Coordinates are from bottom left, not top left
145 src_y = height() - (src_y + h);
146 y = Fl_Window::current()->h() - (y + h);
147
Pierre Ossman455566e2017-02-10 16:37:52 +0100148 render(fl_gc, image, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100149 src_x, src_y, width(), height(), x, y, w, h);
150
151 CGContextRestoreGState(fl_gc);
152}
153
Pierre Ossman455566e2017-02-10 16:37:52 +0100154void 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 +0100155{
156 CGContextRef bitmap;
157
158 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
159
160 // macOS Coordinates are from bottom left, not top left
161 src_y = height() - (src_y + h);
162 y = dst->height() - (y + h);
163
Pierre Ossman455566e2017-02-10 16:37:52 +0100164 render(bitmap, image, kCGBlendModeNormal, (CGFloat)a/255.0,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100165 src_x, src_y, width(), height(), 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}