[mapguide-commits] r10230 - in branches/4.0/MgDev: Common/Stylization Server/src/UnitTesting
svn_mapguide at osgeo.org
svn_mapguide at osgeo.org
Fri Oct 23 15:11:41 PDT 2026
Author: jng
Date: 2026-10-23 15:11:40 -0700 (Fri, 23 Oct 2026)
New Revision: 10230
Modified:
branches/4.0/MgDev/Common/Stylization/Band.cpp
branches/4.0/MgDev/Common/Stylization/BandData.cpp
branches/4.0/MgDev/Common/Stylization/GridData.cpp
branches/4.0/MgDev/Server/src/UnitTesting/CMakeLists.txt
branches/4.0/MgDev/Server/src/UnitTesting/TestRasterStylization.cpp
branches/4.0/MgDev/Server/src/UnitTesting/TestStylization.cpp
Log:
Add more tests to harden the Grid band I/O
Modified: branches/4.0/MgDev/Common/Stylization/Band.cpp
===================================================================
--- branches/4.0/MgDev/Common/Stylization/Band.cpp 2026-10-23 20:37:05 UTC (rev 10229)
+++ branches/4.0/MgDev/Common/Stylization/Band.cpp 2026-10-23 22:11:40 UTC (rev 10230)
@@ -164,7 +164,43 @@
}
break;
#else
- // Linux implementation here
+ // The ByteBandData native accessors are Windows only, so use the
+ // BandData virtual API, which copies the band element size in bytes.
+ case UnsignedInt32:
+ case Int32:
+ {
+ assert (Int32 == m_dataType || UnsignedInt32 == m_dataType);
+ success = m_pBandData->GetValue(i, j, *reinterpret_cast<INT32*>(pValue));
+ }
+ break;
+
+ case Double32:
+ {
+ assert (Double32 == m_dataType);
+ INT32 bits;
+ success = m_pBandData->GetValue(i, j, bits);
+ if (success)
+ memcpy(pValue, &bits, sizeof(float));
+ }
+ break;
+
+ case UnsignedInt64:
+ case Int64:
+ {
+ assert (Int64 == m_dataType || UnsignedInt64 == m_dataType);
+ success = m_pBandData->GetValue(i, j, *reinterpret_cast<INT64*>(pValue));
+ }
+ break;
+
+ case Double64:
+ {
+ assert (Double64 == m_dataType);
+ INT64 bits;
+ success = m_pBandData->GetValue(i, j, bits);
+ if (success)
+ memcpy(pValue, &bits, sizeof(double));
+ }
+ break;
#endif
@@ -230,7 +266,41 @@
}
break;
#else
- // Linux implementation
+ // The ByteBandData native accessors are Windows only, so use the
+ // BandData virtual API, which copies the band element size in bytes.
+ case UnsignedInt32:
+ case Int32:
+ {
+ assert (Int32 == m_dataType || UnsignedInt32 == m_dataType);
+ success = m_pBandData->SetValue(i, j, reinterpret_cast<const INT32*>(pValue));
+ }
+ break;
+
+ case Double32:
+ {
+ assert (Double32 == m_dataType);
+ INT32 bits;
+ memcpy(&bits, pValue, sizeof(float));
+ success = m_pBandData->SetValue(i, j, &bits);
+ }
+ break;
+
+ case UnsignedInt64:
+ case Int64:
+ {
+ assert (Int64 == m_dataType || UnsignedInt64 == m_dataType);
+ success = m_pBandData->SetValue(i, j, reinterpret_cast<const INT64*>(pValue));
+ }
+ break;
+
+ case Double64:
+ {
+ assert (Double64 == m_dataType);
+ INT64 bits;
+ memcpy(&bits, pValue, sizeof(double));
+ success = m_pBandData->SetValue(i, j, &bits);
+ }
+ break;
#endif
@@ -337,7 +407,71 @@
}
break;
#else
- // Linux implementation
+ // The ByteBandData native accessors are Windows only, so use the
+ // BandData virtual API, which copies the band element size in bytes.
+ case UnsignedInt32:
+ {
+ INT32 bits;
+ success = m_pBandData->GetValue(i, j, bits);
+ success = success && (memcmp(&bits, &m_nullValue, 4) != 0); //ensure it is not equal to the null value
+ dvalue = (double)(unsigned int)bits;
+ }
+ break;
+
+ case Int32:
+ {
+ INT32 bits;
+ success = m_pBandData->GetValue(i, j, bits);
+ success = success && (memcmp(&bits, &m_nullValue, 4) != 0); //ensure it is not equal to the null value
+ dvalue = (double)bits;
+ }
+ break;
+
+ case Double32:
+ {
+ INT32 bits;
+ float val = 0.0f;
+ success = m_pBandData->GetValue(i, j, bits);
+ if (success)
+ {
+ memcpy(&val, &bits, sizeof(float));
+ success = (memcmp(&val, &m_nullValue, sizeof(float)) != 0); //ensure it is not equal to the null value
+ dvalue = (double)val;
+ }
+ }
+ break;
+
+ case UnsignedInt64:
+ {
+ INT64 bits;
+ success = m_pBandData->GetValue(i, j, bits);
+ success = success && memcmp(&bits, &m_nullValue, 8) != 0; //ensure it is not equal to the null value
+ dvalue = (double)(UINT64)bits;
+ }
+ break;
+
+ case Int64:
+ {
+ INT64 bits;
+ success = m_pBandData->GetValue(i, j, bits);
+ success = success && (memcmp(&bits, &m_nullValue, 8) != 0); //ensure it is not equal to the null value
+ dvalue = (double)bits;
+ }
+ break;
+
+ case Double64:
+ {
+ INT64 bits;
+ double val = 0.0;
+ success = m_pBandData->GetValue(i, j, bits);
+ if (success)
+ {
+ memcpy(&val, &bits, sizeof(double));
+ success = (memcmp(&val, &m_nullValue, sizeof(double)) != 0); //ensure it is not equal to the null value
+ dvalue = val;
+ }
+ }
+ break;
#endif
default:
@@ -666,6 +800,7 @@
return m_pOwnerGrid->GetXUnitDistance();
}
+ // Ignore any C4723 warning about potenial divide by 0. It's a false positive
return m_nXCount ? m_xExtent / m_nXCount : 0;
}
@@ -842,11 +977,26 @@
if (memcmp(&fbottom, &m_nullValue, 4) == 0)
fbottom = fcenter;
#else
- // TODO: Linux implementation
- fleft = 0.0f;
- fright = 0.0f;
- ftop = 0.0f;
- fbottom = 0.0f;
+ // The ByteBandData native accessors are Windows only, so read the
+ // neighbours through GetValueAsDouble, which applies the same
+ // null-value check the Windows path does inline
+ double dcenter, dleft, dright, dtop, dbottom;
+ if (!GetValueAsDouble(i, j, dcenter))
+ return false;
+ if (!i || !GetValueAsDouble(i - 1, j, dleft))
+ dleft = dcenter;
+ if (i == m_nXCount - 1 || !GetValueAsDouble(i + 1, j, dright))
+ dright = dcenter;
+ if (!j || !GetValueAsDouble(i, j - 1, dtop))
+ dtop = dcenter;
+ if (j == m_nYCount - 1 || !GetValueAsDouble(i, j + 1, dbottom))
+ dbottom = dcenter;
+
+ fcenter = (float)dcenter;
+ fleft = (float)dleft;
+ fright = (float)dright;
+ ftop = (float)dtop;
+ fbottom = (float)dbottom;
#endif
left = fleft;
@@ -887,7 +1037,19 @@
else
bottom = ((ByteBandData*)m_pBandData)->GetNativeDouble64(index+m_nXCount);
#else
- // Linux implementation
+ // The ByteBandData native accessors are Windows only, so read the
+ // neighbours through GetValueAsDouble, which applies the same
+ // null-value check the Windows path does inline
+ if (!GetValueAsDouble(i, j, center))
+ return false;
+ if (!i || !GetValueAsDouble(i - 1, j, left))
+ left = center;
+ if (i == m_nXCount - 1 || !GetValueAsDouble(i + 1, j, right))
+ right = center;
+ if (!j || !GetValueAsDouble(i, j - 1, top))
+ top = center;
+ if (j == m_nYCount - 1 || !GetValueAsDouble(i, j + 1, bottom))
+ bottom = center;
#endif
if (memcmp(&left, &m_nullValue, 8) == 0)
Modified: branches/4.0/MgDev/Common/Stylization/BandData.cpp
===================================================================
--- branches/4.0/MgDev/Common/Stylization/BandData.cpp 2026-10-23 20:37:05 UTC (rev 10229)
+++ branches/4.0/MgDev/Common/Stylization/BandData.cpp 2026-10-23 22:11:40 UTC (rev 10230)
@@ -441,7 +441,7 @@
if (m_size == 8)
{
int pos = (x + y * m_width);
- value = m_pData[pos*8];
+ memcpy(&value, &m_pData[pos*8], sizeof(INT64));
return true;
}
Modified: branches/4.0/MgDev/Common/Stylization/GridData.cpp
===================================================================
--- branches/4.0/MgDev/Common/Stylization/GridData.cpp 2026-10-23 20:37:05 UTC (rev 10229)
+++ branches/4.0/MgDev/Common/Stylization/GridData.cpp 2026-10-23 22:11:40 UTC (rev 10230)
@@ -21,6 +21,8 @@
#include "Bounds.h"
#include "RS_InputStream.h"
+#include <memory>
+
#ifdef MG_MAX_PATH
#undef MG_MAX_PATH
#endif
@@ -56,9 +58,13 @@
return false;
}
-#define READELEVATIONDATA(elevationd_band_type, elevation_fdo_type, function_name) \
- FdoPtr<elevation_fdo_type> val = static_cast<elevation_fdo_type*>(nullValue); \
- elevationd_band_type fdoNullValue= val->function_name(); \
+// null_data_type is the FdoDataType the macro's cast expects. If the raster has
+// no null value, or reports one of a different type, every sample is kept.
+#define READELEVATIONDATA(elevationd_band_type, elevation_fdo_type, function_name, null_data_type) \
+ elevation_fdo_type* val = (nullValue.p && nullValue->GetDataType() == (null_data_type))? \
+ static_cast<elevation_fdo_type*>(nullValue.p) : NULL; \
+ const bool bHasNullValue = (val != NULL); \
+ elevationd_band_type fdoNullValue = bHasNullValue? val->function_name() : (elevationd_band_type)0; \
elevationd_band_type curVal; \
for (unsigned long y = 0; y < actualRows; ++y) \
{ \
@@ -66,10 +72,10 @@
{ \
pCurPos = (unsigned char*)(pRasterData + y*rowLength + x*bitPerPixel/8 ); \
curVal = (*(reinterpret_cast<elevationd_band_type *>(pCurPos))); \
- if (curVal != fdoNullValue) \
+ if (!bHasNullValue || curVal != fdoNullValue) \
{ \
- float val = (float)curVal; \
- pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::Double32, &val); \
+ float fValue = (float)curVal; \
+ pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::Double32, &fValue); \
} \
else \
{ \
@@ -337,373 +343,359 @@
)
{
Band* pGisBand = NULL;
- RS_InputStream* reader = NULL;
- FdoByte* pRasterData = NULL;
- FdoByte* pPaletteBuf = NULL;
wchar_t pUniqueBandName[MG_MAX_PATH] = { 0 };
- try
+
+ //FdoPtr<FdoRasterDataModel> dataModel = pRaster->GetDataModel();
+ //FdoPtr<FdoByteArray> byteArray = pRaster->GetBounds();
+ //FdoPtr<FdoFgfGeometryFactory> pAwkbFactory = FdoFgfGeometryFactory::GetInstance();
+ //FdoPtr<FdoIGeometry> pGeometry = pAwkbFactory->CreateGeometryFromFgf(byteArray);
+ //FdoPtr<FdoIEnvelope> envelope = pGeometry->GetEnvelope();
+ FdoInt32 bitPerPixel = pRaster->GetBitsPerPixel();
+ FdoRasterDataModelType dataModelType = (FdoRasterDataModelType)pRaster->GetDataModelType();
+ RS_Bounds env(pRaster->GetExtent());
+ // Compute the acutal pixel size of returned raster
+ unsigned long actualCols = (unsigned long) (((env.maxx - env.minx) / (xMax - xMin)) * cols + 0.5);
+ unsigned long actualRows = (unsigned long) (((env.maxy - env.miny) / (yMax - yMin)) * rows + 0.5);
+ unsigned long colStartPos = (unsigned long) (((env.minx - xMin) / (xMax - xMin)) * cols + 0.5);
+ unsigned long rowStartPos = (unsigned long) (((yMax - env.maxy) / (yMax - yMin)) * rows + 0.5);
+
+ //pRaster->SetImageXSize(actualCols);
+ //pRaster->SetImageYSize(actualRows);
+ std::unique_ptr<RS_InputStream> reader(pRaster->GetStream(/*this value is ignored*/RS_ImageFormat_ABGR, actualCols, actualRows));
+
+ if (reader)
{
- //FdoPtr<FdoRasterDataModel> dataModel = pRaster->GetDataModel();
- //FdoPtr<FdoByteArray> byteArray = pRaster->GetBounds();
- //FdoPtr<FdoFgfGeometryFactory> pAwkbFactory = FdoFgfGeometryFactory::GetInstance();
- //FdoPtr<FdoIGeometry> pGeometry = pAwkbFactory->CreateGeometryFromFgf(byteArray);
- //FdoPtr<FdoIEnvelope> envelope = pGeometry->GetEnvelope();
- FdoInt32 bitPerPixel = pRaster->GetBitsPerPixel();
- FdoRasterDataModelType dataModelType = (FdoRasterDataModelType)pRaster->GetDataModelType();
- RS_Bounds env(pRaster->GetExtent());
- // Compute the acutal pixel size of returned raster
- unsigned long actualCols = (unsigned long) (((env.maxx - env.minx) / (xMax - xMin)) * cols + 0.5);
- unsigned long actualRows = (unsigned long) (((env.maxy - env.miny) / (yMax - yMin)) * rows + 0.5);
- unsigned long colStartPos = (unsigned long) (((env.minx - xMin) / (xMax - xMin)) * cols + 0.5);
- unsigned long rowStartPos = (unsigned long) (((yMax - env.maxy) / (yMax - yMin)) * rows + 0.5);
+ //FireOnStepQuery();
+ // Compute the row length of image. If bAlignment is true, bits size of each row must be a multiple of 8 byte.
+ unsigned long rowBitSize = (actualCols)*bitPerPixel;
+ unsigned long rowLength;
+ if (bAlignment)
+ rowLength = (rowBitSize%64)? ((rowBitSize + 64 - rowBitSize%64) / 8):(rowBitSize/8);
+ else
+ rowLength = rowBitSize/8;
+ unsigned long bufferLength = rowLength * actualRows;
- //pRaster->SetImageXSize(actualCols);
- //pRaster->SetImageYSize(actualRows);
- reader = pRaster->GetStream(/*this value is ignored*/RS_ImageFormat_ABGR, actualCols, actualRows);
+ if (reader->available() != bufferLength)
+ return;
- if (reader)
+ //Create an buffer to hold data
+ FdoByte* pRasterData = new FdoByte[bufferLength];
+ std::unique_ptr<FdoByte[]> rasterData(pRasterData);
+ if (NULL == pRasterData)
{
- //FireOnStepQuery();
- // Compute the row length of image. If bAlignment is true, bits size of each row must be a multiple of 8 byte.
- unsigned long rowBitSize = (actualCols)*bitPerPixel;
- unsigned long rowLength;
- if (bAlignment)
- rowLength = (rowBitSize%64)? ((rowBitSize + 64 - rowBitSize%64) / 8):(rowBitSize/8);
- else
- rowLength = rowBitSize/8;
- unsigned long bufferLength = rowLength * actualRows;
+ throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(CLNT_5_OUTOFMEMORY)));
+ }
- if (reader->available() != bufferLength)
- {
- delete reader;
- return;
- }
+ reader->read(pRasterData, bufferLength);
- //Create an buffer to hold data
- pRasterData = new FdoByte[bufferLength];
- if (NULL == pRasterData)
- {
- throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(CLNT_5_OUTOFMEMORY)));
- }
+ // Get grid data type
+ Band::BandDataType gridDataType = GetGridDataType(pRaster);
+ GetUniqueBandName(pUniqueBandName, this, pBandName);
- reader->read(pRasterData, bufferLength);
+ pGisBand = CreateBand(gridDataType, pUniqueBandName);
+ FdoString* pVerticalUnit = pRaster->GetVerticalUnits().c_str();
+ pGisBand->SetUnitName(pVerticalUnit);
- // Get grid data type
- Band::BandDataType gridDataType = GetGridDataType(pRaster);
- GetUniqueBandName(pUniqueBandName, this, pBandName);
-
- pGisBand = CreateBand(gridDataType, pUniqueBandName);
- FdoString* pVerticalUnit = pRaster->GetVerticalUnits().c_str();
- pGisBand->SetUnitName(pVerticalUnit);
-
- // Set NULL value for Band
- if (dataModelType == FdoRasterDataModelType_Data)
+ // Set NULL value for Band
+ if (dataModelType == FdoRasterDataModelType_Data)
+ {
+ //TODO: Are we guaranteed
+ //that FLT_MAX will always indicate the NODATA value for all grids?
+ if (gridDataType == Band::Double32)
{
- //TODO: Are we guaranteed
- //that FLT_MAX will always indicate the NODATA value for all grids?
- if (gridDataType == Band::Double32)
- {
- pGisBand->SetNullValue(Band::Double32, (void*)&MG_NODATA_VALUE);
- }
- else
- {
- double nullval = DBL_MAX;
- pGisBand->SetNullValue(Band::Double64, (void*)&nullval);
- }
+ pGisBand->SetNullValue(Band::Double32, (void*)&MG_NODATA_VALUE);
}
- else if ((dataModelType == FdoRasterDataModelType_RGB)
- || (dataModelType == FdoRasterDataModelType_RGBA))
- {
- unsigned int nullValue = 0x00000000;
- pGisBand->SetAllToValue(gridDataType, (char*)&nullValue);
- }
else
{
- unsigned long nullValue(0);
- pGisBand->SetAllToValue(gridDataType, (char*)(&nullValue));
+ double nullval = DBL_MAX;
+ pGisBand->SetNullValue(Band::Double64, (void*)&nullval);
}
+ }
+ else if ((dataModelType == FdoRasterDataModelType_RGB)
+ || (dataModelType == FdoRasterDataModelType_RGBA))
+ {
+ unsigned int nullValue = 0x00000000;
+ pGisBand->SetAllToValue(gridDataType, (char*)&nullValue);
+ }
+ else
+ {
+ unsigned long nullValue(0);
+ pGisBand->SetAllToValue(gridDataType, (char*)(&nullValue));
+ }
- unsigned char* pCurPos;
- FdoRasterDataType rasterDataType = (FdoRasterDataType)pRaster->GetDataType();
- if ((dataModelType == FdoRasterDataModelType_RGB)
- || (dataModelType == FdoRasterDataModelType_RGBA))
+ unsigned char* pCurPos;
+ FdoRasterDataType rasterDataType = (FdoRasterDataType)pRaster->GetDataType();
+ if ((dataModelType == FdoRasterDataModelType_RGB)
+ || (dataModelType == FdoRasterDataModelType_RGBA))
+ {
+ unsigned int color;
+ if (24 == bitPerPixel)
{
- unsigned int color;
- if (24 == bitPerPixel)
+ for (unsigned long y = 0; y < actualRows; ++y)
{
- for (unsigned long y = 0; y < actualRows; ++y)
+ //FireOnStepQuery();
+ for (unsigned long x = 0; x < actualCols; ++x)
{
- //FireOnStepQuery();
- for (unsigned long x = 0; x < actualCols; ++x)
- {
- pCurPos = (unsigned char*)(pRasterData + y*rowLength + x*bitPerPixel/8 );
- color = ( 0xFF000000 | ((*(pCurPos)) << 16) | ((*(pCurPos + 1) )<< 8) | (*(pCurPos + 2)));
- pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::UnsignedInt32, &color );
- }
+ pCurPos = (unsigned char*)(pRasterData + y*rowLength + x*bitPerPixel/8 );
+ color = ( 0xFF000000 | ((*(pCurPos)) << 16) | ((*(pCurPos + 1) )<< 8) | (*(pCurPos + 2)));
+ pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::UnsignedInt32, &color );
}
}
- else if (48 == bitPerPixel) //BUG: 48 bpp should not be using a 32 bit grid size!
+ }
+ else if (48 == bitPerPixel) //BUG: 48 bpp should not be using a 32 bit grid size!
+ {
+ for (unsigned long y = 0; y < actualRows; ++y)
{
- for (unsigned long y = 0; y < actualRows; ++y)
+ //FireOnStepQuery();
+ for (unsigned long x = 0; x < actualCols; ++x)
{
- //FireOnStepQuery();
- for (unsigned long x = 0; x < actualCols; ++x)
- {
- pCurPos = (unsigned char*)(pRasterData + y*rowLength + x*bitPerPixel/8 );
- color = ( 0xFF000000 | ((*(pCurPos)) << 32) | ((*(pCurPos + 2)) << 16) | (*(pCurPos + 4)) << 8 );
- pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::UnsignedInt32, &color );//BUG: 48 bpp should not be using a 32 bit grid size!
- }
+ pCurPos = (unsigned char*)(pRasterData + y*rowLength + x*bitPerPixel/8 );
+ //The provider hands us 16 bit samples in native byte order, so read
+ //each RGB channel as a 16 bit value and reduce it to its high byte
+ UINT16 red = *(reinterpret_cast<UINT16*>(pCurPos));
+ UINT16 green = *(reinterpret_cast<UINT16*>(pCurPos + 2));
+ UINT16 blue = *(reinterpret_cast<UINT16*>(pCurPos + 4));
+ color = ( 0xFF000000 | ((red >> 8) << 16) | ((green >> 8) << 8) | (blue >> 8) );
+ pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::UnsignedInt32, &color );//BUG: 48 bpp should not be using a 32 bit grid size!
}
}
- else //32 bpp
+ }
+ else //32 bpp
+ {
+ //allocate a row where we will do pixel conversion
+ std::unique_ptr<unsigned char[]> row(new unsigned char[actualCols * 4]);
+
+ for (unsigned long y=0; y<actualRows; ++y)
{
- //allocate a row where we will do pixel conversion
- unsigned char* row = new unsigned char[actualCols * 4];
+ //FireOnStepQuery();
- for (unsigned long y=0; y<actualRows; ++y)
+ //convert from RGBA to BGRA
+ pCurPos = pRasterData + y*rowLength; //source pixel pointer
+ unsigned char* pix = row.get(); //destination pixel pointer
+
+ for (unsigned int x=0; x<actualCols; ++x)
{
- //FireOnStepQuery();
+ pix[1] = pCurPos[1];
+ pix[3] = pCurPos[3];
+ pix[2] = pCurPos[0];
+ pix[0] = pCurPos[2];
- //convert from RGBA to BGRA
- pCurPos = pRasterData + y*rowLength; //source pixel pointer
- unsigned char* pix = row; //destination pixel pointer
-
- for (unsigned int x=0; x<actualCols; ++x)
- {
- pix[1] = pCurPos[1];
- pix[3] = pCurPos[3];
- pix[2] = pCurPos[0];
- pix[0] = pCurPos[2];
-
- pCurPos += 4;
- pix += 4;
- }
-
- //set the whole row into the grid
- pGisBand->SetRowValue(0, y, Band::UnsignedInt32, (char*)row, actualCols);
+ pCurPos += 4;
+ pix += 4;
}
- delete [] row;
+ //set the whole row into the grid
+ pGisBand->SetRowValue(0, y, Band::UnsignedInt32, (char*)row.get(), actualCols);
}
}
- else if (dataModelType == FdoRasterDataModelType_Palette)
+ }
+ else if (dataModelType == FdoRasterDataModelType_Palette)
+ {
+ //FdoPtr<FdoIRasterPropertyDictionary> propDict = pRaster->GetAuxiliaryProperties();
+ //FdoPtr<FdoDataValue> pal = propDict->GetProperty(L"Palette");
+ //FdoLOBValue* palLOB = static_cast<FdoLOBValue*>(pal.p);
+ struct RgbColor
{
- //FdoPtr<FdoIRasterPropertyDictionary> propDict = pRaster->GetAuxiliaryProperties();
- //FdoPtr<FdoDataValue> pal = propDict->GetProperty(L"Palette");
- //FdoLOBValue* palLOB = static_cast<FdoLOBValue*>(pal.p);
- struct RgbColor
- {
- union {
- struct { FdoByte red; FdoByte green; FdoByte blue; FdoByte alpha; } rgba;
- unsigned int packed;
- };
+ union {
+ struct { FdoByte red; FdoByte green; FdoByte blue; FdoByte alpha; } rgba;
+ unsigned int packed;
};
+ };
- unsigned int color;
- unsigned int pos;
- if (bitPerPixel >= 8)
+ unsigned int color;
+ unsigned int pos;
+ if (bitPerPixel >= 8)
+ {
+ std::unique_ptr<RS_InputStream> paletteStream(pRaster->GetPalette());
+ FdoByte* pPaletteBuf = new FdoByte[paletteStream->available()];
+ std::unique_ptr<FdoByte[]> paletteBuf(pPaletteBuf);
+ if (NULL == pPaletteBuf)
{
- RS_InputStream* pStream = pRaster->GetPalette();
- pPaletteBuf = new FdoByte[pStream->available()];
- if (NULL == pPaletteBuf)
- {
- throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(CLNT_5_OUTOFMEMORY)));
- }
+ throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(CLNT_5_OUTOFMEMORY)));
+ }
- pStream->read(pPaletteBuf, pStream->available());
- RgbColor* palette = reinterpret_cast<RgbColor*>(pPaletteBuf);
+ paletteStream->read(pPaletteBuf, paletteStream->available());
+ RgbColor* palette = reinterpret_cast<RgbColor*>(pPaletteBuf);
- for (unsigned long y = 0; y < actualRows; ++y)
+ for (unsigned long y = 0; y < actualRows; ++y)
+ {
+ //FireOnStepQuery();
+ for (unsigned long x = 0; x< actualCols; ++x)
{
- //FireOnStepQuery();
- for (unsigned long x = 0; x< actualCols; ++x)
+ pCurPos = (unsigned char*)(pRasterData + y*rowLength + x*bitPerPixel/8 );
+ switch(bitPerPixel)
{
- pCurPos = (unsigned char*)(pRasterData + y*rowLength + x*bitPerPixel/8 );
- switch(bitPerPixel)
- {
- case 8:
- pos = *(reinterpret_cast<UINT8*>(pCurPos));
- break;
- case 16:
- pos = *(reinterpret_cast<UINT16*>(pCurPos));
- break;
- case 32:
- pos = *(reinterpret_cast<UINT32*>(pCurPos));
- break;
- default:
- pos = 0;
- break;
- }
- color = 0xFF000000 | palette[pos].rgba.red << 16 | palette[pos].rgba.green << 8 | palette[pos].rgba.blue;
- pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::UnsignedInt32, &color );
+ case 8:
+ pos = *(reinterpret_cast<UINT8*>(pCurPos));
+ break;
+ case 16:
+ pos = *(reinterpret_cast<UINT16*>(pCurPos));
+ break;
+ case 32:
+ pos = *(reinterpret_cast<UINT32*>(pCurPos));
+ break;
+ default:
+ pos = 0;
+ break;
}
+ color = 0xFF000000 | palette[pos].rgba.red << 16 | palette[pos].rgba.green << 8 | palette[pos].rgba.blue;
+ pGisBand->SetValue(colStartPos + x, rowStartPos + y, Band::UnsignedInt32, &color );
}
}
- else
+ }
+ else
+ {
+ char value, bitNum, temp;
+ unsigned long byteNum;
+
+ for (unsigned long y = 0; y < actualRows; ++y)
{
- char value, bitNum, temp;
- unsigned long byteNum;
-
- for (unsigned long y = 0; y < actualRows; ++y)
+ bitNum = 0;
+ value = 0;
+ temp = 0;
+ byteNum = 0;
+ //FireOnStepQuery();
+ for (unsigned long x = 0; x < actualCols; ++x)
{
- bitNum = 0;
- value = 0;
- temp = 0;
- byteNum = 0;
- //FireOnStepQuery();
- for (unsigned long x = 0; x < actualCols; ++x)
+ if (bitNum >= 8)
{
- if (bitNum >= 8)
- {
- bitNum = 0;
- byteNum++;
- }
- if (bitNum == 0)
- temp = (*((char*)(pRasterData + y*rowLength + byteNum)));
+ bitNum = 0;
+ byteNum++;
+ }
+ if (bitNum == 0)
+ temp = (*((char*)(pRasterData + y*rowLength + byteNum)));
- value = (char)((temp << (8 - bitNum - bitPerPixel)) >> (8 - bitPerPixel));
- pGisBand->SetValue(x, y, gridDataType, &value);
- bitNum += bitPerPixel;
- }
+ value = (char)((temp << (8 - bitNum - bitPerPixel)) >> (8 - bitPerPixel));
+ pGisBand->SetValue(x, y, gridDataType, &value);
+ bitNum += bitPerPixel;
}
}
}
- else if ((dataModelType == FdoRasterDataModelType_Data) &&
- ((rasterDataType == FdoRasterDataType_Integer) ||
- (rasterDataType == FdoRasterDataType_UnsignedInteger)))
+ }
+ else if ((dataModelType == FdoRasterDataModelType_Data) &&
+ ((rasterDataType == FdoRasterDataType_Integer) ||
+ (rasterDataType == FdoRasterDataType_UnsignedInteger)))
+ {
+ Band::BandDataType bandDataType = GetGridDataType(pRaster, false);
+ FdoDataType type = (FdoDataType)pRaster->GetNullValueType();
+ FdoPtr<FdoDataValue> nullValue;
+ switch(type)
{
- Band::BandDataType bandDataType = GetGridDataType(pRaster, false);
- FdoDataType type = (FdoDataType)pRaster->GetNullValueType();
- FdoDataValue* nullValue = NULL;
- switch(type)
+ case FdoDataType_Byte:
+ nullValue = FdoByteValue::Create((FdoByte)pRaster->GetNullValueData());
+ break;
+ case FdoDataType_Double:
+ nullValue = FdoDoubleValue::Create((double)pRaster->GetNullValueData());
+ break;
+ case FdoDataType_Single:
+ nullValue = FdoSingleValue::Create((float)pRaster->GetNullValueData());
+ break;
+ case FdoDataType_Int16:
+ nullValue = FdoInt16Value::Create((FdoInt16)pRaster->GetNullValueData());
+ break;
+ case FdoDataType_Int32:
+ nullValue = FdoInt32Value::Create((FdoInt32)pRaster->GetNullValueData());
+ break;
+ case FdoDataType_Int64:
+ nullValue = FdoInt64Value::Create(pRaster->GetNullValueData());
+ break;
+ default: break;
+ }
+
+ switch (bandDataType)
+ {
+ case Band::UnsignedInt8:
{
- case FdoDataType_Double:
- nullValue = FdoDoubleValue::Create((double)pRaster->GetNullValueData());
+ READELEVATIONDATA(UINT8, FdoByteValue, GetByte, FdoDataType_Byte);
break;
- case FdoDataType_Single:
- nullValue = FdoSingleValue::Create((float)pRaster->GetNullValueData());
+ }
+ case Band::UnsignedInt16:
+ {
+ READELEVATIONDATA(UINT16, FdoInt16Value, GetInt16, FdoDataType_Int16);
break;
- case FdoDataType_Int16:
- nullValue = FdoInt16Value::Create((FdoInt16)pRaster->GetNullValueData());
+ }
+ case Band::UnsignedInt32:
+ {
+ READELEVATIONDATA(UINT32, FdoInt32Value, GetInt32, FdoDataType_Int32);
break;
- case FdoDataType_Int32:
- nullValue = FdoInt32Value::Create((FdoInt32)pRaster->GetNullValueData());
+ }
+ case Band::UnsignedInt64:
+ {
+ READELEVATIONDATA(UINT64, FdoInt64Value, GetInt64, FdoDataType_Int64);
break;
- case FdoDataType_Int64:
- nullValue = FdoInt64Value::Create(pRaster->GetNullValueData());
+ }
+ case Band::Int8:
+ {
+ READELEVATIONDATA(INT8, FdoByteValue, GetByte, FdoDataType_Byte);
break;
- default: break;
}
-
- switch (bandDataType)
+ case Band::Int16:
{
- case Band::UnsignedInt8:
- {
- READELEVATIONDATA(UINT8, FdoByteValue, GetByte);
- break;
- }
- case Band::UnsignedInt16:
- {
- READELEVATIONDATA(UINT16, FdoInt16Value, GetInt16);
- break;
- }
- case Band::UnsignedInt32:
- {
- READELEVATIONDATA(UINT32, FdoInt32Value, GetInt32);
- break;
- }
- case Band::UnsignedInt64:
- {
- READELEVATIONDATA(UINT64, FdoInt64Value, GetInt64);
- break;
- }
- case Band::Int8:
- {
- READELEVATIONDATA(INT8, FdoByteValue, GetByte);
- break;
- }
- case Band::Int16:
- {
- READELEVATIONDATA(INT16, FdoInt16Value, GetInt16);
- break;
- }
- case Band::Int32:
- {
- READELEVATIONDATA(INT32, FdoInt32Value, GetInt32);
- break;
- }
- case Band::Int64:
- {
- READELEVATIONDATA(INT64, FdoInt64Value, GetInt64);
- break;
- }
- case Band::Double32:
- {
- READELEVATIONDATA(float, FdoSingleValue, GetSingle);
- break;
- }
- case Band::Double64:
- {
- READELEVATIONDATA(double, FdoSingleValue, GetSingle);
- break;
- }
+ READELEVATIONDATA(INT16, FdoInt16Value, GetInt16, FdoDataType_Int16);
+ break;
}
- if(nullValue)
- nullValue->Release();
- }
- else
- {
- for (unsigned long y = 0; y < actualRows; ++y)
+ case Band::Int32:
{
- //FireOnStepQuery();
- pGisBand->SetRowValue(colStartPos, rowStartPos + y, gridDataType,
- (char*)(pRasterData + y*rowLength), actualCols);
+ READELEVATIONDATA(INT32, FdoInt32Value, GetInt32, FdoDataType_Int32);
+ break;
}
+ case Band::Int64:
+ {
+ READELEVATIONDATA(INT64, FdoInt64Value, GetInt64, FdoDataType_Int64);
+ break;
+ }
+ case Band::Double32:
+ {
+ READELEVATIONDATA(float, FdoSingleValue, GetSingle, FdoDataType_Single);
+ break;
+ }
+ case Band::Double64:
+ {
+ READELEVATIONDATA(double, FdoSingleValue, GetSingle, FdoDataType_Single);
+ break;
+ }
}
+ }
+ else
+ {
+ for (unsigned long y = 0; y < actualRows; ++y)
+ {
+ //FireOnStepQuery();
+ pGisBand->SetRowValue(colStartPos, rowStartPos + y, gridDataType,
+ (char*)(pRasterData + y*rowLength), actualCols);
+ }
+ }
- // TODO: When we get past the Mako crunch, we should revisit and probably remove this code.
- // Leaf, Brett and I agree that we should not completely remove this code at this
- // time. I changed it from 25000 to 30000, so we can map anything above the earth's
- // surface in feet (Mt Everest is 28717 feet in elevation). - lap - 3/10/06
- // When new version of FDO RFP, which modifies resampling algorithm for dem file to BSpline, is avaiable,
- // we will remove code to smooth elevation and use the following code to recover modified NULL value to its original value.
- // NOTE: The smoothing code has been removed.
- if (dataModelType == FdoRasterDataModelType_Data)
+ // TODO: When we get past the Mako crunch, we should revisit and probably remove this code.
+ // Leaf, Brett and I agree that we should not completely remove this code at this
+ // time. I changed it from 25000 to 30000, so we can map anything above the earth's
+ // surface in feet (Mt Everest is 28717 feet in elevation). - lap - 3/10/06
+ // When new version of FDO RFP, which modifies resampling algorithm for dem file to BSpline, is avaiable,
+ // we will remove code to smooth elevation and use the following code to recover modified NULL value to its original value.
+ // NOTE: The smoothing code has been removed.
+ if (dataModelType == FdoRasterDataModelType_Data)
+ {
+ float fNullValue = MG_NODATA_VALUE;
+ double dCurValue(0.0);
+ for (unsigned long y = rowStartPos; (y < actualRows + rowStartPos); ++y)
{
- float fNullValue = MG_NODATA_VALUE;
- double dCurValue(0.0);
- for (unsigned long y = rowStartPos; (y < actualRows + rowStartPos); ++y)
+ //FireOnStepQuery();
+ for (unsigned long x = colStartPos; (x < actualCols + colStartPos); ++x)
{
- //FireOnStepQuery();
- for (unsigned long x = colStartPos; (x < actualCols + colStartPos); ++x)
+ // If elevation value is greater than 30000, we will consider it a NULL value
+ bool ret = pGisBand->GetValueAsDouble(x, y, dCurValue);
+ if (!ret || (dCurValue > 30000.0)||(dCurValue < -30000.0))
{
- // If elevation value is greater than 30000, we will consider it a NULL value
- bool ret = pGisBand->GetValueAsDouble(x, y, dCurValue);
- if (!ret || (dCurValue > 30000.0)||(dCurValue < -30000.0))
- {
- pGisBand->SetValue(x, y, gridDataType, (char*)(&fNullValue));
- }
+ pGisBand->SetValue(x, y, gridDataType, (char*)(&fNullValue));
}
}
}
- else if (dataModelType == FdoRasterDataModelType_Bitonal)
- {
- //Fix DID 885517
- }
}
+ else if (dataModelType == FdoRasterDataModelType_Bitonal)
+ {
+ //Fix DID 885517
+ }
}
- catch(...)
- {
- delete [] pRasterData;
- delete [] pPaletteBuf;
- delete reader;
-
- throw;
- }
-
- delete [] pRasterData;
- delete [] pPaletteBuf;
- delete reader;
}
Band::BandDataType
Modified: branches/4.0/MgDev/Server/src/UnitTesting/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Server/src/UnitTesting/CMakeLists.txt 2026-10-23 20:37:05 UTC (rev 10229)
+++ branches/4.0/MgDev/Server/src/UnitTesting/CMakeLists.txt 2026-10-23 22:11:40 UTC (rev 10230)
@@ -20,7 +20,6 @@
${MG_SERVER_SERVICES_DIR}/Kml
${MG_SERVER_SERVICES_DIR}/Tile
${FDO_INCLUDE_DIR}
- ${FDO_INCLUDE_DIR}/ExpressionEngine
)
set(MgUnitTesting_SRCS
Modified: branches/4.0/MgDev/Server/src/UnitTesting/TestRasterStylization.cpp
===================================================================
--- branches/4.0/MgDev/Server/src/UnitTesting/TestRasterStylization.cpp 2026-10-23 20:37:05 UTC (rev 10229)
+++ branches/4.0/MgDev/Server/src/UnitTesting/TestRasterStylization.cpp 2026-10-23 22:11:40 UTC (rev 10230)
@@ -119,22 +119,16 @@
const Fixture FIXTURES[] =
{
- // These fixtures already render correctly from unmodified source on both
- // Windows and Linux: they only use code paths that are shared between the
- // platforms (SetRowValue plus the colour handler's memcpy fast path), so
- // they define the behavioural baseline the band fixes must not disturb.
- //
- // The 24/48 bpp RGB and palette fixtures are deliberately absent: their
- // bands are only filled by the 32 bit Band::SetValue path, which is a no-op
- // on Linux before the fixes. They are added, with their expected colours,
- // in the change that fixes those paths.
- //
- // The 64 bpp float fixture is absent for a different reason: before the
- // fixes its behaviour depends on the platform. Windows reads one byte of
- // the eight byte sample and paints an opaque black band, while Linux does
- // not style the band at all and the background shows through. It cannot be
- // part of a baseline that must pass on both, and is added by the change
- // that fixes the 8 byte BandData accessor.
+ // The 24/48 bpp branches and the palette branch pack an opaque ARGB value
+ // per pixel, so these fixtures render exactly the colour in the file.
+ { "RasterStylization_rgb24.tif", "24 bpp RGB",
+ { 0x12569A, 0xFF0000, 0x00FF01, 0x804010 } },
+ { "RasterStylization_rgb48.tif", "48 bpp RGB, high byte of each 16 bit sample",
+ { 0x12569A, 0xFF0000, 0x00FF01, 0x804010 } },
+ { "RasterStylization_rgb48_int16.tif", "48 bpp signed RGB, same bit patterns",
+ { 0x12569A, 0xFF0000, 0x00FF01, 0x804010 } },
+ { "RasterStylization_palette8.tif", "8 bpp palette",
+ { 0x12569A, 0x0000FF, 0x00FF01, 0x804010 } },
// The 32 bpp branch copies the source pixels through unchanged, so the
// source alpha survives and the renderer blends the colour over the white
@@ -160,6 +154,10 @@
// pattern happens to contain (0x45, 0x46, 0x46, 0x40).
{ "RasterStylization_gray32f.tif", "32 bpp float single band, samples read as ARGB",
{ 0xE1E5B9, 0xE8FAB8, 0xBFECB7, 0xDDBBBB } },
+ // Doubles are read as unsigned 64 bit samples, which is far outside the
+ // range a packed ARGB value can hold, so no pixel is coloured.
+ { "RasterStylization_gray64f.tif", "64 bpp float single band, not styled",
+ { 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF } },
};
// Real rasters derived from the FDO GDAL provider test data
@@ -173,10 +171,6 @@
// multiple of 64 falls through to RasterAdapter's legacy decoder instead, which
// is not the code under test here - and which has no 16 bpp case at all.
//
-// The palette sheet is absent for the same reason as the palette fixture above:
-// its colour band is only filled through the 32 bit Band::SetValue path, so it
-// renders blank from unmodified source on Linux and is added with the fixes.
-//
// Nothing here has an expected colour: the point is that genuine imagery and
// elevation data survive the pipeline unaltered, so each fixture is rendered to
// ../UnitTestFiles/Results/RealRaster_<name>.png for a human to look at, and the
@@ -214,6 +208,8 @@
StyleGrayBands, 0, 0 },
{ "RealRaster_spif83.tif", "8 bit grey, NAD83 / California zone 6, ~195 m",
StyleGrayBands, 0, 0 },
+ { "RealRaster_cm13ct.tif", "8 bit palette scanned map, UTM 13N",
+ StyleBand, 0, 0 },
// The DEM holds metres (nodata is -32767), so it needs scaling to become a
// visible grey ramp; 0-3000 m covers the whole tile.
{ "RealRaster_mtjefferson.tif", "Int16 USGS DEM as a 0-3000 m elevation ramp",
Modified: branches/4.0/MgDev/Server/src/UnitTesting/TestStylization.cpp
===================================================================
--- branches/4.0/MgDev/Server/src/UnitTesting/TestStylization.cpp 2026-10-23 20:37:05 UTC (rev 10229)
+++ branches/4.0/MgDev/Server/src/UnitTesting/TestStylization.cpp 2026-10-23 22:11:40 UTC (rev 10230)
@@ -163,15 +163,65 @@
return band->GetValue(0, 0, Band::UnsignedInt32, &color);
}
-// The read path rounds each row up to a 64 bit boundary when it is told the
-// provider pads its rows, and gives up when the stream is shorter than that.
-// This is behavioural baseline: it holds on Windows and Linux, before and after
-// the band accessor work.
-TEST_CASE("Grid colour band row alignment", "[Stylization]")
+// Runs a 16 bpp single component Data raster through ReadRaster and reads back
+// pixel (0, 0) of the elevation band. Returns false when no Double32 band was
+// created.
+bool ReadElevationBandPixel(const std::vector<unsigned char>& bytes,
+ int nullValueType,
+ int64_t nullValueData,
+ double& value)
{
const std::vector<unsigned char> noPalette;
+ GridData grid(Point2D(0.0, 0.0), 4.0, 4.0, 4, 4);
+ TestRaster raster(16, FdoRasterDataModelType_Data, FdoRasterDataType_UnsignedInteger,
+ bytes, noPalette, 4, 4, nullValueType, nullValueData);
+
+ grid.ReadRaster(&raster, L"1", 4, 4, 0.0, 0.0, 4.0, 4.0, false);
+
+ Band* band = grid.GetBand((unsigned int)0);
+ if (NULL == band || Band::Double32 != band->GetDataType())
+ return false;
+
+ // GetValue, not GetValueAsDouble: a Data model raster sets the band's null
+ // value to MG_NODATA, so MG_NODATA samples read back as "no value".
+ float sample = 0.0f;
+ if (!band->GetValue(0, 0, Band::Double32, &sample))
+ return false;
+
+ value = (double)sample;
+ return true;
+}
+
+}
+
+TEST_CASE("Grid colour band packing", "[Stylization]")
+{
+ const std::vector<unsigned char> noPalette;
unsigned int color = 0;
+ SECTION("48 bpp RGB reduces 16 bit samples to their high byte")
+ {
+ // R = 0x1234, G = 0x5678, B = 0x9ABC, little endian
+ const unsigned char pixel[] = { 0x34, 0x12, 0x78, 0x56, 0xBC, 0x9A };
+
+ REQUIRE(ReadColourBandPixel(RepeatPixel(pixel, 6, 16), 48, FdoRasterDataModelType_RGB,
+ 4, 4, false, noPalette, color));
+ REQUIRE(0xFF12569Au == color);
+ }
+
+ SECTION("48 bpp RGB discards the low byte of each channel")
+ {
+ const unsigned char white[] = { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00 }; // 0x00FF per channel
+ REQUIRE(ReadColourBandPixel(RepeatPixel(white, 6, 16), 48, FdoRasterDataModelType_RGB,
+ 4, 4, false, noPalette, color));
+ REQUIRE(0xFF000000u == color);
+
+ const unsigned char almostBlack[] = { 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 }; // 0x0100 per channel
+ REQUIRE(ReadColourBandPixel(RepeatPixel(almostBlack, 6, 16), 48, FdoRasterDataModelType_RGB,
+ 4, 4, false, noPalette, color));
+ REQUIRE(0xFF010101u == color);
+ }
+
SECTION("48 bpp RGB with a row length the provider does not pad produces no band")
{
// 6 pixels * 6 bytes = 36 bytes per row, but bAlignment rounds the
@@ -181,144 +231,175 @@
REQUIRE_FALSE(ReadColourBandPixel(RepeatPixel(pixel, 6, 24), 48, FdoRasterDataModelType_RGB,
6, 4, true, noPalette, color));
}
-}
-// Band creation and the narrow (8 and 16 bit) accessors are implemented the
-// same way on both platforms, so they are baseline behaviour. The 32 and 64 bit
-// accessors are covered by the change that implements them on Linux.
-TEST_CASE("Band creation", "[Stylization]")
-{
- GridData grid(Point2D(0.0, 0.0), 3.0, 3.0, 3, 3);
+ SECTION("24 bpp RGB")
+ {
+ const unsigned char pixel[] = { 0x11, 0x22, 0x33 };
- SECTION("UnsignedInt32") { REQUIRE(NULL != grid.CreateBand(Band::UnsignedInt32, L"u32")); }
- SECTION("Int32") { REQUIRE(NULL != grid.CreateBand(Band::Int32, L"i32")); }
- SECTION("Double32") { REQUIRE(NULL != grid.CreateBand(Band::Double32, L"f32")); }
- SECTION("UnsignedInt64") { REQUIRE(NULL != grid.CreateBand(Band::UnsignedInt64, L"u64")); }
- SECTION("Int64") { REQUIRE(NULL != grid.CreateBand(Band::Int64, L"i64")); }
- SECTION("Double64") { REQUIRE(NULL != grid.CreateBand(Band::Double64, L"f64")); }
- SECTION("Boolean") { REQUIRE(NULL != grid.CreateBand(Band::Boolean, L"b")); }
- SECTION("Bit2") { REQUIRE(NULL != grid.CreateBand(Band::Bit2, L"b2")); }
- SECTION("Bit4") { REQUIRE(NULL != grid.CreateBand(Band::Bit4, L"b4")); }
+ REQUIRE(ReadColourBandPixel(RepeatPixel(pixel, 3, 16), 24, FdoRasterDataModelType_RGB,
+ 4, 4, false, noPalette, color));
+ REQUIRE(0xFF112233u == color);
+ }
+
+ SECTION("32 bpp RGBA keeps the source alpha")
+ {
+ const unsigned char pixel[] = { 0x11, 0x22, 0x33, 0x80 };
+
+ REQUIRE(ReadColourBandPixel(RepeatPixel(pixel, 4, 16), 32, FdoRasterDataModelType_RGBA,
+ 4, 4, false, noPalette, color));
+ REQUIRE(0x80112233u == color);
+ }
+
+ SECTION("8 bpp palette")
+ {
+ const std::vector<unsigned char> palette =
+ {
+ 255, 0, 0, 255, // 0 red
+ 0, 255, 0, 255, // 1 green
+ 0, 0, 255, 255, // 2 blue
+ 255, 255, 255, 255 // 3 white
+ };
+ const unsigned char index = 2;
+
+ REQUIRE(ReadColourBandPixel(RepeatPixel(&index, 1, 16), 8, FdoRasterDataModelType_Palette,
+ 4, 4, false, palette, color));
+ REQUIRE(0xFF0000FFu == color);
+ }
}
-TEST_CASE("Band numeric accessors for narrow types", "[Stylization]")
+TEST_CASE("Grid elevation band", "[Stylization]")
{
- GridData grid(Point2D(0.0, 0.0), 3.0, 3.0, 3, 3);
-
- SECTION("UnsignedInt8")
+ // 16 bpp single component Data model: exercises the READELEVATIONDATA path,
+ // including ownership and validation of the null value it is given.
+ std::vector<unsigned char> bytes;
+ for (int i = 0; i < 16; ++i)
{
- Band* band = grid.CreateBand(Band::UnsignedInt8, L"u8");
- REQUIRE(NULL != band);
+ bytes.push_back(0x34);
+ bytes.push_back(0x12); // 0x1234, little endian
+ }
- INT8 value = 100;
- REQUIRE(band->SetValue(1, 1, Band::UnsignedInt8, &value));
+ double value = 0.0;
- INT8 readBack = 0;
- REQUIRE(band->GetValue(1, 1, Band::UnsignedInt8, &readBack));
- REQUIRE(value == readBack);
- REQUIRE(100.0 == band->GetValueAsDouble(1, 1));
+ SECTION("supported null value type")
+ {
+ REQUIRE(ReadElevationBandPixel(bytes, FdoDataType_Int16, 0, value));
+ REQUIRE(4660.0 == value);
}
- SECTION("UnsignedInt8 above 0x7F reads back negative")
+ SECTION("sample equal to the null value becomes MG_NODATA")
{
- // Pre-existing behaviour, recorded so a change to it is visible: the
- // unsigned 8 bit case in Band::GetValueAsDouble loads the sample into an
- // INT8, so anything from 0x80 up is sign extended. This is why paletted
- // and grey rasters with high sample values behave oddly.
- Band* band = grid.CreateBand(Band::UnsignedInt8, L"u8high");
- REQUIRE(NULL != band);
+ REQUIRE(ReadElevationBandPixel(bytes, FdoDataType_Int16, 4660, value));
+ REQUIRE(FLT_MAX == (float)value);
+ }
- INT8 value = (INT8)200;
- REQUIRE(band->SetValue(1, 1, Band::UnsignedInt8, &value));
+ SECTION("null value of an unsupported type keeps every sample")
+ {
+ REQUIRE(ReadElevationBandPixel(bytes, FdoDataType_DateTime, 0, value));
+ REQUIRE(4660.0 == value);
+ }
- INT8 readBack = 0;
- REQUIRE(band->GetValue(1, 1, Band::UnsignedInt8, &readBack));
- REQUIRE(value == readBack);
- REQUIRE(-56.0 == band->GetValueAsDouble(1, 1));
+ SECTION("null value type that does not match the reader keeps every sample")
+ {
+ REQUIRE(ReadElevationBandPixel(bytes, FdoDataType_Double, 0, value));
+ REQUIRE(4660.0 == value);
}
+}
- SECTION("Int8")
+TEST_CASE("Band numeric accessors", "[Stylization]")
+{
+ GridData grid(Point2D(0.0, 0.0), 3.0, 3.0, 3, 3);
+
+ SECTION("UnsignedInt32")
{
- Band* band = grid.CreateBand(Band::Int8, L"i8");
+ Band* band = grid.CreateBand(Band::UnsignedInt32, L"color");
REQUIRE(NULL != band);
- INT8 value = -100;
- REQUIRE(band->SetValue(2, 0, Band::Int8, &value));
+ unsigned int value = 0xFF12569A;
+ REQUIRE(band->SetValue(1, 1, Band::UnsignedInt32, &value));
- INT8 readBack = 0;
- REQUIRE(band->GetValue(2, 0, Band::Int8, &readBack));
+ unsigned int readBack = 0;
+ REQUIRE(band->GetValue(1, 1, Band::UnsignedInt32, &readBack));
REQUIRE(value == readBack);
- REQUIRE(-100.0 == band->GetValueAsDouble(2, 0));
+ REQUIRE(4279391898.0 == band->GetValueAsDouble(1, 1));
}
- SECTION("UnsignedInt16")
+ SECTION("Int32")
{
- Band* band = grid.CreateBand(Band::UnsignedInt16, L"u16");
+ Band* band = grid.CreateBand(Band::Int32, L"int");
REQUIRE(NULL != band);
- INT16 value = (INT16)60000;
- REQUIRE(band->SetValue(0, 2, Band::UnsignedInt16, &value));
+ int value = -123456;
+ REQUIRE(band->SetValue(2, 0, Band::Int32, &value));
- INT16 readBack = 0;
- REQUIRE(band->GetValue(0, 2, Band::UnsignedInt16, &readBack));
+ int readBack = 0;
+ REQUIRE(band->GetValue(2, 0, Band::Int32, &readBack));
REQUIRE(value == readBack);
- REQUIRE(60000.0 == band->GetValueAsDouble(0, 2));
+ REQUIRE(-123456.0 == band->GetValueAsDouble(2, 0));
}
- SECTION("Int16")
+ SECTION("Double32")
{
- Band* band = grid.CreateBand(Band::Int16, L"i16");
+ Band* band = grid.CreateBand(Band::Double32, L"float");
REQUIRE(NULL != band);
- INT16 value = -12345;
- REQUIRE(band->SetValue(1, 0, Band::Int16, &value));
+ float value = 1234.5f;
+ REQUIRE(band->SetValue(0, 2, Band::Double32, &value));
- INT16 readBack = 0;
- REQUIRE(band->GetValue(1, 0, Band::Int16, &readBack));
+ float readBack = 0.0f;
+ REQUIRE(band->GetValue(0, 2, Band::Double32, &readBack));
REQUIRE(value == readBack);
- REQUIRE(-12345.0 == band->GetValueAsDouble(1, 0));
+ REQUIRE(1234.5 == band->GetValueAsDouble(0, 2));
}
- SECTION("Boolean")
+ SECTION("Double64")
{
- Band* band = grid.CreateBand(Band::Boolean, L"bool");
+ Band* band = grid.CreateBand(Band::Double64, L"double");
REQUIRE(NULL != band);
- INT8 value = 1;
- REQUIRE(band->SetValue(0, 0, Band::Boolean, &value));
+ double value = 1.0 / 3.0;
+ REQUIRE(band->SetValue(1, 0, Band::Double64, &value));
- INT8 readBack = 0;
- REQUIRE(band->GetValue(0, 0, Band::Boolean, &readBack));
+ double readBack = 0.0;
+ REQUIRE(band->GetValue(1, 0, Band::Double64, &readBack));
REQUIRE(value == readBack);
- REQUIRE(1.0 == band->GetValueAsDouble(0, 0));
+ REQUIRE(value == band->GetValueAsDouble(1, 0));
}
- SECTION("Bit2")
+ SECTION("Int64")
{
- Band* band = grid.CreateBand(Band::Bit2, L"bit2");
+ Band* band = grid.CreateBand(Band::Int64, L"long");
REQUIRE(NULL != band);
- INT8 value = 3;
- REQUIRE(band->SetValue(2, 2, Band::Bit2, &value));
+ INT64 value = 0x0102030405060708LL;
+ REQUIRE(band->SetValue(0, 1, Band::Int64, &value));
- INT8 readBack = 0;
- REQUIRE(band->GetValue(2, 2, Band::Bit2, &readBack));
+ INT64 readBack = 0;
+ REQUIRE(band->GetValue(0, 1, Band::Int64, &readBack));
REQUIRE(value == readBack);
- REQUIRE(3.0 == band->GetValueAsDouble(2, 2));
}
+}
- SECTION("Bit4")
+TEST_CASE("Band slope from neighbouring values", "[Stylization]")
+{
+ GridData grid(Point2D(0.0, 0.0), 3.0, 3.0, 3, 3);
+ Band* elevation = grid.CreateBand(Band::Double32, L"elevation");
+ REQUIRE(NULL != elevation);
+
+ // A ramp of one unit per cell: at the centre pixel the neighbours are
+ // left = 0, right = 2, top = bottom = 1, so the slope is 45 degrees.
+ for (unsigned int y = 0; y < 3; ++y)
{
- Band* band = grid.CreateBand(Band::Bit4, L"bit4");
- REQUIRE(NULL != band);
+ for (unsigned int x = 0; x < 3; ++x)
+ {
+ float z = (float)x;
+ REQUIRE(elevation->SetValue(x, y, Band::Double32, &z));
+ }
+ }
- INT8 value = 9;
- REQUIRE(band->SetValue(1, 2, Band::Bit4, &value));
+ const Band* slope = elevation->GetSlopeBand();
+ REQUIRE(NULL != slope);
- INT8 readBack = 0;
- REQUIRE(band->GetValue(1, 2, Band::Bit4, &readBack));
- REQUIRE(value == readBack);
- REQUIRE(9.0 == band->GetValueAsDouble(1, 2));
- }
+ double angle = slope->GetValueAsDouble(1, 1);
+ REQUIRE(44.9 < angle);
+ REQUIRE(45.1 > angle);
}
-}
More information about the mapguide-commits
mailing list