]> git.neil.brown.name Git - edlib.git/commitdiff
Draw:image: support scale and crop.
authorNeilBrown <neil@brown.name>
Fri, 22 Sep 2023 05:41:44 +0000 (15:41 +1000)
committerNeilBrown <neil@brown.name>
Wed, 27 Sep 2023 06:00:03 +0000 (16:00 +1000)
->num if non-zero scales the image.
->num2 if non zero sets absolute height and ->num becomes absolute
->width.

->x and ->y set top-left corner.

Signed-off-by: NeilBrown <neil@brown.name>
DOC/Calls
DOC/Developer/06-rendering.md
DOC/TODO.md
display-ncurses.c
display-x11-xcb.c
python/display-pygtk.py

index 30932d52da852b76bfdc20bb358a3ffc37dd4e56..04350e985f745826b55c000a60ae0ed364ad603e 100644 (file)
--- a/DOC/Calls
+++ b/DOC/Calls
@@ -134,6 +134,13 @@ Draw an image on the pane (or ancestor which as been cleared).
         the purpose of cursor positioning.  If these are present and
         p->cx,cy are not negative, draw a cursor at p->cx,cy highlighting
         the relevant cell.
+- num, num2 if both positive, give width and height to scale to, 
+       over-riding the default scaling.
+       If only num is positive, then it is a scale factor *1024
+       to be applied to the image.
+- x,y give a top-left pixel in the scaled image to display.  Only
+      this pixel and those to right and below might be shown.
+      Negative values allow a margin between pane edge and this image.
 
 ## all-displays
 
index 92d21175030a3b55228e32b6bb041dbda47a1c47..dcc3f3e0e22d36ff201149179b7be93ecbd17b12 100644 (file)
@@ -25,9 +25,9 @@ given.
 
 ### Refresh size
 
-The first step in the sequence ensure all panes have he right size.  If
+The first step in the sequence ensure all panes have the right size.  If
 one pane changes size for any reason, such as a top level window being
-resized, other may need to adjust to this change.  A pane that notices
+resized, others may need to adjust to this change.  A pane that notices
 its size has changed, or might need to change, sets DAMAGED_SIZE with
 the pane_damage() interface.
 
@@ -37,11 +37,11 @@ depth of zero will have their size adjusted to match the parent, which
 will cause DAMAGED_SIZE to be set on them.  Any pane with a larger depth
 will just have DAMAGED_SIZE set.
 
-When pane is thus requested to handle a resize, the sequence starts
+When pane is thus requested to handle a resize, the sequence starts
 again from the root looking for panes that need to handle a resize.  It
-should quickly deal with all pane. It finds that it needs to resize more
-than 1000 panes, it assumes that some pane keeps setting DAMAGED_SIZE on
-itself, and it aborts the loop.
+should quickly deal with all pane.  If it finds that it needs to resize
+more than 1000 panes, it assumes that some pane keeps setting
+DAMAGED_SIZE on itself, and it aborts the loop.
 
 ### Refresh view
 
index 5fd1d6819c3fa62759be8d45058fb426faf6c8ce..e7e1a65dc28fec81763b5b21092816691b229b85 100644 (file)
@@ -250,6 +250,7 @@ Module features
 
 ### lib-server
 
+- [ ] catch broken-pipe errors when sending to sock
 - [ ] ctrl-z in elc doesn't ask edlib to release the terminal
 - [ ] do we need both .term and .disp?  When are they different?
 
index a0102d04422b1267fc58aa74925c3b0bfe75af56..69cd0fe733d7b00ccb5c3ad1d1cfcc1a599fdcca 100644 (file)
@@ -1015,6 +1015,11 @@ DEF_CMD(nc_draw_image)
         *    the purpose of cursor positioning.  If these are present and
         *    p->cx,cy are not negative, draw a cursor at p->cx,cy highlighting
         *    the relevant cell.
+        *
+        * num,num2, if both positive, override the automatic scaling.
+        *    The image is scaled to this many pixels.
+        * x,y is top-left pixel in the scaled image to start display at.
+        *    Negative values allow a margin between pane edge and this image.
         */
        struct pane *p = ci->home;
        struct display_data *dd = p->data;
@@ -1022,6 +1027,9 @@ DEF_CMD(nc_draw_image)
        const char *mode = ci->str2 ?: "";
        bool stretch = strchr(mode, 'S');
        int w = ci->focus->w, h = ci->focus->h * 2;
