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