3d2299a0b9690e88341c660965d913ad105970f2
kent
  Sun Jul 3 15:03:16 2022 -0700
Improving caching behavior.  Allowing it to cache less than a complete sequence. Handling reverse strand better.

diff --git src/hg/hgTracks/cds.c src/hg/hgTracks/cds.c
index c6cbe98..d9c24b9 100644
--- src/hg/hgTracks/cds.c
+++ src/hg/hgTracks/cds.c
@@ -1,36 +1,38 @@
 /* cds.c - code for coloring of bases, codons, or alignment differences. */
 
+
 /* Copyright (C) 2014 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 #include "common.h"
 #include "hCommon.h"
 #include "hash.h"
 #include "jksql.h"
 #include "memalloc.h"
 #include "dystring.h"
 #include "memgfx.h"
 #include "hvGfx.h"
 #include "dnaseq.h"
 #include "dnautil.h"
 #include "hdb.h"
 #include "psl.h"
 #include "fa.h"
 #include "genePred.h"
 #include "cds.h"
 #include "genbank.h"
 #include "twoBit.h"
+#include "cacheTwoBit.h"
 #include "hgTracks.h"
 #include "cdsSpec.h"
 #include "axt.h"
 #include "lrg.h"
 
 /*
  * WARNING: this code is incomprehensible:
  *     - variables named codon often contain amino acids, not condons
  *     - it does two passes, one undocumented function encodes both a
  *       color and an amino acid into the struct simpleFeature grayIx
  *       field and this is decoded in the second pass.
  *     - baseColorDrawItem doesn't draw a item, it draws a single codon,
  *       or a maybe even a single base.
  *     - there are many assumptions and state shared between this module
  *       and the simpleTracks.c.
@@ -44,40 +46,93 @@
 
 Color lighterShade(struct hvGfx *hvg, Color color, double percentLess);
 /* Find a color which is a percentless 'lighter' shade of color */
 /* Forward declaration */
 
 /* Array of colors used in drawing codons/bases/differences: */
 Color cdsColor[CDS_NUM_COLORS];
 boolean cdsColorsMade = FALSE;
 
 Color getCdsColor(int index)
 {
 assert(index < CDS_NUM_COLORS);
 return cdsColor[index];
 }
 
