blob: 62b767fad9eee7ddb5ad997c557aa555e8a07770 [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;
41
42 // Create a mirror of the existing cursor
43 outlined.setPF(getPF());
44 outlined.setSize(width(), height());
45 outlined.hotspot = hotspot;
46
47 // Clear the mirror's background to the outline colour
48 outlined.fillRect(getRect(), c);
49
50 // Blit the existing cursor, using its mask
51 outlined.maskRect(getRect(), data, mask.buf);
52
53 // Now just adjust the mask to add the outline. The outline pixels
54 // will already be the right colour. :)
55 int maskBytesPerRow = (width() + 7) / 8;
56 for (int y = 0; y < height(); y++) {
57 for (int byte=0; byte<maskBytesPerRow; byte++) {
58 rdr::U8 m8 = mask.buf[y*maskBytesPerRow + byte];
59
60 // Handle above & below outline
61 if (y > 0) m8 |= mask.buf[(y-1)*maskBytesPerRow + byte];
62 if (y < height()-1) m8 |= mask.buf[(y+1)*maskBytesPerRow + byte];
63
64 // Left outline
65 m8 |= mask.buf[y*maskBytesPerRow + byte] << 1;
66 if (byte < maskBytesPerRow-1)
67 m8 |= (mask.buf[y*maskBytesPerRow + byte + 1] >> 7) & 1;
68
69 // Right outline
70 m8 |= mask.buf[y*maskBytesPerRow + byte] >> 1;
71 if (byte > 0)
72 m8 |= (mask.buf[y*maskBytesPerRow + byte - 1] << 7) & 128;
73
74 outlined.mask.buf[y*maskBytesPerRow + byte] = m8;
75 }
76 }
77
78 // Replace the existing cursor & mask with the new one
79 delete [] data;
80 delete [] mask.buf;
81 data = outlined.data; outlined.data = 0;
82 mask.buf = outlined.mask.buf; outlined.mask.buf = 0;
83}
84
85rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1)
86{
87 bool gotPix0 = false;
88 bool gotPix1 = false;
89 *pix0 = *pix1 = 0;
90 rdr::U8Array source(maskLen());
91 memset(source.buf, 0, maskLen());
92
93 int maskBytesPerRow = (width() + 7) / 8;
Pierre Ossman01cf5732010-09-30 11:53:15 +000094 const rdr::U8 *data_ptr = data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000095 for (int y = 0; y < height(); y++) {
96 for (int x = 0; x < width(); x++) {
97 int byte = y * maskBytesPerRow + x / 8;
98 int bit = 7 - x % 8;
99 if (mask.buf[byte] & (1 << bit)) {
Pierre Ossman01cf5732010-09-30 11:53:15 +0000100 Pixel pix = getPF().pixelFromBuffer(data_ptr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000101 if (!gotPix0 || pix == *pix0) {
102 gotPix0 = true;
103 *pix0 = pix;
104 } else if (!gotPix1 || pix == *pix1) {
105 gotPix1 = true;
106 *pix1 = pix;
107 source.buf[byte] |= (1 << bit);
108 } else {
109 // not a bitmap
110 return 0;
111 }
112 }
Pierre Ossman01cf5732010-09-30 11:53:15 +0000113 data_ptr += getPF().bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000114 }
115 }
116 return source.takeBuf();
117}
118
119// crop() determines the "busy" rectangle for the cursor - the minimum bounding
120// rectangle containing actual pixels. This isn't the most efficient algorithm
121// but it's short. For sanity, we make sure that the busy rectangle always
122// includes the hotspot (the hotspot is unsigned on the wire so otherwise it
123// would cause problems if it was above or left of the actual pixels)
124
125void Cursor::crop()
126{
127 Rect busy = getRect().intersect(Rect(hotspot.x, hotspot.y,
128 hotspot.x+1, hotspot.y+1));
129 int maskBytesPerRow = (width() + 7) / 8;
130 int x, y;
131 for (y = 0; y < height(); y++) {
132 for (x = 0; x < width(); x++) {
133 int byte = y * maskBytesPerRow + x / 8;
134 int bit = 7 - x % 8;
135 if (mask.buf[byte] & (1 << bit)) {
136 if (x < busy.tl.x) busy.tl.x = x;
137 if (x+1 > busy.br.x) busy.br.x = x+1;
138 if (y < busy.tl.y) busy.tl.y = y;
139 if (y+1 > busy.br.y) busy.br.y = y+1;
140 }
141 }
142 }
143
144 if (width() == busy.width() && height() == busy.height()) return;
145
146 vlog.debug("cropping %dx%d to %dx%d", width(), height(),
147 busy.width(), busy.height());
148
149 // Copy the pixel data
150 int newDataLen = busy.area() * (getPF().bpp/8);
151 rdr::U8* newData = new rdr::U8[newDataLen];
152 getImage(newData, busy);
153
154 // Copy the mask
155 int newMaskBytesPerRow = (busy.width()+7)/8;
156 int newMaskLen = newMaskBytesPerRow * busy.height();
157 rdr::U8* newMask = new rdr::U8[newMaskLen];
158 memset(newMask, 0, newMaskLen);
159 for (y = 0; y < busy.height(); y++) {
160 int newByte, newBit;
161 for (x = 0; x < busy.width(); x++) {
162 int oldByte = (y+busy.tl.y) * maskBytesPerRow + (x+busy.tl.x) / 8;
163 int oldBit = 7 - (x+busy.tl.x) % 8;
164 newByte = y * newMaskBytesPerRow + x / 8;
165 newBit = 7 - x % 8;
166 if (mask.buf[oldByte] & (1 << oldBit))
167 newMask[newByte] |= (1 << newBit);
168 }
169 }
170
171 // Set the size and data to the new, cropped cursor.
172 setSize(busy.width(), busy.height());
173 hotspot = hotspot.subtract(busy.tl);
174 delete [] data;
175 delete [] mask.buf;
176 datasize = newDataLen;
177 data = newData;
178 mask.buf = newMask;
179}
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100180
181RenderedCursor::RenderedCursor()
182{
183}
184
185const rdr::U8* RenderedCursor::getBuffer(const Rect& _r, int* stride)
186{
187 Rect r;
188
189 r = _r.translate(offset.negate());
190 if (!r.enclosed_by(buffer.getRect()))
191 throw Exception("RenderedCursor: Invalid area requested");
192
193 return buffer.getBuffer(r, stride);
194}
195
196void RenderedCursor::update(PixelBuffer* framebuffer,
197 Cursor* cursor, const Point& pos)
198{
199 Point rawOffset;
200 Rect clippedRect;
201
202 const rdr::U8* data;
203 int stride;
204
205 assert(framebuffer);
206 assert(cursor);
207
208 if (!framebuffer->getPF().equal(cursor->getPF()))
209 throw Exception("RenderedCursor: Trying to render cursor on incompatible frame buffer");
210
211 format = framebuffer->getPF();
212 width_ = framebuffer->width();
213 height_ = framebuffer->height();
214
215 rawOffset = pos.subtract(cursor->hotspot);
216 clippedRect = cursor->getRect(rawOffset).intersect(framebuffer->getRect());
217 offset = clippedRect.tl;
218
219 buffer.setPF(cursor->getPF());
220 buffer.setSize(clippedRect.width(), clippedRect.height());
221
222 data = framebuffer->getBuffer(buffer.getRect(offset), &stride);
223 buffer.imageRect(buffer.getRect(), data, stride);
224
225 data = cursor->getBuffer(cursor->getRect(), &stride);
226 buffer.maskRect(cursor->getRect(rawOffset.subtract(offset)),
227 data, cursor->mask.buf);
228}