File: | Bitmap.c |
Warning: | line 544, column 39 Assigned value is garbage or undefined |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | #include "Bitmap.h" | |||
2 | #include "Platform.h" | |||
3 | #include "PackedCol.h" | |||
4 | #include "ExtMath.h" | |||
5 | #include "Deflate.h" | |||
6 | #include "Logger.h" | |||
7 | #include "Stream.h" | |||
8 | #include "Errors.h" | |||
9 | #include "Utils.h" | |||
10 | ||||
11 | void Bitmap_UNSAFE_CopyBlock(int srcX, int srcY, int dstX, int dstY, | |||
12 | struct Bitmap* src, struct Bitmap* dst, int size) { | |||
13 | int x, y; | |||
14 | for (y = 0; y < size; y++) { | |||
15 | BitmapCol* srcRow = Bitmap_GetRow(src, srcY + y)((src)->scan0 + (srcY + y) * (src)->width) + srcX; | |||
16 | BitmapCol* dstRow = Bitmap_GetRow(dst, dstY + y)((dst)->scan0 + (dstY + y) * (dst)->width) + dstX; | |||
17 | for (x = 0; x < size; x++) { dstRow[x] = srcRow[x]; } | |||
18 | } | |||
19 | } | |||
20 | ||||
21 | void Bitmap_Allocate(struct Bitmap* bmp, int width, int height) { | |||
22 | bmp->width = width; bmp->height = height; | |||
23 | bmp->scan0 = (BitmapCol*)Mem_Alloc(width * height, 4, "bitmap data"); | |||
24 | } | |||
25 | ||||
26 | void Bitmap_TryAllocate(struct Bitmap* bmp, int width, int height) { | |||
27 | bmp->width = width; bmp->height = height; | |||
28 | bmp->scan0 = (BitmapCol*)Mem_TryAlloc(width * height, 4); | |||
29 | } | |||
30 | ||||
31 | void Bitmap_AllocateClearedPow2(struct Bitmap* bmp, int width, int height) { | |||
32 | width = Math_NextPowOf2(width); | |||
33 | height = Math_NextPowOf2(height); | |||
34 | ||||
35 | bmp->width = width; bmp->height = height; | |||
36 | bmp->scan0 = (BitmapCol*)Mem_AllocCleared(width * height, 4, "bitmap data"); | |||
37 | } | |||
38 | ||||
39 | void Bitmap_TryAllocateClearedPow2(struct Bitmap* bmp, int width, int height) { | |||
40 | width = Math_NextPowOf2(width); | |||
41 | height = Math_NextPowOf2(height); | |||
42 | ||||
43 | bmp->width = width; bmp->height = height; | |||
44 | bmp->scan0 = (BitmapCol*)Mem_TryAllocCleared(width * height, 4); | |||
45 | } | |||
46 | ||||
47 | void Bitmap_Scale(struct Bitmap* dst, struct Bitmap* src, | |||
48 | int srcX, int srcY, int srcWidth, int srcHeight) { | |||
49 | BitmapCol* dstRow; | |||
50 | BitmapCol* srcRow; | |||
51 | int x, y, width, height; | |||
52 | ||||
53 | width = dst->width; | |||
54 | height = dst->height; | |||
55 | ||||
56 | for (y = 0; y < height; y++) { | |||
57 | srcRow = Bitmap_GetRow(src, srcY + (y * srcHeight / height))((src)->scan0 + (srcY + (y * srcHeight / height)) * (src)-> width); | |||
58 | dstRow = Bitmap_GetRow(dst, y)((dst)->scan0 + (y) * (dst)->width); | |||
59 | ||||
60 | for (x = 0; x < width; x++) { | |||
61 | dstRow[x] = srcRow[srcX + (x * srcWidth / width)]; | |||
62 | } | |||
63 | } | |||
64 | } | |||
65 | ||||
66 | ||||
67 | /*########################################################################################################################* | |||
68 | *------------------------------------------------------PNG decoder--------------------------------------------------------* | |||
69 | *#########################################################################################################################*/ | |||
70 | #define PNG_SIG_SIZE8 8 | |||
71 | #define PNG_IHDR_SIZE13 13 | |||
72 | #define PNG_PALETTE256 256 | |||
73 | #define PNG_FourCC(a, b, c, d)(((cc_uint32)a << 24) | ((cc_uint32)b << 16) | (( cc_uint32)c << 8) | (cc_uint32)d) (((cc_uint32)a << 24) | ((cc_uint32)b << 16) | ((cc_uint32)c << 8) | (cc_uint32)d) | |||
74 | ||||
75 | enum PngCol { | |||
76 | PNG_COL_GRAYSCALE = 0, PNG_COL_RGB = 2, PNG_COL_INDEXED = 3, | |||
77 | PNG_COL_GRAYSCALE_A = 4, PNG_COL_RGB_A = 6 | |||
78 | }; | |||
79 | ||||
80 | enum PngFilter { | |||
81 | PNG_FILTER_NONE, PNG_FILTER_SUB, PNG_FILTER_UP, PNG_FILTER_AVERAGE, PNG_FILTER_PAETH | |||
82 | }; | |||
83 | ||||
84 | typedef void (*Png_RowExpander)(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst); | |||
85 | static const cc_uint8 pngSig[PNG_SIG_SIZE8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; | |||
86 | ||||
87 | cc_bool Png_Detect(const cc_uint8* data, cc_uint32 len) { | |||
88 | int i; | |||
89 | if (len < PNG_SIG_SIZE8) return false0; | |||
90 | ||||
91 | for (i = 0; i < PNG_SIG_SIZE8; i++) { | |||
92 | if (data[i] != pngSig[i]) return false0; | |||
93 | } | |||
94 | return true1; | |||
95 | } | |||
96 | ||||
97 | static void Png_Reconstruct(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* line, cc_uint8* prior, cc_uint32 lineLen) { | |||
98 | cc_uint32 i, j; | |||
99 | switch (type) { | |||
100 | case PNG_FILTER_NONE: | |||
101 | return; | |||
102 | ||||
103 | case PNG_FILTER_SUB: | |||
104 | for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { | |||
105 | line[i] += line[j]; | |||
106 | } | |||
107 | return; | |||
108 | ||||
109 | case PNG_FILTER_UP: | |||
110 | for (i = 0; i < lineLen; i++) { | |||
111 | line[i] += prior[i]; | |||
112 | } | |||
113 | return; | |||
114 | ||||
115 | case PNG_FILTER_AVERAGE: | |||
116 | for (i = 0; i < bytesPerPixel; i++) { | |||
117 | line[i] += (prior[i] >> 1); | |||
118 | } | |||
119 | for (j = 0; i < lineLen; i++, j++) { | |||
120 | line[i] += ((prior[i] + line[j]) >> 1); | |||
121 | } | |||
122 | return; | |||
123 | ||||
124 | case PNG_FILTER_PAETH: | |||
125 | /* TODO: verify this is right */ | |||
126 | for (i = 0; i < bytesPerPixel; i++) { | |||
127 | line[i] += prior[i]; | |||
128 | } | |||
129 | for (j = 0; i < lineLen; i++, j++) { | |||
130 | cc_uint8 a = line[j], b = prior[i], c = prior[j]; | |||
131 | int p = a + b - c; | |||
132 | int pa = Math_AbsI(p - a); | |||
133 | int pb = Math_AbsI(p - b); | |||
134 | int pc = Math_AbsI(p - c); | |||
135 | ||||
136 | if (pa <= pb && pa <= pc) { line[i] += a; } | |||
137 | else if (pb <= pc) { line[i] += b; } | |||
138 | else { line[i] += c; } | |||
139 | } | |||
140 | return; | |||
141 | } | |||
142 | } | |||
143 | ||||
144 | #define Bitmap_Set(dst, r,g,b,a)dst = (((cc_uint8)(r) << 16) | ((cc_uint8)(g) << 8 ) | ((cc_uint8)(b) << 0) | ((cc_uint8)(a) << 24)) ; dst = BitmapCol_Make(r, g, b, a)(((cc_uint8)(r) << 16) | ((cc_uint8)(g) << 8) | ( (cc_uint8)(b) << 0) | ((cc_uint8)(a) << 24)); | |||
145 | ||||
146 | #define PNG_Do_Grayscale(dstI, src, scale)rgb = (src) * scale; dst[dstI] = (((cc_uint8)(rgb) << 16 ) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0 ) | ((cc_uint8)(255) << 24));; rgb = (src) * scale; Bitmap_Set(dst[dstI], rgb, rgb, rgb, 255)dst[dstI] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(255 ) << 24));; | |||
147 | #define PNG_Do_Grayscale_8(dstI, srcI)rgb = src[srcI]; dst[dstI] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(255) << 24));; rgb = src[srcI]; Bitmap_Set(dst[dstI], rgb, rgb, rgb, 255)dst[dstI] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(255 ) << 24));; | |||
148 | #define PNG_Do_Grayscale_A__8(dstI, srcI)rgb = src[srcI]; dst[dstI] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(src[srcI + 1]) << 24));; rgb = src[srcI]; Bitmap_Set(dst[dstI], rgb, rgb, rgb, src[srcI + 1])dst[dstI] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(src [srcI + 1]) << 24));; | |||
149 | #define PNG_Do_RGB__8(dstI, srcI)dst[dstI] = (((cc_uint8)(src[srcI]) << 16) | ((cc_uint8 )(src[srcI + 1]) << 8) | ((cc_uint8)(src[srcI + 2]) << 0) | ((cc_uint8)(255) << 24));; Bitmap_Set(dst[dstI], src[srcI], src[srcI + 1], src[srcI + 2], 255)dst[dstI] = (((cc_uint8)(src[srcI]) << 16) | ((cc_uint8 )(src[srcI + 1]) << 8) | ((cc_uint8)(src[srcI + 2]) << 0) | ((cc_uint8)(255) << 24));; | |||
150 | #define PNG_Do_RGB_A__8(dstI, srcI)dst[dstI] = (((cc_uint8)(src[srcI]) << 16) | ((cc_uint8 )(src[srcI + 1]) << 8) | ((cc_uint8)(src[srcI + 2]) << 0) | ((cc_uint8)(src[srcI + 3]) << 24));; Bitmap_Set(dst[dstI], src[srcI], src[srcI + 1], src[srcI + 2], src[srcI + 3])dst[dstI] = (((cc_uint8)(src[srcI]) << 16) | ((cc_uint8 )(src[srcI + 1]) << 8) | ((cc_uint8)(src[srcI + 2]) << 0) | ((cc_uint8)(src[srcI + 3]) << 24));; | |||
151 | ||||
152 | #define PNG_Mask_1(i)(7 - (i & 7)) (7 - (i & 7)) | |||
153 | #define PNG_Mask_2(i)((3 - (i & 3)) * 2) ((3 - (i & 3)) * 2) | |||
154 | #define PNG_Mask_4(i)((1 - (i & 1)) * 4) ((1 - (i & 1)) * 4) | |||
155 | #define PNG_Get__1(i)((src[i >> 3] >> (7 - (i & 7))) & 1) ((src[i >> 3] >> PNG_Mask_1(i)(7 - (i & 7))) & 1) | |||
156 | #define PNG_Get__2(i)((src[i >> 2] >> ((3 - (i & 3)) * 2)) & 3 ) ((src[i >> 2] >> PNG_Mask_2(i)((3 - (i & 3)) * 2)) & 3) | |||
157 | ||||
158 | static void Png_Expand_GRAYSCALE_1(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
159 | int i; cc_uint8 rgb; /* NOTE: not optimised*/ | |||
160 | for (i = 0; i < width; i++) { PNG_Do_Grayscale(i, PNG_Get__1(i), 255)rgb = (((src[i >> 3] >> (7 - (i & 7))) & 1 )) * 255; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8 )(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8 )(255) << 24));;; } | |||
161 | } | |||
162 | ||||
163 | static void Png_Expand_GRAYSCALE_2(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
164 | int i; cc_uint8 rgb; /* NOTE: not optimised */ | |||
165 | for (i = 0; i < width; i++) { PNG_Do_Grayscale(i, PNG_Get__2(i), 85)rgb = (((src[i >> 2] >> ((3 - (i & 3)) * 2)) & 3)) * 85; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8 )(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8 )(255) << 24));;; } | |||
166 | } | |||
167 | ||||
168 | static void Png_Expand_GRAYSCALE_4(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
169 | int i, j; cc_uint8 rgb; | |||
170 | ||||
171 | for (i = 0, j = 0; i < (width & ~0x1); i += 2, j++) { | |||
172 | PNG_Do_Grayscale(i, src[j] >> 4, 17)rgb = (src[j] >> 4) * 17; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(255) << 24));;; PNG_Do_Grayscale(i + 1, src[j] & 0x0F, 17)rgb = (src[j] & 0x0F) * 17; dst[i + 1] = (((cc_uint8)(rgb ) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb ) << 0) | ((cc_uint8)(255) << 24));;; | |||
173 | } | |||
174 | for (; i < width; i++) { | |||
175 | PNG_Do_Grayscale(i, (src[j] >> PNG_Mask_4(i)) & 15, 17)rgb = ((src[j] >> ((1 - (i & 1)) * 4)) & 15) * 17 ; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(255) << 24));;; | |||
176 | } | |||
177 | } | |||
178 | ||||
179 | static void Png_Expand_GRAYSCALE_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
180 | int i; cc_uint8 rgb; | |||
181 | ||||
182 | for (i = 0; i < (width & ~0x3); i += 4) { | |||
183 | PNG_Do_Grayscale_8(i , i )rgb = src[i]; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8 )(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8 )(255) << 24));;; PNG_Do_Grayscale_8(i + 1, i + 1)rgb = src[i + 1]; dst[i + 1] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0 ) | ((cc_uint8)(255) << 24));;; | |||
184 | PNG_Do_Grayscale_8(i + 2, i + 2)rgb = src[i + 2]; dst[i + 2] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0 ) | ((cc_uint8)(255) << 24));;; PNG_Do_Grayscale_8(i + 3, i + 3)rgb = src[i + 3]; dst[i + 3] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0 ) | ((cc_uint8)(255) << 24));;; | |||
185 | } | |||
186 | for (; i < width; i++) { PNG_Do_Grayscale_8(i, i)rgb = src[i]; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8 )(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8 )(255) << 24));;; } | |||
187 | } | |||
188 | ||||
189 | static void Png_Expand_GRAYSCALE_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
190 | int i; cc_uint8 rgb; /* NOTE: not optimised */ | |||
191 | for (i = 0; i < width; i++) { | |||
192 | rgb = src[i * 2]; Bitmap_Set(dst[i], rgb, rgb, rgb, 255)dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(255) << 24));; | |||
193 | } | |||
194 | } | |||
195 | ||||
196 | static void Png_Expand_RGB_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
197 | int i, j; | |||
198 | ||||
199 | for (i = 0, j = 0; i < (width & ~0x03); i += 4, j += 12) { | |||
200 | PNG_Do_RGB__8(i , j )dst[i] = (((cc_uint8)(src[j]) << 16) | ((cc_uint8)(src[ j + 1]) << 8) | ((cc_uint8)(src[j + 2]) << 0) | ( (cc_uint8)(255) << 24));;; PNG_Do_RGB__8(i + 1, j + 3)dst[i + 1] = (((cc_uint8)(src[j + 3]) << 16) | ((cc_uint8 )(src[j + 3 + 1]) << 8) | ((cc_uint8)(src[j + 3 + 2]) << 0) | ((cc_uint8)(255) << 24));;; | |||
201 | PNG_Do_RGB__8(i + 2, j + 6)dst[i + 2] = (((cc_uint8)(src[j + 6]) << 16) | ((cc_uint8 )(src[j + 6 + 1]) << 8) | ((cc_uint8)(src[j + 6 + 2]) << 0) | ((cc_uint8)(255) << 24));;; PNG_Do_RGB__8(i + 3, j + 9)dst[i + 3] = (((cc_uint8)(src[j + 9]) << 16) | ((cc_uint8 )(src[j + 9 + 1]) << 8) | ((cc_uint8)(src[j + 9 + 2]) << 0) | ((cc_uint8)(255) << 24));;; | |||
202 | } | |||
203 | for (; i < width; i++, j += 3) { PNG_Do_RGB__8(i, j)dst[i] = (((cc_uint8)(src[j]) << 16) | ((cc_uint8)(src[ j + 1]) << 8) | ((cc_uint8)(src[j + 2]) << 0) | ( (cc_uint8)(255) << 24));;; } | |||
204 | } | |||
205 | ||||
206 | static void Png_Expand_RGB_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
207 | int i, j; /* NOTE: not optimised */ | |||
208 | for (i = 0, j = 0; i < width; i++, j += 6) { | |||
209 | Bitmap_Set(dst[i], src[j], src[j + 2], src[j + 4], 255)dst[i] = (((cc_uint8)(src[j]) << 16) | ((cc_uint8)(src[ j + 2]) << 8) | ((cc_uint8)(src[j + 4]) << 0) | ( (cc_uint8)(255) << 24));; | |||
210 | } | |||
211 | } | |||
212 | ||||
213 | static void Png_Expand_INDEXED_1(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
214 | int i; /* NOTE: not optimised */ | |||
215 | for (i = 0; i < width; i++) { dst[i] = palette[PNG_Get__1(i)((src[i >> 3] >> (7 - (i & 7))) & 1)]; } | |||
216 | } | |||
217 | ||||
218 | static void Png_Expand_INDEXED_2(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
219 | int i; /* NOTE: not optimised */ | |||
220 | for (i = 0; i < width; i++) { dst[i] = palette[PNG_Get__2(i)((src[i >> 2] >> ((3 - (i & 3)) * 2)) & 3 )]; } | |||
221 | } | |||
222 | ||||
223 | static void Png_Expand_INDEXED_4(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
224 | int i, j; cc_uint8 cur; | |||
225 | ||||
226 | for (i = 0, j = 0; i < (width & ~0x1); i += 2, j++) { | |||
227 | cur = src[j]; | |||
228 | dst[i] = palette[cur >> 4]; dst[i + 1] = palette[cur & 0x0F]; | |||
229 | } | |||
230 | for (; i < width; i++) { | |||
231 | dst[i] = palette[(src[j] >> PNG_Mask_4(i)((1 - (i & 1)) * 4)) & 15]; | |||
232 | } | |||
233 | } | |||
234 | ||||
235 | static void Png_Expand_INDEXED_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
236 | int i; | |||
237 | ||||
238 | for (i = 0; i < (width & ~0x3); i += 4) { | |||
239 | dst[i] = palette[src[i]]; dst[i + 1] = palette[src[i + 1]]; | |||
240 | dst[i + 2] = palette[src[i + 2]]; dst[i + 3] = palette[src[i + 3]]; | |||
241 | } | |||
242 | for (; i < width; i++) { dst[i] = palette[src[i]]; } | |||
243 | } | |||
244 | ||||
245 | static void Png_Expand_GRAYSCALE_A_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
246 | int i, j; cc_uint8 rgb; | |||
247 | ||||
248 | for (i = 0, j = 0; i < (width & ~0x3); i += 4, j += 8) { | |||
249 | PNG_Do_Grayscale_A__8(i , j )rgb = src[j]; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8 )(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8 )(src[j + 1]) << 24));;; PNG_Do_Grayscale_A__8(i + 1, j + 2)rgb = src[j + 2]; dst[i + 1] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0 ) | ((cc_uint8)(src[j + 2 + 1]) << 24));;; | |||
250 | PNG_Do_Grayscale_A__8(i + 2, j + 4)rgb = src[j + 4]; dst[i + 2] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0 ) | ((cc_uint8)(src[j + 4 + 1]) << 24));;; PNG_Do_Grayscale_A__8(i + 3, j + 6)rgb = src[j + 6]; dst[i + 3] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0 ) | ((cc_uint8)(src[j + 6 + 1]) << 24));;; | |||
251 | } | |||
252 | for (; i < width; i++, j += 2) { PNG_Do_Grayscale_A__8(i, j)rgb = src[j]; dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8 )(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8 )(src[j + 1]) << 24));;; } | |||
253 | } | |||
254 | ||||
255 | static void Png_Expand_GRAYSCALE_A_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
256 | int i; cc_uint8 rgb; /* NOTE: not optimised*/ | |||
257 | for (i = 0; i < width; i++) { | |||
258 | rgb = src[i * 4]; Bitmap_Set(dst[i], rgb, rgb, rgb, src[i * 4 + 2])dst[i] = (((cc_uint8)(rgb) << 16) | ((cc_uint8)(rgb) << 8) | ((cc_uint8)(rgb) << 0) | ((cc_uint8)(src[i * 4 + 2 ]) << 24));; | |||
259 | } | |||
260 | } | |||
261 | ||||
262 | static void Png_Expand_RGB_A_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
263 | int i, j; | |||
264 | ||||
265 | for (i = 0, j = 0; i < (width & ~0x3); i += 4, j += 16) { | |||
266 | PNG_Do_RGB_A__8(i , j )dst[i] = (((cc_uint8)(src[j]) << 16) | ((cc_uint8)(src[ j + 1]) << 8) | ((cc_uint8)(src[j + 2]) << 0) | ( (cc_uint8)(src[j + 3]) << 24));;; PNG_Do_RGB_A__8(i + 1, j + 4 )dst[i + 1] = (((cc_uint8)(src[j + 4]) << 16) | ((cc_uint8 )(src[j + 4 + 1]) << 8) | ((cc_uint8)(src[j + 4 + 2]) << 0) | ((cc_uint8)(src[j + 4 + 3]) << 24));;; | |||
267 | PNG_Do_RGB_A__8(i + 2, j + 8)dst[i + 2] = (((cc_uint8)(src[j + 8]) << 16) | ((cc_uint8 )(src[j + 8 + 1]) << 8) | ((cc_uint8)(src[j + 8 + 2]) << 0) | ((cc_uint8)(src[j + 8 + 3]) << 24));;; PNG_Do_RGB_A__8(i + 3, j + 12)dst[i + 3] = (((cc_uint8)(src[j + 12]) << 16) | ((cc_uint8 )(src[j + 12 + 1]) << 8) | ((cc_uint8)(src[j + 12 + 2]) << 0) | ((cc_uint8)(src[j + 12 + 3]) << 24));;; | |||
268 | } | |||
269 | for (; i < width; i++, j += 4) { PNG_Do_RGB_A__8(i, j)dst[i] = (((cc_uint8)(src[j]) << 16) | ((cc_uint8)(src[ j + 1]) << 8) | ((cc_uint8)(src[j + 2]) << 0) | ( (cc_uint8)(src[j + 3]) << 24));;; } | |||
270 | } | |||
271 | ||||
272 | static void Png_Expand_RGB_A_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { | |||
273 | int i, j; /* NOTE: not optimised*/ | |||
274 | for (i = 0, j = 0; i < width; i++, j += 8) { | |||
275 | Bitmap_Set(dst[i], src[j], src[j + 2], src[j + 4], src[j + 6])dst[i] = (((cc_uint8)(src[j]) << 16) | ((cc_uint8)(src[ j + 2]) << 8) | ((cc_uint8)(src[j + 4]) << 0) | ( (cc_uint8)(src[j + 6]) << 24));; | |||
276 | } | |||
277 | } | |||
278 | ||||
279 | static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) { | |||
280 | switch (col) { | |||
281 | case PNG_COL_GRAYSCALE: | |||
282 | switch (bitsPerSample) { | |||
283 | case 1: return Png_Expand_GRAYSCALE_1; | |||
284 | case 2: return Png_Expand_GRAYSCALE_2; | |||
285 | case 4: return Png_Expand_GRAYSCALE_4; | |||
286 | case 8: return Png_Expand_GRAYSCALE_8; | |||
287 | case 16: return Png_Expand_GRAYSCALE_16; | |||
288 | } | |||
289 | return NULL((void*)0); | |||
290 | ||||
291 | case PNG_COL_RGB: | |||
292 | switch (bitsPerSample) { | |||
293 | case 8: return Png_Expand_RGB_8; | |||
294 | case 16: return Png_Expand_RGB_16; | |||
295 | } | |||
296 | return NULL((void*)0); | |||
297 | ||||
298 | case PNG_COL_INDEXED: | |||
299 | switch (bitsPerSample) { | |||
300 | case 1: return Png_Expand_INDEXED_1; | |||
301 | case 2: return Png_Expand_INDEXED_2; | |||
302 | case 4: return Png_Expand_INDEXED_4; | |||
303 | case 8: return Png_Expand_INDEXED_8; | |||
304 | } | |||
305 | return NULL((void*)0); | |||
306 | ||||
307 | case PNG_COL_GRAYSCALE_A: | |||
308 | switch (bitsPerSample) { | |||
309 | case 8: return Png_Expand_GRAYSCALE_A_8; | |||
310 | case 16: return Png_Expand_GRAYSCALE_A_16; | |||
311 | } | |||
312 | return NULL((void*)0); | |||
313 | ||||
314 | case PNG_COL_RGB_A: | |||
315 | switch (bitsPerSample) { | |||
316 | case 8: return Png_Expand_RGB_A_8; | |||
317 | case 16: return Png_Expand_RGB_A_16; | |||
318 | } | |||
319 | return NULL((void*)0); | |||
320 | } | |||
321 | return NULL((void*)0); | |||
322 | } | |||
323 | ||||
324 | /* Sets alpha to 0 for any pixels in the bitmap whose RGB is same as col */ | |||
325 | static void ComputeTransparency(struct Bitmap* bmp, BitmapCol col) { | |||
326 | BitmapCol trnsRGB = col & BITMAPCOL_RGB_MASK((0xFFU << 16) | (0xFFU << 8) | (0xFFU << 0 )); | |||
327 | int x, y, width = bmp->width, height = bmp->height; | |||
328 | ||||
329 | for (y = 0; y < height; y++) { | |||
330 | BitmapCol* row = Bitmap_GetRow(bmp, y)((bmp)->scan0 + (y) * (bmp)->width); | |||
331 | for (x = 0; x < width; x++) { | |||
332 | BitmapCol rgb = row[x] & BITMAPCOL_RGB_MASK((0xFFU << 16) | (0xFFU << 8) | (0xFFU << 0 )); | |||
333 | row[x] = (rgb == trnsRGB) ? trnsRGB : row[x]; | |||
334 | } | |||
335 | } | |||
336 | } | |||
337 | ||||
338 | /* Most bits per sample is 16. Most samples per pixel is 4. Add 1 for filter byte. */ | |||
339 | /* Need to store both current and prior row, per PNG specification. */ | |||
340 | #define PNG_BUFFER_SIZE((0x8000 * 2 * 4 + 1) * 2) ((PNG_MAX_DIMS0x8000 * 2 * 4 + 1) * 2) | |||
341 | ||||
342 | /* TODO: Test a lot of .png files and ensure output is right */ | |||
343 | cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { | |||
344 | cc_uint8 tmp[PNG_PALETTE256 * 3]; | |||
345 | cc_uint32 dataSize, fourCC; | |||
346 | cc_result res; | |||
347 | ||||
348 | /* header variables */ | |||
349 | static cc_uint32 samplesPerPixel[7] = { 1, 0, 3, 1, 2, 0, 4 }; | |||
350 | cc_uint8 col, bitsPerSample, bytesPerPixel; | |||
351 | Png_RowExpander rowExpander; | |||
352 | cc_uint32 scanlineSize, scanlineBytes; | |||
353 | ||||
354 | /* palette data */ | |||
355 | BitmapCol trnsCol; | |||
356 | BitmapCol palette[PNG_PALETTE256]; | |||
357 | cc_uint32 i; | |||
358 | ||||
359 | /* idat state */ | |||
360 | cc_uint32 curY = 0, begY, rowY, endY; | |||
361 | cc_uint8 buffer[PNG_BUFFER_SIZE((0x8000 * 2 * 4 + 1) * 2)]; | |||
362 | cc_uint32 bufferRows, bufferLen; | |||
363 | cc_uint32 bufferIdx, read, left; | |||
364 | ||||
365 | /* idat decompressor */ | |||
366 | struct InflateState inflate; | |||
367 | struct Stream compStream, datStream; | |||
368 | struct ZLibHeader zlibHeader; | |||
369 | ||||
370 | bmp->width = 0; bmp->height = 0; | |||
371 | bmp->scan0 = NULL((void*)0); | |||
372 | ||||
373 | res = Stream_Read(stream, tmp, PNG_SIG_SIZE8); | |||
374 | if (res) return res; | |||
375 | if (!Png_Detect(tmp, PNG_SIG_SIZE8)) return PNG_ERR_INVALID_SIG; | |||
376 | ||||
377 | trnsCol = BITMAPCOL_BLACK(((cc_uint8)(0) << 16) | ((cc_uint8)(0) << 8) | ( (cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24)); | |||
378 | for (i = 0; i < PNG_PALETTE256; i++) { palette[i] = BITMAPCOL_BLACK(((cc_uint8)(0) << 16) | ((cc_uint8)(0) << 8) | ( (cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24)); } | |||
379 | ||||
380 | Inflate_MakeStream2(&compStream, &inflate, stream); | |||
381 | ZLibHeader_Init(&zlibHeader); | |||
382 | ||||
383 | for (;;) { | |||
384 | res = Stream_Read(stream, tmp, 8); | |||
385 | if (res) return res; | |||
386 | dataSize = Stream_GetU32_BE(tmp + 0); | |||
387 | fourCC = Stream_GetU32_BE(tmp + 4); | |||
388 | ||||
389 | switch (fourCC) { | |||
390 | case PNG_FourCC('I','H','D','R')(((cc_uint32)'I' << 24) | ((cc_uint32)'H' << 16) | ((cc_uint32)'D' << 8) | (cc_uint32)'R'): { | |||
391 | if (dataSize != PNG_IHDR_SIZE13) return PNG_ERR_INVALID_HDR_SIZE; | |||
392 | res = Stream_Read(stream, tmp, PNG_IHDR_SIZE13); | |||
393 | if (res) return res; | |||
394 | ||||
395 | bmp->width = (int)Stream_GetU32_BE(tmp + 0); | |||
396 | bmp->height = (int)Stream_GetU32_BE(tmp + 4); | |||
397 | if (bmp->width < 0 || bmp->width > PNG_MAX_DIMS0x8000) return PNG_ERR_TOO_WIDE; | |||
398 | if (bmp->height < 0 || bmp->height > PNG_MAX_DIMS0x8000) return PNG_ERR_TOO_TALL; | |||
399 | ||||
400 | bmp->scan0 = (BitmapCol*)Mem_TryAlloc(bmp->width * bmp->height, 4); | |||
401 | if (!bmp->scan0) return ERR_OUT_OF_MEMORY; | |||
402 | ||||
403 | bitsPerSample = tmp[8]; col = tmp[9]; | |||
404 | rowExpander = Png_GetExpander(col, bitsPerSample); | |||
405 | if (rowExpander == NULL((void*)0)) return PNG_ERR_INVALID_COL_BPP; | |||
406 | ||||
407 | if (tmp[10] != 0) return PNG_ERR_COMP_METHOD; | |||
408 | if (tmp[11] != 0) return PNG_ERR_FILTER; | |||
409 | if (tmp[12] != 0) return PNG_ERR_INTERLACED; | |||
410 | ||||
411 | bytesPerPixel = ((samplesPerPixel[col] * bitsPerSample) + 7) >> 3; | |||
412 | scanlineSize = ((samplesPerPixel[col] * bitsPerSample * bmp->width) + 7) >> 3; | |||
413 | scanlineBytes = scanlineSize + 1; /* Add 1 byte for filter byte of each scanline */ | |||
414 | ||||
415 | Mem_Set(buffer, 0, scanlineBytes); /* Prior row should be 0 per PNG spec */ | |||
416 | bufferIdx = scanlineBytes; | |||
417 | bufferRows = PNG_BUFFER_SIZE((0x8000 * 2 * 4 + 1) * 2) / scanlineBytes; | |||
418 | bufferLen = bufferRows * scanlineBytes; | |||
419 | } break; | |||
420 | ||||
421 | case PNG_FourCC('P','L','T','E')(((cc_uint32)'P' << 24) | ((cc_uint32)'L' << 16) | ((cc_uint32)'T' << 8) | (cc_uint32)'E'): { | |||
422 | if (dataSize > PNG_PALETTE256 * 3) return PNG_ERR_PAL_SIZE; | |||
423 | if ((dataSize % 3) != 0) return PNG_ERR_PAL_SIZE; | |||
424 | res = Stream_Read(stream, tmp, dataSize); | |||
425 | if (res) return res; | |||
426 | ||||
427 | for (i = 0; i < dataSize; i += 3) { | |||
428 | palette[i / 3] &= BITMAPCOL_A_MASK(0xFFU << 24); /* set RGB to 0 */ | |||
429 | palette[i / 3] |= tmp[i ] << BITMAPCOL_R_SHIFT16; | |||
430 | palette[i / 3] |= tmp[i + 1] << BITMAPCOL_G_SHIFT8; | |||
431 | palette[i / 3] |= tmp[i + 2] << BITMAPCOL_B_SHIFT0; | |||
432 | } | |||
433 | } break; | |||
434 | ||||
435 | case PNG_FourCC('t','R','N','S')(((cc_uint32)'t' << 24) | ((cc_uint32)'R' << 16) | ((cc_uint32)'N' << 8) | (cc_uint32)'S'): { | |||
436 | if (col == PNG_COL_GRAYSCALE) { | |||
437 | if (dataSize != 2) return PNG_ERR_TRANS_COUNT; | |||
438 | res = Stream_Read(stream, tmp, dataSize); | |||
439 | if (res) return res; | |||
440 | ||||
441 | /* RGB is 16 bits big endian, ignore least significant 8 bits */ | |||
442 | trnsCol = BitmapCol_Make(tmp[0], tmp[0], tmp[0], 0)(((cc_uint8)(tmp[0]) << 16) | ((cc_uint8)(tmp[0]) << 8) | ((cc_uint8)(tmp[0]) << 0) | ((cc_uint8)(0) << 24)); | |||
443 | } else if (col == PNG_COL_INDEXED) { | |||
444 | if (dataSize > PNG_PALETTE256) return PNG_ERR_TRANS_COUNT; | |||
445 | res = Stream_Read(stream, tmp, dataSize); | |||
446 | if (res) return res; | |||
447 | ||||
448 | /* set alpha component of palette */ | |||
449 | for (i = 0; i < dataSize; i++) { | |||
450 | palette[i] &= BITMAPCOL_RGB_MASK((0xFFU << 16) | (0xFFU << 8) | (0xFFU << 0 )); /* set A to 0 */ | |||
451 | palette[i] |= tmp[i] << PACKEDCOL_A_SHIFT24; | |||
452 | } | |||
453 | } else if (col == PNG_COL_RGB) { | |||
454 | if (dataSize != 6) return PNG_ERR_TRANS_COUNT; | |||
455 | res = Stream_Read(stream, tmp, dataSize); | |||
456 | if (res) return res; | |||
457 | ||||
458 | /* R,G,B is 16 bits big endian, ignore least significant 8 bits */ | |||
459 | trnsCol = BitmapCol_Make(tmp[0], tmp[2], tmp[4], 0)(((cc_uint8)(tmp[0]) << 16) | ((cc_uint8)(tmp[2]) << 8) | ((cc_uint8)(tmp[4]) << 0) | ((cc_uint8)(0) << 24)); | |||
460 | } else { | |||
461 | return PNG_ERR_TRANS_INVALID; | |||
462 | } | |||
463 | } break; | |||
464 | ||||
465 | case PNG_FourCC('I','D','A','T')(((cc_uint32)'I' << 24) | ((cc_uint32)'D' << 16) | ((cc_uint32)'A' << 8) | (cc_uint32)'T'): { | |||
466 | Stream_ReadonlyPortion(&datStream, stream, dataSize); | |||
467 | inflate.Source = &datStream; | |||
468 | ||||
469 | /* TODO: This assumes zlib header will be in 1 IDAT chunk */ | |||
470 | while (!zlibHeader.done) { | |||
471 | if ((res = ZLibHeader_Read(&datStream, &zlibHeader))) return res; | |||
472 | } | |||
473 | if (!bmp->scan0) return PNG_ERR_NO_DATA; | |||
474 | ||||
475 | while (curY < bmp->height) { | |||
476 | /* Need to leave one row in buffer untouched for storing prior scanline. Illustrated example of process: | |||
477 | * |=====| #-----| |-----| #-----| |-----| | |||
478 | * initial #-----| read 3 |-----| read 3 |-----| read 1 |-----| read 3 |-----| etc | |||
479 | * state |-----| -----> |-----| -----> |=====| -----> |-----| -----> |=====| | |||
480 | * |-----| |=====| #-----| |=====| #-----| | |||
481 | * | |||
482 | * (==== is prior scanline, # is current index in buffer) | |||
483 | * Having initial state this way allows doing two 'read 3' first time (assuming large idat chunks) | |||
484 | */ | |||
485 | ||||
486 | begY = bufferIdx / scanlineBytes; | |||
487 | left = bufferLen - bufferIdx; | |||
488 | /* if row is at 0, last row in buffer is prior row */ | |||
489 | /* hence subtract a row, as don't want to overwrite it */ | |||
490 | if (begY == 0) left -= scanlineBytes; | |||
491 | ||||
492 | res = compStream.Read(&compStream, &buffer[bufferIdx], left, &read); | |||
493 | if (res) return res; | |||
494 | if (!read) break; | |||
495 | ||||
496 | bufferIdx += read; | |||
497 | endY = bufferIdx / scanlineBytes; | |||
498 | /* reached end of buffer, cycle back to start */ | |||
499 | if (bufferIdx == bufferLen) bufferIdx = 0; | |||
500 | ||||
501 | /* NOTE: Need to check curY too, in case IDAT is corrupted and has extra data */ | |||
502 | for (rowY = begY; rowY < endY && curY < bmp->height; rowY++, curY++) { | |||
503 | cc_uint32 priorY = rowY == 0 ? bufferRows : rowY; | |||
504 | cc_uint8* prior = &buffer[(priorY - 1) * scanlineBytes]; | |||
505 | cc_uint8* scanline = &buffer[rowY * scanlineBytes]; | |||
506 | ||||
507 | if (scanline[0] > PNG_FILTER_PAETH) return PNG_ERR_INVALID_SCANLINE; | |||
508 | Png_Reconstruct(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize); | |||
509 | rowExpander(bmp->width, palette, &scanline[1], Bitmap_GetRow(bmp, curY)((bmp)->scan0 + (curY) * (bmp)->width)); | |||
510 | } | |||
511 | } | |||
512 | ||||
513 | if (curY == bmp->height) { | |||
514 | if (!BitmapCol_A(trnsCol)((cc_uint8)(trnsCol >> 24))) ComputeTransparency(bmp, trnsCol); | |||
515 | return 0; | |||
516 | } | |||
517 | } break; | |||
518 | ||||
519 | case PNG_FourCC('I','E','N','D')(((cc_uint32)'I' << 24) | ((cc_uint32)'E' << 16) | ((cc_uint32)'N' << 8) | (cc_uint32)'D'): | |||
520 | /* Reading all image data should be handled by above if in the IDAT chunk */ | |||
521 | /* If we reached here, it means not all of the image data was read */ | |||
522 | return PNG_ERR_REACHED_IEND; | |||
523 | ||||
524 | default: | |||
525 | if ((res = stream->Skip(stream, dataSize))) return res; | |||
526 | break; | |||
527 | } | |||
528 | ||||
529 | if ((res = stream->Skip(stream, 4))) return res; /* Skip CRC32 */ | |||
530 | } | |||
531 | } | |||
532 | ||||
533 | ||||
534 | /*########################################################################################################################* | |||
535 | *------------------------------------------------------PNG encoder--------------------------------------------------------* | |||
536 | *#########################################################################################################################*/ | |||
537 | static void Png_Filter(cc_uint8 filter, const cc_uint8* cur, const cc_uint8* prior, cc_uint8* best, int lineLen, int bpp) { | |||
538 | /* 3 bytes per pixel constant */ | |||
539 | cc_uint8 a, b, c; | |||
540 | int i, p, pa, pb, pc; | |||
541 | ||||
542 | switch (filter) { | |||
543 | case PNG_FILTER_SUB: | |||
544 | for (i = 0; i < bpp; i++) { best[i] = cur[i]; } | |||
| ||||
545 | ||||
546 | for (; i < lineLen; i++) { | |||
547 | best[i] = cur[i] - cur[i - bpp]; | |||
548 | } | |||
549 | break; | |||
550 | ||||
551 | case PNG_FILTER_UP: | |||
552 | for (i = 0; i < lineLen; i++) { | |||
553 | best[i] = cur[i] - prior[i]; | |||
554 | } | |||
555 | break; | |||
556 | ||||
557 | case PNG_FILTER_AVERAGE: | |||
558 | for (i = 0; i < bpp; i++) { best[i] = cur[i] - (prior[i] >> 1); } | |||
559 | ||||
560 | for (; i < lineLen; i++) { | |||
561 | best[i] = cur[i] - ((prior[i] + cur[i - bpp]) >> 1); | |||
562 | } | |||
563 | break; | |||
564 | ||||
565 | case PNG_FILTER_PAETH: | |||
566 | for (i = 0; i < bpp; i++) { best[i] = cur[i] - prior[i]; } | |||
567 | ||||
568 | for (; i < lineLen; i++) { | |||
569 | a = cur[i - bpp]; b = prior[i]; c = prior[i - bpp]; | |||
570 | p = a + b - c; | |||
571 | ||||
572 | pa = Math_AbsI(p - a); | |||
573 | pb = Math_AbsI(p - b); | |||
574 | pc = Math_AbsI(p - c); | |||
575 | ||||
576 | if (pa <= pb && pa <= pc) { best[i] = cur[i] - a; } | |||
577 | else if (pb <= pc) { best[i] = cur[i] - b; } | |||
578 | else { best[i] = cur[i] - c; } | |||
579 | } | |||
580 | break; | |||
581 | } | |||
582 | } | |||
583 | ||||
584 | static void Png_MakeRow(const BitmapCol* src, cc_uint8* dst, int lineLen, cc_bool alpha) { | |||
585 | cc_uint8* end = dst + lineLen; | |||
586 | BitmapCol col; /* if we use *src, register gets reloaded each time */ | |||
587 | ||||
588 | if (alpha
| |||
589 | for (; dst < end; src++, dst += 4) { | |||
590 | col = *src; | |||
591 | dst[0] = BitmapCol_R(col)((cc_uint8)(col >> 16)); dst[1] = BitmapCol_G(col)((cc_uint8)(col >> 8)); | |||
592 | dst[2] = BitmapCol_B(col)((cc_uint8)(col >> 0)); dst[3] = BitmapCol_A(col)((cc_uint8)(col >> 24)); | |||
593 | } | |||
594 | } else { | |||
595 | for (; dst < end; src++, dst += 3) { | |||
596 | col = *src; | |||
597 | dst[0] = BitmapCol_R(col)((cc_uint8)(col >> 16)); dst[1] = BitmapCol_G(col)((cc_uint8)(col >> 8)); | |||
598 | dst[2] = BitmapCol_B(col)((cc_uint8)(col >> 0)); | |||
599 | } | |||
600 | } | |||
601 | } | |||
602 | ||||
603 | static void Png_EncodeRow(const cc_uint8* cur, const cc_uint8* prior, cc_uint8* best, int lineLen, cc_bool alpha) { | |||
604 | cc_uint8* dst; | |||
605 | int bestFilter, bestEstimate = Int32_MaxValue((cc_int32)2147483647L); | |||
606 | int x, filter, estimate; | |||
607 | ||||
608 | dst = best + 1; | |||
609 | /* NOTE: Waste of time trying the PNG_NONE filter */ | |||
610 | for (filter = PNG_FILTER_SUB; filter <= PNG_FILTER_PAETH; filter++) { | |||
611 | Png_Filter(filter, cur, prior, dst, lineLen, alpha
| |||
612 | ||||
613 | /* Estimate how well this filtered line will compress, based on */ | |||
614 | /* smallest sum of magnitude of each byte (signed) in the line */ | |||
615 | /* (see note in PNG specification, 12.8 "Filter selection" ) */ | |||
616 | estimate = 0; | |||
617 | for (x = 0; x < lineLen; x++) { | |||
618 | estimate += Math_AbsI((cc_int8)dst[x]); | |||
619 | } | |||
620 | ||||
621 | if (estimate > bestEstimate) continue; | |||
622 | bestEstimate = estimate; | |||
623 | bestFilter = filter; | |||
624 | } | |||
625 | ||||
626 | /* The bytes in dst are from last filter run (paeth) */ | |||
627 | /* However, we want dst to be bytes from the best filter */ | |||
628 | if (bestFilter != PNG_FILTER_PAETH) { | |||
629 | Png_Filter(bestFilter, cur, prior, dst, lineLen, alpha ? 4 : 3); | |||
630 | } | |||
631 | ||||
632 | best[0] = bestFilter; | |||
633 | } | |||
634 | ||||
635 | static int Png_SelectRow(struct Bitmap* bmp, int y) { return y; } | |||
636 | cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, | |||
637 | Png_RowSelector selectRow, cc_bool alpha) { | |||
638 | cc_uint8 tmp[32]; | |||
639 | /* TODO: This should be * 4 for alpha (should switch to mem_alloc though) */ | |||
640 | cc_uint8 prevLine[PNG_MAX_DIMS0x8000 * 3], curLine[PNG_MAX_DIMS0x8000 * 3]; | |||
641 | cc_uint8 bestLine[PNG_MAX_DIMS0x8000 * 3 + 1]; | |||
642 | ||||
643 | struct ZLibState zlState; | |||
644 | struct Stream chunk, zlStream; | |||
645 | cc_uint32 stream_end, stream_beg; | |||
646 | int y, lineSize; | |||
647 | cc_result res; | |||
648 | ||||
649 | /* stream may not start at 0 (e.g. when making default.zip) */ | |||
650 | if ((res = stream->Position(stream, &stream_beg))) return res; | |||
| ||||
651 | ||||
652 | if (!selectRow) selectRow = Png_SelectRow; | |||
653 | if ((res = Stream_Write(stream, pngSig, PNG_SIG_SIZE8))) return res; | |||
654 | Stream_WriteonlyCrc32(&chunk, stream); | |||
655 | ||||
656 | /* Write header chunk */ | |||
657 | Stream_SetU32_BE(&tmp[0], PNG_IHDR_SIZE13); | |||
658 | Stream_SetU32_BE(&tmp[4], PNG_FourCC('I','H','D','R')(((cc_uint32)'I' << 24) | ((cc_uint32)'H' << 16) | ((cc_uint32)'D' << 8) | (cc_uint32)'R')); | |||
659 | { | |||
660 | Stream_SetU32_BE(&tmp[8], bmp->width); | |||
661 | Stream_SetU32_BE(&tmp[12], bmp->height); | |||
662 | tmp[16] = 8; /* bits per sample */ | |||
663 | tmp[17] = alpha ? PNG_COL_RGB_A : PNG_COL_RGB; | |||
664 | tmp[18] = 0; /* DEFLATE compression method */ | |||
665 | tmp[19] = 0; /* ADAPTIVE filter method */ | |||
666 | tmp[20] = 0; /* Not using interlacing */ | |||
667 | } | |||
668 | Stream_SetU32_BE(&tmp[21], Utils_CRC32(&tmp[4], 17)); | |||
669 | ||||
670 | /* Write PNG body */ | |||
671 | Stream_SetU32_BE(&tmp[25], 0); /* size of IDAT, filled in later */ | |||
672 | if ((res = Stream_Write(stream, tmp, 29))) return res; | |||
673 | Stream_SetU32_BE(&tmp[0], PNG_FourCC('I','D','A','T')(((cc_uint32)'I' << 24) | ((cc_uint32)'D' << 16) | ((cc_uint32)'A' << 8) | (cc_uint32)'T')); | |||
674 | if ((res = Stream_Write(&chunk, tmp, 4))) return res; | |||
675 | ||||
676 | ZLib_MakeStream(&zlStream, &zlState, &chunk); | |||
677 | lineSize = bmp->width * (alpha
| |||
678 | Mem_Set(prevLine, 0, lineSize); | |||
679 | ||||
680 | for (y = 0; y < bmp->height; y++) { | |||
681 | int row = selectRow(bmp, y); | |||
682 | BitmapCol* src = Bitmap_GetRow(bmp, row)((bmp)->scan0 + (row) * (bmp)->width); | |||
683 | cc_uint8* prev = (y & 1) == 0 ? prevLine : curLine; | |||
684 | cc_uint8* cur = (y & 1) == 0 ? curLine : prevLine; | |||
685 | ||||
686 | Png_MakeRow(src, cur, lineSize, alpha); | |||
687 | Png_EncodeRow(cur, prev, bestLine, lineSize, alpha); | |||
688 | ||||
689 | /* +1 for filter byte */ | |||
690 | if ((res = Stream_Write(&zlStream, bestLine, lineSize + 1))) return res; | |||
691 | } | |||
692 | if ((res = zlStream.Close(&zlStream))) return res; | |||
693 | Stream_SetU32_BE(&tmp[0], chunk.Meta.CRC32.CRC32 ^ 0xFFFFFFFFUL); | |||
694 | ||||
695 | /* Write end chunk */ | |||
696 | Stream_SetU32_BE(&tmp[4], 0); | |||
697 | Stream_SetU32_BE(&tmp[8], PNG_FourCC('I','E','N','D')(((cc_uint32)'I' << 24) | ((cc_uint32)'E' << 16) | ((cc_uint32)'N' << 8) | (cc_uint32)'D')); | |||
698 | Stream_SetU32_BE(&tmp[12], 0xAE426082UL); /* CRC32 of IEND */ | |||
699 | if ((res = Stream_Write(stream, tmp, 16))) return res; | |||
700 | ||||
701 | /* Come back to fixup size of data in data chunk */ | |||
702 | if ((res = stream->Length(stream, &stream_end))) return res; | |||
703 | if ((res = stream->Seek(stream, stream_beg + 33))) return res; | |||
704 | ||||
705 | Stream_SetU32_BE(&tmp[0], (stream_end - stream_beg) - 57); | |||
706 | if ((res = Stream_Write(stream, tmp, 4))) return res; | |||
707 | return stream->Seek(stream, stream_end); | |||
708 | } |