+boolean pslTargetToQueryRangeMap(struct psl *psl, int tStart, int tEnd, 
+    int *retQStart, int *retQEnd)
+/* Comes up with qStart/qEnd that corresponds to tStart/tEnd in psl. Returns
+ * FALSE if there's no corresponding part for target in query */
+{
+if (rangeIntersection(tStart, tEnd, psl->tStart, psl->tEnd) <= 0)
+    return FALSE;
+int qStart = psl->qStart, qEnd = psl->qEnd;
+boolean foundStart = FALSE, foundEnd = FALSE;
+int lastBqEnd = psl->qStart;
+int i;
+for (i=0; i<psl->blockCount; ++i)
+    {
+    int bSize = psl->blockSizes[i];
+    int bqStart = psl->qStarts[i];
+    int bqEnd = bqStart + bSize;
+    int btStart = psl->tStarts[i];
+    int btEnd = btStart + bSize;
+    if (!foundStart)
+	{
+	if (btStart <= tStart && tStart < btEnd)
+	    {
+	    qStart = bqStart + (tStart - btStart);
+	    foundStart = TRUE;
+	    }
+	else if (btStart >= tStart)
+	    {
+	    qStart = bqStart;
+	    foundStart = TRUE;
+	    }
+	}
+    if (!foundEnd)
+        {
+	if (btStart < tEnd && tEnd <= btEnd)
+	    {
+	    qEnd = bqStart + (tEnd - btStart);
+	    foundEnd = TRUE;
+	    break;
+	    }
+	else if (btStart >= tEnd)
+	    {
+	    qEnd = lastBqEnd;
+	    foundEnd = TRUE;
+	    break;
+	    }
+	}
+    lastBqEnd = bqEnd;
+    }
+*retQStart = qStart;
+*retQEnd = qEnd;
+return TRUE;
+}
+
+
 static void drawScaledBoxWithText(struct hvGfx *hvg, 
                                         int chromStart, int chromEnd,
                                         double scale, int xOff, int y,
                                         int height, Color color, int score,
                                         MgFont *font, char *text, bool zoomed,
                                         int winStart, int maxPixels, boolean isCoding, boolean justifyString)
 /* Draw a box scaled from chromosome to window coordinates with
    a codon or set of 3 or less bases drawn in the box. */
 {
-
 /*first draw the box itself*/
 drawScaledBox(hvg, chromStart, chromEnd, scale, xOff, y, height, 
 		    color);
 
 /*draw text in box if space, and align properly for codons or DNA*/
 if (zoomed)
     {
     int i;
     Color textColor = hvGfxContrastingColor(hvg, color);
     int x1, x2, w;
     x1 = round((double)(chromStart-winStart)*scale) + xOff;
     x2 = round((double)(chromEnd-winStart)*scale) + xOff;
     if (x2 >= maxPixels)
         x2 = maxPixels - 1;
     w = x2-x1;
@@ -420,60 +475,67 @@
     {
     Color ci = tg->itemColor(tg, lf, hvg);
     if (sameString(tickColor, "contrastingColor"))
 	c = hvGfxContrastingColor(hvg, ci);
     else if (sameString(tickColor, "lighterShade"))
 	c = lighterShade(hvg, ci, 1.5);
     }
 for (sf = lf->components; sf != NULL; sf = sf->next)
     {
     int s = max(winStart, sf->start);
     int e = min(winEnd, sf->end);
     if (s > winEnd || e < winStart)
       continue;
     if (e > s)
 	{
-	int mrnaS = -1;
+	int mrnaS = -1, mrnaE = 0;
 	if (psl)
-	    mrnaS = convertCoordUsingPsl(s, psl);
+	    {
+	    // mrnaS = convertCoordUsingPsl(s, psl);
+	    pslTargetToQueryRangeMap(psl, s, e, &mrnaS, &mrnaE);
+	    }
 	else
+	    {
 	    mrnaS = sf->qStart + (s - sf->start);
+	    }
 	if(mrnaS >= 0)
 	    {
 	    int i;
 	    for (i=0; i < (e - s); i++)
 		{
 		if (qSeq->dna[mrnaS+i-qOffset] != winDna[s-winStart+i])
 		    drawScaledBox(hvg,  s+i, s+i+1, scale, xOff, y+1, heightPer-2, c);
 		}
 	    }
 	}
     }
 }
 
 
-static void maskDiffString( char *retStr, char *s1, char *s2, char mask )
+static void maskDiffString( char *retStr, char *s1, char *s2, char mask, int size)
 /*copies s1, masking off similar characters, and returns result into retStr.
  *if strings are of different size it stops after s1 is done.*/
 {
-int s1Len = strlen(s1);
-memset(retStr, mask, s1Len);
+memset(retStr, mask, size);
 int i;
-for (i=0; i < s1Len; i++)
+for (i=0; i < size; i++)
+    {
+    char c = s1[i];
+    if (c != s2[i])
 	{
-    if (s1[i] != s2[i])
-	retStr[i] = s1[i];
+	retStr[i] = c;
+	}
     }
 retStr[i] = '\0';
 }
 
 Color lighterShade(struct hvGfx *hvg, Color color, double percentLess)
     /* Get lighter shade of a color, with a variable level */ 
 {
     struct rgbColor rgbColor =  hvGfxColorIxToRgb(hvg, color); 
     rgbColor.r = (int)((rgbColor.r+127)/percentLess);
     rgbColor.g = (int)((rgbColor.g+127)/percentLess);
     rgbColor.b = (int)((rgbColor.b+127)/percentLess);
     return hvGfxFindColorIx(hvg, rgbColor.r, rgbColor.g, rgbColor.b);
 }
 
 
@@ -980,72 +1042,45 @@
 /* seqSource is: extFile seqTbl extFileTbl */
 static struct dyString *buf = NULL;
 if (buf == NULL)
     buf = dyStringNew(0);
 dyStringClear(buf);
 dyStringAppend(buf, seqSource);
 char *words[3];
 int nwords = chopByWhite(buf->string, words, ArraySize(words));
 if ((nwords != ArraySize(words)) || !sameString(words[0], "extFile"))
     errAbort("invalid %s track setting: %s", BASE_COLOR_USE_SEQUENCE,
              seqSource);
 return hDnaSeqGet(database, name, words[1], words[2]);
 }
 
 
