OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <stdio.h> |
| 6 #include <stdlib.h> |
| 7 #include <string.h> |
| 8 |
| 9 #if defined(WIN32) |
| 10 #include <fcntl.h> |
| 11 #include <io.h> |
| 12 #endif |
| 13 |
| 14 enum Commands { |
| 15 CROP_PIXELS = 0, |
| 16 HISTOGRAM = 1, |
| 17 BOUNDING_BOX = 2 |
| 18 }; |
| 19 |
| 20 bool ReadInt(int* out) { |
| 21 return fread(out, sizeof(*out), 1, stdin) == 1; |
| 22 } |
| 23 |
| 24 void WriteResponse(void* data, int size) { |
| 25 fwrite(&size, sizeof(size), 1, stdout); |
| 26 fwrite(data, size, 1, stdout); |
| 27 fflush(stdout); |
| 28 } |
| 29 |
| 30 struct Box { |
| 31 Box() : left(), top(), right(), bottom() {} |
| 32 |
| 33 // Expected input is: |
| 34 // left, top, width, height |
| 35 bool Read() { |
| 36 int width; |
| 37 int height; |
| 38 if (!(ReadInt(&left) && ReadInt(&top) && |
| 39 ReadInt(&width) && ReadInt(&height))) { |
| 40 fprintf(stderr, "Could not parse Box.\n"); |
| 41 return false; |
| 42 } |
| 43 if (left < 0 || top < 0 || width < 0 || height < 0) { |
| 44 fprintf(stderr, "Box dimensions must be non-negative.\n"); |
| 45 return false; |
| 46 } |
| 47 right = left + width; |
| 48 bottom = top + height; |
| 49 return true; |
| 50 } |
| 51 |
| 52 void Union(int x, int y) { |
| 53 if (left > x) left = x; |
| 54 if (right <= x) right = x + 1; |
| 55 if (top > y) top = y; |
| 56 if (bottom <= y) bottom = y + 1; |
| 57 } |
| 58 |
| 59 int width() const { return right - left; } |
| 60 int height() const { return bottom - top; } |
| 61 |
| 62 int left; |
| 63 int top; |
| 64 int right; |
| 65 int bottom; |
| 66 }; |
| 67 |
| 68 |
| 69 // Represents a bitmap buffer with a crop box. |
| 70 struct Bitmap { |
| 71 Bitmap() : pixels(NULL) {} |
| 72 |
| 73 ~Bitmap() { |
| 74 if (pixels) |
| 75 delete[] pixels; |
| 76 } |
| 77 |
| 78 // Expected input is: |
| 79 // bpp, width, height, box, pixels |
| 80 bool Read() { |
| 81 int bpp; |
| 82 int width; |
| 83 int height; |
| 84 if (!(ReadInt(&bpp) && ReadInt(&width) && ReadInt(&height))) { |
| 85 fprintf(stderr, "Could not parse Bitmap initializer.\n"); |
| 86 return false; |
| 87 } |
| 88 if (bpp <= 0 || width <= 0 || height <= 0) { |
| 89 fprintf(stderr, "Dimensions must be positive.\n"); |
| 90 return false; |
| 91 } |
| 92 |
| 93 int size = width * height * bpp; |
| 94 |
| 95 row_stride = width * bpp; |
| 96 pixel_stride = bpp; |
| 97 total_size = size; |
| 98 row_size = row_stride; |
| 99 |
| 100 if (!box.Read()) { |
| 101 fprintf(stderr, "Expected crop box argument not found.\n"); |
| 102 return false; |
| 103 } |
| 104 |
| 105 if (box.bottom * row_stride > total_size || |
| 106 box.right * pixel_stride > row_size) { |
| 107 fprintf(stderr, "Crop box overflows the bitmap.\n"); |
| 108 return false; |
| 109 } |
| 110 |
| 111 pixels = new unsigned char[size]; |
| 112 if (fread(pixels, sizeof(pixels[0]), size, stdin) < |
| 113 static_cast<size_t>(size)) { |
| 114 fprintf(stderr, "Not enough pixels found,\n"); |
| 115 return false; |
| 116 } |
| 117 |
| 118 total_size = (box.bottom - box.top) * row_stride; |
| 119 row_size = (box.right - box.left) * pixel_stride; |
| 120 data = pixels + box.top * row_stride + box.left * pixel_stride; |
| 121 return true; |
| 122 } |
| 123 |
| 124 void WriteCroppedPixels() const { |
| 125 int out_size = row_size * box.height(); |
| 126 unsigned char* out = new unsigned char[out_size]; |
| 127 unsigned char* dst = out; |
| 128 for (const unsigned char* row = data; |
| 129 row < data + total_size; |
| 130 row += row_stride, dst += row_size) { |
| 131 // No change in pixel_stride, so we can copy whole rows. |
| 132 memcpy(dst, row, row_size); |
| 133 } |
| 134 |
| 135 WriteResponse(out, out_size); |
| 136 delete[] out; |
| 137 } |
| 138 |
| 139 unsigned char* pixels; |
| 140 Box box; |
| 141 // Points at the top-left pixel in |pixels|. |
| 142 const unsigned char* data; |
| 143 // These counts are in bytes. |
| 144 int row_stride; |
| 145 int pixel_stride; |
| 146 int total_size; |
| 147 int row_size; |
| 148 }; |
| 149 |
| 150 |
| 151 static inline |
| 152 bool PixelsEqual(const unsigned char* pixel1, const unsigned char* pixel2, |
| 153 int tolerance) { |
| 154 // Note: this works for both RGB and RGBA. Alpha channel is ignored. |
| 155 return (abs(pixel1[0] - pixel2[0]) <= tolerance) && |
| 156 (abs(pixel1[1] - pixel2[1]) <= tolerance) && |
| 157 (abs(pixel1[2] - pixel2[2]) <= tolerance); |
| 158 } |
| 159 |
| 160 |
| 161 static inline |
| 162 bool PixelsEqual(const unsigned char* pixel, int color, int tolerance) { |
| 163 unsigned char pixel2[3] = { color >> 16, color >> 8, color }; |
| 164 return PixelsEqual(pixel, pixel2, tolerance); |
| 165 } |
| 166 |
| 167 |
| 168 static |
| 169 bool Histogram(const Bitmap& bmp) { |
| 170 int ignore_color; |
| 171 int tolerance; |
| 172 if (!(ReadInt(&ignore_color) && ReadInt(&tolerance))) { |
| 173 fprintf(stderr, "Could not parse HISTOGRAM command.\n"); |
| 174 return false; |
| 175 } |
| 176 |
| 177 const int kLength = 3 * 256; |
| 178 int counts[kLength] = {}; |
| 179 |
| 180 for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size; |
| 181 row += bmp.row_stride) { |
| 182 for (const unsigned char* pixel = row; pixel < row + bmp.row_size; |
| 183 pixel += bmp.pixel_stride) { |
| 184 if (ignore_color >= 0 && PixelsEqual(pixel, ignore_color, tolerance)) |
| 185 continue; |
| 186 ++(counts[256 * 0 + pixel[0]]); |
| 187 ++(counts[256 * 1 + pixel[1]]); |
| 188 ++(counts[256 * 2 + pixel[2]]); |
| 189 } |
| 190 } |
| 191 |
| 192 WriteResponse(counts, sizeof(counts)); |
| 193 return true; |
| 194 } |
| 195 |
| 196 |
| 197 static |
| 198 bool BoundingBox(const Bitmap& bmp) { |
| 199 int color; |
| 200 int tolerance; |
| 201 if (!(ReadInt(&color) && ReadInt(&tolerance))) { |
| 202 fprintf(stderr, "Could not parse BOUNDING_BOX command.\n"); |
| 203 return false; |
| 204 } |
| 205 |
| 206 Box box; |
| 207 box.left = bmp.total_size; |
| 208 box.top = bmp.total_size; |
| 209 box.right = 0; |
| 210 box.bottom = 0; |
| 211 |
| 212 int count = 0; |
| 213 int y = 0; |
| 214 for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size; |
| 215 row += bmp.row_stride, ++y) { |
| 216 int x = 0; |
| 217 for (const unsigned char* pixel = row; pixel < row + bmp.row_size; |
| 218 pixel += bmp.pixel_stride, ++x) { |
| 219 if (!PixelsEqual(pixel, color, tolerance)) |
| 220 continue; |
| 221 box.Union(x, y); |
| 222 ++count; |
| 223 } |
| 224 } |
| 225 |
| 226 int response[] = { box.left, box.top, box.width(), box.height(), count }; |
| 227 WriteResponse(response, sizeof(response)); |
| 228 return true; |
| 229 } |
| 230 |
| 231 |
| 232 int main() { |
| 233 Bitmap bmp; |
| 234 int command; |
| 235 |
| 236 #if defined(WIN32) |
| 237 _setmode(_fileno(stdin), _O_BINARY); |
| 238 _setmode(_fileno(stdout), _O_BINARY); |
| 239 #else |
| 240 FILE* unused_stdin = freopen(NULL, "rb", stdin); |
| 241 FILE* unused_stdout = freopen(NULL, "wb", stdout); |
| 242 #endif |
| 243 |
| 244 if (!bmp.Read()) return -1; |
| 245 if (!ReadInt(&command)) { |
| 246 fprintf(stderr, "Expected command.\n"); |
| 247 return -1; |
| 248 } |
| 249 switch (command) { |
| 250 case CROP_PIXELS: |
| 251 bmp.WriteCroppedPixels(); |
| 252 break; |
| 253 case BOUNDING_BOX: |
| 254 if (!BoundingBox(bmp)) return -1; |
| 255 break; |
| 256 case HISTOGRAM: |
| 257 if (!Histogram(bmp)) return -1; |
| 258 break; |
| 259 default: |
| 260 fprintf(stderr, "Unrecognized command\n"); |
| 261 return -1; |
| 262 } |
| 263 return 0; |
| 264 } |
OLD | NEW |