Index: uspace/app/gfxdemo/gfxdemo.c
===================================================================
--- uspace/app/gfxdemo/gfxdemo.c	(revision dcc4cb3152f122ace8fc51e1d0531a67547d1b8d)
+++ uspace/app/gfxdemo/gfxdemo.c	(revision e0545de2baba1ee53c20d2582fa17b27b689de4a)
@@ -39,7 +39,9 @@
 #include <fibril.h>
 #include <guigfx/canvas.h>
+#include <gfx/bitmap.h>
 #include <gfx/color.h>
 #include <gfx/render.h>
 #include <io/console.h>
+#include <io/pixelmap.h>
 #include <stdlib.h>
 #include <str.h>
@@ -49,4 +51,6 @@
  *
  * @param gc Graphic context
+ * @param w Width
+ * @param h Height
  */
 static errno_t demo_rects(gfx_context_t *gc, int w, int h)
@@ -54,8 +58,8 @@
 	gfx_color_t *color = NULL;
 	gfx_rect_t rect;
-	int i;
-	errno_t rc;
-
-	while (true) {
+	int i, j;
+	errno_t rc;
+
+	for (j = 0; j < 10; j++) {
 		rc = gfx_color_new_rgb_i16(rand() % 0x10000, rand() % 0x10000,
 		    rand() % 0x10000, &color);
@@ -82,4 +86,91 @@
 		fibril_usleep(500 * 1000);
 	}
+
+	return EOK;
+}
+
+/** Run bitmap demo on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ */
+static errno_t demo_bitmap(gfx_context_t *gc, int w, int h)
+{
+	gfx_bitmap_t *bitmap;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_alloc_t alloc;
+	int i, j;
+	gfx_coord2_t offs;
+	gfx_rect_t srect;
+	errno_t rc;
+	pixelmap_t pixelmap;
+
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p1.x = w;
+	params.rect.p1.y = h;
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	if (rc != EOK)
+		return rc;
+
+	/* Fill bitmap with something. In absence of anything else, use pixelmap */
+	pixelmap.width = w;
+	pixelmap.height = h;
+	pixelmap.data = alloc.pixels;
+
+	for (i = 0; i < w; i++) {
+		for (j = 0; j < h; j++) {
+			pixelmap_put_pixel(&pixelmap, i, j,
+			    PIXEL(255, (i % 30) < 3 ? 255 : 0,
+			    (j % 30) < 3 ? 255 : 0, i / 2));
+		}
+	}
+
+	for (j = 0; j < 10; j++) {
+		for (i = 0; i < 5; i++) {
+			srect.p0.x = rand() % (w - 40);
+			srect.p0.y = rand() % (h - 40);
+			srect.p1.x = srect.p0.x + rand() % (w - srect.p0.x);
+			srect.p1.y = srect.p0.y + rand() % (h - srect.p0.y);
+			offs.x = rand() % (w - srect.p1.x);
+			offs.y = rand() % (h - srect.p1.y);
+			offs.x = 0;
+			offs.y = 0;
+
+			gfx_bitmap_render(bitmap, &srect, &offs);
+			fibril_usleep(500 * 1000);
+		}
+	}
+
+	gfx_bitmap_destroy(bitmap);
+
+	return EOK;
+}
+
+/** Run demo loop on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ */
+static errno_t demo_loop(gfx_context_t *gc, int w, int h)
+{
+	errno_t rc;
+
+	while (true) {
+		rc = demo_rects(gc, w, h);
+		if (rc != EOK)
+			return rc;
+
+		rc = demo_bitmap(gc, w, h);
+		if (rc != EOK)
+			return rc;
+	}
 }
 
@@ -104,5 +195,5 @@
 	gc = console_gc_get_ctx(cgc);
 
-	rc = demo_rects(gc, 80, 25);
+	rc = demo_loop(gc, 80, 25);
 	if (rc != EOK)
 		return rc;
@@ -168,5 +259,5 @@
 	gc = canvas_gc_get_ctx(cgc);
 
-	rc = demo_rects(gc, 400, 300);
+	rc = demo_loop(gc, 400, 300);
 	if (rc != EOK)
 		return rc;
@@ -206,5 +297,5 @@
 	}
 
-	rc = demo_rects(gc, 400, 300);
+	rc = demo_loop(gc, 400, 300);
 	if (rc != EOK)
 		return rc;
Index: uspace/lib/guigfx/private/canvas.h
===================================================================
--- uspace/lib/guigfx/private/canvas.h	(revision dcc4cb3152f122ace8fc51e1d0531a67547d1b8d)
+++ uspace/lib/guigfx/private/canvas.h	(revision e0545de2baba1ee53c20d2582fa17b27b689de4a)
@@ -40,5 +40,7 @@
 #include <canvas.h>
 #include <draw/surface.h>
+#include <gfx/bitmap.h>
 #include <gfx/context.h>
+#include <gfx/coord.h>
 #include <io/pixel.h>
 
@@ -58,4 +60,18 @@
 };
 
+/** Bitmap in canvas GC */
+typedef struct {
+	/** Containing canvas GC */
+	struct canvas_gc *cgc;
+	/** Allocation info */
+	gfx_bitmap_alloc_t alloc;
+	/** @c true if we allocated the bitmap, @c false if allocated by caller */
+	bool myalloc;
+	/** Surface */
+	surface_t *surface;
+	/** Rectangle covered by bitmap */
+	gfx_rect_t rect;
+} canvas_gc_bitmap_t;
+
 #endif
 
