Back to index

lightning-sunbird  0.9+nobinonly
nsXFontAAScaledBitmap.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Brian Stell <bstell@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include <X11/Xatom.h>
00041 #include "gfx-config.h"
00042 #include "nscore.h"
00043 #include "nsXFontAAScaledBitmap.h"
00044 #include "nsRenderingContextGTK.h"
00045 #include "nsX11AlphaBlend.h"
00046 
00047 #include <gdk/gdk.h>
00048 #include <gdk/gdkx.h>
00049 
00050 #include "nsHashtable.h"
00051 
00052 #define IMAGE_BUFFER_SIZE 2048
00053 
00054 #ifdef DEBUG
00055   static PRBool dodump = 0;
00056 # define DEBUG_DUMP(x) if (dodump) {dodump=0; x; }
00057 #else
00058 # define DEBUG_DUMP(x)
00059 #endif
00060 
00061 #define DEBUG_SHOW_GLYPH_BOX 0
00062 #if DEBUG_SHOW_GLYPH_BOX
00063 # define DEBUG_AADRAWBOX(i,x,y,w,h,r,g,b,a) \
00064     PR_BEGIN_MACRO \
00065       nscolor color NS_RGB((r),(g),(b)); \
00066       AADrawBox((i), (x), (y), (w), (h), color, (a)); \
00067     PR_END_MACRO
00068 #else
00069 # define DEBUG_AADRAWBOX(i,x,y,w,h,r,g,b,a)
00070 #endif
00071 
00072 void dump_byte_table(PRUint8 *table, int width, int height);
00073 
00074 static void scale_image(nsAntiAliasedGlyph *, nsAntiAliasedGlyph *);
00075 static void scale_imageAntiJag(nsAntiAliasedGlyph *, nsAntiAliasedGlyph *);
00076 static void WeightTableInitLinearCorrection(PRUint8*, PRUint8, double);
00077 
00078 Display *nsXFontAAScaledBitmap::sDisplay       = nsnull;
00079 GC       nsXFontAAScaledBitmap::sBackgroundGC  = nsnull;
00080 PRUint8  nsXFontAAScaledBitmap::sWeightedScaleDarkText[256];
00081 PRUint8  nsXFontAAScaledBitmap::sWeightedScaleLightText[256];
00082 
00083 //
00084 // Anti-Aliased Scaled Bitmap Fonts (AASB)
00085 // 
00086 // AASB fonts are made by taking a regular X font, drawing the desired
00087 // glyph(s) off screen, bringing the pixels down to the client, and then
00088 // scaling the bits using anti-aliasing to get a correctly weighted
00089 // pixels in the scaled version. The AASB glyphs are visually correct 
00090 // but tend to be a bit weak and blurry. 
00091 //
00092 // To make the glyphs stronger and sharper the pixels are adjusted.
00093 //
00094 // One strategy would be to lighten the light pixels and darken the 
00095 // dark pixels. Lightening the light pixels makes "thin" lines
00096 // disappear completely so this is not done.
00097 //
00098 // To darken the dark pixels the code increases the value of the pixels
00099 // (above a minimum value).
00100 //
00101 //  for (i=0; i<256; i++) {
00102 //    if (i < min_value)
00103 //      val = i;
00104 //    else
00105 //      val = i + ((i - min_value)*gain);
00106 //
00107 // (of course limiting val to between 0 and 255 inclusive)
00108 //
00109 // Dark text (eg: black glyphs on a white background) is weighted
00110 // separately from light text (eg: white text on a blue background).
00111 // Selected text is white text on a blue background. The contrast
00112 // between white text on a blue background is not as strong a black
00113 // black text on a white background so light colored text is
00114 // "sharpened" more than dark text.
00115 
00116 PRUint8 gAASBDarkTextMinValue = 64;
00117 double gAASBDarkTextGain = 0.6;
00118 PRUint8 gAASBLightTextMinValue = 64;
00119 double gAASBLightTextGain = 1.3;
00120 
00121 PRBool
00122 nsXFontAAScaledBitmap::DisplayIsLocal(Display *aDisplay)
00123 {
00124   // if shared memory works then the display is local
00125   if (gdk_get_use_xshm())
00126     return PR_TRUE;
00127 
00128   return PR_FALSE;
00129 
00130 }
00131 
00132 void
00133 nsXFontAAScaledBitmap::DrawText8(GdkDrawable *aDrawable, GdkGC *aGC,
00134                                  PRInt32 aX, PRInt32 aY,
00135                                  const char *aString, PRUint32 aLength)
00136 {
00137   DrawText8or16(aDrawable, aGC, aX, aY, (void*)aString, aLength);
00138 }
00139 
00140 void
00141 nsXFontAAScaledBitmap::DrawText16(GdkDrawable *aDrawable, GdkGC *aGC,
00142                          PRInt32 aX, PRInt32 aY,
00143                         const XChar2b *aString, PRUint32 aLength)
00144 {
00145   DrawText8or16(aDrawable, aGC, aX, aY, (void*)aString, aLength);
00146 }
00147 
00148 //
00149 // We save code space by merging the 8 and 16 bit drawing routines.
00150 // The signature of this fuction is vague but that is okay since
00151 // it is only used internally by nsXFontAAScaledBitmap and not
00152 // presented as an API.
00153 //
00154 void
00155 nsXFontAAScaledBitmap::DrawText8or16(GdkDrawable *aDrawable, GdkGC *aGC,
00156                                      PRInt32 aX, PRInt32 aY,
00157                                      void *a8or16String, PRUint32 aLength)
00158 {
00159   // make the indeterminate input variables determinate
00160   const char    *string8  = (const char    *)a8or16String;
00161   const XChar2b *string16 = (const XChar2b *)a8or16String;
00162 
00163 #if DEBUG_SHOW_GLYPH_BOX
00164   // grey shows image size
00165   // red shows character cells
00166   // green box shows text ink
00167 #endif
00168 
00169   if (aLength < 1) {
00170     return;
00171   }
00172 
00173   //
00174   // Get a area guaranteed to be big enough.
00175   //
00176   // This can be bigger than necessary but for debuggability
00177   // this code can draw the char cells.  Calculating the
00178   // exact size needed both the character cells and
00179   // pixels to the left and right of the character cells
00180   // is quite messy.
00181   //
00182   PRUint32 image_width  = (mScaledMax.width * aLength) + mScaledMax.lbearing;
00183   PRUint32 image_height = mScaledMax.ascent+mScaledMax.descent;
00184   PRInt32 x_pos = mScaledMax.lbearing;
00185 
00186   Drawable win = GDK_WINDOW_XWINDOW(aDrawable);
00187   GC gc = GDK_GC_XGC(aGC);
00188   XGCValues values;
00189   if (!XGetGCValues(mDisplay, gc, GCForeground, &values)) {
00190     NS_ASSERTION(0, "failed to get foreground pixel");
00191     return;
00192   }
00193   nscolor color = nsX11AlphaBlend::PixelToNSColor(values.foreground);
00194 
00195   // weight dark text differently from light text
00196   PRUint32 color_val = NS_GET_R(color) + NS_GET_G(color) + NS_GET_B(color);
00197   PRUint8 *weight_table;
00198   if ((NS_GET_R(color)>200) || (NS_GET_R(color)>200) || (NS_GET_R(color)>200)
00199       || (color_val>3*128))
00200     weight_table = sWeightedScaleLightText;
00201   else
00202     weight_table = sWeightedScaleDarkText;
00203 
00204   //
00205   // Get the background
00206   //
00207   XImage *sub_image = nsX11AlphaBlend::GetBackground(mDisplay, mScreen, win,
00208                                  aX-mScaledMax.lbearing, aY-mScaledMax.ascent,
00209                                  image_width, image_height);
00210   if (sub_image==nsnull) {
00211 #ifdef DEBUG
00212     // complain if the requested area is not completely off screen
00213     int win_width = DisplayWidth(mDisplay, mScreen);
00214     int win_height = DisplayHeight(mDisplay, mScreen);
00215     if (((int)(aX-mScaledMax.lbearing+image_width) > 0)  // not hidden to left
00216         && ((int)(aX-mScaledMax.lbearing) < win_width)   // not hidden to right
00217         && ((int)(aY-mScaledMax.ascent+image_height) > 0)// not hidden to top
00218         && ((int)(aY-mScaledMax.ascent) < win_height))   // not hidden to bottom
00219     {
00220       NS_ASSERTION(sub_image, "failed to get the image");
00221     }
00222 #endif
00223     return;
00224   }
00225 
00226 #if DEBUG_SHOW_GLYPH_BOX
00227   DEBUG_AADRAWBOX(sub_image,0,0,image_width,image_height,0,0,0,255/4);
00228   int lbearing, rbearing, width, ascent, descent;
00229   if (mIsSingleByte)
00230     TextExtents8(aString8, aLength, &lbearing, &rbearing, &width,
00231                  &ascent, &descent);
00232   else
00233     TextExtents16(aString16, aLength, &lbearing, &rbearing, &width,
00234                   &ascent, &descent);
00235 
00236   DEBUG_AADRAWBOX(sub_image, x_pos+lbearing, mScaledMax.ascent-ascent,
00237                   rbearing-lbearing, ascent+descent, 0,255,0, 255/2);
00238 #endif
00239 
00240   //
00241   // Get aa-scaled glyphs and blend with background
00242   //
00243   blendGlyph blendGlyph = nsX11AlphaBlend::GetBlendGlyph();
00244   for (PRUint32 i=0; i<aLength; i++) {
00245 #if DEBUG_SHOW_GLYPH_BOX
00246     PRUint32 box_width;
00247     if (mIsSingleByte)
00248       box_width = TextWidth8(&aString8[i], 1);
00249     else
00250       box_width = TextWidth16(&aString16[i], 1);
00251     nscolor red = NS_RGB(255,0,0);
00252     AADrawBox(sub_image, x_pos, 0, box_width, mScaledMax.ascent, red, 255/4);
00253     AADrawBox(sub_image, x_pos, mScaledMax.ascent, box_width,
00254               mScaledMax.descent, red,255/4);
00255 #endif
00256     nsAntiAliasedGlyph *scaled_glyph;
00257     PRBool got_image;
00258     if (mIsSingleByte)
00259       got_image = GetScaledGreyImage(&string8[i], &scaled_glyph);
00260     else
00261       got_image = GetScaledGreyImage((const char*)&string16[i], &scaled_glyph);
00262     if (!got_image) {
00263       PRUint32 char_width;
00264       if (mIsSingleByte)
00265         char_width = XTextWidth(mUnscaledFontInfo, &string8[i], 1);
00266       else
00267         char_width = XTextWidth16(mUnscaledFontInfo, &string16[i], 1);
00268       x_pos += SCALED_SIZE(char_width);
00269       continue;
00270     }
00271     NS_ASSERTION(scaled_glyph->GetBorder()==0,
00272                  "do not support non-zero border");
00273     DEBUG_DUMP((dump_byte_table(scaled_glyph->GetBuffer(),
00274                     scaled_glyph->GetBufferWidth(),
00275                     scaled_glyph->GetBufferHeight())));
00276     //
00277     // blend the aa-glyph onto the background
00278     //
00279     (*blendGlyph)(sub_image, scaled_glyph, weight_table, color,
00280                   x_pos + scaled_glyph->GetLBearing(), 0);
00281 
00282     DEBUG_DUMP((dump_XImage_blue_data(sub_image)));
00283     x_pos += scaled_glyph->GetAdvance();
00284   }
00285   DEBUG_DUMP((dump_XImage_blue_data(sub_image)));
00286 
00287   //
00288   // Send it to the display
00289   //
00290   XPutImage(mDisplay, win, gc, sub_image,
00291             0, 0, aX-mScaledMax.lbearing, aY-mScaledMax.ascent,
00292             image_width, image_height);
00293   XDestroyImage(sub_image);
00294 }
00295 
00296 void
00297 nsXFontAAScaledBitmap::FreeGlobals()
00298 {
00299   if (sBackgroundGC) {
00300     XFreeGC(sDisplay, sBackgroundGC);
00301     sBackgroundGC = nsnull;
00302   }
00303   sDisplay = nsnull;
00304 }
00305 
00306 static PRBool
00307 FreeGlyphHash(nsHashKey* aKey, void* aData, void* aClosure)
00308 {
00309   delete (nsAntiAliasedGlyph *)aData;
00310 
00311   return PR_TRUE;
00312 }
00313 
00314 PRBool
00315 nsXFontAAScaledBitmap::GetScaledGreyImage(const char *aChar,
00316                                          nsAntiAliasedGlyph **aGreyImage)
00317 {
00318   XChar2b *aChar2b = nsnull;
00319   PRUint32 antiJagPadding;
00320   XImage *ximage;
00321   nsAntiAliasedGlyph *scaled_image;
00322   PRUnichar charKey[2];
00323 
00324   // get the char key
00325   if (mIsSingleByte)
00326     charKey[0] = (PRUnichar)*aChar;
00327   else {
00328     aChar2b = (XChar2b *)aChar;
00329     charKey[0] = aChar2b->byte1<<8 | aChar2b->byte2;
00330   }
00331   charKey[1] = 0;
00332   nsStringKey key(charKey, 1);
00333 
00334   // look in the cache for the glyph image
00335   scaled_image = (nsAntiAliasedGlyph*)mGlyphHash->Get(&key);
00336   if (!scaled_image) {
00337     // get the char metrics
00338     int direction, font_ascent, font_descent;
00339     XCharStruct charMetrics;
00340     if (mIsSingleByte)
00341       XTextExtents(mUnscaledFontInfo, aChar, 1, &direction,
00342                    &font_ascent, &font_descent, &charMetrics);
00343     else
00344       XTextExtents16(mUnscaledFontInfo, aChar2b,1, &direction,
00345                      &font_ascent, &font_descent, &charMetrics);
00346 
00347     // figure the amount to scale
00348     PRInt32 left_edge  = GLYPH_LEFT_EDGE(&charMetrics);
00349     PRInt32 right_edge = GLYPH_RIGHT_EDGE(&charMetrics);
00350     PRUint32 unscaled_width = right_edge - left_edge;
00351     NS_ASSERTION(unscaled_width<=mUnscaledMax.width, "unexpected glyph width");
00352 
00353     // clear the bitmap
00354     XFillRectangle(mDisplay, mUnscaledBitmap, sBackgroundGC, 0, 0,
00355                                 unscaled_width, mUnscaledMax.height);
00356     // draw the char
00357     if (mIsSingleByte)
00358       XDrawString(mDisplay, mUnscaledBitmap, mForegroundGC,
00359                -left_edge, mUnscaledMax.ascent, aChar, 1);
00360     else
00361       XDrawString16(mDisplay, mUnscaledBitmap, mForegroundGC,
00362                -left_edge, mUnscaledMax.ascent, aChar2b, 1);
00363     // get the pixels
00364     ximage = XGetImage(mDisplay, mUnscaledBitmap,
00365                      0, 0, unscaled_width, mUnscaledMax.height,
00366                      AllPlanes, ZPixmap);
00367     NS_ASSERTION(ximage, "failed to XGetSubImage()");
00368     if (!ximage) {
00369       return PR_FALSE;
00370     }
00371     DEBUG_DUMP((dump_XImage_blue_data(ximage)));
00372 
00373     // must pad when anti-jagging
00374     if (mRatio < 1.25)
00375       antiJagPadding = 0;
00376     else
00377       antiJagPadding = 2; // this may change if the anti-jagging code changes
00378 
00379     // create the empty nsAntiAliasedGlyph
00380     nsAntiAliasedGlyph unscaled_image(unscaled_width, mUnscaledMax.height,
00381                                       antiJagPadding);
00382     PRUint8 buf[IMAGE_BUFFER_SIZE]; // try to use the stack for data
00383     if (!unscaled_image.Init(buf, IMAGE_BUFFER_SIZE)) {
00384       NS_ASSERTION(0, "failed to Init() unscaled_image");
00385       XDestroyImage(ximage);
00386       return PR_FALSE;
00387     }
00388 
00389     //
00390     // get ready to scale
00391     //
00392     unscaled_image.SetImage(&charMetrics, ximage);
00393     DEBUG_DUMP((dump_byte_table(unscaled_image.GetBuffer(),
00394                     unscaled_image.GetBufferWidth(),
00395                     unscaled_image.GetBufferHeight())));
00396     XDestroyImage(ximage);
00397 
00398     //
00399     // Create the scaled glyph
00400     //
00401     GlyphMetrics glyphMetrics;
00402     glyphMetrics.width    = SCALED_SIZE(unscaled_width);
00403     glyphMetrics.height   = SCALED_SIZE(mUnscaledMax.height);
00404     glyphMetrics.lbearing = SCALED_SIZE(left_edge);
00405     glyphMetrics.rbearing = SCALED_SIZE(right_edge);
00406     glyphMetrics.advance  = SCALED_SIZE(charMetrics.width);
00407     glyphMetrics.ascent   = SCALED_SIZE(charMetrics.ascent);
00408     glyphMetrics.descent  = SCALED_SIZE(charMetrics.descent);
00409 
00410     scaled_image = new nsAntiAliasedGlyph(SCALED_SIZE(unscaled_width),
00411                                           SCALED_SIZE(mUnscaledMax.height), 0);
00412     NS_ASSERTION(scaled_image, "failed to create scaled_image");
00413     if (!scaled_image) {
00414       return PR_FALSE;
00415     }
00416     if (!scaled_image->Init()) {
00417       NS_ASSERTION(0, "failed to initialize scaled_image");
00418       delete scaled_image;
00419       return PR_FALSE;
00420     }
00421     scaled_image->SetSize(&glyphMetrics);
00422 
00423     //
00424     // scale
00425     //
00426     if (antiJagPadding==0)
00427       scale_image(&unscaled_image, scaled_image);
00428     else
00429       scale_imageAntiJag(&unscaled_image, scaled_image);
00430 
00431     DEBUG_DUMP((dump_byte_table(scaled_image->GetBuffer(),
00432                     scaled_image->GetBufferWidth(),
00433                     scaled_image->GetBufferHeight())));
00434 
00435     // store in hash
00436     mGlyphHash->Put(&key, scaled_image);
00437   }
00438   *aGreyImage = scaled_image;
00439   return PR_TRUE;
00440 }
00441 
00442 PRBool
00443 nsXFontAAScaledBitmap::GetXFontProperty(Atom aAtom, unsigned long *aValue)
00444 {
00445   unsigned long val;
00446   PRBool rslt = ::XGetFontProperty(mUnscaledFontInfo, aAtom, &val);
00447   if (!rslt)
00448     return PR_FALSE;
00449 
00450   switch (aAtom) {
00451     case XA_X_HEIGHT:
00452       if (val >= 0x00ffffff) {// Bug 43214: arbitrary to exclude garbage values
00453         return PR_FALSE;
00454       }
00455     case XA_SUBSCRIPT_Y:
00456     case XA_SUPERSCRIPT_Y:
00457     case XA_UNDERLINE_POSITION:
00458     case XA_UNDERLINE_THICKNESS:
00459       *aValue = (unsigned long)SCALED_SIZE(val);
00460       break;
00461     default:
00462       *aValue = val;
00463   }
00464   return rslt;
00465 }
00466 
00467 XFontStruct *
00468 nsXFontAAScaledBitmap::GetXFontStruct()
00469 {
00470   NS_ASSERTION(mGdkFont, "GetXFontStruct called before font loaded");
00471   if (mGdkFont==nsnull)
00472     return nsnull;
00473 
00474   return &mScaledFontInfo;
00475 }
00476 
00477 PRBool
00478 nsXFontAAScaledBitmap::InitGlobals(Display *aDisplay, int aScreen)
00479 {
00480   Window root_win;
00481 
00482   sDisplay = aDisplay; // used to free shared sBackgroundGC
00483 
00484   // if not a local display then might be slow so don't run
00485   if (!DisplayIsLocal(aDisplay)) {
00486     goto cleanup_and_return;
00487   }
00488 
00489   root_win = RootWindow(sDisplay, aScreen);
00490   sBackgroundGC = XCreateGC(sDisplay, root_win, 0, NULL);
00491   NS_ASSERTION(sBackgroundGC, "failed to create sBackgroundGC");
00492   if (!sBackgroundGC) {
00493     goto cleanup_and_return;
00494   }
00495   XSetForeground(sDisplay, sBackgroundGC, 0);
00496 
00497   WeightTableInitLinearCorrection(sWeightedScaleDarkText,
00498                                   gAASBDarkTextMinValue, gAASBDarkTextGain);
00499   WeightTableInitLinearCorrection(sWeightedScaleLightText,
00500                                   gAASBLightTextMinValue, gAASBLightTextGain);
00501   return PR_TRUE;
00502 
00503 cleanup_and_return:
00504   if (sBackgroundGC) {
00505     XFreeGC(sDisplay, sBackgroundGC);
00506     sBackgroundGC = nsnull;
00507   }
00508 
00509   return PR_FALSE;
00510 }
00511 
00512 PRBool
00513 nsXFontAAScaledBitmap::LoadFont()
00514 {
00515   NS_ASSERTION(!mAlreadyLoaded, "LoadFont called more than once");
00516   mAlreadyLoaded = PR_TRUE;
00517 
00518   if (!mGdkFont)
00519     return PR_FALSE;
00520   mUnscaledFontInfo = (XFontStruct *)GDK_FONT_XFONT(mGdkFont);
00521 
00522   XFontStruct *usfi = mUnscaledFontInfo;
00523   XFontStruct *sfi  = &mScaledFontInfo;
00524 
00525   mIsSingleByte = (usfi->min_byte1 == 0) && (usfi->max_byte1 == 0);
00526 
00527   mUnscaledMax.width    = MAX(usfi->max_bounds.rbearing,usfi->max_bounds.width);
00528   mUnscaledMax.width   -= MIN(usfi->min_bounds.lbearing, 0);
00529   mUnscaledMax.height   = usfi->max_bounds.ascent   + usfi->max_bounds.descent;
00530   mUnscaledMax.lbearing = usfi->max_bounds.lbearing;
00531   mUnscaledMax.rbearing = usfi->max_bounds.rbearing;
00532   mUnscaledMax.advance  = usfi->max_bounds.width;
00533   mUnscaledMax.ascent   = usfi->max_bounds.ascent;
00534   mUnscaledMax.descent  = usfi->max_bounds.descent;
00535 
00536   mScaledMax.width    = SCALED_SIZE(mUnscaledMax.width);
00537   mScaledMax.lbearing = SCALED_SIZE(mUnscaledMax.lbearing);
00538   mScaledMax.rbearing = SCALED_SIZE(mUnscaledMax.rbearing);
00539   mScaledMax.advance  = SCALED_SIZE(mUnscaledMax.width);
00540   mScaledMax.ascent   = SCALED_SIZE(mUnscaledMax.ascent);
00541   mScaledMax.descent  = SCALED_SIZE(mUnscaledMax.ascent + mUnscaledMax.descent)
00542                         - SCALED_SIZE(mUnscaledMax.ascent);
00543   mScaledMax.height   = mScaledMax.ascent + mScaledMax.descent;
00544 
00545   //
00546   // Scale the XFontStruct
00547   //
00548   sfi->fid               = 0;
00549   sfi->direction         = usfi->direction;
00550   sfi->min_char_or_byte2 = usfi->min_char_or_byte2;
00551   sfi->max_char_or_byte2 = usfi->max_char_or_byte2;
00552   sfi->min_byte1         = usfi->min_byte1;
00553   sfi->max_byte1         = usfi->max_byte1;
00554   sfi->all_chars_exist   = usfi->all_chars_exist;
00555   sfi->default_char      = usfi->default_char;
00556   sfi->n_properties      = 0;
00557   sfi->properties        = nsnull;
00558   sfi->ext_data          = nsnull;
00559 
00560   sfi->min_bounds.lbearing = SCALED_SIZE(usfi->min_bounds.lbearing);
00561   sfi->min_bounds.rbearing = SCALED_SIZE(usfi->min_bounds.rbearing);
00562   sfi->min_bounds.width    = SCALED_SIZE(usfi->min_bounds.width);
00563   sfi->min_bounds.ascent   = SCALED_SIZE(usfi->min_bounds.ascent);
00564   sfi->min_bounds.descent  =
00565             SCALED_SIZE(usfi->min_bounds.ascent + usfi->min_bounds.descent)
00566             - SCALED_SIZE(usfi->min_bounds.ascent);
00567   sfi->min_bounds.attributes = usfi->min_bounds.attributes;
00568 
00569   sfi->max_bounds.lbearing = SCALED_SIZE(usfi->max_bounds.lbearing);
00570   sfi->max_bounds.rbearing = SCALED_SIZE(usfi->max_bounds.rbearing);
00571   sfi->max_bounds.width    = SCALED_SIZE(usfi->max_bounds.width);
00572   sfi->max_bounds.ascent   = SCALED_SIZE(usfi->max_bounds.ascent);
00573   sfi->max_bounds.descent  =
00574             SCALED_SIZE(usfi->max_bounds.ascent + usfi->max_bounds.descent)
00575             - SCALED_SIZE(usfi->max_bounds.ascent);
00576   sfi->max_bounds.attributes = usfi->max_bounds.attributes;
00577 
00578   sfi->per_char = nsnull;
00579   sfi->ascent   = SCALED_SIZE(usfi->ascent);
00580   sfi->descent  = SCALED_SIZE(usfi->descent);
00581 
00582   //
00583   // need a unique foreground GC for each font size/face
00584   // to use to draw the unscaled bitmap font
00585   //
00586   mForegroundGC = XCreateGC(mDisplay, RootWindow(mDisplay, mScreen), 0, NULL);
00587   NS_ASSERTION(mForegroundGC, "failed to create mForegroundGC");
00588   if (!mForegroundGC) {
00589     goto cleanup_and_return;
00590   }
00591 
00592   XSetFont(mDisplay, mForegroundGC, usfi->fid);
00593   XSetForeground(mDisplay, mForegroundGC, 0xFFFFFF);
00594 
00595   mUnscaledBitmap = XCreatePixmap(mDisplay,
00596                           RootWindow(mDisplay,DefaultScreen(mDisplay)),
00597                           mUnscaledMax.width, mUnscaledMax.height,
00598                           DefaultDepth(mDisplay, mScreen));
00599   if (!mUnscaledBitmap)
00600     goto cleanup_and_return;
00601 
00602   mGlyphHash = new nsHashtable();
00603   if (!mGlyphHash)
00604     goto cleanup_and_return;
00605 
00606   if (mGdkFont) {
00607 #ifdef NS_FONT_DEBUG_LOAD_FONT
00608     if (gFontDebug & NS_FONT_DEBUG_LOAD_FONT) {
00609       printf("loaded %s\n", mName);
00610     }
00611 #endif
00612     return PR_TRUE;
00613   }
00614   else
00615     return PR_FALSE;
00616 
00617 cleanup_and_return:
00618   if (mUnscaledFontInfo) {
00619     mUnscaledFontInfo = nsnull;
00620   }
00621   if (mForegroundGC) {
00622     XFreeGC(mDisplay, mForegroundGC);
00623     mForegroundGC = nsnull;
00624   }
00625   if (mUnscaledBitmap) {
00626     XFreePixmap(mDisplay, mUnscaledBitmap);
00627     mUnscaledBitmap = nsnull;
00628   }
00629   if (mGlyphHash) {
00630     delete mGlyphHash;
00631     mGlyphHash = nsnull;
00632   }
00633   memset(&mScaledFontInfo, 0, sizeof(mScaledFontInfo));
00634   memset(&mUnscaledMax,    0, sizeof(mUnscaledMax));
00635   memset(&mScaledMax,      0, sizeof(mScaledMax));
00636   return PR_FALSE;
00637 }
00638 
00639 nsXFontAAScaledBitmap::nsXFontAAScaledBitmap(Display *aDisplay,
00640                                              int aScreen,
00641                                              GdkFont *aGdkFont,
00642                                              PRUint16 aSize,
00643                                              PRUint16 aUnscaledSize)
00644 {
00645   mAlreadyLoaded       = PR_FALSE;
00646   mDisplay             = aDisplay;
00647   mScreen              = aScreen;
00648   mGdkFont             = ::gdk_font_ref(aGdkFont);
00649   mUnscaledSize        = aUnscaledSize;
00650   mRatio               = ((double)aSize)/((double)aUnscaledSize);
00651   mIsSingleByte        = 0;
00652   mForegroundGC        = nsnull;
00653   mGlyphHash           = nsnull;
00654   mUnscaledBitmap      = nsnull;
00655   memset(&mScaledFontInfo, 0, sizeof(mScaledFontInfo));
00656   memset(&mUnscaledMax,    0, sizeof(mUnscaledMax));
00657   memset(&mScaledMax,      0, sizeof(mScaledMax));
00658 }
00659 
00660 void
00661 nsXFontAAScaledBitmap::TextExtents8(const char *aString, PRUint32 aLength,
00662                                     PRInt32* aLBearing, PRInt32* aRBearing,
00663                                     PRInt32* aWidth, PRInt32* aAscent,
00664                                     PRInt32* aDescent)
00665 {
00666   TextExtents8or16((void *)aString, aLength, aLBearing, aRBearing, aWidth,
00667                    aAscent, aDescent);
00668 }
00669 
00670 void
00671 nsXFontAAScaledBitmap::TextExtents16(const XChar2b *aString, PRUint32 aLength,
00672                             PRInt32* aLBearing, PRInt32* aRBearing,
00673                             PRInt32* aWidth, PRInt32* aAscent,
00674                             PRInt32* aDescent)
00675 {
00676   TextExtents8or16((void *)aString, aLength, aLBearing, aRBearing, aWidth,
00677                    aAscent, aDescent);
00678 }
00679 
00680 //
00681 // We save code space by merging the 8 and 16 bit text extents routines.
00682 // The signature of this fuction is vague but that is okay since
00683 // it is only used internally by nsXFontAAScaledBitmap and not
00684 // presented as an API.
00685 //
00686 void
00687 nsXFontAAScaledBitmap::TextExtents8or16(void *a8or16String, PRUint32 aLength,
00688                             PRInt32* aLBearing, PRInt32* aRBearing,
00689                             PRInt32* aWidth, PRInt32* aAscent,
00690                             PRInt32* aDescent)
00691 {
00692   // make the indeterminate input variables determinate
00693   const char    *string8  = (const char    *)a8or16String;
00694   const XChar2b *string16 = (const XChar2b *)a8or16String;
00695 
00696   int dir, unscaled_ascent, unscaled_descent;
00697   XCharStruct char_metrics;
00698   int leftBearing  = 0;
00699   int rightBearing = 0;
00700   int width        = 0;
00701   int ascent       = 0;
00702   int descent      = 0;
00703 
00704   // initialize the values
00705   if (aLength >= 1) {
00706     if (mIsSingleByte)
00707       XTextExtents(mUnscaledFontInfo, string8++, 1,
00708                      &dir, &unscaled_ascent, &unscaled_descent, &char_metrics);
00709     else
00710       XTextExtents16(mUnscaledFontInfo, string16++, 1,
00711                      &dir, &unscaled_ascent, &unscaled_descent, &char_metrics);
00712     leftBearing  = SCALED_SIZE(char_metrics.lbearing);
00713     rightBearing = SCALED_SIZE(char_metrics.rbearing);
00714     ascent       = SCALED_SIZE(char_metrics.ascent);
00715     descent      = SCALED_SIZE(mUnscaledMax.ascent+char_metrics.descent)
00716                    - SCALED_SIZE(mUnscaledMax.ascent);
00717     width        = SCALED_SIZE(char_metrics.width);
00718   }
00719 
00720   //
00721   // Must go char by char to handle float->int rounding
00722   // of the x position. If this is not done then when selecting
00723   // (highlighting) text the selection x pos can differ
00724   // which can make the text move around as more/less is selected
00725   //
00726   for (PRUint32 i=1; i<aLength; i++) {
00727     if (mIsSingleByte)
00728       XTextExtents(mUnscaledFontInfo, string8++, 1,
00729                    &dir, &unscaled_ascent, &unscaled_descent, &char_metrics);
00730     else
00731       XTextExtents16(mUnscaledFontInfo, string16++, 1,
00732                    &dir, &unscaled_ascent, &unscaled_descent, &char_metrics);
00733     //
00734     // In theory the second (or later) char may have a
00735     // lbearing more to the left than the first char.
00736     // Similarly for the rbearing.
00737     //
00738     leftBearing  = MIN(leftBearing,  width+SCALED_SIZE(char_metrics.lbearing));
00739     rightBearing = MAX(rightBearing, width+SCALED_SIZE(char_metrics.rbearing));
00740     ascent       = MAX(ascent,  SCALED_SIZE(char_metrics.ascent));
00741     descent      = MAX(descent,
00742                        SCALED_SIZE(mUnscaledMax.ascent+char_metrics.descent)
00743                        - SCALED_SIZE(mUnscaledMax.ascent));
00744     width        += SCALED_SIZE(char_metrics.width);
00745   }
00746   *aLBearing     = leftBearing;
00747   *aRBearing     = rightBearing;
00748   *aWidth        = width;
00749   *aAscent       = ascent;
00750   *aDescent      = descent;
00751 }
00752 
00753 PRInt32
00754 nsXFontAAScaledBitmap::TextWidth8(const char *aString, PRUint32 aLength)
00755 {
00756   int width = 0;
00757   // figure the width calculating all the per-char rounding
00758   for (PRUint32 i=0; i<aLength; i++) {
00759     int unscaled_width = XTextWidth(mUnscaledFontInfo, aString+i, 1);
00760     width += SCALED_SIZE(unscaled_width);
00761   }
00762 
00763   return width;
00764 }
00765 
00766 PRInt32
00767 nsXFontAAScaledBitmap::TextWidth16(const XChar2b *aString, PRUint32 aLength)
00768 {
00769   int width = 0;
00770   // figure the width calculating all the per-char rounding
00771   for (PRUint32 i=0; i<aLength; i++) {
00772     int unscaled_width = XTextWidth16(mUnscaledFontInfo, aString+i, 1);
00773     width += SCALED_SIZE(unscaled_width);
00774   }
00775   return width;
00776 }
00777 
00778 void
00779 nsXFontAAScaledBitmap::UnloadFont()
00780 {
00781   NS_ASSERTION(mGdkFont, "UnloadFont called when font not loaded");
00782   delete this;
00783 }
00784 
00785 nsXFontAAScaledBitmap::~nsXFontAAScaledBitmap()
00786 {
00787   if (mGlyphHash) {
00788     mGlyphHash->Reset(FreeGlyphHash, nsnull);
00789     delete mGlyphHash;
00790   }
00791   if (mForegroundGC) {
00792     XFreeGC(mDisplay, mForegroundGC);
00793   }
00794   if (mGdkFont) {
00795     ::gdk_font_unref(mGdkFont);
00796   }
00797   if (mUnscaledBitmap) {
00798     XFreePixmap(mDisplay, mUnscaledBitmap);
00799   }
00800 }
00801 
00802 //
00803 // scale_image:
00804 //
00805 // Scale an image from a source area to a destination area
00806 // using anti-aliasing.
00807 //
00808 // For performance reasons the scaling is done in 2 steps:
00809 //   horizontal scaling
00810 //   vertical scaling
00811 //
00812 // It is possible to do the scaling by expanding the souce
00813 // into a a temporary buffer that is the Cartesian product of
00814 // the source and destination. For a source image of Sw*Sh and a
00815 // destination of Dw*Dh the temporary buffer is Tw=Sw*Dw wide and
00816 // Th=Sh*Dh high; eg: for a 20x30 source expanding into a 30x45 it would
00817 // use a temporary buffer of ((20*30) * (30*45)) bytes = 810k points.
00818 // It takes (Sh*Dh)*(Sh*Dh) operation to expand into the the temp buffer
00819 // and the same number to compress into the dest buffer for a total
00820 // of 2*(Sh*Dh)*(Sh*Dh) operations (1.6 million for this example).
00821 // 
00822 // If the expansion/compression is first done horizontally and *summed*
00823 // into a temporary buffer that is the width of the destination and
00824 // the height of the source it takes (Sw*Dw)*Sh operations. To 
00825 // expanded/compress the temp buffer into the destination takes
00826 // Tw*(Th*Dh) = Dw*(Sh*Dh) operations. The total is now
00827 // (Sw*Dw)*Sh + Dw*(Sh*Dh) (in this example (20*30)*30 + 30(30*45) =
00828 // 18000 + 40500 = 58.5K operations or 3.7% of the Cartesian product).
00829 
00830 
00831 //
00832 //
00833 static void
00834 scale_image(nsAntiAliasedGlyph *aSrc, nsAntiAliasedGlyph *aDst)
00835 {
00836   PRUint32 x, y, col;
00837   PRUint8 buffer[65536];
00838   PRUint8 *horizontally_scaled_data = buffer;
00839   PRUint8 *pHsd, *pDst;
00840   PRUint32 dst_width = aDst->GetWidth();
00841   PRUint32 dst_buffer_width = aDst->GetBufferWidth();
00842   PRUint32 dst_height = aDst->GetHeight();
00843   PRUint8 *dst = aDst->GetBuffer();
00844 
00845   if (aDst->GetBorder() != 0) {
00846     NS_ASSERTION(aDst->GetBorder()!=0,"border not supported");
00847     return;
00848   }
00849 
00850   PRUint32 ratio;
00851 
00852   PRUint8 *src = aSrc->GetBuffer();
00853   PRUint32 src_width = aSrc->GetWidth();
00854   NS_ASSERTION(src_width,"zero width glyph");
00855   if (src_width==0)
00856     return;
00857 
00858   PRUint32 src_height = aSrc->GetHeight();
00859   NS_ASSERTION(src_height,"zero height glyph");
00860   if (src_height==0)
00861     return;
00862 
00863   //
00864   // scale the data horizontally
00865   //
00866 
00867   // Calculate the ratio between the unscaled horizontal and
00868   // the scaled horizontal. Use interger multiplication 
00869   // using a 24.8 format fixed decimal format.
00870   ratio = (dst_width<<8)/src_width;
00871 
00872   PRUint32 hsd_len = dst_buffer_width * src_height;
00873   // use the stack buffer if possible
00874   if (hsd_len > sizeof(buffer)) {
00875     horizontally_scaled_data = (PRUint8*)nsMemory::Alloc(hsd_len);
00876     memset(horizontally_scaled_data, 0, hsd_len);
00877   }
00878   for (y=0; y<(dst_buffer_width*src_height); y++)
00879     horizontally_scaled_data[y] = 0;
00880 
00881   pHsd = horizontally_scaled_data;
00882   for (y=0; y<src_height; y++,pHsd+=dst_buffer_width) {
00883     for (x=0; x<src_width; x++) {
00884       // get the unscaled value
00885       PRUint8 src_val = src[x + (y*src_width)];
00886       if (!src_val)
00887         continue;
00888       // transfer the unscaled point's value to the scaled image putting
00889       // the correct percentage into the appropiate scaled locations
00890       PRUint32 area_begin = x * ratio; // starts here (24.8 format)
00891       PRUint32 area_end   = (x+1) * ratio; // ends here (24.8 format)
00892       PRUint32 end_pixel  = (area_end+255)&0xffffff00; // round to integer
00893       // Walk thru the scaled pixels putting in the appropiate amount.
00894       // col is in 24.8 format
00895       for (col=(area_begin&0xffffff00); col<end_pixel; col+=256) {
00896         // figure out how much of the unscaled pixel should go
00897         // in this scaled pixel
00898         PRUint32 this_begin = MAX(col,area_begin);
00899         PRUint32 this_end   = MIN((col+256), area_end);
00900         // add in the amount for this scaled pixel
00901         pHsd[col>>8] += (PRUint8)(((this_end-this_begin)*src_val)>>8);
00902       }
00903       DEBUG_DUMP((dump_byte_table(horizontally_scaled_data,
00904                                   dst_width, src_height)));
00905     }
00906   }
00907 
00908   //
00909   // Scale the data vertically
00910   //
00911 
00912   // Calculate the ratio between the unscaled vertical and
00913   // the scaled vertical. Use interger multiplication 
00914   // using a 24.8 format fixed decimal format.
00915   ratio = (dst_height<<8)/src_height;
00916 
00917   for (x=0; x<dst_width; x++) {
00918     pHsd = horizontally_scaled_data + x;
00919     pDst = dst + x;
00920     for (y=0; y<src_height; y++,pHsd+=dst_buffer_width) {
00921       // get the unscaled value
00922       PRUint8 src_val = *pHsd;
00923       if (src_val == 0)
00924         continue;
00925       // transfer the unscaled point's value to the scaled image putting
00926       // the correct percentage into the appropiate scaled locations
00927       PRUint32 area_begin = y * ratio; // starts here (24.8 format)
00928       PRUint32 area_end   = area_begin + ratio; // ends here (24.8 format)
00929       PRUint32 end_pixel  = (area_end+255)&0xffffff00; // round to integer
00930       PRUint32 c;
00931       PRUint32 col;
00932       // Walk thru the scaled pixels putting in the appropiate amount.
00933       // c is in 24.8 format
00934       for (c=(area_begin>>8)*dst_buffer_width,col=(area_begin&0xffffff00);
00935                 col<end_pixel; c+=dst_buffer_width,col+=256) {
00936         // figure out how much of the unscaled pixel should go
00937         // in this scaled pixel
00938         PRUint32 this_begin = MAX(col,area_begin);
00939         PRUint32 this_end   = MIN((col+256), area_end);
00940         // add in the amount for this scaled pixel
00941         pDst[c] += (((this_end-this_begin)*src_val)>>8);
00942       }
00943       DEBUG_DUMP((dump_byte_table(dst, dst_width, dst_height)));
00944     }
00945   }
00946   if (horizontally_scaled_data != buffer)
00947     free(horizontally_scaled_data);
00948 }
00949 
00950 //
00951 // scale_imageAntiJag:
00952 //
00953 // Enlarge (scale) an image from a source area to a destination area
00954 // using anti-aliasing. To smooth the edges the inside corners are 
00955 // fade-filled and the outside corners fade-cleared (anti jagged)
00956 //
00957 // anti-jagging example:
00958 //
00959 //   - - - -                     - - - -            
00960 // |********|                   |  ***   |           
00961 // |********|                   | ****** |           
00962 // |********|                   |********|           
00963 // |********|            ->     | *********          
00964 // |******** - - - -            |   ********* - -    
00965 //  - - - -|********|            - - -*********  |   
00966 //         |********|                   ******** |   
00967 //         |********|                   | ****** |   
00968 //         |********|                   |  ****  |   
00969 //          - - - -                      - - - -
00970 //
00971 // Only fill-in/clear-out one half of the corner. For 45 degree
00972 // lines this takes off the corners that stick out and fills in
00973 // the inside corners. This should fill and clear about the same
00974 // amount of pixels so the image should retain approximately the
00975 // correct visual weight.
00976 //
00977 // This helps *alot* with lines at 45 degree angles.
00978 // This helps somewhat with lines at other angles.
00979 //
00980 // This fills in the corners of lines that cross making the
00981 // image seem a bit heavier.
00982 //
00983 // To do the anti-jaggin the code needs to look a the pixels surrounding
00984 // a pixel to see if the pixel is an inside corner or an outside
00985 // corner. To avoid handling the pixels on the outer edge as special
00986 // cases the image is padded.
00987 //
00988 //
00989 
00990 static void
00991 scale_imageAntiJag(nsAntiAliasedGlyph *aSrc, nsAntiAliasedGlyph *aDst)
00992 {
00993   PRUint32 x, y, col;
00994   PRUint8 buffer[65536];
00995   PRUint8 *padded_src = aSrc->GetBuffer();
00996   PRUint8 exp_buffer[65536];
00997   PRUint8 *horizontally_scaled_data = buffer;
00998   PRUint8 *pHsd, *pDst;
00999   PRUint32 dst_width = aDst->GetWidth();
01000   PRUint32 dst_buffer_width = aDst->GetBufferWidth();
01001   PRUint32 dst_height = aDst->GetHeight();
01002   PRUint8 *dst = aDst->GetBuffer();
01003 
01004   if (aDst->GetBorder() != 0) {
01005     NS_ASSERTION(aDst->GetBorder()==0, "non zero dest border not supported");
01006     return;
01007   }
01008   PRUint32 expand = (((dst_width<<8)/aSrc->GetWidth())+255)>>8;
01009 
01010   PRUint32 src_width     = aSrc->GetWidth();
01011   PRUint32 src_height    = aSrc->GetHeight();
01012   PRUint32 border_width  = aSrc->GetBorder();
01013   PRUint32 padded_width  = aSrc->GetWidth()  + (2*border_width);
01014   PRUint32 padded_height = aSrc->GetHeight() + (2*border_width);
01015 
01016   //
01017   // Expand the data (anti-jagging comes later)
01018   //
01019   PRUint32 expanded_width  = padded_width  * expand;
01020   PRUint32 expanded_height = padded_height * expand;
01021 
01022   PRUint32 start_x = border_width * expand;
01023   PRUint32 start_y = border_width * expand;
01024 
01025   PRUint8 *expanded_data = exp_buffer;
01026   PRUint32 exp_len = expanded_width*expanded_height;
01027   if (exp_len > sizeof(exp_buffer))
01028     expanded_data = (PRUint8*)malloc(expanded_width*expanded_height);
01029   for (y=0; y<padded_height; y++) {
01030     for (int i=0; i<expand; i++) {
01031       for (x=0; x<padded_width; x++) {
01032         PRUint32 padded_index = x+(y*padded_width);
01033         PRUint32 exp_index = (x*expand) + ((i+(y*expand))*expanded_width);
01034         for (int j=0; j<expand; j++) {
01035           expanded_data[exp_index+j] = padded_src[padded_index];
01036         }
01037       }
01038     }
01039   }
01040   DEBUG_DUMP((dump_byte_table(expanded_data, expanded_width, expanded_height)));
01041 
01042 // macro to access the surrounding pixels
01043 //
01044 // +-------+-------+-------+
01045 // |  up   |       |  up   |
01046 // | left  |  up   | right |
01047 // |       |       |       |
01048 // +-------+-------+-------+
01049 // |       |       |       |
01050 // | left  |  SRC  | right |
01051 // |       |       |       |
01052 // +-------+-------+-------+
01053 // |       |       |       |
01054 // | down  | down  | down  |
01055 // | left  |       | right |
01056 // +-------+-------+-------+
01057 //
01058 //
01059 #define SRC_UP_LEFT(ps)    *(ps-padded_width-1)
01060 #define SRC_UP(ps)         *(ps-padded_width)
01061 #define SRC_UP_RIGHT(ps)   *(ps-padded_width+1)
01062 #define SRC_LEFT(ps)       *(ps-1)
01063 #define SRC(ps)            *(ps)
01064 #define SRC_RIGHT(ps)      *(ps+1)
01065 #define SRC_DOWN_LEFT(ps)  *(ps+padded_width-1)
01066 #define SRC_DOWN(ps)       *(ps+padded_width)
01067 #define SRC_DOWN_RIGHT(ps) *(ps+padded_width+1)
01068 #define FILL_VALUE 255
01069 
01070   //
01071   // Anti-Jag by doing triangular fade-fills and fade clears.
01072   //
01073   // To do a triangular fill/clear the code needs to do a double loop: 
01074   //
01075   //    one to scan vertically
01076   //    one to scan horizontally the correct amount to make a triangle
01077   //
01078   // eg: 
01079   //
01080   //    for (i=0; i<jag_len; i++)
01081   //      for (j=0; j<jag_len-i; j++)
01082   //
01083   // The way the index is calculated determines whether the triangle is:
01084   // (pseudo code)
01085   //
01086   //      up-right: buf[ j - i*width]
01087   //       up-left: buf[-j - i*width]
01088   //    down-right: buf[ j + i*width]
01089   //     down-left: buf[-j + i*width]
01090   //
01091   // The value to fill-in/clear is related to the distance from the
01092   // origin of this corner. The correct value is the square root
01093   // of the sum of the squares. This code does a rough approximation
01094   // by adding the x and y offsets 
01095   //
01096   //    (i+j)
01097   //
01098   // This approximation can darken the fill fade by up to 41% but it
01099   // is much less CPU intensive and does not seem to affect the visual
01100   // quality.
01101   //
01102   // Normalize the value to the 0-1 range (24.8) by dividing by the
01103   // triangle's size:
01104   //
01105   // ((i+j)<<8)/jag_len
01106   //
01107   // Finally convert it to a (8 bit) value
01108   //
01109   // (((((i+j)<<8)/jag_len) * FILL_VALUE) >> 8)
01110   //
01111   // Note: this code only works for black (0) and white (255) values
01112   //
01113   DEBUG_DUMP((dump_byte_table(expanded_data, expanded_width,expanded_height)));
01114   PRUint8 *ps, *es;
01115   PRUint32 jag_len = (expand)/2; // fill/clear one half of the corner
01116   PRUint32 i, j;
01117   for (y=0; y<src_height; y++) {
01118     ps = padded_src + (border_width + (border_width+y)*padded_width);
01119     es = expanded_data
01120          + (border_width+((border_width+y)*expanded_width))*expand;
01121     for (x=0; x<src_width; x++,ps++,es+=expand) {
01122       //
01123       // Fill in the inside of corners
01124       //
01125       if (SRC(ps)==0) {
01126         jag_len = ((expand+1)/2);
01127         if ((SRC_RIGHT(ps)==255) && (SRC_DOWN(ps)==255)) {
01128           // triangular fade fill
01129           for (i=0; i<jag_len; i++)
01130             for (j=0; j<jag_len-i; j++)
01131               es[expand-1-j+((expand-1-i)*expanded_width)] =
01132                             255-(((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01133         }
01134         if ((SRC_DOWN(ps)==255) && (SRC_LEFT(ps)==255)) {
01135           // triangular fade fill
01136           for (i=0; i<jag_len; i++)
01137             for (j=0; j<jag_len-i; j++)
01138               es[j+((expand-1-i)*expanded_width)] =
01139                             255-(((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01140         }
01141         if ((SRC_LEFT(ps)==255) && (SRC_UP(ps)==255)) {
01142           // triangular fade fill
01143           for (i=0; i<jag_len; i++)
01144             for (j=0; j<jag_len-i; j++)
01145               es[j+(i*expanded_width)] =
01146                             255-(((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01147         }
01148         if ((SRC_UP(ps)==255) && (SRC_RIGHT(ps)==255)) {
01149           // triangular fade fill
01150           for (i=0; i<jag_len; i++)
01151             for (j=0; j<jag_len-i; j++)
01152               es[expand-1-j+(i*expanded_width)] =
01153                             255-(((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01154         }
01155       }
01156       //
01157       // Round off the outside of corners
01158       else {
01159         jag_len = ((expand+1)/2);
01160         if ((SRC_UP_LEFT(ps)==0) && (SRC_UP(ps)==0) && (SRC_LEFT(ps)==0)) {
01161           // triangular fade clear
01162           for (i=0; i<jag_len; i++)
01163             for (j=0; j<jag_len-i; j++)
01164               es[j+(i*expanded_width)] =
01165                             (((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01166         }
01167         if ((SRC_UP(ps)==0) && (SRC_UP_RIGHT(ps)==0) && (SRC_RIGHT(ps)==0)) {
01168           // triangular fade clear
01169           for (i=0; i<jag_len; i++)
01170             for (j=0; j<jag_len-i; j++)
01171               es[expand-1-j+(i*expanded_width)] =
01172                             (((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01173         }
01174         if ((SRC_LEFT(ps)==0) && (SRC_DOWN_LEFT(ps)==0) && (SRC_DOWN(ps)==0)) {
01175           // triangular fade clear
01176           for (i=0; i<jag_len; i++)
01177             for (j=0; j<jag_len-i; j++)
01178               es[j+((expand-1-i)*expanded_width)] =
01179                             (((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01180         }
01181         if ((SRC_RIGHT(ps)==0) && (SRC_DOWN_RIGHT(ps)==0) && (SRC_DOWN(ps)==0)){
01182           // triangular fade clear
01183           for (i=0; i<jag_len; i++)
01184             for (j=0; j<jag_len-i; j++)
01185               es[(expand-1-j)+((expand-1-i)*expanded_width)] =
01186                             (((((i+j)<<8)/jag_len)*FILL_VALUE)>>8);
01187         }
01188       }
01189     }
01190   }
01191   DEBUG_DUMP((dump_byte_table(expanded_data, expanded_width,expanded_height)));
01192 
01193   //
01194   // scale the data horizontally
01195   //
01196   // note: size is +dst_buffer_width because the loop can go one pixel
01197   // (horizontal or vertical) past array (with a near 0 width area)
01198   PRUint32 ratio = ((dst_width<<8)/expand)/src_width;
01199 
01200   PRUint32 hsd_len = (dst_buffer_width+1) * expanded_height;
01201   if (hsd_len > sizeof(buffer)) {
01202     horizontally_scaled_data = (PRUint8*)nsMemory::Alloc(hsd_len);
01203     memset(horizontally_scaled_data, 0, hsd_len);
01204   }
01205   for (i=0; i<hsd_len; i++)
01206     horizontally_scaled_data[i] = 0;
01207 
01208   PRUint32 len_x   = src_width * expand;
01209   pHsd = horizontally_scaled_data;
01210   for (y=0; y<expanded_height; y++,pHsd+=dst_buffer_width) {
01211     for (x=0; x<len_x; x++) {
01212       PRUint32 exp_index = start_x + x + (y*expanded_width);
01213       PRUint8 src_val = expanded_data[exp_index];
01214       if (!src_val)
01215         continue;
01216       PRUint32 area_begin = x * ratio;
01217       PRUint32 area_end   = (x+1) * ratio;
01218       PRUint32 end_pixel   = (area_end+255)&0xffffff00;
01219       for (col=(area_begin&0xffffff00); col<end_pixel; col+=256) {
01220         PRUint32 this_begin = MAX(col,area_begin);
01221         PRUint32 this_end   = MIN((col+256), area_end);
01222         pHsd[col>>8] += (PRUint8)(((this_end-this_begin)*src_val)>>8);
01223         if ((&pHsd[col>>8]-horizontally_scaled_data) > (int)hsd_len) {
01224           NS_ASSERTION(0, "buffer too small");
01225           return;
01226         }
01227       }
01228     }
01229 
01230     DEBUG_DUMP((dump_byte_table(horizontally_scaled_data,
01231                 dst_width, expanded_height)));
01232   }
01233 
01234   //
01235   // scale the data vertically
01236   //
01237   ratio = ((dst_height<<8)/expand)/src_height;
01238   PRUint32 len_y   = src_height * expand;
01239   for (x=0; x<dst_width; x++) {
01240     pHsd = horizontally_scaled_data + x + (start_y*dst_buffer_width);
01241     pDst = dst + x;
01242     for (y=0; y<len_y; y++,pHsd+=dst_buffer_width) {
01243       PRUint8 src_val = *pHsd;
01244       if (src_val == 0)
01245         continue;
01246       PRUint32 area_begin = y * ratio;
01247       PRUint32 area_end   = area_begin + ratio;
01248       PRUint32 end_pixel   = (area_end+255)&0xffffff00;
01249       PRUint32 c;
01250       PRUint32 col;
01251       for (c=(area_begin>>8)*dst_buffer_width,col=(area_begin&0xffffff00);
01252                   col<end_pixel; c+=dst_buffer_width,col+=256) {
01253         PRUint32 this_begin = MAX(col,area_begin);
01254         PRUint32 this_end   = MIN((col+256), area_end);
01255         PRUint32 val = (((this_end-this_begin)*src_val)>>8);
01256         pDst[c] += val;
01257       }
01258     }
01259     DEBUG_DUMP((dump_byte_table(dst, dst_width, dst_height)));
01260   }
01261   if (expanded_data != exp_buffer)
01262     free(expanded_data);
01263   if (horizontally_scaled_data != buffer)
01264     free(horizontally_scaled_data);
01265 }
01266 
01267 #ifdef DEBUG
01268 void
01269 nsXFontAAScaledBitmap::dump_XImage_blue_data(XImage *ximage)
01270 {
01271   int x, y;
01272   int width  = ximage->width;
01273   int height = ximage->height;
01274   int pitch = ximage->bytes_per_line;
01275   int depth  = DefaultDepth(sDisplay, DefaultScreen(sDisplay));
01276   PRUint8 *lineStart = (PRUint8 *)ximage->data;
01277   printf("dump_byte_table: width=%d, height=%d\n", width, height);
01278   printf("    ");
01279   for (x=0; (x<width)&&(x<75); x++) {
01280     if ((x%10) == 0)
01281       printf("+ ");
01282     else
01283       printf("- ");
01284   }
01285   printf("\n");
01286   if ((depth == 16) || (depth == 15)) {
01287     short *data = (short *)ximage->data;
01288     for (y=0; y<height; y++) {
01289       printf("%2d: ", y);
01290       data = (short *)lineStart;
01291       for (x=0; (x<width)&&(x<75); x++, data++) {
01292         printf("%02x", *data & 0x1F);
01293       }
01294       printf("\n");
01295       lineStart += ximage->bytes_per_line;
01296     }
01297   }
01298   else if ((depth == 24) || (depth == 32)) {
01299     long *data = (long *)ximage->data;
01300     for (y=0; y<height; y++) {
01301       printf("%2d: ", y);
01302       for (x=0; (x<width)&&(x<75); x++) {
01303         printf("%02x", (short)(data[x+(y*pitch)] & 0xFF));
01304       }
01305       printf("\n");
01306     }
01307   }
01308   else {
01309     printf("depth %d not supported\n", DefaultDepth(sDisplay, DefaultScreen(sDisplay)));
01310   }
01311 }
01312 
01313 void
01314 dump_byte_table(PRUint8 *table, int width, int height)
01315 {
01316   int x, y;
01317   printf("dump_byte_table: width=%d, height=%d\n", width, height);
01318   printf("    ");
01319   for (x=0; x<width; x++) {
01320     if ((x%10) == 0)
01321       printf("+ ");
01322     else
01323       printf("- ");
01324   }
01325   printf("\n");
01326   for (y=0; y<height; y++) {
01327     printf("%2d: ", y);
01328     for (x=0; x<width; x++) {
01329       PRUint8 val = table[x+(y*width)];
01330       printf("%02x", val);
01331     }
01332     printf("\n");
01333   }
01334   printf("---\n");
01335 }
01336 #endif
01337 
01338 static void
01339 WeightTableInitLinearCorrection(PRUint8* aTable, PRUint8 aMinValue,
01340                                 double aGain)
01341 {
01342   // setup the wieghting table
01343   for (int i=0; i<256; i++) {
01344     int val = i;
01345     if (i>=aMinValue)
01346       val += (int)rint((double)(i-aMinValue)*aGain);
01347     val = MAX(0, val);
01348     val = MIN(val, 255);
01349     aTable[i] = (PRUint8)val;
01350   }
01351 }
01352