| 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 | } |