Index: uspace/lib/guigfx/src/canvas.c
===================================================================
--- uspace/lib/guigfx/src/canvas.c	(revision dcc4cb3152f122ace8fc51e1d0531a67547d1b8d)
+++ uspace/lib/guigfx/src/canvas.c	(revision e0545de2baba1ee53c20d2582fa17b27b689de4a)
@@ -37,4 +37,6 @@
  */
 
+#include <draw/drawctx.h>
+#include <draw/source.h>
 #include <gfx/color.h>
 #include <gfx/context.h>
@@ -43,4 +45,5 @@
 #include <io/pixel.h>
 #include <stdlib.h>
+#include <transform.h>
 #include "../private/canvas.h"
 //#include "../../private/color.h"
@@ -48,8 +51,17 @@
 static errno_t canvas_gc_set_color(void *, gfx_color_t *);
 static errno_t canvas_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t canvas_gc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t canvas_gc_bitmap_destroy(void *);
+static errno_t canvas_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t canvas_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
 
 gfx_context_ops_t canvas_gc_ops = {
 	.set_color = canvas_gc_set_color,
-	.fill_rect = canvas_gc_fill_rect
+	.fill_rect = canvas_gc_fill_rect,
+	.bitmap_create = canvas_gc_bitmap_create,
+	.bitmap_destroy = canvas_gc_bitmap_destroy,
+	.bitmap_render = canvas_gc_bitmap_render,
+	.bitmap_get_alloc = canvas_gc_bitmap_get_alloc
 };
 
@@ -163,4 +175,142 @@
 }
 
+/** Create bitmap in canvas GC.
+ *
+ * @param arg Canvas GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+errno_t canvas_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	canvas_gc_t *cgc = (canvas_gc_t *) arg;
+	canvas_gc_bitmap_t *cbm = NULL;
+	int w, h;
+	errno_t rc;
+
+	cbm = calloc(1, sizeof(canvas_gc_bitmap_t));
+	if (cbm == NULL)
+		return ENOMEM;
+
+	w = params->rect.p1.x - params->rect.p0.x;
+	h = params->rect.p1.y - params->rect.p0.y;
+	cbm->rect = params->rect;
+
+	if (alloc == NULL) {
+		cbm->surface = surface_create(w, h, NULL, 0);
+		if (cbm->surface == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+
+		cbm->alloc.pitch = w * sizeof(uint32_t);
+		cbm->alloc.off0 = 0;
+		cbm->alloc.pixels = surface_direct_access(cbm->surface);
+		cbm->myalloc = true;
+	} else {
+		cbm->surface = surface_create(w, h, alloc->pixels, 0);
+		if (cbm->surface == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+
+		cbm->alloc = *alloc;
+	}
+
+	cbm->cgc = cgc;
+	*rbm = (void *)cbm;
+	return EOK;
+error:
+	if (cbm != NULL)
+		free(cbm);
+	return rc;
+}
+
+/** Destroy bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_bitmap_destroy(void *bm)
+{
+	canvas_gc_bitmap_t *cbm = (canvas_gc_bitmap_t *)bm;
+	if (cbm->myalloc)
+		surface_destroy(cbm->surface);
+	// XXX if !cbm->myalloc, surface is leaked - no way to destroy it
+	// without destroying the pixel buffer
+	free(cbm);
+	return EOK;
+}
+
+/** Render bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @param srect0 Source rectangle or @c NULL
+ * @param offs0 Offset or @c NULL
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_bitmap_render(void *bm, gfx_rect_t *srect0,
+    gfx_coord2_t *offs0)
+{
+	canvas_gc_bitmap_t *cbm = (canvas_gc_bitmap_t *)bm;
+	gfx_rect_t srect;
+	gfx_rect_t drect;
+	gfx_coord2_t offs;
+
+	if (srect0 != NULL)
+		srect = *srect0;
+	else
+		srect = cbm->rect;
+
+	if (offs0 != NULL) {
+		offs = *offs0;
+	} else {
+		offs.x = 0;
+		offs.y = 0;
+	}
+
+	// XXX Add function to translate rectangle
+	drect.p0.x = srect.p0.x + offs.x;
+	drect.p0.y = srect.p0.y + offs.y;
+	drect.p1.x = srect.p1.x + offs.x;
+	drect.p1.y = srect.p1.y + offs.y;
+
+	transform_t transform;
+	transform_identity(&transform);
+	transform_translate(&transform, offs.x - cbm->rect.p0.x,
+	    offs.y - cbm->rect.p0.y);
+
+	source_t source;
+	source_init(&source);
+	source_set_transform(&source, transform);
+	source_set_texture(&source, cbm->surface,
+	    PIXELMAP_EXTEND_TRANSPARENT_BLACK);
+
+	drawctx_t drawctx;
+	drawctx_init(&drawctx, cbm->cgc->surface);
+
+	drawctx_set_source(&drawctx, &source);
+	drawctx_transfer(&drawctx, drect.p0.x, drect.p0.y,
+	    drect.p1.x - drect.p0.x, drect.p1.y - drect.p0.y);
+
+	update_canvas(cbm->cgc->canvas, cbm->cgc->surface);
+	return EOK;
+}
+
+/** Get allocation info for bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @param alloc Place to store allocation info
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	canvas_gc_bitmap_t *cbm = (canvas_gc_bitmap_t *)bm;
+	*alloc = cbm->alloc;
+	return EOK;
+}
+
 /** @}
  */
