bool OglContext::createPBuffer(ms_uint32 width, ms_uint32 height) { int maxHeight, maxWidth; glXGetFBConfigAttrib(window, *configs, GLX_MAX_PBUFFER_WIDTH, &maxWidth); glXGetFBConfigAttrib(window, *configs, GLX_MAX_PBUFFER_HEIGHT, &maxHeight); ms_uint32 uMaxHeight = maxHeight, uMaxWidth = maxWidth; this->width = MS_MIN(width, uMaxWidth); this->height = MS_MIN(height, uMaxHeight); int iPbufferAttributes[] = { GLX_PBUFFER_WIDTH, this->width, GLX_PBUFFER_HEIGHT, this->height, GLX_LARGEST_PBUFFER, false, 0, 0 }; pbuffer = glXCreatePbuffer(window, *configs, iPbufferAttributes); if (pbuffer == 0) { msSetError(MS_OGLERR, "glXCreatePbuffer failed. glError: %d", "OglContext::init()", glGetError()); return false; } return true; }
/* ** Merges rect b into rect a. Rect a changes, b does not. */ void msMergeRect(rectObj *a, rectObj *b) { a->minx = MS_MIN(a->minx, b->minx); a->maxx = MS_MAX(a->maxx, b->maxx); a->miny = MS_MIN(a->miny, b->miny); a->maxy = MS_MAX(a->maxy, b->maxy); }
static void get_bbox(pointObj *poiList, int numpoints, double *minx, double *miny, double *maxx, double *maxy) { int j; *minx = *maxx = poiList[0].x; *miny = *maxy = poiList[0].y; for(j=1; j<numpoints; j++) { if ((poiList[j].x==-99.0) || (poiList[j].y==-99.0)) continue; *minx = MS_MIN(*minx, poiList[j].x); *maxx = MS_MAX(*maxx, poiList[j].x); *miny = MS_MIN(*miny, poiList[j].y); *maxy = MS_MAX(*maxy, poiList[j].y); } return; }
void computeSymbolStyle(symbolStyleObj *s, styleObj *src, symbolObj *symbol, double scalefactor, double resolutionfactor) { double default_size; double target_size; double style_size; default_size = msSymbolGetDefaultSize(symbol); style_size = (src->size==-1)?default_size:src->size; INIT_SYMBOL_STYLE(*s); if(symbol->type == MS_SYMBOL_PIXMAP) { s->color = s->outlinecolor = NULL; } else if(symbol->filled || symbol->type == MS_SYMBOL_TRUETYPE) { if(MS_VALID_COLOR(src->color)) s->color = &src->color; if(MS_VALID_COLOR(src->outlinecolor)) s->outlinecolor = &src->outlinecolor; } else { if(MS_VALID_COLOR(src->color)) s->outlinecolor = &(src->color); else if(MS_VALID_COLOR(src->outlinecolor)) s->outlinecolor = &(src->outlinecolor); s->color = NULL; } if(MS_VALID_COLOR(src->backgroundcolor)) { s->backgroundcolor = &(src->backgroundcolor); } target_size = style_size * scalefactor; target_size = MS_MAX(target_size, src->minsize*resolutionfactor); target_size = MS_MIN(target_size, src->maxsize*resolutionfactor); s->scale = target_size / default_size; s->gap = src->gap * target_size / style_size; if(s->outlinecolor) { s->outlinewidth = src->width * scalefactor; s->outlinewidth = MS_MAX(s->outlinewidth, src->minwidth*resolutionfactor); s->outlinewidth = MS_MIN(s->outlinewidth, src->maxwidth*resolutionfactor); } else { s->outlinewidth = 0; } s->rotation = src->angle * MS_DEG_TO_RAD; }
int msQuantizeRasterBuffer(rgbaPixel *pixelsIn,int height,int width, unsigned int *reqcolors, rgbaPixel *palette, unsigned int *palette_scaling_maxval) { register rgbaPixel *pP; register int col; unsigned char newmaxval; acolorhist_vector achv, acolormap=NULL; int row; int colors; int newcolors = 0; int x; *palette_scaling_maxval = 255; int row_step = width*4; rgbaPixel **apixels=(rgbaPixel**)malloc(height*sizeof(rgbaPixel**)); for(row=0;row<height;row++) { apixels[row]=(rgbaPixel*)(&(pixelsIn[row*width])); } /* ** Step 2: attempt to make a histogram of the colors, unclustered. ** If at first we don't succeed, lower maxval to increase color ** coherence and try again. This will eventually terminate, with ** maxval at worst 15, since 32^3 is approximately MAXCOLORS. [GRR POSSIBLE BUG: what about 32^4 ?] */ for ( ; ; ) { achv = pam_computeacolorhist( apixels, width, height, MAXCOLORS, &colors ); if ( achv != (acolorhist_vector) 0 ) break; newmaxval = *palette_scaling_maxval / 2; for ( row = 0; row < height; ++row ) for ( col = 0, pP = apixels[row]; col < width; ++col, ++pP ) PAM_DEPTH( *pP, *pP, *palette_scaling_maxval, newmaxval ); *palette_scaling_maxval = newmaxval; } newcolors = MS_MIN(colors, *reqcolors); acolormap = mediancut(achv, colors, width*height, *palette_scaling_maxval, newcolors); pam_freeacolorhist(achv); *reqcolors = newcolors; for (x = 0; x < newcolors; ++x) { palette[x].r = acolormap[x].acolor.r; palette[x].g = acolormap[x].acolor.g; palette[x].b = acolormap[x].acolor.b; palette[x].a = acolormap[x].acolor.a; } free(acolormap); free(apixels); return MS_SUCCESS; }
void msComputeBounds(shapeObj *shape) { int i, j; if(shape->numlines <= 0) return; if(shape->line[0].numpoints <= 0) return; shape->bounds.minx = shape->bounds.maxx = shape->line[0].point[0].x; shape->bounds.miny = shape->bounds.maxy = shape->line[0].point[0].y; for( i=0; i<shape->numlines; i++ ) { for( j=0; j<shape->line[i].numpoints; j++ ) { shape->bounds.minx = MS_MIN(shape->bounds.minx, shape->line[i].point[j].x); shape->bounds.maxx = MS_MAX(shape->bounds.maxx, shape->line[i].point[j].x); shape->bounds.miny = MS_MIN(shape->bounds.miny, shape->line[i].point[j].y); shape->bounds.maxy = MS_MAX(shape->bounds.maxy, shape->line[i].point[j].y); } } }
static void msProjectGrowRect(projectionObj *in, projectionObj *out, rectObj *prj_rect, int *rect_initialized, pointObj *prj_point, int *failure ) { if( msProjectPoint(in, out, prj_point) == MS_SUCCESS ) { if( *rect_initialized ) { prj_rect->miny = MS_MIN(prj_rect->miny, prj_point->y); prj_rect->maxy = MS_MAX(prj_rect->maxy, prj_point->y); prj_rect->minx = MS_MIN(prj_rect->minx, prj_point->x); prj_rect->maxx = MS_MAX(prj_rect->maxx, prj_point->x); } else { prj_rect->minx = prj_rect->maxx = prj_point->x; prj_rect->miny = prj_rect->maxy = prj_point->y; *rect_initialized = MS_TRUE; } } else (*failure)++; }
double msDistancePointToSegment(pointObj *p, pointObj *a, pointObj *b) { double l; /* length of line ab */ double r,s; l = msDistancePointToPoint(a,b); if(l == 0.0) // a = b return( msDistancePointToPoint(a,p)); r = ((a->y - p->y)*(a->y - b->y) - (a->x - p->x)*(b->x - a->x))/(l*l); if(r > 1) /* perpendicular projection of P is on the forward extention of AB */ return(MS_MIN(msDistancePointToPoint(p, b),msDistancePointToPoint(p, a))); if(r < 0) /* perpendicular projection of P is on the backward extention of AB */ return(MS_MIN(msDistancePointToPoint(p, b),msDistancePointToPoint(p, a))); s = ((a->y - p->y)*(b->x - a->x) - (a->x - p->x)*(b->y - a->y))/(l*l); return(fabs(s*l)); }
int msIntersectSegments(const pointObj *a, const pointObj *b, const pointObj *c, const pointObj *d) /* from comp.graphics.alogorithms FAQ */ { double r, s; double denominator, numerator; numerator = ((a->y-c->y)*(d->x-c->x) - (a->x-c->x)*(d->y-c->y)); denominator = ((b->x-a->x)*(d->y-c->y) - (b->y-a->y)*(d->x-c->x)); if((denominator == 0) && (numerator == 0)) { /* lines are coincident, intersection is a line segement if it exists */ if(a->y == c->y) { /* coincident horizontally, check x's */ if(((a->x >= MS_MIN(c->x,d->x)) && (a->x <= MS_MAX(c->x,d->x))) || ((b->x >= MS_MIN(c->x,d->x)) && (b->x <= MS_MAX(c->x,d->x)))) return(MS_TRUE); else return(MS_FALSE); } else { /* test for y's will work fine for remaining cases */ if(((a->y >= MS_MIN(c->y,d->y)) && (a->y <= MS_MAX(c->y,d->y))) || ((b->y >= MS_MIN(c->y,d->y)) && (b->y <= MS_MAX(c->y,d->y)))) return(MS_TRUE); else return(MS_FALSE); } } if(denominator == 0) /* lines are parallel, can't intersect */ return(MS_FALSE); r = numerator/denominator; if((r<0) || (r>1)) return(MS_FALSE); /* no intersection */ numerator = ((a->y-c->y)*(b->x-a->x) - (a->x-c->x)*(b->y-a->y)); s = numerator/denominator; if((s<0) || (s>1)) return(MS_FALSE); /* no intersection */ return(MS_TRUE); }
int agg2RenderBitmapGlyphs2(imageObj *img, textPathObj *tp, colorObj *c, colorObj *oc, int ow) { int size = tp->glyph_size; size = MS_MAX(0,size); size = MS_MIN(4,size); AGG2Renderer *r = AGG_RENDERER(img); glyph_gen glyph(0); mapserver::renderer_raster_htext_solid<renderer_base, glyph_gen> rt(r->m_renderer_base, glyph); glyph.font(rasterfonts[size]); unsigned int cc_start = rasterfonts[size][2]; unsigned int cc_end = cc_start + rasterfonts[size][3]; unsigned int glyph_idx; if(oc) { rt.color(aggColor(oc)); for(int n=0; n<tp->numglyphs; n++) { glyphObj *g = &tp->glyphs[n]; /* do some unicode mapping, for now we just replace unsupported characters with "." */ if(g->glyph->key.codepoint < cc_start || g->glyph->key.codepoint > cc_end) { glyph_idx = '.'; } else { glyph_idx = g->glyph->key.codepoint; } for(int i=-1; i<=1; i++) { for(int j=-1; j<=1; j++) { if(i||j) { rt.render_glyph(g->pnt.x+i , g->pnt.y+j, glyph_idx, true); } } } } } if(c) { rt.color(aggColor(c)); for(int n=0; n<tp->numglyphs; n++) { glyphObj *g = &tp->glyphs[n]; /* do some unicode mapping, for now we just replace unsupported characters with "." */ if(g->glyph->key.codepoint < cc_start || g->glyph->key.codepoint > cc_end) { glyph_idx = '.'; } else { glyph_idx = g->glyph->key.codepoint; } rt.render_glyph(g->pnt.x , g->pnt.y, glyph_idx, true); } } return MS_SUCCESS; }
gdImagePtr rotatePixmapGD(gdImagePtr img, double angle_rad) { gdImagePtr rimg = NULL; double cos_a, sin_a; double x1 = 0.0, y1 = 0.0; /* destination rectangle */ double x2 = 0.0, y2 = 0.0; double x3 = 0.0, y3 = 0.0; double x4 = 0.0, y4 = 0.0; long minx, miny, maxx, maxy; int width=0, height=0; /* int color; */ sin_a = sin(angle_rad); cos_a = cos(angle_rad); /* compute distination rectangle (x1,y1 is known) */ x1 = 0 ; y1 = 0 ; x2 = img->sy * sin_a; y2 = -img->sy * cos_a; x3 = (img->sx * cos_a) + (img->sy * sin_a); y3 = (img->sx * sin_a) - (img->sy * cos_a); x4 = (img->sx * cos_a); y4 = (img->sx * sin_a); minx = (long) MS_MIN(x1,MS_MIN(x2,MS_MIN(x3,x4))); miny = (long) MS_MIN(y1,MS_MIN(y2,MS_MIN(y3,y4))); maxx = (long) MS_MAX(x1,MS_MAX(x2,MS_MAX(x3,x4))); maxy = (long) MS_MAX(y1,MS_MAX(y2,MS_MAX(y3,y4))); width = (int)ceil(maxx-minx); height = (int)ceil(maxy-miny); /* create the new image based on the computed width/height */ if (gdImageTrueColor(img)) { rimg = gdImageCreateTrueColor(width, height); gdImageAlphaBlending(rimg, 0); gdImageFilledRectangle(rimg, 0, 0, width, height, gdImageColorAllocateAlpha(rimg, 0, 0, 0, gdAlphaTransparent)); } else { int tc = gdImageGetTransparent(img); rimg = gdImageCreate(width, height); if(tc != -1) gdImageColorTransparent(rimg, gdImageColorAllocate(rimg, gdImageRed(img, tc), gdImageGreen(img, tc), gdImageBlue(img, tc))); } if(!rimg) { msSetError(MS_GDERR,"failed to create rotated pixmap","rotatePixmapGD()"); return NULL; } gdImageCopyRotated (rimg, img, width*0.5, height*0.5, 0, 0, gdImageSX(img), gdImageSY(img), angle_rad*MS_RAD_TO_DEG); return rimg; }
ms_uint32 OglContext::getTextureSize(GLuint dimension, ms_uint32 value) { glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, value, value, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); GLint check = 0; glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, dimension, &check); if (glGetError() != GL_NO_ERROR) { msSetError(MS_OGLERR, "glGetTexLevelParameteriv failed. glError: %d", "OglContext::getTextureSize()", glGetError()); } if (check == 0) { return MS_MAX(MS_MIN(NextPowerOf2(value), OglContext::MAX_TEXTURE_SIZE), OglContext::MIN_TEXTURE_SIZE); } else { msSetError(MS_OGLERR, "Unable to create opengl texture of map size", "OglContext::getTextureSize()"); return value; } }
int getBitmapGlyphMetrics(int size, unsigned int unicode, glyph_metrics *bounds) { size = MS_MAX(0,size); size = MS_MIN(4,size); unsigned int glyph_idx; /* do some unicode mapping, for now we just replace unsupported characters with "." */ unsigned int cc_start = rasterfonts[size][2]; unsigned int cc_end = cc_start + rasterfonts[size][3]; if(unicode < cc_start || unicode > cc_end) { glyph_idx = '.'; } else { glyph_idx = unicode; } glyph_gen glyph(0); glyph.font(rasterfonts[size]); bounds->minx = 0; bounds->miny = -glyph.base_line(); bounds->maxx = glyph.glyph_width(glyph_idx); bounds->maxy = glyph.height() + bounds->miny; bounds->advance = bounds->maxx; return MS_SUCCESS; }
/* ** Returns the size, in pixels, of a marker symbol defined by a specific style and scalefactor. Used for annotation ** layer collision avoidance. A marker is made up of a number of styles so the calling code must either do the looping ** itself or call this function for the bottom style which should be the largest. */ int msGetMarkerSize(symbolSetObj *symbolset, styleObj *style, double *width, double *height, double scalefactor) { rectObj rect; int size; symbolObj *symbol; *width = *height = 0; /* set a starting value */ if(style->symbol > symbolset->numsymbols || style->symbol < 0) return(MS_FAILURE); /* no such symbol, 0 is OK */ if(style->symbol == 0) { /* single point */ *width = 1; *height = 1; return(MS_SUCCESS); } symbol = symbolset->symbol[style->symbol]; if (symbol->type == MS_SYMBOL_PIXMAP && !symbol->pixmap_buffer) { if (MS_SUCCESS != msPreloadImageSymbol(MS_MAP_RENDERER(symbolset->map), symbol)) return MS_FAILURE; } if(symbol->type == MS_SYMBOL_SVG && !symbol->renderer_cache) { #ifdef USE_SVG_CAIRO if(MS_SUCCESS != msPreloadSVGSymbol(symbol)) return MS_FAILURE; #else msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "msGetMarkerSize()"); return MS_FAILURE; #endif } if(style->size == -1) { size = ( msSymbolGetDefaultSize(symbol) * scalefactor ); } else size = (style->size*scalefactor); size = MS_MAX(size, style->minsize); size = MS_MIN(size, style->maxsize); switch(symbol->type) { case(MS_SYMBOL_TRUETYPE): if(msGetTruetypeTextBBox(MS_MAP_RENDERER(symbolset->map),symbol->font,symbolset->fontset,size,symbol->character,&rect,NULL,0) != MS_SUCCESS) return(MS_FAILURE); *width = MS_MAX(*width, rect.maxx - rect.minx); *height = MS_MAX(*height, rect.maxy - rect.miny); break; case(MS_SYMBOL_PIXMAP): if(size == 1) { *width = MS_MAX(*width, symbol->pixmap_buffer->width); *height = MS_MAX(*height, symbol->pixmap_buffer->height); } else { *width = MS_MAX(*width, (((double)size/(double)symbol->pixmap_buffer->height) * symbol->pixmap_buffer->width)); *height = MS_MAX(*height, size); } break; default: /* vector and ellipses, scalable */ if(style->size > 0) { *width = MS_MAX(*width, ((size/symbol->sizey) * symbol->sizex)); *height = MS_MAX(*height, size); } else { /* use symbol defaults */ *width = MS_MAX(*width, symbol->sizex); *height = MS_MAX(*height, symbol->sizey); } break; } return(MS_SUCCESS); }
/* ** Polygon fill. Based on "Concave Polygon Scan Conversion" by Paul ** Heckbert from "Graphics Gems", Academic Press, 1990. ** */ static void imageFilledPolygon(gdImagePtr im, shapeObj *p, int c) { typedef struct { /* a polygon edge */ double x; /* x coordinate of edge's intersection with current scanline */ double dx; /* change in x with respect to y */ int i; /* point index */ int l; /* line number */ int s; /* scanline */ } pEdge; pointObj *point1, *point2; int k, l, i, j, xl, xr, ymin, ymax, y, n,nvert, nact, m; int wrong_order; pEdge *edge, *temp; pEdge **active; int *yhist, *edgeindex; if(p->numlines == 0) return; n=0; for(i=0; i<p->numlines; i++) { n += p->line[i].numpoints; } if(n == 0) return; edge = (pEdge *) msSmallCalloc(n,sizeof(pEdge)); /* All edges in the polygon */ edgeindex = (int *) msSmallCalloc(n,sizeof(int)); /* Index to edges sorted by scanline */ active = (pEdge **) msSmallCalloc(n,sizeof(pEdge*)); /* Pointers to active edges for current scanline */ nvert=0; ymin= (int) ceil(p->line[0].point[0].y-0.5); ymax= (int) floor(p->line[0].point[0].y-0.5); /* populate the edge table */ for(l=0; l<p->numlines; l++) { for(i=0; i < p->line[l].numpoints; i++) { j = i < p->line[l].numpoints -1 ? i+1 : 0; if (p->line[l].point[i].y < p->line[l].point[j].y ) { point1 = &(p->line[l].point[i]); point2 = &(p->line[l].point[j]); } else { point2 = &(p->line[l].point[i]); point1 = &(p->line[l].point[j]); } edge[nvert].dx = point2->y == point1->y ? 0 : (point2->x - point1->x) / (point2->y - point1->y); edge[nvert].s = MS_NINT( p->line[l].point[i].y ); /* ceil( p->line[l].point[i].y - 0.5 ); */ edge[nvert].x = point1->x; edge[nvert].i = nvert; edge[nvert].l = l; ymin = MS_MIN(ymin,edge[nvert].s); ymax = MS_MAX(ymax,edge[nvert].s); nvert++; } } /* Use histogram sort to create a bucket-sorted edgeindex by scanline */ yhist = (int*) msSmallCalloc(ymax - ymin + 2, sizeof(int)); for(i=0; i<nvert; i++) { yhist[ edge[i].s - ymin + 1 ]++; } for(i=0; i<=(ymax - ymin); i++) {/* Calculate starting point in edgeindex for each scanline */ yhist[i+1] += yhist[i]; } for(i=0; i<nvert; i++) { /* Bucket sort edges into edgeindex */ y = edge[i].s; edgeindex[yhist[y-ymin]] = i; yhist[y-ymin]++; } free(yhist); k=0; nact=0; for (y=ymin; y<=ymax; y++) { /* step through scanlines */ /* scanline y is at y+.5 in continuous coordinates */ /* check vertices between previous scanline and current one, if any */ for (; k<nvert && edge[edgeindex[k]].s <= y; k++) { i = edge[edgeindex[k]].i; /* vertex previous to i */ if(i==0 || edge[i].l != edge[i-1].l) j = i + p->line[edge[i].l].numpoints - 1; else j = i - 1; if (edge[j].s <= y ) { /* old edge, remove from active list */ for (m=0; m<nact && active[m]->i!=j; m++); if (m<nact) { nact--; active[m]=active[nact]; } } else if (edge[j].s > y) { /* new edge, insert into active list */ active[nact]= & edge[j]; nact++; } /* vertex next after i */ if(i==nvert-1 || edge[i].l != edge[i+1].l) j = i - p->line[edge[i].l].numpoints + 1; else j = i + 1; if (edge[j].s <= y - 1 ) { /* old edge, remove from active list */ for (m=0; m<nact && active[m]->i!=i; m++); if (m<nact) { nact--; active[m]=active[nact]; } } else if (edge[j].s > y ) { /* new edge, insert into active list */ active[nact]= & edge[i]; nact++; } } /* Sort active edges by x */ do { wrong_order = 0; for(i=0; i < nact-1; i++) { if(active[i]->x > active[i+1]->x) { wrong_order = 1; SWAP(active[i], active[i+1], temp); } } } while(wrong_order); /* draw horizontal spans for scanline y */ for (j=0; j<nact; j+=2) { /* j -> j+1 is inside, j+1 -> j+2 is outside */ xl = (int) MS_NINT(active[j]->x ); xr = (int) (active[j+1]->x - 0.5) ; if(active[j]->x != active[j+1]->x) imageScanline(im, xl, xr, y, c); active[j]->x += active[j]->dx; /* increment edge coords */ active[j+1]->x += active[j+1]->dx; } } free(active); free(edgeindex); free(edge); }
/** * Compute a palette for the given RGBA rasterBuffer using a median cut quantization. * - rb: the rasterBuffer to quantize * - reqcolors: the desired number of colors the palette should contain. will be set * with the actual number of entries in the computed palette * - forced_palette: entries that should appear in the computed palette * - num_forced_palette_entries: number of entries contained in "force_palette". if 0, * "force_palette" can be NULL * - palette_scaling_maxval: the quantization process may have to reduce the image depth * by iteratively dividing the pixels by 2. In case palette_scaling_maxval is set to * something different than 255, the returned palette colors have to be scaled back up to * 255, and rb's pixels will have been scaled down to maxsize (see bug #3848) */ int msQuantizeRasterBuffer(rasterBufferObj *rb, unsigned int *reqcolors, rgbaPixel *palette, rgbaPixel *forced_palette, int num_forced_palette_entries, unsigned int *palette_scaling_maxval) { rgbaPixel **apixels=NULL; /* pointer to the start rows of truecolor pixels */ register rgbaPixel *pP; register int col; unsigned char newmaxval; acolorhist_vector achv, acolormap=NULL; int row; int colors; int newcolors = 0; int x; /* int channels; */ assert(rb->type == MS_BUFFER_BYTE_RGBA); *palette_scaling_maxval = 255; apixels=(rgbaPixel**)msSmallMalloc(rb->height*sizeof(rgbaPixel*)); for(row=0; row<rb->height; row++) { apixels[row]=(rgbaPixel*)(&(rb->data.rgba.pixels[row * rb->data.rgba.row_step])); } /* ** Step 2: attempt to make a histogram of the colors, unclustered. ** If at first we don't succeed, lower maxval to increase color ** coherence and try again. This will eventually terminate, with ** maxval at worst 15, since 32^3 is approximately MAXCOLORS. [GRR POSSIBLE BUG: what about 32^4 ?] */ for ( ; ; ) { achv = pam_computeacolorhist( apixels, rb->width, rb->height, MAXCOLORS, &colors ); if ( achv != (acolorhist_vector) 0 ) break; newmaxval = *palette_scaling_maxval / 2; for ( row = 0; row < rb->height; ++row ) for ( col = 0, pP = apixels[row]; col < rb->width; ++col, ++pP ) PAM_DEPTH( *pP, *pP, *palette_scaling_maxval, newmaxval ); *palette_scaling_maxval = newmaxval; } newcolors = MS_MIN(colors, *reqcolors); acolormap = mediancut(achv, colors, rb->width*rb->height, *palette_scaling_maxval, newcolors); pam_freeacolorhist(achv); *reqcolors = newcolors; for (x = 0; x < newcolors; ++x) { palette[x].r = acolormap[x].acolor.r; palette[x].g = acolormap[x].acolor.g; palette[x].b = acolormap[x].acolor.b; palette[x].a = acolormap[x].acolor.a; } free(acolormap); free(apixels); return MS_SUCCESS; }
static int msProjectRectAsPolygon(projectionObj *in, projectionObj *out, rectObj *rect) { #ifdef USE_PROJ shapeObj polygonObj; lineObj ring; // pointObj ringPoints[NUMBER_OF_SAMPLE_POINTS*4+4]; pointObj *ringPoints; int ix, iy; double dx, dy; ringPoints = (pointObj*) calloc(sizeof(pointObj),NUMBER_OF_SAMPLE_POINTS*4+4); ring.point = ringPoints; ring.numpoints = 0; msInitShape( &polygonObj ); polygonObj.type = MS_SHAPE_POLYGON; /* -------------------------------------------------------------------- */ /* Build polygon as steps around the source rectangle. */ /* -------------------------------------------------------------------- */ dx = (rect->maxx - rect->minx)/NUMBER_OF_SAMPLE_POINTS; dy = (rect->maxy - rect->miny)/NUMBER_OF_SAMPLE_POINTS; /* sample along top */ if(dx != 0) { for(ix = 0; ix <= NUMBER_OF_SAMPLE_POINTS; ix++ ) { ringPoints[ring.numpoints].x = rect->minx + ix * dx; ringPoints[ring.numpoints++].y = rect->miny; } } /* sample on along right side */ if(dy != 0) { for(iy = 1; iy <= NUMBER_OF_SAMPLE_POINTS; iy++ ) { ringPoints[ring.numpoints].x = rect->maxx; ringPoints[ring.numpoints++].y = rect->miny + iy * dy; } } /* sample along bottom */ if(dx != 0) { for(ix = NUMBER_OF_SAMPLE_POINTS-1; ix >= 0; ix-- ) { ringPoints[ring.numpoints].x = rect->minx + ix * dx; ringPoints[ring.numpoints++].y = rect->maxy; } } /* sample on along left side */ if(dy != 0) { for(iy = NUMBER_OF_SAMPLE_POINTS-1; iy >= 0; iy-- ) { ringPoints[ring.numpoints].x = rect->minx; ringPoints[ring.numpoints++].y = rect->miny + iy * dy; } } msAddLineDirectly( &polygonObj, &ring ); /* -------------------------------------------------------------------- */ /* Attempt to reproject. */ /* -------------------------------------------------------------------- */ msProjectShapeLine( in, out, &polygonObj, 0 ); /* If no points reprojected, try a grid sampling */ if( polygonObj.numlines == 0 || polygonObj.line[0].numpoints == 0 ) { msFreeShape( &polygonObj ); return msProjectRectGrid( in, out, rect ); } /* -------------------------------------------------------------------- */ /* Collect bounds. */ /* -------------------------------------------------------------------- */ rect->minx = rect->maxx = polygonObj.line[0].point[0].x; rect->miny = rect->maxy = polygonObj.line[0].point[0].y; for( ix = 1; ix < polygonObj.line[0].numpoints; ix++ ) { pointObj *pnt = polygonObj.line[0].point + ix; rect->minx = MS_MIN(rect->minx,pnt->x); rect->maxx = MS_MAX(rect->maxx,pnt->x); rect->miny = MS_MIN(rect->miny,pnt->y); rect->maxy = MS_MAX(rect->maxy,pnt->y); } msFreeShape( &polygonObj ); /* -------------------------------------------------------------------- */ /* Special case to handle reprojection from "more than the */ /* whole world" projected coordinates that sometimes produce a */ /* region greater than 360 degrees wide due to various wrapping */ /* logic. */ /* -------------------------------------------------------------------- */ if( out && pj_is_latlong(out->proj) && in && !pj_is_latlong(in->proj) && rect->maxx - rect->minx > 360.0 ) { rect->maxx = 180; rect->minx = -180; } return MS_SUCCESS; #else msSetError(MS_PROJERR, "Projection support is not available.", "msProjectRect()"); return(MS_FAILURE); #endif }
static int msContourLayerReadRaster(layerObj *layer, rectObj rect) { mapObj *map = layer->map; char **bands; char pointer[64], memDSPointer[128]; int band = 1; double adfGeoTransform[6], adfInvGeoTransform[6]; double llx, lly, urx, ury; rectObj copyRect, mapRect; int dst_xsize, dst_ysize; int virtual_grid_step_x, virtual_grid_step_y; int src_xoff, src_yoff, src_xsize, src_ysize; double map_cellsize_x, map_cellsize_y, dst_cellsize_x, dst_cellsize_y; GDALRasterBandH hBand = NULL; CPLErr eErr; contourLayerInfo *clinfo = (contourLayerInfo *) layer->layerinfo; if (layer->debug) msDebug("Entering msContourLayerReadRaster().\n"); if (clinfo == NULL || clinfo->hOrigDS == NULL) { msSetError(MS_MISCERR, "Assertion failed: Contour layer not opened!!!", "msContourLayerReadRaster()"); return MS_FAILURE; } bands = CSLTokenizeStringComplex( CSLFetchNameValue(layer->processing,"BANDS"), " ,", FALSE, FALSE ); if (CSLCount(bands) > 0) { band = atoi(bands[0]); if (band < 1 || band > GDALGetRasterCount(clinfo->hOrigDS)) { msSetError( MS_IMGERR, "BANDS PROCESSING directive includes illegal band '%d', should be from 1 to %d.", "msContourLayerReadRaster()", band, GDALGetRasterCount(clinfo->hOrigDS)); CSLDestroy(bands); return MS_FAILURE; } } CSLDestroy(bands); hBand = GDALGetRasterBand(clinfo->hOrigDS, band); if (hBand == NULL) { msSetError(MS_IMGERR, "Band %d does not exist on dataset.", "msContourLayerReadRaster()", band); return MS_FAILURE; } if (layer->projection.numargs > 0 && EQUAL(layer->projection.args[0], "auto")) { const char *wkt; wkt = GDALGetProjectionRef(clinfo->hOrigDS); if (wkt != NULL && strlen(wkt) > 0) { if (msOGCWKT2ProjectionObj(wkt, &(layer->projection), layer->debug) != MS_SUCCESS) { char msg[MESSAGELENGTH*2]; errorObj *ms_error = msGetErrorObj(); snprintf( msg, sizeof(msg), "%s\n" "PROJECTION AUTO cannot be used for this " "GDAL raster (`%s').", ms_error->message, layer->data); msg[MESSAGELENGTH-1] = '\0'; msSetError(MS_OGRERR, "%s","msDrawRasterLayer()", msg); return MS_FAILURE; } } } /* * Compute the georeferenced window of overlap, and read the source data * downsampled to match output resolution, or at full resolution if * output resolution is lower than the source resolution. * * A large portion of this overlap calculation code was borrowed from * msDrawRasterLayerGDAL(). * Would be possible to move some of this to a reusable function? * * Note: This code works only if no reprojection is involved. It would * need rework to support cases where output projection differs from source * data file projection. */ src_xsize = GDALGetRasterXSize(clinfo->hOrigDS); src_ysize = GDALGetRasterYSize(clinfo->hOrigDS); /* set the Dataset extent */ msGetGDALGeoTransform(clinfo->hOrigDS, map, layer, adfGeoTransform); clinfo->extent.minx = adfGeoTransform[0]; clinfo->extent.maxy = adfGeoTransform[3]; clinfo->extent.maxx = adfGeoTransform[0] + src_xsize * adfGeoTransform[1]; clinfo->extent.miny = adfGeoTransform[3] + src_ysize * adfGeoTransform[5]; if (layer->transform) { if (layer->debug) msDebug("msContourLayerReadRaster(): Entering transform.\n"); InvGeoTransform(adfGeoTransform, adfInvGeoTransform); mapRect = rect; map_cellsize_x = map_cellsize_y = map->cellsize; #ifdef USE_PROJ /* if necessary, project the searchrect to source coords */ if (msProjectionsDiffer( &(map->projection), &(layer->projection))) { if ( msProjectRect(&map->projection, &layer->projection, &mapRect) != MS_SUCCESS ) { msDebug("msContourLayerReadRaster(%s): unable to reproject map request rectangle into layer projection, canceling.\n", layer->name); return MS_FAILURE; } map_cellsize_x = MS_CELLSIZE(mapRect.minx, mapRect.maxx, map->width); map_cellsize_y = MS_CELLSIZE(mapRect.miny, mapRect.maxy, map->height); /* if the projection failed to project the extent requested, we need to calculate the cellsize to preserve the initial map cellsize ratio */ if ( (mapRect.minx < GEO_TRANS(adfGeoTransform,0,src_ysize)) || (mapRect.maxx > GEO_TRANS(adfGeoTransform,src_xsize,0)) || (mapRect.miny < GEO_TRANS(adfGeoTransform+3,0,src_ysize)) || (mapRect.maxy > GEO_TRANS(adfGeoTransform+3,src_xsize,0)) ) { int src_unit, dst_unit; src_unit = GetMapserverUnitUsingProj(&map->projection); dst_unit = GetMapserverUnitUsingProj(&layer->projection); if (src_unit == -1 || dst_unit == -1) { msDebug("msContourLayerReadRaster(%s): unable to reproject map request rectangle into layer projection, canceling.\n", layer->name); return MS_FAILURE; } map_cellsize_x = MS_CONVERT_UNIT(src_unit, dst_unit, MS_CELLSIZE(rect.minx, rect.maxx, map->width)); map_cellsize_y = MS_CONVERT_UNIT(src_unit, dst_unit, MS_CELLSIZE(rect.miny, rect.maxy, map->height)); } } #endif if (map_cellsize_x == 0 || map_cellsize_y == 0) { if (layer->debug) msDebug("msContourLayerReadRaster(): Cellsize can't be 0.\n"); return MS_FAILURE; } /* Adjust MapServer pixel model to GDAL pixel model */ mapRect.minx -= map_cellsize_x*0.5; mapRect.maxx += map_cellsize_x*0.5; mapRect.miny -= map_cellsize_y*0.5; mapRect.maxy += map_cellsize_y*0.5; /* * If raw data cellsize (from geotransform) is larger than output map_cellsize * then we want to extract only enough data to match the output map resolution * which means that GDAL will automatically sample the data on read. * * To prevent bad contour effects on tile edges, we adjust the target cellsize * to align the extracted window with a virtual grid based on the origin of the * raw data and a virtual grid step size corresponding to an integer sampling step. * * If source data has a greater cellsize (i.e. lower res) that requested ouptut map * then we use the raw data cellsize as target cellsize since there is no point in * interpolating the data for contours in this case. */ virtual_grid_step_x = (int)floor(map_cellsize_x / ABS(adfGeoTransform[1])); if (virtual_grid_step_x < 1) virtual_grid_step_x = 1; /* Do not interpolate data if grid sampling step < 1 */ virtual_grid_step_y = (int)floor(map_cellsize_y / ABS(adfGeoTransform[5])); if (virtual_grid_step_y < 1) virtual_grid_step_y = 1; /* Do not interpolate data if grid sampling step < 1 */ /* target cellsize is a multiple of raw data cellsize based on grid step*/ dst_cellsize_x = ABS(adfGeoTransform[1]) * virtual_grid_step_x; dst_cellsize_y = ABS(adfGeoTransform[5]) * virtual_grid_step_y; /* Compute overlap between source and target views */ copyRect = mapRect; if (copyRect.minx < GEO_TRANS(adfGeoTransform,0,src_ysize)) copyRect.minx = GEO_TRANS(adfGeoTransform,0,src_ysize); if (copyRect.maxx > GEO_TRANS(adfGeoTransform,src_xsize,0)) copyRect.maxx = GEO_TRANS(adfGeoTransform,src_xsize,0); if (copyRect.miny < GEO_TRANS(adfGeoTransform+3,0,src_ysize)) copyRect.miny = GEO_TRANS(adfGeoTransform+3,0,src_ysize); if (copyRect.maxy > GEO_TRANS(adfGeoTransform+3,src_xsize,0)) copyRect.maxy = GEO_TRANS(adfGeoTransform+3,src_xsize,0); if (copyRect.minx >= copyRect.maxx || copyRect.miny >= copyRect.maxy) { if (layer->debug) msDebug("msContourLayerReadRaster(): No overlap.\n"); return MS_SUCCESS; } /* * Convert extraction window to raster coordinates */ llx = GEO_TRANS(adfInvGeoTransform+0,copyRect.minx,copyRect.miny); lly = GEO_TRANS(adfInvGeoTransform+3,copyRect.minx,copyRect.miny); urx = GEO_TRANS(adfInvGeoTransform+0,copyRect.maxx,copyRect.maxy); ury = GEO_TRANS(adfInvGeoTransform+3,copyRect.maxx,copyRect.maxy); /* * Align extraction window with virtual grid * (keep in mind raster coordinates origin is at upper-left) * We also add an extra buffer to fix tile boundarie issues when zoomed */ llx = floor(llx / virtual_grid_step_x) * virtual_grid_step_x - (virtual_grid_step_x*5); urx = ceil(urx / virtual_grid_step_x) * virtual_grid_step_x + (virtual_grid_step_x*5); ury = floor(ury / virtual_grid_step_y) * virtual_grid_step_y - (virtual_grid_step_x*5); lly = ceil(lly / virtual_grid_step_y) * virtual_grid_step_y + (virtual_grid_step_x*5); src_xoff = MS_MAX(0,(int) floor(llx+0.5)); src_yoff = MS_MAX(0,(int) floor(ury+0.5)); src_xsize = MS_MIN(MS_MAX(0,(int) (urx - llx + 0.5)), GDALGetRasterXSize(clinfo->hOrigDS) - src_xoff); src_ysize = MS_MIN(MS_MAX(0,(int) (lly - ury + 0.5)), GDALGetRasterYSize(clinfo->hOrigDS) - src_yoff); /* Update the geographic extent (buffer added) */ /* TODO: a better way to go the geo_trans */ copyRect.minx = GEO_TRANS(adfGeoTransform+0,src_xoff,0); copyRect.maxx = GEO_TRANS(adfGeoTransform+0,src_xoff+src_xsize,0); copyRect.miny = GEO_TRANS(adfGeoTransform+3,0,src_yoff+src_ysize); copyRect.maxy = GEO_TRANS(adfGeoTransform+3,0,src_yoff); /* * If input window is to small then stop here */ if (src_xsize < 2 || src_ysize < 2) { if (layer->debug) msDebug("msContourLayerReadRaster(): input window too small, or no apparent overlap between map view and this window(1).\n"); return MS_SUCCESS; } /* Target buffer size */ dst_xsize = (int)ceil((copyRect.maxx - copyRect.minx) / dst_cellsize_x); dst_ysize = (int)ceil((copyRect.maxy - copyRect.miny) / dst_cellsize_y); if (dst_xsize == 0 || dst_ysize == 0) { if (layer->debug) msDebug("msContourLayerReadRaster(): no apparent overlap between map view and this window(2).\n"); return MS_SUCCESS; } if (layer->debug) msDebug( "msContourLayerReadRaster(): src=%d,%d,%d,%d, dst=%d,%d,%d,%d\n", src_xoff, src_yoff, src_xsize, src_ysize, 0, 0, dst_xsize, dst_ysize ); } else { src_xoff = 0; src_yoff = 0; dst_xsize = src_xsize = MS_MIN(map->width,src_xsize); dst_ysize = src_ysize = MS_MIN(map->height,src_ysize); copyRect.minx = copyRect.miny = 0; copyRect.maxx = map->width; copyRect.maxy = map->height; dst_cellsize_y = dst_cellsize_x = 1; } /* -------------------------------------------------------------------- */ /* Allocate buffer, and read data into it. */ /* -------------------------------------------------------------------- */ clinfo->buffer = (double *) malloc(sizeof(double) * dst_xsize * dst_ysize); if (clinfo->buffer == NULL) { msSetError(MS_MEMERR, "Malloc(): Out of memory.", "msContourLayerReadRaster()"); return MS_FAILURE; } eErr = GDALRasterIO(hBand, GF_Read, src_xoff, src_yoff, src_xsize, src_ysize, clinfo->buffer, dst_xsize, dst_ysize, GDT_Float64, 0, 0); if (eErr != CE_None) { msSetError( MS_IOERR, "GDALRasterIO() failed: %s", "msContourLayerReadRaster()", CPLGetLastErrorMsg() ); free(clinfo->buffer); return MS_FAILURE; } memset(pointer, 0, sizeof(pointer)); CPLPrintPointer(pointer, clinfo->buffer, sizeof(pointer)); sprintf(memDSPointer,"MEM:::DATAPOINTER=%s,PIXELS=%d,LINES=%d,BANDS=1,DATATYPE=Float64", pointer, dst_xsize, dst_ysize); clinfo->hDS = GDALOpen(memDSPointer, GA_ReadOnly); if (clinfo->hDS == NULL) { msSetError(MS_IMGERR, "Unable to open GDAL Memory dataset.", "msContourLayerReadRaster()"); free(clinfo->buffer); return MS_FAILURE; } adfGeoTransform[0] = copyRect.minx; adfGeoTransform[1] = dst_cellsize_x; adfGeoTransform[2] = 0; adfGeoTransform[3] = copyRect.maxy; adfGeoTransform[4] = 0; adfGeoTransform[5] = -dst_cellsize_y; clinfo->cellsize = MS_MAX(dst_cellsize_x, dst_cellsize_y); { char buf[64]; sprintf(buf, "%lf", clinfo->cellsize); msInsertHashTable(&layer->metadata, "__data_cellsize__", buf); } GDALSetGeoTransform(clinfo->hDS, adfGeoTransform); return MS_SUCCESS; }
int msDrawBarChart(mapObj *map, imageObj *image, pointObj *center, float *values, styleObj **styles, int numvalues, float width, float height, float *maxVal, float *minVal, float barWidth) { float upperLimit,lowerLimit; float shapeMaxVal,shapeMinVal,pixperval; int c; float vertOrigin,vertOriginClipped,horizStart,y; float left,top,bottom; /*shortcut to pixel boundaries of the chart*/ top=center->y-height/2.; bottom=center->y+height/2.; left=center->x-width/2.; shapeMaxVal=shapeMinVal=values[0]; for(c=1; c<numvalues; c++) { if(maxVal==NULL || minVal==NULL) { /*compute bounds if not specified*/ if(values[c]>shapeMaxVal) shapeMaxVal=values[c]; if(values[c]<shapeMinVal) shapeMinVal=values[c]; } } /* * use specified bounds if wanted * if not, always show the origin */ upperLimit = (maxVal!=NULL)? *maxVal : MS_MAX(shapeMaxVal,0); lowerLimit = (minVal!=NULL)? *minVal : MS_MIN(shapeMinVal,0); if(upperLimit==lowerLimit) { /* treat the case where we would have an unspecified behavior */ upperLimit+=0.5; lowerLimit-=0.5; } pixperval=(float)height/(upperLimit-lowerLimit); vertOrigin=bottom+lowerLimit*pixperval; vertOriginClipped=(vertOrigin<top) ? top : (vertOrigin>bottom) ? bottom : vertOrigin; horizStart=left; /* color = gdImageColorAllocate(image->img.gd, 0,0,0); gdImageRectangle(image->img.gd, left-1,top-1, center.x+width/2.+1,bottom+1,color); */ for(c=0; c<numvalues; c++) { int barHeight=values[c]*pixperval; /*clip bars*/ y=((vertOrigin-barHeight)<top) ? top : (vertOrigin-barHeight>bottom) ? bottom : vertOrigin-barHeight; if(y!=vertOriginClipped) { /*don't draw bars of height == 0 (i.e. either values==0, or clipped)*/ if(values[c]>0) drawRectangle(map, image, horizStart, y, horizStart+barWidth-1, vertOriginClipped, styles[c]); else drawRectangle(map,image, horizStart, vertOriginClipped, horizStart+barWidth-1 , y, styles[c]); } horizStart+=barWidth; } return MS_SUCCESS; }
/* ------------------------------------------------------------------------- */ void msDrawMarkerSymbolIM(mapObj *map, imageObj* img, pointObj *p, styleObj *style, double scalefactor) { symbolObj *symbol; int ox, oy; double size; DEBUG_IF printf("msDrawMarkerSymbolIM\n<BR>"); /* skip this, we don't do text */ if(!p) return; if(style->symbol > map->symbolset.numsymbols || style->symbol < 0) return; /* no such symbol, 0 is OK */ symbol = map->symbolset.symbol[style->symbol]; ox = style->offsetx*scalefactor; oy = style->offsety*scalefactor; if(style->size == -1) { size = msSymbolGetDefaultSize( symbol ); size = MS_NINT(size*scalefactor); } else size = MS_NINT(style->size*scalefactor); size = MS_MAX(size, style->minsize*img->resolutionfactor); size = MS_MIN(size, style->maxsize*img->resolutionfactor); if(size < 1) return; /* size too small */ /* DEBUG_IF printf(".%d.%d.%d.", symbol->type, style->symbol, fc); */ if(style->symbol == 0) { /* simply draw a single pixel of the specified color */ if (dxf) { if (dxf==2) im_iprintf (&imgStr, "POINT\n%.0f %.0f\n%d\n", p->x + ox, p->y + oy, matchdxfcolor(style->color)); else im_iprintf (&imgStr, " 0\nPOINT\n 10\n%f\n 20\n%f\n 30\n0.0\n" " 62\n%6d\n 8\n%s\n", p->x + ox, p->y + oy, matchdxfcolor(style->color), lname); } else { im_iprintf (&imgStr, "<area "); if (strcmp(symbolHrefFmt,"%.s")!=0) { im_iprintf (&imgStr, "href=\""); im_iprintf (&imgStr, symbolHrefFmt, lname); im_iprintf (&imgStr, "\" "); } if (strcmp(symbolMOverFmt,"%.s")!=0) { im_iprintf (&imgStr, "onMouseOver=\""); im_iprintf (&imgStr, symbolMOverFmt, lname); im_iprintf (&imgStr, "\" "); } if (strcmp(symbolMOutFmt,"%.s")!=0) { im_iprintf (&imgStr, "onMouseOut=\""); im_iprintf (&imgStr, symbolMOutFmt, lname); im_iprintf (&imgStr, "\" "); } im_iprintf (&imgStr, "shape=\"circle\" coords=\"%.0f,%.0f, 3\" />\n", p->x + ox, p->y + oy); } /* point2 = &( p->line[j].point[i] ); */ /* if(point1->y == point2->y) {} */ return; } DEBUG_IF printf("A"); switch(symbol->type) { case(MS_SYMBOL_TRUETYPE): DEBUG_IF printf("T"); break; case(MS_SYMBOL_PIXMAP): DEBUG_IF printf("P"); break; case(MS_SYMBOL_VECTOR): DEBUG_IF printf("V"); { double d, offset_x, offset_y; d = size/symbol->sizey; offset_x = MS_NINT(p->x - d*.5*symbol->sizex + ox); offset_y = MS_NINT(p->y - d*.5*symbol->sizey + oy); /* For now I only render filled vector symbols in imagemap, and no */ /* dxf support available yet. */ if(symbol->filled && !dxf /* && fc >= 0 */ ) { /* char *title=(p->numvalues) ? p->values[0] : ""; */ char *title = ""; int j; im_iprintf (&imgStr, "<area "); if (strcmp(symbolHrefFmt,"%.s")!=0) { im_iprintf (&imgStr, "href=\""); im_iprintf (&imgStr, symbolHrefFmt, lname); im_iprintf (&imgStr, "\" "); } if (strcmp(symbolMOverFmt,"%.s")!=0) { im_iprintf (&imgStr, "onMouseOver=\""); im_iprintf (&imgStr, symbolMOverFmt, lname); im_iprintf (&imgStr, "\" "); } if (strcmp(symbolMOutFmt,"%.s")!=0) { im_iprintf (&imgStr, "onMouseOut=\""); im_iprintf (&imgStr, symbolMOutFmt, lname); im_iprintf (&imgStr, "\" "); } im_iprintf (&imgStr, "title=\"%s\" shape=\"poly\" coords=\"", title); for(j=0; j < symbol->numpoints; j++) { im_iprintf (&imgStr, "%s %d,%d", j == 0 ? "": ",", MS_NINT(d*symbol->points[j].x + offset_x), MS_NINT(d*symbol->points[j].y + offset_y) ); } im_iprintf (&imgStr, "\" />\n"); } /* end of imagemap, filled case. */ } break; default: DEBUG_IF printf("DEF"); break; } /* end switch statement */ return; }
int msDrawLineSymbol(mapObj *map, imageObj *image, shapeObj *p, styleObj *style, double scalefactor) { int status = MS_SUCCESS; if (image) { if (MS_RENDERER_PLUGIN(image->format)) { rendererVTableObj *renderer = image->format->vtable; symbolObj *symbol; shapeObj *offsetLine = p; int i; double width; double finalscalefactor; if (p->numlines == 0) return MS_SUCCESS; if (style->symbol >= map->symbolset.numsymbols || style->symbol < 0) return MS_SUCCESS; /* no such symbol, 0 is OK */ symbol = map->symbolset.symbol[style->symbol]; /* store a reference to the renderer to be used for freeing */ symbol->renderer = renderer; width = style->width * scalefactor; width = MS_MIN(width,style->maxwidth*image->resolutionfactor); width = MS_MAX(width,style->minwidth*image->resolutionfactor); if(style->width != 0) { finalscalefactor = width / style->width; } else { finalscalefactor = 1.0; } if(style->offsety==MS_STYLE_SINGLE_SIDED_OFFSET) { offsetLine = msOffsetPolyline(p,style->offsetx * finalscalefactor ,MS_STYLE_SINGLE_SIDED_OFFSET); } else if(style->offsety==MS_STYLE_DOUBLE_SIDED_OFFSET) { offsetLine = msOffsetPolyline(p,style->offsetx * finalscalefactor ,MS_STYLE_DOUBLE_SIDED_OFFSET); } else if(style->offsetx!=0 || style->offsety!=0) { offsetLine = msOffsetPolyline(p, style->offsetx * finalscalefactor, style->offsety * finalscalefactor); } if(style->symbol == 0 || (symbol->type==MS_SYMBOL_SIMPLE)) { strokeStyleObj s; s.linecap = style->linecap; s.linejoin = style->linejoin; s.linejoinmaxsize = style->linejoinmaxsize; s.width = width; s.patternlength = style->patternlength; for(i=0; i<s.patternlength; i++) s.pattern[i] = style->pattern[i] * finalscalefactor; s.patternoffset = (style->initialgap<=0) ? 0 : (style->initialgap * finalscalefactor); if(MS_VALID_COLOR(style->color)) s.color = &style->color; else if(MS_VALID_COLOR(style->outlinecolor)) s.color = &style->outlinecolor; else { /* msSetError(MS_MISCERR,"no color defined for line styling","msDrawLineSymbol()"); * not really an error */ status = MS_SUCCESS; goto draw_line_cleanup; } status = renderer->renderLine(image,offsetLine,&s); } else { symbolStyleObj s; if(preloadSymbol(&map->symbolset, symbol, renderer) != MS_SUCCESS) { return MS_FAILURE; } INIT_SYMBOL_STYLE(s); computeSymbolStyle(&s,style,symbol,scalefactor,image->resolutionfactor); s.style = style; /* compute a markerStyle and use it on the line */ if(style->gap<0) { /* special function that treats any other symbol used as a marker, not a brush */ status = msImagePolylineMarkers(image,offsetLine,symbol,&s,-s.gap, style->initialgap * finalscalefactor, 1); } else if(style->gap>0) { status = msImagePolylineMarkers(image,offsetLine,symbol,&s,s.gap, style->initialgap * finalscalefactor,0); } else { if(renderer->renderLineTiled != NULL) { int pw,ph; imageObj* tile=NULL; if(s.scale != 1) { pw = MS_NINT(symbol->sizex * s.scale); ph = MS_NINT(symbol->sizey * s.scale); } else { pw = symbol->sizex; ph = symbol->sizey; } if(pw<1) pw=1; if(ph<1) ph=1; tile = getTile(image, symbol,&s,pw,ph,0); status = renderer->renderLineTiled(image, offsetLine, tile); } else { msSetError(MS_RENDERERERR, "renderer does not support brushed lines", "msDrawLineSymbol()"); status = MS_FAILURE; } } } draw_line_cleanup: if(offsetLine!=p) { msFreeShape(offsetLine); msFree(offsetLine); } } else if( MS_RENDERER_IMAGEMAP(image->format) ) msDrawLineSymbolIM(map, image, p, style, scalefactor); else { msSetError(MS_RENDERERERR, "unsupported renderer", "msDrawShadeSymbol()"); status = MS_FAILURE; } } else { status = MS_FAILURE; } return status; }
int msDrawShadeSymbol(mapObj *map, imageObj *image, shapeObj *p, styleObj *style, double scalefactor) { int ret = MS_SUCCESS; symbolObj *symbol; if (!p) return MS_SUCCESS; if (p->numlines <= 0) return MS_SUCCESS; if (style->symbol >= map->symbolset.numsymbols || style->symbol < 0) return MS_SUCCESS; /* no such symbol, 0 is OK */ symbol = map->symbolset.symbol[style->symbol]; /* * if only an outlinecolor was defined, and not a color, * switch to the line drawing function * * this behavior is kind of a mapfile hack, and must be * kept for backwards compatibility */ if (symbol->type != MS_SYMBOL_PIXMAP && symbol->type != MS_SYMBOL_SVG ) { if (!MS_VALID_COLOR(style->color)) { if(MS_VALID_COLOR(style->outlinecolor)) return msDrawLineSymbol(map, image, p, style, scalefactor); else { /* just do nothing if no color has been set */ return MS_SUCCESS; } } } if (image) { if (MS_RENDERER_PLUGIN(image->format)) { rendererVTableObj *renderer = image->format->vtable; shapeObj *offsetPolygon = NULL; /* store a reference to the renderer to be used for freeing */ if(style->symbol) symbol->renderer = renderer; if (style->offsetx != 0 || style->offsety != 0) { if(style->offsety==MS_STYLE_SINGLE_SIDED_OFFSET) { offsetPolygon = msOffsetPolyline(p, style->offsetx*scalefactor, MS_STYLE_SINGLE_SIDED_OFFSET); } else if(style->offsety==MS_STYLE_DOUBLE_SIDED_OFFSET) { offsetPolygon = msOffsetPolyline(p,style->offsetx * scalefactor ,MS_STYLE_DOUBLE_SIDED_OFFSET); } else { offsetPolygon = msOffsetPolyline(p, style->offsetx*scalefactor,style->offsety*scalefactor); } } else { offsetPolygon=p; } /* simple polygon drawing, without any specific symbol. * also draws an optional outline */ if(style->symbol == 0 || symbol->type == MS_SYMBOL_SIMPLE) { ret = renderer->renderPolygon(image,offsetPolygon,&style->color); if(ret != MS_SUCCESS) goto cleanup; if(MS_VALID_COLOR(style->outlinecolor)) { strokeStyleObj s; INIT_STROKE_STYLE(s); s.color = &style->outlinecolor; s.color->alpha = style->color.alpha; s.width = (style->width == 0)?scalefactor:style->width*scalefactor; s.width = MS_MIN(s.width, style->maxwidth); s.width = MS_MAX(s.width, style->minwidth); ret = renderer->renderLine(image,offsetPolygon,&s); } goto cleanup; /*finished plain polygon*/ } else if(symbol->type == MS_SYMBOL_HATCH) { double width, spacing; double pattern[MS_MAXPATTERNLENGTH]; int i; if(MS_VALID_COLOR(style->backgroundcolor)) { ret = renderer->renderPolygon(image,offsetPolygon, &style->backgroundcolor); if(ret != MS_SUCCESS) goto cleanup; } width = (style->width <= 0)?scalefactor:style->width*scalefactor; width = MS_MIN(width, style->maxwidth*image->resolutionfactor); width = MS_MAX(width, style->minwidth*image->resolutionfactor); spacing = (style->size <= 0)?scalefactor:style->size*scalefactor; spacing = MS_MIN(spacing, style->maxsize*image->resolutionfactor); spacing = MS_MAX(spacing, style->minsize*image->resolutionfactor); /* scale the pattern by the factor applied to the width */ for(i=0; i<style->patternlength; i++) { pattern[i] = style->pattern[i]*width/style->width; } ret = msHatchPolygon(image,offsetPolygon,spacing,width,pattern,style->patternlength,style->angle, &style->color); goto cleanup; } else { symbolStyleObj s; int pw,ph; imageObj *tile; int seamless = 0; if(preloadSymbol(&map->symbolset,symbol,renderer) != MS_SUCCESS) { return MS_FAILURE; } INIT_SYMBOL_STYLE(s); computeSymbolStyle(&s,style,symbol,scalefactor,image->resolutionfactor); s.style = style; if (!s.color && !s.outlinecolor && symbol->type != MS_SYMBOL_PIXMAP && symbol->type != MS_SYMBOL_SVG) { ret = MS_SUCCESS; /* nothing to do (colors are required except for PIXMAP symbols */ goto cleanup; } if(s.backgroundcolor) { ret = renderer->renderPolygon(image,offsetPolygon, s.backgroundcolor); if(ret != MS_SUCCESS) goto cleanup; } if(s.scale != 1) { if (s.gap > 0) { pw = MS_MAX(MS_NINT(s.gap),symbol->sizex * s.scale); ph = MS_MAX(MS_NINT(s.gap),symbol->sizey * s.scale); } else { pw = MS_NINT(symbol->sizex * s.scale); ph = MS_NINT(symbol->sizey * s.scale); } } else { if (s.gap > 0) { pw = MS_MAX(s.gap,symbol->sizex); ph = MS_MAX(s.gap,symbol->sizey); } else { pw = symbol->sizex; ph = symbol->sizey; } } if(pw<1) pw=1; if(ph<1) ph=1; /* if we're doing vector symbols with an antialiased pixel rendererer, we want to enable * seamless mode, i.e. comute a tile that accounts for the blending of neighbouring * tiles at the tile border */ if(symbol->type == MS_SYMBOL_VECTOR && style->gap == 0 && (image->format->renderer == MS_RENDER_WITH_AGG || image->format->renderer == MS_RENDER_WITH_CAIRO_RASTER)) { seamless = 1; } tile = getTile(image,symbol,&s,pw,ph,seamless); ret = renderer->renderPolygonTiled(image,offsetPolygon, tile); } cleanup: if (offsetPolygon != p) { msFreeShape(offsetPolygon); msFree(offsetPolygon); } return ret; } else if( MS_RENDERER_IMAGEMAP(image->format) ) msDrawShadeSymbolIM(map, image, p, style, scalefactor); } return ret; }
/* ------------------------------------------------------------------------- */ void msDrawShadeSymbolIM(mapObj *map, imageObj* img, shapeObj *p, styleObj *style, double scalefactor) { symbolObj *symbol; int i,j,l; char first = 1; double size; DEBUG_IF printf("msDrawShadeSymbolIM\n<BR>"); if(!p) return; if(p->numlines <= 0) return; symbol = map->symbolset.symbol[style->symbol]; if(style->size == -1) { size = msSymbolGetDefaultSize( symbol ); size = MS_NINT(size*scalefactor); } else size = MS_NINT(style->size*scalefactor); size = MS_MAX(size, style->minsize*img->resolutionfactor); size = MS_MIN(size, style->maxsize*img->resolutionfactor); if (suppressEmpty && p->numvalues==0) return;/* suppress area with empty title */ if(style->symbol == 0) { /* simply draw a single pixel of the specified color // */ for(l=0,j=0; j<p->numlines; j++) { if (dxf == 2) { im_iprintf (&imgStr, "POLY\n%d\n", matchdxfcolor(style->color)); } else if (dxf) { im_iprintf (&imgStr, " 0\nPOLYLINE\n 73\n 1\n 62\n%6d\n 8\n%s\n", matchdxfcolor(style->color), lname); } else { char *title=(p->numvalues) ? p->values[0] : ""; first = 1; im_iprintf (&imgStr, "<area "); if (strcmp(polyHrefFmt,"%.s")!=0) { im_iprintf (&imgStr, "href=\""); im_iprintf (&imgStr, polyHrefFmt, title); im_iprintf (&imgStr, "\" "); } if (strcmp(polyMOverFmt,"%.s")!=0) { im_iprintf (&imgStr, "onMouseOver=\""); im_iprintf (&imgStr, polyMOverFmt, title); im_iprintf (&imgStr, "\" "); } if (strcmp(polyMOutFmt,"%.s")!=0) { im_iprintf (&imgStr, "onMouseOut=\""); im_iprintf (&imgStr, polyMOutFmt, title); im_iprintf (&imgStr, "\" "); } im_iprintf (&imgStr, "title=\"%s\" shape=\"poly\" coords=\"", title); } /* point1 = &( p->line[j].point[p->line[j].numpoints-1] ); */ for(i=0; i < p->line[j].numpoints; i++,l++) { if (dxf == 2) { im_iprintf (&imgStr, "%.0f %.0f\n", p->line[j].point[i].x, p->line[j].point[i].y); } else if (dxf) { im_iprintf (&imgStr, " 0\nVERTEX\n 10\n%f\n 20\n%f\n 30\n%f\n", p->line[j].point[i].x, p->line[j].point[i].y, 0.0); } else { im_iprintf (&imgStr, "%s %.0f,%.0f", first ? "": ",", p->line[j].point[i].x, p->line[j].point[i].y); } first = 0; /* point2 = &( p->line[j].point[i] ); */ /* if(point1->y == point2->y) {} */ } im_iprintf (&imgStr, dxf ? (dxf == 2 ? "": " 0\nSEQEND\n") : "\" />\n"); } return; } /* DEBUG_IF printf ("d"); */ DEBUG_IF printf("-%d-",symbol->type); return; }
int getTruetypeTextBBoxCairo(rendererVTableObj *renderer, char **fonts, int numfonts, double size, char *text, rectObj *rect, double **advances, int bAdjustBaseline) { cairoCacheData *cache = MS_RENDERER_CACHE(renderer); faceCacheObj* face = getFontFace(cache,fonts[0]); int curfontidx = 0; char *utfptr=text; int i,unicode; unsigned long previdx=0; faceCacheObj* prevface = face; int numglyphs = msGetNumGlyphs(text); cairo_glyph_t glyph; cairo_text_extents_t extents; double px=0,py=0; if(face == NULL) { return MS_FAILURE; } cairo_set_font_face(cache->dummycr,face->face); cairo_set_font_size(cache->dummycr,size*96/72.0); if(advances != NULL) { *advances = (double*)malloc(numglyphs*sizeof(double)); } for(i=0; i<numglyphs; i++) { utfptr+=msUTF8ToUniChar(utfptr, &unicode); glyph.x=px; glyph.y=py; if(unicode=='\n') { py += ceil(size*CAIROLINESPACE); px = 0; previdx=0; continue; } if (curfontidx != 0) { face = getFontFace(cache, fonts[0]); cairo_set_font_face(cache->dummycr, face->face); curfontidx = 0; } if (face->ftface->charmap && face->ftface->charmap->encoding == FT_ENCODING_MS_SYMBOL) unicode |= 0xf000; glyph.index = FT_Get_Char_Index(face->ftface, unicode); if (glyph.index == 0) { int j; for (j = 1; j < numfonts; j++) { curfontidx = j; face = getFontFace(cache, fonts[j]); glyph.index = FT_Get_Char_Index(face->ftface, unicode); if (glyph.index != 0) { cairo_set_font_face(cache->dummycr, face->face); break; } } } if( FT_HAS_KERNING((prevface->ftface)) && previdx ) { FT_Vector delta; FT_Get_Kerning( prevface->ftface, previdx, glyph.index, FT_KERNING_DEFAULT, &delta ); px += delta.x / 64.; } cairo_glyph_extents(cache->dummycr,&glyph,1,&extents); if(i==0) { rect->minx = px+extents.x_bearing; rect->miny = py+extents.y_bearing; rect->maxx = px+extents.x_bearing+extents.width; rect->maxy = py+(bAdjustBaseline?1:(extents.y_bearing+extents.height)); } else { rect->minx = MS_MIN(rect->minx,px+extents.x_bearing); rect->miny = MS_MIN(rect->miny,py+extents.y_bearing); rect->maxy = MS_MAX(rect->maxy,py+(bAdjustBaseline?1:(extents.y_bearing+extents.height))); rect->maxx = MS_MAX(rect->maxx,px+extents.x_bearing+extents.width); } if(advances!=NULL) (*advances)[i]=extents.x_advance; px += extents.x_advance; previdx=glyph.index; prevface = face; } return MS_SUCCESS; }
/* helper functions */ int agg2GetTruetypeTextBBox(rendererVTableObj *renderer, char **fonts, int numfonts, double size, char *string, rectObj *rect, double **advances,int bAdjustBaseline) { aggRendererCache *cache = (aggRendererCache*)MS_RENDERER_CACHE(renderer); if(aggLoadFont(cache,fonts[0],size) == MS_FAILURE) return MS_FAILURE; int curfontidx = 0; int unicode, curGlyph = 1, numglyphs = 0; if (advances) { numglyphs = msGetNumGlyphs(string); } const mapserver::glyph_cache* glyph; string += msUTF8ToUniChar(string, &unicode); if(curfontidx != 0) { if(aggLoadFont(cache,fonts[0],size) == MS_FAILURE) return MS_FAILURE; curfontidx = 0; } glyph = cache->m_fman.glyph(unicode); if(!glyph || glyph->glyph_index == 0) { int i; for(i=1; i<numfonts; i++) { if(aggLoadFont(cache,fonts[i],size) == MS_FAILURE) return MS_FAILURE; curfontidx = i; glyph = cache->m_fman.glyph(unicode); if(glyph && glyph->glyph_index != 0) { break; } } } if (glyph) { rect->minx = glyph->bounds.x1; rect->maxx = glyph->bounds.x2; rect->miny = glyph->bounds.y1; rect->maxy = bAdjustBaseline?1:glyph->bounds.y2; } else return MS_FAILURE; if (advances) { *advances = (double*) malloc(numglyphs * sizeof (double)); MS_CHECK_ALLOC(*advances, numglyphs * sizeof (double), MS_FAILURE); (*advances)[0] = glyph->advance_x; } double fx = glyph->advance_x, fy = glyph->advance_y; while (*string) { if (advances) { if (*string == '\r' || *string == '\n') (*advances)[curGlyph++] = -fx; } if (*string == '\r') { fx = 0; string++; continue; } if (*string == '\n') { fx = 0; fy += ceil(size * AGG_LINESPACE); string++; continue; } string += msUTF8ToUniChar(string, &unicode); if(curfontidx != 0) { if(aggLoadFont(cache,fonts[0],size) == MS_FAILURE) return MS_FAILURE; curfontidx = 0; } glyph = cache->m_fman.glyph(unicode); if(!glyph || glyph->glyph_index == 0) { int i; for(i=1; i<numfonts; i++) { if(aggLoadFont(cache,fonts[i],size) == MS_FAILURE) return MS_FAILURE; curfontidx = i; glyph = cache->m_fman.glyph(unicode); if(glyph && glyph->glyph_index != 0) { break; } } } if (glyph) { rect->minx = MS_MIN(rect->minx, fx+glyph->bounds.x1); rect->miny = MS_MIN(rect->miny, fy+glyph->bounds.y1); rect->maxx = MS_MAX(rect->maxx, fx+glyph->bounds.x2); rect->maxy = MS_MAX(rect->maxy, fy+(bAdjustBaseline?1:glyph->bounds.y2)); fx += glyph->advance_x; fy += glyph->advance_y; if (advances) { (*advances)[curGlyph++] = glyph->advance_x; } } } return MS_SUCCESS; }
static int msProjectRectAsPolygon(projectionObj *in, projectionObj *out, rectObj *rect) { #ifdef USE_PROJ shapeObj polygonObj; lineObj ring; /* pointObj ringPoints[NUMBER_OF_SAMPLE_POINTS*4+4]; */ pointObj *ringPoints; int ix, iy; double dx, dy; /* If projecting from longlat to projected */ if( out && !pj_is_latlong(out->proj) && in && pj_is_latlong(in->proj) && fabs(rect->minx - -180.0) < 1e-5 && fabs(rect->miny - -90.0) < 1e-5 && fabs(rect->maxx - 180.0) < 1e-5 && fabs(rect->maxy - 90.0) < 1e-5) { rect->minx = -1e15; rect->miny = -1e15; rect->maxx = 1e15; rect->maxy = 1e15; return MS_SUCCESS; } /* -------------------------------------------------------------------- */ /* Build polygon as steps around the source rectangle. */ /* -------------------------------------------------------------------- */ dx = (rect->maxx - rect->minx)/NUMBER_OF_SAMPLE_POINTS; dy = (rect->maxy - rect->miny)/NUMBER_OF_SAMPLE_POINTS; if(dx==0 && dy==0) { pointObj foo; msDebug( "msProjectRect(): Warning: degenerate rect {%f,%f,%f,%f}\n",rect->minx,rect->miny,rect->minx,rect->miny ); foo.x = rect->minx; foo.y = rect->miny; msProjectPoint(in,out,&foo); rect->minx=rect->maxx=foo.x; rect->miny=rect->maxy=foo.y; return MS_SUCCESS; } ringPoints = (pointObj*) calloc(sizeof(pointObj),NUMBER_OF_SAMPLE_POINTS*4+4); ring.point = ringPoints; ring.numpoints = 0; msInitShape( &polygonObj ); polygonObj.type = MS_SHAPE_POLYGON; /* sample along top */ if(dx != 0) { for(ix = 0; ix <= NUMBER_OF_SAMPLE_POINTS; ix++ ) { ringPoints[ring.numpoints].x = rect->minx + ix * dx; ringPoints[ring.numpoints++].y = rect->miny; } } /* sample on along right side */ if(dy != 0) { for(iy = 1; iy <= NUMBER_OF_SAMPLE_POINTS; iy++ ) { ringPoints[ring.numpoints].x = rect->maxx; ringPoints[ring.numpoints++].y = rect->miny + iy * dy; } } /* sample along bottom */ if(dx != 0) { for(ix = NUMBER_OF_SAMPLE_POINTS-1; ix >= 0; ix-- ) { ringPoints[ring.numpoints].x = rect->minx + ix * dx; ringPoints[ring.numpoints++].y = rect->maxy; } } /* sample on along left side */ if(dy != 0) { for(iy = NUMBER_OF_SAMPLE_POINTS-1; iy >= 0; iy-- ) { ringPoints[ring.numpoints].x = rect->minx; ringPoints[ring.numpoints++].y = rect->miny + iy * dy; } } msAddLineDirectly( &polygonObj, &ring ); #ifdef notdef FILE *wkt = fopen("/tmp/www-before.wkt","w"); char *tmp = msShapeToWKT(&polygonObj); fprintf(wkt,"%s\n", tmp); free(tmp); fclose(wkt); #endif /* -------------------------------------------------------------------- */ /* Attempt to reproject. */ /* -------------------------------------------------------------------- */ msProjectShapeLine( in, out, &polygonObj, 0 ); /* If no points reprojected, try a grid sampling */ if( polygonObj.numlines == 0 || polygonObj.line[0].numpoints == 0 ) { msFreeShape( &polygonObj ); return msProjectRectGrid( in, out, rect ); } #ifdef notdef wkt = fopen("/tmp/www-after.wkt","w"); tmp = msShapeToWKT(&polygonObj); fprintf(wkt,"%s\n", tmp); free(tmp); fclose(wkt); #endif /* -------------------------------------------------------------------- */ /* Collect bounds. */ /* -------------------------------------------------------------------- */ rect->minx = rect->maxx = polygonObj.line[0].point[0].x; rect->miny = rect->maxy = polygonObj.line[0].point[0].y; for( ix = 1; ix < polygonObj.line[0].numpoints; ix++ ) { pointObj *pnt = polygonObj.line[0].point + ix; rect->minx = MS_MIN(rect->minx,pnt->x); rect->maxx = MS_MAX(rect->maxx,pnt->x); rect->miny = MS_MIN(rect->miny,pnt->y); rect->maxy = MS_MAX(rect->maxy,pnt->y); } msFreeShape( &polygonObj ); /* -------------------------------------------------------------------- */ /* Special case to handle reprojection from "more than the */ /* whole world" projected coordinates that sometimes produce a */ /* region greater than 360 degrees wide due to various wrapping */ /* logic. */ /* -------------------------------------------------------------------- */ if( out && pj_is_latlong(out->proj) && in && !pj_is_latlong(in->proj) && rect->maxx - rect->minx > 360.0 ) { rect->maxx = 180; rect->minx = -180; } return MS_SUCCESS; #else msSetError(MS_PROJERR, "Projection support is not available.", "msProjectRect()"); return(MS_FAILURE); #endif }
int getTruetypeTextBBoxCairo(rendererVTableObj *renderer, char *font, double size, char *text, rectObj *rect, double **advances) { cairoCacheData *cache = MS_RENDERER_CACHE(renderer); faceCacheObj *face = getFontFace(cache,font); char *utfptr=text; int i,has_kerning,unicode; unsigned long previdx=0; int numglyphs = msGetNumGlyphs(text); cairo_glyph_t glyph; cairo_text_extents_t extents; double px=0,py=0; if(face == NULL) { return MS_FAILURE; } cairo_set_font_face(cache->dummycr,face->face); cairo_set_font_size(cache->dummycr,size*96/72.0); has_kerning = FT_HAS_KERNING((face->ftface)); if(advances != NULL) { *advances = (double*)malloc(numglyphs*sizeof(double)); } for(i=0;i<numglyphs;i++) { utfptr+=msUTF8ToUniChar(utfptr, &unicode); glyph.x=px; glyph.y=py; if(unicode=='\n') { py += ceil(size*CAIROLINESPACE); px = 0; previdx=0; continue; } glyph.index = FT_Get_Char_Index(face->ftface, unicode); if( has_kerning && previdx ) { FT_Vector delta; FT_Get_Kerning( face->ftface, previdx, glyph.index, FT_KERNING_DEFAULT, &delta ); px += delta.x / 64.; } cairo_glyph_extents(cache->dummycr,&glyph,1,&extents); if(i==0) { rect->minx = px+extents.x_bearing; rect->miny = py+extents.y_bearing; rect->maxx = px+extents.x_bearing+extents.width; rect->maxy = py+extents.y_bearing+extents.height; } else { rect->minx = MS_MIN(rect->minx,px+extents.x_bearing); rect->miny = MS_MIN(rect->miny,py+extents.y_bearing); rect->maxy = MS_MAX(rect->maxy,py+extents.y_bearing+extents.height); rect->maxx = MS_MAX(rect->maxx,px+extents.x_bearing+extents.width); } if(advances!=NULL) (*advances)[i]=extents.x_advance; px += extents.x_advance; previdx=glyph.index; } /* rect->minx = 0; rect->miny = 0; rect->maxx = 1; rect->maxy = 1; */ return MS_SUCCESS; }