-static struct dnaSeq *fetchCachedTwoBitSeq(char *url, char *seqName, boolean doRc)
+static struct dnaSeq *fetchCachedTwoBitSeq(char *url, char *seqName, 
+    int seqStart, int seqEnd, boolean doRc, int *retSeqOffset)
 /* fetch a sequence from a 2bit.  Caches open two bit files and sequence in 
  * both forward and reverse strand */
 {
-/* Init static url hash */
-static struct hash *urlHash = NULL;  // hash of open files
-if (urlHash == NULL)
-    urlHash = hashNew(0);
-
-/* Get cache for a particular two bit URL */
-struct twoBitCache
-/* An open two bit file and a hash of sequences in it */
-    {
-    struct twoBitFile *tbf;
-    struct hash *seqHash;
-    struct hash *rcSeqHash;
-    };
-struct twoBitCache *cache = hashFindVal(urlHash, url);
+/* Init static url cache */
+static struct cacheTwoBitRanges *cache = NULL;  // hash of open files
 if (cache == NULL)
-    {
-    AllocVar(cache);
-    cache->tbf = twoBitOpen(url);
-    cache->seqHash = hashNew(0);
-    cache->rcSeqHash = hashNew(0);
-    hashAdd(urlHash, url, cache);
-    }
-struct hash *seqHash = (doRc ? cache->rcSeqHash : cache->seqHash);
-struct dnaSeq *seq = hashFindVal(seqHash, seqName);
-
-if (seq == NULL)
-    {
-    seq = twoBitReadSeqFrag(cache->tbf, seqName, 0, 0);
-    touppers(seq->dna);
-    if (doRc)
-        reverseComplement(seq->dna, seq->size);
-    hashAdd(seqHash, seqName, seq);
-    }
-return seq;
+    cache = cacheTwoBitRangesNew(TRUE);
+return cacheTwoBitRangesMayFetch(cache, url, seqName, seqStart, seqEnd, doRc, retSeqOffset);
 }
 
