qemacs-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemacs-commit] qemacs display.c display.h fractal.c haiku.cpp ...


From: Charlie Gordon
Subject: [Qemacs-commit] qemacs display.c display.h fractal.c haiku.cpp ...
Date: Mon, 15 May 2017 05:44:22 -0400 (EDT)

CVSROOT:        /sources/qemacs
Module name:    qemacs
Changes by:     Charlie Gordon <chqrlie>        17/05/15 05:44:21

Modified files:
        .              : display.c display.h fractal.c haiku.cpp 
                         html2png.c tty.c win32.c x11.c 

Log message:
        graphics: add device and generic picture display functions
        - add bitmap formats for 4BPP and BGR combinations
        - add color palette in QEPicture
        - add xfactor and yfactor in QEDisplay for preferred pixel granularity
          yfactor is typically 2 for text mode display
        - add dpy_draw_picture() device function
        - add bitmap drawing functions:
            qe_create_picture(int width, int height, QEBitmapFormat format, int 
flags);
            int qe_picture_lock(QEPicture *ip);
            void qe_picture_unlock(QEPicture *ip);
            qe_free_picture(QEPicture **ipp);
            QE_PAL_MODE(r, g, b, incr)
            int qe_picture_set_palette(QEPicture *ip, int mode,
                            unsigned char *p, int count, int tcolor);
            int qe_picture_copy(QEPicture *dst, int dst_x, int dst_y, int 
dst_w, int dst_h,
                            const QEPicture *src, int src_x, int src_y, int 
src_w, int src_h,
                            int flags);
            int qe_draw_picture(QEditScreen *s, int dst_x, int dst_y, int 
dst_w, int dst_h,
                            const QEPicture *ip,
                            int src_x, int src_y, int src_w, int src_h,
                            int flags, QEColor col);
        - add 3 compile time selectable image interfaces in flactal.c benchmark

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/qemacs/display.c?cvsroot=qemacs&r1=1.23&r2=1.24
http://cvs.savannah.gnu.org/viewcvs/qemacs/display.h?cvsroot=qemacs&r1=1.23&r2=1.24
http://cvs.savannah.gnu.org/viewcvs/qemacs/fractal.c?cvsroot=qemacs&r1=1.5&r2=1.6
http://cvs.savannah.gnu.org/viewcvs/qemacs/haiku.cpp?cvsroot=qemacs&r1=1.18&r2=1.19
http://cvs.savannah.gnu.org/viewcvs/qemacs/html2png.c?cvsroot=qemacs&r1=1.20&r2=1.21
http://cvs.savannah.gnu.org/viewcvs/qemacs/tty.c?cvsroot=qemacs&r1=1.86&r2=1.87
http://cvs.savannah.gnu.org/viewcvs/qemacs/win32.c?cvsroot=qemacs&r1=1.19&r2=1.20
http://cvs.savannah.gnu.org/viewcvs/qemacs/x11.c?cvsroot=qemacs&r1=1.54&r2=1.55

Patches:
Index: display.c
===================================================================
RCS file: /sources/qemacs/qemacs/display.c,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -b -r1.23 -r1.24
--- display.c   3 May 2017 12:58:23 -0000       1.23
+++ display.c   15 May 2017 09:44:21 -0000      1.24
@@ -97,7 +97,7 @@
 }
 
 static QEDisplay const dummy_dpy = {
-    "dummy",
+    "dummy", 1, 1,
     NULL, /* dpy_probe */
     dummy_dpy_init,
     dummy_dpy_close,
@@ -120,6 +120,7 @@
     NULL, /* dpy_bmp_draw */
     NULL, /* dpy_bmp_lock */
     NULL, /* dpy_bmp_unlock */
+    NULL, /* dpy_draw_picture */
     NULL, /* dpy_full_screen */
     NULL, /* dpy_describe */
     NULL, /* next */
@@ -422,3 +423,447 @@
     if (h2) fill_rectangle(s->screen, x0 + w1, y0 + h0 - h2, w0 - w1 - w2, h2, 
color);
 }
 
