blob: e226118cac7a3424ab059a5af42aa32dfd9608bd [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +01002 * Copyright 2014 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +010019#include <assert.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000020#include <string.h>
21#include <rfb/Cursor.h>
22#include <rfb/LogWriter.h>
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +010023#include <rfb/Exception.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024
25using namespace rfb;
26
27static LogWriter vlog("Cursor");
28
29void Cursor::setSize(int w, int h) {
30 int oldMaskLen = maskLen();
31 ManagedPixelBuffer::setSize(w, h);
32 if (maskLen() > oldMaskLen) {
33 delete [] mask.buf;
34 mask.buf = new rdr::U8[maskLen()];
35 }
36}
37
38void Cursor::drawOutline(const Pixel& c)
39{
40 Cursor outlined;
Pierre Ossman56f99d62015-06-05 12:57:02 +020041 rdr::U8 cbuf[4];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042
43 // Create a mirror of the existing cursor
44 outlined.setPF(getPF());
45 outlined.setSize(width(), height());
46 outlined.hotspot = hotspot;
47
48 // Clear the mirror's background to the outline colour
Pierre Ossman56f99d62015-06-05 12:57:02 +020049 outlined.getPF().bufferFromPixel(cbuf, c);
50 outlined.fillRect(getRect(), cbuf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051
52 // Blit the existing cursor, using its mask
53 outlined.maskRect(getRect(), data, mask.buf);
54
55 // Now just adjust the mask to add the outline. The outline pixels
56 // will already be the right colour. :)
57 int maskBytesPerRow = (width() + 7) / 8;
58 for (int y = 0; y < height(); y++) {
59 for (int byte=0; byte<maskBytesPerRow; byte++) {
60 rdr::U8 m8 = mask.buf[y*maskBytesPerRow + byte];
61
62 // Handle above & below outline
63 if (y > 0) m8 |= mask.buf[(y-1)*maskBytesPerRow + byte];
64 if (y < height()-1) m8 |= mask.buf[(y+1)*maskBytesPerRow + byte];
65
66 // Left outline
67 m8 |= mask.buf[y*maskBytesPerRow + byte] << 1;
68 if (byte < maskBytesPerRow-1)
69 m8 |= (mask.buf[y*maskBytesPerRow + byte + 1] >> 7) & 1;
70
71 // Right outline
72 m8 |= mask.buf[y*maskBytesPerRow + byte] >> 1;
73 if (byte > 0)
74 m8 |= (mask.buf[y*maskBytesPerRow + byte - 1] << 7) & 128;
75
76 outlined.mask.buf[y*maskBytesPerRow + byte] = m8;
77 }
78 }
79
80 // Replace the existing cursor & mask with the new one
81 delete [] data;
82 delete [] mask.buf;
83 data = outlined.data; outlined.data = 0;
84 mask.buf = outlined.mask.buf; outlined.mask.buf = 0;
85}
86
Pierre Ossmand4f718d2014-02-13 14:37:25 +010087rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1) const
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000088{
89 bool gotPix0 = false;
90 bool gotPix1 = false;
91 *pix0 = *pix1 = 0;
92 rdr::U8Array source(maskLen());
93 memset(source.buf, 0, maskLen());
94
95 int maskBytesPerRow = (width() + 7) / 8;
Pierre Ossman01cf5732010-09-30 11:53:15 +000096 const rdr::U8 *data_ptr = data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000097 for (int y = 0; y < height(); y++) {
98 for (int x = 0; x < width(); x++) {
99 int byte = y * maskBytesPerRow + x / 8;
100 int bit = 7 - x % 8;
101 if (mask.buf[byte] & (1 << bit)) {
Pierre Ossman01cf5732010-09-30 11:53:15 +0000102 Pixel pix = getPF().pixelFromBuffer(data_ptr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000103 if (!gotPix0 || pix == *pix0) {
104 gotPix0 = true;
105 *pix0 = pix;
106 } else if (!gotPix1 || pix == *pix1) {
107 gotPix1 = true;
108 *pix1 = pix;
109 source.buf[byte] |= (1 << bit);
110 } else {
111 // not a bitmap
112 return 0;
113 }
114 }
Pierre Ossman01cf5732010-09-30 11:53:15 +0000115 data_ptr += getPF().bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000116 }
117 }
118 return source.takeBuf();
119}
120
121// crop() determines the "busy" rectangle for the cursor - the minimum bounding
122// rectangle containing actual pixels. This isn't the most efficient algorithm
123// but it's short. For sanity, we make sure that the busy rectangle always
124// includes the hotspot (the hotspot is unsigned on the wire so otherwise it
125// would cause problems if it was above or left of the actual pixels)
126
127void Cursor::crop()
128{
129 Rect busy = getRect().intersect(Rect(hotspot.x, hotspot.y,
130 hotspot.x+1, hotspot.y+1));
131 int maskBytesPerRow = (width() + 7) / 8;
132 int x, y;
133 for (y = 0; y < height(); y++) {
134 for (x = 0; x < width(); x++) {
135 int byte = y * maskBytesPerRow + x / 8;
136 int bit = 7 - x % 8;
137 if (mask.buf[byte] & (1 << bit)) {
138 if (x < busy.tl.x) busy.tl.x = x;
139 if (x+1 > busy.br.x) busy.br.x = x+1;
140 if (y < busy.tl.y) busy.tl.y = y;
141 if (y+1 > busy.br.y) busy.br.y = y+1;
142 }
143 }
144 }
145
146 if (width() == busy.width() && height() == busy.height()) return;
147
148 vlog.debug("cropping %dx%d to %dx%d", width(), height(),
149 busy.width(), busy.height());
150
151 // Copy the pixel data
152 int newDataLen = busy.area() * (getPF().bpp/8);
153 rdr::U8* newData = new rdr::U8[newDataLen];
154 getImage(newData, busy);
155
156 // Copy the mask
157 int newMaskBytesPerRow = (busy.width()+7)/8;
158 int newMaskLen = newMaskBytesPerRow * busy.height();
159 rdr::U8* newMask = new rdr::U8[newMaskLen];
160 memset(newMask, 0, newMaskLen);
161 for (y = 0; y < busy.height(); y++) {
162 int newByte, newBit;
163 for (x = 0; x < busy.width(); x++) {
164 int oldByte = (y+busy.tl.y) * maskBytesPerRow + (x+busy.tl.x) / 8;
165 int oldBit = 7 - (x+busy.tl.x) % 8;
166 newByte = y * newMaskBytesPerRow + x / 8;
167 newBit = 7 - x % 8;
168 if (mask.buf[oldByte] & (1 << oldBit))
169 newMask[newByte] |= (1 << newBit);
170 }
171 }
172
173 // Set the size and data to the new, cropped cursor.
174 setSize(busy.width(), busy.height());
175 hotspot = hotspot.subtract(busy.tl);
176 delete [] data;
177 delete [] mask.buf;
178 datasize = newDataLen;
179 data = newData;
180 mask.buf = newMask;
181}
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100182
183RenderedCursor::RenderedCursor()
184{
185}
186
Pierre Ossmand4f718d2014-02-13 14:37:25 +0100187const rdr::U8* RenderedCursor::getBuffer(const Rect& _r, int* stride) const
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100188{
189 Rect r;
190
191 r = _r.translate(offset.negate());
192 if (!r.enclosed_by(buffer.getRect()))
193 throw Exception("RenderedCursor: Invalid area requested");
194
195 return buffer.getBuffer(r, stride);
196}
197
198void RenderedCursor::update(PixelBuffer* framebuffer,
199 Cursor* cursor, const Point& pos)
200{
Pierre Ossman1391fc42017-01-20 15:58:44 +0100201 Point rawOffset, diff;
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100202 Rect clippedRect;
203
204 const rdr::U8* data;
205 int stride;
206
207 assert(framebuffer);
208 assert(cursor);
209
210 if (!framebuffer->getPF().equal(cursor->getPF()))
211 throw Exception("RenderedCursor: Trying to render cursor on incompatible frame buffer");
212
213 format = framebuffer->getPF();
214 width_ = framebuffer->width();
215 height_ = framebuffer->height();
216
217 rawOffset = pos.subtract(cursor->hotspot);
218 clippedRect = cursor->getRect(rawOffset).intersect(framebuffer->getRect());
219 offset = clippedRect.tl;
220
221 buffer.setPF(cursor->getPF());
222 buffer.setSize(clippedRect.width(), clippedRect.height());
223
224 data = framebuffer->getBuffer(buffer.getRect(offset), &stride);
225 buffer.imageRect(buffer.getRect(), data, stride);
226
Pierre Ossman1391fc42017-01-20 15:58:44 +0100227 diff = offset.subtract(rawOffset);
228 data = cursor->getBuffer(buffer.getRect(diff), &stride);
229
230 buffer.maskRect(buffer.getRect(), data, cursor->mask.buf, diff,
231 stride, (cursor->width() + 7) / 8);
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100232}