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