+/*---------------- QEPicture handling functions ----------------*/
+
+int qe_draw_picture(QEditScreen *s, int dst_x, int dst_y, int dst_w, int dst_h,
+                    const QEPicture *ip,
+                    int src_x, int src_y, int src_w, int src_h,
+                    int flags, QEColor col)
+{
+#ifndef CONFIG_TINY
+    int x1 = dst_x;
+    int y1 = dst_y;
+    int x2 = x1 + dst_w;
+    int y2 = y1 + dst_h;
+    int w1, h1;
+
+    /* quick clip rejection */
+    if (x2 <= s->clip_x1 || y2 <= s->clip_y1 ||
+        x1 >= s->clip_x2 || y1 >= s->clip_y2)
+        return 1;
+
+    /* region update */
+    if (x2 > s->clip_x2)
+        x2 = s->clip_x2;
+    if (y2 > s->clip_y2)
+        y2 = s->clip_y2;
+    if (x1 < s->clip_x1)
+        x1 = s->clip_x1;
+    if (y1 < s->clip_y1)
+        y1 = s->clip_y1;
+
+    /* rejection if zero size */
+    w1 = x2 - x1;
+    h1 = y2 - y1;
+    if (w1 < 0 || h1 < 0)
+        return 1;
+
+    if (w1 != dst_w) {
+        int dx = min((x1 - dst_x) * src_w / dst_w, src_w - 1);
+        src_x += dx;
+        src_w = clamp(src_w * w1 / dst_w, 1, src_w - dx);
+    }
+    if (h1 != dst_h) {
+        int dy = min((y1 - dst_y) * src_h / dst_h, src_h - 1);
+        src_y += dy;
+        src_h = clamp(src_h * h1 / dst_h, 1, src_h - dy);
+    }
+
+    if (s->dpy.dpy_draw_picture &&
+        !s->dpy.dpy_draw_picture(s, x1, y1, w1, h1,
+                                 ip, src_x, src_y, src_w, src_h, flags)) {
+        return 0;
+    } else {
+        if (col != COLOR_TRANSPARENT) {
+            /* if image cannot be displayed, just draw a rectangle */
+            fill_rectangle(s, x1, y1, w1, h1, col);
+        }
+        return 2;
+    }
+#else
+    return 1;
+#endif
+}
+
+#ifndef CONFIG_TINY
+static int qe_picture_format_bits(QEBitmapFormat format) {
+    switch (format) {
+    case QEBITMAP_FORMAT_1BIT:
+        return 1;
+    case QEBITMAP_FORMAT_4BIT:
+        return 4;
+    case QEBITMAP_FORMAT_8BIT:
+        return 8;
+    case QEBITMAP_FORMAT_RGB565:
+    case QEBITMAP_FORMAT_RGB555:
+        return 16;
+    case QEBITMAP_FORMAT_RGB24:
+    case QEBITMAP_FORMAT_BGR24:
+        return 24;
+    case QEBITMAP_FORMAT_RGBA32:
+    case QEBITMAP_FORMAT_BGRA32:
+        return 32;
+    case QEBITMAP_FORMAT_YUV420P:
+        /* unsupported for now */
+    default:
+        return 0;
+    }
+}
+
+QEPicture *qe_create_picture(int width, int height,
+                             QEBitmapFormat format, int flags)
+{
+    QEPicture *ip;
+    unsigned int bits;
+
+    bits = qe_picture_format_bits(format);
+    if (bits <= 0)
+        return NULL;
+
+    ip = qe_mallocz(QEPicture);
+    if (ip) {
+        /* align pixmap lines on 64 bit boundaries */
+        unsigned int wb = width * bits + 63 / 64 * 8;
+        ip->width = width;
+        ip->height = height;
+        ip->format = format;
+        ip->data[0] = qe_malloc_array(unsigned char, wb * height);
+        ip->linesize[0] = wb;
+    }
+    return ip;
+}
+
+void qe_free_picture(QEPicture **ipp) {
+    if (*ipp) {
+        qe_free(&(*ipp)->data[0]);
+        qe_free(ipp);
+    }
+}
+
+int qe_picture_set_palette(QEPicture *ip, int mode,
+                           unsigned char *p, int count, int tcolor)
+{
+    int i, r, g, b, incr;
+
+    if (!ip)
+        return 1;
+
+    if (mode == 0)
+        mode = QE_PAL_QECOLOR;
+
+    incr = mode & 15;
+    b = (mode >>  4) & 15;
+    g = (mode >>  8) & 15;
+    r = (mode >> 12) & 15;
+
+    ip->tcolor = tcolor;
+    if (ip->palette_size != 256) {
+        qe_free(&ip->palette);
+        ip->palette_size = 256;
+        ip->palette = qe_malloc_array(QEColor, 256);
+        if (ip->palette == NULL) {
+            ip->palette_size = 0;
+            return -1;
+        }
+        /* Default colors to standard palette */
+        memcpy(ip->palette, xterm_colors, 256 * sizeof(*ip->palette));
+    }
+    count = min(count, 256);
+    for (i = 0; i < count; i++) {
+        ip->palette[i] = QERGB(p[r], p[g], p[b]);
+        p += incr;
+    }
+    return 0;
+}
+
+static int qe_picture_scale(QEPicture *to, int dst_x, int dst_y, int dst_w, 
int dst_h,
+                            const QEPicture *from,
+                            int src_x, int src_y, int src_w, int src_h, int 
flags)
+{
+    if (from->format != to->format)
+        return 1;
+
+    if (from->format != QEBITMAP_FORMAT_RGBA32
+    &&  from->format != QEBITMAP_FORMAT_BGRA32)
+        return 1;
+    
+#if 0
+    /* Bilinear interpolation */
+    uint32_t w = src_w, h = src_h, w2 = dst_w, h2 = dst_h;
+    uint32_t x_ratio = ((w - 1) << 16) / w2;
+    uint32_t y_ratio = ((h - 1) << 16) / h2;
+    uint32_t pitch = from->linesize[0];
+    uint32_t pitch2 = to->linesize[0];
+    uint32_t y = 0;
+    for (uint32_t i = 0; i < h2; i++, y += y_ratio) {
+        uint32_t yr = y >> 16;
+        uint32_t y_diff = y & 0xFFFF;
+        const uint8_t *src = from->data[0] + (src_y + yr) * pitch + src_x * 4;
+        uint8_t *dst = to->data[0] + (dst_y + i) * pitch2 + dst_x * 4;
+        uint32_t x = 0;
+        for (uint32_t j = 0; j < w2; j++, x += x_ratio) {
+            uint32_t xr = x >> 16;
+            uint32_t x_diff = x & 0xFFFF;
+            uint32_t m0 = (0x10000 - x_diff) * (0x10000 - y_diff);
+            uint32_t m1 = x_diff * (0x10000 - y_diff);
+            uint32_t m2 = (0x10000 - x_diff) * y_diff;
+            uint32_t m3 = x_diff * y_diff;
+
+            for (int k = 0; k < 3; k++) {
+                uint32_t v = (src[xr * 4 + k + 0] * (int64_t)m0 +
+                              src[xr * 4 + k + 4] * (int64_t)m1 +
+                              src[xr * 4 + k + pitch + 0] * (int64_t)m2 +
+                              src[xr * 4 + k + pitch + 4] * (int64_t)m3) >> 32;
+                dst[j * 4 + k] = (uint8_t)v;
+            }
+        }
+    }
+#else
+    int x, y, sx, sy, sx0, sy0, dx, dy, width = dst_w, height = dst_h;
+
+    /* Nearest-neighbor interpolation */
+    dx = dy = sx0 = sy0 = 0;
+    if (dst_w > 1) {
+        if (dst_w > src_w) {
+            dx = src_w * 0x10000 / dst_w;
+        } else {
+            dx = (src_w - 1) * 0x10000 / (dst_w - 1);
+            sx0 = 0x8000;
+        }
+    }
+    if (dst_h > 1) {
+        if (dst_h > src_h) {
+            dy = src_h * 0x10000 / dst_h;
+        } else {
+            dy = (src_h - 1) * 0x10000 / (dst_h - 1);
+            sy0 = 0x8000;
+        }
+    }
+    for (y = 0, sy = sy0; y < height; y++, sy += dy) {
+        const uint32_t *src = (const uint32_t *)(void *)(from->data[0] +
+                                                         (src_y + (sy >> 16)) 
* from->linesize[0]) + src_x;
+        uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+
+        for (x = 0, sx = sx0; x < width; x++, sx += dx) {
+            /* XXX: No filtering */
+            dst[x + 0] = src[sx >> 16];
+        }
+    }
+#endif
+    return 0;
+}
+
+int qe_picture_copy(QEPicture *to, int dst_x, int dst_y, int dst_w, int dst_h,
+                    const QEPicture *from,
+                    int src_x, int src_y, int src_w, int src_h, int flags)
+{
+    const uint32_t *palette;
+    int x, y, width = src_w, height = src_h;
+    int res = 0;
+
+    if (src_w != dst_w || src_h != dst_h) {
+        /* Generic scaling */
+        QEPicture *ip1 = NULL;
+
+        if (from->format != QEBITMAP_FORMAT_RGBA32) {
+            ip1 = qe_create_picture(src_w, src_h, QEBITMAP_FORMAT_RGBA32, 0);
+            if (!ip1)
+                return -1;
+            res = qe_picture_copy(ip1, 0, 0, src_w, src_h,
+                                  from, src_x, src_y, src_w, src_h, 0);
+            from = ip1;
+            src_x = src_y = 0;
+        }
+        if (!res) {
+            res = qe_picture_scale(to, dst_x, dst_y, dst_w, dst_h,
+                                   from, src_x, src_y, src_w, src_h, flags);
+        }
+        qe_free_picture(&ip1);
+        return res;
+    }
+
+    if (from->format == QEBITMAP_FORMAT_8BIT && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        palette = from->palette;
+        if (palette == NULL) {
+            palette = xterm_colors;
+        }
+        for (y = 0; y < height; y++) {
+            const unsigned char *src = from->data[0] + (src_y + y) * 
from->linesize[0] + src_x;
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            int w4 = width & ~3;
+
+            for (x = 0; x < w4; x += 4) {
+                dst[x + 0] = palette[src[x + 0]];
+                dst[x + 1] = palette[src[x + 1]];
+                dst[x + 2] = palette[src[x + 2]];
+                dst[x + 3] = palette[src[x + 3]];
+            }
+            for (; x < width; x++) {
+                dst[x] = palette[src[x]];
+            }
+        }
+        return 0;
+    }
+    if (from->format == QEBITMAP_FORMAT_4BIT && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        palette = from->palette;
+        if (palette == NULL) {
+            palette = xterm_colors;
+        }
+        for (y = 0; y < height; y++) {
+            const unsigned char *src = from->data[0] + (src_y + y) * 
from->linesize[0] + (src_x >> 1);
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            int shift, w4;
+
+            x = 0;
+            if (src_x & 1) {
+                /* Convert incomplete left block */
+                dst[x] = palette[*src++ & 15];
+                x++;
+            }
+            /* Convert middle block */
+            w4 = x + ((width - x) & ~3);
+            for (x = 0; x < w4; x += 4, src += 2) {
+                dst[x + 0] = palette[src[0] >> 4];
+                dst[x + 1] = palette[src[0] & 15];
+                dst[x + 2] = palette[src[1] >> 4];
+                dst[x + 3] = palette[src[1] & 15];
+            }
+            /* Convert right block */
+            shift = 4;
+            for (; x < width; x++) {
+                dst[x] = palette[(src[0] >> shift) & 15];
+                src += (shift ^= 4) >> 2;
+            }
+        }
+        return 0;
+    }
+    if (from->format == QEBITMAP_FORMAT_1BIT && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        QEColor bw[2] = { QERGB(0, 0, 0), QERGB(0xff, 0xff, 0xff) };
+
+        palette = from->palette;
+        if (palette == NULL) {
+            palette = bw;
+        }
+        for (y = 0; y < height; y++) {
+            const unsigned char *src = from->data[0] + (src_y + y) * 
from->linesize[0] + (src_x >> 3);
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            int bits, shift, w8;
+
+            x = 0;
+            if (src_x & 7) {
+                /* Convert incomplete left block */
+                bits = *src++;
+                shift = 8 - (src_x & 7);
+                for (; x < width && shift != 0; x++) {
+                    dst[x] = palette[(bits >> --shift) & 1];
+                }
+            }
+            /* Convert middle block */
+            w8 = x + ((width - x) & ~7);
+            for (; x < w8; x += 8) {
+                bits = *src++;
+                dst[x + 0] = palette[(bits >> 7) & 1];
+                dst[x + 1] = palette[(bits >> 6) & 1];
+                dst[x + 2] = palette[(bits >> 5) & 1];
+                dst[x + 3] = palette[(bits >> 4) & 1];
+                dst[x + 4] = palette[(bits >> 3) & 1];
+                dst[x + 5] = palette[(bits >> 2) & 1];
+                dst[x + 6] = palette[(bits >> 1) & 1];
+                dst[x + 7] = palette[(bits >> 0) & 1];
+            }
+            if (x < width) {
+                /* Convert incomplete right block */
+                bits = *src;
+                shift = 8;
+                for (; x < width; x++) {
+                    dst[x] = palette[(bits >> --shift) & 1];
+                }
+            }
+        }
+        return 0;
+    }
+    if (from->format == QEBITMAP_FORMAT_RGB565 && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        /* XXX: deal with endianness */
+        for (y = 0; y < height; y++) {
+            const uint16_t *src = (const uint16_t *)(void *)(from->data[0] + 
(src_y + y) * from->linesize[0]) + src_x;
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            for (x = 0; x < width; x++, src++) {
+                unsigned int r = (src[0] >> 8) & 0xF8;
+                unsigned int g = (src[0] >> 3) & 0xFC;
+                unsigned int b = (src[0] << 3) & 0xF8;
+                dst[x] = QERGB(r | (r >> 5), g | (g >> 6), b | (b >> 5));
+            }
+        }
+        return 0;
+    }
+    if (from->format == QEBITMAP_FORMAT_RGB555 && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        /* XXX: deal with endianness */
+        for (y = 0; y < height; y++) {
+            const uint16_t *src = (const uint16_t *)(void *)(from->data[0] + 
(src_y + y) * from->linesize[0]) + src_x;
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            for (x = 0; x < width; x++, src++) {
+                unsigned int r = (src[0] >> 7) & 0xF8;
+                unsigned int g = (src[0] >> 2) & 0xF8;
+                unsigned int b = (src[0] << 3) & 0xF8;
+                dst[x] = QERGB(r | (r >> 5), g | (g >> 5), b | (b >> 5));
+            }
+        }
+        return 0;
+    }
+    if (from->format == QEBITMAP_FORMAT_RGB24 && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        for (y = 0; y < height; y++) {
+            const unsigned char *src = from->data[0] + (src_y + y) * 
from->linesize[0] + src_x * 3;
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            int w4 = width & ~3;
+
+            for (x = 0; x < w4; x += 4, src += 12) {
+                dst[x + 0] = QERGB(src[0], src[1], src[2]);
+                dst[x + 1] = QERGB(src[3], src[4], src[5]);
+                dst[x + 2] = QERGB(src[6], src[7], src[8]);
+                dst[x + 3] = QERGB(src[9], src[10], src[11]);
+            }
+            for (; x < width; x++, src += 3) {
+                dst[x] = QERGB(src[0], src[1], src[2]);
+            }
+        }
+        return 0;
+    }
+    if (from->format == QEBITMAP_FORMAT_BGR24 && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        for (y = 0; y < height; y++) {
+            const unsigned char *src = from->data[0] + (src_y + y) * 
from->linesize[0] + src_x * 3;
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            int w4 = width & ~3;
+
+            for (x = 0; x < w4; x += 4, src += 12) {
+                dst[x + 0] = QERGB(src[2], src[1], src[0]);
+                dst[x + 1] = QERGB(src[5], src[4], src[3]);
+                dst[x + 2] = QERGB(src[8], src[7], src[6]);
+                dst[x + 3] = QERGB(src[11], src[10], src[9]);
+            }
+            for (; x < width; x++, src += 3) {
+                dst[x] = QERGB(src[2], src[1], src[0]);
+            }
+        }
+        return 0;
+    }
+    if (from->format == QEBITMAP_FORMAT_BGRA32 && to->format == 
QEBITMAP_FORMAT_RGBA32) {
+        for (y = 0; y < height; y++) {
+            const unsigned char *src = from->data[0] + (src_y + y) * 
from->linesize[0] + src_x * 4;
+            uint32_t *dst = (uint32_t *)(void *)(to->data[0] + (dst_y + y) * 
to->linesize[0]) + dst_x;
+            int w4 = width & ~3;
+
+            for (x = 0; x < w4; x += 4, src += 16) {
+                dst[x + 0] = QERGB(src[0], src[1], src[2]);
+                dst[x + 1] = QERGB(src[4], src[5], src[6]);
+                dst[x + 2] = QERGB(src[8], src[9], src[10]);
+                dst[x + 3] = QERGB(src[12], src[13], src[14]);
+            }
+            for (; x < width; x++, src += 4) {
+                dst[x] = QERGB(src[0], src[1], src[2]);
+            }
+        }
+        return 0;
+    }
+    return 1;
+}
+#endif  /* !CONFIG_TINY */

