text: /* Copyright (C) 2000-2012 by George Williams */
  /*
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
  
   * Redistributions of source code must retain the above copyright notice, this
   * list of conditions and the following disclaimer.
  
   * Redistributions in binary form must reproduce the above copyright notice,
   * this list of conditions and the following disclaimer in the documentation
   * and/or other materials provided with the distribution.
  
   * The name of the author may not be used to endorse or promote products
   * derived from this software without specific prior written permission.
  
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
   * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  #include "fontforgevw.h"
  #include "ustring.h"
  #include <math.h>
  #include "bitmapcontrol.h"
  
  int bdfcontrol_lastwhich = bd_selected;
  
  static void RemoveBDFWindows(BDFFont *bdf) {
      int i;
  
      for ( i=0; i<bdf->glyphcnt; ++i ) if ( bdf->glyphs[i]!=NULL ) {
  	BCDestroyAll(bdf->glyphs[i]);
      }
      if ( !no_windowing_ui ) {
  	ff_progress_allow_events();
  	/* Just in case... */
  	ff_progress_allow_events();
      }
      /* We can't free the bdf until all the windows have executed their destroy*/
      /*  routines (which they will when they get the destroy window event) */
      /*  because those routines depend on the bdf existing ... */
  }
  
  static BDFFont *BDFNew(SplineFont *sf,int pixel_size, int depth) {
      BDFFont *new = chunkalloc(sizeof(BDFFont));
      int linear_scale = 1<<(depth/2);
  
      new->sf = sf;
      new->glyphcnt = new->glyphmax = sf->glyphcnt;
      new->glyphs = gcalloc(new->glyphcnt,sizeof(BDFChar *));
      new->pixelsize = pixel_size;
      new->ascent = (sf->ascent*pixel_size+.5)/(sf->ascent+sf->descent);
      new->descent = pixel_size-new->ascent;
      new->res = -1;
      if ( linear_scale!=1 )
  	BDFClut(new,linear_scale);
  return( new );
  }
  
  static void SFRemoveUnwantedBitmaps(SplineFont *sf,int32 *sizes) {
      BDFFont *bdf, *prev, *next;
      FontViewBase *fv;
      int i;
  
      for ( prev = NULL, bdf=sf->bitmaps; bdf!=NULL; bdf = next ) {
  	next = bdf->next;
  	for ( i=0; sizes[i]!=0 && ((sizes[i]&0xffff)!=bdf->pixelsize || (sizes[i]>>16)!=BDFDepth(bdf)); ++i );
  	if ( sizes[i]==0 ) {
  	    /* To be deleted */
  	    if ( prev==NULL )
  		sf->bitmaps = next;
  	    else
  		prev->next = next;
  	    for ( fv=sf->fv; fv!=NULL; fv=fv->nextsame ) {
  		if ( fv->active_bitmap==bdf ) {
  		    if ( sf->onlybitmaps && sf->bitmaps!=NULL )
  			FVChangeDisplayBitmap(fv,sf->bitmaps);
  		    else
  			FVShowFilled(fv);
  		}
  	    }
  	    RemoveBDFWindows(bdf);
  	    BDFFontFree(bdf);
  	    sf->changed = true;
  	} else {
  	    sizes[i] = -sizes[i];		/* don't need to create it */
  	    prev = bdf;
  	}
      }
  }
  
  static void SFFigureBitmaps(SplineFont *sf,int32 *sizes,int usefreetype,int rasterize,int layer) {
      BDFFont *bdf;
      int i, first;
      void *freetypecontext = NULL;
  
      SFRemoveUnwantedBitmaps(sf,sizes);
  
      first = true;
      for ( i=0; sizes[i]!=0 ; ++i ) if ( sizes[i]>0 ) {
  	if ( first && autohint_before_generate )
  	    SplineFontAutoHint(sf,layer);
  	if ( first && usefreetype )
  	    freetypecontext = FreeTypeFontContext(sf,NULL,NULL,layer);
  	if ( !rasterize )
  	    bdf = BDFNew(sf,sizes[i]&0xffff,sizes[i]>>16);
  	else if ( freetypecontext )
  	    bdf = SplineFontFreeTypeRasterize(freetypecontext,sizes[i]&0xffff,sizes[i]>>16);
  	else if ( usefreetype )
  	    bdf = SplineFontFreeTypeRasterizeNoHints(sf,layer,sizes[i]&0xffff,sizes[i]>>16);
  	else
  	    bdf = SplineFontAntiAlias(sf,layer,sizes[i]&0xffff,1<<((sizes[i]>>16)/2));
  	bdf->next = sf->bitmaps;
  	sf->bitmaps = bdf;
  	sf->changed = true;
  	first = false;
      }
      if ( freetypecontext )
  	FreeTypeFreeContext(freetypecontext);
  
      /* Order the list */
      SFOrderBitmapList(sf);
  }
  
  static int SizeExists(BDFFont *list, int size) {
      BDFFont *bdf;
  
      for ( bdf=list; bdf!=NULL; bdf = bdf->next ) {
  	if ( (size&0xffff)==bdf->pixelsize && (size>>16)==BDFDepth(bdf) )
  return( true );
      }
  return( false );
  }
  
  static void FVScaleBitmaps(FontViewBase *fv,int32 *sizes, int rasterize) {
      BDFFont *bdf, *scale;
      int i, cnt=0;
  
      for ( i=0; sizes[i]!=0 ; ++i ) if ( sizes[i]>0 )
  	++cnt;
      scale = fv->active_bitmap;
      ff_progress_start_indicator(10,_("Scaling Bitmaps"),_("Scaling Bitmaps"),0,cnt,1);
  
      for ( i=0; sizes[i]!=0 ; ++i ) if ( !SizeExists(fv->sf->bitmaps,sizes[i]) ) {
  	if ( rasterize )
  	    bdf = BitmapFontScaleTo(scale,sizes[i]);
  	else
  	    bdf = BDFNew(fv->sf,sizes[i]&0xffff,sizes[i]>>16);
  	if ( bdf==NULL )
      continue;
  	bdf->next = fv->sf->bitmaps;
  	fv->sf->bitmaps = bdf;
  	fv->sf->changed = true;
  	if ( !ff_progress_next())
      break;
      }
      ff_progress_end_indicator();
  
      /* Order the list */
      SFOrderBitmapList(fv->sf);
  
      SFRemoveUnwantedBitmaps(fv->sf,sizes);
  }
  
  static void ReplaceBDFC(SplineFont *sf,int32 *sizes,int gid,
  	void *freetypecontext, int usefreetype,int layer) {
      BDFFont *bdf;
      BDFChar *bdfc, temp;
      int i;
  
      if ( gid==-1 || gid>=sf->glyphcnt || sf->glyphs[gid]==NULL )
  return;
  
      for ( bdf=sf->bitmaps; bdf!=NULL; bdf=bdf->next ) {
  	for ( i=0; sizes[i]!=0 && (bdf->pixelsize!=(sizes[i]&0xffff) || BDFDepth(bdf)!=(sizes[i]>>16)); ++i );
  	if ( sizes[i]!=0 ) {
  	    bdfc = NULL;
  	    if ( freetypecontext )
  		bdfc = SplineCharFreeTypeRasterize(freetypecontext,gid,bdf->pixelsize,72,BDFDepth(bdf));
  	    else if ( usefreetype )
  		bdfc = SplineCharFreeTypeRasterizeNoHints(sf->glyphs[gid],layer,bdf->pixelsize,72,BDFDepth(bdf));
  	    if ( bdfc==NULL ) {
  		if ( autohint_before_generate && 
  			sf->glyphs[gid]->changedsincelasthinted &&
  			!sf->glyphs[gid]->manualhints )
  		    SplineCharAutoHint(sf->glyphs[gid],layer,NULL);
  		bdfc = SplineCharAntiAlias(sf->glyphs[gid],layer,bdf->pixelsize,(1<<(BDFDepth(bdf)/2)));
  	    }
  	    if ( bdf->glyphs[gid]==NULL )
  		bdf->glyphs[gid] = bdfc;
  	    else {
  		temp = *(bdf->glyphs[gid]);
  		*bdf->glyphs[gid] = *bdfc;
  		*bdfc = temp;
  		bdf->glyphs[gid]->views = bdfc->views;
  		bdfc->views = NULL;
  		BDFCharFree(bdfc);
  		BCRefreshAll(bdf->glyphs[gid]);
  	    }
  	}
      }
  }
  
  static int FVRegenBitmaps(CreateBitmapData *bd,int32 *sizes,int usefreetype) {
      FontViewBase *fv = bd->fv, *selfv = bd->which==bd_all ? NULL : fv;
      SplineFont *sf = bd->sf, *subsf, *bdfsf = sf->cidmaster!=NULL ? sf->cidmaster : sf;
      int i,j;
      BDFFont *bdf;
      void *freetypecontext=NULL;
  
      for ( i=0; sizes[i]!=0; ++i ) {
  	for ( bdf = bdfsf->bitmaps; bdf!=NULL &&
  		(bdf->pixelsize!=(sizes[i]&0xffff) || BDFDepth(bdf)!=(sizes[i]>>16));
  		bdf=bdf->next );
  	if ( bdf==NULL ) {
  	    ff_post_notice(_("Missing Bitmap"),_("Attempt to regenerate a pixel size that has not been created (%d@%d)"),
  			    sizes[i]&0xffff, sizes[i]>>16);
  return( false );
  	}
      }
      if ( bd->which==bd_current && bd->sc!=NULL ) {
  	if ( usefreetype )
  	    freetypecontext = FreeTypeFontContext(bd->sc->parent,bd->sc, selfv,bd->layer);
  	ReplaceBDFC(bd->sc->parent,sizes,bd->sc->orig_pos,freetypecontext,usefreetype,bd->layer);
  	if ( freetypecontext )
  	    FreeTypeFreeContext(freetypecontext);
      } else {
  	if ( bdfsf->subfontcnt!=0 && bd->which==bd_all ) {
  	    for ( j=0 ; j<bdfsf->subfontcnt; ++j ) {
  		subsf = bdfsf->subfonts[j];
  		for ( i=0; i<subsf->glyphcnt; ++i ) {
  		    if ( SCWorthOutputting(subsf->glyphs[i])) {
  			if ( usefreetype && freetypecontext==NULL )
  			    freetypecontext = FreeTypeFontContext(subsf,NULL, selfv, bd->layer);
  			ReplaceBDFC(subsf,sizes,i,freetypecontext,usefreetype, bd->layer);
  		    }
  		}
  		if ( freetypecontext )
  		    FreeTypeFreeContext(freetypecontext);
  		freetypecontext = NULL;
  	    }
  	} else {
  	    for ( i=0; i<fv->map->enccount; ++i ) {
  		if ( fv->selected[i] || bd->which == bd_all ) {
  		    if ( usefreetype && freetypecontext==NULL )
  			freetypecontext = FreeTypeFontContext(sf,NULL, selfv, bd->layer);
  		    ReplaceBDFC(sf,sizes,fv->map->map[i],freetypecontext,usefreetype, bd->layer);
  		}
  	    }
  	    if ( freetypecontext )
  		FreeTypeFreeContext(freetypecontext);
  	    freetypecontext = NULL;
  	}
      }
      sf->changed = true;
      FVRefreshAll(fv->sf);
  return( true );
  }
  
  static void BDFClearGlyph(BDFFont *bdf, int gid, int pass) {
  
      if ( bdf==NULL || bdf->glyphs[gid]==NULL )
  return;
      if ( pass==0 ) {
  	BCDestroyAll(bdf->glyphs[gid]);
  	ff_progress_allow_events();
      } else {
  	BDFCharFree(bdf->glyphs[gid]);
  	bdf->glyphs[gid] = NULL;
      }
  }
  
  
  static int FVRemoveBitmaps(CreateBitmapData *bd,int32 *sizes) {
      FontViewBase *fv = bd->fv;
      SplineFont *sf = bd->sf, *bdfsf = sf->cidmaster!=NULL ? sf->cidmaster : sf;
      int i,j,pass;
      BDFFont *bdf;
      int gid;
  
      /* The two passes are because we want to make sure any windows open on */
      /*  these bitmaps will get closed before we free any data. Complicated */
      /*  because the Window manager can delay things */
      for ( pass=0; pass<2; ++pass ) {
  	for ( i=0; sizes[i]!=0; ++i ) {
  	    for ( bdf = bdfsf->bitmaps; bdf!=NULL &&
  		    (bdf->pixelsize!=(sizes[i]&0xffff) || BDFDepth(bdf)!=(sizes[i]>>16));
  		    bdf=bdf->next );
  	    if ( bdf!=NULL ) {
  		if ( bd->which==bd_current && bd->sc!=NULL )
  		    BDFClearGlyph(bdf,bd->sc->orig_pos,pass);
  		else if ( bd->which==bd_all ) {
  		    for ( j=0; j<bdf->glyphcnt; ++j )
  			BDFClearGlyph(bdf,j,pass);
  		} else {
  		    for ( j=0; j<fv->map->enccount; ++j )
  			if ( fv->selected[j] && (gid = fv->map->map[j])!=-1 )
  			    BDFClearGlyph(bdf,gid,pass);
  		}
  	    }
  	}
  	ff_progress_allow_events();
      }
      sf->changed = true;
      FVRefreshAll(fv->sf);
  return( true );
  }
  
  void BitmapsDoIt(CreateBitmapData *bd,int32 *sizes,int usefreetype) {
  
      if ( bd->isavail==-1 )
  	FVRemoveBitmaps(bd,sizes);
      else if ( bd->isavail && bd->sf->onlybitmaps && bd->sf->bitmaps!=NULL )
  	FVScaleBitmaps(bd->fv,sizes,bd->rasterize);
      else if ( bd->isavail ) {
  	SFFigureBitmaps(bd->sf,sizes,usefreetype,bd->rasterize,bd->layer);
  	/* If we had an empty font, to which we've just added bitmaps, then */
  	/*  presumably we should treat this as a bitmap font and switch the */
  	/*  fontview so that it shows one of the bitmaps */
  	if ( bd->sf->onlybitmaps && bd->sf->bitmaps!=NULL ) {
  	    BDFFont *bdf;
  	    FontViewBase *fvs;
  	    for ( bdf=bd->sf->bitmaps; bdf->next!=NULL; bdf=bdf->next );
  	    for ( fvs = bd->sf->fv; fvs!=NULL; fvs= fvs->nextsame )
  		FVChangeDisplayBitmap(fvs,bdf);
  	}
      } else {
  	if ( FVRegenBitmaps(bd,sizes,usefreetype))
  	    bdfcontrol_lastwhich = bd->which;
  	else {
  	    bd->done = false;
  return;
  	}
      }
      bd->done = true;
  }
  
  
  int BitmapControl(FontViewBase *fv,int32 *sizes,int isavail,int rasterize) {
      CreateBitmapData bd;
  
      memset(&bd,0,sizeof(bd));
      bd.fv = fv;
      bd.sf = fv->sf;
      bd.isavail = isavail;
      bd.which = bd_selected;
      bd.rasterize = rasterize;
      bd.layer = fv->active_layer;
      BitmapsDoIt(&bd,sizes,hasFreeType());
  return( bd.done );
  }
  