"Fossies" - the Fresh Open Source Software Archive

Member "libgd-2.3.2/src/gdfx.c" (3 Mar 2021, 15819 Bytes) of package /linux/www/libgd-2.3.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gdfx.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.3.1_vs_2.3.2.

    1 #ifdef HAVE_CONFIG_H
    2 #include "config.h"
    3 #endif /* HAVE_CONFIG_H */
    4 
    5 #include "gd.h"
    6 #include "gd_errors.h"
    7 #include "gd_intern.h"
    8 #include <math.h>
    9 
   10 /* In tests this is sufficient to prevent obvious artifacts */
   11 #define MAG 4
   12 
   13 #define PI 3.141592
   14 #define DEG2RAD(x) ((x)*PI/180.)
   15 
   16 #define MAX4(x,y,z,w) \
   17     ((MAX((x),(y))) > (MAX((z),(w))) ? (MAX((x),(y))) : (MAX((z),(w))))
   18 #define MIN4(x,y,z,w) \
   19     ((MIN((x),(y))) < (MIN((z),(w))) ? (MIN((x),(y))) : (MIN((z),(w))))
   20 
   21 #define MAXX(x) MAX4(x[0],x[2],x[4],x[6])
   22 #define MINX(x) MIN4(x[0],x[2],x[4],x[6])
   23 #define MAXY(x) MAX4(x[1],x[3],x[5],x[7])
   24 #define MINY(x) MIN4(x[1],x[3],x[5],x[7])
   25 
   26 /**
   27  * Function: gdImageStringFTCircle
   28  *
   29  * Draw text curved along the top and bottom of a circular area of an image.
   30  *
   31  * Parameters:
   32  *  im          - The image to draw onto.
   33  *  cx          - The x-coordinate of the center of the circular area.
   34  *  cy          - The y-coordinate of the center of the circular area.
   35  *  radius      - The radius of the circular area.
   36  *  textRadius  - The height of each character; if textRadius is 1/2 of radius,
   37  *                characters extend halfway from the edge to the center.
   38  *  fillPortion - The percentage of the 180 degrees of the circular area
   39  *                assigned to each section of text, that is actually occupied
   40  *                by text. The value has to be in range 0.0 to 1.0, with useful
   41  *                values from about 0.4 to 0.9; 0.9 looks better than 1.0 which
   42  *                is rather crowded.
   43  *  font        - The fontlist that is passed to <gdImageStringFT>.
   44  *  points      - The point size, which functions as a hint. Although the size
   45  *                of the text is determined by radius, textRadius and
   46  *                fillPortion, a point size that 'hints' appropriately should be
   47  *                passed. If it's known that the text will be large, a large
   48  *                point size such as 24.0 should be passed to get the best
   49  *                results.
   50  *  top         - The text to draw clockwise at the top of the circular area.
   51  *  bottom      - The text to draw counterclockwise at the bottom of the
   52  *                circular area.
   53  *  fgcolor     - The font color.
   54  *
   55  * Returns:
   56  *  NULL on success, or an error string on failure.
   57  */
   58 BGD_DECLARE(char*)
   59 gdImageStringFTCircle (gdImagePtr im,
   60                        int cx,
   61                        int cy,
   62                        double radius,
   63                        double textRadius,
   64                        double fillPortion,
   65                        char *font,
   66                        double points, char *top, char *bottom, int fgcolor)
   67 {
   68     char *err;
   69     int w;
   70     int brect[8];
   71     int sx1, sx2, sy1, sy2, sx, sy;
   72     int x, y;
   73     int fr, fg, fb, fa;
   74     int ox, oy;
   75     double prop;
   76     gdImagePtr im1;
   77     gdImagePtr im2;
   78     gdImagePtr im3;
   79     /* obtain brect so that we can size the image */
   80     err = gdImageStringFT ((gdImagePtr) NULL,
   81                            &brect[0], 0, font, points * MAG, 0, 0, 0, bottom);
   82     if (err) {
   83         return err;
   84     }
   85     sx1 = MAXX (brect) - MINX (brect) + 6;
   86     sy1 = MAXY (brect) - MINY (brect) + 6;
   87     err = gdImageStringFT ((gdImagePtr) NULL,
   88                            &brect[0], 0, font, points * MAG, 0, 0, 0, top);
   89     if (err) {
   90         return err;
   91     }
   92     sx2 = MAXX (brect) - MINX (brect) + 6;
   93     sy2 = MAXY (brect) - MINY (brect) + 6;
   94     /* Pad by 4 pixels to allow for slight errors
   95        observed in the bounding box returned by freetype */
   96     if (sx1 > sx2) {
   97         sx = sx1 * 2 + 4;
   98     } else {
   99         sx = sx2 * 2 + 4;
  100     }
  101     if (sy1 > sy2) {
  102         sy = sy1;
  103     } else {
  104         sy = sy2;
  105     }
  106     im1 = gdImageCreateTrueColor (sx, sy);
  107     if (!im1) {
  108         return "could not create first image";
  109     }
  110     err = gdImageStringFT (im1, 0, gdTrueColor (255, 255, 255),
  111                            font, points * MAG,
  112                            0, ((sx / 2) - sx1) / 2, points * MAG, bottom);
  113     if (err) {
  114         gdImageDestroy (im1);
  115         return err;
  116     }
  117     /* We don't know the descent, which would be needed to do this
  118        with the angle parameter. Instead, implement a simple
  119        flip operation ourselves. */
  120     err = gdImageStringFT (im1, 0, gdTrueColor (255, 255, 255),
  121                            font, points * MAG,
  122                            0, sx / 2 + ((sx / 2) - sx2) / 2, points * MAG, top);
  123     if (err) {
  124         gdImageDestroy (im1);
  125         return err;
  126     }
  127     /* Flip in place is tricky, be careful not to double-swap things */
  128     if (sy & 1) {
  129         for (y = 0; (y <= (sy / 2)); y++) {
  130             int xlimit = sx - 2;
  131             if (y == (sy / 2)) {
  132                 /* If there is a "middle" row, be careful
  133                    not to swap twice! */
  134                 xlimit -= (sx / 4);
  135             }
  136             for (x = (sx / 2) + 2; (x < xlimit); x++) {
  137                 int t;
  138                 int ox = sx - x + (sx / 2) - 1;
  139                 int oy = sy - y - 1;
  140                 t = im1->tpixels[oy][ox];
  141                 im1->tpixels[oy][ox] = im1->tpixels[y][x];
  142                 im1->tpixels[y][x] = t;
  143             }
  144         }
  145     } else {
  146         for (y = 0; (y < (sy / 2)); y++) {
  147             int xlimit = sx - 2;
  148             for (x = (sx / 2) + 2; (x < xlimit); x++) {
  149                 int t;
  150                 int ox = sx - x + (sx / 2) - 1;
  151                 int oy = sy - y - 1;
  152                 t = im1->tpixels[oy][ox];
  153                 im1->tpixels[oy][ox] = im1->tpixels[y][x];
  154                 im1->tpixels[y][x] = t;
  155             }
  156         }
  157     }
  158 #if STEP_PNGS
  159     {
  160         FILE *out = fopen ("gdfx1.png", "wb");
  161         gdImagePng (im1, out);
  162         fclose (out);
  163     }
  164 #endif /* STEP_PNGS */
  165     /* Resample taller; the exact proportions of the text depend on the
  166        ratio of textRadius to radius, and the value of fillPortion */
  167     if (sx > sy * 10) {
  168         w = sx;
  169     } else {
  170         w = sy * 10;
  171     }
  172     im2 = gdImageCreateTrueColor (w, w);
  173     if (!im2) {
  174         gdImageDestroy (im1);
  175         return "could not create resampled image";
  176     }
  177     prop = textRadius / radius;
  178     gdImageCopyResampled (im2, im1,
  179                           gdImageSX (im2) * (1.0 - fillPortion) / 4,
  180                           sy * 10 * (1.0 - prop),
  181                           0, 0,
  182                           gdImageSX (im2) * fillPortion / 2, sy * 10 * prop,
  183                           gdImageSX (im1) / 2, gdImageSY (im1));
  184     gdImageCopyResampled (im2, im1,
  185                           (gdImageSX (im2) / 2) +
  186                           gdImageSX (im2) * (1.0 - fillPortion) / 4,
  187                           sy * 10 * (1.0 - prop),
  188                           gdImageSX (im1) / 2, 0,
  189                           gdImageSX (im2) * fillPortion / 2, sy * 10 * prop,
  190                           gdImageSX (im1) / 2, gdImageSY (im1));
  191 #if STEP_PNGS
  192     {
  193         FILE *out = fopen ("gdfx2.png", "wb");
  194         gdImagePng (im2, out);
  195         fclose (out);
  196     }
  197 #endif /* STEP_PNGS */
  198 
  199     gdImageDestroy (im1);
  200 
  201     /* Ready to produce a circle */
  202     im3 = gdImageSquareToCircle (im2, radius);
  203     if (im3 == NULL) {
  204         gdImageDestroy(im2);
  205         return 0;
  206     }
  207     gdImageDestroy (im2);
  208     /* Now blend im3 with the destination. Cheat a little. The
  209        source (im3) is white-on-black, so we can use the
  210        red component as a basis for alpha as long as we're
  211        careful to shift off the extra bit and invert
  212        (alpha ranges from 0 to 127 where 0 is OPAQUE).
  213        Also be careful to allow for an alpha component
  214        in the fgcolor parameter itself (gug!) */
  215     fr = gdTrueColorGetRed (fgcolor);
  216     fg = gdTrueColorGetGreen (fgcolor);
  217     fb = gdTrueColorGetBlue (fgcolor);
  218     fa = gdTrueColorGetAlpha (fgcolor);
  219     ox = cx - (im3->sx / 2);
  220     oy = cy - (im3->sy / 2);
  221     for (y = 0; (y < im3->sy); y++) {
  222         for (x = 0; (x < im3->sx); x++) {
  223             int a = gdTrueColorGetRed (im3->tpixels[y][x]) >> 1;
  224             a *= (127 - fa);
  225             a /= 127;
  226             a = 127 - a;
  227             gdImageSetPixel (im, x + ox, y + oy,
  228                              gdTrueColorAlpha (fr, fg, fb, a));
  229         }
  230     }
  231     gdImageDestroy (im3);
  232     return 0;
  233 }
  234 
  235 #if GDFX_MAIN
  236 
  237 int
  238 main (int argc, char *argv[])
  239 {
  240     FILE *in;
  241     FILE *out;
  242     gdImagePtr im;
  243     int radius;
  244     /* Create an image of text on a circle, with an
  245        alpha channel so that we can copy it onto a
  246        background */
  247     in = fopen ("eleanor.jpg", "rb");
  248     if (!in) {
  249         im = gdImageCreateTrueColor (300, 300);
  250     } else {
  251         im = gdImageCreateFromJpeg (in);
  252         fclose (in);
  253     }
  254     if (gdImageSX (im) < gdImageSY (im)) {
  255         radius = gdImageSX (im) / 2;
  256     } else {
  257         radius = gdImageSY (im) / 2;
  258     }
  259     gdImageStringFTCircle (im,
  260                            gdImageSX (im) / 2,
  261                            gdImageSY (im) / 2,
  262                            radius,
  263                            radius / 2,
  264                            0.8,
  265                            "arial",
  266                            24,
  267                            "top text",
  268                            "bottom text", gdTrueColorAlpha (240, 240, 255, 32));
  269     out = fopen ("gdfx.png", "wb");
  270     if (!out) {
  271         gd_error("Can't create gdfx.png\n");
  272         return 1;
  273     }
  274     gdImagePng (im, out);
  275     fclose (out);
  276     gdImageDestroy (im);
  277     return 0;
  278 }
  279 
  280 #endif /* GDFX_MAIN */
  281 
  282 /* Note: don't change these */
  283 #define SUPER 2
  284 #define SUPERBITS1 1
  285 #define SUPERBITS2 2
  286 
  287 /**
  288  * Function: gdImageSquareToCircle
  289  *
  290  * Apply polar coordinate transformation to an image.
  291  *
  292  * The X axis of the original will be remapped to theta (angle) and the Y axis
  293  * of the original will be remapped to rho (distance from center).
  294  *
  295  * Parameters:
  296  *  im     - The image, which must be square, i.e. width == height.
  297  *  radius - The radius of the new image, i.e. width == height == radius * 2.
  298  *
  299  * Returns:
  300  *  The transformed image, or NULL on failure.
  301  */
  302 BGD_DECLARE(gdImagePtr)
  303 gdImageSquareToCircle (gdImagePtr im, int radius)
  304 {
  305     int x, y;
  306     double c;
  307     gdImagePtr im2;
  308     if (im->sx != im->sy) {
  309         /* Source image must be square */
  310         return 0;
  311     }
  312     im2 = gdImageCreateTrueColor (radius * 2, radius * 2);
  313     if (!im2) {
  314         return 0;
  315     }
  316     /* Supersampling for a nicer result */
  317     c = (im2->sx / 2) * SUPER;
  318     for (y = 0; (y < im2->sy * SUPER); y++) {
  319         for (x = 0; (x < im2->sx * SUPER); x++) {
  320             double rho = sqrt ((x - c) * (x - c) + (y - c) * (y - c));
  321             int pix;
  322             int cpix;
  323             double theta;
  324             double ox;
  325             double oy;
  326             int red, green, blue, alpha;
  327             if (rho > c) {
  328                 continue;
  329             }
  330             theta = atan2 (x - c, y - c) + PI / 2;
  331             if (theta < 0) {
  332                 theta += 2 * PI;
  333             }
  334             /* Undo supersampling */
  335             oy = (rho * im->sx) / (im2->sx * SUPER / 2);
  336             ox = theta * im->sx / (3.141592653 * 2);
  337             pix = gdImageGetPixel (im, ox, oy);
  338             cpix = im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1];
  339             red =
  340                 (gdImageRed (im, pix) >> SUPERBITS2) + gdTrueColorGetRed (cpix);
  341             green =
  342                 (gdImageGreen (im, pix) >> SUPERBITS2) +
  343                 gdTrueColorGetGreen (cpix);
  344             blue =
  345                 (gdImageBlue (im, pix) >> SUPERBITS2) + gdTrueColorGetBlue (cpix);
  346             alpha =
  347                 (gdImageAlpha (im, pix) >> SUPERBITS2) +
  348                 gdTrueColorGetAlpha (cpix);
  349             im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1] =
  350                 gdTrueColorAlpha (red, green, blue, alpha);
  351         }
  352     }
  353     /* Restore full dynamic range, 0-63 yields 0-252. Replication of
  354        first 2 bits in last 2 bits has the desired effect. Note
  355        slightly different arithmetic for alpha which is 7-bit.
  356        NOTE: only correct for SUPER == 2 */
  357     for (y = 0; (y < im2->sy); y++) {
  358         for (x = 0; (x < im2->sx); x++) {
  359             /* Copy first 2 bits to last 2 bits, matching the
  360                dynamic range of the original cheaply */
  361             int cpix = im2->tpixels[y][x];
  362 
  363             im2->tpixels[y][x] = gdTrueColorAlpha ((gdTrueColorGetRed (cpix) &
  364                                                     0xFC) +
  365                                                    ((gdTrueColorGetRed (cpix) &
  366                                                            0xC0) >> 6),
  367                                                    (gdTrueColorGetGreen (cpix) &
  368                                                     0xFC) +
  369                                                    ((gdTrueColorGetGreen (cpix)
  370                                                            & 0xC0) >> 6),
  371                                                    (gdTrueColorGetBlue (cpix) &
  372                                                     0xFC) +
  373                                                    ((gdTrueColorGetBlue (cpix) &
  374                                                            0xC0) >> 6),
  375                                                    (gdTrueColorGetAlpha (cpix) &
  376                                                     0x7C) +
  377                                                    ((gdTrueColorGetAlpha (cpix)
  378                                                            & 0x60) >> 6));
  379         }
  380     }
  381     return im2;
  382 }
  383 
  384 /* 2.0.16: Called by gdImageSharpen to avoid excessive code repetition
  385     Added on 2003-11-19 by
  386     Paul Troughton (paul<dot>troughton<at>ieee<dot>org)
  387     Given filter coefficents and colours of three adjacent pixels,
  388 returns new colour for centre pixel
  389 */
  390 
  391 int
  392 gdImageSubSharpen (int pc, int c, int nc, float inner_coeff, float
  393                    outer_coeff)
  394 {
  395     float red, green, blue, alpha;
  396 
  397     red = inner_coeff * gdTrueColorGetRed (c) + outer_coeff *
  398           (gdTrueColorGetRed (pc) + gdTrueColorGetRed (nc));
  399     green = inner_coeff * gdTrueColorGetGreen (c) + outer_coeff *
  400             (gdTrueColorGetGreen (pc) + gdTrueColorGetGreen (nc));
  401     blue = inner_coeff * gdTrueColorGetBlue (c) + outer_coeff *
  402            (gdTrueColorGetBlue (pc) + gdTrueColorGetBlue (nc));
  403     alpha = gdTrueColorGetAlpha (c);
  404 
  405     /* Clamping, as can overshoot bounds in either direction */
  406     if (red > 255.0f) {
  407         red = 255.0f;
  408     }
  409     if (green > 255.0f) {
  410         green = 255.0f;
  411     }
  412     if (blue > 255.0f) {
  413         blue = 255.0f;
  414     }
  415     if (red < 0.0f) {
  416         red = 0.0f;
  417     }
  418     if (green < 0.0f) {
  419         green = 0.0f;
  420     }
  421     if (blue < 0.0f) {
  422         blue = 0.0f;
  423     }
  424 
  425     return gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha);
  426 }
  427 
  428 /**
  429  * Function: gdImageSharpen
  430  *
  431  * Sharpen an image.
  432  *
  433  * Uses a simple 3x3 convolution kernel and makes use of separability.
  434  * It's faster, but less flexible, than full-blown unsharp masking.
  435  * Silently does nothing to non-truecolor images and for pct<0, as it's not a useful blurring function.
  436  *
  437  * Parameters:
  438  *  pct - The sharpening percentage, which can be greater than 100.
  439  *
  440  * Author:
  441  *  Paul Troughton (paul<dot>troughton<at>ieee<dot>org)
  442  */
  443 BGD_DECLARE(void)
  444 gdImageSharpen (gdImagePtr im, int pct)
  445 {
  446     int x, y;
  447     int sx, sy;
  448     float inner_coeff, outer_coeff;
  449 
  450     sx = im->sx;
  451     sy = im->sy;
  452 
  453     /* Must sum to 1 to avoid overall change in brightness.
  454      * Scaling chosen so that pct=100 gives 1-D filter [-1 6 -1]/4,
  455      * resulting in a 2-D filter [1 -6 1; -6 36 -6; 1 -6 1]/16,
  456      * which gives noticeable, but not excessive, sharpening
  457      */
  458 
  459     outer_coeff = -pct / 400.0;
  460     inner_coeff = 1 - 2 * outer_coeff;
  461 
  462     /* Don't try to do anything with non-truecolor images, as
  463        pointless,
  464        * nor for pct<=0, as small kernel size leads to nasty
  465        artefacts when blurring
  466      */
  467     if ((im->trueColor) && (pct > 0)) {
  468 
  469         /* First pass, 1-D convolution column-wise */
  470         for (x = 0; x < sx; x++) {
  471 
  472             /* pc is colour of previous pixel; c of the
  473                current pixel and nc of the next */
  474             int pc, c, nc;
  475 
  476             /* Replicate edge pixel at image boundary */
  477             pc = gdImageGetPixel (im, x, 0);
  478 
  479             /* Stop looping before last pixel to avoid
  480                conditional within loop */
  481             for (y = 0; y < sy - 1; y++) {
  482 
  483                 c = gdImageGetPixel (im, x, y);
  484 
  485                 nc = gdImageGetTrueColorPixel (im, x, y + 1);
  486 
  487                 /* Update centre pixel to new colour */
  488                 gdImageSetPixel (im, x, y,
  489                                  gdImageSubSharpen (pc, c, nc, inner_coeff,
  490                                                     outer_coeff));
  491 
  492                 /* Save original colour of current
  493                    pixel for next time round */
  494                 pc = c;
  495             }
  496 
  497             /* Deal with last pixel, replicating current
  498                pixel at image boundary */
  499             c = gdImageGetPixel (im, x, y);
  500             gdImageSetPixel (im, x, y, gdImageSubSharpen
  501                              (pc, c, c, inner_coeff, outer_coeff));
  502         }
  503 
  504         /* Second pass, 1-D convolution row-wise */
  505         for (y = 0; y < sy; y++) {
  506             int pc, c;
  507             pc = gdImageGetPixel (im, 0, y);
  508             for (x = 0; x < sx - 1; x++) {
  509                 int c, nc;
  510                 c = gdImageGetPixel (im, x, y);
  511                 nc = gdImageGetTrueColorPixel (im, x + 1, y);
  512                 gdImageSetPixel (im, x, y,
  513                                  gdImageSubSharpen (pc, c, nc, inner_coeff,
  514                                                     outer_coeff));
  515                 pc = c;
  516             }
  517             c = gdImageGetPixel (im, x, y);
  518             gdImageSetPixel (im, x, y, gdImageSubSharpen
  519                              (pc, c, c, inner_coeff, outer_coeff));
  520         }
  521     }
  522 }