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 
ReadInt(int * out)20 bool ReadInt(int* out) {
21   return fread(out, sizeof(*out), 1, stdin) == 1;
22 }
23 
WriteResponse(void * data,int size)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 {
BoxBox31   Box() : left(), top(), right(), bottom() {}
32 
33   // Expected input is:
34   // left, top, width, height
ReadBox35   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 
UnionBox52   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 
widthBox59   int width() const { return right - left; }
heightBox60   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 {
BitmapBitmap71   Bitmap() : pixels(NULL) {}
72 
~BitmapBitmap73   ~Bitmap() {
74     if (pixels)
75       delete[] pixels;
76   }
77 
78   // Expected input is:
79   // bpp, width, height, box, pixels
ReadBitmap80   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 
WriteCroppedPixelsBitmap124   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
PixelsEqual(const unsigned char * pixel1,const unsigned char * pixel2,int tolerance)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
PixelsEqual(const unsigned char * pixel,int color,int tolerance)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
Histogram(const Bitmap & bmp)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
BoundingBox(const Bitmap & bmp)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 
main()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   static_cast<void>(freopen(NULL, "rb", stdin));
241   static_cast<void>(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