- UID
- 2443
- 积分
- 1311
- 帖子
- 78
- 主题
- 21
- 论坛币
- 909
- 威望
- 8
- EP值
- 449
- MP值
- 0
- 阅读权限
- 100
- 注册时间
- 2015-3-7
- 在线时间
- 121 小时
- 最后登录
- 2018-7-21
|
本帖最后由 面麻 于 2015-9-10 22:48 编辑
当初我在一个MAD作品里看到的效果,就是拆分字形,想用ASS来实现一下。
一共2个方法,都是灾厄前辈想的,代码也是他写的。
我在其中也学到了很多,很感谢灾厄。
可能代码里面有一些东西涉及到高层次的Python语法,尽量看吧。
注释也很多,如果还有问题,直接跟帖。
完整工程在下面,可以Prase一下看看。
两种方法可能还有点小bug,各有优劣。
test_split_font_temp.zip
(17.56 KB, 下载次数: 2851)
第一种方法,是拆分矢量字体的绘图代码,先通过 'm' 字符判断大致拆分开,然后再考虑内外框分别是顺时针和逆时针的情况,下面的代码是以逆时针为内框。
需要说明的是,不同字体,内外框的绕行方向是不确定的,所以如果出现大片字形不正确,可以修改函数check_clockwise()的clockwise值。
其实,这个判断是比较复杂的,尤其是凹多边形很难判断,如果出现个别字形不正确,只能手动判断和修改绘图代码。- from tcaxPy import *
- from util.gdiFont import *
- import re
- def tcaxPy_Init():
- global font # Font和GdiFont效果差不多
- global fontsize
- global GdiFont
- fontsize = GetVal(val_FontSize)
- font = InitFont(GetVal(val_FontFileName), GetVal(val_FaceID), fontsize, GetVal(val_Spacing), GetVal(val_SpaceScale), 0xFFFFFF, 0, 0)
- GdiFont = gfInitFont(GetVal(val_FontFaceName), fontsize, GetVal(val_Spacing), GetVal(val_SpaceScale), 0, False) #GDIfont
- def tcaxPy_User():
- pass
- def tcaxPy_Fin():
- FinFont(font)
- def checkacw(str): # 判斷內框
- Ppath = ppath(str)
-
- if Ppath[0]==Ppath[-1]: #字體矢量有重複最後坐標點的習慣 貌似重複的基本是外框 不過總有bug的地方 總之取首尾不同的 扔掉最後重複
- Ppath.pop()
- return check_clockwise(Ppath) # 设为内框
- #構建一個本項和後項結合的list 比方說poly為[(1,1),(2,2),(3,3),(4,4)] 則新list為[((1,1),(2,2)),((2,2),(3,3)),((3,3),(4,4)),((4,4),(1,1))]
- def segments(poly): #網上照搬
- return zip(poly, poly[1:] + [poly[0]])
- def check_clockwise(poly): #判斷順時針 網上搬 算法之前判斷用的極值求叉積法 總有凹多邊形不準確 這個遍歷所有點算叉積 最後以總和的正負來判斷
- clockwise = False
- if (sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(poly))) < 0: # 叉積为負值是逆時針
- clockwise = not clockwise
- return clockwise
- def ppath(str): # 從str 將路徑儲存為 [(1,1),(2,2),(3,3),(4,4)] 這種格式 b 標籤捨棄中間點 做直線考慮
- if str[0] != 'm':
- str = 'm'+ str
- sb = re.split('m|l|b ',str) # re是一个正则表达式Module,这里的split其实就是字符串的split函数的翻版,只是可以对分隔符用正则
- ppath = []
- for s in sb:
- b = s.split()
- if len(b) > 1:
- if b[-1]=='c':
- ppath.append((int(b[-3]),int(b[-2])))
- else:
- ppath.append((int(b[-2]),int(b[-1])))
- ppath.pop() # 所有路徑 首尾都相同 捨棄掉最後重複
- return ppath
- def polycmp(poly1,poly2): #判斷多邊形是否在另一個多邊形內
- result = True
- if poly2[-1]==poly2[0]:
- poly2.pop()
- for p1 in poly1:
- x1,y1= p1
- if not pointinpoly(x1,y1,poly2): #遍歷多邊形所有點是否在多邊形內
- result = False
- return result
- def pointinpoly(x,y,poly): # 判斷一個點是否在多邊形內 網上搬
- n = len(poly)
- inside = False
- p1x,p1y = poly[0]
- for i in range(n+1):
- p2x,p2y = poly[i % n]
- if y > min(p1y,p2y):
- if y <= max(p1y,p2y):
- if x <= max(p1x,p2x):
- if p1y != p2y:
- xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
- if p1x == p2x or x <= xints:
- inside = not inside
- p1x,p1y = p2x,p2y
- return inside
- def tcaxPy_Main(_i, _j, _n, _start, _end, _elapk, _k, _x, _y, _a, _txt):
- ASS_BUF = [] # used for saving ASS FX lines
- #############################
- # TODO: write your codes here #
- dx = _x - int(_a / 2 + 0.5)
- dy = _y - int(fontsize / 2 + 0.5)
- outline = gfGetOutline(GdiFont, _txt, dx,dy)
- draw = outline.split('m')
- num = len(draw)
- tcaxLog(draw)
- acdl = [] #用來儲存內框矢量
- cdl = [] #儲存外框矢量
- if _i>-1 :
- for i in range(num): #遍歷所有m標籤矢量 判斷內外框 并進行儲存
- if draw[i] == '':
- continue
-
- if checkacw(draw[i]):
- acd = 'm'+draw[i]
- acdl.append(acd)
- else:
- cd = 'm'+draw[i]
- cdl.append(cd)
-
- cdlcopy = cdl[:] #複製一遍list
- for acd in acdl:
- index = -1
- for i in range(len(cdlcopy)):
- if polycmp(ppath(acd),ppath(cdlcopy[i])): #遍歷所有內框外框 判斷內框是否在外框範圍內
- if index == -1:
- index = i
- else:
- if polycmp(ppath(cdlcopy[i]),ppath(cdlcopy[index])): #若存在多個可匹配的範圍 取最小的框 如"回"字
- index = i
- if index != -1:
- cdl[index] += acd #將內框矢量 加在外框矢量之後
- else:
- cdl.append(acd) #若沒找到匹配的外框 則作為外框 這個有可能是順時針逆時針的判斷問題 也有可能是字體設計的問題 總之是保險
- for cd in cdl: #繪製
- posX = randint(-_a, _a) + randint(-10, 10)
- posY = randint(-fontsize , fontsize ) + randint(-10, 10)
- EFT = an(7) + move(posX, posY, 0, 0, 0, 500) + p(4)
- ass_main(ASS_BUF, SubL(_start, _end), EFT, cd)
- #############################
- return (ASS_BUF, None)
复制代码 第二种方法,是用粒子,这里面用到了判断连通性的Labeling算法,网络上搜索 'Labeling' 就可以找到相关资料,比如:http://blog.csdn.net/icvpr/article/details/10259577
具体的算法不做解释,但还是需要自己查看一下才能看懂下面的代码。- from tcaxPy import *
- def tcaxPy_Init():
- global font
- global fontsize
- fontsize = GetVal(val_FontSize)
- font = InitFont(GetVal(val_FontFileName), GetVal(val_FaceID), fontsize, GetVal(val_Spacing), GetVal(val_SpaceScale), 0xFFFFFF, 0, 0)
- def tcaxPy_User():
- pass
- def tcaxPy_Fin():
- FinFont(font)
- def GetPixelPointFromPix(PIX, InitPosX, InitPosY): # 将方形PIX[1][0]xPIX[1][1]范围的pixel扫描成一个list,保存位置、RGBA、label信息
- pixel = []
- label = 0
- for h in range(PIX[1][1]):
- posY = InitPosY + h
- for w in range(PIX[1][0]):
- posX = InitPosX + w
- idx = 4 * (h * PIX[1][0] + w)
- pixR = PIX[2][idx + 0]
- pixG = PIX[2][idx + 1]
- pixB = PIX[2][idx + 2]
- pixA = PIX[2][idx + 3]
- pixel.append([(posX, posY), (pixR, pixG, pixB, pixA), label])
-
- return pixel
- def DeleteRepeat(labelset): # 删除list中的重复元素
- labelset2 = []
- [labelset2.append(label) for label in labelset if not label in labelset2]
- return labelset2
- def UnionTwoLists(list1, list2): # 合并2个list,默认它们含有相同元素
- for elem in list1:
- if elem in list2:
- continue
- else:
- list2.append(elem)
- return list2
- def UnionLabelSet(labelset): # 整理labelset,合并
- labelset = sorted(labelset, key=lambda k: k[0]) # 排序
- labelset = DeleteRepeat(labelset) # 刪除重複
- labelset2 = []
- for label in labelset:
- if labelset2 == []: #第一個 label 直接添加
- labelset2.append(label)
- else:
- flag = 0
- for i in range(len(labelset2)): #遍歷之前的label找交集 若存在交集 向對應位置 添加未存在的元素
- #tcaxLog(label2)
- if (label[0] not in labelset2[i]) and (label[1] in labelset2[i]):
- flag = 1
- labelset2[i].append(label[0])
- elif (label[0] in labelset2[i]) and (label[1] not in labelset2[i]):
- flag = 1
- labelset2[i].append(label[1])
- elif (label[0] in labelset2[i]) and (label[1] in labelset2[i]):
- flag = 1
- if flag == 0: # 若不存在交集 則另存
- labelset2.append(label)
- #tcaxLog(labelset2)
- return labelset2
-
- def tcaxPy_Main(_i, _j, _n, _start, _end, _elapk, _k, _x, _y, _a, _txt):
- ASS_BUF = [] # used for saving ASS FX lines
- #############################
- # TODO: write your codes here #
- PIX = TextPix(font, _txt)
- InitPosX = _x - int(_a / 2 + 0.5) + PIX[0][0]
- InitPosY = _y - int(fontsize / 2 + 0.5) + PIX[0][1]
- pixel = GetPixelPointFromPix(PIX, InitPosX, InitPosY)
- num = len(pixel)
- label = 0
- labelset = []
- # 1-pass
- for i in range(num):
- if pixel[i][1][3] != 0: # 如果是有效像素
- if i == 0: # 如果是第1个像素,没有左面和上面的相邻像素,需要单独考虑
- label += 1
- pixel[i][2] = label
- elif i % PIX[1][0] == 0: # 如果是第1列像素,没有左面的相邻像素,需要单独考虑
- uppixelindex = i - PIX[1][0] # 取上面的相邻像素index
- if pixel[uppixelindex][2] != 0:
- pixel[i][2] = pixel[uppixelindex][2] # 如果上面的相邻像素是有效像素,就把上面像素的label赋给当前像素
- else: # 如果上面的相邻像素不是有效像素,就把label加1,然后赋给当前像素
- label += 1
- pixel[i][2] = label
- elif i <= PIX[1][0] - 1: # 如果是第1行像素,没有上面的相邻像素,需要单独考虑
- leftpixelindex = i - 1
- if pixel[leftpixelindex][2] != 0:
- pixel[i][2] = pixel[leftpixelindex][2]
- else:
- label += 1
- pixel[i][2] = label
- else: # 同时考虑上面和左面的相邻像素是否是有效像素
- uppixelindex = i - PIX[1][0]
- leftpixelindex = i - 1
- if pixel[leftpixelindex][2] != 0 and pixel[uppixelindex][2] != 0: # 如果上面和左面的相邻像素都是有效像素,
- pixel[i][2] = min(pixel[leftpixelindex][2], pixel[uppixelindex][2]) # 就把它们的label的最小值赋给当前像素
- if pixel[leftpixelindex][2] != pixel[uppixelindex][2]: # 如果它们的label不相等,
- labelset.append(sorted([pixel[leftpixelindex][2], pixel[uppixelindex][2]])) # 就把它们的label组成一个list加入labelset
- elif pixel[leftpixelindex][2] != 0 and pixel[uppixelindex][2] == 0: # 如果上面的相邻像素不是有效像素,
- pixel[i][2] = pixel[leftpixelindex][2] # 就把左面像素的label赋给相邻像素
- elif pixel[leftpixelindex][2] == 0 and pixel[uppixelindex][2] != 0: # 如果左面的相邻像素不是有效像素,
- pixel[i][2] = pixel[uppixelindex][2] # 就把上面像素的label赋给相邻像素
- else: # 如果如果上面和左面的相邻像素都不是有效像素,
- label += 1 # 把label加1,赋给当前像素
- pixel[i][2] = label
- #tcaxLog(pixel)
- labelset = UnionLabelSet(labelset)
- #print(labelset)
-
- # 2-pass #這個地方寫法冗長 比較亂 沒仔細想
- for i in range(len(pixel)): #按labelset 修改相同 label
- if pixel[i][2] != 0:
- for j in range(len(labelset)):
- if pixel[i][2] in labelset[j]:
- pixel[i][2] = min(labelset[j])
-
- #tcaxLog(min(labelset[j]))
- rndx = []
- rndy = []
- cor = []
- labelbuf = []
- for i in range(len(pixel)): # 將不連續的label修改成最小連續數列
- label = pixel[i][2]
- if label != 0:
- if label not in labelbuf:
- labelbuf.append(label)
- pixel[i][2] = labelbuf.index(label) +1
- for i in range(len(labelbuf)): # 按照label個數生成隨機位置和顏色list
- rndx.append(randint(-50, 50))
- rndy.append(randint(-100, 100))
- cor.append(RandRGB())
- #tcaxLog(sorted(labelbuf))
- #if _i == 0 and _j == 2:
- # tcaxLog(pixel)
-
- for i in range(len(pixel)): #繪製
- label = pixel[i][2]
- if label != 0:
- EFT = move(pixel[i][0][0] + rndx[label-1], pixel[i][0][1] + rndy[label-1], pixel[i][0][0], pixel[i][0][1], 0, 500) + alpha(255 - pixel[i][1][3]) + color1(cor[label-1])
- ass_main(ASS_BUF, SubL(_start, _end, 0, Pix_Style), EFT, DrawPoint())
- #############################
- return (ASS_BUF, None)
复制代码 |
-
6
查看全部评分
-
|