+       int pw = w, ph = h;
+       int xo = 0, yo = 0;
+       int cix, ciy;
        int cx = -1, cy = -1;
        MagickBooleanType status;
        MagickWand *wd;
@@ -1055,7 +1063,20 @@ DEF_CMD(nc_draw_image)
                return Einval;
 
        MagickAutoOrientImage(wd);
-       if (!stretch) {
+       if (ci->num > 0 && ci->num2 > 0) {
+               w = ci->num;
+               h = ci->num2;
+       } else if (ci->num > 0) {
+               int ih = MagickGetImageHeight(wd);
+               int iw = MagickGetImageWidth(wd);
+
+               if (iw <= 0 || iw <= 0) {
+                       DestroyMagickWand(wd);
+                       return Efail;
+               }
+               w = iw * ci->num / 1024;
+               h = ih * ci->num / 1024;
+       } else if (!stretch) {
                int ih = MagickGetImageHeight(wd);
                int iw = MagickGetImageWidth(wd);
 
@@ -1086,8 +1107,28 @@ DEF_CMD(nc_draw_image)
                }
        }
        MagickAdaptiveResizeImage(wd, w, h);
+       cix = ci->x;
+       ciy = ci->y;
+       if (cix < 0) {
+               xo -= cix;
+               pw += cix;
+               cix = 0;
+       }
+       if (ciy < 0) {
+               yo -= ciy;
+               ph += ciy;
+               ciy = 0;
+       }
+       if (w - cix <= pw)
+               w -= cix;
+       else
+               w = pw;
+       if (h - ciy <= ph)
+               h -= ciy;
+       else
+               h = ph;
        buf = malloc(h * w * 4);
-       MagickExportImagePixels(wd, 0, 0, w, h, "RGBA", CharPixel, buf);
+       MagickExportImagePixels(wd, cix, ciy, w, h, "RGBA", CharPixel, buf);
 
        if (ci->focus->cx >= 0 && strchr(mode, ':')) {
                /* We want a cursor */
@@ -1352,6 +1393,8 @@ static struct pane *ncurses_init(struct pane *ed safe,
        dd->scr_file = f;
        dd->is_xterm = (term && strstarts(term, "xterm"));
 
+       attr_set_str(&p->attrs, "Display:pixels", "1x2");
+
        set_screen(p);
 
        ncurses_start(p);
index 237cf3b069270e184b8645ce623d7000e47b0788..e9cb956055440dc0c3844da228833ed4f9d6bd57 100644 (file)
@@ -825,13 +825,20 @@ DEF_CMD(xcb_draw_image)
         *    the purpose of cursor positioning.  If these are present and
         *    p->cx,cy are not negative, draw a cursor at p->cx,cy highlighting
         *    the relevant cell.
+        *
+        * num,num2, if both positive, override the automatic scaling.
+        *    The image is scaled to this many pixels.
+        * x,y is top-left pixel in the scaled image to start display at.
+        *    Negative values allow a margin between pane edge and this image.
         */
        struct xcb_data *xd = ci->home->data;
        const char *mode = ci->str2 ?: "";
        bool stretch = strchr(mode, 'S');
-       int w = ci->focus->w, h = ci->focus->h;
+       int wh;
        int x = 0, y = 0;
+       int pw, ph;
        int xo, yo;
+       int cix, ciy;
        int stride;
        struct panes *ps;
        MagickBooleanType status;
@@ -875,7 +882,22 @@ DEF_CMD(xcb_draw_image)
                return Einval;
 
        MagickAutoOrientImage(wd);
-       if (!stretch) {
+       w = ci->focus->w;
+       h = ci->focus->h;
+       if (ci->num > 0 && ci->num2 > 0) {
+               w = ci->num;
+               h = ci->num2;
+       } else if (ci->num > 0) {
+               int ih = MagickGetImageHeight(wd);
+               int iw = MagickGetImageWidth(wd);
+
+               if (iw <= 0 || iw <= 0) {
+                       DestroyMagickWand(wd);
+                       return Efail;
+               }
+               w = iw * ci->num / 1024;
+               h = ih * ci->num / 1024;
+       } else if (!stretch) {
                int ih = MagickGetImageHeight(wd);
                int iw = MagickGetImageWidth(wd);
 
@@ -905,6 +927,28 @@ DEF_CMD(xcb_draw_image)
                }
        }
        MagickAdaptiveResizeImage(wd, w, h);
+       pw = ci->focus->w;
+       ph = ci->focus->h;
+       cix = ci->x;
+       ciy = ci->y;
+       if (cix < 0) {
+               xo -= cix;
+               pw += cix;
+               cix = 0;
+       }
+       if (ciy < 0) {
+               yo -= ciy;
+               ph += ciy;
+               ciy = 0;
+       }
+       if (w - cix <= pw)
+               w -= cix;
+       else
+               w = pw;
+       if (h - ciy <= ph)
+               h -= ciy;
+       else
+               h = ph;
        stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
        buf = malloc(h * stride);
        // Cairo expects 32bit values with A in the high byte, then RGB.
@@ -913,7 +957,8 @@ DEF_CMD(xcb_draw_image)
 
        fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
        fmt[1] = 0;
-       MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt, CharPixel, buf);
+       MagickExportImagePixels(wd, cix, ciy, w, h,
+                               (char*)fmt, CharPixel, buf);
        surface = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
                                                      w, h, stride);
        cairo_set_source_surface(ps->ctx, surface, x + xo, y + yo);
