[mapguide-commits] r10259 - in branches/4.0/MgDev: Common/Renderers Server/src/UnitTesting
svn_mapguide at osgeo.org
svn_mapguide at osgeo.org
Tue Nov 3 03:44:16 PST 2026
Author: jng
Date: 2026-11-03 03:44:14 -0800 (Tue, 03 Nov 2026)
New Revision: 10259
Modified:
branches/4.0/MgDev/Common/Renderers/AGGRenderer.cpp
branches/4.0/MgDev/Common/Renderers/agg_context.h
branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp
Log:
Fix area clip settings not being honored.
Fixes #2572
Modified: branches/4.0/MgDev/Common/Renderers/AGGRenderer.cpp
===================================================================
--- branches/4.0/MgDev/Common/Renderers/AGGRenderer.cpp 2026-11-03 10:35:20 UTC (rev 10258)
+++ branches/4.0/MgDev/Common/Renderers/AGGRenderer.cpp 2026-11-03 11:44:14 UTC (rev 10259)
@@ -1475,6 +1475,53 @@
//////////////////////////////////////////////////////////////////////////////
+// Draws the glyphs of a string using the supplied scanline renderer.
+template<class RendererType>
+static void DrawStringGlyphs(agg_context* cxt, unsigned int* p, double& x, double& y,
+ agg::glyph_rendering glyph_type, RendererType& ren)
+{
+ if (glyph_type == agg::glyph_ren_agg_gray8)
+ {
+ while (*p)
+ {
+ const agg::glyph_cache* glyph = cxt->fman.glyph(*p++);
+ cxt->fman.add_kerning(&x, &y);
+ cxt->fman.init_embedded_adaptors(glyph, x, y);
+
+ agg::render_scanlines(cxt->fman.gray8_adaptor(), cxt->fman.gray8_scanline(), ren);
+
+ x += glyph->advance_x;
+ y += glyph->advance_y;
+ }
+ }
+ else
+ {
+ font_curve_type curves(cxt->fman.path_adaptor());
+ font_contour_type contour(curves);
+
+ cxt->ras.filling_rule(agg::fill_non_zero);
+
+ while (*p)
+ {
+ const agg::glyph_cache* glyph = cxt->fman.glyph(*p++);
+ cxt->fman.add_kerning(&x, &y);
+ cxt->fman.init_embedded_adaptors(glyph, x, y);
+
+ cxt->ras.reset();
+ cxt->ras.add_path(contour);
+
+ agg::render_scanlines(cxt->ras, cxt->sl, ren);
+
+ x += glyph->advance_x;
+ y += glyph->advance_y;
+ }
+
+ cxt->ras.filling_rule(agg::fill_even_odd);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
void AGGRenderer::DrawString(agg_context* cxt,
const RS_String& s,
double x,
@@ -1552,9 +1599,6 @@
cxt->last_font_transform = mtx;
}
- mg_ren_aa_solid ren_aa_solid(cxt->ren);
- ren_aa_solid.color(agg::argb8_packed(color.argb()));
-
unsigned int* text;
#ifdef _WIN32
// TODO: check if we really need to convert UCS-2 to UCS-4 on Windows or
@@ -1566,47 +1610,20 @@
#endif
unsigned int* p = text;
- if (glyph_type == agg::glyph_ren_agg_gray8)
+ if (cxt->bPolyClip)
{
- while (*p)
- {
- const agg::glyph_cache* glyph = cxt->fman.glyph(*p++);
- cxt->fman.add_kerning(&x, &y);
- cxt->fman.init_embedded_adaptors(glyph, x, y);
-
- agg::render_scanlines(cxt->fman.gray8_adaptor(), cxt->fman.gray8_scanline(), ren_aa_solid);
-
- x += glyph->advance_x;
- y += glyph->advance_y;
- }
+ mg_clip_mask_ren_aa_solid clip_ren_aa_solid(cxt->clip_rb);
+ clip_ren_aa_solid.color(agg::argb8_packed(color.argb()));
+ DrawStringGlyphs(cxt, p, x, y, glyph_type, clip_ren_aa_solid);
}
else
{
- font_curve_type curves(cxt->fman.path_adaptor());
- font_contour_type contour(curves);
-
- cxt->ras.filling_rule(agg::fill_non_zero);
-
- while (*p)
- {
- const agg::glyph_cache* glyph = cxt->fman.glyph(*p++);
- cxt->fman.add_kerning(&x, &y);
- cxt->fman.init_embedded_adaptors(glyph, x, y);
-
- cxt->ras.reset();
- cxt->ras.add_path(contour);
-
- agg::render_scanlines(cxt->ras, cxt->sl, ren_aa_solid);
-
- x += glyph->advance_x;
- y += glyph->advance_y;
- }
-
- cxt->ras.filling_rule(agg::fill_even_odd);
+ mg_ren_aa_solid ren_aa_solid(cxt->ren);
+ ren_aa_solid.color(agg::argb8_packed(color.argb()));
+ DrawStringGlyphs(cxt, p, x, y, glyph_type, ren_aa_solid);
}
}
-
//////////////////////////////////////////////////////////////////////////////
void AGGRenderer::MeasureString(const RS_String& s,
double height,
@@ -1818,6 +1835,7 @@
// this is important because we will only clear that section of the alpha mask
// buffer and the rest may be dirty
c()->clip_rb.clip_box(iminx, iminy, imaxx, imaxy);
+ c()->clip_rb_pre.clip_box(iminx, iminy, imaxx, imaxy);
// transform to screen coords and transfer to AGG storage
unsigned int* pathids = (unsigned int*)alloca(polygon->geom_count() * sizeof(unsigned int));
@@ -2671,6 +2689,18 @@
//////////////////////////////////////////////////////////////////////////////
// Renders the source image to the destination context, using the specified
// affine transformation matrix.
+template<class RendererType, class SpanAllocatorType, class SpanGeneratorType>
+static void RenderImageSpans(agg_context* cxt, RendererType& ren,
+ SpanAllocatorType& sa, SpanGeneratorType& sg,
+ bool antiAlias)
+{
+ if (antiAlias)
+ agg::render_scanlines_aa(cxt->ras, cxt->sl, ren, sa, sg);
+ else
+ agg::render_scanlines_bin(cxt->ras, cxt->sl, ren, sa, sg);
+}
+
+
void AGGRenderer::RenderWithTransform(mg_rendering_buffer& src, agg_context* cxt,
agg::trans_affine& img_mtx, RS_ImageFormat format, bool antiAlias)
{
@@ -2690,10 +2720,10 @@
agg::span_allocator<mg_pixfmt_type::color_type> sa;
- if (antiAlias)
- agg::render_scanlines_aa(cxt->ras, cxt->sl, cxt->ren_pre, sa, sg);
+ if (cxt->bPolyClip)
+ RenderImageSpans(cxt, cxt->clip_rb_pre, sa, sg, antiAlias);
else
- agg::render_scanlines_bin(cxt->ras, cxt->sl, cxt->ren_pre, sa, sg);
+ RenderImageSpans(cxt, cxt->ren_pre, sa, sg, antiAlias);
}
else if (format == RS_ImageFormat_ARGB)
{
@@ -2712,10 +2742,10 @@
agg::span_allocator<mg_pixfmt_type::color_type> sa;
// we are using the alpha premultiplied renderer since the source image is premultiplied
- if (antiAlias)
- agg::render_scanlines_aa(cxt->ras, cxt->sl, cxt->ren_pre, sa, sg);
+ if (cxt->bPolyClip)
+ RenderImageSpans(cxt, cxt->clip_rb_pre, sa, sg, antiAlias);
else
- agg::render_scanlines_bin(cxt->ras, cxt->sl, cxt->ren_pre, sa, sg);
+ RenderImageSpans(cxt, cxt->ren_pre, sa, sg, antiAlias);
}
else if (format == RS_ImageFormat_NATIVE)
{
@@ -2734,10 +2764,10 @@
agg::span_allocator<mg_pixfmt_type::color_type> sa;
- if (antiAlias)
- agg::render_scanlines_aa(cxt->ras, cxt->sl, cxt->ren_pre, sa, sg);
+ if (cxt->bPolyClip)
+ RenderImageSpans(cxt, cxt->clip_rb_pre, sa, sg, antiAlias);
else
- agg::render_scanlines_bin(cxt->ras, cxt->sl, cxt->ren_pre, sa, sg);
+ RenderImageSpans(cxt, cxt->ren_pre, sa, sg, antiAlias);
}
}
Modified: branches/4.0/MgDev/Common/Renderers/agg_context.h
===================================================================
--- branches/4.0/MgDev/Common/Renderers/agg_context.h 2026-11-03 10:35:20 UTC (rev 10258)
+++ branches/4.0/MgDev/Common/Renderers/agg_context.h 2026-11-03 11:44:14 UTC (rev 10259)
@@ -103,6 +103,9 @@
typedef agg::renderer_base<mg_pixfmt_clip_mask_type> mg_clip_mask_ren_base;
typedef agg::renderer_scanline_aa_solid<mg_clip_mask_ren_base> mg_clip_mask_ren_aa_solid;
+// premultiplied variant, used to clip images and other premultiplied spans
+typedef agg::pixfmt_amask_adaptor<mg_pixfmt_type_pre, mg_alpha_mask_type> mg_pixfmt_clip_mask_type_pre;
+typedef agg::renderer_base<mg_pixfmt_clip_mask_type_pre> mg_clip_mask_ren_base_pre;
struct RS_Font;
@@ -154,6 +157,9 @@
clip_mask = new mg_pixfmt_clip_mask_type(clip_pixf, mask_mask);
clip_rb.attach(*clip_mask);
clip_ren.attach(clip_rb);
+
+ clip_mask_pre = new mg_pixfmt_clip_mask_type_pre(pf_pre, mask_mask);
+ clip_rb_pre.attach(*clip_mask_pre);
}
~agg_context()
@@ -161,6 +167,7 @@
if (ownrows)
delete [] m_rows;
+ delete clip_mask_pre;
delete clip_mask;
delete mask_pixf;
delete [] mask_buf;
@@ -193,6 +200,10 @@
mg_pixfmt_type clip_pixf;
mg_pixfmt_clip_mask_type* clip_mask;
+ // premultiplied alpha-masked renderer, for image spans
+ mg_clip_mask_ren_base_pre clip_rb_pre;
+ mg_pixfmt_clip_mask_type_pre* clip_mask_pre;
+
// font engine state caching
font_engine_type feng;
font_manager_type fman;
Modified: branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp
===================================================================
--- branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp 2026-11-03 10:35:20 UTC (rev 10258)
+++ branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp 2026-11-03 11:44:14 UTC (rev 10259)
@@ -24,10 +24,13 @@
//#include "AGGRenderer.h"
#include "FoundationDefs.h"
#include "SE_Renderer.h"
+#include "AGGImageIO.h"
#include "TestServiceFactory.h"
#include "catch.hpp"
+#include <memory>
+
static MgMap* CreateTestMapWithWatermark()
{
Ptr<MgServerSiteService> svcSite = TestServiceFactory::CreateSiteService();
@@ -2057,4 +2060,293 @@
{
throw;
}
+}
+
+
+///----------------------------------------------------------------------------
+/// Test Case Description:
+///
+/// Renders a polygon whose area style is a custom symbol that tiles an image and
+/// a text primitive across the polygon's bounding box. The symbol's clipping
+/// control is Clip, so nothing the symbol draws may appear outside the polygon.
+/// The polygon covers the left half of the rendered image, which leaves the
+/// right half free of it; the test asserts the right half contains neither the
+/// image color nor the text color.
+///
+/// The ticket this covers is
+/// https://trac.osgeo.org/mapguide/ticket/2572 - an area usage symbol with an
+/// image used to draw an unclipped rectangle over the map.
+///----------------------------------------------------------------------------
+TEST_CASE("RenderAreaSymbolClipping", "[RenderingService]")
+{
+ try
+ {
+ // Set the user information for the current thread to be administrator.
+ Ptr<MgUserInformation> adminUserInfo = new MgUserInformation(MgUser::Administrator, L"");
+ MgUserInformation::SetCurrentUserInfo(adminUserInfo);
+
+ MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+ if (serviceManager == NULL)
+ throw new MgException(MgExceptionCodes::MgNullReferenceException, L"TestRenderingService.RenderAreaSymbolClipping", __LINE__, __WFILE__, NULL, L"", NULL);
+
+ Ptr<MgFeatureService> svcFeature = dynamic_cast<MgFeatureService*>(serviceManager->RequestService(MgServiceType::FeatureService));
+ if (svcFeature == NULL)
+ throw new MgException(MgExceptionCodes::MgServiceNotAvailableException, L"TestRenderingService.RenderAreaSymbolClipping", __LINE__, __WFILE__, NULL, L"", NULL);
+
+ Ptr<MgResourceService> svcResource = TestServiceFactory::CreateResourceService();
+
+ Ptr<MgResourceIdentifier> fsId = new MgResourceIdentifier(L"Library://UnitTests/Data/AreaSymbolClippingTest.FeatureSource");
+ Ptr<MgResourceIdentifier> ldfId = new MgResourceIdentifier(L"Library://UnitTests/Layers/AreaSymbolClippingTest.LayerDefinition");
+ Ptr<MgResourceIdentifier> mdfId = new MgResourceIdentifier(L"Library://UnitTests/Maps/AreaSymbolClippingTest.MapDefinition");
+
+ // Drop anything an earlier run left behind.
+ if (svcResource->ResourceExists(mdfId)) svcResource->DeleteResource(mdfId);
+ if (svcResource->ResourceExists(ldfId)) svcResource->DeleteResource(ldfId);
+ if (svcResource->ResourceExists(fsId)) svcResource->DeleteResource(fsId);
+
+ Ptr<MgCoordinateSystemFactory> csFactory = new MgCoordinateSystemFactory();
+ STRING csWkt = csFactory->ConvertCoordinateSystemCodeToWkt(L"LL84");
+ std::string csWktMb = MgUtil::WideCharToMultiByte(csWkt);
+
+ STRING scName = L"Default";
+
+ Ptr<MgFeatureSchema> schema = new MgFeatureSchema(L"Default", L"Area symbol clipping test schema");
+ Ptr<MgClassDefinition> klass = new MgClassDefinition();
+ klass->SetName(L"TestClass");
+
+ Ptr<MgPropertyDefinitionCollection> clsProps = klass->GetProperties();
+ Ptr<MgPropertyDefinitionCollection> clsIdProps = klass->GetIdentityProperties();
+
+ Ptr<MgDataPropertyDefinition> id = new MgDataPropertyDefinition(L"ID");
+ id->SetDataType(MgPropertyType::Int32);
+ id->SetAutoGeneration(true);
+ clsProps->Add(id);
+ clsIdProps->Add(id);
+
+ Ptr<MgGeometricPropertyDefinition> geom = new MgGeometricPropertyDefinition(L"Geom");
+ geom->SetGeometryTypes(MgFeatureGeometricType::Surface);
+ geom->SetSpatialContextAssociation(scName);
+ clsProps->Add(geom);
+
+ klass->SetDefaultGeometryPropertyName(L"Geom");
+
+ Ptr<MgClassDefinitionCollection> classes = schema->GetClasses();
+ classes->Add(klass);
+
+ Ptr<MgFileFeatureSourceParams> fsParams = new MgFileFeatureSourceParams(L"OSGeo.SDF", scName, csWkt, schema);
+ svcFeature->CreateFeatureSource(fsId, fsParams);
+
+ // The left half of the rendered image. Everything to the right of x = 0
+ // is outside the polygon.
+ MgGeometryFactory geomFactory;
+
+ Ptr<MgCoordinateCollection> ringCoords = new MgCoordinateCollection();
+ Ptr<MgCoordinate> pt = geomFactory.CreateCoordinateXY(-1.0, -1.0);
+ ringCoords->Add(pt);
+ pt = geomFactory.CreateCoordinateXY(0.0, -1.0);
+ ringCoords->Add(pt);
+ pt = geomFactory.CreateCoordinateXY(0.0, 1.0);
+ ringCoords->Add(pt);
+ pt = geomFactory.CreateCoordinateXY(-1.0, 1.0);
+ ringCoords->Add(pt);
+ pt = geomFactory.CreateCoordinateXY(-1.0, -1.0);
+ ringCoords->Add(pt);
+
+ Ptr<MgLinearRing> ring = geomFactory.CreateLinearRing(ringCoords);
+ Ptr<MgGeometry> polygon = geomFactory.CreatePolygon(ring, nullptr);
+
+ Ptr<MgAgfReaderWriter> agfRw = new MgAgfReaderWriter();
+ Ptr<MgByteReader> agf = agfRw->Write(polygon);
+
+ Ptr<MgPropertyCollection> propVals = new MgPropertyCollection();
+ Ptr<MgGeometryProperty> geomVal = new MgGeometryProperty(L"Geom", agf);
+ propVals->Add(geomVal);
+
+ Ptr<MgFeatureReader> fr = svcFeature->InsertFeatures(fsId, L"Default:TestClass", propVals);
+ fr->Close();
+
+ // An 8x8 opaque red PNG, embedded in the symbol definition. It is drawn
+ // 100 mm wide, which is large enough that an unclipped copy covers a
+ // substantial part of the rendered image.
+ const char* pngBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAUSURBVChTY/jPwPAfHx4RChj+AwDC13+BykaqugAAAABJRU5ErkJggg==";
+
+ std::string ldfXml;
+ ldfXml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ ldfXml += "<LayerDefinition xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" version=\"2.4.0\" xsi:noNamespaceSchemaLocation=\"LayerDefinition-2.4.0.xsd\">";
+ ldfXml += "<VectorLayerDefinition>";
+ ldfXml += "<ResourceId>Library://UnitTests/Data/AreaSymbolClippingTest.FeatureSource</ResourceId>";
+ ldfXml += "<Watermarks/>";
+ ldfXml += "<FeatureName>Default:TestClass</FeatureName>";
+ ldfXml += "<FeatureNameType>FeatureClass</FeatureNameType>";
+ ldfXml += "<Geometry>Geom</Geometry>";
+ ldfXml += "<VectorScaleRange>";
+ ldfXml += "<CompositeTypeStyle>";
+ ldfXml += "<CompositeRule>";
+ ldfXml += "<LegendLabel/>";
+ ldfXml += "<CompositeSymbolization>";
+ ldfXml += "<SymbolInstance>";
+ ldfXml += "<SimpleSymbolDefinition>";
+ ldfXml += "<Name>Area Clipping Symbol</Name>";
+ ldfXml += "<Graphics>";
+ ldfXml += "<Image>";
+ ldfXml += "<Content>";
+ ldfXml += pngBase64;
+ ldfXml += "</Content>";
+ ldfXml += "<SizeX>100</SizeX>";
+ ldfXml += "<SizeY>100</SizeY>";
+ ldfXml += "<SizeScalable>false</SizeScalable>";
+ ldfXml += "</Image>";
+ ldfXml += "<Text>";
+ ldfXml += "<Content>'CLIP'</Content>";
+ ldfXml += "<FontName>'Arial'</FontName>";
+ ldfXml += "<Height>30</Height>";
+ ldfXml += "<HeightScalable>false</HeightScalable>";
+ ldfXml += "<TextColor>ff00ff00</TextColor>";
+ ldfXml += "</Text>";
+ ldfXml += "</Graphics>";
+ ldfXml += "<AreaUsage>";
+ ldfXml += "<AngleControl>'FromAngle'</AngleControl>";
+ ldfXml += "<OriginControl>'Global'</OriginControl>";
+ ldfXml += "<ClippingControl>'Clip'</ClippingControl>";
+ ldfXml += "<Angle>0</Angle>";
+ ldfXml += "<OriginX>0</OriginX>";
+ ldfXml += "<OriginY>0</OriginY>";
+ ldfXml += "<RepeatX>100</RepeatX>";
+ ldfXml += "<RepeatY>100</RepeatY>";
+ ldfXml += "</AreaUsage>";
+ ldfXml += "<ParameterDefinition/>";
+ ldfXml += "</SimpleSymbolDefinition>";
+ ldfXml += "<ParameterOverrides/>";
+ ldfXml += "<SizeContext>DeviceUnits</SizeContext>";
+ ldfXml += "<GeometryContext>Polygon</GeometryContext>";
+ ldfXml += "</SymbolInstance>";
+ ldfXml += "</CompositeSymbolization>";
+ ldfXml += "</CompositeRule>";
+ ldfXml += "</CompositeTypeStyle>";
+ ldfXml += "</VectorScaleRange>";
+ ldfXml += "</VectorLayerDefinition>";
+ ldfXml += "</LayerDefinition>";
+
+ Ptr<MgByteSource> ldfBs = new MgByteSource((BYTE_ARRAY_IN)ldfXml.c_str(), (INT32)ldfXml.length());
+ Ptr<MgByteReader> ldfContent = ldfBs->GetReader();
+ svcResource->SetResource(ldfId, ldfContent, nullptr);
+
+ std::string mdfXml;
+ mdfXml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ mdfXml += "<MapDefinition xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" version=\"3.0.0\" xsi:noNamespaceSchemaLocation=\"MapDefinition-3.0.0.xsd\">";
+ mdfXml += "<Name>AreaSymbolClippingTest</Name>";
+ mdfXml += "<CoordinateSystem>";
+ mdfXml += csWktMb;
+ mdfXml += "</CoordinateSystem>";
+ mdfXml += "<Extents><MinX>-1</MinX><MaxX>1</MaxX><MinY>-1</MinY><MaxY>1</MaxY></Extents>";
+ mdfXml += "<BackgroundColor>ffffffff</BackgroundColor>";
+ mdfXml += "<MapLayer>";
+ mdfXml += "<Name>AreaSymbolClipping</Name>";
+ mdfXml += "<ResourceId>Library://UnitTests/Layers/AreaSymbolClippingTest.LayerDefinition</ResourceId>";
+ mdfXml += "<Selectable>true</Selectable>";
+ mdfXml += "<ShowInLegend>true</ShowInLegend>";
+ mdfXml += "<LegendLabel>AreaSymbolClipping</LegendLabel>";
+ mdfXml += "<ExpandInLegend>true</ExpandInLegend>";
+ mdfXml += "<Visible>true</Visible>";
+ mdfXml += "<Group />";
+ mdfXml += "</MapLayer>";
+ mdfXml += "<Watermarks />";
+ mdfXml += "</MapDefinition>";
+
+ Ptr<MgByteSource> mdfBs = new MgByteSource((BYTE_ARRAY_IN)mdfXml.c_str(), (INT32)mdfXml.length());
+ Ptr<MgByteReader> mdfContent = mdfBs->GetReader();
+ svcResource->SetResource(mdfId, mdfContent, nullptr);
+
+ Ptr<MgServerSiteService> svcSite = TestServiceFactory::CreateSiteService();
+ Ptr<MgSiteConnection> siteConnection = TestServiceFactory::CreateSiteConnection(svcSite);
+
+ Ptr<MgMap> map = new MgMap(siteConnection);
+ map->Create(mdfId, mdfId->GetName());
+ map->SetDisplayDpi(96);
+ map->SetDisplayWidth(512);
+ map->SetDisplayHeight(512);
+
+ // Render exactly the extents the polygon is defined against, so it
+ // covers the left half of the image.
+ const INT32 imageSize = 512;
+ Ptr<MgEnvelope> extents = new MgEnvelope(-1.0, -1.0, 1.0, 1.0);
+ Ptr<MgColor> bgColor = new MgColor(255, 255, 255, 255);
+
+ Ptr<MgRenderingService> svcRendering = TestServiceFactory::CreateRenderingService();
+ Ptr<MgByteReader> png = svcRendering->RenderMap(map, nullptr, extents, imageSize, imageSize, bgColor, MgImageFormats::Png);
+ png->ToFile(L"../UnitTestFiles/Results/RenderAreaSymbolClipping.png");
+
+ Ptr<MgByteSink> sink = new MgByteSink(png);
+ Ptr<MgByte> bytes = sink->ToBuffer();
+ REQUIRE(bytes->GetLength() > 0);
+
+ int width = 0;
+ int height = 0;
+ unsigned int* decoded = AGGImageIO::DecodePNG(bytes->Bytes(), (size_t)bytes->GetLength(), width, height);
+ REQUIRE(decoded != NULL);
+ // DecodePNG hands back a buffer that has to be released with delete[].
+ std::unique_ptr<unsigned int[]> pixels(decoded);
+ REQUIRE(width == imageSize);
+ REQUIRE(height == imageSize);
+
+ // Count the image and text colors inside and to the right of the polygon.
+ // DecodePNG returns the image bottom up.
+ int redInside = 0, redOutside = 0;
+ int greenInside = 0, greenOutside = 0;
+ for (int y = 0; y < height; ++y)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ unsigned int argb = decoded[(height - 1 - y) * width + x];
+ int r = (int)((argb >> 16) & 0xFF);
+ int g = (int)((argb >> 8) & 0xFF);
+ int b = (int)(argb & 0xFF);
+
+ bool isRed = (r > 200 && g < 60 && b < 60);
+ bool isGreen = (g > 200 && r < 60 && b < 60);
+
+ // The polygon's right edge is at x = width / 2. Look a quarter
+ // of the image further right to stay clear of the boundary's
+ // antialiasing.
+ bool outside = (x > width * 3 / 4);
+
+ if (isRed)
+ outside ? ++redOutside : ++redInside;
+ if (isGreen)
+ outside ? ++greenOutside : ++greenInside;
+ }
+ }
+
+ INFO("red pixels inside=" << redInside << " outside=" << redOutside
+ << ", green pixels inside=" << greenInside << " outside=" << greenOutside);
+
+ // The symbol must actually be drawing, otherwise the clipping checks
+ // below would pass for the wrong reason.
+ CHECK(redInside > 1000);
+ CHECK(greenInside > 0);
+
+ // Nothing the area symbol draws may leak outside the polygon.
+ CHECK(redOutside == 0);
+ CHECK(greenOutside == 0);
+
+ // Leave the repository as we found it.
+ svcResource->DeleteResource(mdfId);
+ svcResource->DeleteResource(ldfId);
+ svcResource->DeleteResource(fsId);
+ }
+ catch (MgException* e)
+ {
+ STRING message = e->GetDetails(TestServiceFactory::TEST_LOCALE);
+ SAFE_RELEASE(e);
+ FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch (FdoException* e)
+ {
+ FDO_SAFE_RELEASE(e);
+ FAIL("FdoException occurred");
+ }
+ catch (...)
+ {
+ throw;
+ }
}
\ No newline at end of file
More information about the mapguide-commits
mailing list