1 /* $Id: image.c,v 1.37 2010/12/21 10:13:55 htrb Exp $ */
2
3 #include "fm.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <signal.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #ifdef HAVE_WAITPID
10 #include <sys/wait.h>
11 #endif
12
13 #ifdef USE_IMAGE
14
15 static int image_index = 0;
16
17 /* display image */
18
19 typedef struct _termialImage {
20 ImageCache *cache;
21 short x;
22 short y;
23 short sx;
24 short sy;
25 short width;
26 short height;
27 } TerminalImage;
28
29 static TerminalImage *terminal_image = NULL;
30 static int n_terminal_image = 0;
31 static int max_terminal_image = 0;
32 static FILE *Imgdisplay_rf = NULL, *Imgdisplay_wf = NULL;
33 static pid_t Imgdisplay_pid = 0;
34 static int openImgdisplay();
35 static void closeImgdisplay();
36 int getCharSize();
37
38 void
initImage()39 initImage()
40 {
41 if (activeImage)
42 return;
43 if (getCharSize())
44 activeImage = TRUE;
45 }
46
47 int get_pixel_per_cell(int *ppc, int *ppl);
48
49 int
getCharSize()50 getCharSize()
51 {
52 FILE *f;
53 Str tmp;
54 int w = 0, h = 0;
55
56 set_environ("W3M_TTY", ttyname_tty());
57
58 if (enable_inline_image) {
59 int ppc, ppl;
60
61 if (get_pixel_per_cell(&ppc,&ppl)) {
62 pixel_per_char_i = ppc ;
63 pixel_per_line_i = ppl ;
64 pixel_per_char = (double)ppc;
65 pixel_per_line = (double)ppl;
66 }
67 else {
68 pixel_per_char_i = (int)pixel_per_char;
69 pixel_per_line_i = (int)pixel_per_line;
70 }
71
72 return TRUE;
73 }
74
75 tmp = Strnew();
76 if (!strchr(Imgdisplay, '/'))
77 Strcat_m_charp(tmp, w3m_auxbin_dir(), "/", NULL);
78 Strcat_m_charp(tmp, Imgdisplay, " -test 2>/dev/null", NULL);
79 f = popen(tmp->ptr, "r");
80 if (!f)
81 return FALSE;
82 while (fscanf(f, "%d %d", &w, &h) < 0) {
83 if (feof(f))
84 break;
85 }
86 pclose(f);
87
88 if (!(w > 0 && h > 0))
89 return FALSE;
90 if (!set_pixel_per_char)
91 pixel_per_char = (int)(1.0 * w / COLS + 0.5);
92 if (!set_pixel_per_line)
93 pixel_per_line = (int)(1.0 * h / LINES + 0.5);
94 return TRUE;
95 }
96
97 void
termImage()98 termImage()
99 {
100 if (!activeImage)
101 return;
102 clearImage();
103 if (Imgdisplay_wf) {
104 fputs("2;\n", Imgdisplay_wf); /* ClearImage() */
105 fflush(Imgdisplay_wf);
106 }
107 closeImgdisplay();
108 }
109
110 static int
openImgdisplay()111 openImgdisplay()
112 {
113 char *cmd;
114
115 if (!strchr(Imgdisplay, '/'))
116 cmd = Strnew_m_charp(w3m_auxbin_dir(), "/", Imgdisplay, NULL)->ptr;
117 else
118 cmd = Imgdisplay;
119 Imgdisplay_pid = open_pipe_rw(&Imgdisplay_rf, &Imgdisplay_wf);
120 if (Imgdisplay_pid < 0)
121 goto err0;
122 if (Imgdisplay_pid == 0) {
123 /* child */
124 setup_child(FALSE, 2, -1);
125 myExec(cmd);
126 /* XXX: ifdef __EMX__, use start /f ? */
127 }
128 activeImage = TRUE;
129 return TRUE;
130 err0:
131 Imgdisplay_pid = 0;
132 activeImage = FALSE;
133 return FALSE;
134 }
135
136 static void
closeImgdisplay()137 closeImgdisplay()
138 {
139 if (Imgdisplay_wf)
140 fclose(Imgdisplay_wf);
141 if (Imgdisplay_rf) {
142 /* sync with the child */
143 getc(Imgdisplay_rf); /* EOF expected */
144 fclose(Imgdisplay_rf);
145 }
146 if (Imgdisplay_pid)
147 kill(Imgdisplay_pid, SIGKILL);
148 Imgdisplay_rf = NULL;
149 Imgdisplay_wf = NULL;
150 Imgdisplay_pid = 0;
151 }
152
153 void
addImage(ImageCache * cache,int x,int y,int sx,int sy,int w,int h)154 addImage(ImageCache * cache, int x, int y, int sx, int sy, int w, int h)
155 {
156 TerminalImage *i;
157
158 if (!activeImage)
159 return;
160 if (n_terminal_image >= max_terminal_image) {
161 max_terminal_image = max_terminal_image ? (2 * max_terminal_image) : 8;
162 terminal_image = New_Reuse(TerminalImage, terminal_image,
163 max_terminal_image);
164 }
165 i = &terminal_image[n_terminal_image];
166 i->cache = cache;
167 i->x = x;
168 i->y = y;
169 i->sx = sx;
170 i->sy = sy;
171 i->width = w;
172 i->height = h;
173 n_terminal_image++;
174 }
175
176 static void
syncImage(void)177 syncImage(void)
178 {
179 if (enable_inline_image) {
180 return;
181 }
182
183 fputs("3;\n", Imgdisplay_wf); /* XSync() */
184 fputs("4;\n", Imgdisplay_wf); /* put '\n' */
185 while (fflush(Imgdisplay_wf) != 0) {
186 if (ferror(Imgdisplay_wf))
187 goto err;
188 }
189 if (!fgetc(Imgdisplay_rf))
190 goto err;
191 return;
192 err:
193 closeImgdisplay();
194 image_index += MAX_IMAGE;
195 n_terminal_image = 0;
196 }
197
198 void put_image_osc5379(char *url, int x, int y, int w, int h, int sx, int sy, int sw, int sh);
199 void put_image_sixel(char *url, int x, int y, int w, int h, int sx, int sy, int sw, int sh, int n_terminal_image);
200 void put_image_iterm2(char *url, int x, int y, int w, int h);
201 void put_image_kitty(char *url, int x, int y, int w, int h, int sx, int sy, int
202 sw, int sh, int c, int r);
203
204 void
drawImage()205 drawImage()
206 {
207 static char buf[64];
208 int j, draw = FALSE;
209 TerminalImage *i;
210 struct stat st ;
211
212 if (!activeImage)
213 return;
214 if (!n_terminal_image)
215 return;
216 for (j = 0; j < n_terminal_image; j++) {
217 i = &terminal_image[j];
218
219 if (enable_inline_image) {
220 /*
221 * So this shouldn't ever happen, but if it does then at least let's
222 * not have external programs fetch images from the Internet...
223 */
224 if (!i->cache->touch || stat(i->cache->file,&st))
225 return;
226
227 char *url = i->cache->file;
228
229 int x = i->x / pixel_per_char_i;
230 int y = i->y / pixel_per_line_i;
231
232 int w = i->cache->a_width > 0 ? (
233 (i->cache->width + i->x % pixel_per_char_i + pixel_per_char_i - 1) /
234 pixel_per_char_i) : 0;
235 int h = i->cache->a_height > 0 ? (
236 (i->cache->height + i->y % pixel_per_line_i + pixel_per_line_i - 1) /
237 pixel_per_line_i) : 0;
238
239 int sx = i->sx / pixel_per_char_i;
240 int sy = i->sy / pixel_per_line_i;
241
242 int sw = (i->width + i->sx % pixel_per_char_i + pixel_per_char_i - 1) /
243 pixel_per_char_i;
244 int sh = (i->height + i->sy % pixel_per_line_i + pixel_per_line_i - 1) /
245 pixel_per_line_i;
246
247 #if 0
248 fprintf(stderr,"file %s x %d y %d w %d h %d sx %d sy %d sw %d sh %d (ppc %d ppl %d)\n",
249 i->cache->file,
250 x, y, w, h, sx, sy, sw, sh,
251 pixel_per_char_i, pixel_per_line_i);
252 #endif
253
254
255 if (enable_inline_image == INLINE_IMG_SIXEL) {
256 put_image_sixel(url, x, y, w, h, sx, sy, sw, sh, n_terminal_image);
257 } else if (enable_inline_image == INLINE_IMG_OSC5379) {
258 put_image_osc5379(url, x, y, w, h, sx, sy, sw, sh);
259 } else if (enable_inline_image == INLINE_IMG_ITERM2) {
260 put_image_iterm2(url, x, y, sw, sh);
261 } else if (enable_inline_image == INLINE_IMG_KITTY) {
262 put_image_kitty(url, x, y, i->width, i->height, i->sx, i->sy, sw * pixel_per_char, sh * pixel_per_line_i, sw, sh);
263 }
264
265 continue ;
266 }
267
268 if (!(i->cache->loaded & IMG_FLAG_LOADED &&
269 i->width > 0 && i->height > 0))
270 continue;
271 if (!(Imgdisplay_rf && Imgdisplay_wf)) {
272 if (!openImgdisplay())
273 return;
274 }
275 if (i->cache->index > 0) {
276 i->cache->index *= -1;
277 fputs("0;", Imgdisplay_wf); /* DrawImage() */
278 }
279 else
280 fputs("1;", Imgdisplay_wf); /* DrawImage(redraw) */
281 sprintf(buf, "%d;%d;%d;%d;%d;%d;%d;%d;%d;",
282 (-i->cache->index - 1) % MAX_IMAGE + 1, i->x, i->y,
283 (i->cache->width > 0) ? i->cache->width : 0,
284 (i->cache->height > 0) ? i->cache->height : 0,
285 i->sx, i->sy, i->width, i->height);
286 fputs(buf, Imgdisplay_wf);
287 fputs(i->cache->file, Imgdisplay_wf);
288 fputs("\n", Imgdisplay_wf);
289 draw = TRUE;
290 }
291
292 if (!enable_inline_image) {
293 if (!draw)
294 return;
295 syncImage();
296 }
297 else
298 n_terminal_image = 0;
299
300 touch_cursor();
301 refresh();
302 }
303
304 void
clearImage()305 clearImage()
306 {
307 static char buf[64];
308 int j;
309 TerminalImage *i;
310
311 if (!activeImage)
312 return;
313 if (!n_terminal_image)
314 return;
315 if (!Imgdisplay_wf) {
316 n_terminal_image = 0;
317 return;
318 }
319 for (j = 0; j < n_terminal_image; j++) {
320 i = &terminal_image[j];
321 if (!(i->cache->loaded & IMG_FLAG_LOADED &&
322 i->width > 0 && i->height > 0))
323 continue;
324 sprintf(buf, "6;%d;%d;%d;%d\n", i->x, i->y, i->width, i->height);
325 fputs(buf, Imgdisplay_wf);
326 }
327 syncImage();
328 n_terminal_image = 0;
329 }
330
331 /* load image */
332
333 #ifndef MAX_LOAD_IMAGE
334 #define MAX_LOAD_IMAGE 8
335 #endif
336 static int n_load_image = 0;
337 static Hash_sv *image_hash = NULL;
338 static Hash_sv *image_file = NULL;
339 static GeneralList *image_list = NULL;
340 static ImageCache **image_cache = NULL;
341 static Buffer *image_buffer = NULL;
342
343 void
deleteImage(Buffer * buf)344 deleteImage(Buffer *buf)
345 {
346 AnchorList *al;
347 Anchor *a;
348 int i;
349
350 if (!buf)
351 return;
352 al = buf->img;
353 if (!al)
354 return;
355 for (i = 0, a = al->anchors; i < al->nanchor; i++, a++) {
356 if (a->image && a->image->cache &&
357 a->image->cache->loaded != IMG_FLAG_UNLOADED &&
358 !(a->image->cache->loaded & IMG_FLAG_DONT_REMOVE) &&
359 a->image->cache->index < 0)
360 unlink(a->image->cache->file);
361 }
362 loadImage(NULL, IMG_FLAG_STOP);
363 }
364
365 void
getAllImage(Buffer * buf)366 getAllImage(Buffer *buf)
367 {
368 AnchorList *al;
369 Anchor *a;
370 ParsedURL *current;
371 int i;
372
373 image_buffer = buf;
374 if (!buf)
375 return;
376 buf->image_loaded = TRUE;
377 al = buf->img;
378 if (!al)
379 return;
380 current = baseURL(buf);
381 for (i = 0, a = al->anchors; i < al->nanchor; i++, a++) {
382 if (a->image) {
383 a->image->cache = getImage(a->image, current, buf->image_flag);
384 if (a->image->cache &&
385 a->image->cache->loaded == IMG_FLAG_UNLOADED)
386 buf->image_loaded = FALSE;
387 }
388 }
389 }
390
391 void
showImageProgress(Buffer * buf)392 showImageProgress(Buffer *buf)
393 {
394 AnchorList *al;
395 Anchor *a;
396 int i, l, n;
397
398 if (!buf)
399 return;
400 al = buf->img;
401 if (!al)
402 return;
403 for (i = 0, l = 0, n = 0, a = al->anchors; i < al->nanchor; i++, a++) {
404 if (a->image && a->hseq >= 0) {
405 n++;
406 if (a->image->cache && a->image->cache->loaded & IMG_FLAG_LOADED)
407 l++;
408 }
409 }
410 if (n) {
411 if (enable_inline_image && n == l)
412 drawImage();
413 message(Sprintf("%d/%d images loaded", l, n)->ptr,
414 buf->cursorX + buf->rootX, buf->cursorY + buf->rootY);
415 refresh();
416 }
417 }
418
419 void
loadImage(Buffer * buf,int flag)420 loadImage(Buffer *buf, int flag)
421 {
422 ImageCache *cache;
423 struct stat st;
424 int i, draw = FALSE;
425 /* int wait_st; */
426 #ifdef DONT_CALL_GC_AFTER_FORK
427 char *loadargs[7];
428 #endif
429
430 if (maxLoadImage > MAX_LOAD_IMAGE)
431 maxLoadImage = MAX_LOAD_IMAGE;
432 else if (maxLoadImage < 1)
433 maxLoadImage = 1;
434 if (n_load_image == 0)
435 n_load_image = maxLoadImage;
436 if (!image_cache) {
437 image_cache = New_N(ImageCache *, MAX_LOAD_IMAGE);
438 bzero(image_cache, sizeof(ImageCache *) * MAX_LOAD_IMAGE);
439 }
440 for (i = 0; i < n_load_image; i++) {
441 cache = image_cache[i];
442 if (!cache || !cache->touch)
443 continue;
444 if (lstat(cache->touch, &st))
445 continue;
446 if (cache->pid) {
447 kill(cache->pid, SIGKILL);
448 /*
449 * #ifdef HAVE_WAITPID
450 * waitpid(cache->pid, &wait_st, 0);
451 * #else
452 * wait(&wait_st);
453 * #endif
454 */
455 cache->pid = 0;
456 }
457 if (!stat(cache->file, &st)) {
458 cache->loaded = IMG_FLAG_LOADED;
459 if (getImageSize(cache)) {
460 if (image_buffer)
461 image_buffer->need_reshape = TRUE;
462 }
463 draw = TRUE;
464 }
465 else
466 cache->loaded = IMG_FLAG_ERROR;
467 unlink(cache->touch);
468 image_cache[i] = NULL;
469 }
470
471 for (i = (buf != image_buffer) ? 0 : maxLoadImage; i < n_load_image; i++) {
472 cache = image_cache[i];
473 if (!cache || !cache->touch)
474 continue;
475 if (cache->pid) {
476 kill(cache->pid, SIGKILL);
477 /*
478 * #ifdef HAVE_WAITPID
479 * waitpid(cache->pid, &wait_st, 0);
480 * #else
481 * wait(&wait_st);
482 * #endif
483 */
484 cache->pid = 0;
485 }
486 /*TODO make sure removing this didn't break anything
487 unlink(cache->touch);
488 */
489 image_cache[i] = NULL;
490 }
491
492 if (flag == IMG_FLAG_STOP) {
493 image_list = NULL;
494 image_file = NULL;
495 n_load_image = maxLoadImage;
496 image_buffer = NULL;
497 return;
498 }
499
500 if (draw && image_buffer) {
501 if (!enable_inline_image)
502 drawImage();
503 showImageProgress(image_buffer);
504 }
505
506 image_buffer = buf;
507
508 if (!image_list)
509 return;
510 for (i = 0; i < n_load_image; i++) {
511 if (image_cache[i])
512 continue;
513 while (1) {
514 cache = (ImageCache *) popValue(image_list);
515 if (!cache) {
516 for (i = 0; i < n_load_image; i++) {
517 if (image_cache[i])
518 return;
519 }
520 image_list = NULL;
521 image_file = NULL;
522 if (image_buffer)
523 displayBuffer(image_buffer, B_NORMAL);
524 return;
525 }
526 if (cache->loaded == IMG_FLAG_UNLOADED)
527 break;
528 }
529 image_cache[i] = cache;
530 if (!cache->touch) {
531 continue;
532 }
533
534 flush_tty();
535 #ifdef DONT_CALL_GC_AFTER_FORK
536 loadargs[0] = MyProgramName;
537 loadargs[1] = "-$$getimage";
538 loadargs[2] = conv_to_system(cache->url);
539 loadargs[3] = conv_to_system(parsedURL2Str(cache->current)->ptr);
540 loadargs[4] = cache->file;
541 loadargs[5] = cache->touch;
542 loadargs[6] = NULL;
543 if ((cache->pid = fork()) == 0) {
544 setup_child(FALSE, 0, -1);
545 execvp(MyProgramName, loadargs);
546 exit(1);
547 }
548 else if (cache->pid < 0) {
549 cache->pid = 0;
550 return;
551 }
552 #else /* !DONT_CALL_GC_AFTER_FORK */
553 if ((cache->pid = fork()) == 0) {
554 Buffer *b;
555 /*
556 * setup_child(TRUE, 0, -1);
557 */
558 setup_child(FALSE, 0, -1);
559 image_source = cache->file;
560 b = loadGeneralFile(cache->url, cache->current, NULL, 0, NULL);
561 /* TODO make sure removing this didn't break anything
562 if (!b || !b->real_type || strncasecmp(b->real_type, "image/", 6))
563 unlink(cache->file);
564 */
565 #if defined(HAVE_SYMLINK) && defined(HAVE_LSTAT)
566 symlink(cache->file, cache->touch);
567 #else
568 {
569 FILE *f = fopen(cache->touch, "w");
570 if (f)
571 fclose(f);
572 }
573 #endif
574 exit(0);
575 }
576 else if (cache->pid < 0) {
577 cache->pid = 0;
578 return;
579 }
580 #endif /* !DONT_CALL_GC_AFTER_FORK */
581 }
582 }
583
584 ImageCache *
getImage(Image * image,ParsedURL * current,int flag)585 getImage(Image * image, ParsedURL *current, int flag)
586 {
587 Str key = NULL;
588 ImageCache *cache;
589
590 if (!activeImage)
591 return NULL;
592 if (!image_hash)
593 image_hash = newHash_sv(100);
594 if (image->cache)
595 cache = image->cache;
596 else {
597 key = Sprintf("%d;%d;%s", image->width, image->height, image->url);
598 cache = (ImageCache *) getHash_sv(image_hash, key->ptr, NULL);
599 }
600 if (cache && cache->index && abs(cache->index) <= image_index - MAX_IMAGE) {
601 struct stat st;
602 if (stat(cache->file, &st))
603 cache->loaded = IMG_FLAG_UNLOADED;
604 cache->index = 0;
605 }
606
607 if (!cache) {
608 if (flag == IMG_FLAG_SKIP)
609 return NULL;
610
611 cache = New(ImageCache);
612 cache->url = image->url;
613 cache->current = current;
614 cache->file = tmpfname(TMPF_DFL, image->ext)->ptr;
615 cache->pid = 0;
616 cache->index = 0;
617 cache->loaded = IMG_FLAG_UNLOADED;
618 if (enable_inline_image == INLINE_IMG_OSC5379) {
619 if (image->width > 0 && image->width % pixel_per_char_i > 0)
620 image->width += (pixel_per_char_i - image->width % pixel_per_char_i);
621
622 if (image->height > 0 && image->height % pixel_per_line_i > 0)
623 image->height += (pixel_per_line_i - image->height % pixel_per_line_i);
624 if (image->height > 0 && image->width > 0) {
625 cache->loaded = IMG_FLAG_LOADED;
626 }
627 }
628 if (cache->loaded == IMG_FLAG_UNLOADED) {
629 cache->touch = tmpfname(TMPF_DFL, NULL)->ptr;
630 }
631 else {
632 cache->touch = NULL;
633 }
634
635 cache->width = image->width ;
636 cache->height = image->height ;
637 cache->a_width = image->width;
638 cache->a_height = image->height;
639 putHash_sv(image_hash, key->ptr, (void *)cache);
640 }
641 if (flag != IMG_FLAG_SKIP) {
642 if (cache->loaded == IMG_FLAG_UNLOADED) {
643 if (!image_file)
644 image_file = newHash_sv(100);
645 if (!getHash_sv(image_file, cache->file, NULL)) {
646 putHash_sv(image_file, cache->file, (void *)cache);
647 if (!image_list)
648 image_list = newGeneralList();
649 pushValue(image_list, (void *)cache);
650 }
651 }
652 if (!cache->index)
653 cache->index = ++image_index;
654 }
655 if (cache->loaded & IMG_FLAG_LOADED)
656 getImageSize(cache);
657 return cache;
658 }
659
660 static int
parseImageHeader(char * path,u_int * width,u_int * height)661 parseImageHeader(char *path, u_int *width, u_int *height)
662 {
663 FILE *fp;
664 u_char buf[8];
665
666 if (!(fp = fopen(path, "r"))) return FALSE;
667
668 if (fread(buf, 1, 2, fp) != 2) goto error;
669
670 if (memcmp(buf, "\xff\xd8", 2) == 0) {
671 /* JPEG */
672 if (fseek(fp, 2, SEEK_CUR) < 0) goto error; /* 0xffe0 */
673 while (fread(buf, 1, 2, fp) == 2) {
674 size_t len = ((buf[0] << 8) | buf[1]) - 2;
675 if (fseek(fp, len, SEEK_CUR) < 0) goto error;
676 if (fread(buf, 1, 2, fp) == 2 &&
677 /* SOF0 or SOF2 */
678 (memcmp(buf, "\xff\xc0", 2) == 0 || memcmp(buf, "\xff\xc2", 2) == 0)) {
679 fseek(fp, 3, SEEK_CUR);
680 if (fread(buf, 1, 2, fp) == 2) {
681 *height = (buf[0] << 8) | buf[1];
682 if (fread(buf, 1, 2, fp) == 2) {
683 *width = (buf[0] << 8) | buf[1];
684 goto success;
685 }
686 }
687 break;
688 }
689 }
690 goto error;
691 }
692
693 if (fread(buf + 2, 1, 1, fp) != 1) goto error;
694
695 if (memcmp(buf, "GIF", 3) == 0) {
696 /* GIF */
697 if (fseek(fp, 3, SEEK_CUR) < 0) goto error;
698 if (fread(buf, 1, 2, fp) == 2) {
699 *width = (buf[1] << 8) | buf[0];
700 if (fread(buf, 1, 2, fp) == 2) {
701 *height = (buf[1] << 8) | buf[0];
702 goto success;
703 }
704 }
705 goto error;
706 }
707
708 if (fread(buf + 3, 1, 5, fp) != 5) goto error;
709
710 if (memcmp(buf, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0) {
711 /* PNG */
712 if (fseek(fp, 8, SEEK_CUR) < 0) goto error;
713 if (fread(buf, 1, 4, fp) == 4) {
714 *width = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
715 if (fread(buf, 1, 4, fp) == 4) {
716 *height = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
717 goto success;
718 }
719 }
720 goto error;
721 }
722
723 error:
724 fclose(fp);
725 return FALSE;
726
727 success:
728 fclose(fp);
729 return TRUE;
730 }
731
732 int
getImageSize(ImageCache * cache)733 getImageSize(ImageCache * cache)
734 {
735 Str tmp;
736 FILE *f;
737 int w = 0, h = 0;
738
739 if (!activeImage)
740 return FALSE;
741 if (!cache || !(cache->loaded & IMG_FLAG_LOADED) ||
742 (cache->width > 0 && cache->height > 0))
743 return FALSE;
744
745 if (parseImageHeader(cache->file, &w, &h))
746 goto got_image_size;
747
748 tmp = Strnew();
749 if (!strchr(Imgdisplay, '/'))
750 Strcat_m_charp(tmp, w3m_auxbin_dir(), "/", NULL);
751 Strcat_m_charp(tmp, Imgdisplay, " -size ", shell_quote(cache->file), NULL);
752 f = popen(tmp->ptr, "r");
753 if (!f)
754 return FALSE;
755 while (fscanf(f, "%d %d", &w, &h) < 0) {
756 if (feof(f))
757 break;
758 }
759 pclose(f);
760
761 if (!(w > 0 && h > 0))
762 return FALSE;
763
764 got_image_size:
765 w = (int)(w * image_scale / 100 + 0.5);
766 if (w == 0)
767 w = 1;
768 h = (int)(h * image_scale / 100 + 0.5);
769 if (h == 0)
770 h = 1;
771 if (cache->width < 0 && cache->height < 0) {
772 cache->width = (w > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : w;
773 cache->height = (h > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : h;
774 }
775 else if (cache->width < 0) {
776 int tmp = (int)((double)cache->height * w / h + 0.5);
777 cache->a_width = cache->width = (tmp > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : tmp;
778 }
779 else if (cache->height < 0) {
780 int tmp = (int)((double)cache->width * h / w + 0.5);
781 cache->a_height = cache->height = (tmp > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : tmp;
782 }
783 if (cache->width == 0)
784 cache->width = 1;
785 if (cache->height == 0)
786 cache->height = 1;
787 tmp = Sprintf("%d;%d;%s", cache->width, cache->height, cache->url);
788 putHash_sv(image_hash, tmp->ptr, (void *)cache);
789 return TRUE;
790 }
791 #endif
792