Initial text support implementation, pt. II.

Switch from fixed-function pipeline to shaders.
This commit is contained in:
Lucio Andrés Illanes Albornoz 2018-06-28 19:46:17 +02:00
parent f5dee0bf20
commit 22c4904c20
70 changed files with 150 additions and 33782 deletions

View File

@ -10,7 +10,7 @@
# 3) -s: effects: rotate, smash into bricks, swirl, wave, ...
# 4) XXX autodetect video width from widest mircart
# 5) XXX convert TTF to texture PNG & coords TXT, render accordingly
# 6) XXX dont stall GPU w/ glReadPixels(), switch to asynchronous model w/ PBOs
# 6) XXX dont stall GPU w/ glReadPixels(), switch to asynchronous model w/ FBO or PBO (http://www.songho.ca/opengl/gl_fbo.html, http://www.songho.ca/opengl/gl_pbo.html)
# 7) XXX render mircart as 3D blocks vs flat surface
#
@ -91,8 +91,8 @@ class ENNToolApp(object):
MiRCART += ENNToolMiRCARTImporter(inFile).outMap
curY, rotateX, rotateY, translateY = 0, 0, 0, scrollRate
artTextureId = panelGLCanvas.initTexture(texturePathName)
artVbo, artVboLen, lastY, numVertices = panelGLCanvas.renderMiRCART(MiRCART, cubeSize=optdict["-R"])
artTextureId, artInfo = panelGLCanvas.initTexture(texturePathName)
artVbo, artVboLen, lastY, numVertices = panelGLCanvas.renderMiRCART(artInfo, MiRCART, cubeSize=optdict["-R"])
if "-v" in optdict:
print("{} vertices".format(numVertices))
w, h = panelGLCanvas.GetClientSize(); w, h = max(w, 1.0), max(h, 1.0);
@ -121,7 +121,9 @@ class ENNToolApp(object):
videoFps, videoPath = int(optdict["-f"]), optdict["-o"]
panelGLCanvas = ENNToolGLCanvasPanel(appPanelSkin, size=appFrameSize, videoPath=videoPath)
panelGLCanvas.initOpenGL(); panelGLCanvas.initVideoWriter(fps=videoFps)
panelGLCanvas.initOpenGL()
panelGLCanvas.initShaders()
panelGLCanvas.initVideoWriter(fps=videoFps)
if "-v" in optdict:
time0 = time.time()

View File

