lib/memgfx.c

Go to the documentation of this file.
00001 /* memgfx - routines for drawing on bitmaps in memory.
00002  * Currently limited to 256 color bitmaps. 
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 "memgfx.h"
00009 #include "gemfont.h"
00010 #include "localmem.h"
00011 #include "vGfx.h"
00012 #include "vGfxPrivate.h"
00013 #include "colHash.h"
00014 
00015 static char const rcsid[] = "$Id: memgfx.c,v 1.47 2007/04/15 00:33:35 galt Exp $";
00016 
00017 static void mgSetDefaultColorMap(struct memGfx *mg)
00018 /* Set up default color map for a memGfx. */
00019 {
00020 /* Note dependency in order here and in MG_WHITE, MG_BLACK, etc. */
00021 int i;
00022 for (i=0; i<ArraySize(mgFixedColors); ++i)
00023     {
00024     struct rgbColor *c = &mgFixedColors[i];
00025     mgAddColor(mg, c->r, c->g, c->b);
00026     }
00027 }
00028 
00029 
00030 void mgSetClip(struct memGfx *mg, int x, int y, int width, int height)
00031 /* Set clipping rectangle. */
00032 {
00033 int x2, y2;
00034 if (x < 0)
00035     x = 0;
00036 if (y < 0)
00037     y = 0;
00038 x2 = x + width;
00039 if (x2 > mg->width)
00040     x2 = mg->width;
00041 y2 = y + height;
00042 if (y2 > mg->height)
00043     y2 = mg->height;
00044 mg->clipMinX = x;
00045 mg->clipMaxX = x2;
00046 mg->clipMinY = y;
00047 mg->clipMaxY = y2;
00048 }
00049 
00050 void mgUnclip(struct memGfx *mg)
00051 /* Set clipping rect cover full thing. */
00052 {
00053 mgSetClip(mg, 0,0,mg->width, mg->height);
00054 }
00055 
00056 struct memGfx *mgNew(int width, int height)
00057 /* Return new memGfx. Note new pixel memory is uninitialized */
00058 {
00059 struct memGfx *mg;
00060 
00061 mg = needMem(sizeof(*mg));
00062 mg->pixels = needLargeMem(width*height);
00063 mg->width = width;
00064 mg->height = height;
00065 mg->colorHash = colHashNew();
00066 mgSetDefaultColorMap(mg);
00067 mgUnclip(mg);
00068 return mg;
00069 }
00070 
00071 void mgClearPixels(struct memGfx *mg)
00072 /* Set all pixels to background. */
00073 {
00074 zeroBytes(mg->pixels, mg->width*mg->height);
00075 }
00076 
00077 Color mgFindColor(struct memGfx *mg, unsigned char r, unsigned char g, unsigned char b)
00078 /* Returns closest color in color map to rgb values.  If it doesn't
00079  * already exist in color map and there's room, it will create
00080  * exact color in map. */
00081 {
00082 struct colHashEl *che;
00083 if ((che = colHashLookup(mg->colorHash, r, g, b)) != NULL)
00084     return che->ix;
00085 if (mgColorsFree(mg))
00086     return mgAddColor(mg, r, g, b);
00087 return mgClosestColor(mg, r, g, b);
00088 }
00089 
00090 
00091 struct rgbColor mgColorIxToRgb(struct memGfx *mg, int colorIx)
00092 /* Return rgb value at color index. */
00093 {
00094 return mg->colorMap[colorIx];
00095 }
00096 
00097 Color mgClosestColor(struct memGfx *mg, unsigned char r, unsigned char g, unsigned char b)
00098 /* Returns closest color in color map to r,g,b */
00099 {
00100 struct rgbColor *c = mg->colorMap;
00101 int closestDist = 0x7fffffff;
00102 int closestIx = -1;
00103 int dist, dif;
00104 int i;
00105 for (i=0; i<mg->colorsUsed; ++i)
00106     {
00107     dif = c->r - r;
00108     dist = dif*dif;
00109     dif = c->g - g;
00110     dist += dif*dif;
00111     dif = c->b - b;
00112     dist += dif*dif;
00113     if (dist < closestDist)
00114         {
00115         closestDist = dist;
00116         closestIx = i;
00117         }
00118     ++c;
00119     }
00120 return closestIx;
00121 }
00122 
00123 
00124 Color mgAddColor(struct memGfx *mg, unsigned char r, unsigned char g, unsigned char b)
00125 /* Adds color to end of color map if there's room. */
00126 {
00127 int colIx = mg->colorsUsed;
00128 if (colIx < 256)
00129     {
00130     struct rgbColor *c = mg->colorMap + mg->colorsUsed;
00131     c->r = r;
00132     c->g = g;
00133     c->b = b;
00134     mg->colorsUsed += 1;
00135     colHashAdd(mg->colorHash, r, g, b, colIx);
00136     }
00137 return (Color)colIx;
00138 }
00139 
00140 int mgColorsFree(struct memGfx *mg)
00141 /* Returns # of unused colors in color map. */
00142 {
00143 return 256-mg->colorsUsed;
00144 }
00145 
00146 void mgFree(struct memGfx **pmg)
00147 {
00148 struct memGfx *mg = *pmg;
00149 if (mg != NULL)
00150     {
00151     if (mg->pixels != NULL)
00152         freeMem(mg->pixels);
00153     colHashFree(&mg->colorHash);
00154     zeroBytes(mg, sizeof(*mg));
00155     freeMem(mg);
00156     }
00157 *pmg = NULL;
00158 }
00159 
00160 static void nonZeroCopy(Color *d, Color *s, int width)
00161 /* Copy non-zero colors. */
00162 {
00163 Color c;
00164 int i;
00165 for (i=0; i<width; ++i)
00166     {
00167     if ((c = s[i]) != 0)
00168         d[i] = c;
00169     }
00170 }
00171 
00172 static void mgPutSegMaybeZeroClear(struct memGfx *mg, int x, int y, int width, Color *dots, boolean zeroClear)
00173 /* Put a series of dots starting at x, y and going to right width pixels.
00174  * Possibly don't put zero dots though. */
00175 {
00176 int x2;
00177 Color *pt;
00178 if (y < mg->clipMinY || y > mg->clipMaxY)
00179     return;
00180 x2 = x + width;
00181 if (x2 > mg->clipMaxX)
00182     x2 = mg->clipMaxX;
00183 if (x < mg->clipMinX)
00184     {
00185     dots += mg->clipMinX - x;
00186     x = mg->clipMinX;
00187     }
00188 width = x2 - x;
00189 if (width > 0)
00190     {
00191     pt = _mgPixAdr(mg, x, y);
00192     if (zeroClear)
00193         nonZeroCopy(pt, dots, width);
00194     else
00195         memcpy(pt, dots, width);
00196     }
00197 }
00198 
00199 void mgVerticalSmear(struct memGfx *mg,
00200         int xOff, int yOff, int width, int height, 
00201         unsigned char *dots, boolean zeroClear)
00202 /* Put a series of one 'pixel' width vertical lines. */
00203 {
00204 while (--height >= 0)
00205     {
00206     mgPutSegMaybeZeroClear(mg, xOff, yOff, width, dots, zeroClear);
00207     ++yOff;
00208     }
00209 }
00210 
00211 void mgDrawBox(struct memGfx *mg, int x, int y, int width, int height, Color color)
00212 {
00213 int i;
00214 Color *pt;
00215 int x2 = x + width;
00216 int y2 = y + height;
00217 int wrapCount;
00218 
00219 if (x < mg->clipMinX)
00220     x = mg->clipMinX;
00221 if (y < mg->clipMinY)
00222     y = mg->clipMinY;
00223 if (x2 > mg->clipMaxX)
00224     x2 = mg->clipMaxX;
00225 if (y2 > mg->clipMaxY)
00226     y2 = mg->clipMaxY;
00227 width = x2-x;
00228 height = y2-y;
00229 if (width > 0 && height > 0)
00230     {
00231     pt = _mgPixAdr(mg,x,y);
00232     /*colorBin[x][color]++;  increment color count for this pixel */
00233     wrapCount = _mgBpr(mg) - width;
00234     while (--height >= 0)
00235         {
00236         i = width;
00237         while (--i >= 0)
00238             *pt++ = color;
00239         pt += wrapCount;
00240         }
00241     }
00242 }
00243 
00244 void mgBrezy(struct memGfx *mg, int x1, int y1, int x2, int y2, Color color,
00245         int yBase, boolean fillFromBase)
00246 /* Brezenham line algorithm.  Optionally fill in under line. */
00247 {
00248 if (x1 == x2)
00249     {
00250     int y,height;
00251     if (y1 > y2)
00252         {
00253         y = y2;
00254         height = y1-y2+1;
00255         }
00256     else
00257         {
00258         y = y1;
00259         height = y2-y1+1;
00260         }
00261     if (fillFromBase)
00262         {
00263         if (y < yBase)
00264             mgDrawBox(mg, x1, y, 1, yBase-y, color);
00265         }
00266     else
00267         mgDrawBox(mg, x1, y, 1, height, color);
00268     }
00269 else if (y1 == y2)
00270     {
00271     int x,width;
00272     if (x1 > x2)
00273         {
00274         x = x2;
00275         width = x1-x2+1;
00276         }
00277     else
00278         {
00279         x = x1;
00280         width = x2-x1+1;
00281         }
00282     if (fillFromBase)
00283         {
00284         if (y1 < yBase)
00285             mgDrawBox(mg, x, y1, width, yBase - y1, color);
00286         }
00287     else
00288         {
00289         mgDrawBox(mg, x, y1, width, 1, color);
00290         }
00291     }
00292 else
00293     {
00294     int duty_cycle;
00295     int incy;
00296     int delta_x, delta_y;
00297     int dots;
00298     delta_y = y2-y1;
00299     delta_x = x2-x1;
00300     if (delta_y < 0) 
00301         {
00302         delta_y = -delta_y;
00303         incy = -1;
00304         }
00305     else
00306         {
00307         incy = 1;
00308         }
00309     if (delta_x < 0) 
00310         {
00311         delta_x = -delta_x;
00312         incy = -incy;
00313         x1 = x2;
00314         y1 = y2;
00315         }
00316     duty_cycle = (delta_x - delta_y)/2;
00317     if (delta_x >= delta_y)
00318         {
00319         dots = delta_x+1;
00320         while (--dots >= 0)
00321             {
00322             if (fillFromBase)
00323                 {
00324                 if (y1 < yBase)
00325                     mgDrawBox(mg,x1,y1,1,yBase-y1,color);
00326                 }
00327             else
00328                 mgPutDot(mg,x1,y1,color);
00329             duty_cycle -= delta_y;
00330             x1 += 1;
00331             if (duty_cycle < 0)
00332                 {
00333                 duty_cycle += delta_x;    /* update duty cycle */
00334                 y1+=incy;
00335                 }
00336             }
00337         }
00338     else
00339         {
00340         dots = delta_y+1;
00341         while (--dots >= 0)
00342             {
00343             if (fillFromBase)
00344                 {
00345                 if (y1 < yBase)
00346                     mgDrawBox(mg,x1,y1,1,yBase-y1,color);
00347                 }
00348             else
00349                 mgPutDot(mg,x1,y1,color);
00350             duty_cycle += delta_x;
00351             y1+=incy;
00352             if (duty_cycle > 0)
00353                 {
00354                 duty_cycle -= delta_y;    /* update duty cycle */
00355                 x1 += 1;
00356                 }
00357             }
00358         }
00359     }
00360 }
00361 
00362 void mgDrawLine(struct memGfx *mg, int x1, int y1, int x2, int y2, Color color)
00363 /* Draw a line from one point to another. */
00364 {
00365 mgBrezy(mg, x1, y1, x2, y2, color, 0, FALSE);
00366 }
00367 
00368 void mgFillUnder(struct memGfx *mg, int x1, int y1, int x2, int y2, 
00369         int bottom, Color color)
00370 /* Draw a 4 sided filled figure that has line x1/y1 to x2/y2 at
00371  * it's top, a horizontal line at bottom as it's bottom, and
00372  * vertical lines from the bottom to y1 on the left and bottom to
00373  * y2 on the right. */
00374 {
00375 mgBrezy(mg, x1, y1, x2, y2, color, bottom, TRUE);
00376 }
00377 
00378 void mgPutSeg(struct memGfx *mg, int x, int y, int width, Color *dots)
00379 /* Put a series of dots starting at x, y and going to right width pixels. */
00380 {
00381 mgPutSegMaybeZeroClear(mg, x, y, width, dots, FALSE);
00382 }
00383 
00384 void mgPutSegZeroClear(struct memGfx *mg, int x, int y, int width, Color *dots)
00385 /* Put a series of dots starting at x, y and going to right width pixels.
00386  * Don't put zero dots though. */
00387 {
00388 mgPutSegMaybeZeroClear(mg, x, y, width, dots, TRUE);
00389 }
00390 
00391 
00392 void mgDrawHorizontalLine(struct memGfx *mg, int y1, Color color)
00393 /*special case of mgDrawLine, for horizontal line across entire window 
00394   at y-value y1.*/
00395 {
00396 mgDrawLine( mg, mg->clipMinX, y1, mg->clipMaxX, y1, color);
00397 }
00398 
00399 void mgLineH(struct memGfx *mg, int y, int x1, int x2, Color color)
00400 /* Draw horizizontal line width pixels long starting at x/y in color */
00401 {
00402 if (y >= mg->clipMinY && y < mg->clipMaxY)
00403     {
00404     int w;
00405     if (x1 < mg->clipMinX)
00406         x1 = mg->clipMinX;
00407     if (x2 > mg->clipMaxX)
00408         x2 = mg->clipMaxX;
00409     w = x2 - x1;
00410     if (w > 0)
00411         {
00412         Color *pt = _mgPixAdr(mg,x1,y);
00413         while (--w >= 0)
00414             *pt++ = color;
00415         }
00416     }
00417 }
00418 
00419 
00420 boolean mgClipForBlit(int *w, int *h, int *sx, int *sy,
00421         struct memGfx *dest, int *dx, int *dy)
00422 {
00423 /* Make sure we don't overwrite destination. */
00424 int over;
00425 
00426 if ((over = dest->clipMinX - *dx) > 0)
00427     {
00428     *w -= over;
00429     *sx += over;
00430     *dx = dest->clipMinX;
00431     }
00432 if ((over = dest->clipMinY - *dy) > 0)
00433     {
00434     *h -= over;
00435     *sy += over;
00436     *dy = dest->clipMinY;
00437     }
00438 if ((over = *w + *dx - dest->clipMaxX) > 0)
00439     *w -= over; 
00440 if ((over = *h + *dy - dest->clipMaxY) > 0)
00441     *h -= over;
00442 return (*h > 0 && *w > 0);
00443 }
00444 
00445 void mgTextBlit(int width, int height, int bitX, int bitY,
00446         unsigned char *bitData, int bitDataRowBytes, 
00447         struct memGfx *dest, int destX, int destY, 
00448         Color color, Color backgroundColor)
00449 {
00450 UBYTE *inLine;
00451 UBYTE *outLine;
00452 UBYTE inLineBit;
00453 
00454 if (!mgClipForBlit(&width, &height, &bitX, &bitY, dest, &destX, &destY))
00455     return;
00456 
00457 inLine = bitData + (bitX>>3) + bitY * bitDataRowBytes;
00458 inLineBit = (0x80 >> (bitX&7));
00459 outLine = _mgPixAdr(dest,destX,destY);
00460 while (--height >= 0)
00461     {
00462     UBYTE *in = inLine;
00463     UBYTE *out = outLine;
00464     UBYTE inBit = inLineBit;
00465     UBYTE inByte = *in++;
00466     int i = width;
00467     while (--i >= 0)
00468         {
00469         if (inBit & inByte)
00470             *out = color;
00471         ++out;
00472         if ((inBit >>= 1) == 0)
00473             {
00474             inByte = *in++;
00475             inBit = 0x80;
00476             }
00477         }
00478     inLine += bitDataRowBytes;
00479     outLine += _mgBpr(dest);
00480     }
00481 }
00482 
00483 void mgTextBlitSolid(int width, int height, int bitX, int bitY,
00484         unsigned char *bitData, int bitDataRowBytes, 
00485         struct memGfx *dest, int destX, int destY, 
00486         Color color, Color backgroundColor)
00487 {
00488 UBYTE *inLine;
00489 UBYTE *outLine;
00490 UBYTE inLineBit;
00491 
00492 if (!mgClipForBlit(&width, &height, &bitX, &bitY, dest, &destX, &destY))
00493     return;
00494 inLine = bitData + (bitX>>3) + bitY * bitDataRowBytes;
00495 inLineBit = (0x80 >> (bitX&7));
00496 outLine = _mgPixAdr(dest,destX,destY);
00497 while (--height >= 0)
00498     {
00499     UBYTE *in = inLine;
00500     UBYTE *out = outLine;
00501     UBYTE inBit = inLineBit;
00502     UBYTE inByte = *in++;
00503     int i = width;
00504     while (--i >= 0)
00505         {
00506         *out++ = ((inBit & inByte) ? color : backgroundColor);
00507         if ((inBit >>= 1) == 0)
00508             {
00509             inByte = *in++;
00510             inBit = 0x80;
00511             }
00512         }
00513     inLine += bitDataRowBytes;
00514     outLine += _mgBpr(dest);
00515     }
00516 }
00517 
00518 
00519 void mgText(struct memGfx *mg, int x, int y, Color color, 
00520         MgFont *font, char *text)
00521 /* Draw a line of text with upper left corner x,y. */
00522 {
00523 gfText(mg, font, text, x, y, color, mgTextBlit, MG_WHITE);
00524 }
00525 
00526 void mgTextCentered(struct memGfx *mg, int x, int y, int width, int height, 
00527         Color color, MgFont *font, char *text)
00528 /* Draw a line of text centered in box defined by x/y/width/height */
00529 {
00530 int fWidth, fHeight;
00531 int xoff, yoff;
00532 fWidth = mgFontStringWidth(font, text);
00533 fHeight = mgFontPixelHeight(font);
00534 xoff = x + (width - fWidth)/2;
00535 yoff = y + (height - fHeight)/2;
00536 if (font == mgSmallFont())
00537     {
00538     xoff += 1;
00539     yoff += 1;
00540     }
00541 mgText(mg, xoff, yoff, color, font, text);
00542 }
00543 
00544 void mgTextRight(struct memGfx *mg, int x, int y, int width, int height, 
00545         Color color, MgFont *font, char *text)
00546 /* Draw a line of text right justified in box defined by x/y/width/height */
00547 {
00548 int fWidth, fHeight;
00549 int xoff, yoff;
00550 fWidth = mgFontStringWidth(font, text);
00551 fHeight = mgFontPixelHeight(font);
00552 xoff = x + width - fWidth - 1;
00553 yoff = y + (height - fHeight)/2;
00554 if (font == mgSmallFont())
00555     {
00556     xoff += 1;
00557     yoff += 1;
00558     }
00559 mgText(mg, xoff, yoff, color, font, text);
00560 }
00561 
00562 int mgFontPixelHeight(MgFont *font)
00563 /* How high in pixels is font? */
00564 {
00565 return font_cel_height(font);
00566 }
00567 
00568 int mgFontLineHeight(MgFont *font)
00569 /* How many pixels to next line ideally? */
00570 {
00571 int celHeight = font_cel_height(font);
00572 return celHeight + 1 + (celHeight/5);
00573 }
00574 
00575 int mgFontWidth(MgFont *font, char *chars, int charCount)
00576 /* How wide are a couple of letters? */
00577 {
00578 return fnstring_width(font, (unsigned char *)chars, charCount);
00579 }
00580 
00581 int mgFontStringWidth(MgFont *font, char *string)
00582 /* How wide is a string? */
00583 {
00584 return mgFontWidth(font, string, strlen(string));
00585 }
00586 
00587 int mgFontCharWidth(MgFont *font, char c)
00588 /* How wide is a character? */
00589 {
00590 return mgFontWidth(font, &c, 1);
00591 }
00592 
00593 void mgSlowDot(struct memGfx *mg, int x, int y, int colorIx)
00594 /* Draw a dot when a macro won't do. */
00595 {
00596 mgPutDot(mg, x, y, colorIx);
00597 }
00598 
00599 int mgSlowGetDot(struct memGfx *mg, int x, int y)
00600 /* Fetch a dot when a macro won't do. */
00601 {
00602 return mgGetDot(mg, x, y);
00603 }
00604 
00605 struct memGfx *mgRotate90(struct memGfx *in)
00606 /* Create a copy of input that is rotated 90 degrees clockwise. */
00607 {
00608 int iWidth = in->width, iHeight = in->height;
00609 struct memGfx *out = mgNew(iHeight, iWidth);
00610 Color *inCol, *outRow, *outRowStart;
00611 int i,j;
00612 
00613 memcpy(out->colorMap, in->colorMap, sizeof(out->colorMap));
00614 outRowStart = out->pixels;
00615 for (i=0; i<iWidth; ++i)
00616     {
00617     inCol = in->pixels + i;
00618     outRow = outRowStart;
00619     outRowStart += _mgBpr(out);
00620     j = iHeight;
00621     while (--j >= 0)
00622         {
00623         outRow[j] = *inCol;
00624         inCol += _mgBpr(in);
00625         }
00626     }
00627 return out;
00628 }
00629 
00630 void mgSetHint(char *hint)
00631 /* dummy function */
00632 {
00633 return;
00634 }
00635 
00636 char *mgGetHint(char *hint)
00637 /* dummy function */
00638 {
00639 return "";
00640 }
00641 
00642 void vgMgMethods(struct vGfx *vg)
00643 /* Fill in virtual graphics methods for memory based drawing. */
00644 {
00645 vg->pixelBased = TRUE;
00646 vg->close = (vg_close)mgFree;
00647 vg->dot = (vg_dot)mgSlowDot;
00648 vg->getDot = (vg_getDot)mgSlowGetDot;
00649 vg->box = (vg_box)mgDrawBox;
00650 vg->line = (vg_line)mgDrawLine;
00651 vg->text = (vg_text)mgText;
00652 vg->textRight = (vg_textRight)mgTextRight;
00653 vg->textCentered = (vg_textCentered)mgTextCentered;
00654 vg->findColorIx = (vg_findColorIx)mgFindColor;
00655 vg->colorIxToRgb = (vg_colorIxToRgb)mgColorIxToRgb;
00656 vg->setClip = (vg_setClip)mgSetClip;
00657 vg->unclip = (vg_unclip)mgUnclip;
00658 vg->verticalSmear = (vg_verticalSmear)mgVerticalSmear;
00659 vg->fillUnder = (vg_fillUnder)mgFillUnder;
00660 vg->drawPoly = (vg_drawPoly)mgDrawPoly;
00661 vg->setHint = (vg_setHint)mgSetHint;
00662 vg->getHint = (vg_getHint)mgGetHint;
00663 }
00664 

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