uvtd: vt: implement VT_GETMODE/SETMODE ioctl state-tracking
[platform/upstream/kmscon.git] / src / uterm_fbdev_render.c
1 /*
2  * uterm - Linux User-Space Terminal fbdev module
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * FBDEV module rendering functions
28  */
29
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include "shl_log.h"
35 #include "uterm_fbdev_internal.h"
36 #include "uterm_video.h"
37 #include "uterm_video_internal.h"
38
39 #define LOG_SUBSYSTEM "fbdev_render"
40
41 static int clamp_value(int val, int low, int up)
42 {
43         if (val < low)
44                 return low;
45         else if (val > up)
46                 return up;
47         else
48                 return val;
49 }
50
51 static uint_fast32_t xrgb32_to_device(struct uterm_display *disp,
52                                       uint32_t pixel)
53 {
54         uint8_t r, g, b, nr, ng, nb;
55         int i;
56         uint_fast32_t res;
57         struct fbdev_display *fbdev = disp->data;
58
59         r = (pixel >> 16) & 0xff;
60         g = (pixel >>  8) & 0xff;
61         b = (pixel >>  0) & 0xff;
62
63         if (disp->flags & DISPLAY_DITHERING) {
64                 /* This is some very basic dithering which simply does small
65                  * rotations in the lower pixel bits. TODO: Let's take a look
66                  * at Floyd-Steinberg dithering which should give much better
67                  * results. It is slightly slower, though.
68                  * Or even better would be some Sierra filter like the Sierra
69                  * LITE. */
70                 fbdev->dither_r = r - fbdev->dither_r;
71                 fbdev->dither_g = g - fbdev->dither_g;
72                 fbdev->dither_b = b - fbdev->dither_b;
73                 r = clamp_value(fbdev->dither_r, 0, 255) >> (8 - fbdev->len_r);
74                 g = clamp_value(fbdev->dither_g, 0, 255) >> (8 - fbdev->len_g);
75                 b = clamp_value(fbdev->dither_b, 0, 255) >> (8 - fbdev->len_b);
76                 nr = r << (8 - fbdev->len_r);
77                 ng = g << (8 - fbdev->len_g);
78                 nb = b << (8 - fbdev->len_b);
79
80                 for (i = fbdev->len_r; i < 8; i <<= 1)
81                         nr |= nr >> i;
82                 for (i = fbdev->len_g; i < 8; i <<= 1)
83                         ng |= ng >> i;
84                 for (i = fbdev->len_b; i < 8; i <<= 1)
85                         nb |= nb >> i;
86
87                 fbdev->dither_r = nr - fbdev->dither_r;
88                 fbdev->dither_g = ng - fbdev->dither_g;
89                 fbdev->dither_b = nb - fbdev->dither_b;
90
91                 res  = r << fbdev->off_r;
92                 res |= g << fbdev->off_g;
93                 res |= b << fbdev->off_b;
94         } else {
95                 res  = (r >> (8 - fbdev->len_r)) << fbdev->off_r;
96                 res |= (g >> (8 - fbdev->len_g)) << fbdev->off_g;
97                 res |= (b >> (8 - fbdev->len_b)) << fbdev->off_b;
98         }
99
100         return res;
101 }
102
103 int uterm_fbdev_display_blit(struct uterm_display *disp,
104                              const struct uterm_video_buffer *buf,
105                              unsigned int x, unsigned int y)
106 {
107         unsigned int tmp;
108         uint8_t *dst, *src;
109         unsigned int width, height, i;
110         uint32_t val;
111         struct fbdev_display *fbdev = disp->data;
112
113         if (!buf || buf->format != UTERM_FORMAT_XRGB32)
114                 return -EINVAL;
115
116         tmp = x + buf->width;
117         if (tmp < x || x >= fbdev->xres)
118                 return -EINVAL;
119         if (tmp > fbdev->xres)
120                 width = fbdev->xres - x;
121         else
122                 width = buf->width;
123
124         tmp = y + buf->height;
125         if (tmp < y || y >= fbdev->yres)
126                 return -EINVAL;
127         if (tmp > fbdev->yres)
128                 height = fbdev->yres - y;
129         else
130                 height = buf->height;
131
132         if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
133                 dst = fbdev->map;
134         else
135                 dst = &fbdev->map[fbdev->yres * fbdev->stride];
136         dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
137         src = buf->data;
138
139         if (fbdev->xrgb32) {
140                 while (height--) {
141                         memcpy(dst, src, 4 * width);
142                         dst += fbdev->stride;
143                         src += buf->stride;
144                 }
145         } else if (fbdev->Bpp == 2) {
146                 while (height--) {
147                         for (i = 0; i < width; ++i) {
148                                 val = ((uint32_t*)src)[i];
149                                 ((uint16_t*)dst)[i] = xrgb32_to_device(disp, val);
150                         }
151                         dst += fbdev->stride;
152                         src += buf->stride;
153                 }
154         } else if (fbdev->Bpp == 4) {
155                 while (height--) {
156                         for (i = 0; i < width; ++i) {
157                                 val = ((uint32_t*)src)[i];
158                                 ((uint32_t*)dst)[i] = xrgb32_to_device(disp, val);
159                         }
160                         dst += fbdev->stride;
161                         src += buf->stride;
162                 }
163         } else {
164                 log_debug("invalid Bpp");
165         }
166
167         return 0;
168 }
169
170 int uterm_fbdev_display_fake_blendv(struct uterm_display *disp,
171                                     const struct uterm_video_blend_req *req,
172                                     size_t num)
173 {
174         unsigned int tmp;
175         uint8_t *dst, *src;
176         unsigned int width, height, i, j;
177         unsigned int r, g, b;
178         uint32_t val;
179         struct fbdev_display *fbdev = disp->data;
180
181         if (!req)
182                 return -EINVAL;
183
184         for (j = 0; j < num; ++j, ++req) {
185                 if (!req->buf)
186                         continue;
187
188                 if (req->buf->format != UTERM_FORMAT_GREY)
189                         return -EOPNOTSUPP;
190
191                 tmp = req->x + req->buf->width;
192                 if (tmp < req->x || req->x >= fbdev->xres)
193                         return -EINVAL;
194                 if (tmp > fbdev->xres)
195                         width = fbdev->xres - req->x;
196                 else
197                         width = req->buf->width;
198
199                 tmp = req->y + req->buf->height;
200                 if (tmp < req->y || req->y >= fbdev->yres)
201                         return -EINVAL;
202                 if (tmp > fbdev->yres)
203                         height = fbdev->yres - req->y;
204                 else
205                         height = req->buf->height;
206
207                 if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
208                         dst = fbdev->map;
209                 else
210                         dst = &fbdev->map[fbdev->yres * fbdev->stride];
211                 dst = &dst[req->y * fbdev->stride + req->x * fbdev->Bpp];
212                 src = req->buf->data;
213
214                 /* Division by 256 instead of 255 increases
215                  * speed by like 20% on slower machines.
216                  * Downside is, full white is 254/254/254
217                  * instead of 255/255/255. */
218                 if (fbdev->xrgb32) {
219                         while (height--) {
220                                 for (i = 0; i < width; ++i) {
221                                         if (src[i] == 0) {
222                                                 r = req->br;
223                                                 g = req->bg;
224                                                 b = req->bb;
225                                         } else if (src[i] == 255) {
226                                                 r = req->fr;
227                                                 g = req->fg;
228                                                 b = req->fb;
229                                         } else {
230                                                 r = req->fr * src[i] +
231                                                     req->br * (255 - src[i]);
232                                                 r /= 256;
233                                                 g = req->fg * src[i] +
234                                                     req->bg * (255 - src[i]);
235                                                 g /= 256;
236                                                 b = req->fb * src[i] +
237                                                     req->bb * (255 - src[i]);
238                                                 b /= 256;
239                                         }
240                                         val = (r << 16) | (g << 8) | b;
241                                         ((uint32_t*)dst)[i] = val;
242                                 }
243                                 dst += fbdev->stride;
244                                 src += req->buf->stride;
245                         }
246                 } else if (fbdev->Bpp == 2) {
247                         while (height--) {
248                                 for (i = 0; i < width; ++i) {
249                                         if (src[i] == 0) {
250                                                 r = req->br;
251                                                 g = req->bg;
252                                                 b = req->bb;
253                                         } else if (src[i] == 255) {
254                                                 r = req->fr;
255                                                 g = req->fg;
256                                                 b = req->fb;
257                                         } else {
258                                                 r = req->fr * src[i] +
259                                                     req->br * (255 - src[i]);
260                                                 r /= 256;
261                                                 g = req->fg * src[i] +
262                                                     req->bg * (255 - src[i]);
263                                                 g /= 256;
264                                                 b = req->fb * src[i] +
265                                                     req->bb * (255 - src[i]);
266                                                 b /= 256;
267                                         }
268                                         val = (r << 16) | (g << 8) | b;
269                                         ((uint16_t*)dst)[i] =
270                                                 xrgb32_to_device(disp, val);
271                                 }
272                                 dst += fbdev->stride;
273                                 src += req->buf->stride;
274                         }
275                 } else if (fbdev->Bpp == 4) {
276                         while (height--) {
277                                 for (i = 0; i < width; ++i) {
278                                         if (src[i] == 0) {
279                                                 r = req->br;
280                                                 g = req->bg;
281                                                 b = req->bb;
282                                         } else if (src[i] == 255) {
283                                                 r = req->fr;
284                                                 g = req->fg;
285                                                 b = req->fb;
286                                         } else {
287                                                 r = req->fr * src[i] +
288                                                     req->br * (255 - src[i]);
289                                                 r /= 256;
290                                                 g = req->fg * src[i] +
291                                                     req->bg * (255 - src[i]);
292                                                 g /= 256;
293                                                 b = req->fb * src[i] +
294                                                     req->bb * (255 - src[i]);
295                                                 b /= 256;
296                                         }
297                                         val = (r << 16) | (g << 8) | b;
298                                         ((uint32_t*)dst)[i] =
299                                                 xrgb32_to_device(disp, val);
300                                 }
301                                 dst += fbdev->stride;
302                                 src += req->buf->stride;
303                         }
304                 } else {
305                         log_warning("invalid Bpp");
306                 }
307         }
308
309         return 0;
310 }
311
312 int uterm_fbdev_display_fill(struct uterm_display *disp,
313                              uint8_t r, uint8_t g, uint8_t b,
314                              unsigned int x, unsigned int y,
315                              unsigned int width, unsigned int height)
316 {
317         unsigned int tmp, i;
318         uint8_t *dst;
319         uint32_t full_val, rgb32;
320         struct fbdev_display *fbdev = disp->data;
321
322         tmp = x + width;
323         if (tmp < x || x >= fbdev->xres)
324                 return -EINVAL;
325         if (tmp > fbdev->xres)
326                 width = fbdev->xres - x;
327         tmp = y + height;
328         if (tmp < y || y >= fbdev->yres)
329                 return -EINVAL;
330         if (tmp > fbdev->yres)
331                 height = fbdev->yres - y;
332
333         if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
334                 dst = fbdev->map;
335         else
336                 dst = &fbdev->map[fbdev->yres * fbdev->stride];
337         dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
338
339         full_val  = ((r & 0xff) >> (8 - fbdev->len_r)) << fbdev->off_r;
340         full_val |= ((g & 0xff) >> (8 - fbdev->len_g)) << fbdev->off_g;
341         full_val |= ((b & 0xff) >> (8 - fbdev->len_b)) << fbdev->off_b;
342
343         if (fbdev->Bpp == 2) {
344                 if (disp->flags & DISPLAY_DITHERING) {
345                         rgb32  = (r & 0xff) << 16;
346                         rgb32 |= (g & 0xff) <<  8;
347                         rgb32 |= (b & 0xff) <<  0;
348                         while (height--) {
349                                 for (i = 0; i < width; ++i)
350                                         ((uint16_t*)dst)[i] = xrgb32_to_device(disp, rgb32);
351                                 dst += fbdev->stride;
352                         }
353                 } else {
354                         full_val &= 0xffff;
355                         while (height--) {
356                                 for (i = 0; i < width; ++i)
357                                         ((uint16_t*)dst)[i] = full_val;
358                                 dst += fbdev->stride;
359                         }
360                 }
361         } else if (fbdev->Bpp == 4) {
362                 while (height--) {
363                         for (i = 0; i < width; ++i)
364                                 ((uint32_t*)dst)[i] = full_val;
365                         dst += fbdev->stride;
366                 }
367         } else {
368                 log_error("invalid Bpp");
369                 return -EFAULT;
370         }
371
372         return 0;
373 }