aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/jpg/jpg.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/jpg/jpg.c')
-rw-r--r--src/cmd/jpg/jpg.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/src/cmd/jpg/jpg.c b/src/cmd/jpg/jpg.c
new file mode 100644
index 00000000..cf438a93
--- /dev/null
+++ b/src/cmd/jpg/jpg.c
@@ -0,0 +1,342 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int jflag = 0;
+int fflag = 0;
+int Fflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int colorspace = CYCbCr; /* default for 8-bit displays: combine color rotation with dither */
+int output = 0;
+ulong outchan = CMAP8;
+Image *image;
+int defaultcolor = 1;
+
+enum{
+ Border = 2,
+ Edge = 5
+};
+
+char *show(int, char*, int);
+
+void
+eresized(int new)
+{
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0){
+ fprint(2, "jpg: can't reattach to window\n");
+ exits("resize");
+ }
+ if(image == nil)
+ return;
+ r = insetrect(screen->clipr, Edge+Border);
+ r.max.x = r.min.x+Dx(image->r);
+ r.max.y = r.min.y+Dy(image->r);
+ border(screen, r, -Border, nil, ZP);
+ draw(screen, r, image, nil, image->r.min);
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, i, yflag;
+ char *err;
+ char buf[12+1];
+
+ yflag = 0;
+ ARGBEGIN{
+ case 'c': /* produce encoded, compressed, bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ case 'd': /* suppress display of image */
+ dflag++;
+ break;
+ case 'e': /* disable floyd-steinberg error diffusion */
+ eflag++;
+ break;
+ case 'F':
+ Fflag++; /* make a movie */
+ fflag++; /* merge two fields per image */
+ break;
+ case 'f':
+ fflag++; /* merge two fields per image */
+ break;
+ case 'J': /* decode jpeg only; no display or remap (for debugging, etc.) */
+ jflag++;
+ break;
+ case 'k': /* force black and white */
+ defaultcolor = 0;
+ outchan = GREY8;
+ break;
+ case 'r':
+ colorspace = CRGB;
+ break;
+ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
+ threeflag++;
+ /* fall through */
+ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ defaultcolor = 0;
+ outchan = RGB24;
+ break;
+ case 'v': /* force RGBV */
+ defaultcolor = 0;
+ outchan = CMAP8;
+ break;
+ case 'y': /* leave it in CYCbCr; for debugging only */
+ yflag = 1;
+ colorspace = CYCbCr;
+ break;
+ case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
+ nineflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ default:
+ fprint(2, "usage: jpg -39cdefFkJrtv [file.jpg ...]\n");
+ exits("usage");
+ }ARGEND;
+
+ if(yflag==0 && dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */
+ fd = open("/dev/screen", OREAD);
+ if(fd > 0){
+ buf[12] = '\0';
+ if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8)
+ colorspace = CRGB;
+ close(fd);
+ }
+ }
+
+ err = nil;
+ if(argc == 0)
+ err = show(0, "<stdin>", outchan);
+ else{
+ for(i=0; i<argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd < 0){
+ fprint(2, "jpg: can't open %s: %r\n", argv[i]);
+ err = "open";
+ }else{
+ err = show(fd, argv[i], outchan);
+ close(fd);
+ }
+ if((nineflag || cflag) && argc>1 && err==nil){
+ fprint(2, "jpg: exiting after one file\n");
+ break;
+ }
+ }
+ }
+ exits(err);
+}
+
+Rawimage**
+vidmerge(Rawimage **aa1, Rawimage **aa2)
+{
+ Rawimage **aao, *ao, *a1, *a2;
+ int i, c, row, col;
+
+ aao = nil;
+ for (i = 0; aa1[i]; i++) {
+
+ a1 = aa1[i];
+ a2 = aa2[i];
+ if (a2 == nil){
+ fprint(2, "jpg: vidmerge: unequal lengths\n");
+ return nil;
+ }
+ aao = realloc(aao, (i+2)*sizeof(Rawimage *));
+ if (aao == nil){
+ fprint(2, "jpg: vidmerge: realloc\n");
+ return nil;
+ }
+ aao[i+1] = nil;
+ ao = aao[i] = malloc(sizeof(Rawimage));
+ if (ao == nil){
+ fprint(2, "jpg: vidmerge: realloc\n");
+ return nil;
+ }
+ memcpy(ao, a1, sizeof(Rawimage));
+ if (!eqrect(a1->r , a2->r)){
+ fprint(2, "jpg: vidmerge: rects different in img %d\n", i);
+ return nil;
+ }
+ if (a1->cmaplen != a2->cmaplen){
+ fprint(2, "jpg: vidmerge: cmaplen different in img %d\n", i);
+ return nil;
+ }
+ if (a1->nchans != a2->nchans){
+ fprint(2, "jpg: vidmerge: nchans different in img %d\n", i);
+ return nil;
+ }
+ if (a1->fields != a2->fields){
+ fprint(2, "jpg: vidmerge: fields different in img %d\n", i);
+ return nil;
+ }
+ ao->r.max.y += Dy(ao->r);
+ ao->chanlen += ao->chanlen;
+ if (ao->chanlen != Dx(ao->r)*Dy(ao->r)){
+ fprint(2, "jpg: vidmerge: chanlen wrong %d != %d*%d\n",
+ ao->chanlen, Dx(ao->r), Dy(ao->r));
+ return nil;
+ }
+ row = Dx(a1->r);
+ for (c = 0; c < ao->nchans; c++) {
+ uchar *po, *p1, *p2;
+
+ ao->chans[c] = malloc(ao->chanlen);
+ po = ao->chans[c];
+ p1 = a1->chans[c];
+ p2 = a2->chans[c];
+ for (col = 0; col < Dy(a1->r); col++) {
+ memcpy(po, p1, row);
+ po += row, p1 += row;
+ memcpy(po, p2, row);
+ po += row, p2 += row;
+ }
+ free(a1->chans[c]);
+ free(a2->chans[c]);
+ }
+ if(a2->cmap != nil)
+ free(a2->cmap);
+ free(a1);
+ free(a2);
+ }
+ if (aa2[i] != nil)
+ fprint(2, "jpg: vidmerge: unequal lengths\n");
+ free(aa1);
+ free(aa2);
+ return aao;
+}
+
+char*
+show(int fd, char *name, int outc)
+{
+ Rawimage **array, *r, *c;
+ static int inited;
+ Image *i;
+ int j, ch, outchan;
+ Biobuf b;
+ char buf[32];
+
+ if(Binit(&b, fd, OREAD) < 0)
+ return nil;
+ outchan = outc;
+rpt: array = Breadjpg(&b, colorspace);
+ if(array == nil || array[0]==nil){
+ fprint(2, "jpg: decode %s failed: %r\n", name);
+ return "decode";
+ }
+ if (fflag) {
+ Rawimage **a;
+
+ a = Breadjpg(&b, colorspace);
+ if(a == nil || a[0]==nil){
+ fprint(2, "jpg: decode %s-2 failed: %r\n", name);
+ return "decode";
+ }
+ array = vidmerge(a, array);
+ } else
+ Bterm(&b);
+
+ r = array[0];
+ c = nil;
+ if(jflag)
+ goto Return;
+ if(!dflag && !inited){
+ if(initdraw(0, 0, 0) < 0){
+ fprint(2, "jpg: initdraw failed: %r\n");
+ return "initdraw";
+ }
+ if(Fflag == 0)
+ einit(Ekeyboard|Emouse);
+ if(defaultcolor && screen->depth>8 && outchan==CMAP8)
+ outchan = RGB24;
+ inited++;
+ }
+ if(outchan == CMAP8)
+ c = torgbv(r, !eflag);
+ else{
+ if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){
+ c = totruecolor(r, CY);
+ outchan = GREY8;
+ }else
+ c = totruecolor(r, CRGB24);
+ }
+ if(c == nil){
+ fprint(2, "jpg: conversion of %s failed: %r\n", name);
+ return "torgbv";
+ }
+ if(!dflag){
+ if(c->chandesc == CY)
+ i = allocimage(display, c->r, GREY8, 0, 0);
+ else
+ i = allocimage(display, c->r, outchan, 0, 0);
+ if(i == nil){
+ fprint(2, "jpg: allocimage %s failed: %r\n", name);
+ return "allocimage";
+ }
+ if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
+ fprint(2, "jpg: loadimage %s failed: %r\n", name);
+ return "loadimage";
+ }
+ image = i;
+ eresized(0);
+ if (Fflag) {
+ freeimage(i);
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r->cmap);
+ free(r);
+ free(array);
+ goto rpt;
+ }
+ if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
+ exits(nil);
+ draw(screen, screen->clipr, display->white, nil, ZP);
+ image = nil;
+ freeimage(i);
+ }
+ if(nineflag){
+ chantostr(buf, outchan);
+ print("%11s %11d %11d %11d %11d ", buf,
+ c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
+ if(write(1, c->chans[0], c->chanlen) != c->chanlen){
+ fprint(2, "jpg: %s: write error %r\n", name);
+ return "write";
+ }
+ }else if(cflag){
+ if(writerawimage(1, c) < 0){
+ fprint(2, "jpg: %s: write error: %r\n", name);
+ return "write";
+ }
+ }
+ Return:
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r->cmap);
+ free(r);
+ free(array);
+ if(c){
+ free(c->chans[0]);
+ free(c);
+ }
+ if (Fflag) goto rpt;
+ return nil;
+}