index aa8e5cbd1ce85e7a5aab0d46040b4e7ee225baaf..a9882c3b75be8dc61e220be343a62483aeac0c6b 100644 (file)
@@ -276,7 +276,7 @@ class EdDisplay(edlib.Pane):
 
         return True
 
-    def handle_image(self, key, focus, str1, str2, **a):
+    def handle_image(self, key, focus, num, num2, str1, str2, xy, **a):
         "handle:Draw:image"
         self.damaged(edlib.DAMAGED_POSTORDER)
         # 'str1' identifies the image. Options are:
@@ -297,6 +297,11 @@ class EdDisplay(edlib.Pane):
         #     the purpose of cursor positioning.  If these are present and
         #     focus.cx,cy are not negative, draw a cursor at cx,cy highlighting
         #     the relevant cell.
+        #  num,num2, if both positive, override the automatic scaling.
+        #    The image is scaled to this many pixels.
+        #    If num2 <=0, then num is 1024 times a scale factor
+        #  x,y  is top-left pixel in the scaled image to start display at.
+        #    Negative values allow a margin between pane edge and this image.
         if not str1:
             return edlib.Enoarg
         mode = str2 if str2 else ""
@@ -314,10 +319,17 @@ class EdDisplay(edlib.Pane):
                 return edlib.Einval
         except:
             # create a red error image
-            pb = Gdk.Pixbuf(Gdk.COLORSPACE_RGB, False, 8, w, h)
+            pb = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB,
+                                  False, 8, w, h)
             pb.fill(0xff000000)
 
-        if not stretch:
+        if num > 0 and num2 > 0:
+            w = num
+            h = num2
+        elif num > 0:
+            w = pb.get_width() * num / 1024
+            h = pb.get_height() * num / 1024
+        elif not stretch:
             if pb.get_width() * h > pb.get_height() * w:
                 # image is wider than space, reduce height
                 h2 = pb.get_height() * w / pb.get_width()
@@ -334,9 +346,35 @@ class EdDisplay(edlib.Pane):
                 elif 'L' not in mode:
                     x = (w - w2) / 2
                 w = w2
-        scale = pb.scale_simple(w, h, GdkPixbuf.InterpType.HYPER)
         pm, xo, yo, pbg = self.find_pixmap(focus, True)
+        sh = h / pb.get_height()
+        sw = w / pb.get_width()
+
+        pw = focus.w
+        ph = focus.h
+        cix, ciy = xy
+        if cix < 0:
+            xo -= cix
+            pw += cix
+            cix = 0
+        if ciy < 0:
+            yo -= ciy
+            ph += ciy
+            ciy = 0
+        if w - cix <= pw:
+            w -= cix
+        else:
+            w = pw
+        if h - ciy <= ph:
+            h -= ciy
+        else:
+            h = ph
+
         cr = cairo.Context(pm)
+        scale = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB,
+                                     False, 8, w, h)
+        pb.scale(scale, 0,0, w, h, -cix, -ciy, sw, sh,
+                 GdkPixbuf.InterpType.BILINEAR)
         Gdk.cairo_set_source_pixbuf(cr, scale, x + xo, y + yo)
         cr.paint()