str_io_c.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. #include "str_io.h"
  2. #include "zlib_util_c.h"
  3. #include "base64_c.h"
  4. #include "snprintf_c.h" /* snprintf() */
  5. #include <stdio.h> /* fputs() */
  6. #include <ctype.h> /* isdigit() */
  7. #include <stdlib.h> /* atoi() */
  8. #include <string.h> /* strlen() */
  9. #include <assert.h>
  10. #if defined(_MSC_VER)
  11. #include "ms_stdbool.h"
  12. #else
  13. #include <stdbool.h>
  14. #endif
  15. #define STR_BITS_PER_PIXEL 24
  16. #define STR_BYTES_PER_PIXEL ((STR_BITS_PER_PIXEL) / 8)
  17. #define MAX_DIMENSION_LEN 5 /* Maximum length for [width] or [height]
  18. * in string. */
  19. const char *MMBitmapStringErrorString(MMBMPStringError err)
  20. {
  21. switch (err) {
  22. case kMMBMPStringInvalidHeaderError:
  23. return "Invalid header for string";
  24. case kMMBMPStringDecodeError:
  25. return "Error decoding string";
  26. case kMMBMPStringDecompressError:
  27. return "Error decompressing string";
  28. case kMMBMPStringSizeError:
  29. return "String not of expected size";
  30. case MMMBMPStringEncodeError:
  31. return "Error encoding string";
  32. case kMMBMPStringCompressError:
  33. return "Error compressing string";
  34. default:
  35. return NULL;
  36. }
  37. }
  38. /* Parses beginning of string in the form of "[width],[height],*".
  39. *
  40. * If successful, |width| and |height| are set to the appropropriate values,
  41. * |len| is set to the length of [width] + the length of [height] + 2,
  42. * and true is returned; otherwise, false is returned.
  43. */
  44. static bool getSizeFromString(const uint8_t *buf, size_t buflen,
  45. size_t *width, size_t *height,
  46. size_t *len);
  47. MMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,
  48. MMBMPStringError *err)
  49. {
  50. uint8_t *decoded, *decompressed;
  51. size_t width, height;
  52. size_t len, bytewidth;
  53. if (*buffer++ != 'b' || !getSizeFromString(buffer, --buflen,
  54. &width, &height, &len)) {
  55. if (err != NULL) *err = kMMBMPStringInvalidHeaderError;
  56. return NULL;
  57. }
  58. buffer += len;
  59. buflen -= len;
  60. decoded = base64decode(buffer, buflen, NULL);
  61. if (decoded == NULL) {
  62. if (err != NULL) *err = kMMBMPStringDecodeError;
  63. return NULL;
  64. }
  65. decompressed = zlib_decompress(decoded, &len);
  66. free(decoded);
  67. if (decompressed == NULL) {
  68. if (err != NULL) *err = kMMBMPStringDecompressError;
  69. return NULL;
  70. }
  71. bytewidth = width * STR_BYTES_PER_PIXEL; /* Note that bytewidth is NOT
  72. * aligned to a padding. */
  73. if (height * bytewidth != len) {
  74. if (err != NULL) *err = kMMBMPStringSizeError;
  75. return NULL;
  76. }
  77. return createMMBitmap(decompressed, width, height,
  78. bytewidth, STR_BITS_PER_PIXEL, STR_BYTES_PER_PIXEL);
  79. }
  80. /* Returns bitmap data suitable for encoding to a string; that is, 24-bit BGR
  81. * bitmap with no padding and 3 bytes per pixel.
  82. *
  83. * Caller is responsible for free()'ing returned buffer. */
  84. static uint8_t *createRawBitmapData(MMBitmapRef bitmap);
  85. uint8_t *createStringFromMMBitmap(MMBitmapRef bitmap, MMBMPStringError *err)
  86. {
  87. uint8_t *raw, *compressed;
  88. uint8_t *ret, *encoded;
  89. size_t len, retlen;
  90. assert(bitmap != NULL);
  91. raw = createRawBitmapData(bitmap);
  92. if (raw == NULL) {
  93. if (err != NULL) *err = kMMBMPStringGenericError;
  94. return NULL;
  95. }
  96. compressed = zlib_compress(raw,
  97. bitmap->width * bitmap->height *
  98. STR_BYTES_PER_PIXEL,
  99. 9, &len);
  100. free(raw);
  101. if (compressed == NULL) {
  102. if (err != NULL) *err = kMMBMPStringCompressError;
  103. return NULL;
  104. }
  105. encoded = base64encode(compressed, len - 1, &retlen);
  106. free(compressed);
  107. if (encoded == NULL) {
  108. if (err != NULL) *err = MMMBMPStringEncodeError;
  109. return NULL;
  110. }
  111. retlen += 3 + (MAX_DIMENSION_LEN * 2);
  112. ret = calloc(sizeof(char), (retlen + 1));
  113. snprintf((char *)ret, retlen, "b%lu,%lu,%s", (unsigned long)bitmap->width,
  114. (unsigned long)bitmap->height,
  115. encoded);
  116. ret[retlen] = '\0';
  117. free(encoded);
  118. return ret;
  119. }
  120. static uint32_t parseDimension(const uint8_t *buf, size_t buflen,
  121. size_t *numlen);
  122. static bool getSizeFromString(const uint8_t *buf, size_t buflen,
  123. size_t *width, size_t *height,
  124. size_t *len)
  125. {
  126. size_t numlen;
  127. assert(buf != NULL);
  128. assert(width != NULL);
  129. assert(height != NULL);
  130. if ((*width = parseDimension(buf, buflen, &numlen)) == 0) {
  131. return false;
  132. }
  133. *len = numlen + 1;
  134. if ((*height = parseDimension(buf + *len, buflen, &numlen)) == 0) {
  135. return false;
  136. }
  137. *len += numlen + 1;
  138. return true;
  139. }
  140. /* Parses one dimension from string as described in getSizeFromString().
  141. * Returns dimension on success, or 0 on error. */
  142. static uint32_t parseDimension(const uint8_t *buf, size_t buflen,
  143. size_t *numlen)
  144. {
  145. char num[MAX_DIMENSION_LEN + 1];
  146. size_t i;
  147. // ssize_t len;
  148. // size_t len;
  149. uint8_t * len;
  150. assert(buf != NULL);
  151. assert(len != NULL);
  152. for (i = 0; i < buflen && buf[i] != ',' && buf[i] != '\0'; ++i) {
  153. if (!isdigit(buf[i]) || i > MAX_DIMENSION_LEN) return 0;
  154. num[i] = buf[i];
  155. }
  156. num[i] = '\0';
  157. *numlen = i;
  158. return (uint32_t)atoi(num);
  159. }
  160. static uint8_t *createRawBitmapData(MMBitmapRef bitmap)
  161. {
  162. uint8_t *raw = calloc(STR_BYTES_PER_PIXEL, bitmap->width * bitmap->height);
  163. size_t y;
  164. for (y = 0; y < bitmap->height; ++y) {
  165. /* No padding is added to string bitmaps. */
  166. const size_t rowOffset = y * bitmap->width * STR_BYTES_PER_PIXEL;
  167. size_t x;
  168. for (x = 0; x < bitmap->width; ++x) {
  169. /* Copy in BGR format. */
  170. const size_t colOffset = x * STR_BYTES_PER_PIXEL;
  171. uint8_t *dest = raw + rowOffset + colOffset;
  172. MMRGBColor *srcColor = MMRGBColorRefAtPoint(bitmap, x, y);
  173. dest[0] = srcColor->blue;
  174. dest[1] = srcColor->green;
  175. dest[2] = srcColor->red;
  176. }
  177. }
  178. return raw;
  179. }