@ -10,13 +10,20 @@
# Wed, 27 Jun 2018 16:02:12 +0200 [3] <https://www.khronos.org/opengl/wiki/How_lighting_works#Good_Settings.>
# Wed, 27 Jun 2018 16:02:13 +0200 [4] <https://www.khronos.org/opengl/wiki/Common_Mistakes>
# Wed, 27 Jun 2018 16:02:14 +0200 [5] <https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout>
# Thu, 28 Jun 2018 17:03:16 +0200 [6] <https://stackoverflow.com/questions/384759/how-to-convert-a-pil-image-into-a-numpy-array>
# Thu, 28 Jun 2018 17:04:59 +0200 [7] <https://www.khronos.org/opengl/wiki/Common_Mistakes#y-axis>
# Thu, 28 Jun 2018 18:32:50 +0200 [8] <https://stackoverflow.com/questions/18935203/shader-position-vec4-or-vec3>
#
from OpenGL.GL import *
from OpenGL.GL import shaders
from PIL import Image
import cv2, numpy
import ctypes, sys
import ctypes, os, sys, time
import wx, wx.glcanvas
import yaml
from ENNToolMiRCARTColours import ENNToolMiRCARTColoursFloat
class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
"""XXX"""
@ -34,22 +41,56 @@ class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
glEnable(GL_DEPTH_TEST)
glClearColor(0, 0, 0, 1); glClearDepth(1);
glTranslatef(-5.0, 3.0, -5)
# [3]
glLight(GL_LIGHT0, GL_AMBIENT, (0, 0, 0, 1))
glLight(GL_LIGHT0, GL_DIFFUSE, (1, 1, 1, 1))
glLight(GL_LIGHT0, GL_POSITION, (1, 1, 0+2, 0))
glLight(GL_LIGHT0, GL_SPECULAR, (1, 1, 1, 1))
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (0.2, 0.2, 0.2, 1))
glEnable(GL_LIGHTING); glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
# }}}
# {{{ initTexture(self, pathName): XXX
def initTexture(self, pathName):
# {{{ initShaders(self): XXX
def initShaders(self):
fs = shaders.compileShader("""
#version 330 core
in vec4 bgColour;
in vec2 fgTexCoord;
uniform sampler2D texture;
void main() {
vec4 texel = texture2D(texture, fgTexCoord);
if (texel.a > 0.0) {
gl_FragColor = vec4(texel.r, texel.g, texel.b, 1.0);
} else {
gl_FragColor = bgColour;
}
}
""", GL_FRAGMENT_SHADER)
vs = shaders.compileShader("""
#version 330 core
layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec4 colour;
layout(location = 3) in vec2 texcoord;
out vec4 bgColour;
out vec2 fgTexCoord;
uniform mat4 model;
uniform mat4 projection;
void main() {
gl_Position = projection * model * vertex;
bgColour = colour;
fgTexCoord = texcoord;
}
""", GL_VERTEX_SHADER)
self.shader = shaders.compileProgram(vs, fs)
# }}}
# {{{ initTexture(self, pathName, infoPathName=os.path.join("assets", "textures.yaml")): XXX
def initTexture(self, pathName, infoPathName=os.path.join("assets", "textures.yaml")):
with open(infoPathName, "r") as fileObject:
artInfo = yaml.load(fileObject)
# [6], [7]
artTextureId = glGenTextures(1)
artTextureImage = Image.open(pathName)
artTextureImageData = numpy.array(list(artTextureImage.getdata()), numpy.uint8)
artTextureImage = Image.open(pathName).transpose(Image.FLIP_TOP_BOTTOM)
artTextureImageData = numpy.array(artTextureImage)
glBindTexture(GL_TEXTURE_2D, artTextureId)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
@ -61,11 +102,11 @@ class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
# [4][5]
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
artTextureImage.size[0], artTextureImage.size[1],
0, GL_RGBA, GL_UNSIGNED_BYTE, artTextureImageData)
glBindTexture(GL_TEXTURE_2D, artTextureId)
return artTextureId
return artTextureId, artInfo
# }}}
# {{{ initVideoWriter(self): XXX
def initVideoWriter(self, fourcc="XVID", fps=25):
@ -76,54 +117,94 @@ class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
def renderFrame(self, artTextureId, artVbo, artVboLen):
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glEnable(GL_TEXTURE_2D)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glBindTexture(GL_TEXTURE_2D, artTextureId)
glBindBuffer(GL_ARRAY_BUFFER, artVbo)
glVertexPointer(3, GL_FLOAT, 32, ctypes.c_void_p(0))
glNormalPointer(GL_FLOAT, 32, ctypes.c_void_p(12))
glTexCoordPointer(2, GL_FLOAT, 32, ctypes.c_void_p(24))
glUseProgram(self.shader)
model = (GLfloat * 16)()
glGetFloatv(GL_MODELVIEW_MATRIX, model)
projection = (GLfloat * 16)()
glGetFloatv(GL_PROJECTION_MATRIX, projection)
glUniformMatrix4fv(glGetUniformLocation(self.shader, "model"), 1, GL_FALSE, model)
glUniformMatrix4fv(glGetUniformLocation(self.shader, "projection"), 1, GL_FALSE, projection)
glUniform1i(glGetUniformLocation(self.shader, "texture"), 0)
# [8]
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, False, 48, ctypes.c_void_p(0))
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 3, GL_FLOAT, False, 48, ctypes.c_void_p(12))
glEnableVertexAttribArray(2)
glVertexAttribPointer(2, 4, GL_FLOAT, False, 48, ctypes.c_void_p(24))
glEnableVertexAttribArray(3)
glVertexAttribPointer(3, 2, GL_FLOAT, False, 48, ctypes.c_void_p(40))
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glVertexPointer(3, GL_FLOAT, 48, ctypes.c_void_p(0))
glNormalPointer(GL_FLOAT, 48, ctypes.c_void_p(12))
glColorPointer(4, GL_FLOAT, 48, ctypes.c_void_p(24))
glTexCoordPointer(2, GL_FLOAT, 48, ctypes.c_void_p(40))
glDrawArrays(GL_QUADS, 0, artVboLen)
glDisableVertexAttribArray(0)
glDisable(GL_BLEND)
glDisable(GL_TEXTURE_2D)
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)
# }}}
# {{{ renderMiRCART(self, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2), texelHeight=0.0625, texelWidth=0.0625): XXX
def renderMiRCART(self, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2), texelHeight=0.0625, texelWidth=0.0625):
# {{{ renderMiRCART(self, artInfo, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2)): XXX
def renderMiRCART(self, artInfo, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2)):
curPos = [0, 0, 0]; vertices = []; numVertices = 0;
for numRow in range(len(artMap)):
if centre and (len(artMap[numRow]) < canvasCols):
curPos[0] += (((canvasCols - len(artMap[numRow])) * cubeSize[0]) / 2)
for numCol in range(len(artMap[numRow])):
cubeColour = artMap[numRow][numCol][1] * texelWidth
cubeColour = [*ENNToolMiRCARTColoursFloat[artMap[numRow][numCol][1]], 1.0]
if artMap[numRow][numCol][0] != artMap[numRow][numCol][1]:
colColour = artMap[numRow][numCol][0]
cubeChar = artMap[numRow][numCol][3]
if ord(cubeChar) >= 128:
print("dont have {}".format(cubeChar))
cubeChar = " "
else:
colColour = artMap[numRow][numCol][1]
cubeChar = " "
# Top Right
vertices += curPos
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour+texelWidth, texelHeight]
vertices += cubeColour
vertices += [float(((ord(cubeChar) + 1) * artInfo["rowWidth"]) / artInfo["texWidth"]), ((colColour + 1) * artInfo["rowHeight"]) / float(artInfo["texHeight"])]
numVertices += 1
# Top Left
vertices += [curPos[0]-cubeSize[0], curPos[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour, texelHeight]
vertices += cubeColour
vertices += [float(((ord(cubeChar) + 0) * artInfo["rowWidth"]) / artInfo["texWidth"]), ((colColour + 1) * artInfo["rowHeight"]) / float(artInfo["texHeight"])]
numVertices += 1
# Bottom Left
vertices += [curPos[0]-cubeSize[0], curPos[1]-cubeSize[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour, 0.0]
vertices += cubeColour
vertices += [float(((ord(cubeChar) + 0) * artInfo["rowWidth"]) / artInfo["texWidth"]), ((colColour) * artInfo["rowHeight"]) / float(artInfo["texHeight"])]
numVertices += 1
# Bottom Right
vertices += [curPos[0], curPos[1]-cubeSize[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour+texelWidth, 0.0]
vertices += cubeColour
vertices += [float(((ord(cubeChar) + 1) * artInfo["rowWidth"]) / artInfo["texWidth"]), ((colColour) * artInfo["rowHeight"]) / float(artInfo["texHeight"])]
numVertices += 1
curPos[0] += cubeSize[0]

View File

@ -16,75 +16,61 @@ from ENNToolMiRCARTImporter import ENNToolMiRCARTImporter
def main(*argv):
fontNormalPathName = os.path.join("assets", "DejaVuSansMono.ttf")
fontBoldPathName = os.path.join("assets", "DejaVuSansMono-Bold.ttf")
fontSize = int("11")
fontSize = int("26")
outInfoFileName = os.path.join("assets", "textures.yaml")
outPathName = os.path.join("assets", "textures")
outFileName = os.path.join("assets", "DejaVuSansMono.png")
if not os.path.exists(os.path.dirname(outInfoFileName)):
os.makedirs(os.path.dirname(outInfoFileName))
if not os.path.exists(outPathName):
os.makedirs(outPathName)
if not os.path.exists(os.path.dirname(outFileName)):
os.makedirs(os.path.dirname(outFileName))
pilFontNormal = ImageFont.truetype(fontNormalPathName, fontSize)
pilFontBold = ImageFont.truetype(fontBoldPathName, fontSize)
pilFontSize = list(pilFontNormal.getsize(" "))
pilFontSize[0] += (8 - (pilFontSize[0] % 8))
pilFontSize[1] = pilFontSize[0] * 2
pilImageSize = (pilFontSize[0] * 128, pilFontSize[1])
pilImageSize = (pilFontSize[0] * 128, (pilFontSize[1] * 16 * 4))
print("font size: {}, image size: {}".format(pilFontSize, pilImageSize))
charMap = {}
for fontAttrs in [[ENNToolMiRCARTImporter._CellState.CS_NONE],
[ENNToolMiRCARTImporter._CellState.CS_BOLD],
[ENNToolMiRCARTImporter._CellState.CS_UNDERLINE],
[ENNToolMiRCARTImporter._CellState.CS_BOLD, ENNToolMiRCARTImporter._CellState.CS_UNDERLINE]]:
for fontColour in range(16):
curPos = [0, 0]
pilImage = Image.new("RGBA", pilImageSize, (0, 0, 0, 0))
pilImageDraw = ImageDraw.Draw(pilImage)
pilImageTmp = Image.new("RGBA", pilFontSize, (0, 0, 0, 0))
pilImageTmpDraw = ImageDraw.Draw(pilImageTmp)
for fontAttr in [ENNToolMiRCARTImporter._CellState.CS_BOLD | ENNToolMiRCARTImporter._CellState.CS_UNDERLINE,
ENNToolMiRCARTImporter._CellState.CS_UNDERLINE,
ENNToolMiRCARTImporter._CellState.CS_BOLD,
ENNToolMiRCARTImporter._CellState.CS_NONE]:
for fontColour in reversed(range(16)):
for fontChar in [chr(n) for n in range(128)]:
pilFont, underLine = None, False
for fontAttr in fontAttrs:
if fontAttr == ENNToolMiRCARTImporter._CellState.CS_NONE:
pilFont = pilFontNormal
elif fontAttr == ENNToolMiRCARTImporter._CellState.CS_BOLD:
pilFont, underLine = pilFontNormal, False
if fontAttr & ENNToolMiRCARTImporter._CellState.CS_BOLD:
pilFont = pilFontBold
elif fontAttr == ENNToolMiRCARTImporter._CellState.CS_UNDERLINE:
if fontAttr & ENNToolMiRCARTImporter._CellState.CS_UNDERLINE:
underLine = True
else:
raise ValueError
if fontChar in string.printable:
pilImageDraw.text(curPos, fontChar,
pilImageTmpDraw.text((0, 0), fontChar,
(*ENNToolMiRCARTColours[fontColour], 255), pilFont)
pilImage.paste(pilImageTmp, tuple(curPos))
pilImageTmpDraw.rectangle((0, 0, pilFontSize[0], pilFontSize[1]),
fill=(0, 0, 0, 0))
if underLine:
pilImageDraw.line(
xy=(curPos[0], curPos[1] + (pilFontSize[1] - 2),
curPos[0] + pilFontSize[0], curPos[1] + (pilFontSize[1] - 2)),
fill=(*ENNToolMiRCARTColours[fontColour], 255))
if not fontChar in charMap:
charMap[fontChar] = {}
if not fontColour in charMap[fontChar]:
charMap[fontChar][fontColour] = []
charMap[fontChar][fontColour] \
+= [{"attrs":fontAttrs,
"bl":[float((curPos[0])/pilImageSize[0]), float((curPos[1])/pilImageSize[1])],
"br":[float((curPos[0] + pilFontSize[0])/pilImageSize[0]), float((curPos[1])/pilImageSize[1])],
"tl":[float((curPos[0])/pilImageSize[0]), float((curPos[1] + pilFontSize[1])/pilImageSize[1])],
"tr":[float((curPos[0] + pilFontSize[0])/pilImageSize[0]), float((curPos[1] + pilFontSize[1])/pilImageSize[1])]}]
curPos[0] += pilFontSize[0]
fontAttrName = ""
for fontAttr in fontAttrs:
if fontAttr == ENNToolMiRCARTImporter._CellState.CS_NONE:
fontAttrName += "Normal"
elif fontAttr == ENNToolMiRCARTImporter._CellState.CS_BOLD:
fontAttrName += "Bold"
elif fontAttr == ENNToolMiRCARTImporter._CellState.CS_UNDERLINE:
fontAttrName += "Underline"
else:
raise ValueError
pilImage.save(os.path.join(outPathName, "{}Fg{:02d}.png".format(fontAttrName, fontColour)))
curPos[0], curPos[1] = 0, curPos[1] + pilFontSize[1]
pilImage.save(outFileName)
artInfo = {}
artInfo["rowHeight"] = pilFontSize[1]
artInfo["rowWidth"] = pilFontSize[0]
artInfo["texHeight"] = pilImageSize[1]
artInfo["texWidth"] = pilImageSize[0]
with open(outInfoFileName, "w") as fileObject:
yaml.dump(charMap, fileObject)
yaml.dump(artInfo, fileObject)
if __name__ == "__main__":
main(*sys.argv)

BIN
assets/DejaVuSansMono.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 18 B

1
assets/texture.png Symbolic link
View File

@ -0,0 +1 @@
DejaVuSansMono.png

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 18 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB