gif.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <draw.h>
  5. #include <event.h>
  6. #include "imagefile.h"
  7. int cflag = 0;
  8. int dflag = 0;
  9. int eflag = 0;
  10. int nineflag = 0;
  11. int threeflag = 0;
  12. int output = 0;
  13. ulong outchan = CMAP8;
  14. Image **allims;
  15. Image **allmasks;
  16. int which;
  17. int defaultcolor = 1;
  18. enum{
  19. Border = 2,
  20. Edge = 5
  21. };
  22. char *show(int, char*);
  23. Rectangle
  24. imager(void)
  25. {
  26. Rectangle r;
  27. if(allims==nil || allims[0]==nil)
  28. return screen->r;
  29. r = insetrect(screen->clipr, Edge+Border);
  30. r.max.x = r.min.x+Dx(allims[0]->r);
  31. r.max.y = r.min.y+Dy(allims[0]->r);
  32. return r;
  33. }
  34. void
  35. eresized(int new)
  36. {
  37. Rectangle r;
  38. if(new && getwindow(display, Refnone) < 0){
  39. fprint(2, "gif: can't reattach to window\n");
  40. exits("resize");
  41. }
  42. if(allims==nil || allims[which]==nil)
  43. return;
  44. r = imager();
  45. border(screen, r, -Border, nil, ZP);
  46. r.min.x += allims[which]->r.min.x - allims[0]->r.min.x;
  47. r.min.y += allims[which]->r.min.y - allims[0]->r.min.y;
  48. drawop(screen, r, allims[which], allmasks[which], allims[which]->r.min, S);
  49. flushimage(display, 1);
  50. }
  51. void
  52. main(int argc, char *argv[])
  53. {
  54. int fd, i;
  55. char *err;
  56. ARGBEGIN{
  57. case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
  58. threeflag++;
  59. /* fall through */
  60. case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
  61. cflag++;
  62. dflag++;
  63. output++;
  64. defaultcolor = 0;
  65. outchan = RGB24;
  66. break;
  67. case 'c': /* produce encoded, compressed, bitmap file; no display by default */
  68. cflag++;
  69. dflag++;
  70. output++;
  71. if(defaultcolor)
  72. outchan = CMAP8;
  73. break;
  74. case 'd': /* suppress display of image */
  75. dflag++;
  76. break;
  77. case 'e': /* disable floyd-steinberg error diffusion */
  78. eflag++;
  79. break;
  80. case 'k': /* force black and white */
  81. defaultcolor = 0;
  82. outchan = GREY8;
  83. break;
  84. case 'v': /* force RGBV */
  85. defaultcolor = 0;
  86. outchan = CMAP8;
  87. break;
  88. case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
  89. nineflag++;
  90. dflag++;
  91. output++;
  92. if(defaultcolor)
  93. outchan = CMAP8;
  94. break;
  95. default:
  96. fprint(2, "usage: gif -39cdektv [file.gif ...]\n");
  97. exits("usage");
  98. }ARGEND;
  99. err = nil;
  100. if(argc == 0)
  101. err = show(0, "<stdin>");
  102. else{
  103. for(i=0; i<argc; i++){
  104. fd = open(argv[i], OREAD);
  105. if(fd < 0){
  106. fprint(2, "gif: can't open %s: %r\n", argv[i]);
  107. err = "open";
  108. }else{
  109. err = show(fd, argv[i]);
  110. close(fd);
  111. }
  112. if(output && argc>1 && err==nil){
  113. fprint(2, "gif: exiting after one file\n");
  114. break;
  115. }
  116. }
  117. }
  118. exits(err);
  119. }
  120. Image*
  121. transparency(Rawimage *r, char *name)
  122. {
  123. Image *i;
  124. int j, index;
  125. uchar *pic, *mpic, *mask;
  126. if((r->gifflags&TRANSP) == 0)
  127. return nil;
  128. i = allocimage(display, r->r, GREY8, 0, 0);
  129. if(i == nil){
  130. fprint(2, "gif: allocimage for mask of %s failed: %r\n", name);
  131. return nil;
  132. }
  133. pic = r->chans[0];
  134. mask = malloc(r->chanlen);
  135. if(mask == nil){
  136. fprint(2, "gif: malloc for mask of %s failed: %r\n", name);
  137. freeimage(i);
  138. return nil;
  139. }
  140. index = r->giftrindex;
  141. mpic = mask;
  142. for(j=0; j<r->chanlen; j++)
  143. if(*pic++ == index)
  144. *mpic++ = 0;
  145. else
  146. *mpic++ = 0xFF;
  147. if(loadimage(i, i->r, mask, r->chanlen) < 0){
  148. fprint(2, "gif: loadimage for mask of %s failed: %r\n", name);
  149. free(mask);
  150. freeimage(i);
  151. return nil;
  152. }
  153. free(mask);
  154. return i;
  155. }
  156. /* interleave alpha values of 0xFF in data stream. alpha value comes first, then b g r */
  157. uchar*
  158. expand(uchar *u, int chanlen, int nchan)
  159. {
  160. int j, k;
  161. uchar *v, *up, *vp;
  162. v = malloc(chanlen*(nchan+1));
  163. if(v == nil){
  164. fprint(2, "gif: malloc fails: %r\n");
  165. exits("malloc");
  166. }
  167. up = u;
  168. vp = v;
  169. for(j=0; j<chanlen; j++){
  170. *vp++ = 0xFF;
  171. for(k=0; k<nchan; k++)
  172. *vp++ = *up++;
  173. }
  174. return v;
  175. }
  176. void
  177. addalpha(Rawimage *i)
  178. {
  179. char buf[32];
  180. switch(outchan){
  181. case CMAP8:
  182. i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
  183. i->chanlen = 2*(i->chanlen/1);
  184. i->chandesc = CRGBVA16;
  185. outchan = CHAN2(CMap, 8, CAlpha, 8);
  186. break;
  187. case GREY8:
  188. i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
  189. i->chanlen = 2*(i->chanlen/1);
  190. i->chandesc = CYA16;
  191. outchan = CHAN2(CGrey, 8, CAlpha, 8);
  192. break;
  193. case RGB24:
  194. i->chans[0] = expand(i->chans[0], i->chanlen/3, 3);
  195. i->chanlen = 4*(i->chanlen/3);
  196. i->chandesc = CRGBA32;
  197. outchan = RGBA32;
  198. break;
  199. default:
  200. chantostr(buf, outchan);
  201. fprint(2, "gif: can't add alpha to type %s\n", buf);
  202. exits("err");
  203. }
  204. }
  205. /*
  206. * Called only when writing output. If the output is RGBA32,
  207. * we must write four bytes per pixel instead of two.
  208. * There's always at least two: data plus alpha.
  209. * r is used only for reference; the image is already in c.
  210. */
  211. void
  212. whiteout(Rawimage *r, Rawimage *c)
  213. {
  214. int i, trindex;
  215. uchar *rp, *cp;
  216. rp = r->chans[0];
  217. cp = c->chans[0];
  218. trindex = r->giftrindex;
  219. if(outchan == RGBA32)
  220. for(i=0; i<r->chanlen; i++){
  221. if(*rp == trindex){
  222. *cp++ = 0x00;
  223. *cp++ = 0xFF;
  224. *cp++ = 0xFF;
  225. *cp++ = 0xFF;
  226. }else{
  227. *cp++ = 0xFF;
  228. cp += 3;
  229. }
  230. rp++;
  231. }
  232. else
  233. for(i=0; i<r->chanlen; i++){
  234. if(*rp == trindex){
  235. *cp++ = 0x00;
  236. *cp++ = 0xFF;
  237. }else{
  238. *cp++ = 0xFF;
  239. cp++;
  240. }
  241. rp++;
  242. }
  243. }
  244. int
  245. init(void)
  246. {
  247. static int inited;
  248. if(inited == 0){
  249. if(initdraw(0, 0, 0) < 0){
  250. fprint(2, "gif: initdraw failed: %r\n");
  251. return -1;
  252. }
  253. einit(Ekeyboard|Emouse);
  254. inited++;
  255. }
  256. return 1;
  257. }
  258. char*
  259. show(int fd, char *name)
  260. {
  261. Rawimage **images, **rgbv;
  262. Image **ims, **masks;
  263. int j, k, n, ch, nloop, loopcount, dt;
  264. char *err;
  265. char buf[32];
  266. err = nil;
  267. images = readgif(fd, CRGB);
  268. if(images == nil){
  269. fprint(2, "gif: decode %s failed: %r\n", name);
  270. return "decode";
  271. }
  272. for(n=0; images[n]; n++)
  273. ;
  274. ims = malloc((n+1)*sizeof(Image*));
  275. masks = malloc((n+1)*sizeof(Image*));
  276. rgbv = malloc((n+1)*sizeof(Rawimage*));
  277. if(masks==nil || rgbv==nil || ims==nil){
  278. fprint(2, "gif: malloc of masks for %s failed: %r\n", name);
  279. err = "malloc";
  280. goto Return;
  281. }
  282. memset(masks, 0, (n+1)*sizeof(Image*));
  283. memset(ims, 0, (n+1)*sizeof(Image*));
  284. memset(rgbv, 0, (n+1)*sizeof(Rawimage*));
  285. if(!dflag){
  286. if(init() < 0){
  287. err = "initdraw";
  288. goto Return;
  289. }
  290. if(defaultcolor && screen->depth>8)
  291. outchan = RGB24;
  292. }
  293. for(k=0; k<n; k++){
  294. if(outchan == CMAP8)
  295. rgbv[k] = torgbv(images[k], !eflag);
  296. else{
  297. if(outchan==GREY8 || (images[k]->chandesc==CY && threeflag==0))
  298. rgbv[k] = totruecolor(images[k], CY);
  299. else
  300. rgbv[k] = totruecolor(images[k], CRGB24);
  301. }
  302. if(rgbv[k] == nil){
  303. fprint(2, "gif: converting %s to local format failed: %r\n", name);
  304. err = "torgbv";
  305. goto Return;
  306. }
  307. if(!dflag){
  308. masks[k] = transparency(images[k], name);
  309. if(rgbv[k]->chandesc == CY)
  310. ims[k] = allocimage(display, rgbv[k]->r, GREY8, 0, 0);
  311. else
  312. ims[k] = allocimage(display, rgbv[k]->r, outchan, 0, 0);
  313. if(ims[k] == nil){
  314. fprint(2, "gif: allocimage %s failed: %r\n", name);
  315. err = "allocimage";
  316. goto Return;
  317. }
  318. if(loadimage(ims[k], ims[k]->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){
  319. fprint(2, "gif: loadimage %s failed: %r\n", name);
  320. err = "loadimage";
  321. goto Return;
  322. }
  323. }
  324. }
  325. allims = ims;
  326. allmasks = masks;
  327. loopcount = images[0]->gifloopcount;
  328. if(!dflag){
  329. nloop = 0;
  330. do{
  331. for(k=0; k<n; k++){
  332. which = k;
  333. eresized(0);
  334. dt = images[k]->gifdelay*10;
  335. if(dt < 50)
  336. dt = 50;
  337. while(n==1 || ecankbd()){
  338. if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) /* an odd, democratic list */
  339. exits(nil);
  340. if(ch == '\n')
  341. goto Out;
  342. }sleep(dt);
  343. }
  344. /* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape's rule)*/
  345. }while(loopcount==0 || ++nloop<loopcount);
  346. /* loop count has run out */
  347. ekbd();
  348. Out:
  349. drawop(screen, screen->clipr, display->white, nil, ZP, S);
  350. }
  351. if(n>1 && output)
  352. fprint(2, "gif: warning: only writing first image in %d-image GIF %s\n", n, name);
  353. if(nineflag){
  354. if(images[0]->gifflags&TRANSP){
  355. addalpha(rgbv[0]);
  356. whiteout(images[0], rgbv[0]);
  357. }
  358. chantostr(buf, outchan);
  359. print("%11s %11d %11d %11d %11d ", buf,
  360. rgbv[0]->r.min.x, rgbv[0]->r.min.y, rgbv[0]->r.max.x, rgbv[0]->r.max.y);
  361. if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv[0]->chanlen){
  362. fprint(2, "gif: %s: write error %r\n", name);
  363. return "write";
  364. }
  365. }else if(cflag){
  366. if(images[0]->gifflags&TRANSP){
  367. addalpha(rgbv[0]);
  368. whiteout(images[0], rgbv[0]);
  369. }
  370. if(writerawimage(1, rgbv[0]) < 0){
  371. fprint(2, "gif: %s: write error: %r\n", name);
  372. return "write";
  373. }
  374. }
  375. Return:
  376. allims = nil;
  377. allmasks = nil;
  378. for(k=0; images[k]; k++){
  379. for(j=0; j<images[k]->nchans; j++)
  380. free(images[k]->chans[j]);
  381. free(images[k]->cmap);
  382. if(rgbv[k])
  383. free(rgbv[k]->chans[0]);
  384. freeimage(ims[k]);
  385. freeimage(masks[k]);
  386. free(images[k]);
  387. free(rgbv[k]);
  388. }
  389. free(images);
  390. free(masks);
  391. free(ims);
  392. return err;
  393. }