Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: tools/telemetry/telemetry/core/bitmaptools/bitmaptools.cc

Issue 130153003: [telemetry] Implement per-pixel algorithms in Bitmap as a C++ extension. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: remove blank from <(output_path) Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 <Python.h>
6 #include <string.h>
7
8
9 struct Box {
10 Box() : left(), top(), right(), bottom() {}
11
12 bool ParseArg(PyObject* obj) {
13 int width;
14 int height;
15 if (!PyArg_ParseTuple(obj, "iiii", &left, &top, &width, &height))
16 return false;
17 if (left < 0 || top < 0 || width < 0 || height < 0) {
18 PyErr_SetString(PyExc_ValueError, "Box dimensions must be non-negative.");
19 return false;
20 }
21 right = left + width;
22 bottom = top + height;
23 return true;
24 }
25
26 PyObject* MakeObject() const {
27 if (right <= left || bottom <= top)
28 return Py_None;
29 return Py_BuildValue("iiii", left, top, right - left, bottom - top);
30 }
31
32 void Union(int x, int y) {
33 if (left > x) left = x;
34 if (right <= x) right = x + 1;
35 if (top > y) top = y;
36 if (bottom <= y) bottom = y + 1;
37 }
38
39 int width() const { return right - left; }
40 int height() const { return bottom - top; }
41
42 int left;
43 int top;
44 int right;
45 int bottom;
46 };
47
48
49 // Represents a bitmap buffer with a crop box.
50 struct Bitmap {
51 Bitmap() {}
52
53 ~Bitmap() {
54 if (pixels.buf)
55 PyBuffer_Release(&pixels);
56 }
57
58 bool ParseArg(PyObject* obj) {
59 int width;
60 int bpp;
61 PyObject* box_object;
62 if (!PyArg_ParseTuple(obj, "s*iiO", &pixels, &width, &bpp, &box_object))
63 return false;
64 if (width <= 0 || bpp <= 0) {
65 PyErr_SetString(PyExc_ValueError, "Width and bpp must be positive.");
66 return false;
67 }
68
69 row_stride = width * bpp;
70 pixel_stride = bpp;
71 total_size = pixels.len;
72 row_size = row_stride;
73
74 if (pixels.len % row_stride != 0) {
75 PyErr_SetString(PyExc_ValueError, "Length must be a multiple of width "
76 "and bpp.");
77 return false;
78 }
79
80 if (!box.ParseArg(box_object))
81 return false;
82
83 if (box.bottom * row_stride > total_size ||
84 box.right * pixel_stride > row_size) {
85 PyErr_SetString(PyExc_ValueError, "Crop box overflows the bitmap.");
86 return false;
87 }
88
89 total_size = (box.bottom - box.top) * row_stride;
90 row_size = (box.right - box.left) * pixel_stride;
91 data = reinterpret_cast<const unsigned char*>(pixels.buf) +
92 box.top * row_stride + box.left * pixel_stride;
93 return true;
94 }
95
96 Py_buffer pixels;
97 Box box;
98 // Points at the top-left pixel in |pixels.buf|.
99 const unsigned char* data;
100 // These counts are in bytes.
101 int row_stride;
102 int pixel_stride;
103 int total_size;
104 int row_size;
105 };
106
107
108 static
109 PyObject* Histogram(PyObject* self, PyObject* bmp_object) {
110 Bitmap bmp;
111 if (!bmp.ParseArg(bmp_object))
112 return NULL;
113
114 const int kLength = 3 * 256;
115 int counts[kLength] = {};
116
117 for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size;
118 row += bmp.row_stride) {
119 for (const unsigned char* pixel = row; pixel < row + bmp.row_size;
120 pixel += bmp.pixel_stride) {
121 ++(counts[256 * 0 + pixel[0]]);
122 ++(counts[256 * 1 + pixel[1]]);
123 ++(counts[256 * 2 + pixel[2]]);
124 }
125 }
126
127 PyObject* list = PyList_New(kLength);
128 if (!list)
129 return NULL;
130
131 for (int i = 0; i < kLength; ++i)
132 PyList_SetItem(list, i, PyInt_FromLong(counts[i]));
133
134 return list;
135 }
136
137
138 static inline
139 bool PixelsEqual(const unsigned char* pixel1, const unsigned char* pixel2,
140 int tolerance) {
141 // Note: this works for both RGB and RGBA. Alpha channel is ignored.
142 return (abs(pixel1[0] - pixel2[0]) <= tolerance) &&
143 (abs(pixel1[1] - pixel2[1]) <= tolerance) &&
144 (abs(pixel1[2] - pixel2[2]) <= tolerance);
145 }
146
147
148 static inline
149 bool PixelsEqual(const unsigned char* pixel, int color, int tolerance) {
150 unsigned char pixel2[3] = { color >> 16, color >> 8, color };
151 return PixelsEqual(pixel, pixel2, tolerance);
152 }
153
154
155 static
156 PyObject* Equal(PyObject* self, PyObject* args) {
157 PyObject* bmp_obj1;
158 PyObject* bmp_obj2;
159 int tolerance;
160 if (!PyArg_ParseTuple(args, "OOi", &bmp_obj1, &bmp_obj2, &tolerance))
161 return NULL;
162
163 Bitmap bmp1, bmp2;
164 if (!bmp1.ParseArg(bmp_obj1) || !bmp2.ParseArg(bmp_obj2))
165 return NULL;
166
167 if (bmp1.box.width() != bmp2.box.width() ||
168 bmp1.box.height() != bmp2.box.height()) {
169 PyErr_SetString(PyExc_ValueError, "Bitmap dimensions don't match.");
170 return NULL;
171 }
172
173 bool simple_match = (tolerance == 0) &&
174 (bmp1.pixel_stride == 3) &&
175 (bmp2.pixel_stride == 3);
176 for (const unsigned char *row1 = bmp1.data, *row2 = bmp2.data;
177 row1 < bmp1.data + bmp1.total_size;
178 row1 += bmp1.row_stride, row2 += bmp2.row_stride) {
179 if (simple_match) {
180 if (memcmp(row1, row2, bmp1.row_size) != 0)
181 return Py_False;
182 continue;
183 }
184 for (const unsigned char *pixel1 = row1, *pixel2 = row2;
185 pixel1 < row1 + bmp1.row_size;
186 pixel1 += bmp1.pixel_stride, pixel2 += bmp2.pixel_stride) {
187 if (!PixelsEqual(pixel1, pixel2, tolerance))
188 return Py_False;
189 }
190 }
191
192 return Py_True;
193 }
194
195
196 static
197 PyObject* BoundingBox(PyObject* self, PyObject* args) {
198 PyObject* bmp_object;
199 int color;
200 int tolerance;
201 if (!PyArg_ParseTuple(args, "Oii", &bmp_object, &color, &tolerance))
202 return NULL;
203
204 Bitmap bmp;
205 if (!bmp.ParseArg(bmp_object))
206 return NULL;
207
208 Box box;
209 box.left = bmp.pixels.len;
210 box.top = bmp.pixels.len;
211 box.right = 0;
212 box.bottom = 0;
213
214 int count = 0;
215 int y = 0;
216 for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size;
217 row += bmp.row_stride, ++y) {
218 int x = 0;
219 for (const unsigned char* pixel = row; pixel < row + bmp.row_size;
220 pixel += bmp.pixel_stride, ++x) {
221 if (!PixelsEqual(pixel, color, tolerance))
222 continue;
223 box.Union(x, y);
224 ++count;
225 }
226 }
227
228 return Py_BuildValue("Oi", box.MakeObject(), count);
229 }
230
231
232 static
233 PyObject* Crop(PyObject* self, PyObject* bmp_object) {
234 Bitmap bmp;
235 if (!bmp.ParseArg(bmp_object))
236 return NULL;
237
238 int out_size = bmp.row_size * bmp.box.height();
239 unsigned char* out = new unsigned char[out_size];
240 unsigned char* dst = out;
241 for (const unsigned char* row = bmp.data;
242 row < bmp.data + bmp.total_size;
243 row += bmp.row_stride, dst += bmp.row_size) {
244 // No change in pixel_stride, so we can copy whole rows.
245 memcpy(dst, row, bmp.row_size);
246 }
247
248 PyObject* result = Py_BuildValue("s#", out, out_size);
249 delete[] out;
250 return result;
251 }
252
253
254 static PyMethodDef module_methods[] = {
255 {"Histogram", Histogram, METH_O,
256 "Calculates histogram of bitmap colors. Returns a list of 3x256 ints."},
257 {"Equal", Equal, METH_VARARGS,
258 "Checks if the two bmps are equal."},
259 {"BoundingBox", BoundingBox, METH_VARARGS,
260 "Calculates bounding box of matching color."},
261 {"Crop", Crop, METH_O,
262 "Crops the bmp to crop box."},
263 {NULL, NULL, 0, NULL} /* sentinel */
264 };
265
266 PyMODINIT_FUNC initbitmaptools(void) {
267 Py_InitModule("bitmaptools", module_methods);
268 }
OLDNEW
« no previous file with comments | « tools/telemetry/telemetry/core/bitmaptools/__init__.py ('k') | tools/telemetry/telemetry/core/build_extension.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698