Magic原始碼閱讀(三)——資料匯入和構建

GaleZhang發表於2020-10-09

我們之前已經瞭解了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;
}

整體呼叫關係:

在這裡插入圖片描述

相關文章