Each character cell can be 2 pixel. This allows low-res image display.
Signed-off-by: NeilBrown <neil@brown.name>
Bugs to be fixed
----------------
+- [ ] renderline *knows* about scaling and when it places the cursor
+ in an image, it gets it wrong for ncurses. It should ask about
+ scaling.
- [ ] notmuch shouldn't clear tag:new until *all* views
on search have closed.
- [X] I think attaching a path starting ~/ to an email fails.
- [ ] add full list of colour names (to lib-colourmap)
- [ ] allow a pane to require 'true-colour' and discover number of colours available
Colour map gets changed when it becomes the focus.
-- [ ] merge 'catpic' code to draw low-res images.
+- [X] merge 'catpic' code to draw low-res images.
- [ ] When only 16 colours, maybe add underline when insufficient contrast available.
- [ ] automatically ensure the fg colour contrasts with bg, unless explicitly disabled.
If bg is bright, reduce fg brightness. If bg is dark, reduce saturation.
- [ ] detect and hide cited text
- [ ] maybe detect "-----Original Message-----" as indicating cited text
- [ ] Make long to/cc headers truncate unless selected.
-- [ ] display image on ncurses.
+- [X] display image on ncurses.
- [ ] Make addresses active (menu?) to allow adding to a saved search
with options and/or/andnot. Also "mail to" or "save"..
- [ ] Allow any selection to be added to a saved search.
LIBS-lang-python = $(shell pkg-config --libs $(pypkg))
INC-lang-python = $(shell pkg-config --cflags $(pypkg))
-LIBS-display-ncurses = $(shell pkg-config --libs panelw ncursesw)
-INC-display-ncurses = $(shell pkg-config --cflags panelw ncursesw)
+LIBS-display-ncurses = $(shell pkg-config --libs panelw ncursesw MagickWand)
+INC-display-ncurses = $(shell pkg-config --cflags panelw ncursesw MagickWand) -Wno-strict-prototypes
O/display-ncurses.o : md5.h
LIBS-lib-aspell = -laspell
#include <ctype.h>
#include <signal.h>
#include <sys/ioctl.h>
-#include <term.h>
#include <netdb.h>
+#include <wand/MagickWand.h>
+#ifdef __CHECKER__
+// enums confuse sparse...
+#define MagickBooleanType int
+#endif
+
+#include <term.h>
+
#include "core.h"
#ifdef RECORD_REPLAY
return 1;
}
+DEF_CMD(nc_draw_image)
+{
+ /* 'str' identifies the image. Options are:
+ * file:filename - load file from fs
+ * comm:command - run command collecting bytes
+ * 'num' is '16' if image should be stretched to fill pane
+ * Otherwise it is the 'or' of
+ * 0,1,2 for left/middle/right in x direction
+ * 0,4,8 for top/middle/bottom in y direction
+ * only one of these can be used as image will fill pane
+ * in other direction.
+ * If 'x' and 'y' are both positive, draw cursor box at
+ * p->cx, p->cy of a size so that 'x' will fit across and
+ * 'y' will fit down.
+ */
+ struct pane *p = ci->home;
+ struct display_data *dd = p->data;
+ int x = 0, y = 0;
+ bool stretch = ci->num & 16;
+ int pos = ci->num;
+ int w = ci->focus->w, h = ci->focus->h * 2;
+ int cx = -1, cy = -1;
+ MagickBooleanType status;
+ MagickWand *wd;
+ unsigned char *buf;
+ int i, j;
+
+ if (!ci->str)
+ return Enoarg;
+ if (strncmp(ci->str, "file:", 5) == 0) {
+ wd = NewMagickWand();
+ status = MagickReadImage(wd, ci->str + 5);
+ if (status == MagickFalse) {
+ DestroyMagickWand(wd);
+ return Efail;
+ }
+ } else if (strncmp(ci->str, "comm:", 5) == 0) {
+ struct call_return cr;
+ wd = NewMagickWand();
+ cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
+ if (!cr.s) {
+ DestroyMagickWand(wd);
+ return Efail;
+ }
+ status = MagickReadImageBlob(wd, cr.s, cr.i);
+ free(cr.s);
+ if (status == MagickFalse) {
+ DestroyMagickWand(wd);
+ return Efail;
+ }
+ } else
+ return Einval;
+
+ MagickAutoOrientImage(wd);
+ if (!stretch) {
+ int ih = MagickGetImageHeight(wd);
+ int iw = MagickGetImageWidth(wd);
+
+ if (iw <= 0 || iw <= 0) {
+ DestroyMagickWand(wd);
+ return Efail;
+ }
+ if (iw * h > ih * w) {
+ /* Image is wider than space, use less height */
+ ih = ih * w / iw;
+ switch(pos & (8+4)) {
+ case 4: /* center */
+ y = (h - ih) / 2; break;
+ case 8: /* bottom */
+ y = h - ih; break;
+ }
+ /* Keep 'h' even! */
+ h = ((ih+1)/2) * 2;
+ } else {
+ /* image is too tall, use less width */
+ iw = iw * h / ih;
+ switch (pos & (1+2)) {
+ case 1: /* center */
+ x = (w - iw) / 2; break;
+ case 2: /* right */
+ x = w - iw ; break;
+ }
+ w = iw;
+ }
+ }
+ MagickAdaptiveResizeImage(wd, w, h);
+ buf = malloc(h * w * 4);
+ MagickExportImagePixels(wd, 0, 0, w, h, "RGBA", CharPixel, buf);
+
+ if (ci->x > 0 && ci->y > 0 && ci->focus->cx >= 0) {
+ /* We want a cursor */
+ cx = x + ci->focus->cx;
+ cy = y + ci->focus->cy;
+ }
+ for (i = 0; i < h; i+= 2) {
+ static wint_t hilo = 0x2580; /* L'▀' */
+ for (j = 0; j < w ; j+= 1) {
+ unsigned char *p1 = buf + i*w*4 + j*4;
+ unsigned char *p2 = buf + (i+1)*w*4 + j*4;
+ int rgb1[3] = { p1[0]*1000/255, p1[1]*1000/255, p1[2]*1000/255 };
+ int rgb2[3] = { p2[0]*1000/255, p2[1]*1000/255, p2[2]*1000/255 };
+ int fg = find_col(dd, rgb1);
+ int bg = find_col(dd, rgb2);
+
+ if (p1[3] < 128 || p2[3] < 128) {
+ /* transparent */
+ cchar_t cc;
+ short f,b;
+ struct pane *pn2 = ci->focus;
+ PANEL *pan = pane_panel(pn2, NULL);
+
+ while (!pan && pn2->parent != pn2) {
+ pn2 = pn2->parent;
+ pan = pane_panel(pn2, NULL);
+ }
+ if (pan) {
+ wgetbkgrnd(panel_window(pan), &cc);
+ pair_content(cc.ext_color, &f, &b);
+ if (p1[3] < 128)
+ fg = b;
+ if (p2[3] < 128)
+ bg = b;
+ }
+ }
+ /* FIXME this doesn't work because
+ * render-line knows too much and gets it wrong.
+ */
+ if (cx == x+j && cy == y + (i/2))
+ ncurses_text(ci->focus, p, 'X', 0,
+ to_pair(dd, 0, 0),
+ x+j, y+(i/2), 1);
+ else
+ ncurses_text(ci->focus, p, hilo, 0,
+ to_pair(dd, fg, bg),
+ x+j, y+(i/2), 0);
+
+ }
+ }
+ free(buf);
+
+ DestroyMagickWand(wd);
+
+ pane_damaged(ci->home, DAMAGED_POSTORDER);
+
+ return 1;
+}
+
+DEF_CMD(nc_image_size)
+{
+ MagickBooleanType status;
+ MagickWand *wd;
+ int ih, iw;
+
+ if (!ci->str)
+ return Enoarg;
+ if (strncmp(ci->str, "file:", 5) == 0) {
+ wd = NewMagickWand();
+ status = MagickReadImage(wd, ci->str + 5);
+ if (status == MagickFalse) {
+ DestroyMagickWand(wd);
+ return Efail;
+ }
+ } else if (strncmp(ci->str, "comm:", 5) == 0) {
+ struct call_return cr;
+ wd = NewMagickWand();
+ cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
+ if (!cr.s) {
+ DestroyMagickWand(wd);
+ return Efail;
+ }
+ status = MagickReadImageBlob(wd, cr.s, cr.i);
+ free(cr.s);
+ if (status == MagickFalse) {
+ DestroyMagickWand(wd);
+ return Efail;
+ }
+ } else
+ return Einval;
+
+ MagickAutoOrientImage(wd);
+ ih = MagickGetImageHeight(wd);
+ iw = MagickGetImageWidth(wd);
+
+ DestroyMagickWand(wd);
+ comm_call(ci->comm2, "callback:size", ci->focus,
+ 0, NULL, NULL, 0, NULL, NULL,
+ iw, ih);
+ return 1;
+}
+
DEF_CMD(nc_refresh_size)
{
struct pane *p = ci->home;
key_add(nc_map, "Draw:clear", &nc_clear);
key_add(nc_map, "Draw:text-size", &nc_text_size);
key_add(nc_map, "Draw:text", &nc_draw_text);
+
+ key_add(nc_map, "Draw:image", &nc_draw_image);
+ key_add(nc_map, "Draw:image-size", &nc_image_size);
+
key_add(nc_map, "Refresh:size", &nc_refresh_size);
key_add(nc_map, "Refresh:postorder", &nc_refresh_post);
key_add(nc_map, "Paste:get", &nc_get_paste);
Key ":C-V"
Display 80,30 723F0458401FBB2B8EE91FF2E330C364 41,14
Key ":C-V"
-Display 80,30 79595F464D732CA0ABB1D5E2A670B03B 41,23
+Display 80,30 3B328DBDA68F45B61927CFF26C04DE2B 41,23
Key ":C-V"
Display 80,30 58B83018BB0E7C6BF86FEEA853B7D0E7 41,23
Key ":C-V"
-Display 80,30 D99B4726C0BA5CA9CB3F6142A5980DCC 41,19
+Display 80,30 8AF4F30D9AE7F72B6C2CE83062FB8A1B 41,19
Key ":C-V"
-Display 80,30 DDB596455E4FB24A6E66846286A718A8 41,25
+Display 80,30 3DC89B674296F8F0EEBA32AF913A0CCB 41,25
Key ":C-V"
-Display 80,30 341A976477CC9D0790C8BF0CA9822966 41,19
+Display 80,30 CB4B810BA885BAFC7B6926DE70E4AE0A 41,19
Key ":C-V"
-Display 80,30 8AB3BED5D441E01FC81A72999ECBA8C9 41,25
+Display 80,30 E0BC8AEBBCBA9E2DCD119CC0365D32F4 41,25
Key ":C-V"
-Display 80,30 4A32411D13D3B26FA19FB50B472C7CAA 41,21
+Display 80,30 BE456097FB843B20B3B7A0440F3A8B67 41,21
Key ":C-V"
-Display 80,30 E0B009206406E0F14A8356834AD89824 41,20
+Display 80,30 CD005511873C7F4F5BA64CB38611F7BD 41,20
Key ":C-V"
-Display 80,30 9A397612361034D694752C596C3357CB 41,27
+Display 80,30 1D95A578C44DC390B601563886C6A6C4 41,27
Key ":A-v"
-Display 80,30 217C027F44331EEB28331150E0CC89BA 41,19
+Display 80,30 D28AEA7D77B6FEEADA52BF66ADB0820D 41,19
Key ":A-v"
-Display 80,30 BF728199F869F41990D07497FCF8C8E7 41,4
+Display 80,30 63BCC4810C580652636EEBC1EA9BB70D 41,4
Key ":A-v"
-Display 80,30 764F696B3B0F1398A085C42DFCEEB984 41,3
+Display 80,30 DF3166618B75991D58669065A0FC7E6E 41,3
Key ":A-v"
-Display 80,30 8B40FCD3A5EAF0746FAB8FA70D61ED15 41,6
+Display 80,30 24988827E9C260FC7732215D39F34703 41,6
Key ":C-X"
-Display 80,30 3F367CE9D7CEE7E4C12F5C81CCB7BE21 41,6
+Display 80,30 B98A84BD3FFB4073BEA3FF502ABB2C6D 41,6
Key "-o"
-Display 80,30 8B40FCD3A5EAF0746FAB8FA70D61ED15 41,6
+Display 80,30 24988827E9C260FC7732215D39F34703 41,6
Key "- "
-Display 80,30 7F1A3C4B9AF526FDF87F0DED4B5D3924 41,6
+Display 80,30 C98F2DAD7602B9373411CAB1180C5F32 41,6
Key "- "
Display 80,30 26AAA5FF0FB53CE6F09E28737E89F4CC 41,6
Key ":Backspace"
-Display 80,30 DD6D405AEE5B2D4F6503DB02394A317F 41,6
+Display 80,30 C57B318B84914EB7A5AA55B1FCF756DD 41,6
Key ":Backspace"
Display 80,30 FDAE252AB12E082CC914F06CBE18846A 41,6
Key ":C-X"
Key ":C-V"
Display 80,30 15A6375488AAAB2A50F6537A57C69FF4 41,14
Key ":C-V"
-Display 80,30 23908995811429680EB53B41399FAF7B 41,23
+Display 80,30 141DB06AD827E40CEA0DB459D70F26B1 41,23
Key ":C-S"
-Display 80,30 F608E450DDD730F144D1A8D569103A16 70,0
+Display 80,30 B0170AB49201ACFEEBAADB56BD92580C 70,0
Key "-e"
-Display 80,30 B039C6296C9EB955D1626797AFD33E1B 71,0
-Display 80,30 E3DA2CD0DC52171F14CDE6020E8DBA4C 71,0
+Display 80,30 2E14A636535200CAA1A824EB3AE7DEB4 71,0
+Display 80,30 9E0F7CEA7310D628BEB25CF97031EA5A 71,0
Key "-m"
-Display 80,30 0F2C3D854158665969A211A1098299B2 72,0
+Display 80,30 7BDACD6E9BC1D42692F10A131891B30A 72,0
Key "-p"
-Display 80,30 9E79066979B0A8773C848AE5F5D83AA5 73,0
-Display 80,30 5204E6D4AD398441CF51E38B81E9AFD3 73,0
+Display 80,30 4E6FA27F57A29A1EDEE4403C5B20B40E 73,0
+Display 80,30 B66FC1265DE66359945402C362BC6BB8 73,0
Key ":Enter"
-Display 80,30 FD4151E497AA682CCEFAC0FBBA4EBF1B 52,13
+Display 80,30 E82BA22E6B4C3593A91600521C28DAF7 52,13
Key ":C-L"
-Display 80,30 DC8E40C3AB17F1986F9421F4ED4D8782 52,13
+Display 80,30 0B0303CA8A43537FFC33265CB28BD319 52,13
Key ":C-S"
-Display 80,30 82BB743859F3892171E6B939E6FF7411 70,0
+Display 80,30 BC1AFEA3CF728F32D35D4BA2AF7C4FCE 70,0
Key ":C-S"
-Display 80,30 009B860F851CC71954093C70D05C2B46 73,0
-Display 80,30 AFDC3770D58F4E93FDA772D309464BE7 73,0
+Display 80,30 E8E27AF5E40D19C8D32D176112DCA3DC 73,0
+Display 80,30 4BFE535B8BDFAA26E4663769EACD822E 73,0
Key ":C-S"
-Display 80,30 706B19FCDA756055EF05D9A3AF6FB206 72,0
-Display 80,30 48EC1AE6318A704EA5D4B6FB0013AA2D 72,0
+Display 80,30 48046EAA95F9A3BD2B63B546651EAE24 72,0
+Display 80,30 5CC9B8DB70BF934ED27A97327CBB5171 72,0
Key ":Enter"
-Display 80,30 69DB653BD93BAE6093F2BE2C1F2AD1CE 52,21
+Display 80,30 5C36EDC7853F89170F73B5F164533E92 52,21
Key ":C-V"
-Display 80,30 825CEC0E5A930BD1BFF9E5ABF1DE92BB 41,25
+Display 80,30 B7E0CD0828B88C82E16A55C7B2BC0F7A 41,25
Key ":C-V"
Display 80,30 A2958EB3DA369084F3AA0A8C220474CF 41,20
Key ":A->"