blob: 73c10f46ec689022ef309e89ce6312b3c6f0d819 [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 Ossmande6a5802017-01-02 20:07:10 +010031static void render(CGContextRef gc, CGImageRef image, CGBlendMode mode,
Pierre Ossman3d74d882017-01-02 19:49:52 +010032 int src_x, int src_y, int src_w, int src_h,
33 int x, int y, int w, int h)
34{
35 CGRect rect;
36
37 CGContextSaveGState(gc);
38
Pierre Ossmande6a5802017-01-02 20:07:10 +010039 CGContextSetBlendMode(gc, mode);
40
Pierre Ossman3d74d882017-01-02 19:49:52 +010041 // We have to use clipping to partially display an image
42 rect.origin.x = x;
43 rect.origin.y = y;
44 rect.size.width = w;
45 rect.size.height = h;
46
47 CGContextClipToRect(gc, rect);
48
49 rect.origin.x = x - src_x;
50 rect.origin.y = y - src_y;
51 rect.size.width = src_w;
52 rect.size.height = src_h;
53
54 CGContextDrawImage(gc, rect, image);
55
56 CGContextRestoreGState(gc);
57}
58
Pierre Ossmande6a5802017-01-02 20:07:10 +010059static CGContextRef make_bitmap(int width, int height, unsigned char* data)
60{
61 CGColorSpaceRef lut;
62 CGContextRef bitmap;
63
64 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
65 if (!lut) {
66 lut = CGColorSpaceCreateDeviceRGB();
67 if (!lut)
68 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
69 }
70
71 bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, lut,
72 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
73 CGColorSpaceRelease(lut);
74 if (!bitmap)
75 throw rdr::Exception("CGBitmapContextCreate");
76
77 return bitmap;
78}
79
Pierre Ossman403ac272017-01-02 17:00:41 +010080void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
81{
82 unsigned char* out;
83 int x, y;
84
85 r = (unsigned)r * a / 255;
86 g = (unsigned)g * a / 255;
87 b = (unsigned)b * a / 255;
88
89 out = data;
90 for (y = 0;y < width();y++) {
91 for (x = 0;x < height();x++) {
92 *out++ = b;
93 *out++ = g;
94 *out++ = r;
95 *out++ = a;
96 }
97 }
98}
99
100void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
101{
Pierre Ossman403ac272017-01-02 17:00:41 +0100102 CGContextSaveGState(fl_gc);
103
104 // Reset the transformation matrix back to the default identity
105 // matrix as otherwise we get a massive performance hit
106 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
107
108 // macOS Coordinates are from bottom left, not top left
109 src_y = height() - (src_y + h);
110 y = Fl_Window::current()->h() - (y + h);
111
Pierre Ossmande6a5802017-01-02 20:07:10 +0100112 render(fl_gc, image, kCGBlendModeCopy,
113 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman403ac272017-01-02 17:00:41 +0100114
115 CGContextRestoreGState(fl_gc);
116}
117
Pierre Ossman3d74d882017-01-02 19:49:52 +0100118void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
119{
Pierre Ossman3d74d882017-01-02 19:49:52 +0100120 CGContextRef bitmap;
121
Pierre Ossmande6a5802017-01-02 20:07:10 +0100122 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100123
124 // macOS Coordinates are from bottom left, not top left
125 src_y = height() - (src_y + h);
126 y = dst->height() - (y + h);
127
Pierre Ossmande6a5802017-01-02 20:07:10 +0100128 render(bitmap, image, kCGBlendModeCopy,
129 src_x, src_y, width(), height(), x, y, w, h);
130
131 CGContextRelease(bitmap);
132}
133
134void Surface::blend(int src_x, int src_y, int x, int y, int w, int h)
135{
136 CGContextSaveGState(fl_gc);
137
138 // Reset the transformation matrix back to the default identity
139 // matrix as otherwise we get a massive performance hit
140 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
141
142 // macOS Coordinates are from bottom left, not top left
143 src_y = height() - (src_y + h);
144 y = Fl_Window::current()->h() - (y + h);
145
146 render(fl_gc, image, kCGBlendModeNormal,
147 src_x, src_y, width(), height(), x, y, w, h);
148
149 CGContextRestoreGState(fl_gc);
150}
151
152void Surface::blend(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
153{
154 CGContextRef bitmap;
155
156 bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
157
158 // macOS Coordinates are from bottom left, not top left
159 src_y = height() - (src_y + h);
160 y = dst->height() - (y + h);
161
162 render(bitmap, image, kCGBlendModeNormal,
163 src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100164
165 CGContextRelease(bitmap);
166}
167
Pierre Ossman403ac272017-01-02 17:00:41 +0100168void Surface::alloc()
169{
170 CGColorSpaceRef lut;
171 CGDataProviderRef provider;
172
173 data = new unsigned char[width() * height() * 4];
174
175 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
176 if (!lut) {
177 lut = CGColorSpaceCreateDeviceRGB();
178 if (!lut)
179 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
180 }
181
182 provider = CGDataProviderCreateWithData(NULL, data,
183 width() * height() * 4, NULL);
184 if (!provider)
185 throw rdr::Exception("CGDataProviderCreateWithData");
186
187 image = CGImageCreate(width(), height(), 8, 32, width() * 4, lut,
Pierre Ossmande6a5802017-01-02 20:07:10 +0100188 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,
Pierre Ossman403ac272017-01-02 17:00:41 +0100189 provider, NULL, false, kCGRenderingIntentDefault);
190 CGColorSpaceRelease(lut);
191 CGDataProviderRelease(provider);
192 if (!image)
193 throw rdr::Exception("CGImageCreate");
194}
195
196void Surface::dealloc()
197{
198 CGImageRelease(image);
199 delete [] data;
200}
201
202void Surface::update(const Fl_RGB_Image* image)
203{
204 int x, y;
205 const unsigned char* in;
206 unsigned char* out;
207
208 assert(image->w() == width());
209 assert(image->h() == height());
210
211 // Convert data and pre-multiply alpha
212 in = (const unsigned char*)image->data()[0];
213 out = data;
214 for (y = 0;y < image->h();y++) {
215 for (x = 0;x < image->w();x++) {
216 switch (image->d()) {
217 case 1:
218 *out++ = in[0];
219 *out++ = in[0];
220 *out++ = in[0];
221 *out++ = 0xff;
222 break;
223 case 2:
224 *out++ = (unsigned)in[0] * in[1] / 255;
225 *out++ = (unsigned)in[0] * in[1] / 255;
226 *out++ = (unsigned)in[0] * in[1] / 255;
227 *out++ = in[1];
228 break;
229 case 3:
230 *out++ = in[2];
231 *out++ = in[1];
232 *out++ = in[0];
233 *out++ = 0xff;
234 break;
235 case 4:
236 *out++ = (unsigned)in[2] * in[3] / 255;
237 *out++ = (unsigned)in[1] * in[3] / 255;
238 *out++ = (unsigned)in[0] * in[3] / 255;
239 *out++ = in[3];
240 break;
241 }
242 in += image->d();
243 }
244 if (image->ld() != 0)
245 in += image->ld() - image->w() * image->d();
246 }
247}