source: mainline/uspace/lib/memgfx/src/memgc.c

Last change on this file was bc52b5b, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Allow the use of EGA attributes/24-bit characters alongside RGB

In a big hack (since we cannot have different pixel formats yet) we
use a pixel format that allows both 24-bit RGB (without character)
or 24-bit character with 8-bit attributes. Thus in GFX we cannot
currently have characters with any RGB color, but we can set
foreground and background individually (and it even works in EGA mode).

  • Property mode set to 100644
File size: 13.0 KB
Line 
1/*
2 * Copyright (c) 2021 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libmemgfx
30 * @{
31 */
32/**
33 * @file GFX memory backend
34 *
35 * This implements a graphics context over a block of memory (i.e. a simple
36 * software renderer).
37 */
38
39#include <as.h>
40#include <assert.h>
41#include <gfx/color.h>
42#include <gfx/context.h>
43#include <gfx/render.h>
44#include <io/pixel.h>
45#include <io/pixelmap.h>
46#include <memgfx/memgc.h>
47#include <stdlib.h>
48#include "../private/memgc.h"
49
50static errno_t mem_gc_set_clip_rect(void *, gfx_rect_t *);
51static errno_t mem_gc_set_color(void *, gfx_color_t *);
52static errno_t mem_gc_fill_rect(void *, gfx_rect_t *);
53static errno_t mem_gc_update(void *);
54static errno_t mem_gc_bitmap_create(void *, gfx_bitmap_params_t *,
55 gfx_bitmap_alloc_t *, void **);
56static errno_t mem_gc_bitmap_destroy(void *);
57static errno_t mem_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
58static errno_t mem_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
59static errno_t mem_gc_cursor_get_pos(void *, gfx_coord2_t *);
60static errno_t mem_gc_cursor_set_pos(void *, gfx_coord2_t *);
61static errno_t mem_gc_cursor_set_visible(void *, bool);
62static void mem_gc_invalidate_rect(mem_gc_t *, gfx_rect_t *);
63
64gfx_context_ops_t mem_gc_ops = {
65 .set_clip_rect = mem_gc_set_clip_rect,
66 .set_color = mem_gc_set_color,
67 .fill_rect = mem_gc_fill_rect,
68 .update = mem_gc_update,
69 .bitmap_create = mem_gc_bitmap_create,
70 .bitmap_destroy = mem_gc_bitmap_destroy,
71 .bitmap_render = mem_gc_bitmap_render,
72 .bitmap_get_alloc = mem_gc_bitmap_get_alloc,
73 .cursor_get_pos = mem_gc_cursor_get_pos,
74 .cursor_set_pos = mem_gc_cursor_set_pos,
75 .cursor_set_visible = mem_gc_cursor_set_visible
76};
77
78/** Set clipping rectangle on memory GC.
79 *
80 * @param arg Memory GC
81 * @param rect Rectangle
82 *
83 * @return EOK on success or an error code
84 */
85static errno_t mem_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
86{
87 mem_gc_t *mgc = (mem_gc_t *) arg;
88
89 if (rect != NULL)
90 gfx_rect_clip(rect, &mgc->rect, &mgc->clip_rect);
91 else
92 mgc->clip_rect = mgc->rect;
93
94 return EOK;
95}
96
97/** Set color on memory GC.
98 *
99 * Set drawing color on memory GC.
100 *
101 * @param arg Memory GC
102 * @param color Color
103 *
104 * @return EOK on success or an error code
105 */
106static errno_t mem_gc_set_color(void *arg, gfx_color_t *color)
107{
108 mem_gc_t *mgc = (mem_gc_t *) arg;
109 uint16_t r, g, b;
110 uint8_t attr;
111
112 gfx_color_get_rgb_i16(color, &r, &g, &b);
113 gfx_color_get_ega(color, &attr);
114 mgc->color = PIXEL(attr, r >> 8, g >> 8, b >> 8);
115 return EOK;
116}
117
118/** Fill rectangle on memory GC.
119 *
120 * @param arg Memory GC
121 * @param rect Rectangle
122 *
123 * @return EOK on success or an error code
124 */
125static errno_t mem_gc_fill_rect(void *arg, gfx_rect_t *rect)
126{
127 mem_gc_t *mgc = (mem_gc_t *) arg;
128 gfx_rect_t crect;
129 gfx_coord_t x, y;
130 pixelmap_t pixelmap;
131
132 /* Make sure we have a sorted, clipped rectangle */
133 gfx_rect_clip(rect, &mgc->clip_rect, &crect);
134
135 assert(mgc->rect.p0.x == 0);
136 assert(mgc->rect.p0.y == 0);
137 assert(mgc->alloc.pitch == mgc->rect.p1.x * (int)sizeof(uint32_t));
138 pixelmap.width = mgc->rect.p1.x;
139 pixelmap.height = mgc->rect.p1.y;
140 pixelmap.data = mgc->alloc.pixels;
141
142 for (y = crect.p0.y; y < crect.p1.y; y++) {
143 for (x = crect.p0.x; x < crect.p1.x; x++) {
144 pixelmap_put_pixel(&pixelmap, x, y, mgc->color);
145 }
146 }
147
148 mem_gc_invalidate_rect(mgc, &crect);
149 return EOK;
150}
151
152/** Update memory GC.
153 *
154 * @param arg Memory GC
155 *
156 * @return EOK on success or an error code
157 */
158static errno_t mem_gc_update(void *arg)
159{
160 mem_gc_t *mgc = (mem_gc_t *) arg;
161
162 mgc->cb->update(mgc->cb_arg);
163 return EOK;
164}
165
166/** Create memory GC.
167 *
168 * Create graphics context for rendering into a block of memory.
169 *
170 * @param rect Bounding rectangle
171 * @param alloc Allocation info
172 * @param cb Pointer to memory GC callbacks
173 * @param cb_arg Argument to callback functions
174 * @param rgc Place to store pointer to new memory GC
175 *
176 * @return EOK on success or an error code
177 */
178errno_t mem_gc_create(gfx_rect_t *rect, gfx_bitmap_alloc_t *alloc,
179 mem_gc_cb_t *cb, void *cb_arg, mem_gc_t **rgc)
180{
181 mem_gc_t *mgc = NULL;
182 gfx_context_t *gc = NULL;
183 errno_t rc;
184
185 mgc = calloc(1, sizeof(mem_gc_t));
186 if (mgc == NULL) {
187 rc = ENOMEM;
188 goto error;
189 }
190
191 rc = gfx_context_new(&mem_gc_ops, mgc, &gc);
192 if (rc != EOK)
193 goto error;
194
195 mgc->gc = gc;
196 mgc->rect = *rect;
197 mgc->clip_rect = *rect;
198 mgc->alloc = *alloc;
199
200 mgc->cb = cb;
201 mgc->cb_arg = cb_arg;
202
203 *rgc = mgc;
204 return EOK;
205error:
206 if (mgc != NULL)
207 free(mgc);
208 gfx_context_delete(gc);
209 return rc;
210}
211
212/** Delete memory GC.
213 *
214 * @param mgc Memory GC
215 */
216errno_t mem_gc_delete(mem_gc_t *mgc)
217{
218 errno_t rc;
219
220 rc = gfx_context_delete(mgc->gc);
221 if (rc != EOK)
222 return rc;
223
224 free(mgc);
225 return EOK;
226}
227
228/** Retarget memory GC to a different block of memory.
229 *
230 * @param mgc Memory GC
231 * @param rect New bounding rectangle
232 * @param alloc Allocation info of the new block
233 */
234void mem_gc_retarget(mem_gc_t *mgc, gfx_rect_t *rect,
235 gfx_bitmap_alloc_t *alloc)
236{
237 mgc->rect = *rect;
238 mgc->clip_rect = *rect;
239 mgc->alloc = *alloc;
240}
241
242/** Get generic graphic context from memory GC.
243 *
244 * @param mgc Memory GC
245 * @return Graphic context
246 */
247gfx_context_t *mem_gc_get_ctx(mem_gc_t *mgc)
248{
249 return mgc->gc;
250}
251
252static void mem_gc_invalidate_rect(mem_gc_t *mgc, gfx_rect_t *rect)
253{
254 mgc->cb->invalidate(mgc->cb_arg, rect);
255}
256
257/** Create bitmap in memory GC.
258 *
259 * @param arg Memory GC
260 * @param params Bitmap params
261 * @param alloc Bitmap allocation info or @c NULL
262 * @param rbm Place to store pointer to new bitmap
263 * @return EOK on success or an error code
264 */
265errno_t mem_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
266 gfx_bitmap_alloc_t *alloc, void **rbm)
267{
268 mem_gc_t *mgc = (mem_gc_t *) arg;
269 mem_gc_bitmap_t *mbm = NULL;
270 gfx_coord2_t dim;
271 errno_t rc;
272
273 /* Check that we support all requested flags */
274 if ((params->flags & ~(bmpf_color_key | bmpf_colorize |
275 bmpf_direct_output)) != 0)
276 return ENOTSUP;
277
278 mbm = calloc(1, sizeof(mem_gc_bitmap_t));
279 if (mbm == NULL)
280 return ENOMEM;
281
282 gfx_coord2_subtract(&params->rect.p1, &params->rect.p0, &dim);
283 mbm->rect = params->rect;
284 mbm->flags = params->flags;
285 mbm->key_color = params->key_color;
286
287 if ((params->flags & bmpf_direct_output) != 0) {
288 /* Caller cannot specify allocation for direct output */
289 if (alloc != NULL) {
290 rc = EINVAL;
291 goto error;
292 }
293
294 /* Bounding rectangle must be within GC bounding rectangle */
295 if (!gfx_rect_is_inside(&mbm->rect, &mgc->rect)) {
296 rc = EINVAL;
297 goto error;
298 }
299
300 mbm->alloc = mgc->alloc;
301
302 /* Don't free pixel array when destroying bitmap */
303 mbm->myalloc = false;
304 } else if (alloc == NULL) {
305#if 0
306 /*
307 * TODO: If the bitmap is not required to be sharable,
308 * we could allocate it with a simple malloc.
309 * Need to have a bitmap flag specifying that the
310 * allocation should be sharable. IPC GC could
311 * automatically add this flag
312 */
313 mbm->alloc.pitch = dim.x * sizeof(uint32_t);
314 mbm->alloc.off0 = 0;
315 mbm->alloc.pixels = malloc(mbm->alloc.pitch * dim.y);
316 mbm->myalloc = true;
317
318 if (mbm->alloc.pixels == NULL) {
319 rc = ENOMEM;
320 goto error;
321 }
322#endif
323 mbm->alloc.pitch = dim.x * sizeof(uint32_t);
324 mbm->alloc.off0 = 0;
325 mbm->alloc.pixels = as_area_create(AS_AREA_ANY,
326 dim.x * dim.y * sizeof(uint32_t), AS_AREA_READ |
327 AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
328 mbm->myalloc = true;
329
330 if (mbm->alloc.pixels == AS_MAP_FAILED) {
331 rc = ENOMEM;
332 goto error;
333 }
334 } else {
335 mbm->alloc = *alloc;
336 }
337
338 mbm->mgc = mgc;
339 *rbm = (void *)mbm;
340 return EOK;
341error:
342 if (mbm != NULL)
343 free(mbm);
344 return rc;
345}
346
347/** Destroy bitmap in memory GC.
348 *
349 * @param bm Bitmap
350 * @return EOK on success or an error code
351 */
352static errno_t mem_gc_bitmap_destroy(void *bm)
353{
354 mem_gc_bitmap_t *mbm = (mem_gc_bitmap_t *)bm;
355 if (mbm->myalloc) {
356#if 0
357 /* TODO: if we alloc allocating the bitmap with malloc */
358 free(mbm->alloc.pixels);
359#endif
360 as_area_destroy(mbm->alloc.pixels);
361 }
362
363 free(mbm);
364 return EOK;
365}
366
367/** Render bitmap in memory GC.
368 *
369 * @param bm Bitmap
370 * @param srect0 Source rectangle or @c NULL
371 * @param offs0 Offset or @c NULL
372 * @return EOK on success or an error code
373 */
374static errno_t mem_gc_bitmap_render(void *bm, gfx_rect_t *srect0,
375 gfx_coord2_t *offs0)
376{
377 mem_gc_bitmap_t *mbm = (mem_gc_bitmap_t *)bm;
378 gfx_rect_t srect;
379 gfx_rect_t drect;
380 gfx_rect_t crect;
381 gfx_coord2_t offs;
382 gfx_coord_t x, y;
383 pixelmap_t smap;
384 pixelmap_t dmap;
385 pixel_t pixel;
386
387 if (srect0 != NULL)
388 gfx_rect_clip(srect0, &mbm->rect, &srect);
389 else
390 srect = mbm->rect;
391
392 if (offs0 != NULL) {
393 offs = *offs0;
394 } else {
395 offs.x = 0;
396 offs.y = 0;
397 }
398
399 /* Destination rectangle */
400 gfx_rect_translate(&offs, &srect, &drect);
401
402 /* Clip destination rectangle */
403 gfx_rect_clip(&drect, &mbm->mgc->clip_rect, &crect);
404
405 assert(mbm->alloc.pitch == (mbm->rect.p1.x - mbm->rect.p0.x) *
406 (int)sizeof(uint32_t));
407 smap.width = mbm->rect.p1.x - mbm->rect.p0.x;
408 smap.height = mbm->rect.p1.y - mbm->rect.p0.y;
409 smap.data = mbm->alloc.pixels;
410
411 assert(mbm->mgc->rect.p0.x == 0);
412 assert(mbm->mgc->rect.p0.y == 0);
413 assert(mbm->mgc->alloc.pitch == mbm->mgc->rect.p1.x * (int)sizeof(uint32_t));
414 dmap.width = mbm->mgc->rect.p1.x;
415 dmap.height = mbm->mgc->rect.p1.y;
416 dmap.data = mbm->mgc->alloc.pixels;
417
418 if ((mbm->flags & bmpf_direct_output) != 0) {
419 /* Nothing to do */
420 } else if ((mbm->flags & bmpf_color_key) == 0) {
421 /* Simple copy */
422 for (y = crect.p0.y; y < crect.p1.y; y++) {
423 for (x = crect.p0.x; x < crect.p1.x; x++) {
424 pixel = pixelmap_get_pixel(&smap,
425 x - mbm->rect.p0.x - offs.x,
426 y - mbm->rect.p0.y - offs.y);
427 pixelmap_put_pixel(&dmap, x, y, pixel);
428 }
429 }
430 } else if ((mbm->flags & bmpf_colorize) == 0) {
431 /* Color key */
432 for (y = crect.p0.y; y < crect.p1.y; y++) {
433 for (x = crect.p0.x; x < crect.p1.x; x++) {
434 pixel = pixelmap_get_pixel(&smap,
435 x - mbm->rect.p0.x - offs.x,
436 y - mbm->rect.p0.y - offs.y);
437 if (pixel != mbm->key_color)
438 pixelmap_put_pixel(&dmap, x, y, pixel);
439 }
440 }
441 } else {
442 /* Color key & colorization */
443 for (y = crect.p0.y; y < crect.p1.y; y++) {
444 for (x = crect.p0.x; x < crect.p1.x; x++) {
445 pixel = pixelmap_get_pixel(&smap,
446 x - mbm->rect.p0.x - offs.x,
447 y - mbm->rect.p0.y - offs.y);
448 if (pixel != mbm->key_color)
449 pixelmap_put_pixel(&dmap, x, y,
450 mbm->mgc->color);
451 }
452 }
453 }
454
455 mem_gc_invalidate_rect(mbm->mgc, &crect);
456 return EOK;
457}
458
459/** Get allocation info for bitmap in memory GC.
460 *
461 * @param bm Bitmap
462 * @param alloc Place to store allocation info
463 * @return EOK on success or an error code
464 */
465static errno_t mem_gc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
466{
467 mem_gc_bitmap_t *mbm = (mem_gc_bitmap_t *)bm;
468 *alloc = mbm->alloc;
469 return EOK;
470}
471
472/** Get cursor position on memory GC.
473 *
474 * @param arg Memory GC
475 * @param pos Place to store position
476 *
477 * @return EOK on success or an error code
478 */
479static errno_t mem_gc_cursor_get_pos(void *arg, gfx_coord2_t *pos)
480{
481 mem_gc_t *mgc = (mem_gc_t *) arg;
482
483 if (mgc->cb->cursor_get_pos != NULL)
484 return mgc->cb->cursor_get_pos(mgc->cb_arg, pos);
485 else
486 return ENOTSUP;
487}
488
489/** Set cursor position on memory GC.
490 *
491 * @param arg Memory GC
492 * @param pos New position
493 *
494 * @return EOK on success or an error code
495 */
496static errno_t mem_gc_cursor_set_pos(void *arg, gfx_coord2_t *pos)
497{
498 mem_gc_t *mgc = (mem_gc_t *) arg;
499
500 if (mgc->cb->cursor_set_pos != NULL)
501 return mgc->cb->cursor_set_pos(mgc->cb_arg, pos);
502 else
503 return ENOTSUP;
504}
505
506/** Set cursor visibility on memory GC.
507 *
508 * @param arg Memory GC
509 * @param visible @c true iff cursor should be made visible
510 *
511 * @return EOK on success or an error code
512 */
513static errno_t mem_gc_cursor_set_visible(void *arg, bool visible)
514{
515 mem_gc_t *mgc = (mem_gc_t *) arg;
516
517 if (mgc->cb->cursor_set_visible != NULL)
518 return mgc->cb->cursor_set_visible(mgc->cb_arg, visible);
519 else
520 return ENOTSUP;
521}
522
523/** @}
524 */
Note: See TracBrowser for help on using the repository browser.