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