Index: display.h
===================================================================
RCS file: /sources/qemacs/qemacs/display.h,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -b -r1.23 -r1.24
--- display.h   5 May 2017 20:07:52 -0000       1.23
+++ display.h   15 May 2017 09:44:21 -0000      1.24
@@ -64,11 +64,14 @@
 
 typedef enum QEBitmapFormat {
     QEBITMAP_FORMAT_1BIT = 0,
+    QEBITMAP_FORMAT_4BIT,
     QEBITMAP_FORMAT_8BIT,
     QEBITMAP_FORMAT_RGB565,
     QEBITMAP_FORMAT_RGB555,
     QEBITMAP_FORMAT_RGB24,
+    QEBITMAP_FORMAT_BGR24,
     QEBITMAP_FORMAT_RGBA32,
+    QEBITMAP_FORMAT_BGRA32,
     QEBITMAP_FORMAT_YUV420P,
 } QEBitmapFormat;
 
@@ -93,6 +96,9 @@
     QEBitmapFormat format;
     unsigned char *data[4];
     int linesize[4];
+    QEColor *palette;
+    int palette_size;
+    int tcolor;
 } QEPicture;
 
 typedef struct QEditScreen QEditScreen;
@@ -100,6 +106,7 @@
 
 struct QEDisplay {
     const char *name;
+    int xfactor, yfactor;
     int (*dpy_probe)(void);
     int (*dpy_init)(QEditScreen *s, int w, int h);
     void (*dpy_close)(QEditScreen *s);
@@ -135,6 +142,11 @@
     void (*dpy_bmp_lock)(QEditScreen *s, QEBitmap *bitmap, QEPicture *pict,
                          int x1, int y1, int w1, int h1);
     void (*dpy_bmp_unlock)(QEditScreen *s, QEBitmap *b);
+    int (*dpy_draw_picture)(QEditScreen *s,
+                            int dst_x, int dst_y, int dst_w, int dst_h,
+                            const QEPicture *ip,
+                            int src_x, int src_y, int src_w, int src_h,
+                            int flags);
     void (*dpy_full_screen)(QEditScreen *s, int full_screen);
     void (*dpy_describe)(QEditScreen *s, EditBuffer *b);
     QEDisplay *next;
@@ -285,4 +297,27 @@
         font->refcount--;
 }
 