-static struct dnaSeq *maybeGetSeqUpper(struct linkedFeatures *lf, char *mrnaName,
-				       char *tableName, struct track *tg, boolean doRc)
+static struct dnaSeq *maybeGetSeqUpper(struct linkedFeatures *lf, 
+		    char *mrnaName, int mrnaStart, int mrnaEnd,
+		    char *tableName, struct track *tg, boolean doRc, int *retMrnaOffset)
 /* Look up the sequence in genbank tables (hGenBankGetMrna also searches 
  * seq if it can't find it in GB tables) or user's blat sequence, 
  * uppercase and return it if we find it, return NULL if we don't find it. */
 {
 boolean doUpper = TRUE;
 struct dnaSeq *mrnaSeq = NULL;
 char *name = getItemDataName(tg, mrnaName);
 if (sameString(tableName,"refGene") || sameString(tableName,"refSeqAli"))
     mrnaSeq = hGenBankGetMrna(database, name, "refMrna");
 else
     {
     char *seqSource = trackDbSetting(tg->tdb, BASE_COLOR_USE_SEQUENCE);
     if (seqSource != NULL)
 	{
 	if (sameString(seqSource, "ss"))
@@ -1076,31 +1111,31 @@
 		errAbort("baseColorDrawSetup: sequence for track '%s' not loaded when sequence option is set in trackDb\n", tg->track);
 	    mrnaSeq = newDnaSeq(cloneString(lf->extra), strlen(lf->extra), lf->extra);
 	    if (lf->orientation == -1)
 		reverseComplement(mrnaSeq->dna, mrnaSeq->size);
 	    }
 	else if (sameString("lrg", seqSource))
 	    {
 	    struct lrg *lrg = lf->original;
 	    mrnaSeq = lrgReconstructSequence(lrg, database);
 	    }
 	else if (sameString("2bit", seqSource))
 	    {
 	    char *url = trackDbSetting(tg->tdb, "otherTwoBitUrl");
 	    if (url == NULL)
 		errAbort("missing otherTwoBitUrl in baseColorUseSequence 2bit trackDb setting");
-	    mrnaSeq = fetchCachedTwoBitSeq(url, name, doRc);
+	    mrnaSeq = fetchCachedTwoBitSeq(url, name, mrnaStart, mrnaEnd, doRc, retMrnaOffset);
 	    doRc = FALSE;	    // Handled it already
 	    doUpper = FALSE;    // Handled it already
 	    }
 	else if (startsWith("table ", seqSource))
 	    {
 	    char *table = seqSource;
 	    nextWord(&table);
 	    mrnaSeq = hGenBankGetMrna(database, name, table);
 	    }
 	else if (startsWithWord("db", seqSource))
 	    {
 	    char *sourceDb = seqSource;
 	    nextWord(&sourceDb);
 	    if (isEmpty(sourceDb))
 		sourceDb = database;
@@ -1477,31 +1512,31 @@
 		 starts[i] < winEnd && exonStart > winStart)
 	    {
             AllocVar(sf);
             sf->start = starts[i];
             sf->end = exonStart;
             slAddHead(&sfList, sf);
 	    }
 	} // end loop on exons
 
     if(posStrand)
         slReverse(&sfList);
     return(sfList);
 }
 
 
