Merging in JPEG rendering code
Merge remote branch 'alexjurkiewicz/master'
This commit is contained in:
@@ -121,6 +121,12 @@ Options
|
|||||||
|
|
||||||
python gmap.py --cachedir=<chunk cache dir> <world> <output dir>
|
python gmap.py --cachedir=<chunk cache dir> <world> <output dir>
|
||||||
|
|
||||||
|
--imgformat=FORMAT
|
||||||
|
Set the output image format used for the tiles. The default is 'png',
|
||||||
|
but 'jpg' is also supported. Note that regardless of what you choose,
|
||||||
|
Overviewer will still use PNG for cached images to avoid recompression
|
||||||
|
artifacts.
|
||||||
|
|
||||||
-p PROCS, --processes=PROCS
|
-p PROCS, --processes=PROCS
|
||||||
Adding the "-p" option will utilize more cores during processing. This
|
Adding the "-p" option will utilize more cores during processing. This
|
||||||
can speed up rendering quite a bit. The default is set to the same
|
can speed up rendering quite a bit. The default is set to the same
|
||||||
|
|||||||
14
gmap.py
14
gmap.py
@@ -32,8 +32,7 @@ import quadtree
|
|||||||
|
|
||||||
helptext = """
|
helptext = """
|
||||||
%prog [OPTIONS] <World # / Path to World> <tiles dest dir>
|
%prog [OPTIONS] <World # / Path to World> <tiles dest dir>
|
||||||
%prog -d <World # / Path to World / Path to cache dir> [tiles dest dir]
|
%prog -d <World # / Path to World / Path to cache dir> [tiles dest dir]"""
|
||||||
"""
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
@@ -46,6 +45,7 @@ def main():
|
|||||||
parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true")
|
parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true")
|
||||||
parser.add_option("--cachedir", dest="cachedir", help="Sets the directory where the Overviewer will save chunk images, which is an intermediate step before the tiles are generated. You must use the same directory each time to gain any benefit from the cache. If not set, this defaults to your world directory.")
|
parser.add_option("--cachedir", dest="cachedir", help="Sets the directory where the Overviewer will save chunk images, which is an intermediate step before the tiles are generated. You must use the same directory each time to gain any benefit from the cache. If not set, this defaults to your world directory.")
|
||||||
parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.")
|
parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.")
|
||||||
|
parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg. NOTE: png will always be used as the intermediate image format.")
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
@@ -85,12 +85,20 @@ def main():
|
|||||||
else:
|
else:
|
||||||
chunklist = None
|
chunklist = None
|
||||||
|
|
||||||
|
if options.imgformat:
|
||||||
|
if options.imgformat not in ('jpg','png'):
|
||||||
|
parser.error("Unknown imgformat!")
|
||||||
|
else:
|
||||||
|
imgformat = options.imgformat
|
||||||
|
else:
|
||||||
|
imgformat = 'png'
|
||||||
|
|
||||||
# First generate the world's chunk images
|
# First generate the world's chunk images
|
||||||
w = world.WorldRenderer(worlddir, cachedir, chunklist=chunklist)
|
w = world.WorldRenderer(worlddir, cachedir, chunklist=chunklist)
|
||||||
w.go(options.procs)
|
w.go(options.procs)
|
||||||
|
|
||||||
# Now generate the tiles
|
# Now generate the tiles
|
||||||
q = quadtree.QuadtreeGen(w, destdir, depth=options.zoom)
|
q = quadtree.QuadtreeGen(w, destdir, depth=options.zoom, imgformat=imgformat)
|
||||||
q.go(options.procs)
|
q.go(options.procs)
|
||||||
|
|
||||||
def delete_all(worlddir, tiledir):
|
def delete_all(worlddir, tiledir):
|
||||||
|
|||||||
52
quadtree.py
52
quadtree.py
@@ -52,7 +52,7 @@ def catch_keyboardinterrupt(func):
|
|||||||
return newfunc
|
return newfunc
|
||||||
|
|
||||||
class QuadtreeGen(object):
|
class QuadtreeGen(object):
|
||||||
def __init__(self, worldobj, destdir, depth=None):
|
def __init__(self, worldobj, destdir, depth=None, imgformat=None):
|
||||||
"""Generates a quadtree from the world given into the
|
"""Generates a quadtree from the world given into the
|
||||||
given dest directory
|
given dest directory
|
||||||
|
|
||||||
@@ -62,6 +62,9 @@ class QuadtreeGen(object):
|
|||||||
minimum depth that contains all chunks is calculated and used.
|
minimum depth that contains all chunks is calculated and used.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
assert(imgformat)
|
||||||
|
self.imgformat = imgformat
|
||||||
|
|
||||||
if depth is None:
|
if depth is None:
|
||||||
# Determine quadtree depth (midpoint is always 0,0)
|
# Determine quadtree depth (midpoint is always 0,0)
|
||||||
for p in xrange(15):
|
for p in xrange(15):
|
||||||
@@ -109,13 +112,15 @@ class QuadtreeGen(object):
|
|||||||
print "{0}/{1} tiles complete on level {2}/{3}".format(
|
print "{0}/{1} tiles complete on level {2}/{3}".format(
|
||||||
complete, total, level, self.p)
|
complete, total, level, self.p)
|
||||||
|
|
||||||
def write_html(self, zoomlevel):
|
def write_html(self, zoomlevel, imgformat):
|
||||||
"""Writes out index.html"""
|
"""Writes out index.html"""
|
||||||
templatepath = os.path.join(os.path.split(__file__)[0], "template.html")
|
templatepath = os.path.join(os.path.split(__file__)[0], "template.html")
|
||||||
|
|
||||||
html = open(templatepath, 'r').read()
|
html = open(templatepath, 'r').read()
|
||||||
html = html.replace(
|
html = html.replace(
|
||||||
"{maxzoom}", str(zoomlevel))
|
"{maxzoom}", str(zoomlevel))
|
||||||
|
html = html.replace(
|
||||||
|
"{imgformat}", str(imgformat))
|
||||||
|
|
||||||
with open(os.path.join(self.destdir, "index.html"), 'w') as output:
|
with open(os.path.join(self.destdir, "index.html"), 'w') as output:
|
||||||
output.write(html)
|
output.write(html)
|
||||||
@@ -159,8 +164,8 @@ class QuadtreeGen(object):
|
|||||||
newdir = "new" + str(dirnum)
|
newdir = "new" + str(dirnum)
|
||||||
newdirpath = getpath(newdir)
|
newdirpath = getpath(newdir)
|
||||||
|
|
||||||
files = [str(dirnum)+".png", str(dirnum)+".hash", str(dirnum)]
|
files = [str(dirnum)+"."+imgformat, str(dirnum)+".hash", str(dirnum)]
|
||||||
newfiles = [str(newnum)+".png", str(newnum)+".hash", str(newnum)]
|
newfiles = [str(newnum)+"."+imgformat, str(newnum)+".hash", str(newnum)]
|
||||||
|
|
||||||
os.mkdir(newdirpath)
|
os.mkdir(newdirpath)
|
||||||
for f, newf in zip(files, newfiles):
|
for f, newf in zip(files, newfiles):
|
||||||
@@ -221,7 +226,7 @@ class QuadtreeGen(object):
|
|||||||
# (even if tilechunks is empty, render_worldtile will delete
|
# (even if tilechunks is empty, render_worldtile will delete
|
||||||
# existing images if appropriate)
|
# existing images if appropriate)
|
||||||
yield pool.apply_async(func=render_worldtile, args= (tilechunks,
|
yield pool.apply_async(func=render_worldtile, args= (tilechunks,
|
||||||
colstart, colend, rowstart, rowend, dest))
|
colstart, colend, rowstart, rowend, dest, self.imgformat))
|
||||||
|
|
||||||
def _apply_render_inntertile(self, pool, zoom):
|
def _apply_render_inntertile(self, pool, zoom):
|
||||||
"""Same as _apply_render_worltiles but for the inntertile routine.
|
"""Same as _apply_render_worltiles but for the inntertile routine.
|
||||||
@@ -233,7 +238,7 @@ class QuadtreeGen(object):
|
|||||||
dest = os.path.join(self.destdir, "tiles", *(str(x) for x in path[:-1]))
|
dest = os.path.join(self.destdir, "tiles", *(str(x) for x in path[:-1]))
|
||||||
name = str(path[-1])
|
name = str(path[-1])
|
||||||
|
|
||||||
yield pool.apply_async(func=render_innertile, args= (dest, name))
|
yield pool.apply_async(func=render_innertile, args= (dest, name, self.imgformat))
|
||||||
|
|
||||||
def go(self, procs):
|
def go(self, procs):
|
||||||
"""Renders all tiles"""
|
"""Renders all tiles"""
|
||||||
@@ -260,7 +265,7 @@ class QuadtreeGen(object):
|
|||||||
else:
|
else:
|
||||||
pool = multiprocessing.Pool(processes=procs)
|
pool = multiprocessing.Pool(processes=procs)
|
||||||
|
|
||||||
self.write_html(self.p)
|
self.write_html(self.p, self.imgformat)
|
||||||
|
|
||||||
# Render the highest level of tiles from the chunks
|
# Render the highest level of tiles from the chunks
|
||||||
results = collections.deque()
|
results = collections.deque()
|
||||||
@@ -317,7 +322,7 @@ class QuadtreeGen(object):
|
|||||||
pool.join()
|
pool.join()
|
||||||
|
|
||||||
# Do the final one right here:
|
# Do the final one right here:
|
||||||
render_innertile(os.path.join(self.destdir, "tiles"), "base")
|
render_innertile(os.path.join(self.destdir, "tiles"), "base", self.imgformat)
|
||||||
|
|
||||||
def _get_range_by_path(self, path):
|
def _get_range_by_path(self, path):
|
||||||
"""Returns the x, y chunk coordinates of this tile"""
|
"""Returns the x, y chunk coordinates of this tile"""
|
||||||
@@ -348,28 +353,28 @@ class QuadtreeGen(object):
|
|||||||
return chunklist
|
return chunklist
|
||||||
|
|
||||||
@catch_keyboardinterrupt
|
@catch_keyboardinterrupt
|
||||||
def render_innertile(dest, name):
|
def render_innertile(dest, name, imgformat):
|
||||||
"""
|
"""
|
||||||
Renders a tile at os.path.join(dest, name)+".png" by taking tiles from
|
Renders a tile at os.path.join(dest, name)+".ext" by taking tiles from
|
||||||
os.path.join(dest, name, "{0,1,2,3}.png")
|
os.path.join(dest, name, "{0,1,2,3}.png")
|
||||||
"""
|
"""
|
||||||
imgpath = os.path.join(dest, name) + ".png"
|
imgpath = os.path.join(dest, name) + "." + imgformat
|
||||||
hashpath = os.path.join(dest, name) + ".hash"
|
hashpath = os.path.join(dest, name) + ".hash"
|
||||||
|
|
||||||
if name == "base":
|
if name == "base":
|
||||||
q0path = os.path.join(dest, "0.png")
|
q0path = os.path.join(dest, "0." + imgformat)
|
||||||
q1path = os.path.join(dest, "1.png")
|
q1path = os.path.join(dest, "1." + imgformat)
|
||||||
q2path = os.path.join(dest, "2.png")
|
q2path = os.path.join(dest, "2." + imgformat)
|
||||||
q3path = os.path.join(dest, "3.png")
|
q3path = os.path.join(dest, "3." + imgformat)
|
||||||
q0hash = os.path.join(dest, "0.hash")
|
q0hash = os.path.join(dest, "0.hash")
|
||||||
q1hash = os.path.join(dest, "1.hash")
|
q1hash = os.path.join(dest, "1.hash")
|
||||||
q2hash = os.path.join(dest, "2.hash")
|
q2hash = os.path.join(dest, "2.hash")
|
||||||
q3hash = os.path.join(dest, "3.hash")
|
q3hash = os.path.join(dest, "3.hash")
|
||||||
else:
|
else:
|
||||||
q0path = os.path.join(dest, name, "0.png")
|
q0path = os.path.join(dest, name, "0." + imgformat)
|
||||||
q1path = os.path.join(dest, name, "1.png")
|
q1path = os.path.join(dest, name, "1." + imgformat)
|
||||||
q2path = os.path.join(dest, name, "2.png")
|
q2path = os.path.join(dest, name, "2." + imgformat)
|
||||||
q3path = os.path.join(dest, name, "3.png")
|
q3path = os.path.join(dest, name, "3." + imgformat)
|
||||||
q0hash = os.path.join(dest, name, "0.hash")
|
q0hash = os.path.join(dest, name, "0.hash")
|
||||||
q1hash = os.path.join(dest, name, "1.hash")
|
q1hash = os.path.join(dest, name, "1.hash")
|
||||||
q2hash = os.path.join(dest, name, "2.hash")
|
q2hash = os.path.join(dest, name, "2.hash")
|
||||||
@@ -434,13 +439,16 @@ def render_innertile(dest, name):
|
|||||||
img.paste(quad3, (192, 192))
|
img.paste(quad3, (192, 192))
|
||||||
|
|
||||||
# Save it
|
# Save it
|
||||||
|
if imgformat == 'jpg':
|
||||||
|
img.save(imgpath, quality=95, subsampling=0)
|
||||||
|
else: # png
|
||||||
img.save(imgpath)
|
img.save(imgpath)
|
||||||
with open(hashpath, "wb") as hashout:
|
with open(hashpath, "wb") as hashout:
|
||||||
hashout.write(newhash)
|
hashout.write(newhash)
|
||||||
|
|
||||||
|
|
||||||
@catch_keyboardinterrupt
|
@catch_keyboardinterrupt
|
||||||
def render_worldtile(chunks, colstart, colend, rowstart, rowend, path):
|
def render_worldtile(chunks, colstart, colend, rowstart, rowend, path, imgformat):
|
||||||
"""Renders just the specified chunks into a tile and save it. Unlike usual
|
"""Renders just the specified chunks into a tile and save it. Unlike usual
|
||||||
python conventions, rowend and colend are inclusive. Additionally, the
|
python conventions, rowend and colend are inclusive. Additionally, the
|
||||||
chunks around the edges are half-way cut off (so that neighboring tiles
|
chunks around the edges are half-way cut off (so that neighboring tiles
|
||||||
@@ -449,7 +457,7 @@ def render_worldtile(chunks, colstart, colend, rowstart, rowend, path):
|
|||||||
chunks is a list of (col, row, filename) of chunk images that are relevant
|
chunks is a list of (col, row, filename) of chunk images that are relevant
|
||||||
to this call
|
to this call
|
||||||
|
|
||||||
The image is saved to path+".png" and a hash is saved to path+".hash"
|
The image is saved to path+".ext" and a hash is saved to path+".hash"
|
||||||
|
|
||||||
If there are no chunks, this tile is not saved (if it already exists, it is
|
If there are no chunks, this tile is not saved (if it already exists, it is
|
||||||
deleted)
|
deleted)
|
||||||
@@ -490,7 +498,7 @@ def render_worldtile(chunks, colstart, colend, rowstart, rowend, path):
|
|||||||
# Before we render any tiles, check the hash of each image in this tile to
|
# Before we render any tiles, check the hash of each image in this tile to
|
||||||
# see if it's changed.
|
# see if it's changed.
|
||||||
hashpath = path + ".hash"
|
hashpath = path + ".hash"
|
||||||
imgpath = path + ".png"
|
imgpath = path + "." + imgformat
|
||||||
|
|
||||||
if not chunks:
|
if not chunks:
|
||||||
# No chunks were found in this tile
|
# No chunks were found in this tile
|
||||||
|
|||||||
@@ -14,11 +14,11 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var config = {
|
var config = {
|
||||||
path: 'tiles',
|
path: 'tiles',
|
||||||
fileExt: 'png',
|
fileExt: '{imgformat}',
|
||||||
tileSize: 384,
|
tileSize: 384,
|
||||||
defaultZoom: 1,
|
defaultZoom: 1,
|
||||||
maxZoom: {maxzoom},
|
maxZoom: {maxzoom},
|
||||||
cacheMinutes: 0, // Change this to have browsers automatically requiest new images every x minutes
|
cacheMinutes: 0, // Change this to have browsers automatically request new images every x minutes
|
||||||
debug: false
|
debug: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user