+QEPicture *qe_create_picture(int width, int height,
+                             QEBitmapFormat format, int flags);
+static inline int qe_picture_lock(QEPicture *ip) { return ip == NULL; }
+static inline void qe_picture_unlock(QEPicture *ip) {}
+void qe_free_picture(QEPicture **ipp);
+
+#define QE_PAL_MODE(r, g, b, incr)  (((r) << 12) | ((g) << 8) | ((b) << 4) | 
(incr))
+#define QE_PAL_RGB3     QE_PAL_MODE(0, 1, 2, 3)
+#define QE_PAL_RGB4     QE_PAL_MODE(0, 1, 2, 4)
+#define QE_PAL_BGR3     QE_PAL_MODE(2, 1, 0, 3)
+#define QE_PAL_BGR4     QE_PAL_MODE(2, 1, 0, 4)
+#define QE_PAL_QECOLOR  QE_PAL_MODE(2, 1, 0, 4)   /* XXX: depends on 
endianness */
+int qe_picture_set_palette(QEPicture *ip, int mode,
+                           unsigned char *p, int count, int tcolor);
+
+int qe_picture_copy(QEPicture *dst, int dst_x, int dst_y, int dst_w, int dst_h,
+                    const QEPicture *src, int src_x, int src_y, int src_w, int 
src_h,
+                    int flags);
+
+int qe_draw_picture(QEditScreen *s, int dst_x, int dst_y, int dst_w, int dst_h,
+                    const QEPicture *ip,
+                    int src_x, int src_y, int src_w, int src_h,
+                    int flags, QEColor col);
 #endif

Index: fractal.c
===================================================================
RCS file: /sources/qemacs/qemacs/fractal.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -b -r1.5 -r1.6
--- fractal.c   8 May 2017 10:47:23 -0000       1.5
+++ fractal.c   15 May 2017 09:44:21 -0000      1.6
@@ -309,7 +309,8 @@
 
 /*---------------- Interactive fractal explorer ----------------*/
 
-#define USE_BITMAP_API  1   /* Using device bitmap API */
+#define USE_BITMAP_API    0   /* Using device bitmap API */
+#define USE_DRAW_PICTURE  1   /* Using qe_draw_picture() */
 
 static ModeDef fractal_mode;
 