-static void getMrnaBases(struct psl *psl, struct dnaSeq *mrnaSeq,
+static void getMrnaBases(struct psl *psl, struct dnaSeq *mrnaSeq, int mrnaOffset,
 			 int mrnaS, int s, int e, boolean isRc,
 			 char retMrnaBases[4], boolean *retQueryInsertion)
 /* Get mRNA bases for the current mRNA codon triplet.  If this is a split
  * codon, retrieve the adjacent mRNA bases to make a full triplet. */
 {
 int size = e - s;
 if(size < 3)
     {
     if (mrnaSeq != NULL && mrnaS-(3-size) > 0)
         {
 	int i=0;
 	int idx = -1;
 	int newIdx = -1;
 	unsigned *gaps = NULL;
 	boolean appendAtStart = FALSE;
@@ -1523,80 +1558,89 @@
 	    else if (e == tEnd)
 		{
 		idx = i+1;
 		appendAtStart = FALSE;
 		break;
 		}
 	    }
 
 	getHiddenGaps(psl, gaps);
 	if(idx >= 0 && gaps[idx] > 0 && retQueryInsertion != NULL)
 	    *retQueryInsertion = TRUE;
 
 	if (!appendAtStart)
             {
 	    newIdx = mrnaS + size;
-	    memcpy(retMrnaBases, &mrnaSeq->dna[mrnaS], size);
-	    memcpy(retMrnaBases+size, &mrnaSeq->dna[newIdx], 3-size);
+	    memcpy(retMrnaBases, &mrnaSeq->dna[mrnaS - mrnaOffset], size);
+	    memcpy(retMrnaBases+size, &mrnaSeq->dna[newIdx - mrnaOffset], 3-size);
             }
 	else
             {
 	    newIdx = mrnaS - (3 - size);
-	    memcpy(retMrnaBases, &mrnaSeq->dna[newIdx], 3);
+	    memcpy(retMrnaBases, &mrnaSeq->dna[newIdx - mrnaOffset], 3);
             }
         }
     else
 	{
 	memcpy(retMrnaBases, "NNN", 3);
 	}
     }
 else
-    memcpy(retMrnaBases, &mrnaSeq->dna[mrnaS], 3);
+    memcpy(retMrnaBases, &mrnaSeq->dna[mrnaS - mrnaOffset], 3);
 retMrnaBases[3] = '\0';
 if (isRc)
     reverseComplement(retMrnaBases, strlen(retMrnaBases));
 }
 
 static void drawDiffTextBox(struct hvGfx *hvg, int xOff, int y, 
         double scale, int heightPer, MgFont *font, Color color, 
         char *chrom, unsigned s, unsigned e, struct simpleFeature *sf, struct psl *psl, 
-        struct dnaSeq *mrnaSeq, struct linkedFeatures *lf,
+        struct dnaSeq *mrnaSeq, int mrnaOffset, struct linkedFeatures *lf,
         int grayIx, enum baseColorDrawOpt drawOpt,
         int maxPixels, Color *trackColors, Color ixColor)
 {
-int mrnaS = -1;
+int mrnaS = -1, mrnaE = -1;
+/* Clip s and e to what is actually visible */
+if (s < winStart) s = winStart;
+if (e > winEnd) e = winEnd;
 if (psl)
-    mrnaS = convertCoordUsingPsl( s, psl ); 
+    pslTargetToQueryRangeMap(psl, max(psl->tStart, s), min(psl->tEnd, e), 
+	&mrnaS, &mrnaE);
 else if (sf)
     mrnaS = sf->qStart;
 if (mrnaS >= 0)
     {
+    if (mrnaS < mrnaOffset)
+         {
+	 warn("curious mrnaS %d < mrnaOffest %d\n", mrnaS, mrnaOffset);
+	 mrnaS = mrnaOffset;
+	 }
     struct dyString *dyMrnaSeq = dyStringNew(256);
     char mrnaBases[4];
     char genomicCodon[2];
     char mrnaCodon[2]; 
     boolean queryInsertion = FALSE;
     boolean isCoding = (drawOpt == baseColorDrawItemCodons || drawOpt == baseColorDrawDiffCodons);
 
     mrnaBases[0] = '\0';
     if (psl && isCoding)
-	getMrnaBases(psl, mrnaSeq, mrnaS, s, e, (lf->orientation == -1),
+	getMrnaBases(psl, mrnaSeq, mrnaOffset, mrnaS, s, e, (lf->orientation == -1),
 		    mrnaBases, &queryInsertion);
     if (queryInsertion && isCoding)
 	color = cdsColor[CDS_QUERY_INSERTION];
 
-    dyStringAppendN(dyMrnaSeq, (char*)&mrnaSeq->dna[mrnaS], e-s);
+    dyStringAppendN(dyMrnaSeq, (char*)&mrnaSeq->dna[mrnaS - mrnaOffset], e-s);
 
     if (drawOpt == baseColorDrawItemBases)
 	{
 	if (cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE))
 	    complement(dyMrnaSeq->string, dyMrnaSeq->stringSize);
 	drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, 
 				    color, lf->score, font, dyMrnaSeq->string,
 				    zoomedToBaseLevel, winStart, maxPixels, isCoding, TRUE);
 	}
     else if (drawOpt == baseColorDrawItemCodons)
 	{
 	if (e <= lf->tallEnd)
 	    {
 	    boolean startColor = FALSE;
 	    /* re-set color of this block based on mrna codons rather than
@@ -1610,31 +1654,32 @@
 	    if (startColor && sameString(mrnaCodon,"M"))
                 color = cdsColor[CDS_START];
 	    drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, 
 					color, lf->score, font, mrnaCodon,
 					zoomedToCodonLevel, winStart,
 					maxPixels, isCoding, TRUE);
 	    }
 	else
 	    drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
 	}
     else if (drawOpt == baseColorDrawDiffBases)
 	{
 	char *diffStr = NULL;
 	char *genoDna = getCachedDna(s, e);
 	diffStr = needMem(sizeof(char) * (e - s + 1));
-	maskDiffString(diffStr, dyMrnaSeq->string, genoDna, ' ');
+	maskDiffString(diffStr, dyMrnaSeq->string, genoDna, ' ', dyMrnaSeq->stringSize);
+	// fprintf(stderr, "drawOpt =- diffBases. %d %d %d %d\n", (int)strlen(genoDna), (int)strlen(dyMrnaSeq->string), (int)dyMrnaSeq->stringSize, e-s);
 	if (cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE))
 	    complement(diffStr, strlen(diffStr));
 	drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, 
 				    color, lf->score, font, diffStr, 
 				    zoomedToBaseLevel, winStart, maxPixels, isCoding, TRUE);
 	freeMem(diffStr);
 	}
     else if (drawOpt == baseColorDrawDiffCodons)
 	{
 	if (e <= lf->tallEnd)
 	    {
 	    /* Color codons red wherever mrna differs from genomic;
 	     * keep the odd/even cycle of dark/light shades. */
 	    colorAndCodonFromGrayIx(hvg, genomicCodon, grayIx, ixColor);
 	    int mrnaGrayIx = mrnaCodonToGrayIx(mrnaBases, genomicCodon[0],
@@ -1652,42 +1697,45 @@
 					    winStart, maxPixels, isCoding, TRUE);
 		}
 	    else
 		drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
 	    }
 	else
 	    drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
 	}
     else if (drawOpt != baseColorDrawCds)
         errAbort("Unknown drawOpt: %d<br>\n", drawOpt);
 
     dyStringFree(&dyMrnaSeq);
     }
 else
     {
+    if (s < e)
+	{
 	/*show we have an error by coloring entire exon block yellow*/
 	drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, MG_YELLOW);
 	// FIXME: this shouldn't ever happen, should be an errAbort
 	warn("Bug: drawDiffTextBox: convertCoordUsingPsl failed<br>\n");
 	}
     }
