30
|
1 |
cdef extern from "errno.h" :
|
|
2 |
extern int errno
|
|
3 |
|
|
4 |
cdef extern from "string.h" :
|
|
5 |
char* strerror (int err)
|
|
6 |
|
78
|
7 |
void* memset (void *, int, size_t)
|
|
8 |
void* memcpy (void *, void *, size_t)
|
|
9 |
|
30
|
10 |
cimport stdio
|
|
11 |
cimport stdlib
|
|
12 |
cimport python_string
|
|
13 |
|
|
14 |
cdef extern from "Python.h" :
|
|
15 |
int PyFile_Check (object p)
|
|
16 |
stdio.FILE* PyFile_AsFile (object p)
|
|
17 |
void PyFile_IncUseCount (object p)
|
|
18 |
void PyFile_DecUseCount (object p)
|
|
19 |
|
|
20 |
cdef extern from "pngtile.h" :
|
|
21 |
struct pt_ctx :
|
|
22 |
pass
|
|
23 |
|
|
24 |
struct pt_image :
|
|
25 |
pass
|
|
26 |
|
|
27 |
enum pt_open_mode :
|
78
|
28 |
PT_OPEN_READ # 0
|
30
|
29 |
PT_OPEN_UPDATE
|
|
30 |
|
|
31 |
enum pt_cache_status :
|
78
|
32 |
PT_CACHE_ERROR # -1
|
30
|
33 |
PT_CACHE_FRESH
|
|
34 |
PT_CACHE_NONE
|
|
35 |
PT_CACHE_STALE
|
57
|
36 |
PT_CACHE_INCOMPAT
|
30
|
37 |
|
|
38 |
struct pt_image_info :
|
78
|
39 |
size_t img_width, img_height, img_bpp
|
|
40 |
int image_mtime, cache_mtime, cache_version
|
|
41 |
size_t image_bytes, cache_bytes
|
57
|
42 |
size_t cache_blocks
|
|
43 |
|
|
44 |
struct pt_image_params :
|
|
45 |
int background_color[4]
|
83
|
46 |
|
30
|
47 |
struct pt_tile_info :
|
|
48 |
size_t width, height
|
|
49 |
size_t x, y
|
34
|
50 |
int zoom
|
83
|
51 |
|
|
52 |
## functions
|
30
|
53 |
int pt_image_open (pt_image **image_ptr, pt_ctx *ctx, char *png_path, int cache_mode)
|
83
|
54 |
int pt_image_info_ "pt_image_info" (pt_image *image, pt_image_info **info_ptr)
|
30
|
55 |
int pt_image_status (pt_image *image)
|
57
|
56 |
int pt_image_update (pt_image *image, pt_image_params *params)
|
30
|
57 |
int pt_image_tile_file (pt_image *image, pt_tile_info *info, stdio.FILE *out)
|
|
58 |
int pt_image_tile_mem (pt_image *image, pt_tile_info *info, char **buf_ptr, size_t *len_ptr)
|
|
59 |
void pt_image_destroy (pt_image *image)
|
83
|
60 |
|
|
61 |
# error code -> name
|
30
|
62 |
char* pt_strerror (int err)
|
|
63 |
|
78
|
64 |
## constants
|
|
65 |
# Image()
|
|
66 |
OPEN_READ = PT_OPEN_READ
|
|
67 |
OPEN_UPDATE = PT_OPEN_UPDATE
|
|
68 |
|
|
69 |
# Image.status -> ...
|
|
70 |
CACHE_FRESH = PT_CACHE_FRESH
|
|
71 |
CACHE_NONE = PT_CACHE_NONE
|
|
72 |
CACHE_STALE = PT_CACHE_STALE
|
|
73 |
CACHE_INCOMPAT = PT_CACHE_INCOMPAT
|
30
|
74 |
|
|
75 |
class Error (BaseException) :
|
|
76 |
pass
|
|
77 |
|
83
|
78 |
# raise Error if the given return value is <0
|
30
|
79 |
cdef int trap_err (char *op, int ret) except -1 :
|
|
80 |
if ret < 0 :
|
|
81 |
raise Error("%s: %s: %s" % (op, pt_strerror(ret), strerror(errno)))
|
|
82 |
|
|
83 |
else :
|
|
84 |
return ret
|
|
85 |
|
|
86 |
cdef class Image :
|
78
|
87 |
"""
|
|
88 |
An image file on disk (.png) and an associated .cache file.
|
|
89 |
|
|
90 |
Open the .png file at the given path using the given mode.
|
|
91 |
|
|
92 |
path - filesystem path to .png file
|
|
93 |
mode - mode to operate cache in
|
|
94 |
OPEN_READ - read-only access to cache
|
|
95 |
OPEN_UPDATE - allow .update()
|
|
96 |
"""
|
|
97 |
|
30
|
98 |
cdef pt_image *image
|
|
99 |
|
78
|
100 |
|
|
101 |
# open the pt_image
|
|
102 |
def __cinit__ (self, char *path, int mode = 0) :
|
30
|
103 |
trap_err("pt_image_open",
|
78
|
104 |
pt_image_open(&self.image, NULL, path, mode)
|
30
|
105 |
)
|
78
|
106 |
|
|
107 |
|
30
|
108 |
def info (self) :
|
78
|
109 |
"""
|
|
110 |
Return a dictionary containing various information about the image.
|
|
111 |
|
|
112 |
img_width - pixel dimensions of the source image
|
|
113 |
img_height only available if the cache was opened
|
|
114 |
img_bpp - bits per pixel for the source image
|
|
115 |
|
|
116 |
image_mtime - last modification timestamp for source image
|
|
117 |
image_bytes - size of source image file in bytes
|
|
118 |
|
|
119 |
cache_version - version of cache file available
|
|
120 |
cache_mtime - last modification timestamp for cache file
|
|
121 |
cache_bytes - size of cache file in bytes
|
|
122 |
cache_blocks - size of cache file in disk blocks - 512 bytes / block
|
|
123 |
"""
|
|
124 |
|
|
125 |
cdef pt_image_info *info
|
30
|
126 |
|
|
127 |
trap_err("pt_image_info",
|
83
|
128 |
pt_image_info_(self.image, &info)
|
30
|
129 |
)
|
|
130 |
|
78
|
131 |
# return as a struct
|
|
132 |
return info[0]
|
|
133 |
|
|
134 |
|
30
|
135 |
def status (self) :
|
78
|
136 |
"""
|
|
137 |
Return a code describing the status of the underlying cache file.
|
|
138 |
|
|
139 |
CACHE_FRESH - the cache file exists and is up-to-date
|
|
140 |
CACHE_NONE - the cache file does not exist
|
|
141 |
CACHE_STALE - the cache file exists, but is older than the source image
|
|
142 |
CACHE_INCOMPAT - the cache file exists, but is incompatible with this version of the library
|
|
143 |
"""
|
|
144 |
|
30
|
145 |
return trap_err("pt_image_status",
|
|
146 |
pt_image_status(self.image)
|
|
147 |
)
|
78
|
148 |
|
|
149 |
|
|
150 |
def update (self, background_color = None) :
|
|
151 |
"""
|
|
152 |
Update the underlying cache file from the source image.
|
|
153 |
|
|
154 |
background_color - skip consecutive pixels that match this byte pattern in output
|
|
155 |
|
|
156 |
Requires that the Image was opened using OPEN_UPDATE.
|
|
157 |
"""
|
|
158 |
|
|
159 |
cdef pt_image_params params
|
|
160 |
cdef char *bgcolor
|
|
161 |
memset(¶ms, 0, sizeof(params))
|
|
162 |
|
|
163 |
# params
|
|
164 |
if background_color :
|
|
165 |
# cast
|
|
166 |
bgcolor = <char *>background_color
|
|
167 |
|
|
168 |
if 0 >= len(bgcolor) > 4 :
|
|
169 |
raise ValueError("background_color must be a str of between 1 and 4 bytes")
|
|
170 |
|
|
171 |
# decode
|
|
172 |
memcpy(params.background_color, bgcolor, len(bgcolor))
|
30
|
173 |
|
78
|
174 |
# run update
|
30
|
175 |
trap_err("pt_image_update",
|
78
|
176 |
pt_image_update(self.image, ¶ms)
|
30
|
177 |
)
|
|
178 |
|
78
|
179 |
|
34
|
180 |
def tile_file (self, size_t width, size_t height, size_t x, size_t y, int zoom, object out) :
|
83
|
181 |
"""
|
|
182 |
Render a region of the source image as a PNG tile to the given output file.
|
|
183 |
|
|
184 |
width - dimensions of the output tile in px
|
|
185 |
height
|
|
186 |
x - coordinates in the source file
|
|
187 |
y
|
|
188 |
zoom - zoom level: out = 2**(-zoom) * in
|
|
189 |
out - output file
|
|
190 |
|
|
191 |
Note that the given file object MUST be a *real* stdio FILE*, not a fake Python object.
|
|
192 |
"""
|
|
193 |
|
30
|
194 |
cdef stdio.FILE *outf
|
|
195 |
cdef pt_tile_info ti
|
|
196 |
|
83
|
197 |
memset(&ti, 0, sizeof(ti))
|
|
198 |
|
|
199 |
# convert to FILE
|
30
|
200 |
if not PyFile_Check(out) :
|
|
201 |
raise TypeError("out: must be a file object")
|
|
202 |
|
|
203 |
outf = PyFile_AsFile(out)
|
|
204 |
|
|
205 |
if not outf :
|
|
206 |
raise TypeError("out: must have a FILE*")
|
83
|
207 |
|
|
208 |
# pack params
|
30
|
209 |
ti.width = width
|
|
210 |
ti.height = height
|
|
211 |
ti.x = x
|
|
212 |
ti.y = y
|
34
|
213 |
ti.zoom = zoom
|
30
|
214 |
|
83
|
215 |
# render
|
30
|
216 |
trap_err("pt_image_tile_file",
|
|
217 |
pt_image_tile_file(self.image, &ti, outf)
|
|
218 |
)
|
|
219 |
|
78
|
220 |
|
34
|
221 |
def tile_mem (self, size_t width, size_t height, size_t x, size_t y, int zoom) :
|
83
|
222 |
"""
|
|
223 |
Render a region of the source image as a PNG tile, and return the PNG data a a string.
|
|
224 |
|
|
225 |
width - dimensions of the output tile in px
|
|
226 |
height
|
|
227 |
x - coordinates in the source file
|
|
228 |
y
|
|
229 |
zoom - zoom level: out = 2**(-zoom) * in
|
|
230 |
"""
|
|
231 |
|
30
|
232 |
cdef pt_tile_info ti
|
|
233 |
cdef char *buf
|
|
234 |
cdef size_t len
|
83
|
235 |
|
|
236 |
memset(&ti, 0, sizeof(ti))
|
|
237 |
|
|
238 |
# pack params
|
30
|
239 |
ti.width = width
|
|
240 |
ti.height = height
|
|
241 |
ti.x = x
|
|
242 |
ti.y = y
|
34
|
243 |
ti.zoom = zoom
|
30
|
244 |
|
83
|
245 |
# render and return via buf/len
|
30
|
246 |
trap_err("pt_image_tile_mem",
|
|
247 |
pt_image_tile_mem(self.image, &ti, &buf, &len)
|
|
248 |
)
|
|
249 |
|
|
250 |
# copy buffer as str...
|
|
251 |
data = python_string.PyString_FromStringAndSize(buf, len)
|
|
252 |
|
|
253 |
# drop buffer...
|
|
254 |
stdlib.free(buf)
|
|
255 |
|
|
256 |
return data
|
|
257 |
|
78
|
258 |
# release the pt_image
|
30
|
259 |
def __dealloc__ (self) :
|
|
260 |
if self.image :
|
|
261 |
pt_image_destroy(self.image)
|
|
262 |
|
83
|
263 |
self.image = NULL
|
|
264 |
|