blob: dbf0c41819dd505f7be9a2e3971f9c53f29d6812 [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 Ossman3d74d882017-01-02 19:49:52 +010031static void render(CGContextRef gc, CGImageRef image,
32 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
39 // We have to use clipping to partially display an image
40 rect.origin.x = x;
41 rect.origin.y = y;
42 rect.size.width = w;
43 rect.size.height = h;
44
45 CGContextClipToRect(gc, rect);
46
47 rect.origin.x = x - src_x;
48 rect.origin.y = y - src_y;
49 rect.size.width = src_w;
50 rect.size.height = src_h;
51
52 CGContextDrawImage(gc, rect, image);
53
54 CGContextRestoreGState(gc);
55}
56
Pierre Ossman403ac272017-01-02 17:00:41 +010057void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
58{
59 unsigned char* out;
60 int x, y;
61
62 r = (unsigned)r * a / 255;
63 g = (unsigned)g * a / 255;
64 b = (unsigned)b * a / 255;
65
66 out = data;
67 for (y = 0;y < width();y++) {
68 for (x = 0;x < height();x++) {
69 *out++ = b;
70 *out++ = g;
71 *out++ = r;
72 *out++ = a;
73 }
74 }
75}
76
77void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
78{
Pierre Ossman403ac272017-01-02 17:00:41 +010079 CGContextSaveGState(fl_gc);
80
81 // Reset the transformation matrix back to the default identity
82 // matrix as otherwise we get a massive performance hit
83 CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
84
85 // macOS Coordinates are from bottom left, not top left
86 src_y = height() - (src_y + h);
87 y = Fl_Window::current()->h() - (y + h);
88
Pierre Ossman3d74d882017-01-02 19:49:52 +010089 render(fl_gc, image, src_x, src_y, width(), height(), x, y, w, h);
Pierre Ossman403ac272017-01-02 17:00:41 +010090
91 CGContextRestoreGState(fl_gc);
92}
93
Pierre Ossman3d74d882017-01-02 19:49:52 +010094void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
95{
96 CGColorSpaceRef lut;
97 CGContextRef bitmap;
98
99 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
100 if (!lut) {
101 lut = CGColorSpaceCreateDeviceRGB();
102 if (!lut)
103 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
104 }
105
106 bitmap = CGBitmapContextCreate(dst->data, dst->width(),
107 dst->height(), 8, dst->width()*4, lut,
108 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
109 CGColorSpaceRelease(lut);
110 if (!bitmap)
111 throw rdr::Exception("CGBitmapContextCreate");
112
113 // macOS Coordinates are from bottom left, not top left
114 src_y = height() - (src_y + h);
115 y = dst->height() - (y + h);
116
117 render(bitmap, image, src_x, src_y, width(), height(), x, y, w, h);
118
119 CGContextRelease(bitmap);
120}
121
Pierre Ossman403ac272017-01-02 17:00:41 +0100122void Surface::alloc()
123{
124 CGColorSpaceRef lut;
125 CGDataProviderRef provider;
126
127 data = new unsigned char[width() * height() * 4];
128
129 lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
130 if (!lut) {
131 lut = CGColorSpaceCreateDeviceRGB();
132 if (!lut)
133 throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
134 }
135
136 provider = CGDataProviderCreateWithData(NULL, data,
137 width() * height() * 4, NULL);
138 if (!provider)
139 throw rdr::Exception("CGDataProviderCreateWithData");
140
141 image = CGImageCreate(width(), height(), 8, 32, width() * 4, lut,
142 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
143 provider, NULL, false, kCGRenderingIntentDefault);
144 CGColorSpaceRelease(lut);
145 CGDataProviderRelease(provider);
146 if (!image)
147 throw rdr::Exception("CGImageCreate");
148}
149
150void Surface::dealloc()
151{
152 CGImageRelease(image);
153 delete [] data;
154}
155
156void Surface::update(const Fl_RGB_Image* image)
157{
158 int x, y;
159 const unsigned char* in;
160 unsigned char* out;
161
162 assert(image->w() == width());
163 assert(image->h() == height());
164
165 // Convert data and pre-multiply alpha
166 in = (const unsigned char*)image->data()[0];
167 out = data;
168 for (y = 0;y < image->h();y++) {
169 for (x = 0;x < image->w();x++) {
170 switch (image->d()) {
171 case 1:
172 *out++ = in[0];
173 *out++ = in[0];
174 *out++ = in[0];
175 *out++ = 0xff;
176 break;
177 case 2:
178 *out++ = (unsigned)in[0] * in[1] / 255;
179 *out++ = (unsigned)in[0] * in[1] / 255;
180 *out++ = (unsigned)in[0] * in[1] / 255;
181 *out++ = in[1];
182 break;
183 case 3:
184 *out++ = in[2];
185 *out++ = in[1];
186 *out++ = in[0];
187 *out++ = 0xff;
188 break;
189 case 4:
190 *out++ = (unsigned)in[2] * in[3] / 255;
191 *out++ = (unsigned)in[1] * in[3] / 255;
192 *out++ = (unsigned)in[0] * in[3] / 255;
193 *out++ = in[3];
194 break;
195 }
196 in += image->d();
197 }
198 if (image->ld() != 0)
199 in += image->ld() - image->w() * image->d();
200 }
201}