+}
 
 void baseColorDrawItem(struct track *tg,  struct linkedFeatures *lf, 
 		       int grayIx, struct hvGfx *hvg, int xOff, 
                        int y, double scale, MgFont *font, int s, int e, 
                        int heightPer, boolean zoomedToCodonLevel, 
-                       struct dnaSeq *mrnaSeq, int qOffset, struct simpleFeature *sf, struct psl *psl, 
+                       struct dnaSeq *qSeq, int qOffset, struct simpleFeature *sf, struct psl *psl, 
 		       enum baseColorDrawOpt drawOpt,
                        int maxPixels, int winStart, 
                        Color originalColor)
 /* Draw codon/base-colored item. */
 {
 char codon[64] = " ";
 Color color = colorAndCodonFromGrayIx(hvg, codon, grayIx, originalColor);
 if (sf->codonIndex && ( e - s >= 3))  // don't put exon numbers on split codons because there isn't space.
     safef(codon, sizeof(codon), "%c %d", codon[0], sf->codonIndex);
 /* When we are zoomed out far enough so that multiple bases/codons share the 
  * same pixel, we have to draw differences in a separate pass (baseColorOverdrawDiff)
  * so don't waste time drawing the differences here: */
 boolean zoomedOutToPostProcessing =
     ((drawOpt == baseColorDrawDiffBases && !zoomedToBaseLevel) ||
      (drawOpt == baseColorDrawDiffCodons && !zoomedToCdsColorLevel));
@@ -1697,64 +1745,63 @@
     if (lf->highlightColor)
 	{
 	drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, 
 			    lf->highlightColor);
 	drawScaledBoxWithText(hvg, s, e, scale, xOff, y+1, heightPer-2, 
 				    color, lf->score, font, codon, 
 				    zoomedToCodonLevel, winStart, maxPixels, TRUE, !sf->codonIndex);
 	}
     else
 	{
 	drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, 
 				    color, lf->score, font, codon, 
 				    zoomedToCodonLevel, winStart, maxPixels, TRUE, !sf->codonIndex);
 	}
     }
