Kaydet (Commit) 4c945b22 authored tarafından Noel Grandin's avatar Noel Grandin

new loplugin:methodcycles

look for closed cycles of methods i.e. unused code that would not
otherwise be found by the unusedmethods loplugin

Change-Id: I3fb052132d97aabca573bb8e9fc424201b1e2042
Reviewed-on: https://gerrit.libreoffice.org/60875
Tested-by: Jenkins
Reviewed-by: 's avatarNoel Grandin <noel.grandin@collabora.co.uk>
üst 893f2128
......@@ -341,56 +341,6 @@ void SbxArray::Merge( SbxArray* p )
}
}
// Search of an element via the user data. If the element is
// object, it will also be scanned.
SbxVariable* SbxArray::FindUserData( sal_uInt32 nData )
{
SbxVariable* p = nullptr;
for (auto& rEntry : mVarEntries)
{
if (!rEntry.mpVar.is())
continue;
if (rEntry.mpVar->IsVisible() && rEntry.mpVar->GetUserData() == nData)
{
p = rEntry.mpVar.get();
p->ResetFlag( SbxFlagBits::ExtFound );
break; // JSM 1995-10-06
}
// Did we have an array/object with extended search?
if (rEntry.mpVar->IsSet(SbxFlagBits::ExtSearch))
{
switch (rEntry.mpVar->GetClass())
{
case SbxClassType::Object:
{
// Objects are not allowed to scan their parent.
SbxFlagBits nOld = rEntry.mpVar->GetFlags();
rEntry.mpVar->ResetFlag(SbxFlagBits::GlobalSearch);
p = static_cast<SbxObject&>(*rEntry.mpVar).FindUserData(nData);
rEntry.mpVar->SetFlags(nOld);
}
break;
case SbxClassType::Array:
// Casting SbxVariable to SbxArray? Really?
p = reinterpret_cast<SbxArray&>(*rEntry.mpVar).FindUserData(nData);
break;
default:
;
}
if (p)
{
p->SetFlag(SbxFlagBits::ExtFound);
break;
}
}
}
return p;
}
// Search of an element by his name and type. If an element is an object,
// it will also be scanned..
......
......@@ -87,19 +87,6 @@ void SbxCollection::Initialize()
p->SetFlag( SbxFlagBits::DontStore );
}
SbxVariable* SbxCollection::FindUserData( sal_uInt32 nData )
{
if( GetParameters() )
{
SbxObject* pObj = static_cast<SbxObject*>(GetObject());
return pObj ? pObj->FindUserData( nData ) : nullptr;
}
else
{
return SbxObject::FindUserData( nData );
}
}
SbxVariable* SbxCollection::Find( const OUString& rName, SbxClassType t )
{
if( GetParameters() )
......
......@@ -175,38 +175,6 @@ bool SbxObject::IsClass( const OUString& rName ) const
return aClassName.equalsIgnoreAsciiCase( rName );
}
SbxVariable* SbxObject::FindUserData( sal_uInt32 nData )
{
SbxVariable* pRes = pMethods->FindUserData( nData );
if( !pRes )
{
pRes = pProps->FindUserData( nData );
}
if( !pRes )
{
pRes = pObjs->FindUserData( nData );
}
// Search in the parents?
if( !pRes && IsSet( SbxFlagBits::GlobalSearch ) )
{
SbxObject* pCur = this;
while( !pRes && pCur->pParent )
{
// I myself was already searched!
SbxFlagBits nOwn = pCur->GetFlags();
pCur->ResetFlag( SbxFlagBits::ExtSearch );
// I search already global!
SbxFlagBits nPar = pCur->pParent->GetFlags();
pCur->pParent->ResetFlag( SbxFlagBits::GlobalSearch );
pRes = pCur->pParent->FindUserData( nData );
pCur->SetFlags( nOwn );
pCur->pParent->SetFlags( nPar );
pCur = pCur->pParent;
}
}
return pRes;
}
SbxVariable* SbxObject::Find( const OUString& rName, SbxClassType t )
{
#ifdef DBG_UTIL
......
This diff is collapsed.
#!/usr/bin/python
from collections import defaultdict
import io
import re
import subprocess
import sys
# --------------------------------------------------------------------------------------------
# globals
# --------------------------------------------------------------------------------------------
definitionSet = set() # set of method_name
definitionToSourceLocationMap = dict()
# for the "unused methods" analysis
callDict = defaultdict(set) # map of from_method_name -> set(method_name)
# clang does not always use exactly the same numbers in the type-parameter vars it generates
# so I need to substitute them to ensure we can match correctly.
normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+")
def normalizeTypeParams( line ):
line = normalizeTypeParamsRegex.sub("type-parameter-?-?", line)
# make some of the types a little prettier
line = line.replace("std::__debug", "std::")
line = line.replace("class ", "")
line = line.replace("struct ", "")
line = line.replace("_Bool", "bool")
return line
# --------------------------------------------------------------------------------------------
# primary input loop
# --------------------------------------------------------------------------------------------
cnt = 0
with io.open("workdir/loplugin.methodcycles.log2", "rb", buffering=1024*1024) as txt:
for line in txt:
tokens = line.strip().split("\t")
if tokens[0] == "definition:":
returnType = tokens[1]
nameAndParams = tokens[2]
sourceLocation = tokens[3]
funcInfo = (normalizeTypeParams(returnType) + " " + normalizeTypeParams(nameAndParams)).strip()
definitionSet.add(funcInfo)
definitionToSourceLocationMap[funcInfo] = sourceLocation
elif tokens[0] == "call:":
returnTypeFrom = tokens[1]
nameAndParamsFrom = tokens[2]
returnTypeTo = tokens[3]
nameAndParamsTo = tokens[4]
caller = (normalizeTypeParams(returnTypeFrom) + " " + normalizeTypeParams(nameAndParamsFrom)).strip()
callee = (normalizeTypeParams(returnTypeTo) + " " + normalizeTypeParams(nameAndParamsTo)).strip()
callDict[caller].add(callee)
else:
print( "unknown line: " + line)
cnt = cnt + 1
#if cnt > 100000: break
# Invert the definitionToSourceLocationMap.
# If we see more than one method at the same sourceLocation, it's being autogenerated as part of a template
# and we should just ignore it.
#sourceLocationToDefinitionMap = {}
#for k, v in definitionToSourceLocationMap.iteritems():
# sourceLocationToDefinitionMap[v] = sourceLocationToDefinitionMap.get(v, [])
# sourceLocationToDefinitionMap[v].append(k)
#for k, definitions in sourceLocationToDefinitionMap.iteritems():
# if len(definitions) > 1:
# for d in definitions:
# definitionSet.remove(d)
# sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
return [int(text) if text.isdigit() else text.lower()
for text in re.split(_nsre, s)]
def sort_set_by_natural_key(s):
return sorted(s, key=lambda v: natural_sort_key(v[1]))
# --------------------------------------------------------------------------------------------
# analysis
# --------------------------------------------------------------------------------------------
# follow caller-callee chains, removing all methods reachable from a root method
def remove_reachable(startCaller):
worklist = list()
worklist.append(startCaller)
while len(worklist) > 0:
caller = worklist.pop()
if not caller in callDict:
continue
calleeSet = callDict[caller]
del callDict[caller]
if caller in definitionSet:
definitionSet.remove(caller)
for c in calleeSet:
worklist.append(c)
# look for all the external entry points and remove code called from there
to_be_removed = set()
to_be_removed.add("int main(int,char **)")
# random dynload entrypoints that we don't otherwise find
to_be_removed.add("bool TestImportOLE2(SvStream &)")
to_be_removed.add("void SbiRuntime::StepREDIMP()")
to_be_removed.add("_object * (anonymous namespace)::createUnoStructHelper(_object *,_object *,_object *)");
for caller in definitionSet:
if not caller in definitionToSourceLocationMap:
to_be_removed.append(caller)
continue
location = definitionToSourceLocationMap[caller]
if "include/com/" in location \
or "include/cppu/" in location \
or "include/cppuhelper/" in location \
or "include/osl/" in location \
or "include/rtl/" in location \
or "include/sal/" in location \
or "include/salhelper/" in location \
or "include/systools/" in location \
or "include/typelib/" in location \
or "include/uno/" in location \
or "workdir/UnpackedTarball/" in location \
or "workdir/UnoApiHeadersTarget/" in location \
or "workdir/CustomTarget/officecfg/" in location \
or "workdir/LexTarget/" in location \
or "workdir/CustomTarget/i18npool/localedata/" in location \
or "workdir/SdiTarget/" in location \
or "/qa/" in location \
or "include/test/" in location:
to_be_removed.add(caller)
# TODO calls to destructors are not mentioned in the AST, so we'll just have to assume they get called,
# which is not ideal
if "::~" in caller:
to_be_removed.add(caller)
# dyload entry points for VCL builder
if "(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)" in caller:
to_be_removed.add(caller)
if "(VclPtr<vcl::Window> &,VclPtr<vcl::Window> &,std::::map<rtl::OString, rtl::OUString, std::less<rtl::OString>, std::allocator<std::pair<const rtl::OString, rtl::OUString> > > &)" in caller:
to_be_removed.add(caller)
# find all the UNO load-by-symbol-name entrypoints
uno_constructor_entrypoints = set()
git_grep_process = subprocess.Popen("git grep -h 'constructor=' -- *.component", stdout=subprocess.PIPE, shell=True)
with git_grep_process.stdout as txt:
for line in txt:
idx1 = line.find("\"")
idx2 = line.find("\"", idx1 + 1)
func = line[idx1+1 : idx2]
uno_constructor_entrypoints.add(func)
for caller in callDict:
if "(com::sun::star::uno::XComponentContext *,const com::sun::star::uno::Sequence<com::sun::star::uno::Any> &)" in caller:
for func in uno_constructor_entrypoints:
if func in caller:
to_be_removed.add(caller)
# remove everything reachable from the found entry points
for caller in to_be_removed:
remove_reachable(caller)
for caller in callDict:
callDict[caller] -= to_be_removed
print_tree_recurse_set = set() # protect against cycles
def print_tree(f, caller, depth):
if depth == 0:
f.write("\n") # add an empty line before each tree
print_tree_recurse_set.clear()
# protect against cycles
if caller in print_tree_recurse_set:
return
# when printing out trees, things that are not in the map are things that are reachable,
# so we're not interested in them
if not caller in callDict:
return
print_tree_recurse_set.add(caller)
f.write(" " * depth + caller + "\n")
f.write(" " * depth + definitionToSourceLocationMap[caller] + "\n")
calleeSet = callDict[caller]
for c in calleeSet:
print_tree(f, c, depth+1)
# create a reverse call graph
inverseCallDict = defaultdict(set) # map of from_method_name -> set(method_name)
for caller in callDict:
for callee in callDict[caller]:
inverseCallDict[callee].add(caller)
# find possible roots (ie. entrypoints) by looking for methods that are not called
def dump_possible_roots():
possibleRootList = list()
for caller in callDict:
if not caller in inverseCallDict and caller in definitionToSourceLocationMap:
possibleRootList.append(caller)
possibleRootList.sort()
# print out first 100 trees of caller->callees
count = 0
with open("compilerplugins/clang/methodcycles.roots", "wt") as f:
f.write("callDict size " + str(len(callDict)) + "\n")
f.write("possibleRootList size " + str(len(possibleRootList)) + "\n")
f.write("\n")
for caller in possibleRootList:
f.write(caller + "\n")
f.write(" " + definitionToSourceLocationMap[caller] + "\n")
#print_tree(f, caller, 0)
count = count + 1
#if count>1000: break
# Look for cycles in a directed graph
# Adapted from:
# https://codereview.stackexchange.com/questions/86021/check-if-a-directed-graph-contains-a-cycle
with open("compilerplugins/clang/methodcycles.results", "wt") as f:
path = set()
visited = set()
def printPath(path):
if len(path) < 2:
return
# we may have found a cycle, but if the cycle is called from outside the cycle
# the code is still in use.
for p in path:
for caller in inverseCallDict[p]:
if not caller in path:
return
f.write("found cycle\n")
for p in path:
f.write(" " + p + "\n")
f.write(" " + definitionToSourceLocationMap[p] + "\n")
f.write("\n")
def checkCyclic(vertex):
if vertex in visited:
return
visited.add(vertex)
path.add(vertex)
if vertex in callDict:
for neighbour in callDict[vertex]:
if neighbour in path:
printPath(path)
break
else:
checkCyclic(neighbour)
path.remove(vertex)
for caller in callDict:
checkCyclic(caller)
found cycle
bool connectivity::OSQLParseTreeIterator::impl_getColumnTableRange(const connectivity::OSQLParseNode *,rtl::OUString &) const
include/connectivity/sqliterator.hxx:272
bool connectivity::OSQLParseTreeIterator::getColumnTableRange(const connectivity::OSQLParseNode *,rtl::OUString &) const
include/connectivity/sqliterator.hxx:259
found cycle
void (anonymous namespace)::traceValue(_typelib_TypeDescriptionReference *,void *)
cppu/source/LogBridge/LogBridge.cxx:132
void uno_ext_getMapping(_uno_Mapping **,_uno_Environment *,_uno_Environment *)
cppu/source/UnsafeBridge/UnsafeBridge.cxx:133
void LogProbe(bool,void *,void *,_typelib_TypeDescriptionReference *,_typelib_MethodParameter *,int,const _typelib_TypeDescription *,void *,void **,_uno_Any **)
cppu/source/LogBridge/LogBridge.cxx:192
found cycle
rtl::OUString lcl_dbg_out(SwNodes &)
sw/source/core/doc/dbgoutsw.cxx:743
void lcl_dbg_nodes_inner(rtl::OUString &,SwNodes &,unsigned long &)
sw/source/core/doc/dbgoutsw.cxx:694
const char * dbg_out(SwNodes &)
sw/inc/dbgoutsw.hxx:67
found cycle
void OutputDevice::DrawPixel(const tools::Polygon &,const Color &)
include/vcl/outdev.hxx:710
void OutputDevice::DrawPixel(const tools::Polygon &,const Color *)
include/vcl/outdev.hxx:709
found cycle
void SbxVariable::Dump(SvStream &,bool)
include/basic/sbxvar.hxx:260
void SbxObject::Dump(SvStream &,bool)
include/basic/sbxobj.hxx:81
void SbRtl_DumpAllObjects(StarBASIC *,SbxArray &,bool)
basic/source/inc/rtlproto.hxx:293
found cycle
SbxVariable * SbxArray::FindUserData(unsigned int)
include/basic/sbx.hxx:138
SbxVariable * SbxObject::FindUserData(unsigned int)
include/basic/sbxobj.hxx:60
found cycle
unsigned long slideshow::internal::hash::operator()(const type-parameter-?-? &) const
slideshow/source/inc/tools.hxx:83
unsigned long com::sun::star::uno::hash_value(const Reference<type-parameter-?-?> &)
slideshow/source/inc/tools.hxx:93
found cycle
void ScDPResultDimension::DumpState(const ScDPResultMember *,ScDocument *,ScAddress &) const
sc/inc/dptabres.hxx:583
void ScDPResultMember::DumpState(const ScDPResultMember *,ScDocument *,ScAddress &) const
sc/inc/dptabres.hxx:413
......@@ -153,14 +153,19 @@ MyFuncInfo UnusedMethods::niceName(const FunctionDecl* functionDecl)
aInfo.returnType = "";
}
if (const CXXMethodDecl* methodDecl = dyn_cast<CXXMethodDecl>(functionDecl)) {
if (auto methodDecl = dyn_cast<CXXMethodDecl>(functionDecl)) {
const CXXRecordDecl* recordDecl = methodDecl->getParent();
aInfo.nameAndParams += recordDecl->getQualifiedNameAsString();
aInfo.nameAndParams += "::";
aInfo.nameAndParams = recordDecl->getQualifiedNameAsString()
+ "::"
+ functionDecl->getNameAsString()
+ "(";
if (methodDecl->isVirtual())
aInfo.virtualness = "virtual";
}
aInfo.nameAndParams += functionDecl->getNameAsString() + "(";
else
{
aInfo.nameAndParams = functionDecl->getQualifiedNameAsString() + "(";
}
bool bFirst = true;
for (const ParmVarDecl *pParmVarDecl : compat::parameters(*functionDecl)) {
if (bFirst)
......
......@@ -780,73 +780,6 @@ void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef,
}
bool OSQLParseTreeIterator::getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const
{
OUString tmp;
if(impl_getColumnTableRange(pNode, tmp))
{
rTableRange = tmp;
return true;
}
else
return false;
}
bool OSQLParseTreeIterator::impl_getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const
{
// See if all columns belong to one table
if (SQL_ISRULE(pNode,column_ref))
{
OUString aColName, aTableRange;
getColumnRange(pNode, aColName, aTableRange);
if (aTableRange.isEmpty()) // None found
{
// Look for the columns in the tables
for (auto const& table : *m_pImpl->m_pTables)
{
if (table.second.is())
{
try
{
Reference< XNameAccess > xColumns = table.second->getColumns();
if(xColumns->hasByName(aColName))
{
Reference< XPropertySet > xColumn;
if (xColumns->getByName(aColName) >>= xColumn)
{
OSL_ENSURE(xColumn.is(),"Column isn't a propertyset!");
aTableRange = table.first;
break;
}
}
}
catch(Exception&)
{
}
}
}
if (aTableRange.isEmpty())
return false;
}
if (rTableRange.isEmpty())
rTableRange = aTableRange;
else if (rTableRange != aTableRange)
return false;
}
else
{
for (sal_uInt32 i = 0, ncount = pNode->count(); i < ncount; i++)
{
if (!getColumnTableRange(pNode->getChild(i), rTableRange))
return false;
}
}
return true;
}
void OSQLParseTreeIterator::traverseCreateColumns(const OSQLParseNode* pSelectNode)
{
// aIteratorStatus.Clear();
......
......@@ -135,7 +135,6 @@ public:
void Merge( SbxArray* );
OUString GetAlias( sal_uInt16 );
void PutAlias( const OUString&, sal_uInt16 );
SbxVariable* FindUserData( sal_uInt32 nUserData );
SbxVariable* Find( const OUString&, SbxClassType );
// Additional methods for 32-bit indices
......@@ -212,7 +211,6 @@ public:
SbxCollection();
SbxCollection( const SbxCollection& );
SbxCollection& operator=( const SbxCollection& );
virtual SbxVariable* FindUserData( sal_uInt32 nUserData ) override;
virtual SbxVariable* Find( const OUString&, SbxClassType ) override;
virtual void Clear() override;
};
......
......@@ -57,7 +57,6 @@ public:
SbxProperty* GetDfltProperty();
void SetDfltProperty( const OUString& r );
// Search for an element
virtual SbxVariable* FindUserData( sal_uInt32 nUserData );
virtual SbxVariable* Find( const OUString&, SbxClassType );
SbxVariable* FindQualified( const OUString&, SbxClassType );
// Quick-Call-Interface for Methods
......
......@@ -255,9 +255,6 @@ namespace connectivity
OUString &_rColumnName,
OUString& _rTableRange);
// empty if ambiguous
bool getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const;
// return true when the tableNode is a rule like catalog_name, schema_name or table_name
static bool isTableNode(const OSQLParseNode* _pTableNode);
......@@ -268,8 +265,6 @@ namespace connectivity
::std::vector< TNodePair >& getJoinConditions() const;
private:
// helper to implement getColumnTableRange
bool impl_getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const;
/** traverses the list of table names, and fills _rTables
*/
......
......@@ -706,8 +706,6 @@ public:
void DrawPixel( const Point& rPt );
void DrawPixel( const Point& rPt, const Color& rColor );
void DrawPixel( const tools::Polygon& rPts, const Color* pColors );
void DrawPixel( const tools::Polygon& rPts, const Color& rColor );
Color GetPixel( const Point& rPt ) const;
///@}
......
......@@ -64,7 +64,6 @@ SW_DLLPUBLIC const char * dbg_out(const SwpHints &rHints);
SW_DLLPUBLIC const char * dbg_out(const SfxPoolItem & rItem);
SW_DLLPUBLIC const char * dbg_out(const SfxPoolItem * pItem);
SW_DLLPUBLIC const char * dbg_out(const SfxItemSet & rSet);
SW_DLLPUBLIC const char * dbg_out(SwNodes & rNodes);
SW_DLLPUBLIC const char * dbg_out(const SwPosition & rPos);
SW_DLLPUBLIC const char * dbg_out(const SwPaM & rPam);
SW_DLLPUBLIC const char * dbg_out(const SwNodeNum & rNum);
......
......@@ -691,77 +691,6 @@ const char * dbg_out(const SwTextNode * pNode)
return nullptr;
}
static void lcl_dbg_nodes_inner(OUString & aStr, SwNodes & rNodes, sal_uLong & nIndex)
{
SwNode * pNode = rNodes[nIndex];
SwStartNode * pStartNode = dynamic_cast<SwStartNode *> (pNode);
SwNode * pEndNode = nullptr;
if (pStartNode != nullptr)
pEndNode = pStartNode->EndOfSectionNode();
sal_uLong nCount = rNodes.Count();
sal_uLong nStartIndex = nIndex;
bool bDone = false;
OUString aTag;
if (pNode->IsTableNode())
aTag += "table";
else if (pNode->IsSectionNode())
aTag += "section";
else
aTag += "nodes";
aStr += "<";
aStr += aTag;
aStr += ">";
while (! bDone)
{
if (pNode->IsStartNode() && nIndex != nStartIndex)
lcl_dbg_nodes_inner(aStr, rNodes, nIndex);
else
{
aStr += lcl_dbg_out(*pNode);
aStr += "\n";
nIndex++;
}
if (pNode == pEndNode || nIndex >= nCount)
bDone = true;
else
pNode = rNodes[nIndex];
}
aStr += "</";
aStr += aTag;
aStr += ">\n";
}
static OUString lcl_dbg_out(SwNodes & rNodes)
{
OUString aStr("<nodes-array>");
sal_uLong nIndex = 0;
sal_uLong nCount = rNodes.Count();
while (nIndex < nCount)
{
lcl_dbg_nodes_inner(aStr, rNodes, nIndex);
}
aStr += "</nodes-array>\n";
return aStr;
}
const char * dbg_out(SwNodes & rNodes)
{
return dbg_out(lcl_dbg_out(rNodes));
}
static OUString lcl_dbg_out(const SwUndo & rUndo)
{
return "[ " + OUString::number(static_cast<int>(rUndo.GetId()))
......
......@@ -110,71 +110,4 @@ void OutputDevice::DrawPixel( const Point& rPt, const Color& rColor )
mpAlphaVDev->DrawPixel( rPt );
}
void OutputDevice::DrawPixel( const tools::Polygon& rPts, const Color* pColors )
{
assert(!is_double_buffered_window());
if ( !pColors )
{
DrawPixel( rPts, GetLineColor() );
}
else
{
SAL_WARN_IF( !pColors, "vcl", "OutputDevice::DrawPixel: No color array specified" );
const sal_uInt16 nSize = rPts.GetSize();
if ( nSize )
{
if ( mpMetaFile )
{
for ( sal_uInt16 i = 0; i < nSize; i++ )
{
mpMetaFile->AddAction( new MetaPixelAction( rPts[ i ], pColors[ i ] ) );
}
}
if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
return;
if ( mpGraphics || AcquireGraphics() )
{
if ( mbInitClipRegion )
InitClipRegion();
if ( mbOutputClipped )
return;
for ( sal_uInt16 i = 0; i < nSize; i++ )
{
const Point aPt( ImplLogicToDevicePixel( rPts[ i ] ) );
mpGraphics->DrawPixel( aPt.X(), aPt.Y(), pColors[ i ], this );
}
}
}
}
if( mpAlphaVDev )
mpAlphaVDev->DrawPixel( rPts, pColors );
}
void OutputDevice::DrawPixel( const tools::Polygon& rPts, const Color& rColor )
{
assert(!is_double_buffered_window());
if( rColor != COL_TRANSPARENT && ! ImplIsRecordLayout() )
{
const sal_uInt16 nSize = rPts.GetSize();
std::unique_ptr<Color[]> pColArray(new Color[ nSize ]);
for( sal_uInt16 i = 0; i < nSize; i++ )
{
pColArray[ i ] = rColor;
}
DrawPixel( rPts, pColArray.get() );
}
if( mpAlphaVDev )
mpAlphaVDev->DrawPixel( rPts, rColor );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment