/* color.c */ /************************************************************************ Part of the dvipng distribution This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . Copyright (C) 2002-2015,2019 Jan-Åke Larsson ************************************************************************/ #include "dvipng.h" /* * Color. We delete and recreate the gdImage for each new page. This * means that the stack must contain rgb value not color index. * Besides, the current antialiasing implementation needs rgb anyway. */ struct colorname { struct colorname *next; char* color; char name[1]; } *colornamep=NULL,*xcp=NULL; const char *colordef[]={"xcolor.sty","dvipsnam.def", "svgnam.def","x11nam.def",NULL}; char *xcpname=NULL; void initcolor(void) { csp = 1; cstack[0].red=255; cstack[0].green=255; cstack[0].blue=255; cstack[1].red=0; cstack[1].green=0; cstack[1].blue=0; } static struct colorname * NewColor(const char* prefix, int nprefix, char* name, int nname, char* model, int nmodel, char* values, int nvalues) { struct colorname *tmp; if ((tmp=malloc(sizeof(struct colorname)+3+nprefix+nname+nmodel+nvalues))==NULL) Fatal("Cannot malloc space for color name"); tmp->color=tmp->name+nprefix+nname+1; strncpy(tmp->name,prefix,nprefix); strncpy(tmp->name+nprefix,name,nname); tmp->name[nprefix+nname]='\0'; strncpy(tmp->color,model,nmodel); tmp->color[nmodel]=' '; strncpy(tmp->color+nmodel+1,values,nvalues); tmp->color[nmodel+nvalues+1]='\0'; model=tmp->color; while(*model!='\0') { if (*model==',') *model=' '; model++; } DEBUG_PRINT(DEBUG_COLOR,("\n COLOR NAME:\t'%s' '%s'", tmp->name,tmp->color)); return(tmp); } #define FINDWORD(s) while(snext=list; list=tmp; } else if ((pos+15next=list; list=tmp; FINDWORD(pos); } } else { pos++; while (posnext; free(xcp); xcp=next; } if (xcpname!=NULL) { free(xcpname); xcpname=NULL; } } void ClearColorNames(void) { struct colorname *next; while (colornamep) { next=colornamep->next; free(colornamep); colornamep=next; } ClearXColorPrologue(); } void InitXColorPrologue(const char* name) { ClearXColorPrologue(); if ((xcpname=malloc(strlen(name)+1))==NULL) Fatal("cannot malloc memory for xcolor prologue name"); strcpy(xcpname,name); } static struct colorname* LoadXColorPrologue(void) { struct colorname *list=NULL,*tmp=NULL; char *filepath,*pos,*max; const char *prefix=""; char *name,*values,*model; int nprefix=0,nname,nvalues,nmodel; struct filemmap fmmap; boolean mmapfailed; filepath=kpse_find_file(xcpname,kpse_program_text_format,false); if (filepath == NULL) return NULL; DEBUG_PRINT(DEBUG_COLOR,("\n OPEN XCOLOR PROLOGUE:\t'%s'", filepath)); mmapfailed = MmapFile(filepath,&fmmap); free(filepath); if (mmapfailed) return NULL; pos=fmmap.data; max=fmmap.data+fmmap.size; while(posnext=list; list=tmp; } } UnMmapFile(&fmmap); return(list); } #define FTO255(a) ((int) (255*a+0.5)) #define WARN_IF_FAILED(a,b) if (a==b) { page_flags |= PAGE_GAVE_WARN; \ Warning("missing color-specification value, treated as zero"); } #define SKIPSPACES(s) while(s && *s==' ' && *s!='\0') s++ #define NEXTFLOAT255(c) FTO255(strtod(c,&end)); WARN_IF_FAILED(c,end); c=end #define NEXTFLOAT(c) strtod(c,&end); WARN_IF_FAILED(c,end); c=end #define NEXTINT(c) strtol(c,&end,10); WARN_IF_FAILED(c,end); c=end #define NEXTHEX(c) strtol(c,&end,16); WARN_IF_FAILED(c,end); c=end void stringrgb(const char* color,int *r,int *g,int *b) { char* end; static int unloaded=0; DEBUG_PRINT(DEBUG_COLOR,("\n COLOR SPEC:\t'%s' (",color)); SKIPSPACES(color); if (strcmp(color,"Black")==0) { *r = *g = *b = 0; } else if (strcmp(color,"White")==0) { *r = *g = *b = 255; } else if (strncmp(color,"gray ",5)==0) { color+=5; *r = *g = *b = NEXTFLOAT255(color); } else if (strncmp(color,"rgb ",4)==0) { color+=4; *r = NEXTFLOAT255(color); *g = NEXTFLOAT255(color); *b = NEXTFLOAT255(color); } else if (strncmp(color,"Gray ",5)==0) { color+=5; *r = *g = *b = NEXTINT(color); } else if (strncmp(color,"RGB ",4)==0) { color+=4; *r = NEXTINT(color); *g = NEXTINT(color); *b = NEXTINT(color); } else if (strncmp(color,"HTML ",5)==0) { color+=5; *b = NEXTHEX(color); *r = *b/65536; *g = *b/256; *b -= *g*256; *g -= *r*256; } else if (strncmp(color,"cmy ",4)==0 || strncmp(color,"cmyk ",5)==0) { int c,m,y,k; color+=3; k=(*color=='k'); if (k) color++; c = NEXTFLOAT255(color); m = NEXTFLOAT255(color); y = NEXTFLOAT255(color); if (k) k = NEXTFLOAT255(color); *r = c+k<255 ? 255-(c+k) : 0; *g = m+k<255 ? 255-(m+k) : 0; *b = y+k<255 ? 255-(y+k) : 0; } else if (strncmp(color,"hsb ",4)==0 || strncmp(color,"HSB ",4)==0) { /* The hsb and HSB models really need more presicion. Use double and convert back*/ double hu,sa,br,f,R,G,B; int i; if (*color=='h') { color+=4; hu = NEXTFLOAT(color); sa = NEXTFLOAT(color); br = NEXTFLOAT(color); } else { color+=4; hu = (float)NEXTINT(color); hu /= 255; sa = (float)NEXTINT(color); sa /= 255; br = (float)NEXTINT(color); br /= 255; } i=6*hu; f=6*hu-i; switch(i) { case 0: R = br*(1-sa*0); G = br*(1-sa*(1-f)); B = br*(1-sa*1); break; case 1: R = br*(1-sa*f); G = br*(1-sa*0); B = br*(1-sa*1); break; case 2: R = br*(1-sa*1); G = br*(1-sa*0); B = br*(1-sa*(1-f)); break; case 3: R = br*(1-sa*1); G = br*(1-sa*f); B = br*(1-sa*0); break; case 4: R = br*(1-sa*(1-f)); G = br*(1-sa*1); B = br*(1-sa*0); break; case 5: R = br*(1-sa*0); G = br*(1-sa*1); B = br*(1-sa*f); break; default: R = br*(1-sa*0); G = br*(1-sa*1); B = br*(1-sa*1); } *r=FTO255(R); *g=FTO255(G); *b=FTO255(B); } else { /* Model not found, probably a color name */ struct colorname *tmp; if (xcp==NULL && xcpname!=NULL) xcp=LoadXColorPrologue(); tmp=xcp; while(tmp!=NULL && strcmp(color,tmp->name)!=0) tmp=tmp->next; if (tmp==NULL) { while (colornamep==NULL && colordef[unloaded]!=NULL) colornamep=LoadColornameFile(colordef[unloaded++]); tmp=colornamep; while(tmp!=NULL && strcmp(color,tmp->name)!=0) { while (tmp->next==NULL && colordef[unloaded]!=NULL) tmp->next=LoadColornameFile(colordef[unloaded++]); tmp=tmp->next; } } if (tmp!=NULL) { /* Found: one-level recursion */ DEBUG_PRINT(DEBUG_COLOR,("\n ---RECURSION--- ")) stringrgb(tmp->color,r,g,b); } else { /* Not found, warn */ page_flags |= PAGE_GAVE_WARN; Warning("unimplemented color specification '%s'",color); } } DEBUG_PRINT(DEBUG_COLOR,("%d %d %d) ",*r,*g,*b)) } void background(const char* p) { stringrgb(p, &cstack[0].red, &cstack[0].green, &cstack[0].blue); DEBUG_PRINT(DEBUG_COLOR,("\n BACKGROUND:\t(%d %d %d) ", cstack[0].red, cstack[0].green, cstack[0].blue)); } void pushcolor(const char * p) { if ( ++csp == STACK_SIZE ) Fatal("out of color stack space") ; stringrgb(p, &cstack[csp].red, &cstack[csp].green, &cstack[csp].blue); DEBUG_PRINT(DEBUG_COLOR,("\n COLOR PUSH:\t(%d %d %d) ", cstack[csp].red, cstack[csp].green, cstack[csp].blue)) } void popcolor(void) { if (csp > 1) csp--; /* Last color is global */ DEBUG_PRINT(DEBUG_COLOR,("\n COLOR POP\t")) } void resetcolorstack(const char * p) { if ( csp > 1 ) Warning("global color change within nested colors"); csp=0; pushcolor(p) ; DEBUG_PRINT(DEBUG_COLOR,("\n RESET COLOR:\tbottom of stack:")) } void StoreColorStack(struct page_list *tpagep) { int i=0; DEBUG_PRINT(DEBUG_COLOR,("\n STORE COLOR STACK:\t %d ", csp)); tpagep->csp=csp; while ( i <= csp ) { DEBUG_PRINT(DEBUG_COLOR,("\n COLOR STACK:\t %d (%d %d %d) ",i, cstack[i].red, cstack[i].green, cstack[i].blue)); tpagep->cstack[i].red = cstack[i].red; tpagep->cstack[i].green = cstack[i].green; tpagep->cstack[i].blue = cstack[i].blue; i++; } } void ReadColorStack(struct page_list *tpagep) { int i=0; DEBUG_PRINT(DEBUG_COLOR,("\n READ COLOR STACK:\t %d ", tpagep->csp)); csp=tpagep->csp; while ( i <= tpagep->csp ) { DEBUG_PRINT(DEBUG_COLOR,("\n COLOR STACK:\t %d (%d %d %d) ",i, cstack[i].red, cstack[i].green, cstack[i].blue)); cstack[i].red = tpagep->cstack[i].red; cstack[i].green = tpagep->cstack[i].green; cstack[i].blue = tpagep->cstack[i].blue; i++; } } void StoreBackgroundColor(struct page_list *tpagep) { /* Background color changes affect the _whole_ page */ tpagep->cstack[0].red = cstack[0].red; tpagep->cstack[0].green = cstack[0].green; tpagep->cstack[0].blue = cstack[0].blue; }