Skip to content
Snippets Groups Projects
CMedit.C 11.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • slevy's avatar
    slevy committed
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <memory.h>
    #include <string.h>
    
    
    slevy's avatar
    slevy committed
    #include <ctype.h>
    #undef isspace
    
    #include "CMedit.H"
    
    #include "colorpatch.H"
    
    #ifdef __APPLE__
    #include <OpenGL/gl.h>
    #else
    #include <GL/gl.h>
    #endif
    
    slevy's avatar
    slevy committed
    
    CMedit :: CMedit(int x, int y, int w, int h, const char *label)
    		: Fl_Gl_Window(x,y,w,h,label) {
      init();
      end();
    }
    
    int CMedit::fload( FILE *inf ) {
      char line[256];
      char *cp;
      int count = -1;
      int ix, ox, nix, prevox;
      float rgba[4], hsba[4], phsba[4];
      int lno;
      static enum CMfield flds[4] = { HUE, SAT, BRIGHT, ALPHA };
      int f;
      char tc[2];
    
      lno = 0;
      while(fgets(line, sizeof(line), inf) != NULL) {
        lno++;
        for(cp = line; *cp && isspace(*cp); cp++)
    	;
        if(*cp == '\0' || *cp == '\n')
    	continue;
    
        if(*cp == '#') {
    	if(ncomments >= maxcomments) {
    	    maxcomments *= 2;
    	    comments = (char **)realloc( comments, maxcomments * sizeof(char *) );
    	}
    	comments[ncomments++] = strdup( line );
    	continue;
        }
    
        /* ``nnn:'' entries set the colormap pointer ... */
        if(sscanf(line, "%d%1[:]", &nix, tc) == 2) {
    	ix = nix;
    	cp = strchr(line, ':') + 1;
    	while(*cp && isspace(*cp)) cp++;
    	if(*cp == '\0' || *cp == '\n' || *cp == '#')
    	    continue;
        }
    
        if(count == -1) {
    	if(!sscanf(line, "%d", &count) || count < 1) {
    	    fprintf(stderr, "Not a .cmap file?  Doesn't begin with a number.\n");
    	    return 0;
    	}
    	cment( count );
    	ix = 0, prevox = 0;
    	continue;
        } else {
    
    	if(ix >= count)
    	    break;
    	    
    	rgba[3] = 1;
    	if(sscanf(cp, "%f%f%f%f", &rgba[0],&rgba[1],&rgba[2],&rgba[3]) < 3) {
    	    fprintf(stderr, "Couldn't read colormap line %d (cmap entry %d of 0..%d)\n",
    		lno, ix, count-1);
    	    return 0;
    	}
    	rgb2hsb( rgba[0],rgba[1],rgba[2], &hsba[0],&hsba[1],&hsba[2] );
    	hsba[3] = rgba[3];
    	if(ox == 0)
    	    memcpy(phsba, hsba, sizeof(phsba));
    	else
    	    phsba[0] = huenear(phsba[0], hsba[0]);
    	ox = (cment_-1) * ix / count + 1;
    	for(f = 0; f < 4; f++) {
    	    dragrange( prevox, ox, flds[f], phsba[f], hsba[f], 1.0 );
    	    phsba[f] = hsba[f];
    	}
    	prevox = ox;
    	ix++;
        }
    
      }
      if(count <= 0) {
        fprintf(stderr, "Empty colormap file?\n");
        return 0;
      }
      if(ix < count)
        fprintf(stderr, "Only got %d colormap entries, expected %d\n", ix, count);
    
      for(f = 0; f < 4; f++)
        dragrange( prevox, cment_, flds[f], phsba[f], phsba[f], 1.0 );
      return ix > 0;
    }
    
    int CMedit::fsave( FILE *outf ) {
      float r,g,b;
      int i, ok = 1;
      for(i = 0; i < ncomments; i++)
        fputs(comments[i], outf);
      if(ncomments > 0)
        fputs("\n", outf);
    
      fprintf(outf, "%d\n", cment_);
      for(i = 0; i < cment_; i++) {
        hsb2rgb( vh[i], vs[i], vb[i], &r, &g, &b );
        if(fprintf(outf, "%f %f %f %f\n", r, g, b, alpha[i]) <= 0)
    	ok = 0;
      }
      return ok;
    }
        
    #define  YMAX  (1.0)
    #define  YMIN  (-.25)
    #define  YBAR0  (-.01)
    #define  YBAR1  (-.06)
    
    #define  XMIN  (-cment_ / 16.f)
    #define  XMAX  cment_
    
    int CMedit::wx2x( int wx ) {
    
      return (int) (XMIN + wx * (XMAX-XMIN) / w());
    
    slevy's avatar
    slevy committed
    }
    
    float CMedit::wy2y( int wy ) {
    
      return (int) (YMAX - wy * (YMAX-YMIN) / h());
    
    slevy's avatar
    slevy committed
    }
    
    void CMedit::draw() {
      int i;
      float v;
      int coarse = (w() >= 2*cment());
    
      if(!valid() || damage()/* == FL_DAMAGE_ALL*/) {
        /* Assume reshaped */
        valid(1);
        remin = 0;  remax = cment()-1;
        glViewport( 0, 0, w(), h() );
    
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        glOrtho( XMIN, XMAX,  YMIN, 1,  -1, 1 );
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
      }
    
      if(remax-remin < cment()-1) {
        glEnable( GL_SCISSOR_TEST );
        glScissor( remin*w()/cment(), (remax+1)*w()/cment(), 0, h() );
      } else {
        glDisable( GL_SCISSOR_TEST );
      }
    
      glClearColor( 0,0,0,0 );
      glClear( GL_COLOR_BUFFER_BIT );
    
      glDisable( GL_DEPTH_TEST );
      glDisable( GL_LIGHTING );
      glDisable( GL_TEXTURE_2D );
      glDisable( GL_COLOR_MATERIAL );
    
      glBegin( GL_QUADS );
      glColor3f( 0,0,0 );
      glVertex2f( remin, -.05 );
      glVertex2f( remax+1, -.05 );
      glColor3f( 1,1,1 );
      glVertex2f( remax+1, YMIN );
      glVertex2f( remin, YMIN );
      glEnd();
    
      glLineWidth( 1 );
      glDisable( GL_BLEND );
    
      if(hsbmode) {
        glColor3f( 1,1,0 ); /* Hue: yellow */
        float midhue = y2hue(.5);
        glBegin( GL_LINE_STRIP );
        for(i = remin; i <= remax; i++) {
    	glVertex2f( i, v = hue2y( huenear( vh[i], midhue ) ) );
    	if(coarse) glVertex2f( i+1, v );
        }
        glEnd();
    
        glColor3f( .3,1,.25 );  /* Saturation: teal */
        glBegin( GL_LINE_STRIP );
        for(i = remin; i <= remax; i++) {
    	glVertex2f( i, vs[i] );
    	if(coarse) glVertex2f( i+1, vs[i] );
        }
        glEnd();
    
        glColor3f( .5,.2,1 );  /* Brightness: purple */
        glBegin( GL_LINE_STRIP );
        for(i = remin; i <= remax; i++) {
    	glVertex2f( i, vb[i] );
    	if(coarse) glVertex2f( i+1, vb[i] );
        }
        glEnd();
      } else {
    
        float r[CMENTMAX], g[CMENTMAX], b[CMENTMAX];
    
        for(i = remin; i <= remax; i++)
    	hsb2rgb( vh[i], vs[i], vb[i], &r[i], &g[i], &b[i] );
    
        glColor3f( 1,0,0 ); /* red */
        glBegin( GL_LINE_STRIP );
        for(i = remin; i <= remax; i++) {
    	glVertex2f( i, r[i] );
    	if(coarse) glVertex2f( i+1, r[i] );
        }
        glEnd();
    
        glColor3f( 0,1,0 );  /* green */
        glBegin( GL_LINE_STRIP );
        for(i = remin; i <= remax; i++) {
    	glVertex2f( i, g[i] );
    	if(coarse) glVertex2f( i+1, g[i] );
        }
        glEnd();
    
        glColor3f( 0,0,1 );  /* blue */
        glBegin( GL_LINE_STRIP );
        for(i = remin; i <= remax; i++) {
    	glVertex2f( i, b[i] );
    	if(coarse) glVertex2f( i+1, b[i] );
        }
        glEnd();
      }   
    
      glColor3f( .7,.7,.7 );  /* alpha: gray */
      glBegin( GL_LINE_STRIP );
      for(i = remin; i <= remax-coarse; i++) {
        glVertex2f( i, alpha[i] );
        if(coarse) glVertex2f( i+1, alpha[i] );
      }
      glEnd();
    
      glDisable( GL_BLEND );
      glShadeModel( GL_SMOOTH );
      glBegin( GL_QUAD_STRIP );
      for(i = remin; i <= remax; i++) {
        float rgb[3];
        hsb2rgb( vh[i], vs[i], vb[i], &rgb[0],&rgb[1],&rgb[2] );
        glColor3fv( rgb );
        glVertex2f( i, YBAR0 );
        glVertex2f( i, YBAR1 );
        if(coarse) {
    	glVertex2f( i+1, YBAR0 );
    	glVertex2f( i+1, YBAR1 );
        }
      }
      glEnd();
    
      glEnable( GL_BLEND );
      glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
      glShadeModel( GL_SMOOTH );
      glBegin( GL_QUAD_STRIP );
      for(i = remin; i <= remax; i++) {
        float rgba[4];
        hsb2rgb( vh[i], vs[i], vb[i], &rgba[0],&rgba[1],&rgba[2] );
        rgba[3] = alpha[i];
        glColor4fv( rgba );
        glVertex2f( i, YBAR1 );
        glVertex2f( i, YMIN );
        if(coarse) {
    	glVertex2f( i+1, YBAR1 );
    	glVertex2f( i+1, YMIN );
        }
      }
      glEnd();
      glDisable( GL_BLEND );
    
      if(remin <= 0) {
        glBegin( GL_QUAD_STRIP );
        for(i = 0; i < 128; i++) {
    	float rgb[3];
    	float y = i / 127.;
    	hsb2rgb( y2hue(y), 1, 1, &rgb[0],&rgb[1],&rgb[2] );
    	glColor3fv( rgb );
    	glVertex2f( XMIN, y );
    	glVertex2f( XMIN/4, y );
        }
        glEnd();
      }
    
      glFinish();
    
      /* draw (I hope) any children lying on top of us */
      if(children()) Fl_Gl_Window::draw();
    }
    
    
    float CMedit::drag( int x, enum CMfield field, float y, float lerp ) {
      float ny, oy, *vp;
      float rgb[3];
      int usergb = 0;
      if(x < 0 || x >= cment())
        return 0;
      if(locked && (x < lockmin || x > lockmax))
        return 0;
      switch(field) {
      case HUE: vp = &vh[x]; break;
      case SAT: vp = &vs[x]; break;
      case BRIGHT: vp = &vb[x]; break;
      case ALPHA: vp = &alpha[x]; break;
      default:
    	hsb2rgb( vh[x],vs[x],vb[x], &rgb[0],&rgb[1],&rgb[2] );
    	vp = &rgb[ field - RED ];
    	usergb = 1;
      }
      if(field == HUE) {
        oy = hue2y(*vp);
        y = hue2y( huenear( y2hue(y), *vp ) );
        ny = (1-lerp) * oy + lerp * y;
        *vp = y2hue(ny);
      } else {
        oy = *vp;
        ny = (1-lerp) * oy + lerp * y;
        if(ny < 0) ny = 0;
        else if(ny > 1) ny = 1;
        *vp = ny;
        if(usergb)
    	rgb2hsb( rgb[0],rgb[1],rgb[2], &vh[x],&vs[x],&vb[x] );
      }
      return ny;
    }
    
    void CMedit::dragrange( int x0, int x1, enum CMfield field, float y0, float y1, float lerp ) {
      int x;
      float dy;
      if(x0 == x1  || x0 < 0 && x1 < 0  ||  x0 >= cment() && x1 >= cment())
        return;
    
      dy = (y1 - y0) / (x1 - x0);
      if(x0 < x1)
        for(x = x0; x < x1; x++)
    	drag( x, field, y0 + dy*(x-x0), lerp );
      else
        for(x = x0; x > x1; x--)
    	drag( x, field, y0 + dy*(x-x0), lerp );
    }
    
    
    int CMedit::handle(int ev) {
      int x = wx2x( Fl::event_x() );
      float y = wy2y( Fl::event_y() );
      int xmin, xmax;
     
      switch(ev) {
    
      case FL_SHORTCUT:
        if(Fl::event_key() == 'u') {
    	undo();
    	return 1;
        }
        return 0;
    
      case FL_PUSH:
        dragfrom = x, dragval = y, dragamount = 0;
        draghue = ( (x - XMIN) * (x - (XMIN/4)) < 0 );	/* If dragging on hue strip */
    
        if(Fl::event_state(FL_SHIFT)) {
    	dragfield = ALPHA;
        } else {
    	if(hsbmode) {
    	    if(Fl::event_state(FL_BUTTON1)) dragfield = HUE;
    	    else if(Fl::event_state(FL_BUTTON2)) dragfield = SAT;
    	    else dragfield = BRIGHT;
    	} else {
    	    if(Fl::event_state(FL_BUTTON1)) dragfield = RED;
    	    else if(Fl::event_state(FL_BUTTON2)) dragfield = GREEN;
    	    else dragfield = BLUE;
    	}
        }
    
        if(Fl::event_key('l')) {
    	/*...*/
        } else if(Fl::event_key('r')) {
    	/*...*/
        } else {
    	snapshot();
        }
        /* Fall into ... */
        
      case FL_DRAG:
      case FL_RELEASE:
    
    #ifdef NOTYET
        if(draghue) {
    	float h0 = y2hue( y );
    	hueshift += (y - dragval) / huezoom;
    	huezoom *= (x - dragfrom)
        } else { ... }
    #endif
        dragrange( dragfrom, x, dragfield, dragval, y,
    		    Fl::event_state(FL_CTRL) ? 1.0 : lerpval );
        xmin = (dragfrom<x) ? dragfrom : x;
        xmax = dragfrom+x-xmin;
        if(damage() == 0) {
    	remin = xmin, remax = xmax;
        } else {
    	if(remin > xmin) remin = xmin;
    	if(remax < xmax) remax = xmax;
        }
        damage(1);
        if(dragfrom != x)
    	dragamount = 1;
        if(ev == FL_RELEASE && dragamount == 0)
    	drag( x, dragfield, y, Fl::event_state(FL_CTRL) ? 1.0 : lerpval );
    
        dragfrom = x, dragval = y;
        report( x );
        return 1;
    
      case FL_ENTER:
      case FL_LEAVE:
        report( x );
        return 1;
    
      case FL_MOVE:
        report( x );
        return 1;
        
      }
      return 0;
    }
    
    float CMedit::huenear( float hue, float hueref ) {
      float h = hue;
      if(h - hueref > .5f)
        do { h -= 1.0f; } while (h - hueref > .5);
      else
        while(h - hueref < -.5f) h += 1.0f;
      return h;
    }
    
    float CMedit::hue2y( float hue ) {
      /* Find closest multiple of given hue to reference y-position y0 */
      return (hue - hueshift) * huezoom;
    }
    
    float CMedit::y2hue( float y ) {
      return y / huezoom + hueshift;
    }
    
    static float sample( float *a, int ents, float at, int smooth ) {
      if(at <= 0 || ents <= 1) return a[0];
      if(at >= 1) return a[ents-1];
      float eat = at * ents;
      int iat = (int)eat;
      return smooth ? a[iat]*(1 - (eat-iat)) + a[iat+1]*(eat-iat)
    		: a[iat];
    }
    
    void CMedit::cment( int newcment ) {
      if(newcment < 1) return;
      if(newcment > CMENTMAX) {
        fprintf(stderr, "Oops, can't ask for more than %d colormap entries -- using %d\n",
    	CMENTMAX,CMENTMAX);
        newcment = CMENTMAX;
      }
      if(cment_ == newcment)
        return;
    
      /* Resample */
    
      snapshot();
    
      int smooth = 0; // (cment_ < newcment);
      for(int o = 0; o < newcment; o++) {
        float at = newcment > 1 ? (float)o / (newcment-1) : 0;
        vh[o] = sample( &snap[0][0], cment_, at, smooth );
        vs[o] = sample( &snap[1][0], cment_, at, smooth );
        vb[o] = sample( &snap[2][0], cment_, at, smooth );
        alpha[o] = sample( &snap[3][0], cment_, at, smooth );
      }
      cment_ = newcment;
      remin = 0;
      remax = cment_ - 1;
      lockmin = 0;
      lockmax = cment_ - 1;
      redraw();
    }
    
    
    void CMedit::reportto( void (*func)(int x, float h,float s,float b,float a, float red,float green,float blue) ) {
      reportfunc = func;
    }
    
    void CMedit::report( int x ) {
      float r,g,b;
      if(x < 0 || x > cment()-1 || reportfunc == NULL) 
        return;
    
      hsb2rgb( vh[x], vs[x], vb[x], &r,&g,&b );
      (*reportfunc)( x, vh[x], vs[x], vb[x], alpha[x], r,g,b );
    }
    
    
    void colorpatch::draw() {
        glClearColor( vr,vg,vb,va );
        glClear( GL_COLOR_BUFFER_BIT );
    }