@@ -328,7 +329,7 @@
 struct FractalState {
     QEModeData base;
 
-    int cols, rows;     /* fractal size in pixels */
+    int width, height;  /* fractal size in pixels */
     int type;           /* fractal type 0..8 */
     int maxiter;        /* maximum iteration number */
     int cb, nc;         /* color palette base and length */
@@ -338,11 +339,13 @@
     fnum_t bailout;     /* maximum squared module (default 4.0) */
     fnum_t x, y;        /* center position */
     fnum_t m0, m1, m2, m3; /* rotation matrix */
-    int hfactor;        /* vertical pixel granularity */
     int shift;          /* color animation base */
     QEColor colors[256]; /* color palette */
     QEditScreen *screen;    /* for bmp_free() */
     QEBitmap *disp_bmp;     /* device image */
+#if USE_DRAW_PICTURE
+    QEPicture *ip;
+#endif
 };
 
 const char fractal_default_parameters[] = {
@@ -508,7 +511,7 @@
     /* This will force fractal image recomputation */
     /* XXX: color changes should not cause recomputation
        if the fractal is computed as a paletted image */
-    ms->cols = ms->rows = 0;
+    ms->width = ms->height = 0;
 }
 
 static void fractal_set_rotation(FractalState *ms, int rot) {
@@ -566,11 +569,11 @@
     if (p) {
         if (strmatchword(p, "gray256", pp)) {
             int c;
-            for (c = 0; c < 256; c++) {
-                ms->colors[c] = QERGB(c, c, c);
+            for (c = 1; c < 256; c++) {
+                ms->colors[256 - c] = QERGB(c, c, c);
             }
-            ms->cb = 0;
-            ms->nc = 256;
+            ms->cb = 1;
+            ms->nc = 255;
         } else
         if (strmatchword(p, "gray", pp)) {
             ms->cb = 232;
@@ -619,7 +622,7 @@
 {
     const char *p;
 
-    ms->cols = ms->rows = 0;    /* force refresh */
+    ms->width = ms->height = 0;    /* force redraw */
 
     for (p = parms;;) {
         p += strspn(p, ";, \t\r\n");
@@ -670,7 +673,7 @@
 static void do_fractal_draw(EditState *s, FractalState *ms)
 {
 #if USE_BITMAP_API
-    int cols = ms->cols, rows = ms->rows;
+    int width = ms->width, height = ms->height;
     int maxiter = ms->maxiter + ms->zoom, nc = ms->nc;
     int i, nx, ny, shift;
     fnum_t x, y, dx, dy;
@@ -680,33 +683,29 @@
     uint32_t *palette32 = NULL;
     QEPicture pict;
 
-    if (s->height == 0 || s->width == 0 || rows == 0 || cols == 0 || nc == 0)
+    if (s->width == 0 || s->height == 0 || width == 0 || height == 0 || nc == 
0)
         return;
 
     if (s->width == s->cols) {
-        /* character based, assume 80x25 4/3 aspect ratio */
-        /* XXX: get subsampling and aspect ratio from window or screen */
-        ms->hfactor = 2;
-        rows *= ms->hfactor;
-        dx = 3.2 * ms->scale / cols;
+        /* character based, assume 80x25 4/3 aspect ratio, 2 pixels per char */
+        dx = 3.2 * ms->scale / width;
         dy = dx * 1.2;
     } else {
         /* pixel based, assume 100% pixel aspect ratio */
-        ms->hfactor = 1;
-        cols = s->width;
-        rows = s->height;
-        dy = dx = 3.2 * ms->scale / cols;
+        dy = dx = 3.2 * ms->scale / width;
     }
-    if (ms->disp_bmp == NULL || ms->disp_bmp->width != cols || 
ms->disp_bmp->height != rows) {
+    if (ms->disp_bmp == NULL
+    ||  ms->disp_bmp->width != width
+    ||  ms->disp_bmp->height != height) {
         bmp_free(ms->screen, &ms->disp_bmp);
         /* create the displayed bitmap and put the image in it */
         ms->screen = s->screen;
-        ms->disp_bmp = bmp_alloc(ms->screen, cols, rows, 0);
+        ms->disp_bmp = bmp_alloc(ms->screen, width, height, 0);
     }
     if (!ms->disp_bmp)
         return;
 
-    bmp_lock(ms->screen, ms->disp_bmp, &pict, 0, 0, cols, rows);
+    bmp_lock(ms->screen, ms->disp_bmp, &pict, 0, 0, width, height);
 
     /* Compute shifted palette */
     shift = nc + ms->shift % nc; /* 0 < shift < 2 * nc */
@@ -724,10 +723,10 @@
     }
 
     /* Compute fractal bitmap */
-    for (ny = 0, y = -dy * rows / 2; ny < rows; ny++, y += dy) {
+    for (ny = 0, y = -dy * height / 2; ny < height; ny++, y += dy) {
         if (pict.format == QEBITMAP_FORMAT_8BIT) {
             unsigned char *pb = pict.data[0] + ny * pict.linesize[0];
-            for (nx = 0, x = -dx * cols / 2; nx < cols; nx++, x += dx) {
+            for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
                 fnum_t xr = ms->x + x * ms->m0 + y * ms->m1;
                 fnum_t yr = ms->y + x * ms->m2 + y * ms->m3;
                 pb[nx] = palette8[(*func)(xr, yr, ms->bailout, maxiter)];
@@ -735,7 +734,7 @@
         } else
         if (pict.format == QEBITMAP_FORMAT_RGBA32) {
             uint32_t *pb = (uint32_t *)(void*)(pict.data[0] + ny * 
pict.linesize[0]);
-            for (nx = 0, x = -dx * cols / 2; nx < cols; nx++, x += dx) {
+            for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
                 fnum_t xr = ms->x + x * ms->m0 + y * ms->m1;
                 fnum_t yr = ms->y + x * ms->m2 + y * ms->m3;
                 pb[nx] = palette32[(*func)(xr, yr, ms->bailout, maxiter)];
@@ -746,8 +745,46 @@
     qe_free(&palette8);
     qe_free(&palette32);
     edit_invalidate(s, 1);
+#elif USE_DRAW_PICTURE
+    int width = ms->width, height = ms->height, zoom = ms->zoom;
+    int maxiter = ms->maxiter + zoom, cb = ms->cb, nc = ms->nc;
+    int i, nx, ny;
+    fnum_t xc = ms->x, yc = ms->y, scale = ms->scale;
+    fnum_t bailout = ms->bailout;
+    fnum_t x, y, dx, dy, xr, yr;
+    int (*func)(fnum_t x, fnum_t y, fnum_t bailout, int maxiter) =
+        fractal_type[ms->type].func;
+
+    if (s->width == 0 || s->height == 0 || width == 0 || height == 0 || nc == 
0)
+        return;
+
+    if (s->width == s->cols) {
+        /* character based, assume 80x25 4/3 aspect ratio, 2 pixels per char */
+        dx = 3.2 * scale / width;
+        dy = dx * 1.2;
+    } else {
+        /* pixel based, assume 100% pixel aspect ratio */
+        dy = dx = 3.2 * scale / width;
+    }
+    if (ms->ip == NULL || ms->ip->width != width || ms->ip->height != height) {
+        qe_free_picture(&ms->ip);
+        ms->ip = qe_create_picture(width, height, QEBITMAP_FORMAT_8BIT, 0);
+    }
+    if (!ms->ip)
+        return;
+
+    for (ny = 0, y = -dy * height / 2; ny < height; ny++, y += dy) {
+        unsigned char *pb = ms->ip->data[0] + ny * ms->ip->linesize[0];
+        for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
+            xr = xc + x * ms->m0 + y * ms->m1;
+            yr = yc + x * ms->m2 + y * ms->m3;
+            i = (*func)(xr, yr, bailout, maxiter);
+            pb[nx] = (i >= maxiter) ? 0 : cb + i % nc;
+        }
+    }
+    edit_invalidate(s, 1);
 #else
-    int cols = ms->cols, rows = ms->rows;
+    int width = ms->width, height = ms->height / 2;
     int maxiter = ms->maxiter + ms->zoom, nc = ms->nc;
     int i, nx, ny, fg, bg, shift;
     fnum_t x, y, dx, dy;
@@ -755,17 +792,16 @@
         fractal_type[ms->type].func;
     unsigned char *palette8 = NULL;
 
-    if (s->height == 0 || s->width == 0 || rows == 0 || cols == 0 || nc == 0)
+    if (s->width == 0 || s->height == 0 || width == 0 || height == 0 || nc == 
0)
         return;
 
-    ms->hfactor = 2;
-    dx = 3.2 * ms->scale / cols;
+    dx = 3.2 * ms->scale / width;
     if (s->width == s->cols) {
-        /* character based, assume 80x25 4/3 aspect ratio */
+        /* character based, assume 80x25 4/3 aspect ratio, 2 pixels per char */
         dy = dx * 2.4;
     } else {
         /* pixel based, assume 100% pixel aspect ratio */
-        dy = dx * cols / s->width * s->height / rows;
+        dy = dx * width / s->width * s->height / height;
     }
 
     /* Compute shifted palette */
@@ -779,8 +815,8 @@
 
     eb_delete_range(s->b, 0, s->b->total_size);
 
-    for (ny = 0, y = -dy * rows / 2; ny < rows; ny++, y += dy) {
-        for (nx = 0, x = -dx * cols / 2; nx < cols; nx++, x += dx) {
+    for (ny = 0, y = -dy * height / 2; ny < height; ny++, y += dy) {
+        for (nx = 0, x = -dx * width / 2; nx < width; nx++, x += dx) {
             fnum_t xr = ms->x + x * ms->m0 + y * ms->m1;
             fnum_t yr = ms->y + x * ms->m2 + y * ms->m3;
             bg = palette8[(*func)(xr, yr, ms->bailout, maxiter)];
@@ -801,12 +837,12 @@
 #if USE_BITMAP_API
 static void fractal_display(EditState *s) {
     FractalState *ms = fractal_get_state(s, 0);
-    int col = QERGB(0, 0, 0);
+    QEColor col = qe_styles[QE_STYLE_GUTTER].bg_color;
 
     if (s->display_invalid) {
-        if (ms && ms->disp_bmp && ms->hfactor) {
+        if (ms && ms->disp_bmp) {
             int w = min(s->width, ms->disp_bmp->width);
-            int h = min(s->height, ms->disp_bmp->height / ms->hfactor);
+            int h = min(s->height, ms->disp_bmp->height / 
s->screen->dpy.yfactor);
             int x = (s->width - w) / 2;
             int y = (s->height - h) / 2;
 
@@ -834,6 +870,52 @@
 }
 #endif
 
+#if USE_DRAW_PICTURE
+static void fractal_display(EditState *s) {
+    FractalState *ms = fractal_get_state(s, 0);
+    QEColor col = qe_styles[QE_STYLE_GUTTER].bg_color;
+
+    if (s->display_invalid) {
+        if (ms && ms->ip) {
+            int w = min(s->width, ms->ip->width);
+            int h = min(s->height, ms->ip->height / s->screen->dpy.yfactor);
+            int x0 = (s->width - w) / 2;
+            int y0 = (s->height - h) / 2;
+            uint32_t palette[256];
+            int c;
+
+            palette[0] = ms->colors[0];
+            for (c = 1; c < 256; c++) {
+                palette[c] = ms->colors[(c + ms->shift) & 255];
+            }
+            ms->ip->palette = palette;
+            ms->ip->palette_size = 256;
+            qe_draw_picture(s->screen, s->xleft + x0, s->ytop + y0, w, h,
+                            ms->ip, 0, 0, w, h * s->screen->dpy.yfactor,
+                            0, QERGB(128, 128, 128));
+            ms->ip->palette = NULL;
+            fill_border(s, x0, y0, w, h, col);
+        } else {
+            fill_rectangle(s->screen, s->xleft, s->ytop, s->width, s->height, 
col);
+        }
+        s->display_invalid = 0;
+    }
+    if (s->qe_state->active_window == s) {
+        /* Update cursor */
+        int xc = s->xleft;
+        int yc = s->ytop;
+        int w = s->char_width;
+        int h = s->line_height;
+        if (s->screen->dpy.dpy_cursor_at) {
+            /* hardware cursor */
+            s->screen->dpy.dpy_cursor_at(s->screen, xc, yc, w, h);
+        } else {
+            xor_rectangle(s->screen, xc, yc, w, h, QERGB(0xFF, 0xFF, 0xFF));
+        }
+    }
+}
+#endif
+
 static void do_fractal_move(EditState *s, int deltax, int deltay) {
     FractalState *ms = fractal_get_state(s, 1);
     if (ms) {
@@ -871,7 +953,11 @@
     FractalState *ms = fractal_get_state(s, 1);
     if (ms) {
         ms->shift += delta;
+#if USE_BITMAP_API
         fractal_invalidate(ms);
+#else
+        edit_invalidate(s, 1);
+#endif
     }
 }
 
@@ -885,7 +971,11 @@
         if (type == 1) {
             fractal_set_colors(ms, "gray", NULL);
         }
+#if USE_BITMAP_API
         fractal_invalidate(ms);
+#else
+        edit_invalidate(s, 1);
+#endif
     }
 }
 
@@ -942,7 +1032,7 @@
     eb_printf(b, "%*s: %s\n", w, "formula", fractal_type[ms->type].formula);
     eb_printf(b, "%*s: "MFT"\n", w, "x", ms->x);
     eb_printf(b, "%*s: "MFT"\n", w, "y", ms->y);
-    eb_printf(b, "%*s: %dx%d\n", w, "size", ms->cols, ms->rows * ms->hfactor);
+    eb_printf(b, "%*s: %dx%d\n", w, "size", ms->width, ms->height);
     eb_printf(b, "%*s: %d\n", w, "zoom", ms->zoom);
     eb_printf(b, "%*s: %.6g\n", w, "scale", (double)ms->scale);
     eb_printf(b, "%*s: %d\n", w, "rot", ms->rot);
@@ -975,12 +1065,19 @@
 static void fractal_display_hook(EditState *s) {
     FractalState *ms = fractal_get_state(s, 0);
     if (ms) {
+#if USE_BITMAP_API || USE_DRAW_PICTURE
+        int width = s->width;
+        int height = s->height * s->screen->dpy.yfactor;
+#else
+        int width = s->cols;
+        int height = s->rows * 2;
+#endif
         if (s->xleft == 0 && s->ytop == 0
-        &&  (s->rows != ms->rows || s->cols != ms->cols)) {
+        &&  (ms->height != height || ms->width != width)) {
             /* XXX: should use a separate thread for this */
             /* XXX: should use a different bitmap for each window */
-            ms->cols = s->cols;
-            ms->rows = s->rows;
+            ms->width = width;
+            ms->height = height;
             do_fractal_draw(s, ms);
         }
     }
@@ -1059,6 +1156,12 @@
 
     bmp_free(ms->screen, &ms->disp_bmp);
 #endif
+#if USE_DRAW_PICTURE
+    FractalState *ms = state;
+
+    /* free bitmap and palette */
+    qe_free_picture(&ms->ip);
+#endif
 }
 
 static void do_mandelbrot_test(EditState *s) {
@@ -1075,7 +1178,7 @@
         fractal_mode.mode_free = fractal_mode_free;
         fractal_mode.display_hook = fractal_display_hook;
         fractal_mode.default_wrap = WRAP_TRUNCATE;
-#if USE_BITMAP_API
+#if USE_BITMAP_API || USE_DRAW_PICTURE
         fractal_mode.display = fractal_display;
 #endif
         qe_register_mode(&fractal_mode, MODEF_NOCMD | MODEF_VIEW);

Index: haiku.cpp
===================================================================
RCS file: /sources/qemacs/qemacs/haiku.cpp,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -b -r1.18 -r1.19
--- haiku.cpp   18 Apr 2017 09:14:55 -0000      1.18
+++ haiku.cpp   15 May 2017 09:44:21 -0000      1.19
@@ -864,7 +864,7 @@
 }
 
 static QEDisplay haiku_dpy = {
-    "haiku",
+    "haiku", 1, 1,
     haiku_probe,
     haiku_init,
     haiku_close,
@@ -886,6 +886,7 @@
     NULL, /* dpy_bmp_draw */
     NULL, /* dpy_bmp_lock */
     NULL, /* dpy_bmp_unlock */
+    NULL, /* dpy_draw_picture */
     haiku_full_screen,
     NULL, /* dpy_describe */
     NULL, /* next */

Index: html2png.c
===================================================================
RCS file: /sources/qemacs/qemacs/html2png.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -b -r1.20 -r1.21
--- html2png.c  18 Apr 2017 09:14:55 -0000      1.20
+++ html2png.c  15 May 2017 09:44:21 -0000      1.21
@@ -108,7 +108,7 @@
 static void ppm_close(QEditScreen *s);
 
 static QEDisplay ppm_dpy = {
-    "ppm",
+    "ppm", 1, 1,
     NULL,
     ppm_init,
     ppm_close,
@@ -130,6 +130,7 @@
     NULL, /* dpy_bmp_draw */
     NULL, /* dpy_bmp_lock */
     NULL, /* dpy_bmp_unlock */
+    NULL, /* dpy_draw_picture */
     NULL, /* dpy_full_screen */
     NULL, /* dpy_describe */
     NULL, /* next */

Index: tty.c
===================================================================
RCS file: /sources/qemacs/qemacs/tty.c,v
retrieving revision 1.86
retrieving revision 1.87
diff -u -b -r1.86 -r1.87
--- tty.c       7 May 2017 06:18:40 -0000       1.86
+++ tty.c       15 May 2017 09:44:21 -0000      1.87
@@ -39,6 +39,8 @@
 #define TTY_STYLE_BITS        32
 #define TTY_FG_COLORS         7936
 #define TTY_BG_COLORS         7936
+#define TTY_RGB_FG(r,g,b)     (0x1000 | (((r) & 0xF0) << 4) | ((g) & 0xF0) | 
((b) >> 4))
+#define TTY_RGB_BG(r,g,b)     (0x1000 | (((r) & 0xF0) << 4) | ((g) & 0xF0) | 
((b) >> 4))
 #define TTY_CHAR(ch,fg,bg)    ((uint32_t)(ch) | ((uint64_t)((fg) | ((bg) << 
17)) << 32))
 #define TTY_CHAR2(ch,col)     ((uint32_t)(ch) | ((uint64_t)(col) << 32))
 #define TTY_CHAR_GET_CH(cc)   ((uint32_t)(cc))
@@ -61,6 +63,8 @@
 #define TTY_STYLE_BITS        16
 #define TTY_FG_COLORS         256
 #define TTY_BG_COLORS         16
+#define TTY_RGB_FG(r,g,b)     (16 + ((r) / 51 * 36) + ((g) / 51 * 6) | ((b) / 
51))
+#define TTY_RGB_BG(r,g,b)     qe_map_color(QERGB(r, g, b), xterm_colors, 16, 
NULL)
 #define TTY_CHAR(ch,fg,bg)    ((uint32_t)(ch) | ((uint32_t)((fg) | ((bg) << 
12)) << 16))
 #define TTY_CHAR2(ch,col)     ((uint32_t)(ch) | ((uint32_t)(col) << 16))
 #define TTY_CHAR_GET_CH(cc)   ((cc) & 0xFFFF)
@@ -1430,13 +1434,91 @@
     }
 }
 
+#ifdef CONFIG_TINY
+#define tty_dpy_draw_picture   NULL
+#else
+static int tty_dpy_draw_picture(QEditScreen *s,
+                                int dst_x, int dst_y, int dst_w, int dst_h,
+                                const QEPicture *ip0,
+                                int src_x, int src_y, int src_w, int src_h,
+                                int flags)
+{
+    TTYState *ts = s->priv_data;
+    TTYChar *ptr;
+    int x, y;
+    const QEPicture *ip = ip0;
+    QEPicture *ip1 = NULL;
+
+    if ((src_w == dst_w && src_h == 2 * dst_h)
+    &&  ip->format == QEBITMAP_FORMAT_8BIT
+    &&  ip->palette
+    &&  ip->palette_size == 256
+    &&  !memcmp(ip->palette, xterm_colors, 256 * sizeof(*ip->palette))) {
+        /* Handle 8-bit picture with direct terminal colors */
+        ptr = ts->screen + dst_y * s->width + dst_x;
+        for (y = 0; y < dst_h; y++) {
+            unsigned char *p1 = ip->data[0] + (src_y + y * 2) * 
ip->linesize[0] + src_x;
+            unsigned char *p2 = p1 + ip->linesize[0];
+            ts->line_updated[dst_y + y] = 1;
+            for (x = 0; x < dst_w; x++) {
+                int bg = p1[x];
+                int fg = p2[x];
+                if (fg == bg)
+                    ptr[x] = TTY_CHAR(' ', fg, bg);
+                else
+                    ptr[x] = TTY_CHAR(0x2584, fg, bg);
+            }
+            ptr += s->width;
+        }
+    } else {
+        /* Convert picture to true color bitmap */
+        if (ip->format != QEBITMAP_FORMAT_RGBA32
+        ||  !(src_w == dst_w && src_h == 2 * dst_h)) {
+            ip1 = qe_create_picture(dst_w, 2 * dst_h, QEBITMAP_FORMAT_RGBA32, 
0);
+            if (!ip1)
+                return -1;
+            if (qe_picture_copy(ip1, 0, 0, ip1->width, ip1->height,
+                                ip0, src_x, src_y, src_w, src_h, flags)) {
+                /* unsupported conversion or scaling */
+                qe_free_picture(&ip1);
+                return -1;
+            }
+            ip = ip1;
+            src_x = src_y = 0;
+            src_w = ip1->width;
+            src_h = ip1->height;
+        }
+        /* Use terminal true color emulation */
+        ptr = ts->screen + dst_y * s->width + dst_x;
+        for (y = 0; y < dst_h; y++) {
+            uint32_t *p1 = (uint32_t*)(void*)(ip->data[0] + (src_y + y * 2) * 
ip->linesize[0]) + src_x;
+            uint32_t *p2 = p1 + (ip->linesize[0] >> 2);
+            ts->line_updated[dst_y + y] = 1;
+            for (x = 0; x < dst_w; x++) {
+                int bg = p1[x];
+                int fg = p2[x];
+                bg = TTY_RGB_BG(QERGB_RED(bg), QERGB_GREEN(bg), 
QERGB_BLUE(bg));
+                fg = TTY_RGB_FG(QERGB_RED(fg), QERGB_GREEN(fg), 
QERGB_BLUE(fg));
+                if (fg == bg)
+                    ptr[x] = TTY_CHAR(' ', fg, bg);
+                else
+                    ptr[x] = TTY_CHAR(0x2584, fg, bg);
+            }
+            ptr += s->width;
+        }
+        qe_free_picture(&ip1);
+    }
+    return 0;
+}
+#endif
+
 static void tty_dpy_describe(QEditScreen *s, EditBuffer *b)
 {
     comb_cache_describe(s, b);
 }
 
 static QEDisplay tty_dpy = {
-    "vt100",
+    "vt100", 1, 2,
     tty_dpy_probe,
     tty_dpy_init,
     tty_dpy_close,
@@ -1458,6 +1540,7 @@
     tty_dpy_bmp_draw,
     tty_dpy_bmp_lock,
     tty_dpy_bmp_unlock,
+    tty_dpy_draw_picture,
     NULL, /* dpy_full_screen */
     tty_dpy_describe,
     NULL, /* next */

Index: win32.c
===================================================================
RCS file: /sources/qemacs/qemacs/win32.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -b -r1.19 -r1.20
--- win32.c     18 Apr 2017 09:14:55 -0000      1.19
+++ win32.c     15 May 2017 09:44:21 -0000      1.20
@@ -511,7 +511,7 @@
 }
 
 static QEDisplay win32_dpy = {
-    "win32",
+    "win32", 1, 1,
     win_probe,
     win_init,
     win_close,
@@ -533,6 +533,7 @@
     NULL, /* dpy_bmp_draw */
     NULL, /* dpy_bmp_lock */
     NULL, /* dpy_bmp_unlock */
+    NULL, /* dpy_draw_picture */
     NULL, /* dpy_full_screen */
     NULL, /* dpy_describe */
     NULL, /* next */

Index: x11.c
===================================================================
RCS file: /sources/qemacs/qemacs/x11.c,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -b -r1.54 -r1.55
--- x11.c       5 May 2017 20:05:35 -0000       1.54
+++ x11.c       15 May 2017 09:44:21 -0000      1.55
@@ -1059,6 +1059,7 @@
         r++;
     }
 #endif
+    /* XXX: update cursor? */
     XFlush(xs->display);
 #ifdef CONFIG_DOUBLE_BUFFER
     update_reset(xs);
@@ -1895,8 +1896,72 @@
     }
 }
 
+static int x11_dpy_draw_picture(QEditScreen *s,
+                                int dst_x, int dst_y, int dst_w, int dst_h,
+                                const QEPicture *ip0,
+                                int src_x, int src_y, int src_w, int src_h,
+                                int flags)
+{
+    X11State *xs = s->priv_data;
+    XImage *im;
+    int status = 0, depth;
+    const QEPicture *ip = ip0;
+    QEPicture *ip1 = NULL;
+
+    /* Only support 32-bit true color X11 displays */
+    /* XXX: should enumerate visuals? */
+    depth = DefaultDepth(xs->display, xs->xscreen);
+    if (depth != 24)
+        return 1;
+
+    if (ip->format != QEBITMAP_FORMAT_RGBA32
+    ||  !(src_w == dst_w && src_h == dst_h)) {
+        ip1 = qe_create_picture(dst_w, dst_h, QEBITMAP_FORMAT_RGBA32, 0);
+        if (!ip1)
+            return 2;
+        /* Convert and/or resize picture */
+        if (qe_picture_copy(ip1, 0, 0, dst_w, dst_h,
+                            ip0, src_x, src_y, src_w, src_h, flags)) {
+            qe_free_picture(&ip1);
+            return 3;
+        }
+        ip = ip1;
+        src_x = src_y = 0;
+        src_w = dst_w;
+        src_h = dst_h;
+    }
+
+    im = XCreateImage(xs->display, xs->attr.visual, depth, ZPixmap, 0,
+                      NULL, dst_w, dst_h, 32, ip->linesize[0] / 4);
+    if (im == NULL) {
+        qe_free_picture(&ip1);
+        return 4;
+    }
+    if (im->bits_per_pixel != 32) {
+        XDestroyImage(im);
+        qe_free_picture(&ip1);
+        return 5;
+    }
+
+    if (ip->format == QEBITMAP_FORMAT_RGBA32) {
+        im->data = (char *)(ip->data[0] + src_y * ip->linesize[0] + src_x * 4);
+        im->bytes_per_line = ip->linesize[0];
+        update_rect(xs, dst_x, dst_y, dst_x + dst_w, dst_y + dst_h);
+        status = XPutImage(xs->display, xs->dbuffer, xs->gc, im,
+                           0, 0, dst_x, dst_y, dst_w, dst_h);
+        XFlush(xs->display);
+    } else {
+        status = 6;
+    }
+
+    im->data = NULL;
+    XDestroyImage(im);
+    qe_free_picture(&ip1);
+    return status;
+}
+
 static QEDisplay x11_dpy = {
-    "x11",
+    "x11", 1, 1,
     x11_dpy_probe,
     x11_dpy_init,
     x11_dpy_close,
@@ -1918,6 +1983,7 @@
     x11_dpy_bmp_draw,
     x11_dpy_bmp_lock,
     x11_dpy_bmp_unlock,
+    x11_dpy_draw_picture,
     x11_dpy_full_screen,
     NULL, /* dpy_describe */
     NULL, /* next */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]