Magic原始碼閱讀(三)——資料匯入和構建
我們之前已經瞭解了magic的角勾鏈資料結構和GDSII版圖檔案,那麼我們的magic是如何實現將GDSII資料庫檔案構建為基於角勾鏈這種資料結構的圖的呢?
Magic構圖方法
無論是從命令列讀入GDSII檔案,還是在軟體中以UI的方式開啟檔案,最終都是觸發的CmdCalma(MagWindow *w, TxCommand *cmd)
這個函式,當命令為read file時,我們會觸發函式switch分支中
case CALMA_READ:
if (cmd->tx_argc != 3) goto wrongNumArgs;
/* Check for various common file extensions, including */
/* no extension (as-is), ".gds", ".gds2", and ".strm". */
for (ext = 0; gdsExts[ext] != NULL; ext++)
if ((f = PaOpen(cmd->tx_argv[2], "r", gdsExts[ext], Path,
(char *) NULL, &namep)) != (FILE *)NULL)
break;
if (f == (FILE *) NULL)
{
TxError("Cannot open %s.gds, %s.strm or %s to read "
"GDS-II stream input.\n",
cmd->tx_argv[2], cmd->tx_argv[2], cmd->tx_argv[2]);
return;
}
CalmaReadFile(f, namep);
(void) fclose(f);
return;
這個分支,程式碼還是比較好懂的,先進行一系列的開啟嘗試,開啟失敗返回相應的錯誤,開啟成功後,我們發現呼叫了CalmaReadFile()
這個函式,最後不要忘記關閉檔案流。
我們看一下CalmaReadFile()
這個函式。
void
CalmaReadFile(file, filename)
FILE *file; /* File from which to read Calma */
char *filename; /* The real name of the file read */
{
int k, version;
char *libname = NULL;
MagWindow *mw;
static int hdrSkip[] = { CALMA_FORMAT, CALMA_MASK, CALMA_ENDMASKS,
CALMA_REFLIBS, CALMA_FONTS, CALMA_ATTRTABLE,
CALMA_STYPTABLE, CALMA_GENERATIONS, -1 };
static int skipBeforeLib[] = { CALMA_LIBDIRSIZE, CALMA_SRFNAME,
CALMA_LIBSECUR, -1 };
/* We will use full cell names as keys in this hash table */
CIFReadCellInit(0);
if (CIFWarningLevel == CIF_WARN_REDIRECT)
{
if (CIFErrorFilename == NULL)
calmaErrorFile = NULL;
else
calmaErrorFile = PaOpen(CIFErrorFilename, "w", (char *)NULL, ".",
(char *)NULL, (char **)NULL);
}
if (cifCurReadStyle == NULL)
{
TxError("Don't know how to read GDS-II:\n");
TxError("Nothing in \"cifinput\" section of tech file.\n");
return;
}
TxPrintf("Warning: Calma reading is not undoable! I hope that's OK.\n");
UndoDisable();
calmaTotalErrors = 0;
CalmaPolygonCount = 0;
CalmaPathCount = 0;
/* 我們建立一個hash表來維護structure名字與模組的對映,加速查詢 */
HashInit(&calmaDefInitHash, 32, 0);
calmaLApresent = FALSE;
calmaInputFile = file;
/* 讀取GDS-II的檔案頭*/
if (!calmaReadI2Record(CALMA_HEADER, &version)) goto done;
if (version < 600)
TxPrintf("Library written using GDS-II Release %d.0\n", version);
else
TxPrintf("Library written using GDS-II Release %d.%d\n",
version / 100, version % 100);
if (!calmaSkipExact(CALMA_BGNLIB)) goto done;
calmaSkipSet(skipBeforeLib);
if (!calmaReadStringRecord(CALMA_LIBNAME, &libname)) goto done;
if ((libname != NULL) && (libname[0] != '\0'))
{
/* 避免名字中出現空格,將空格替換為_*/
for (k = 0; k < strlen(libname); k++)
if (libname[k] == ' ')
libname[k] = '_';
TxPrintf("Library name: %s\n", libname);
}
/* Skip the reflibs, fonts, etc. cruft */
calmaSkipSet(hdrSkip);
/* Set the scale factors */
if (!calmaParseUnits()) goto done;
/*###################### 用一個while迴圈讀取GDS-II檔案的主體 ###################*/
while (calmaParseStructure(filename))
if (SigInterruptPending)
goto done;
(void) calmaSkipExact(CALMA_ENDLIB);
done:
/* 顯示在window中 */
if (libname != NULL)
{
mw = CmdGetRootPoint((Point *)NULL, (Rect *)NULL);
if (mw == NULL)
windCheckOnlyWindow(&mw, DBWclientID);
if (mw != NULL)
{
if (calmaLookCell(libname, NULL) != (CellDef *)NULL)
DBWloadWindow(mw, libname, FALSE, FALSE);
}
freeMagic(libname);
}
CIFReadCellCleanup(FILE_CALMA);
HashKill(&calmaDefInitHash);
UndoEnable();
if (calmaErrorFile != NULL) fclose(calmaErrorFile);
}
然後我們又找到了更底一層的核心函式calmaParseStructure()
,讀取檔案中的單個structure。我們繼續往裡看:
bool
calmaParseStructure(filename)
char *filename; /* Name of the GDS file read */
{
static int structs[] = { CALMA_STRCLASS, CALMA_STRTYPE, -1 };
int nbytes, rtype, nsrefs, osrefs, npaths;
char *strname = NULL;
HashEntry *he;
int suffix;
int mfactor;
off_t filepos;
bool was_called;
bool was_initialized;
CellDef *def;
/* 各種校驗。。。 */
PEEKRH(nbytes, rtype);
if (nbytes <= 0 || rtype != CALMA_BGNSTR)
return (FALSE);
/* Read the structure name */
was_initialized = FALSE;
if (!calmaSkipExact(CALMA_BGNSTR)) goto syntaxerror;
if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
TxPrintf("Reading \"%s\".\n", strname);
if (CalmaReadOnly)
filepos = ftello(calmaInputFile);
/* Set up the cell definition */
he = HashFind(&calmaDefInitHash, strname);
if ((def = (CellDef *)HashGetValue(he)) != NULL) //已經定義該name
{
if (def->cd_flags & CDPROCESSEDGDS) //可以直接返回失敗
{
if (!CalmaPostOrder)
{
CalmaReadError("Cell \"%s\" was already defined in this file.\n",
strname);
CalmaReadError("Ignoring duplicate definition\n");
}
calmaNextCell();
return TRUE;
}
else //也可以在這裡修改name
{
char *newname;
CalmaReadError("Cell \"%s\" was already defined in this file.\n",
strname);
newname = (char *)mallocMagic(strlen(strname) + 20);
for (suffix = 1; HashGetValue(he) != NULL; suffix++)
{
(void) sprintf(newname, "%s_%d", strname, suffix);
he = HashFind(&calmaDefInitHash, newname);
}
CalmaReadError("Giving this cell a new name: %s\n", newname);
freeMagic(strname);
strname = mallocMagic(strlen(newname) + 1);
strcpy(strname, newname);
freeMagic(newname);
}
}
cifReadCellDef = calmaFindCell(strname, &was_called);
DBCellClearDef(cifReadCellDef);
DBCellSetAvail(cifReadCellDef);
HashSetValue(he, cifReadCellDef);
cifCurReadPlanes = cifSubcellPlanes;
cifReadCellDef->cd_flags &= ~CDDEREFERENCE;
/* Done with strname */
if (strname != NULL) freeMagic(strname);
/* For read-only cells, set flag in def */
if (CalmaReadOnly)
cifReadCellDef->cd_flags |= CDVENDORGDS;
/* Skip CALMA_STRCLASS or CALMA_STRTYPE */
calmaSkipSet(structs);
/* Initialize the hash table for layer errors */
HashInit(&calmaLayerHash, 32, sizeof (CalmaLayerType) / sizeof (unsigned));
was_initialized = TRUE;
/*######################## 解析structure中的一個個element圖素 #################**/
osrefs = nsrefs = 0;
npaths = 0;
calmaNonManhattan = 0;
while (calmaParseElement(filename, &nsrefs, &npaths))
{
if (SigInterruptPending)
goto done;
if (nsrefs > osrefs && (nsrefs % 100) == 0)
TxPrintf(" %d uses\n", nsrefs);
osrefs = nsrefs;
calmaNonManhattan = 0;
}
if (CalmaReadOnly)
{
/* Writing the file position into a string is slow, but */
/* it prevents requiring special handling when printing */
/* out the properties. */
char *fpcopy = (char *)mallocMagic(20);
char *fncopy = StrDup(NULL, filename);
sprintf(fpcopy, "%"DLONG_PREFIX"d", (dlong) filepos);
DBPropPut(cifReadCellDef, "GDS_START", (ClientData)fpcopy);
fpcopy = (char *)mallocMagic(20);
filepos = ftello(calmaInputFile);
sprintf(fpcopy, "%"DLONG_PREFIX"d", (dlong) filepos);
DBPropPut(cifReadCellDef, "GDS_END", (ClientData)fpcopy);
DBPropPut(cifReadCellDef, "GDS_FILE", (ClientData)fncopy);
/* Do not lock the cell, or else we can't save the */
/* magic cell with its GDS pointers to disk. . . */
/* cifReadCellDef->cd_flags |= CDNOEDIT; */
}
/* Make sure it ends with an ENDSTR record */
if (!calmaSkipExact(CALMA_ENDSTR)) goto syntaxerror;
/*
* 不會立刻繪製,當被instanced時再繪製
*/
if (CalmaFlattenUses && (!was_called) && (npaths < CalmaFlattenLimit)
&& (nsrefs == 0))
{
/* If CDFLATGDS is already set, may need to remove */
/* existing planes and free memory. */
if ((cifReadCellDef->cd_client != (ClientData)CLIENTDEFAULT) &&
(cifReadCellDef->cd_flags & CDFLATGDS))
{
Plane **cifplanes = (Plane **)cifReadCellDef->cd_client;
int pNum;
for (pNum = 0; pNum < MAXCIFRLAYERS; pNum++)
{
if (cifplanes[pNum] != NULL)
{
DBFreePaintPlane(cifplanes[pNum]);
TiFreePlane(cifplanes[pNum]);
}
}
freeMagic((char *)cifReadCellDef->cd_client);
cifReadCellDef->cd_client = (ClientData)CLIENTDEFAULT;
}
TxPrintf("Saving contents of cell %s\n", cifReadCellDef->cd_name);
cifReadCellDef->cd_client = (ClientData) calmaExact();
cifReadCellDef->cd_flags |= CDFLATGDS;
cifReadCellDef->cd_flags &= ~CDFLATTENED;
}
else
{
/*
* Do the geometrical processing and paint this material back into
* the appropriate cell of the database.
*/
CIFPaintCurrent(FILE_CALMA);
}
DBAdjustLabelsNew(cifReadCellDef, &TiPlaneRect,
(cifCurReadStyle->crs_flags & CRF_NO_RECONNECT_LABELS) ? 1 : 0);
DBReComputeBbox(cifReadCellDef);
/* Don't bother to register with DRC if we're going to delete the */
/* cell, or if the cell is read-only, or if "calma drcnocheck true" */
/* has been issued. */
if (!CalmaReadOnly && !CalmaNoDRCCheck)
DRCCheckThis(cifReadCellDef, TT_CHECKPAINT, &cifReadCellDef->cd_bbox);
DBWAreaChanged(cifReadCellDef, &cifReadCellDef->cd_bbox,
DBW_ALLWINDOWS, &DBAllButSpaceBits);
DBCellSetModified(cifReadCellDef, TRUE);
/*
* Assign use-identifiers to all the cell uses.
* These identifiers are generated so as to be
* unique.
*/
DBGenerateUniqueIds(cifReadCellDef, FALSE);
cifReadCellDef->cd_flags |= CDPROCESSEDGDS;
done:
HashKill(&calmaLayerHash);
return (TRUE);
/* Syntax error: skip to CALMA_ENDSTR */
syntaxerror:
if (was_initialized == TRUE) HashKill(&calmaLayerHash);
return (calmaSkipTo(CALMA_ENDSTR));
}
接下來是calmaParseElement(filename, &nsrefs, &npaths)
,解析單個圖素
bool
calmaParseElement(filename, pnsrefs, pnpaths)
char *filename;
int *pnsrefs, *pnpaths;
{
static int node[] = { CALMA_ELFLAGS, CALMA_PLEX, CALMA_LAYER,
CALMA_NODETYPE, CALMA_XY, -1 };
int nbytes, rtype, madeinst;
READRH(nbytes, rtype);
if (nbytes < 0)
{
CalmaReadError("Unexpected EOF.\n");
return (FALSE);
}
switch (rtype)
{
case CALMA_AREF:
case CALMA_SREF:
madeinst = calmaElementSref(filename);
if (madeinst >= 0)
(*pnsrefs) += madeinst;
break;
case CALMA_BOUNDARY:
calmaElementBoundary();
(*pnpaths)++;
break;
case CALMA_BOX:
calmaElementBox();
(*pnpaths)++;
break;
case CALMA_PATH:
calmaElementPath();
(*pnpaths)++;
break;
case CALMA_TEXT:
calmaElementText();
break;
case CALMA_NODE:
CalmaReadError("NODE elements not supported: skipping.\n");
calmaSkipSet(node);
break;
default:
UNREADRH(nbytes, rtype);
return (FALSE);
}
return (calmaSkipTo(CALMA_ENDEL));
}
對於不同的型別,採用不同的解析方式,我們看一下最典型的多邊形BOUNDARY:
void
calmaElementBoundary()
{
int dt, layer, ciftype;
CIFPath *pathheadp;
LinkedRect *rp;
Plane *plane;
CellUse *use;
CellDef *savedef, *newdef = NULL;
/* Skip CALMA_ELFLAGS, CALMA_PLEX */
calmaSkipSet(calmaElementIgnore);
/* Read layer and data type */
if (!calmaReadI2Record(CALMA_LAYER, &layer)
|| !calmaReadI2Record(CALMA_DATATYPE, &dt))
{
CalmaReadError("Missing layer or datatype in boundary/box.\n");
return;
}
/* Set current plane */
ciftype = CIFCalmaLayerToCifLayer(layer, dt, cifCurReadStyle);
if (ciftype < 0)
{
plane = NULL;
calmaLayerError("Unknown layer/datatype in boundary", layer, dt);
}
else
plane = cifCurReadPlanes[ciftype];
/* Read the path itself, building up a path structure */
if (!calmaReadPath(&pathheadp, (plane == NULL) ? 0 : 1))
{
if (plane != NULL)
CalmaReadError("Error while reading path for boundary/box; ignored.\n");
return;
}
/* Note that calmaReadPath() may reallocate planes of cifCurReadPlanes */
/* so we need to set it again. */
if (ciftype >= 0) plane = cifCurReadPlanes[ciftype];
/* Convert the polygon to rectangles. */
if (CalmaSubcellPolygons && (calmaNonManhattan > 0))
{
/* Place the polygon in its own subcell */
char newname[] = "polygonXXXXX";
HashEntry *he;
savedef = cifReadCellDef;
/* Make up name for cell */
sprintf(newname + 7, "%05d", ++CalmaPolygonCount);
he = HashFind(&calmaDefInitHash, newname);
if (!HashGetValue(he))
{
newdef = calmaFindCell(newname, NULL);
cifReadCellDef = newdef;
DBCellClearDef(cifReadCellDef);
DBCellSetAvail(cifReadCellDef);
/* cifEditCellPlanes is not used by the gds reader, so it's */
/* available to be used to store the polygon. */
cifCurReadPlanes = cifEditCellPlanes;
if (plane != NULL)
plane = cifCurReadPlanes[ciftype];
}
}
//在這裡將多邊形轉化為矩形,維護角勾鏈的資料結構
rp = CIFPolyToRects(pathheadp, plane, CIFPaintTable, (PaintUndoInfo *)NULL);
CIFFreePath(pathheadp);
/* If the input layer is designated for ports by a "label" */
/* statement in the cifinput section, then find any label */
/* bounded by the path and attach the path to it. Note */
/* that this assumes two things: (1) that labels can only */
/* be attached to simple rectangles, and (2) that the */
/* rectangle appears in the GDS stream after the label. If */
/* either assumption is violated, this method needs to be */
/* re-coded. */
if (rp != NULL)
{
Rect rpc;
int savescale;
/* Convert rp to magic database units to compare to label rects */
rpc = rp->r_r;
rpc.r_xbot /= cifCurReadStyle->crs_scaleFactor;
rpc.r_xtop /= cifCurReadStyle->crs_scaleFactor;
rpc.r_ybot /= cifCurReadStyle->crs_scaleFactor;
rpc.r_ytop /= cifCurReadStyle->crs_scaleFactor;
if ((ciftype >= 0) &&
(cifCurReadStyle->crs_labelSticky[ciftype] != LABEL_TYPE_NONE))
{
Label *lab;
TileType type;
type = cifCurReadStyle->crs_labelLayer[ciftype];
for (lab = cifReadCellDef->cd_labels; lab; lab = lab->lab_next)
{
if ((GEO_SURROUND(&rpc, &lab->lab_rect)) && (lab->lab_type == type))
{
lab->lab_rect = rpc; /* Replace with larger rectangle */
break;
}
}
if (lab == NULL)
{
/* There was no label in the area. Create a placeholder label */
DBPutLabel(cifReadCellDef, &rpc, GEO_CENTER, "", type, 0);
}
}
}
/* Paint the rectangles (if any) */
for (; rp != NULL ; rp = rp->r_next)
{
if (plane) //繪製,更底層的核心方法
DBPaintPlane(plane, &rp->r_r, CIFPaintTable, (PaintUndoInfo *)NULL);
freeMagic((char *) rp);
}
if (cifCurReadPlanes == cifEditCellPlanes)
{
CIFPaintCurrent(FILE_CALMA);
DBReComputeBbox(cifReadCellDef);
DRCCheckThis(cifReadCellDef, TT_CHECKPAINT, &cifReadCellDef->cd_bbox);
DBWAreaChanged(cifReadCellDef, &cifReadCellDef->cd_bbox,
DBW_ALLWINDOWS, &DBAllButSpaceBits);
DBCellSetModified(cifReadCellDef, TRUE);
DBGenerateUniqueIds(cifReadCellDef, FALSE); /* Is this necessary? */
cifCurReadPlanes = cifSubcellPlanes;
cifReadCellDef = savedef;
use = DBCellNewUse(newdef, (char *)NULL);
DBSetTrans(use, &GeoIdentityTransform);
DBPlaceCell(use, cifReadCellDef);
}
}
終於到最後一個,也是最底層的方法了,DBPaintPlane0
():
void
DBPaintPlane0(plane, area, resultTbl, undo, method)
Plane *plane; /* Plane whose paint is to be modified */
Rect *area; /* Area to be changed */
PaintResultType *resultTbl; /* Table, indexed by the type of tile already
* present in the plane, giving the type to
* which the existing tile must change as a
* result of this paint operation.
*/
PaintUndoInfo *undo; /* Record containing everything needed to
* save undo entries for this operation.
* If NULL, the undo package is not used.
*/
unsigned char method; /* If PAINT_MARK, the routine marks tiles as it
* goes to avoid processing tiles twice.
*/
{
Point start;
int clipTop, mergeFlags;
TileType oldType, newType;
Tile *tile, *newtile;
Tile *tpnew; /* Used for area search */
Tile *tp; /* Used for paint */
bool haschanged;
if (area->r_xtop <= area->r_xbot || area->r_ytop <= area->r_ybot)
return;
/*
* The following is a modified version of the area enumeration
* algorithm. It expects the in-line paint code below to leave
* 'tile' pointing to the tile from which we should continue the
* search.
*/
start.p_x = area->r_xbot;
start.p_y = area->r_ytop - 1;
tile = plane->pl_hint;
GOTOPOINT(tile, &start);
/* Each iteration visits another tile on the LHS of the search area */
while (TOP(tile) > area->r_ybot)
{
/***
*** AREA SEARCH.
*** Each iteration enumerates another tile.
***/
enumerate:
if (SigInterruptPending)
break;
clipTop = TOP(tile);
if (clipTop > area->r_ytop) clipTop = area->r_ytop;
/* Skip processed tiles, if the "method" option was PAINT_MARK */
if (method == (unsigned char)PAINT_MARK)
if (tile->ti_client != (ClientData) CLIENTDEFAULT)
goto paintdone;
oldType = TiGetTypeExact(tile);
#ifdef PAINTDEBUG
if (dbPaintDebug)
dbPaintShowTile(tile, undo, "area enum");
#endif /* PAINTDEBUG */
/***
*** ---------- THE FOLLOWING IS IN-LINE PAINT CODE ----------
***/
/*
* Set up the directions in which we will have to
* merge initially. Clipping can cause some of these
* to be turned off.
*/
mergeFlags = MRG_TOP | MRG_LEFT;
if (RIGHT(tile) >= area->r_xtop) mergeFlags |= MRG_RIGHT;
if (BOTTOM(tile) <= area->r_ybot) mergeFlags |= MRG_BOTTOM;
/*
* Determine new type of this tile.
* Change the type if necessary.
*/
haschanged = FALSE;
/* If the source tile is split, apply table to each side */
if (method == (unsigned char)PAINT_XOR)
newType = *resultTbl;
else if (!IsSplit(tile))
newType = resultTbl[oldType];
else
newType = resultTbl[SplitLeftType(tile)]
| (resultTbl[SplitRightType(tile)] << 14)
| (oldType & (TT_DIAGONAL | TT_DIRECTION | TT_SIDE));
if (oldType != newType)
{
/*
* Clip the tile against the clipping rectangle.
* Merging is only necessary if we clip to the left or to
* the right, and then only to the top or the bottom.
* We do the merge in-line for efficiency.
*/
/* Clip up */
if (TOP(tile) > area->r_ytop)
{
if (IsSplit(tile))
{
haschanged |= TiNMSplitY(&tile, &newtile, area->r_ytop, 1, undo);
if (!IsSplit(tile))
{
oldType = TiGetTypeExact(tile);
newType = (method == (unsigned char)PAINT_XOR) ?
*resultTbl : resultTbl[oldType];
tile = TiNMMergeLeft(tile, plane);
TiNMMergeRight(TR(newtile), plane);
}
else
{
TiNMMergeLeft(newtile, plane);
TiNMMergeRight(TR(tile), plane);
}
}
else
{
newtile = TiSplitY(tile, area->r_ytop);
TiSetBody(newtile, TiGetBody(tile));
}
mergeFlags &= ~MRG_TOP;
}
/* Clipping diagonals can cause the new tile to no longer be */
/* in the search path! */
if (RIGHT(tile) <= area->r_xbot)
goto paintdone;
if (oldType == newType) goto clipdone;
/* Clip down */
if (BOTTOM(tile) < area->r_ybot)
{
if (IsSplit(tile))
{
haschanged |= TiNMSplitY(&tile, &newtile, area->r_ybot, 0, undo);
if (!IsSplit(tile))
{
oldType = TiGetTypeExact(tile);
newType = (method == (unsigned char)PAINT_XOR) ?
*resultTbl : resultTbl[oldType];
tile = TiNMMergeLeft(tile, plane);
TiNMMergeRight(TR(newtile), plane);
}
else
{
TiNMMergeLeft(newtile, plane);
TiNMMergeRight(TR(tile), plane);
}
}
else
{
newtile = tile, tile = TiSplitY(tile, area->r_ybot);
TiSetBody(tile, TiGetBody(newtile));
}
mergeFlags &= ~MRG_BOTTOM;
}
/* Clipping diagonals can cause the new tile to no longer be */
/* in the search path! */
if (RIGHT(tile) <= area->r_xbot)
goto paintdone;
if (oldType == newType) goto clipdone;
/* Clip right */
if (RIGHT(tile) > area->r_xtop)
{
if (IsSplit(tile))
{
haschanged |= TiNMSplitX(&tile, &newtile, area->r_xtop, 1, undo);
if (!IsSplit(tile))
{
oldType = TiGetTypeExact(tile);
newType = (method == (unsigned char)PAINT_XOR) ?
*resultTbl : resultTbl[oldType];
tile = TiNMMergeLeft(tile, plane);
TiNMMergeRight(LB(newtile), plane);
}
else
{
TiNMMergeRight(newtile, plane);
TiNMMergeLeft(LB(tile), plane);
}
}
else
{
TISPLITX(newtile, tile, area->r_xtop);
TiSetBody(newtile, TiGetBody(tile));
/* Merge the outside tile to its top */
tp = RT(newtile);
if (CANMERGE_Y(newtile, tp)) TiJoinY(newtile, tp, plane);
/* Merge the outside tile to its bottom */
tp = LB(newtile);
if (CANMERGE_Y(newtile, tp)) TiJoinY(newtile, tp, plane);
}
mergeFlags &= ~MRG_RIGHT;
}
/* Clipping diagonals can cause the new tile */
/* to no longer be in the search path! */
if (BOTTOM(tile) >= area->r_ytop || RIGHT(tile) <= area->r_xbot)
goto paintdone;
if (oldType == newType) goto clipdone;
/* Clip left */
if (LEFT(tile) < area->r_xbot)
{
if (IsSplit(tile))
{
haschanged |= TiNMSplitX(&tile, &newtile, area->r_xbot, 0, undo);
if (!IsSplit(tile))
{
oldType = TiGetTypeExact(tile);
newType = (method == (unsigned char)PAINT_XOR) ?
*resultTbl : resultTbl[oldType];
// tile = TiNMMergeRight(tile, plane);
TiNMMergeLeft(LB(newtile), plane);
}
else
{
TiNMMergeLeft(newtile, plane);
// TiNMMergeRight(LB(tile), plane);
}
}
else
{
newtile = tile;
TISPLITX(tile, tile, area->r_xbot);
TiSetBody(tile, TiGetBody(newtile));
/* Merge the outside tile to its top */
tp = RT(newtile);
if (CANMERGE_Y(newtile, tp)) TiJoinY(newtile, tp, plane);
/* Merge the outside tile to its bottom */
tp = LB(newtile);
if (CANMERGE_Y(newtile, tp)) TiJoinY(newtile, tp, plane);
}
mergeFlags &= ~MRG_LEFT;
}
/* Clipping diagonals can cause the new tile */
/* to no longer be in the search path! */
if (BOTTOM(tile) >= area->r_ytop)
goto paintdone;
#ifdef PAINTDEBUG
if (dbPaintDebug)
dbPaintShowTile(tile, undo, "after clip");
#endif /* PAINTDEBUG */
}
clipdone:
if (newType & TT_DIAGONAL)
{
/* If left and right types of a diagonal tile are */
/* the same, revert back to a rectangular tile. */
if ((newType & TT_LEFTMASK) == ((newType & TT_RIGHTMASK) >> 14))
{
newType &= TT_LEFTMASK;
if (undo && UndoIsEnabled())
DBPAINTUNDO(tile, newType, undo);
TiSetBody(tile, newType);
// if (method == PAINT_MARK) tile->ti_client = (ClientData)1;
/* Reinstate the left and right merge requirements */
mergeFlags |= MRG_LEFT;
if (RIGHT(tile) >= area->r_xtop) mergeFlags |= MRG_RIGHT;
}
else
mergeFlags = 0;
}
/*
* Merge the tile back into the parts of the plane that have
* already been visited. Note that if we clipped in a particular
* direction we avoid merging in that direction.
*
* We avoid calling dbPaintMerge if at all possible.
*/
if (mergeFlags & MRG_LEFT)
{
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
if (TiGetTypeExact(tp) == newType)
{
tile = dbPaintMerge(tile, newType, area, plane, mergeFlags,
undo, (method == (unsigned char)PAINT_MARK)
? TRUE : FALSE);
goto paintdone;
}
mergeFlags &= ~MRG_LEFT;
}
if (mergeFlags & MRG_RIGHT)
{
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
if (TiGetTypeExact(tp) == newType)
{
tile = dbPaintMerge(tile, newType, area, plane, mergeFlags,
undo, (method == (unsigned char)PAINT_MARK)
? TRUE : FALSE);
goto paintdone;
}
mergeFlags &= ~MRG_RIGHT;
}
/*
* Cheap and dirty merge -- we don't have to merge to the
* left or right, so the top/bottom merge is very fast.
*
* Now it's safe to change the type of this tile, and
* record the event on the undo list.
*/
if (undo && UndoIsEnabled())
if (haschanged || (oldType != newType))
DBPAINTUNDO(tile, newType, undo);
TiSetBody(tile, newType);
if (method == (unsigned char)PAINT_MARK) tile->ti_client = (ClientData)1;
#ifdef PAINTDEBUG
if (dbPaintDebug)
dbPaintShowTile(tile, undo, "changed type");
#endif /* PAINTDEBUG */
if (mergeFlags & MRG_TOP)
{
tp = RT(tile);
if (CANMERGE_Y(tile, tp)) TiJoinY(tile, tp, plane);
#ifdef PAINTDEBUG
if (dbPaintDebug)
dbPaintShowTile(tile, undo, "merged up (CHEAP)");
#endif /* PAINTDEBUG */
}
if (mergeFlags & MRG_BOTTOM)
{
tp = LB(tile);
if (CANMERGE_Y(tile, tp)) TiJoinY(tile, tp, plane);
#ifdef PAINTDEBUG
if (dbPaintDebug)
dbPaintShowTile(tile, undo, "merged down (CHEAP)");
#endif /* PAINTDEBUG */
}
paintdone:
/***
*** END OF PAINT CODE
*** ---------- BACK TO AREA SEARCH ----------
***/
/* Move right if possible */
tpnew = TR(tile);
if (LEFT(tpnew) < area->r_xtop)
{
/* Move back down into clipping area if necessary */
while (BOTTOM(tpnew) >= clipTop) tpnew = LB(tpnew);
if (BOTTOM(tpnew) >= BOTTOM(tile) || BOTTOM(tile) <= area->r_ybot)
{
tile = tpnew;
goto enumerate;
}
}
/* Each iteration returns one tile further to the left */
while (LEFT(tile) > area->r_xbot)
{
/* Move left if necessary */
if (BOTTOM(tile) <= area->r_ybot)
goto done;
/* Move down if possible; left otherwise */
tpnew = LB(tile); tile = BL(tile);
if (BOTTOM(tpnew) >= BOTTOM(tile) || BOTTOM(tile) <= area->r_ybot)
{
tile = tpnew;
goto enumerate;
}
}
/* At left edge -- walk down to next tile along the left edge */
for (tile = LB(tile); RIGHT(tile) <= area->r_xbot; tile = TR(tile))
/* Nothing */;
}
done:
if (method == (unsigned char)PAINT_MARK)
{
/* Now unmark the processed tiles with the same search algorithm */
/* Expand the area by one to catch tiles that were clipped at */
/* the area boundary. */
area->r_xbot -= 1;
area->r_ybot -= 1;
area->r_xtop += 1;
area->r_ytop += 1;
start.p_x = area->r_xbot;
start.p_y = area->r_ytop - 1;
tile = plane->pl_hint;
GOTOPOINT(tile, &start);
while (TOP(tile) > area->r_ybot)
{
enum2:
clipTop = TOP(tile);
if (clipTop > area->r_ytop) clipTop = area->r_ytop;
tile->ti_client = (ClientData)CLIENTDEFAULT;
/* Move right if possible */
tpnew = TR(tile);
if (LEFT(tpnew) < area->r_xtop)
{
/* Move back down into clipping area if necessary */
while (BOTTOM(tpnew) >= clipTop) tpnew = LB(tpnew);
if (BOTTOM(tpnew) >= BOTTOM(tile) || BOTTOM(tile) <= area->r_ybot)
{
tile = tpnew;
goto enum2;
}
}
/* Each iteration returns one tile further to the left */
while (LEFT(tile) > area->r_xbot)
{
/* Move left if necessary */
if (BOTTOM(tile) <= area->r_ybot)
goto done2;
/* Move down if possible; left otherwise */
tpnew = LB(tile); tile = BL(tile);
if (BOTTOM(tpnew) >= BOTTOM(tile) || BOTTOM(tile) <= area->r_ybot)
{
tile = tpnew;
goto enum2;
}
tile->ti_client = (ClientData)CLIENTDEFAULT;
}
/* At left edge -- walk down to next tile along the left edge */
for (tile = LB(tile); RIGHT(tile) <= area->r_xbot; tile = TR(tile))
tile->ti_client = (ClientData)CLIENTDEFAULT;
}
tile->ti_client = (ClientData)CLIENTDEFAULT;
}
done2:
plane->pl_hint = tile;
}
整體呼叫關係:
相關文章
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- redis資料結構原始碼閱讀——字串編碼過程Redis資料結構原始碼字串編碼
- Laravel 原始碼閱讀指南 -- Database 查詢構建器Laravel原始碼Database
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- Laravel 原始碼閱讀指南 -- 載入和讀取 ENV 配置Laravel原始碼
- Mysql8.0原始碼閱讀建議MySql原始碼
- Java類載入原始碼閱讀Java原始碼
- 對比資料泵與原始匯入匯出工具(三)
- 【詳解】ThreadPoolExecutor原始碼閱讀(三)thread原始碼
- Qt原始碼閱讀(三) 物件樹管理QT原始碼物件
- 原始碼閱讀:AFNetworking(三)——AFURLResponseSerialization原始碼
- Vue 原始碼閱讀(三)Special AttributesVue原始碼
- MySQL資料庫結構和資料的匯出和匯入 (轉)MySql資料庫
- jQuery原始碼閱讀1—jQuery架構jQuery原始碼架構
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- DM 原始碼閱讀系列文章(三)資料同步處理單元介紹原始碼
- 原始碼閱讀:SDWebImage(三)——NSData+ImageContentType原始碼Web
- zk原始碼閱讀9:資料模型ACL之基礎資料結構以及驗證原始碼模型資料結構
- 為什麼建議你常閱讀原始碼?原始碼
- Data-Mediator 專案結構和原始碼閱讀(比如demo)原始碼
- ReactorKit原始碼閱讀React原始碼
- AQS原始碼閱讀AQS原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- HashMap 原始碼閱讀HashMap原始碼
- delta原始碼閱讀原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- NGINX原始碼閱讀Nginx原始碼
- Mux 原始碼閱讀UX原始碼
- HashMap原始碼閱讀HashMap原始碼
- fuzz原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼
- express 原始碼閱讀Express原始碼
- muduo原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- Appdash原始碼閱讀——RecentStore和LimitStoreAPP原始碼MIT
- php讀取excel檔案資料的匯入和匯出PHPExcel
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- TiKV 原始碼閱讀三部曲(三)寫流程原始碼