lib/pscmGfx.c

Go to the documentation of this file.
00001 /* pscmGfx - routines for making postScript output seem a
00002  * lot like 256 color bitmap output. 
00003  *
00004  * This file is copyright 2002 Jim Kent, but license is hereby
00005  * granted for all use - public, private or commercial. */
00006 
00007 #include "common.h"
00008 #include "hash.h"
00009 #include "memgfx.h"
00010 #include "gfxPoly.h"
00011 #include "colHash.h"
00012 #include "psGfx.h"
00013 #include "pscmGfx.h"
00014 #include "gemfont.h"
00015 #include "vGfx.h"
00016 #include "vGfxPrivate.h"
00017 
00018 static char const rcsid[] = "$Id: pscmGfx.c,v 1.20 2007/04/15 00:43:41 galt Exp $";
00019 
00020 
00021 static struct pscmGfx *boxPscm;  /* Used to keep from drawing the same box again
00022                                   * and again with no other calls between.  This
00023                                   * ends up cutting down the file size by 5x
00024                                   * in the whole chromosome case of the browser. */
00025 
00026 void pscmSetHint(struct pscmGfx *pscm, char *hint, char *value)
00027 /* set a hint */
00028 {
00029 if (!value) return;
00030 if (sameString(value,""))
00031     {
00032     hashRemove(pscm->hints, hint);
00033     }
00034 struct hashEl *el = hashLookup(pscm->hints, hint);
00035 if (el) 
00036     {
00037     freeMem(el->val);
00038     el->val = cloneString(value);
00039     }
00040 else
00041     {
00042     hashAdd(pscm->hints, hint, cloneString(value));
00043     }
00044 }
00045 
00046 char *pscmGetHint(struct pscmGfx *pscm, char *hint)
00047 /* get a hint */
00048 {
00049 return hashOptionalVal(pscm->hints, hint, "");
00050 }
00051 void pscmSetClip(struct pscmGfx *pscm, int x, int y, int width, int height)
00052 /* Set clipping rectangle. */
00053 {
00054 double x2 = x + width;
00055 double y2 = y + height;
00056 pscm->clipMinX = x;
00057 pscm->clipMinY = y;
00058 pscm->clipMaxX = x2;     /* one beyond actual last pixel */
00059 pscm->clipMaxY = y2;
00060 /* adjust to pixel-centered coordinates */
00061 x2 -= 1;
00062 y2 -= 1;
00063 double x1 = x;
00064 double y1 = y;
00065 /* pad a half-pixel all the way around the box */
00066 x1 -= 0.5;
00067 y1 -= 0.5;
00068 x2 += 0.5;
00069 y2 += 0.5;
00070 psClipRect(pscm->ps, x1, y1, x2-x1, y2-y1);
00071 }
00072 
00073 void pscmUnclip(struct pscmGfx *pscm)
00074 /* Set clipping rect to cover full thing. */
00075 {
00076 psClipRect(pscm->ps, 0, 0, pscm->ps->userWidth, pscm->ps->userHeight);
00077 }
00078 
00079 static Color pscmClosestColor(struct pscmGfx *pscm, 
00080         unsigned char r, unsigned char g, unsigned char b)
00081 /* Returns closest color in color map to r,g,b */
00082 {
00083 struct rgbColor *c = pscm->colorMap;
00084 int closestDist = 0x7fffffff;
00085 int closestIx = -1;
00086 int dist, dif;
00087 int i;
00088 
00089 for (i=0; i<pscm->colorsUsed; ++i)
00090     {
00091     dif = c->r - r;
00092     dist = dif*dif;
00093     dif = c->g - g;
00094     dist += dif*dif;
00095     dif = c->b - b;
00096     dist += dif*dif;
00097     if (dist < closestDist)
00098         {
00099         closestDist = dist;
00100         closestIx = i;
00101         }
00102     ++c;
00103     }
00104 return closestIx;
00105 }
00106 
00107 static Color pscmAddColor(struct pscmGfx *pscm, 
00108         unsigned char r, unsigned char g, unsigned char b)
00109 /* Adds color to end of color map if there's room. */
00110 {
00111 int colIx = pscm->colorsUsed;
00112 struct rgbColor *c = pscm->colorMap + pscm->colorsUsed;
00113 c->r = r;
00114 c->g = g;
00115 c->b = b;
00116 pscm->colorsUsed += 1;
00117 colHashAdd(pscm->colorHash, r, g, b, colIx);;
00118 return (Color)colIx;
00119 }
00120 
00121 int pscmFindColorIx(struct pscmGfx *pscm, int r, int g, int b)
00122 /* Returns closest color in color map to rgb values.  If it doesn't
00123  * already exist in color map and there's room, it will create
00124  * exact color in map. */
00125 {
00126 struct colHashEl *che;
00127 if (r>255||g>255||b>255) 
00128     errAbort("RGB values out of range (0-255).  r:%d g:%d b:%d", r, g, b);
00129 if ((che = colHashLookup(pscm->colorHash, r, g, b)) != NULL)
00130     return che->ix;
00131 if (pscm->colorsUsed < 256)
00132     return pscmAddColor(pscm, r, g, b);
00133 return pscmClosestColor(pscm, r, g, b);
00134 }
00135 
00136 
00137 struct rgbColor pscmColorIxToRgb(struct pscmGfx *pscm, int colorIx)
00138 /* Return rgb value at color index. */
00139 {
00140 return pscm->colorMap[colorIx];
00141 }
00142 
00143 static void pscmSetDefaultColorMap(struct pscmGfx *pscm)
00144 /* Set up default color map for a memGfx. */
00145 {
00146 /* Note dependency in order here and in MG_WHITE, MG_BLACK, etc. */
00147 int i;
00148 for (i=0; i<ArraySize(mgFixedColors); ++i)
00149     {
00150     struct rgbColor *c = &mgFixedColors[i];
00151     pscmFindColorIx(pscm, c->r, c->g, c->b);
00152     }
00153 }
00154 
00155 struct pscmGfx *pscmOpen(int width, int height, char *file)
00156 /* Return new pscmGfx. */
00157 {
00158 struct pscmGfx *pscm;
00159 
00160 AllocVar(pscm);
00161 pscm->ps = psOpen(file, width, height, 72.0 * 7.5, 0, 0);
00162 psTranslate(pscm->ps,0.5,0.5);  /* translate all coordinates to pixel centers */
00163 pscm->colorHash = colHashNew();
00164 pscmSetDefaultColorMap(pscm);
00165 pscm->clipMinX = pscm->clipMinY = 0;
00166 pscm->clipMaxX = width;     
00167 pscm->clipMaxY = height;
00168 pscm->hints = hashNew(6);
00169 return pscm;
00170 }
00171 
00172 void pscmClose(struct pscmGfx **pPscm)
00173 /* Finish writing out and free structure. */
00174 {
00175 struct pscmGfx *pscm = *pPscm;
00176 if (pscm != NULL)
00177     {
00178     psClose(&pscm->ps);
00179     colHashFree(&pscm->colorHash);
00180     freez(pPscm);
00181     }
00182 }
00183 
00184 void pscmSetColor(struct pscmGfx *pscm, int colorIx)
00185 /* Set color to index. */
00186 {
00187 struct rgbColor *col = pscm->colorMap + colorIx;
00188 if (colorIx != pscm->curColor)
00189     {
00190     psSetColor(pscm->ps, col->r, col->g, col->b);
00191     pscm->curColor = colorIx;
00192     }
00193 }
00194 
00195 void pscmBoxToPs(struct pscmGfx *pscm, int x, int y, 
00196         int width, int height)
00197 /* adjust coordinates for PS */
00198 {
00199 /* Do some clipping here to make the postScript
00200  * easier to edit in illustrator. */
00201 double x2 = x + width;
00202 double y2 = y + height;
00203 
00204 if (x < pscm->clipMinX) x = pscm->clipMinX;
00205 if (y < pscm->clipMinY) y = pscm->clipMinY;
00206 if (x2 > pscm->clipMaxX) x2 = pscm->clipMaxX;
00207 if (y2 > pscm->clipMaxY) y2 = pscm->clipMaxY;
00208 
00209 /* adjust to pixel-centered coordinates */
00210 x2 -= 1;
00211 y2 -= 1;
00212 double x1 = x;
00213 double y1 = y;
00214 /* pad a half-pixel all the way around the box */
00215 x1 -= 0.5;
00216 y1 -= 0.5;
00217 x2 += 0.5;
00218 y2 += 0.5;
00219 psDrawBox(pscm->ps, x1, y1, x2-x1, y2-y1);
00220 }
00221 
00222 void pscmBox(struct pscmGfx *pscm, int x, int y, 
00223         int width, int height, int color)
00224 /* Draw a box. */
00225 {
00226 /* When viewing whole chromosomes the browser tends
00227  * to draw the same little vertical tick over and
00228  * over again.  This tries to remove the worst of
00229  * the redundancy anyway. */
00230 
00231 static int lx, ly, lw, lh, lc=-1;
00232 if (x != lx || y != ly || width != lw || height != lh || color != lc || 
00233         pscm != boxPscm)
00234     {
00235     pscmSetColor(pscm, color);
00236     pscmBoxToPs(pscm, x, y, width, height);
00237     lx = x;
00238     ly = y;
00239     lw = width;
00240     lh = height;
00241     lc = color;
00242     boxPscm = pscm;
00243     }
00244 }
00245 
00246 void pscmDot(struct pscmGfx *pscm, int x, int y, int color)
00247 /* Set a dot. */
00248 {
00249 pscmSetColor(pscm, color);
00250 psDrawBox(pscm->ps, x, y, 1, 1);
00251 }
00252 
00253 void pscmLine(struct pscmGfx *pscm, 
00254         int x1, int y1, int x2, int y2, int color)
00255 /* Draw a line from one point to another. */
00256 {
00257 pscmSetColor(pscm, color);
00258 if ((x1==x2) || (y1 == y2))
00259     {
00260     /* pad a half-pixel at each end */
00261     psDrawBox(pscm->ps, x1-0.5, y1-0.5, x2-x1+1, y2-y1+1);
00262     }
00263 else
00264     {
00265     psDrawLine(pscm->ps, x1, y1, x2, y2);
00266     }
00267 boxPscm = NULL;
00268 }
00269 
00270 static void pscmVerticalSmear(struct pscmGfx *pscm,
00271         int xOff, int yOff, int width, int height, 
00272         unsigned char *dots, boolean zeroClear)
00273 /* Put a series of one 'pixel' width vertical lines. */
00274 {
00275 int x, i;
00276 struct psGfx *ps = pscm->ps;
00277 Color c;
00278 for (i=0; i<width; ++i)
00279     {
00280     x = xOff + i;
00281     c = dots[i];
00282     if (c != 0 || !zeroClear)
00283         {
00284         pscmSetColor(pscm, c);
00285         psDrawBox(ps, x, yOff, 1, height);
00286         }
00287     }
00288 }
00289 
00290 static void pscmSetFont(struct pscmGfx *pscm, MgFont *font)
00291 /* Set font. */
00292 {
00293 /* For now we basically still get the sizing info from the old
00294  * gem fonts.   I'm not sure how to get sizing info out of
00295  * PostScript.  We'll try and arrange it so that the PostScript
00296  * fonts match the gem fonts more or less. */
00297 void *v = font;
00298 if (v != pscm->curFont)
00299     {
00300     psTimesFont(pscm->ps, font->frm_hgt);
00301     pscm->curFont = v;
00302     }
00303 }
00304 
00305 void pscmText(struct pscmGfx *pscm, int x, int y, int color, 
00306         MgFont *font, char *text)
00307 /* Draw a line of text with upper left corner x,y. */
00308 {
00309 pscmSetColor(pscm, color);
00310 pscmSetFont(pscm, font);
00311 psTextAt(pscm->ps, x, y, text);
00312 boxPscm = NULL;
00313 }
00314 
00315 void pscmTextRight(struct pscmGfx *pscm, int x, int y, int width, int height,
00316         int color, MgFont *font, char *text)
00317 /* Draw a line of text right justified in box defined by x/y/width/height */
00318 {
00319 pscmSetColor(pscm, color);
00320 pscmSetFont(pscm, font);
00321 psTextRight(pscm->ps, x, y, width, height, text);
00322 boxPscm = NULL;
00323 }
00324 
00325 void pscmTextCentered(struct pscmGfx *pscm, int x, int y, 
00326         int width, int height, int color, MgFont *font, char *text)
00327 /* Draw a line of text centered in box defined by x/y/width/height */
00328 {
00329 pscmSetColor(pscm, color);
00330 pscmSetFont(pscm, font);
00331 psTextCentered(pscm->ps, x, y, width, height, text);
00332 boxPscm = NULL;
00333 }
00334 
00335 void pscmFillUnder(struct pscmGfx *pscm, int x1, int y1, int x2, int y2, 
00336         int bottom, Color color)
00337 /* Draw a 4 sided filled figure that has line x1/y1 to x2/y2 at
00338  * it's top, a horizontal line at bottom at it's bottom,  and
00339  * vertical lines from the bottom to y1 on the left and bottom to
00340  * y2 on the right. */
00341 {
00342 pscmSetColor(pscm, color);
00343 psFillUnder(pscm->ps, x1, y1, x2, y2, bottom);
00344 boxPscm = NULL;
00345 }
00346 
00347 void pscmDrawPoly(struct pscmGfx *pscm, struct gfxPoly *poly, Color color, 
00348         boolean filled)
00349 /* Draw a polygon, possibly filled, in color. */
00350 {
00351 struct gfxPoint *p = poly->ptList;
00352 struct psPoly *psPoly = psPolyNew();
00353 if (poly->ptCount < 1)  /* nothing to do */
00354     {
00355     return;
00356     }
00357 if (poly->ptCount == 1)  /* a single point */
00358     {
00359     struct gfxPoint *p = poly->ptList;
00360     pscmBox(pscm, p->x, p->y, 1, 1, color);
00361     return;
00362     }
00363 if (poly->ptCount == 2)  /* a single line */
00364     {
00365     struct gfxPoint *p1 = poly->ptList;
00366     struct gfxPoint *p2 = p1->next;
00367     pscmLine(pscm, p1->x, p1->y, p2->x, p2->y, color);
00368     }
00369 pscmSetColor(pscm, color);
00370 
00371 for (;;)
00372     {
00373     psPolyAddPoint(psPoly,p->x, p->y);
00374     p = p->next;
00375     if (p == poly->ptList)
00376         break;
00377     }
00378 psDrawPoly(pscm->ps, psPoly, filled);
00379 
00380 psPolyFree(&psPoly);
00381 }
00382 
00383 
00384 struct vGfx *vgOpenPostScript(int width, int height, char *fileName)
00385 /* Open up something that will someday be a PostScript file. */
00386 {
00387 struct vGfx *vg = vgHalfInit(width, height);
00388 vg->data = pscmOpen(width, height, fileName);
00389 vg->close = (vg_close)pscmClose;
00390 vg->dot = (vg_dot)pscmDot;
00391 vg->box = (vg_box)pscmBox;
00392 vg->line = (vg_line)pscmLine;
00393 vg->text = (vg_text)pscmText;
00394 vg->textRight = (vg_textRight)pscmTextRight;
00395 vg->textCentered = (vg_textCentered)pscmTextCentered;
00396 vg->findColorIx = (vg_findColorIx)pscmFindColorIx;
00397 vg->colorIxToRgb = (vg_colorIxToRgb)pscmColorIxToRgb;
00398 vg->setClip = (vg_setClip)pscmSetClip;
00399 vg->unclip = (vg_unclip)pscmUnclip;
00400 vg->verticalSmear = (vg_verticalSmear)pscmVerticalSmear;
00401 vg->fillUnder = (vg_fillUnder)pscmFillUnder;
00402 vg->drawPoly = (vg_drawPoly)pscmDrawPoly;
00403 vg->setHint = (vg_setHint)pscmSetHint;
00404 vg->getHint = (vg_getHint)pscmGetHint;
00405 return vg;
00406 }
00407 

Generated on Tue Dec 25 18:39:31 2007 for blat by  doxygen 1.5.2