-else if (mrnaSeq != NULL && (psl != NULL || sf != NULL) && !zoomedOutToPostProcessing &&
+else if (qSeq != NULL && (psl != NULL || sf != NULL) && !zoomedOutToPostProcessing &&
 	 drawOpt != baseColorDrawGenomicCodons && drawOpt != baseColorDrawOff)
     {
     if (lf->highlightColor)
 	{
 	drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, 
 			    lf->highlightColor);
 	drawDiffTextBox(hvg, xOff+1, y+1, scale, heightPer-2, font, 
-			color, chromName, s, e, sf, psl, mrnaSeq, lf,
+			color, chromName, s, e, sf, psl, qSeq, qOffset, lf,
 			grayIx, drawOpt, maxPixels,
 			tg->colorShades, originalColor);
 	}
     else
 	{
 	drawDiffTextBox(hvg, xOff, y, scale, heightPer, font, 
-			color, chromName, s, e, sf, psl, mrnaSeq, lf,
+			color, chromName, s, e, sf, psl, qSeq, qOffset, lf,
 			grayIx, drawOpt, maxPixels,
 			tg->colorShades, originalColor);
 	}
     }
 else
     {
     /* revert to normal coloring */
     if (lf->highlightColor)
 	{
 	drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, 
 			    lf->highlightColor);
 	drawScaledBox(hvg, s, e, scale, xOff+1, y+1, heightPer -2, 
 			    color);
 	}
     else
 	{
-	drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, 
-			    color);
+	drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
 	}
     }
 }
 
 
 static void drawCdsDiffCodonsOnly(struct track *tg,  struct linkedFeatures *lf,
 			   struct hvGfx *hvg, int xOff,
 			   int y, double scale, int heightPer,
 			   struct dnaSeq *qSeq, int qOffset, struct psl *psl,
 			   int winStart)
 /* Draw red boxes only where mRNA codons differ from genomic.  This assumes
  * that lf has been drawn already, we're zoomed out past zoomedToCdsColorLevel,
  * we're not in dense mode etc. */
 {
 struct simpleFeature *sf = NULL;
@@ -1770,31 +1817,31 @@
     if (s < lf->tallStart)
 	s = lf->tallStart;
     if (e > lf->tallEnd)
 	e = lf->tallEnd;
     if (s > winEnd || e < winStart)
       continue;
     if (e > s)
 	{
 	int mrnaS = convertCoordUsingPsl( s, psl ); 
 	if (mrnaS >= 0)
 	    {
 	    char mrnaBases[4];
 	    char genomicCodon[2], mrnaCodon;
 	    boolean queryInsertion = FALSE;
 	    Color color = cdsColor[CDS_STOP];
-	    getMrnaBases(psl, qSeq, mrnaS, s, e, (lf->orientation == -1),
+	    getMrnaBases(psl, qSeq, qOffset, mrnaS, s, e, (lf->orientation == -1),
 			 mrnaBases, &queryInsertion);
 	    if (queryInsertion)
 		color = cdsColor[CDS_QUERY_INSERTION];
 	    mrnaCodon = baseColorLookupCodon(mrnaBases);
 	    colorAndCodonFromGrayIx(hvg, genomicCodon, sf->grayIx, dummyColor);
 	    if (queryInsertion)
 		drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
             if (mrnaCodon != genomicCodon[0])
                 {
                 if (mrnaCodon != genomicCodon[0] && protEquivalent(genomicCodon[0], mrnaCodon))
                     color = cdsColor[CDS_SYN_PROT];
                 /* this was a call to drawScaledBoxBlend, but this breaks under
                  * 32-bit color, so for the moment we're going to depend 
                  * on the painter's algorithm */
 		drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
@@ -2110,70 +2157,77 @@
 {
 enum baseColorDrawOpt drawOpt = baseColorGetDrawOpt(tg);
 boolean indelShowDoubleInsert, indelShowQueryInsert, indelShowPolyA;
 indelEnabled(cart, (tg ? tg->tdb : NULL), basesPerPixel,
 	     &indelShowDoubleInsert, &indelShowQueryInsert, &indelShowPolyA);
 
 if (drawOpt <= baseColorDrawOff && !(indelShowQueryInsert || indelShowPolyA))
     return drawOpt;
 
 checkTrackInited(tg, "calling baseColorDrawSetup");
 
 /* If we are using item sequence, fetch alignment and sequence: */
 struct psl *psl = NULL;
 struct dnaSeq *mrnaSeq = NULL;
 int mrnaOffset = 0;
+int mrnaStart = 0, mrnaEnd = 0;
 if (indelShowQueryInsert || indelShowPolyA || drawOpt > baseColorDrawOff)
     {
     char *type = tg->tdb->type;
     boolean needPsl = FALSE;
     char *qName = lf->name;
     if (sameString("lrg", tg->tdb->track))
 	{
 	psl = lrgToPsl(lf->original, hChromSize(database, chromName));
 	needPsl = TRUE;
 	}
     else if (startsWith("psl", type) || sameString("bigPsl", type) || startsWithWord("bam", type))
 	{
 	psl = (struct psl *)(lf->original);
 	needPsl = TRUE;
 	}
     else if (startsWithWord("chain", type) || startsWithWord("bigChain", type))
 	{
 	qName = cloneFirstWord(lf->name);
         psl = linkedFeatureToPsl(lf, qName, chromName, hChromSize(database, chromName));
 	needPsl = TRUE;
 	}
     boolean doRc = FALSE;
     if (needPsl)
 	{
         if (psl == NULL)
 	    drawOpt = baseColorDrawOff;
 	else
+	    {
 	    doRc = (psl->strand[0] == '-' || psl->strand[1] == '-');
+	    pslTargetToQueryRangeMap(psl, max(psl->tStart, winStart), min(psl->tEnd, winEnd),
+		&mrnaStart, &mrnaEnd);
+	    }
 	}
     /* Do we need the sequence for display, if so get it */
     if (drawOpt == baseColorDrawItemBases ||
 	drawOpt == baseColorDrawDiffBases ||
 	drawOpt == baseColorDrawItemCodons ||
 	drawOpt == baseColorDrawDiffCodons || indelShowPolyA)
 	{
-	mrnaSeq = maybeGetSeqUpper(lf, qName, tg->table, tg, doRc);
+	mrnaSeq = maybeGetSeqUpper(lf, qName, mrnaStart, mrnaEnd, tg->table, tg, doRc, &mrnaOffset);
 	if (mrnaSeq == NULL) 
+	    {
 	    drawOpt = baseColorDrawOff;
 	    }
 	}
+    }
 *retPsl = psl;
 *retMrnaSeq = mrnaSeq;
 *retMrnaOffset = mrnaOffset;
 return drawOpt;
 }
 
 void baseColorDrawRulerCodons(struct hvGfx *hvg, struct simpleFeature *sfList,
                 double scale, int xOff, int y, int height, MgFont *font, 
                 int winStart, int maxPixels, bool zoomedToText)
 /* Draw amino acid translation of genomic sequence based on a list
    of codons. Used for browser ruler in full mode*/
 {
 struct simpleFeature *sf;
 
 if (!cdsColorsMade)