5d028b1ce2b846df0794e25f6d101620fe6a8828 angie Fri Jul 1 16:12:49 2011 -0700 While working on VCF, caught a couple little bugs in pgSnpTextRight: 1. When restoring the clipping rectangle after snapping a label to the left, pgSnpTextRight thought it was using the track y offset -- but the DrawAt routine only gets the item y, not track y. So when an item on row N had a label that snapped left, the restored clipping rectangle clips out any subsequent items on rows 0..N-1. The top row(s) looks suspiciously empty, as if the packer hadn't fully utilized the row, but it's only that those items are made invisible. The fix was to get the real y clipping coords before clipping for left text, so they could be restored correctly. 2. The text width was subtracted twice from the item pixel start before the comparison with insideX (by the caller, and inside pgSnpTextRight). Thus snapLeft was triggered too easily, causing overwriting of left labels when items in the same row were close to each other and to the left edge. Fix: the caller doesn't subtract (width + 2); pgSnpTextRight uses textX, not x1, when drawing text. diff --git src/hg/inc/hvGfx.h src/hg/inc/hvGfx.h index 3c161d0..87840a1 100644 --- src/hg/inc/hvGfx.h +++ src/hg/inc/hvGfx.h @@ -1,268 +1,281 @@ /* hvGfx - browser graphics interface. This is a thin layer on top of vGfx * providing genome browser-specific features.*/ #ifndef BRGFX_H #define BRGFX_H #include "vGfx.h" #include "dnautil.h" struct hvGfx /* browser graphics interface */ { struct hvGfx *next; /* Next in list. */ struct vGfx *vg; /* virtual graphics object */ boolean rc; /* reverse-complement display (negative strand) */ boolean pixelBased; /* Real pixels, not PostScript or something. */ int width, height; /* Virtual pixel dimensions. */ int clipMinX; /* X clipping region, before reverse */ int clipMaxX; int clipMinY; int clipMaxY; }; struct hvGfx *hvGfxOpenPng(int width, int height, char *fileName, boolean useTransparency); /* Open up something that we'll write out as a PNG someday. * If useTransparency, then the first color in memgfx's colormap/palette is * assumed to be the image background color, and pixels of that color * are made transparent. */ struct hvGfx *hvGfxOpenPostScript(int width, int height, char *fileName); /* Open up something that will someday be a PostScript file. */ void hvGfxClose(struct hvGfx **pHvg); /* Close down virtual graphics object, and finish writing it to file. */ INLINE int hvGfxAdjX(struct hvGfx *hvg, int x) /* Return an X position, updating if reverse-complement mode */ { if (hvg->rc) return hvg->width - (x+1); else return x; } INLINE int hvGfxAdjXX(struct hvGfx *hvg, int x1, int *x2Ptr, int *y1Ptr, int *y2Ptr) /* Update a pair of coordinates if reverse-coordiates mode */ { if (hvg->rc) { reverseIntRange(&x1, x2Ptr, hvg->width); int hold = *y1Ptr; *y1Ptr = *y2Ptr; *y2Ptr = hold; } return x1; } INLINE int hvGfxAdjXW(struct hvGfx *hvg, int x, int width) /* Update a X position and width if reverse-complement mode */ { if (hvg->rc) { int x2 = (x + width); reverseIntRange(&x, &x2, hvg->width); } return x; } INLINE void hvGfxDot(struct hvGfx *hvg, int x, int y, int colorIx) /* Draw a single pixel. Try to work at a higher level when possible! */ { vgDot(hvg->vg, hvGfxAdjX(hvg, x), y, colorIx); } INLINE void hvGfxBox(struct hvGfx *hvg, int x, int y, int width, int height, int colorIx) /* Draw a box. */ { vgBox(hvg->vg, hvGfxAdjXW(hvg, x, width), y, width, height, colorIx); } INLINE void hvGfxLine(struct hvGfx *hvg, int x1, int y1, int x2, int y2, int colorIx) /* Draw a line from one point to another. */ { x1 = hvGfxAdjXX(hvg, x1, &x2, &y1, &y2); vgLine(hvg->vg, x1, y1, x2, y2, colorIx); } INLINE void hvGfxText(struct hvGfx *hvg, int x, int y, int colorIx, void *font, char *text) /* Draw a line of text with upper left corner x,y. */ { if (hvg->rc) { // FIXME: test // move x,y to lower-left corner int width = vgGetFontStringWidth(hvg->vg, font, text); int height = vgGetFontPixelHeight(hvg->vg, font); vgTextRight(hvg->vg, hvGfxAdjXW(hvg, x, width), y, width, height, colorIx, font, text); } else vgText(hvg->vg, x, y, colorIx, font, text); } INLINE void hvGfxTextRight(struct hvGfx *hvg, int x, int y, int width, int height, int colorIx, void *font, char *text) /* Draw a line of text with upper right corner x,y. */ { if (hvg->rc) { // move x,y to upper right corner int fHeight = vgGetFontPixelHeight(hvg->vg, font); // this is what mgTextRight does, not sure why y += (height - fHeight)/2 + ((font == mgSmallFont()) ? 1 : 0); vgText(hvg->vg, hvGfxAdjXW(hvg, x, width), y, colorIx, font, text); } else vgTextRight(hvg->vg, x, y, width, height, colorIx, font, text); } INLINE void hvGfxTextCentered(struct hvGfx *hvg, int x, int y, int width, int height, int colorIx, void *font, char *text) /* Draw a line of text in middle of box. */ { vgTextCentered(hvg->vg, hvGfxAdjXW(hvg, x, width), y, width, height, colorIx, font, text); } INLINE void hvGfxSetWriteMode(struct hvGfx *hvg, int writeMode) /* set write mode */ { vgSetWriteMode(hvg->vg, writeMode); } INLINE void hvGfxSetClip(struct hvGfx *hvg, int x, int y, int width, int height) /* Set clipping rectangle. */ { // this should not be adjusted for RC hvg->clipMinX = x; hvg->clipMaxX = x + width; hvg->clipMinY = y; hvg->clipMaxY = y + height; // clipping region is reversed in actual vGfx if (hvg->rc) x = hvg->width - (x + width); vgSetClip(hvg->vg, x, y, width, height); } +INLINE void hvGfxGetClip(struct hvGfx *hvg, int *retX, int *retY, int *retWidth, int *retHeight) +/* Get clipping rectangle. */ +{ +if (retX != NULL) + *retX = hvg->clipMinX; +if (retY != NULL) + *retY = hvg->clipMinY; +if (retWidth != NULL) + *retWidth = (hvg->clipMaxX - hvg->clipMinX); +if (retHeight != NULL) + *retHeight = (hvg->clipMaxY - hvg->clipMinY); +} + INLINE void hvGfxUnclip(struct hvGfx *hvg) /* Set clipping rect cover full thing. */ { hvg->clipMinX = 0; hvg->clipMaxX = hvg->width; hvg->clipMinY = 0; hvg->clipMaxY = hvg->height; vgUnclip(hvg->vg); } INLINE void hvGfxVerticalSmear(struct hvGfx *hvg, int xOff, int yOff, int width, int height, Color *dots, boolean zeroClear) /* Put a series of one '8-bit pixel' width vertical lines. */ { vgVerticalSmear(hvg->vg, hvGfxAdjXW(hvg, xOff, width), yOff, width, height, dots, zeroClear); } INLINE void hvGfxFillUnder(struct hvGfx *hvg, int x1, int y1, int x2, int y2, int bottom, Color color) /* Draw a 4 sided filled figure that has line x1/y1 to x2/y2 at * it's top, a horizontal line at bottom at it's bottom, and * vertical lines from the bottom to y1 on the left and bottom to * y2 on the right. */ { x1 = hvGfxAdjXX(hvg, x1, &x2, &y1, &y2); vgFillUnder(hvg->vg, x1, y1, x2, y2, bottom, color); } INLINE void hvGfxRevPoly(struct hvGfx *hvg, struct gfxPoly *poly) /* reverse coordinates in polygons */ { /* reverse but don't clip; clipping requires working at the line level */ struct gfxPoint *pt = poly->ptList; int i; for (i = 0; i < poly->ptCount; i++, pt = pt->next) pt->x = hvg->width - (pt->x+1); } INLINE void hvGfxDrawPoly(struct hvGfx *hvg, struct gfxPoly *poly, Color color, boolean filled) /* Draw polygon, possibly filled in color. */ { if (hvg->rc) hvGfxRevPoly(hvg, poly); vgDrawPoly(hvg->vg, poly, color, filled); if (hvg->rc) hvGfxRevPoly(hvg, poly); // restore } INLINE int hvGfxFindColorIx(struct hvGfx *hvg, int r, int g, int b) /* Find color in map if possible, otherwise create new color or * in a pinch a close color. */ { return vgFindColorIx(hvg->vg, r, g, b); } INLINE struct rgbColor hvGfxColorIxToRgb(struct hvGfx *hvg, int colorIx) /* Return rgb values for given color index. */ { return vgColorIxToRgb(hvg->vg, colorIx); }; INLINE void hvGfxSetHint(struct hvGfx *hvg, char *hint, char *value) /* Set hint */ { vgSetHint(hvg->vg,hint,value); } INLINE char* hvGfxGetHint(struct hvGfx *hvg, char *hint) /* Get hint */ { return vgGetHint(hvg->vg, hint); } INLINE int hvGfxFindRgb(struct hvGfx *hvg, struct rgbColor *rgb) /* Find color index corresponding to rgb color. */ { return vgFindRgb(hvg->vg, rgb); } INLINE Color hvGfxContrastingColor(struct hvGfx *hvg, int backgroundIx) /* Return black or white whichever would be more visible over * background. */ { return vgContrastingColor(hvg->vg, backgroundIx); } void hvGfxMakeColorGradient(struct hvGfx *hvg, struct rgbColor *start, struct rgbColor *end, int steps, Color *colorIxs); /* Make a color gradient that goes smoothly from start to end colors in given * number of steps. Put indices in color table in colorIxs */ void hvGfxBarbedHorizontalLine(struct hvGfx *hvg, int x, int y, int width, int barbHeight, int barbSpacing, int barbDir, Color color, boolean needDrawMiddle); /* Draw a horizontal line starting at xOff, yOff of given width. Will * put barbs (successive arrowheads) to indicate direction of line. * BarbDir of 1 points barbs to right, of -1 points them to left. */ void hvGfxDrawRulerBumpText(struct hvGfx *hvg, int xOff, int yOff, int height, int width, Color color, MgFont *font, int startNum, int range, int bumpX, int bumpY); /* Draw a ruler inside the indicated part of mg with numbers that start at * startNum and span range. Bump text positions slightly. */ void hvGfxDrawRuler(struct hvGfx *hvg, int xOff, int yOff, int height, int width, Color color, MgFont *font, int startNum, int range); /* Draw a ruler inside the indicated part of mg with numbers that start at * startNum and span range. */ void hvGfxNextItemButton(struct hvGfx *hvg, int x, int y, int w, int h, Color color, Color hvgColor, boolean nextItem); /* Draw a button that looks like a fast-forward or rewind button on */ /* a remote control. If nextItem is TRUE, it points right, otherwise */ /* left. color is the outline color, and hvgColor is the fill color. */ #endif