fb0d415d29a5786b48478ef7fb87b8adc8d8af61 kent Sun Feb 17 10:51:11 2013 -0800 Changing way transparency is done so that it shows up in PDF, and also can be normalized when we add more cell lines. diff --git src/hg/hgTracks/wigTrack.c src/hg/hgTracks/wigTrack.c index 922462f..a63eb4a 100644 --- src/hg/hgTracks/wigTrack.c +++ src/hg/hgTracks/wigTrack.c @@ -5,30 +5,31 @@ #include "common.h" #include "obscure.h" #include "hash.h" #include "linefile.h" #include "jksql.h" #include "hdb.h" #include "hgTracks.h" #include "wiggle.h" #include "hmmstats.h" #include "scoredRef.h" #ifndef GBROWSE #include "customTrack.h" #endif /* GBROWSE */ #include "wigCommon.h" #include "imageV2.h" +#include "memgfx.h" struct wigItem /* A wig track item. */ { struct wigItem *next; int start, end; /* Start/end in chrom (aka browser) coordinates. */ char *db; /* Database */ int ix; /* Position in list. */ int height; /* Pixel height of item. */ unsigned span; /* each value spans this many bases */ unsigned count; /* number of values to use */ unsigned offset; /* offset in File to fetch data */ char *file; /* path name to data file, one byte per value */ double lowerLimit; /* lowest data value in this block */ @@ -777,68 +778,99 @@ colorArray[x1] = tg->ixColor; } } /* Fill in colors from alternate track if necessary. */ if (colorTrack != NULL) { struct track *cTrack = hashMustFindVal(trackHash, colorTrack); wigFillInColorArray(tg, hvg, colorArray, width, cTrack); } return colorArray; } -void graphPreDraw(struct preDrawElement *preDraw, int preDrawZero, int width, - struct track *tg, struct hvGfx *hvg, int xOff, int yOff, +void vLineViaHvg(void *image, int x, int y, int height, Color color) +/* A vertical line drawer that works via hvGfx system. */ +{ +hvGfxBox(image, x, y, 1, height, color); +} + +Color somewhatLighterColor32(Color color) +/* Get a somewhat lighter shade of a color - 1/3 of the way towards white. + * Specialized here to bypass image parameter requirement.*/ +{ +#ifndef COLOR32 +#error COLOR32 must be defined these days, transparency depends on it. +#endif /* COLOR32 */ +struct rgbColor rgbColor = mgColorIxToRgb(NULL, color); +rgbColor.r = (2*rgbColor.r+255)/3; +rgbColor.g = (2*rgbColor.g+255)/3; +rgbColor.b = (2*rgbColor.b+255)/3; +return MAKECOLOR_32(rgbColor.r, rgbColor.g, rgbColor.b); +} + +struct wigGraphOutput *wigGraphOutputSolid(int xOff, int yOff, struct hvGfx *image) +/* Get appropriate wigGraphOutput for non-transparent rendering */ +{ +struct wigGraphOutput *wgo; +AllocVar(wgo); +wgo->image = image; +wgo->vLine = vLineViaHvg; +wgo->xOff = xOff; +wgo->yOff = yOff; +return wgo; +} + +static void graphPreDraw(struct preDrawElement *preDraw, int preDrawZero, int width, + struct track *tg, void *image, WigVerticalLineVirtual vLine, int xOff, int yOff, double graphUpperLimit, double graphLowerLimit, double graphRange, double epsilon, Color *colorArray, enum trackVisibility vis, struct wigCartOptions *wigCart) /* graph the preDraw array */ { int x1; int h = tg->lineHeight; /* the height of our drawing window */ double scaleFactor = h/graphRange; Color oldDrawColor = colorArray[0] + 1; /* Just to be different from 1st drawColor. */ Color mediumColor = MG_BLACK; // Will be overriden Color lightColor = MG_BLACK; // Will be overriden Color clipColor = MG_MAGENTA; enum wiggleTransformFuncEnum transformFunc = wigCart->transformFunc; enum wiggleGraphOptEnum lineBar = wigCart->lineBar; boolean whiskers = (wigCart->windowingFunction == wiggleWindowingWhiskers && width < winEnd-winStart); /* right now this is a simple pixel by pixel loop. Future * enhancements could draw boxes where pixels * are all the same height in a run. */ for (x1 = 0; x1 < width; ++x1) { int x = x1 + xOff; int preDrawIndex = x1 + preDrawZero; struct preDrawElement *p = &preDraw[preDrawIndex]; Color drawColor = colorArray[x1]; if (drawColor != oldDrawColor) { - mediumColor = somewhatLighterColor(hvg, drawColor); - lightColor = somewhatLighterColor(hvg, mediumColor); + mediumColor = somewhatLighterColor32(drawColor); + lightColor = somewhatLighterColor32(mediumColor); oldDrawColor = drawColor; } - /* count is non-zero meaning valid data exists here */ if (p->count) { /* data value has been picked by previous scanning. * Could be smoothed, maybe not. */ double dataValue = p->smooth; /* The graphing coordinate conversion situation is: * graph coordinate y = 0 is graphUpperLimit data space * and total graph height is h which is graphRange in data space * The Y axis is positive down, negative up. * * Taking a simple coordinate conversion from data space * to the graphing space, the data value is at: @@ -867,34 +899,34 @@ { int scaledMin = scaleHeightToPixels(doTransform(p->min, transformFunc)); int lightHeight = max(1,scaledMin-zeroPos); int mediumHeight = lightHeight; if (!isnan(std)) { // Test needed due to bug in version 1.5 bigWiles double minus = doTransform(mean - std, transformFunc); int scaledMinus = scaleHeightToPixels(minus); mediumHeight = max(1,scaledMinus-zeroPos); } int darkHeight = max(1,scaledVal-zeroPos); if (zeroPos == (yOff+h)) // bottom pixel special case zeroPos -= 1; if (((zeroPos-yOff)+darkHeight) == 0) darkHeight += 1; // top pixel special case - hvGfxBox(hvg, x,zeroPos,1, darkHeight, drawColor); - hvGfxBox(hvg, x, zeroPos+darkHeight, 1, mediumHeight-darkHeight, + vLine(image, x,zeroPos, darkHeight, drawColor); + vLine(image, x, zeroPos+darkHeight, mediumHeight-darkHeight, mediumColor); - hvGfxBox(hvg, x, zeroPos+mediumHeight,1, lightHeight-mediumHeight, + vLine(image, x, zeroPos+mediumHeight, lightHeight-mediumHeight, lightColor); } else { /* The calculations here are a little convoluted because * of the history. Originally it drew from the baseline * up to the max first in the lightest color, then from the * baseline to the mean+std in medium color, and finally * from baseline to mean in dark color. This ended up * drawing the same pixels up to three times which messed * things up in transparent overlay mode. The code was * refactored to accomplish this without having to worry * about +/- 1 differences. In particular be aware the * xyzHeight calculations are done assuming the other end is * the baseline. */ @@ -914,121 +946,118 @@ double plus = doTransform(mean + std, transformFunc); int scaledPlus = scaleHeightToPixels(plus); int boxHeight = max(1,zeroPos-scaledPlus); mediumTop = scaledPlus, mediumHeight = boxHeight; } /* Calculate light part from max. */ int scaledMax = scaleHeightToPixels(doTransform(p->max, transformFunc)); if (scaledMax == (h+yOff)) scaledMax = (h+yOff) - 1; boxHeight = max(1,zeroPos-scaledMax); int lightTop = scaledMax, lightHeight = boxHeight; /* Draw, making sure not to overwrite pixels since * would mess up transparent drawing. */ - hvGfxBox(hvg,x,darkTop,1, darkHeight, drawColor); - hvGfxBox(hvg, x, mediumTop, 1, mediumHeight-darkHeight, mediumColor); - hvGfxBox(hvg,x,lightTop,1,lightHeight-mediumHeight, lightColor); + vLine(image,x,darkTop, darkHeight, drawColor); + vLine(image, x, mediumTop, mediumHeight-darkHeight, mediumColor); + vLine(image,x,lightTop,lightHeight-mediumHeight, lightColor); } } else { int y0 = graphUpperLimit * scaleFactor; int y1 = (graphUpperLimit - dataValue)*scaleFactor; int boxHeight = max(1,abs(y1 - y0)); int boxTop = min(y1,y0); // positive data value exactly equal to Bottom pixel // make sure it draws at least a pixel there if (boxTop == h) boxTop = h - 1; // negative data value exactly equal to top pixel // make sure it draws something if ((boxTop+boxHeight) == 0) boxHeight += 1; - hvGfxBox(hvg,x, yOff+boxTop, 1, boxHeight, drawColor); + vLine(image,x, yOff+boxTop, boxHeight, drawColor); } } else { /* draw a 3 pixel height box */ if (whiskers) { int scaledMin = scaleHeightToPixels(doTransform(p->min, transformFunc)); int scaledMax = scaleHeightToPixels(doTransform(p->max, transformFunc)); double mean = p->sumData/p->count; int boxHeight = max(1,scaledMin - scaledMax); - hvGfxBox(hvg, x, scaledMax, 1, boxHeight, lightColor); + vLine(image, x, scaledMax, boxHeight, lightColor); int scaledMean = scaleHeightToPixels(dataValue); double std = calcStdFromSums(p->sumData, p->sumSquares, p->count); if (!isnan(std)) // Test needed because of bug in version 1.5 bigWiles { int scaledPlus = scaleHeightToPixels(doTransform(mean+std, transformFunc)); int scaledMinus = scaleHeightToPixels(doTransform(mean-std, transformFunc)); int boxHeight = max(1,scaledMinus - scaledPlus); - hvGfxBox(hvg, x, scaledPlus, 1, boxHeight, mediumColor); + vLine(image, x, scaledPlus, boxHeight, mediumColor); } - hvGfxBox(hvg, x, scaledMean, 1, 1, drawColor); + vLine(image, x, scaledMean, 1, drawColor); } else { int yPointGraph = scaleHeightToPixels(dataValue) - 1; - hvGfxBox(hvg, x, yPointGraph, 1, 3, drawColor); + vLine(image, x, yPointGraph, 3, drawColor); } } if (dataValue > graphUpperLimit) - hvGfxBox(hvg, x, yOff, 1, 2, clipColor); + vLine(image, x, yOff, 2, clipColor); else if (dataValue < graphLowerLimit) - hvGfxBox(hvg, x, yOff + h - 1, 1, 2, clipColor); + vLine(image, x, yOff + h - 1, 2, clipColor); #undef scaleHeightToPixels /* No longer use this symbol */ } /* vis == tvFull || vis == tvPack */ else if (vis == tvDense || vis == tvSquish) { double grayValue; int grayIndex; /* honor the viewLimits, data below is white, data above is black */ grayValue = max(dataValue,graphLowerLimit); grayValue = min(grayValue,graphUpperLimit); grayIndex = ((grayValue-graphLowerLimit)/graphRange)*MAX_WIG_VALUE; drawColor = tg->colorShades[grayInRange(grayIndex, 0, MAX_WIG_VALUE)]; - hvGfxBox(hvg, x, yOff, 1, tg->lineHeight, drawColor); + vLine(image, x, yOff, tg->lineHeight, drawColor); } /* vis == tvDense || vis == tvSquish */ } /* if (preDraw[].count) */ } /* for (x1 = 0; x1 < width; ++x1) */ } /* graphPreDraw() */ -static void graphAllInContainer(struct preDrawContainer *preDrawList, int preDrawZero, int width, - struct track *tg, struct hvGfx *hvg, int xOff, int yOff, - double graphUpperLimit, double graphLowerLimit, double graphRange, - enum trackVisibility vis, struct wigCartOptions *wigCart) +static void graphPreDrawContainer(struct preDrawContainer *preDrawContainer, + int preDrawZero, int width, struct track *tg, struct hvGfx *hvg, + int xOff, int yOff, double graphUpperLimit, double graphLowerLimit, + double graphRange, enum trackVisibility vis, struct wigCartOptions *wigCart) /* Draw the graphs for all tracks in container. */ { double epsilon = graphRange / tg->lineHeight; -struct preDrawElement *preDraw = preDrawList->preDraw; +struct preDrawElement *preDraw = preDrawContainer->preDraw; Color *colorArray = makeColorArray(preDraw, width, preDrawZero, wigCart, tg, hvg); -struct preDrawContainer *preContainer; -for(preContainer = preDrawList; preContainer; preContainer = preContainer->next) - { - struct preDrawElement *preDraw = preContainer->preDraw; +struct wigGraphOutput *wgo = tg->wigGraphOutput; graphPreDraw(preDraw, preDrawZero, width, - tg, hvg, xOff, yOff, graphUpperLimit, graphLowerLimit, graphRange, + tg, wgo->image, wgo->vLine, wgo->xOff, wgo->yOff, + graphUpperLimit, graphLowerLimit, graphRange, epsilon, colorArray, vis, wigCart); - } freez(&colorArray); } void drawZeroLine(enum trackVisibility vis, enum wiggleGridOptEnum horizontalGrid, double graphUpperLimit, double graphLowerLimit, struct hvGfx *hvg, int xOff, int yOff, int width, int lineHeight) /* draw a line at y=0 on the graph */ { /* Do we need to draw a zero line ? * This is to be generalized in the future to allow horizontal grid * lines, perhaps user specified to indicate thresholds. */ @@ -1133,119 +1162,107 @@ Span = ptToInt(el->val); if ((Span < basesPerPixel) && (Span > usingDataSpan)) usingDataSpan = Span; if (Span < minimalSpan) minimalSpan = Span; } hashElFreeList(&elList); /* There may not be a span of 1, use whatever is lowest */ if (minimalSpan > usingDataSpan) usingDataSpan = minimalSpan; return usingDataSpan; } +void wigTrackSetGraphOutputDefault(struct track *tg, int xOff, int yOff, struct hvGfx *hvg) +/* Set up to draw on hvg if no other destination set already */ +{ +if (tg->wigGraphOutput == NULL) + tg->wigGraphOutput = wigGraphOutputSolid(xOff, yOff, hvg); +} + void wigDrawPredraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis, - struct preDrawContainer *preDrawList, int preDrawZero, + struct preDrawContainer *preContainer, int preDrawZero, int preDrawSize, double *retGraphUpperLimit, double *retGraphLowerLimit) /* Draw once we've figured out predraw (numerical values to graph) we draw it here. * This code is shared by wig, bigWig, and bedGraph drawers. */ { +wigTrackSetGraphOutputDefault(tg, xOff, yOff, hvg); enum wiggleYLineMarkEnum yLineOnOff; double yLineMark; /* determined from data */ double overallUpperLimit = wigEncodeStartingUpperLimit; double overallLowerLimit = wigEncodeStartingLowerLimit; double overallRange=0; /* determined from data */ double graphUpperLimit=0; /* scaling choice will set these */ double graphLowerLimit=0; /* scaling choice will set these */ double graphRange=0; /* scaling choice will set these */ double epsilon; /* range of data in one pixel */ struct wigCartOptions *wigCart = (struct wigCartOptions *) tg->extraUiData; yLineOnOff = wigCart->yLineOnOff; yLineMark = wigCart->yLineMark; /* width - width of drawing window in pixels * pixelsPerBase - pixels per base * basesPerPixel - calculated as 1.0/pixelsPerBase */ -struct preDrawContainer *preContainer; - -/* loop through all the preDraw containers */ -for(preContainer = preDrawList; preContainer; preContainer = preContainer->next) - { struct preDrawElement *preDraw = preContainer->preDraw; double thisOverallUpperLimit; double thisOverallLowerLimit; double thisGraphUpperLimit; double thisGraphLowerLimit; preDrawWindowFunction(preDraw, preDrawSize, wigCart->windowingFunction, wigCart->transformFunc); preDrawSmoothing(preDraw, preDrawSize, wigCart->smoothingWindow); overallRange = preDrawLimits(preDraw, preDrawZero, width, &thisOverallUpperLimit, &thisOverallLowerLimit); graphRange = preDrawAutoScale(preDraw, preDrawZero, width, wigCart->autoScale, &thisOverallUpperLimit, &thisOverallLowerLimit, &thisGraphUpperLimit, &thisGraphLowerLimit, &overallRange, &epsilon, tg->lineHeight, wigCart->maxY, wigCart->minY, wigCart->alwaysZero); - if (preContainer == preDrawList) // first time through - { - overallUpperLimit = thisOverallUpperLimit; - overallLowerLimit = thisOverallLowerLimit; - graphUpperLimit = thisGraphUpperLimit; - graphLowerLimit = thisGraphLowerLimit; - } - else - { - if (overallUpperLimit < thisOverallUpperLimit) overallUpperLimit = thisOverallUpperLimit; - if (overallLowerLimit > thisOverallLowerLimit) overallLowerLimit = thisOverallLowerLimit; - if (graphUpperLimit < thisGraphUpperLimit) graphUpperLimit = thisGraphUpperLimit; - if (graphLowerLimit > thisGraphLowerLimit) graphLowerLimit = thisGraphLowerLimit; - } - } overallRange = overallUpperLimit - overallLowerLimit; /* if we're autoscaling and the range is 0 this implies that all values * in the given range are the same. We create a bottom of the scale * by subtracting one from the only value. * This results in drawing a box that fills the range. */ if ((graphUpperLimit == graphLowerLimit)) { graphRange = 1.0; graphLowerLimit = graphUpperLimit - 1; } else { graphRange = graphUpperLimit - graphLowerLimit; } -graphAllInContainer(preDrawList, preDrawZero, width, tg, hvg, xOff, yOff, +graphPreDrawContainer(preContainer, preDrawZero, width, tg, hvg, xOff, yOff, graphUpperLimit, graphLowerLimit, graphRange, vis, wigCart); drawZeroLine(vis, wigCart->horizontalGrid, graphUpperLimit, graphLowerLimit, hvg, xOff, yOff, width, tg->lineHeight); drawArbitraryYLine(vis, wigCart->yLineOnOff, graphUpperLimit, graphLowerLimit, hvg, xOff, yOff, width, tg->lineHeight, wigCart->yLineMark, graphRange, wigCart->yLineOnOff); wigMapSelf(tg, hvg, seqStart, seqEnd, xOff, yOff, width); if (retGraphUpperLimit != NULL) *retGraphUpperLimit = graphUpperLimit;