Index: trunk/pngds/pngresizer.py |
— | — | @@ -0,0 +1,74 @@ |
| 2 | +import math |
| 3 | + |
| 4 | +from pngreader import * |
| 5 | + |
| 6 | +class PNGResizer(PNGReader): |
| 7 | + def __init__(self, stream, width, height, out = None): |
| 8 | + PNGReader.__init__(self, stream) |
| 9 | + self.resize_width = width |
| 10 | + self.resize_height = height |
| 11 | + |
| 12 | + self.current_scanline = 0 |
| 13 | + self.written_lines = 0 |
| 14 | + self.scanlines = [] |
| 15 | + self.last_line = '' |
| 16 | + |
| 17 | + if out: |
| 18 | + self.out = out |
| 19 | + else: |
| 20 | + self.out = StringIO() |
| 21 | + |
| 22 | + def read_chunk(self): |
| 23 | + has_data = PNGReader.read_chunk(self) |
| 24 | + if not has_data: |
| 25 | + while self.written_lines < self.resize_height: |
| 26 | + self.out.write(self.last_line) |
| 27 | + self.written_lines += 1 |
| 28 | + return has_data |
| 29 | + |
| 30 | + def read_header(self, length): |
| 31 | + PNGReader.read_header(self, length) |
| 32 | + |
| 33 | + self.fx = float(self.width) / self.resize_width |
| 34 | + self.fy = float(self.height) / self.resize_height |
| 35 | + if self.fx < 1.0 or self.fy < 1.0: |
| 36 | + raise ValueError('Upsampling unsupported') |
| 37 | + |
| 38 | + def completed_scanline(self): |
| 39 | + pixels = tuple(self.read_scanline()) |
| 40 | + |
| 41 | + current_scanline = [] |
| 42 | + for i in xrange(self.resize_width): |
| 43 | + start = int(math.ceil(self.fx * i)) |
| 44 | + end = int(math.ceil(self.fx * (i + 1))) |
| 45 | + divisor = end - start |
| 46 | + |
| 47 | + new_pixel = [0] * COLOR_DEPTH[self.colortype] |
| 48 | + for j in xrange(len(new_pixel)): |
| 49 | + for k in xrange(end - start): |
| 50 | + new_pixel[j] += ord(pixels[start + k][j]) / divisor |
| 51 | + new_pixel[j] = chr(new_pixel[j]) |
| 52 | + |
| 53 | + #current_scanline.append(''.join(new_pixel)) |
| 54 | + current_scanline.extend(new_pixel) |
| 55 | + |
| 56 | + self.scanlines.append(current_scanline) |
| 57 | + self.current_scanline += 1 |
| 58 | + if (self.current_scanline / self.fy) > (self.written_lines + 1): |
| 59 | + line = [0] * len(current_scanline) |
| 60 | + for i in xrange(len(line)): |
| 61 | + for j in xrange(len(self.scanlines)): |
| 62 | + line[i] += ord(self.scanlines[j][i]) / len(self.scanlines) |
| 63 | + line[i] = chr(line[i]) |
| 64 | + self.last_line = ''.join(line) |
| 65 | + |
| 66 | + self.out.write(self.last_line) |
| 67 | + self.scanlines = [] |
| 68 | + self.written_lines += 1 |
| 69 | + |
| 70 | +def test(filename): |
| 71 | + reader = PNGResizer(open(filename, 'rb'), 32, 32) |
| 72 | + reader.parse() |
| 73 | + reader.scanline = reader.previous_scanline = None |
| 74 | + print reader.__dict__ |
| 75 | + return reader |
Property changes on: trunk/pngds/pngresizer.py |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 76 | + native |
Index: trunk/pngds/pngreader.c |
— | — | @@ -0,0 +1,232 @@ |
| 2 | +#include "zlib.h" |
| 3 | + |
| 4 | +#include "pngreader.h" |
| 5 | + |
| 6 | +#define BUFFER_IN_SIZE 32768 |
| 7 | +#define BUFFER_OUT_SIZE 65536 |
| 8 | + |
| 9 | +void png_resize(FILE* fin, FILE* fout, unsigned int width, unsigned int height) |
| 10 | +{ |
| 11 | + pngreader info; |
| 12 | + |
| 13 | + info.width = width; |
| 14 | + info.height = height; |
| 15 | + info.fin = fin; |
| 16 | + info.fout = fout; |
| 17 | + |
| 18 | + char header[8]; |
| 19 | + fread(header, 1, 8, fin); |
| 20 | + if (strcmp(header, "\x89PNG\r\n\x1a\n", 8)) |
| 21 | + png_die("header", header, 8); |
| 22 | + |
| 23 | + while (png_read_chunk(&info)); |
| 24 | +} |
| 25 | + |
| 26 | +int png_die(char *msg, void *data, int data_len) |
| 27 | +{ |
| 28 | + fwrite(msg, 1, strlen(msg), stderr); |
| 29 | + exit(1); |
| 30 | +} |
| 31 | + |
| 32 | +int png_read_chunk(pngresize *info) |
| 33 | +{ |
| 34 | + chunkheader c_head; |
| 35 | + fread(&c_head, 4, 2, info->fin); |
| 36 | + |
| 37 | + if (strncmp(c_head.type, "IHDR", 4) == 0) |
| 38 | + { |
| 39 | + png_read_header(info, c_head.length); |
| 40 | + } |
| 41 | + else if (strncmp(c_head.type, "PLTE", 4) == 0) |
| 42 | + { |
| 43 | + png_read_palette(info, c_head.length); |
| 44 | + } |
| 45 | + else if (strncmp(c_head.type, "IDAT", 4) == 0) |
| 46 | + { |
| 47 | + png_read_data(info, c_head.length); |
| 48 | + } |
| 49 | + else if (c_head.type[0] & 32) |
| 50 | + { |
| 51 | + png_read_ancillary(info, c_head.length); |
| 52 | + } |
| 53 | + else if (strncmp(c_head.type, "IEND", 4) != 0) |
| 54 | + { |
| 55 | + png_die("critical_chunk", c_head.type, 4); |
| 56 | + } |
| 57 | + |
| 58 | + unsigned int crc; |
| 59 | + fread(&crc, 4, 1, info->fin); |
| 60 | + |
| 61 | + return strncmp(c_head.type, "IEND", 42); |
| 62 | +} |
| 63 | + |
| 64 | +void png_read_header(pngreader *info, int length) |
| 65 | +{ |
| 66 | + info->header = malloc(sizeof(pngheader)); |
| 67 | + fread(info->header, sizeof(pngheader), 1, info->fin); |
| 68 | + |
| 69 | + if (info->header->compression != COMPRESS_DEFLATE) |
| 70 | + png_die("unknown_compression", info->header->compression, 1); |
| 71 | + if (info->header->filter_method != FILTER_METHOD_BASIC_ADAPTIVE) |
| 72 | + png_die("unknown_filter_method", info->header->filter_method, 1); |
| 73 | + if (info->header->interlace) |
| 74 | + png_die("interlace_unsupported", NULL, 0); |
| 75 | + |
| 76 | + info->bytedepth = info->header->bpp / 8; |
| 77 | + if (info->header->bitdepth % 8) info->bytedepth++; |
| 78 | + |
| 79 | + info->bpp = info->bytedepth; |
| 80 | + switch (info->header->colortype) |
| 81 | + { |
| 82 | + case COLOR_GRAY: |
| 83 | + case COLOR_PALETTE: |
| 84 | + info->bpp *= 1; |
| 85 | + break; |
| 86 | + case COLOR_GRAYA: |
| 87 | + info->bpp *= 2; |
| 88 | + break; |
| 89 | + case COLOR_RGB: |
| 90 | + info->bpp *= 3; |
| 91 | + break; |
| 92 | + case COLOR_RGBA: |
| 93 | + info->bpp *= 4; |
| 94 | + break; |
| 95 | + default: |
| 96 | + png_die("unknown_colortype", info->header->colortype, 1); |
| 97 | + } |
| 98 | + info->expect_filter = 0; |
| 99 | + info->previous_scanline = malloc(info->header->width * info->bpp); |
| 100 | + memset(info->previous_scanline, 0, info->header->width * info->bpp); |
| 101 | + info->current_scanline = malloc(info->header->width * info->bpp); |
| 102 | + |
| 103 | + info->zst.zalloc = (alloc_func)NULL; |
| 104 | + info->zst.zfree = (free_func)Z_NULL; |
| 105 | + |
| 106 | + inflateInit(&info->zst); |
| 107 | + |
| 108 | + info->zst.next_in = malloc(BUFFER_IN_SIZE); |
| 109 | + info->zst.next_out = malloc(BUFFER_OUT_SIZE); |
| 110 | +} |
| 111 | + |
| 112 | +void png_read_palette(pngreader *info, int length) |
| 113 | +{ |
| 114 | + if (length % 3) |
| 115 | + png_die("malformed_palette_length", length); |
| 116 | + |
| 117 | + info->palette = malloc(sizeof(*rgbcolor) * length / 3); |
| 118 | + for (int i = 0; i < length; i += 3) |
| 119 | + { |
| 120 | + *(info->palette + i / 3) = malloc(sizeof(color)); |
| 121 | + fread(*(info->palette + i / 3), 3, 1, info->fin); |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +void png_read_data(pngreader *info, int length) |
| 126 | +{ |
| 127 | + // Does not work, need to find out how the buffers work |
| 128 | + while (length) |
| 129 | + { |
| 130 | + int size = min(length, BUFFER_IN_SIZE); |
| 131 | + info->zst.next_in = malloc(size); |
| 132 | + info->zst.avail_in = size; |
| 133 | + fread(info->zst.next_in, 1, size, info->fin); |
| 134 | + length -= size; |
| 135 | + |
| 136 | + int err = inflate(info->zst, Z_SYNC_FLUSH); |
| 137 | + switch (err) |
| 138 | + { |
| 139 | + case (Z_STREAM_END): |
| 140 | + if (size) |
| 141 | + die_png("premature_input_end", NULL, 0); |
| 142 | + return; |
| 143 | + |
| 144 | + // Code from <http://svn.python.org/view/python/trunk/Modules/zlibmodule.c?rev=61874&view=auto> |
| 145 | + case (Z_BUF_ERROR): |
| 146 | + /* |
| 147 | + * If there is at least 1 byte of room according to zst.avail_out |
| 148 | + * and we get this error, assume that it means zlib cannot |
| 149 | + * process the inflate call() due to an error in the data. |
| 150 | + */ |
| 151 | + if (info->zst.avail_out > 0) |
| 152 | + die_debug("input_error", NULL, 0); |
| 153 | + // Fall through |
| 154 | + case (Z_OK): |
| 155 | + png_defilter(info, info->zst.next_out, |
| 156 | + BUFFER_OUT_SIZE - info->zst.avail_out); |
| 157 | + default: |
| 158 | + // Hmm... |
| 159 | + } |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +void png_defilter(pngreader *info, unsigned char *buffer, int size) |
| 164 | +{ |
| 165 | + for (int i = 0; i < size; i++) |
| 166 | + { |
| 167 | + int x; |
| 168 | + unsigned char byte = *(buffer + byte); |
| 169 | + |
| 170 | + if (info->expect_filter) |
| 171 | + { |
| 172 | + info->expect_filter = 0; |
| 173 | + info->filter = byte; |
| 174 | + continue; |
| 175 | + } |
| 176 | + |
| 177 | + switch (info->filter) |
| 178 | + { |
| 179 | + case (FILTER_NONE): |
| 180 | + break; |
| 181 | + case (FILTER_SUB): |
| 182 | + x = info->scan_pos - info->bpp; |
| 183 | + if (x >= 0) byte += *(info->current_scanline + x); |
| 184 | + break; |
| 185 | + case (FILTER_UP): |
| 186 | + byte += *(info->previous_scanline + info->scan_pos); |
| 187 | + break; |
| 188 | + case (FILTER_AVERAGE): |
| 189 | + x = info->scan_pos - info->bpp; |
| 190 | + if (x >= 0) |
| 191 | + byte += (*(info->current_scanline + x) + |
| 192 | + *(info->previous_scanline + info->scan_pos)) / 2; |
| 193 | + else |
| 194 | + byte += *(info->previous_scanline + info->scan_pos) / 2; |
| 195 | + break; |
| 196 | + case (FILTER_PAETH): |
| 197 | + unsigned char a, b, c; |
| 198 | + unsigned char pa, pb, pc; |
| 199 | + short p; |
| 200 | + |
| 201 | + x = info->scan_pos - info->bpp; |
| 202 | + b = *(info->previous_scanline + info->scan_pos); |
| 203 | + if (x >= 0) |
| 204 | + { |
| 205 | + a = *(info->current_scanline + x); |
| 206 | + c = *(info->previous_scanline + x); |
| 207 | + } |
| 208 | + else |
| 209 | + { |
| 210 | + a = c = 0; |
| 211 | + } |
| 212 | + |
| 213 | + p = a + b - c; |
| 214 | + pa = abs(p - a); |
| 215 | + pb = abs(p - b); |
| 216 | + pc = abs(p - c); |
| 217 | + |
| 218 | + if (pa <= pb && pa <= pc) byte += a; |
| 219 | + else if (pb <= pc) byte += b; |
| 220 | + else byte += c; |
| 221 | + default: |
| 222 | + png_die("unknown_filter", &info->filter, 1); |
| 223 | + } |
| 224 | + *(info->scanline + i) = byte; |
| 225 | + |
| 226 | + if ((info->scan_pos % info->bpp) == 0 && |
| 227 | + (info->scan_pos / info->bpp) == 0) |
| 228 | + { |
| 229 | + info->previous_scanline = info->current_scanline; |
| 230 | + info->expect_filter = 1; |
| 231 | + } |
| 232 | + } |
| 233 | +} |
\ No newline at end of file |
Property changes on: trunk/pngds/pngreader.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 234 | + native |
Index: trunk/pngds/README.txt |
— | — | @@ -0,0 +1,10 @@ |
| 2 | +Portable Network Graphics Downsampler is a tool which allows downsizing of PNG |
| 3 | +images without loading the entire file in memory. This makes it possible to |
| 4 | +resize extremely large PNGs. |
| 5 | + |
| 6 | +The implementation is Python works and uses indeed only few memory, but is much |
| 7 | +too slow for use. This implementation also only outputs raw data and does not |
| 8 | +recompress to PNG. |
| 9 | + |
| 10 | +The C version is supposed to be faster and even less memory using, but not yet |
| 11 | +working. |
\ No newline at end of file |
Property changes on: trunk/pngds/README.txt |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 12 | + native |
Index: trunk/pngds/pngreader.h |
— | — | @@ -0,0 +1,81 @@ |
| 2 | +#include "zlib.h" |
| 3 | + |
| 4 | +/* |
| 5 | + * Defines |
| 6 | + */ |
| 7 | + |
| 8 | +#define COLOR_GRAY 0 |
| 9 | +#define COLOR_RGB 2 |
| 10 | +#define COLOR_PALETTE 3 |
| 11 | +#define COLOR_GRAYA 4 |
| 12 | +#define COLOR_RGBA 6 |
| 13 | + |
| 14 | +#define COMPRESS_DEFLATE 0 |
| 15 | +#define COMPRESS_FLAG_DEFLATE 8 |
| 16 | + |
| 17 | +#define FILTER_METHOD_BASIC_ADAPTIVE 0 |
| 18 | + |
| 19 | +#define FILTER_NONE 0 |
| 20 | +#define FILTER_SUB 1 |
| 21 | +#define FILTER_UP 2 |
| 22 | +#define FILTER_AVERAGE 3 |
| 23 | +#define FILTER_PAETH 4 |
| 24 | + |
| 25 | + |
| 26 | +/* |
| 27 | + * Types |
| 28 | + */ |
| 29 | + |
| 30 | +typedef struct |
| 31 | +{ |
| 32 | + unsigned int width; |
| 33 | + unsigned int height; |
| 34 | + unsigned char bitdepth; |
| 35 | + unsigned char colortype; |
| 36 | + unsigned char compression; |
| 37 | + unsigned char filter_method; |
| 38 | + unsigned char interlace; |
| 39 | +} pngheader; |
| 40 | + |
| 41 | +typedef struct |
| 42 | +{ |
| 43 | + unsigned int length; |
| 44 | + char[4] type; |
| 45 | +} chunkheader; |
| 46 | + |
| 47 | +typedef struct |
| 48 | +{ |
| 49 | + unsigned char r; |
| 50 | + unsigned char g; |
| 51 | + unsigned char b; |
| 52 | +} rgbcolor; |
| 53 | + |
| 54 | +typedef struct |
| 55 | +{ |
| 56 | + pngheader *header; |
| 57 | + |
| 58 | + unsigned char bytedepth; |
| 59 | + unsigned char bpp; |
| 60 | + rgbcolor **palette; |
| 61 | + |
| 62 | + z_stream zst; |
| 63 | + |
| 64 | + unsigned char expect_filter; |
| 65 | + unsigned char filter; |
| 66 | + int scan_pos; |
| 67 | + unsigned char *previous_scanline; |
| 68 | + unsigned char *current_scanline; |
| 69 | + |
| 70 | + FILE *fin; |
| 71 | + FILE *fout; |
| 72 | + |
| 73 | + void *extra1; |
| 74 | +} pngreader; |
| 75 | + |
| 76 | +typedef struct |
| 77 | +{ |
| 78 | + unsigned int width; |
| 79 | + unsigned int height; |
| 80 | + double fx; |
| 81 | + double fy; |
| 82 | +} pngresize; |
\ No newline at end of file |
Property changes on: trunk/pngds/pngreader.h |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 83 | + native |
Index: trunk/pngds/pngreader.py |
— | — | @@ -0,0 +1,183 @@ |
| 2 | +""" |
| 3 | + PNGReader - Copyright 2008 - Bryan Tong Minh |
| 4 | + Licensed under the GNU Public License v2 or above |
| 5 | +""" |
| 6 | + |
| 7 | +import struct |
| 8 | +import zlib |
| 9 | +from cStringIO import StringIO |
| 10 | + |
| 11 | +COLOR_GRAY, COLOR_RGB, COLOR_PALETTE = (0, 2, 3) |
| 12 | +COLOR_GRAYA, COLOR_RGBA = (4, 6) |
| 13 | +COLOR_DEPTH = { |
| 14 | + COLOR_GRAY: 1, |
| 15 | + COLOR_RGB: 3, |
| 16 | + COLOR_PALETTE: 1, |
| 17 | + COLOR_GRAYA: 2, |
| 18 | + COLOR_RGBA: 4, |
| 19 | +} |
| 20 | + |
| 21 | +COMPRESS_DEFLATE = 0 |
| 22 | +COMPRESS_FLAG_DEFLATE = 8 |
| 23 | + |
| 24 | +FILTER_METHOD_BASIC_ADAPTIVE = 0 |
| 25 | + |
| 26 | +FILTER_NONE, FILTER_SUB, FILTER_UP = (0, 1, 2) |
| 27 | +FILTER_AVERAGE, FILTER_PAETH = (3, 4) |
| 28 | + |
| 29 | +class PNGReader(object): |
| 30 | + def __init__(self, stream, out = None): |
| 31 | + self.stream = stream |
| 32 | + self.previous_scanline = None |
| 33 | + self.scanline = [] |
| 34 | + self.expect_filter = True |
| 35 | + self.chunks = [] |
| 36 | + |
| 37 | + if out: |
| 38 | + self.out = out |
| 39 | + else: |
| 40 | + self.out = StringIO() |
| 41 | + |
| 42 | + def parse(self): |
| 43 | + header = self.stream.read(8) |
| 44 | + if header != '\x89PNG\r\n\x1a\n': |
| 45 | + raise ValueError('Invalid header', header) |
| 46 | + |
| 47 | + while self.read_chunk(): |
| 48 | + pass |
| 49 | + self.out.close() |
| 50 | + |
| 51 | + def read_chunk(self): |
| 52 | + length = struct.unpack('!L', self.stream.read(4))[0] |
| 53 | + chunk_type = self.stream.read(4) |
| 54 | + ancillary = ord(chunk_type[0]) & 32 |
| 55 | + private = ord(chunk_type[1]) & 32 |
| 56 | + |
| 57 | + self.chunks.append(chunk_type) |
| 58 | + |
| 59 | + if chunk_type == 'IHDR': |
| 60 | + self.read_header(length) |
| 61 | + elif chunk_type == 'PLTE': |
| 62 | + self.read_palette(length) |
| 63 | + elif chunk_type == 'IDAT': |
| 64 | + self.read_data(length) |
| 65 | + elif ancillary: |
| 66 | + self.stream.read(length) |
| 67 | + elif chunk_type != 'IEND': |
| 68 | + raise ValueError('Unrecognized critical chunk', chunk_type) |
| 69 | + |
| 70 | + crc = self.stream.read(4) |
| 71 | + |
| 72 | + if chunk_type == 'IEND': |
| 73 | + return False |
| 74 | + else: |
| 75 | + return True |
| 76 | + |
| 77 | + def read_header(self, length): |
| 78 | + self.width, self.height = struct.unpack('!LL', self.stream.read(8)) |
| 79 | + self.bitdepth, self.colortype = struct.unpack('!BB', self.stream.read(2)) |
| 80 | + |
| 81 | + self.compression, self.filter_method, self.interlace = struct.unpack('!BBB', self.stream.read(3)) |
| 82 | + |
| 83 | + if self.compression != COMPRESS_DEFLATE: |
| 84 | + raise ValueError('Unknown compressor', self.compression) |
| 85 | + if self.filter_method != FILTER_METHOD_BASIC_ADAPTIVE: |
| 86 | + raise ValueError('Unknown filter method', self.filtermethod) |
| 87 | + if self.interlace: |
| 88 | + raise ValueError('Interlacing is unsupported') |
| 89 | + |
| 90 | + self.decompress = zlib.decompressobj() |
| 91 | + |
| 92 | + self.bytedepth = self.bitdepth / 8 |
| 93 | + if self.bitdepth % 8: self.bytedepth += 1 |
| 94 | + self.bpp = COLOR_DEPTH[self.colortype] * self.bytedepth |
| 95 | + |
| 96 | + self.previous_scanline = [0] * self.bpp * self.width |
| 97 | + |
| 98 | + def read_palette(self, length): |
| 99 | + self.palette = [] |
| 100 | + for i in xrange(length / 3): |
| 101 | + self.palette.append(struct.unpack('!BBB', self.stream.read(3))) |
| 102 | + |
| 103 | + def read_data(self, length): |
| 104 | + left = length |
| 105 | + while left: |
| 106 | + size = min(4096, left) |
| 107 | + data = self.stream.read(size) |
| 108 | + left -= size |
| 109 | + |
| 110 | + self.defilter(self.decompress.decompress(data)) |
| 111 | + |
| 112 | + def defilter(self, data): |
| 113 | + for byte in data: |
| 114 | + byte = ord(byte) |
| 115 | + if self.expect_filter: |
| 116 | + self.filter = byte |
| 117 | + self.expect_filter = False |
| 118 | + continue |
| 119 | + |
| 120 | + if self.filter == FILTER_NONE: |
| 121 | + pass |
| 122 | + elif self.filter == FILTER_SUB: |
| 123 | + i = len(self.scanline) - self.bpp |
| 124 | + if i >= 0: byte += self.scanline[i] |
| 125 | + elif self.filter == FILTER_UP: |
| 126 | + i = len(self.scanline) |
| 127 | + byte += self.previous_scanline[i] |
| 128 | + elif self.filter == FILTER_AVERAGE: |
| 129 | + i = len(self.scanline) - self.bpp |
| 130 | + j = len(self.scanline) |
| 131 | + if i >= 0: |
| 132 | + byte += (self.scanline[i] + |
| 133 | + self.previous_scanline[j]) / 2 |
| 134 | + else: |
| 135 | + byte += self.previous_scanline[j] / 2 |
| 136 | + elif self.filter == FILTER_PAETH: |
| 137 | + i = len(self.scanline) - self.bpp |
| 138 | + j = len(self.scanline) |
| 139 | + b = self.previous_scanline[j] |
| 140 | + if i >= 0: |
| 141 | + a = self.scanline[i] |
| 142 | + c = self.previous_scanline[i] |
| 143 | + else: |
| 144 | + a = c = 0 |
| 145 | + p = a + b - c |
| 146 | + pa = abs(p - a) |
| 147 | + pb = abs(p - b) |
| 148 | + pc = abs(p - c) |
| 149 | + if pa <= pb and pa <= pc: byte += a |
| 150 | + elif pb <= pc: byte += b |
| 151 | + else: byte += c |
| 152 | + else: |
| 153 | + raise ValueError('Unknown filter', self.filter) |
| 154 | + |
| 155 | + byte %= 256 |
| 156 | + self.scanline.append(byte) |
| 157 | + |
| 158 | + if (len(self.scanline) % self.bpp == 0) and \ |
| 159 | + (len(self.scanline) / self.bpp) == self.width: |
| 160 | + self.previous_scanline = self.scanline |
| 161 | + self.completed_scanline() |
| 162 | + self.scanline = [] |
| 163 | + self.expect_filter = filter |
| 164 | + |
| 165 | + def read_scanline(self): |
| 166 | + scanline = self.scanline[:] |
| 167 | + while scanline: |
| 168 | + yield [''.join((chr(scanline.pop(0)) for i in xrange(self.bytedepth))) |
| 169 | + for j in xrange(COLOR_DEPTH[self.colortype])] |
| 170 | + |
| 171 | + def completed_scanline(self): |
| 172 | + # Override this in a subclass |
| 173 | + for pixel in self.read_scanline(): |
| 174 | + self.out.write(''.join(pixel)) |
| 175 | + |
| 176 | + |
| 177 | +def test(filename): |
| 178 | + reader = PNGReader(open(filename, 'rb')) |
| 179 | + try: |
| 180 | + reader.parse() |
| 181 | + reader.scanline = reader.previous_scanline = None |
| 182 | + print reader.__dict__ |
| 183 | + finally: |
| 184 | + return reader |
\ No newline at end of file |
Property changes on: trunk/pngds/pngreader.py |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 185 | + native |