通过修改osgearth自带的agglite插件,实现矢量描边效果,可以自定义描边的颜色和宽度(单位像素)
测试文件osgearth_features.cpp
#include <osg/Notify> #include <osgGA/StateSetManipulator> #include <osgViewer/Viewer> #include <osgViewer/ViewerEventHandlers> #include <osgEarth/Map> #include <osgEarth/MapNode> #include <osgEarthUtil/ExampleResources> #include <osgEarthUtil/EarthManipulator> #include <osgEarthUtil/AutoClipPlaneHandler> #include <osgEarthSymbology/Style> #include <osgEarthFeatures/ConvertTypeFilter> #include <osgEarthDrivers/gdal/GDALOptions> #include <osgEarthDrivers/feature_ogr/OGRFeatureOptions> #include "AGGLiteOptions2" #include <osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions> #include <osgDB/WriteFile> #ifdef _DEBUG #pragma comment(lib, "osgd.lib") #pragma comment(lib, "osgDBd.lib") #pragma comment(lib, "osgViewerd.lib") #pragma comment(lib, "osgGAd.lib") #pragma comment(lib, "osgEarthd.lib") #pragma comment(lib, "osgEarthSymbologyd.lib") #pragma comment(lib, "osgEarthUtild.lib") #pragma comment(lib, "osgEarthFeaturesd.lib") #else #pragma comment(lib, "osg.lib") #pragma comment(lib, "osgDB.lib") #pragma comment(lib, "osgViewer.lib") #pragma comment(lib, "osgGA.lib") #pragma comment(lib, "osgEarth.lib") #pragma comment(lib, "osgEarthSymbology.lib") #pragma comment(lib, "osgEarthUtil.lib") #pragma comment(lib, "osgEarthFeatures.lib") #endif // DEBUG using namespace osgEarth; using namespace osgEarth::Features; using namespace osgEarth::Drivers; using namespace osgEarth::Symbology; using namespace osgEarth::Util; // // NOTE: run this sample from the repo/tests directory. // int main(int argc, char** argv) { osg::ArgumentParser arguments(&argc, argv); osgViewer::Viewer viewer(arguments); // Start by creating the map: Map* map = new Map(); // Start with a basemap imagery layer; we'll be using the GDAL driver // to load a local GeoTIFF file: GDALOptions basemapOpt; basemapOpt.url() = "world.tif"; map->addImageLayer(new ImageLayer(ImageLayerOptions("basemap", basemapOpt))); // Next we add a feature layer. OGRFeatureOptions featureOptions; // Configures the feature driver to load the vectors from a shapefile: featureOptions.url() = "world.shp"; // That's it, the map is ready; now create a MapNode to render the Map: MapNodeOptions mapNodeOptions; mapNodeOptions.enableLighting() = false; MapNode* mapNode = new MapNode(map, mapNodeOptions); osg::Group* root = new osg::Group(); root->addChild(mapNode); viewer.setSceneData(root); viewer.setCameraManipulator(new EarthManipulator()); // Process cmdline args MapNodeHelper().parse(mapNode, arguments, &viewer, root, new LabelControl("Features Demo")); // Define a style for the feature data. Since we are going to render the // vectors as lines, configure the line symbolizer: Style style; LineSymbol* ls = style.getOrCreateSymbol<LineSymbol>(); ls->stroke()->color() = Color::Yellow; ls->stroke()->width() = 6.0f; ls->stroke()->lineCap() = osgEarth::Symbology::Stroke::LINECAP_ROUND;//使连接处圆滑一些 AGGLiteOptions2 rasterOptions; rasterOptions.featureOptions() = featureOptions; rasterOptions.styles() = new StyleSheet(); rasterOptions.styles()->addStyle(style); rasterOptions.borderColor() = Color::Red;//包边颜色 rasterOptions.borderWidth() = 3.0f;//包边宽度(单位像素) map->addImageLayer(new ImageLayer("my features", rasterOptions)); // add some stock OSG handlers: viewer.addEventHandler(new osgViewer::StatsHandler()); viewer.addEventHandler(new osgViewer::WindowSizeHandler()); viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); viewer.setUpViewInWindow(100, 500, 800, 600); return viewer.run(); }
头文件AGGLiteOptions2
#ifndef OSGEARTH_DRIVER_AGGLITE2_DRIVEROPTIONS #define OSGEARTH_DRIVER_AGGLITE2_DRIVEROPTIONS 1 #include <osgEarth/Common> #include <osgEarthFeatures/FeatureTileSource> namespace osgEarth { namespace Drivers { using namespace osgEarth; using namespace osgEarth::Features; class AGGLiteOptions2 : public FeatureTileSourceOptions // NO EXPORT; header only { public: /** * Whether to downsample line features to that they are no higher resolution than * the target image resolution. Defaults to true, but you can disable this (for a possible * performance increase) if you know your data to be of a relatively low resolution. * (Default = true) */ optional<bool>& optimizeLineSampling() { return _optimizeLineSampling; } const optional<bool>& optimizeLineSampling() const { return _optimizeLineSampling; } /** * Set the gamma parameter applied on the AggLite rasterizer : allow to control geometry antialiasing * (Default = 1.3) */ optional<double>& gamma() { return _gamma; } const optional<double>& gamma() const { return _gamma; } //w.g.描边的颜色和宽度(单位像素) /** Border color. */ Color& borderColor() { return _borderColor; } const Color& borderColor() const { return _borderColor; } /** Line border rendering width. */ optional<double>& borderWidth() { return _borderWidth; } const optional<double>& borderWidth() const { return _borderWidth; } public: AGGLiteOptions2( const TileSourceOptions& options =TileSourceOptions() ) : FeatureTileSourceOptions( options ), _optimizeLineSampling ( true ), _gamma ( 1.3 ), _borderColor(Color::White), _borderWidth(1) { setDriver( "agglite2" ); fromConfig( _conf ); } /** dtor */ virtual ~AGGLiteOptions2() { } public: Config getConfig() const { Config conf = FeatureTileSourceOptions::getConfig(); conf.updateIfSet("optimize_line_sampling", _optimizeLineSampling); conf.updateIfSet("gamma", _gamma ); conf.add("borderColor", _borderColor.toHTML()); conf.updateIfSet("borderWidth", _borderWidth); return conf; } protected: void mergeConfig( const Config& conf ) { FeatureTileSourceOptions::mergeConfig( conf ); fromConfig(conf); } private: void fromConfig( const Config& conf ) { conf.getIfSet( "optimize_line_sampling", _optimizeLineSampling ); conf.getIfSet( "gamma", _gamma ); _borderColor = Color(conf.value("borderColor")); conf.getIfSet("borderWidth", _borderWidth); } optional<bool> _optimizeLineSampling; optional<double> _gamma; //w.g.描边的颜色和宽度(单位像素) Color _borderColor; optional<double> _borderWidth; }; } } // namespace osgEarth::Drivers #endif // OSGEARTH_DRIVER_AGGLITE2_DRIVEROPTIONS
插件文件AGGLiteRasterizerTileSource2.cpp
#include <osgEarthFeatures/FeatureTileSource> #include <osgEarthFeatures/ResampleFilter> #include <osgEarthFeatures/TransformFilter> #include <osgEarthFeatures/BufferFilter> #include <osgEarthSymbology/Style> //TODO: replace this with GeometryRasterizer #include <osgEarthSymbology/AGG.h> #include <osgEarth/Registry> #include <osgEarth/FileUtils> #include <osgEarth/ImageUtils> #include <osg/Notify> #include <osgDB/FileNameUtils> #include <osgDB/FileUtils> #include <osgDB/Registry> #include <osgDB/ReadFile> #include <osgDB/WriteFile> #include "AGGLiteOptions2" #include <sstream> #include <OpenThreads/Mutex> #include <OpenThreads/ScopedLock> #define LC "[AGGLite2] " using namespace osgEarth; using namespace osgEarth::Features; using namespace osgEarth::Symbology; using namespace osgEarth::Drivers; using namespace OpenThreads; namespace { struct float32 { float32() : value(NO_DATA_VALUE) { } float32(float v) : value(v) { } float value; }; struct span_coverage32 { static void render(unsigned char* ptr, int x, unsigned count, const unsigned char* covers, const float32& c) { unsigned char* p = ptr + (x << 2); float* f = (float*)p; do { unsigned char cover = *covers++; int hasData = cover > 127; *f++ = hasData ? c.value : NO_DATA_VALUE; } while(--count); } static void hline(unsigned char* ptr, int x, unsigned count, const float32& c) { unsigned char* p = ptr + (x << 2); float* f = (float*)p; do { *f++ = c.value; } while(--count); } static float32 get(unsigned char* ptr, int x) { unsigned char* p = ptr + (x << 2); float* f = (float*)p; return float32(*f); } }; } /********************************************************************/ class AGGLiteRasterizerTileSource2 : public FeatureTileSource { public: struct RenderFrame { double xmin, ymin; double xf, yf; }; public: AGGLiteRasterizerTileSource2( const TileSourceOptions& options ) : FeatureTileSource( options ), _options( options ) { //nop } //override osg::Image* allocateImage() { osg::Image* image = 0L; if ( _options.coverage() == true ) { image = new osg::Image(); image->allocateImage(getPixelsPerTile(), getPixelsPerTile(), 1, GL_LUMINANCE, GL_FLOAT); image->setInternalTextureFormat(GL_LUMINANCE32F_ARB); ImageUtils::markAsUnNormalized(image, true); } return image; } //override bool preProcess(osg::Image* image, osg::Referenced* buildData) { agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 ); // clear the buffer. if ( _options.coverage() == true ) { // For coverage data, FLT_MAX = no data. agg::renderer<span_coverage32, float32> ren(rbuf); ren.clear( float32(NO_DATA_VALUE) ); } else { agg::renderer<agg::span_abgr32, agg::rgba8> ren(rbuf); ren.clear(agg::rgba8(0,0,0,0)); } return true; } //override bool renderFeaturesForStyle( Session* session, const Style& style, const FeatureList& features, osg::Referenced* buildData, const GeoExtent& imageExtent, osg::Image* image ) { //w.g.先绘制描边 renderFeaturesForStyle2(session, style, features, buildData, imageExtent, image); OE_DEBUG << LC << "Rendering " << features.size() << " features for " << imageExtent.toString() << " "; // A processing context to use with the filters: FilterContext context( session ); context.setProfile( getFeatureSource()->getFeatureProfile() ); const LineSymbol* masterLine = style.getSymbol<LineSymbol>(); const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>(); const CoverageSymbol* masterCov = style.getSymbol<CoverageSymbol>(); // sort into bins, making a copy for lines that require buffering. FeatureList polygons; FeatureList lines; for(FeatureList::const_iterator f = features.begin(); f != features.end(); ++f) { if ( f->get()->getGeometry() ) { bool hasPoly = false; bool hasLine = false; if ( masterPoly || f->get()->style()->has<PolygonSymbol>() ) { polygons.push_back( f->get() ); hasPoly = true; } if ( masterLine || f->get()->style()->has<LineSymbol>() ) { Feature* newFeature = new Feature( *f->get() ); if ( !newFeature->getGeometry()->isLinear() ) { newFeature->setGeometry( newFeature->getGeometry()->cloneAs(Geometry::TYPE_RING) ); } lines.push_back( newFeature ); hasLine = true; } // if there are no geometry symbols but there is a coverage symbol, default to polygons. if ( !hasLine && !hasPoly ) { if ( masterCov || f->get()->style()->has<CoverageSymbol>() ) { polygons.push_back( f->get() ); } } } } // initialize: RenderFrame frame; frame.xmin = imageExtent.xMin(); frame.ymin = imageExtent.yMin(); frame.xf = (double)image->s() / imageExtent.width(); frame.yf = (double)image->t() / imageExtent.height(); if ( lines.size() > 0 ) { // We are buffering in the features native extent, so we need to use the // transformed extent to get the proper "resolution" for the image const SpatialReference* featureSRS = context.profile()->getSRS(); GeoExtent transformedExtent = imageExtent.transform(featureSRS); double trans_xf = (double)image->s() / transformedExtent.width(); double trans_yf = (double)image->t() / transformedExtent.height(); // resolution of the image (pixel extents): double xres = 1.0/trans_xf; double yres = 1.0/trans_yf; // downsample the line data so that it is no higher resolution than to image to which // we intend to rasterize it. If you don't do this, you run the risk of the buffer // operation taking forever on very high-res input data. if ( _options.optimizeLineSampling() == true ) { ResampleFilter resample; resample.minLength() = osg::minimum( xres, yres ); context = resample.push( lines, context ); } // now run the buffer operation on all lines: BufferFilter buffer; double lineWidth = 1.0; if ( masterLine ) { buffer.capStyle() = masterLine->stroke()->lineCap().value(); if ( masterLine->stroke()->width().isSet() ) { lineWidth = masterLine->stroke()->width().value(); GeoExtent imageExtentInFeatureSRS = imageExtent.transform(featureSRS); double pixelWidth = imageExtentInFeatureSRS.width() / (double)image->s(); // if the width units are specified, process them: if (masterLine->stroke()->widthUnits().isSet() && masterLine->stroke()->widthUnits().get() != Units::PIXELS) { const Units& featureUnits = featureSRS->getUnits(); const Units& strokeUnits = masterLine->stroke()->widthUnits().value(); // if the units are different than those of the feature data, we need to // do a units conversion. if ( featureUnits != strokeUnits ) { if ( Units::canConvert(strokeUnits, featureUnits) ) { // linear to linear, no problem lineWidth = strokeUnits.convertTo( featureUnits, lineWidth ); } else if ( strokeUnits.isLinear() && featureUnits.isAngular() ) { // linear to angular? approximate degrees per meter at the // latitude of the tile's centroid. double lineWidthM = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth); double mPerDegAtEquatorInv = 360.0/(featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI); double lon, lat; imageExtent.getCentroid(lon, lat); lineWidth = lineWidthM * mPerDegAtEquatorInv * cos(osg::DegreesToRadians(lat)); } } // enfore a minimum width of one pixel. float minPixels = masterLine->stroke()->minPixels().getOrUse( 1.0f ); lineWidth = osg::clampAbove(lineWidth, pixelWidth*minPixels); } else // pixels { lineWidth *= pixelWidth; } } } buffer.distance() = lineWidth * 0.5; // since the distance is for one side buffer.push( lines, context ); } // Transform the features into the map's SRS: TransformFilter xform( imageExtent.getSRS() ); xform.setLocalizeCoordinates( false ); FilterContext polysContext = xform.push( polygons, context ); FilterContext linesContext = xform.push( lines, context ); // set up the AGG renderer: agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 ); // Create the renderer and the rasterizer agg::rasterizer ras; // Setup the rasterizer if ( _options.coverage() == true ) ras.gamma(1.0); else ras.gamma(_options.gamma().get()); ras.filling_rule(agg::fill_even_odd); // construct an extent for cropping the geometry to our tile. // extend just outside the actual extents so we don't get edge artifacts: GeoExtent cropExtent = GeoExtent(imageExtent); cropExtent.scale(1.1, 1.1); osg::ref_ptr<Symbology::Polygon> cropPoly = new Symbology::Polygon( 4 ); cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMin(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMin(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMax(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMax(), 0 )); // If there's a coverage symbol, make a copy of the expressions so we can evaluate them optional<NumericExpression> covValue; const CoverageSymbol* covsym = style.get<CoverageSymbol>(); if (covsym && covsym->valueExpression().isSet()) covValue = covsym->valueExpression().get(); // render the polygons for(FeatureList::iterator i = polygons.begin(); i != polygons.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if ( geometry->crop( cropPoly.get(), croppedGeometry ) ) { const PolygonSymbol* poly = feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() : masterPoly; if ( _options.coverage() == true && covValue.isSet() ) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { osg::Vec4f color = poly->fill()->color(); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } // render the lines for(FeatureList::iterator i = lines.begin(); i != lines.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if ( geometry->crop( cropPoly.get(), croppedGeometry ) ) { const LineSymbol* line = feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() : masterLine; if ( _options.coverage() == true && covValue.isSet() ) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { osg::Vec4f color = line ? static_cast<osg::Vec4>(line->stroke()->color()) : osg::Vec4(1,1,1,1); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } return true; } //w.g.描边 bool renderFeaturesForStyle2( Session* session, const Style& style, const FeatureList& features, osg::Referenced* buildData, const GeoExtent& imageExtent, osg::Image* image) { OE_DEBUG << LC << "Rendering " << features.size() << " features for " << imageExtent.toString() << " "; // A processing context to use with the filters: FilterContext context(session); context.setProfile(getFeatureSource()->getFeatureProfile()); const LineSymbol* masterLine = style.getSymbol<LineSymbol>(); const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>(); const CoverageSymbol* masterCov = style.getSymbol<CoverageSymbol>(); // sort into bins, making a copy for lines that require buffering. FeatureList polygons; FeatureList lines; for (FeatureList::const_iterator f = features.begin(); f != features.end(); ++f) { if (f->get()->getGeometry()) { bool hasPoly = false; bool hasLine = false; if (masterPoly || f->get()->style()->has<PolygonSymbol>()) { polygons.push_back(f->get()); hasPoly = true; } if (masterLine || f->get()->style()->has<LineSymbol>()) { Feature* newFeature = new Feature(*f->get()); if (!newFeature->getGeometry()->isLinear()) { newFeature->setGeometry(newFeature->getGeometry()->cloneAs(Geometry::TYPE_RING)); } lines.push_back(newFeature); hasLine = true; } // if there are no geometry symbols but there is a coverage symbol, default to polygons. if (!hasLine && !hasPoly) { if (masterCov || f->get()->style()->has<CoverageSymbol>()) { polygons.push_back(f->get()); } } } } // initialize: RenderFrame frame; frame.xmin = imageExtent.xMin(); frame.ymin = imageExtent.yMin(); frame.xf = (double)image->s() / imageExtent.width(); frame.yf = (double)image->t() / imageExtent.height(); if (lines.size() > 0) { // We are buffering in the features native extent, so we need to use the // transformed extent to get the proper "resolution" for the image const SpatialReference* featureSRS = context.profile()->getSRS(); GeoExtent transformedExtent = imageExtent.transform(featureSRS); double trans_xf = (double)image->s() / transformedExtent.width(); double trans_yf = (double)image->t() / transformedExtent.height(); // resolution of the image (pixel extents): double xres = 1.0 / trans_xf; double yres = 1.0 / trans_yf; // downsample the line data so that it is no higher resolution than to image to which // we intend to rasterize it. If you don't do this, you run the risk of the buffer // operation taking forever on very high-res input data. if (_options.optimizeLineSampling() == true) { ResampleFilter resample; resample.minLength() = osg::minimum(xres, yres); context = resample.push(lines, context); } // now run the buffer operation on all lines: BufferFilter buffer; double lineWidth = 1.0; if (masterLine) { buffer.capStyle() = masterLine->stroke()->lineCap().value(); if (masterLine->stroke()->width().isSet()) { //w.g.描边的宽度(单位像素) lineWidth = masterLine->stroke()->width().value()+_options.borderWidth().value()*2; GeoExtent imageExtentInFeatureSRS = imageExtent.transform(featureSRS); double pixelWidth = imageExtentInFeatureSRS.width() / (double)image->s(); // if the width units are specified, process them: if (masterLine->stroke()->widthUnits().isSet() && masterLine->stroke()->widthUnits().get() != Units::PIXELS) { const Units& featureUnits = featureSRS->getUnits(); const Units& strokeUnits = masterLine->stroke()->widthUnits().value(); // if the units are different than those of the feature data, we need to // do a units conversion. if (featureUnits != strokeUnits) { if (Units::canConvert(strokeUnits, featureUnits)) { // linear to linear, no problem lineWidth = strokeUnits.convertTo(featureUnits, lineWidth); } else if (strokeUnits.isLinear() && featureUnits.isAngular()) { // linear to angular? approximate degrees per meter at the // latitude of the tile's centroid. double lineWidthM = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth); double mPerDegAtEquatorInv = 360.0 / (featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI); double lon, lat; imageExtent.getCentroid(lon, lat); lineWidth = lineWidthM * mPerDegAtEquatorInv * cos(osg::DegreesToRadians(lat)); } } // enfore a minimum width of one pixel. float minPixels = masterLine->stroke()->minPixels().getOrUse(1.0f); lineWidth = osg::clampAbove(lineWidth, pixelWidth*minPixels); } else // pixels { lineWidth *= pixelWidth; } } } buffer.distance() = lineWidth*0.5; buffer.push(lines, context); } // Transform the features into the map's SRS: TransformFilter xform(imageExtent.getSRS()); xform.setLocalizeCoordinates(false); FilterContext polysContext = xform.push(polygons, context); FilterContext linesContext = xform.push(lines, context); // set up the AGG renderer: agg::rendering_buffer rbuf(image->data(), image->s(), image->t(), image->s() * 4); // Create the renderer and the rasterizer agg::rasterizer ras; // Setup the rasterizer if (_options.coverage() == true) ras.gamma(1.0); else ras.gamma(_options.gamma().get()); ras.filling_rule(agg::fill_even_odd); // construct an extent for cropping the geometry to our tile. // extend just outside the actual extents so we don't get edge artifacts: GeoExtent cropExtent = GeoExtent(imageExtent); cropExtent.scale(1.1, 1.1); osg::ref_ptr<Symbology::Polygon> cropPoly = new Symbology::Polygon(4); cropPoly->push_back(osg::Vec3d(cropExtent.xMin(), cropExtent.yMin(), 0)); cropPoly->push_back(osg::Vec3d(cropExtent.xMax(), cropExtent.yMin(), 0)); cropPoly->push_back(osg::Vec3d(cropExtent.xMax(), cropExtent.yMax(), 0)); cropPoly->push_back(osg::Vec3d(cropExtent.xMin(), cropExtent.yMax(), 0)); // If there's a coverage symbol, make a copy of the expressions so we can evaluate them optional<NumericExpression> covValue; const CoverageSymbol* covsym = style.get<CoverageSymbol>(); if (covsym && covsym->valueExpression().isSet()) covValue = covsym->valueExpression().get(); // render the polygons for (FeatureList::iterator i = polygons.begin(); i != polygons.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if (geometry->crop(cropPoly.get(), croppedGeometry)) { const PolygonSymbol* poly = feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() : masterPoly; if (_options.coverage() == true && covValue.isSet()) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { osg::Vec4f color = poly->fill()->color(); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } // render the lines for (FeatureList::iterator i = lines.begin(); i != lines.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if (geometry->crop(cropPoly.get(), croppedGeometry)) { const LineSymbol* line = feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() : masterLine; if (_options.coverage() == true && covValue.isSet()) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { //w.g.描边的颜色 osg::Vec4f color = static_cast<osg::Vec4>(_options.borderColor()); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } return true; } //override bool postProcess( osg::Image* image, osg::Referenced* data ) { if ( _options.coverage() == false ) { //convert from ABGR to RGBA unsigned char* pixel = image->data(); for(int i=0; i<image->s()*image->t()*4; i+=4, pixel+=4) { std::swap( pixel[0], pixel[3] ); std::swap( pixel[1], pixel[2] ); } } return true; } // rasterizes a geometry to color void rasterize(const Geometry* geometry, const osg::Vec4& color, RenderFrame& frame, agg::rasterizer& ras, agg::rendering_buffer& buffer) { unsigned a = (unsigned)(127.0f+(color.a()*255.0f)/2.0f); // scale alpha up agg::rgba8 fgColor = agg::rgba8( (unsigned)(color.r()*255.0f), (unsigned)(color.g()*255.0f), (unsigned)(color.b()*255.0f), a ); ConstGeometryIterator gi( geometry ); while( gi.hasMore() ) { const Geometry* g = gi.next(); for( Geometry::const_iterator p = g->begin(); p != g->end(); p++ ) { const osg::Vec3d& p0 = *p; double x0 = frame.xf*(p0.x()-frame.xmin); double y0 = frame.yf*(p0.y()-frame.ymin); if ( p == g->begin() ) ras.move_to_d( x0, y0 ); else { ras.line_to_d(x0, y0); } } } agg::renderer<agg::span_abgr32, agg::rgba8> ren(buffer); ras.render(ren, fgColor); ras.reset(); } void rasterizeCoverage(const Geometry* geometry, float value, RenderFrame& frame, agg::rasterizer& ras, agg::rendering_buffer& buffer) { ConstGeometryIterator gi( geometry ); while( gi.hasMore() ) { const Geometry* g = gi.next(); for( Geometry::const_iterator p = g->begin(); p != g->end(); p++ ) { const osg::Vec3d& p0 = *p; double x0 = frame.xf*(p0.x()-frame.xmin); double y0 = frame.yf*(p0.y()-frame.ymin); if ( p == g->begin() ) ras.move_to_d( x0, y0 ); else ras.line_to_d( x0, y0 ); } } agg::renderer<span_coverage32, float32> ren(buffer); ras.render(ren, value); ras.reset(); } virtual std::string getExtension() const { return "png"; } private: const AGGLiteOptions2 _options; std::string _configPath; }; /** * Plugin entry point for the AGGLite2 feature rasterizer */ class AGGLiteRasterizerTileSourceDriver2 : public TileSourceDriver { public: AGGLiteRasterizerTileSourceDriver2() {} virtual const char* className() const { return "AGG-Lite2 feature rasterizer"; } virtual bool acceptsExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive( extension, "osgearth_agglite2" ) || osgDB::equalCaseInsensitive( extension, "osgearth_rasterize2" ); } virtual ReadResult readObject(const std::string& file_name, const Options* options) const { std::string ext = osgDB::getFileExtension( file_name ); if ( !acceptsExtension( ext ) ) { return ReadResult::FILE_NOT_HANDLED; } return new AGGLiteRasterizerTileSource2( getTileSourceOptions(options) ); } }; REGISTER_OSGPLUGIN(osgearth_agglite2, AGGLiteRasterizerTileSourceDriver2)