Chromium Code Reviews| Index: tools/telemetry/telemetry/core/bitmaptools.cc |
| diff --git a/tools/telemetry/telemetry/core/bitmaptools.cc b/tools/telemetry/telemetry/core/bitmaptools.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b4b1923bb41a173e746ba8c238c85b5c27b38812 |
| --- /dev/null |
| +++ b/tools/telemetry/telemetry/core/bitmaptools.cc |
| @@ -0,0 +1,254 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| + |
|
bulach
2014/01/17 10:43:44
nit: could put all of these in an anonymous namesp
szym
2014/01/17 20:38:20
A bit meaningless since this is the binary's only
|
| +enum Commands { |
| + CROP_PIXELS = 0, |
| + HISTOGRAM = 1, |
| + BOUNDING_BOX = 2 |
| +}; |
| + |
| +bool readInt(int* out) { |
|
bulach
2014/01/17 10:43:44
nit: "R"eadInt, ditto for "W"riteResponse below..
szym
2014/01/17 20:38:20
Done.
|
| + return fread(out, sizeof(*out), 1, stdin) == 1; |
| +} |
| + |
| +void writeResponse(void* data, int size) { |
| + fwrite(&size, sizeof(size), 1, stdout); |
| + fwrite(data, size, 1, stdout); |
| + fflush(stdout); |
| +} |
| + |
| +struct Box { |
| + Box() : left(), top(), right(), bottom() {} |
| + |
| + // Expected input is: |
| + // left, top, width, height |
| + bool Read() { |
| + int width; |
| + int height; |
| + if (!(readInt(&left) && readInt(&top) && |
| + readInt(&width) && readInt(&height))) { |
| + fprintf(stderr, "Could not parse Box.\n"); |
| + return false; |
| + } |
| + if (left < 0 || top < 0 || width < 0 || height < 0) { |
| + fprintf(stderr, "Box dimensions must be non-negative.\n"); |
| + return false; |
| + } |
| + right = left + width; |
| + bottom = top + height; |
| + return true; |
| + } |
| + |
| + void Union(int x, int y) { |
| + if (left > x) left = x; |
| + if (right <= x) right = x + 1; |
| + if (top > y) top = y; |
| + if (bottom <= y) bottom = y + 1; |
| + } |
| + |
| + int width() const { return right - left; } |
| + int height() const { return bottom - top; } |
| + |
| + int left; |
| + int top; |
| + int right; |
| + int bottom; |
| +}; |
| + |
| + |
| +// Represents a bitmap buffer with a crop box. |
| +struct Bitmap { |
| + Bitmap() : pixels(NULL) {} |
| + |
| + ~Bitmap() { |
| + if (pixels) |
| + delete[] pixels; |
| + } |
| + |
| + // Expected input is: |
| + // bpp, width, height, box, pixels |
| + bool Read() { |
| + int bpp; |
| + int width; |
| + int height; |
| + if (!(readInt(&bpp) && readInt(&width) && readInt(&height))) { |
| + fprintf(stderr, "Could not parse Bitmap initializer.\n"); |
| + return false; |
| + } |
| + if (bpp <= 0 || width <= 0 || height <= 0) { |
| + fprintf(stderr, "Dimensions must be positive.\n"); |
| + return false; |
| + } |
| + |
| + int size = width * height * bpp; |
| + |
| + row_stride = width * bpp; |
| + pixel_stride = bpp; |
| + total_size = size; |
| + row_size = row_stride; |
| + |
| + if (!box.Read()) { |
| + fprintf(stderr, "Expected crop box argument not found.\n"); |
| + return false; |
| + } |
| + |
| + if (box.bottom * row_stride > total_size || |
| + box.right * pixel_stride > row_size) { |
| + fprintf(stderr, "Crop box overflows the bitmap.\n"); |
| + return false; |
| + } |
| + |
| + pixels = new unsigned char[size]; |
| + if (fread(pixels, sizeof(pixels[0]), size, stdin) < |
| + static_cast<size_t>(size)) { |
| + fprintf(stderr, "Not enough pixels found,\n"); |
| + return false; |
| + } |
| + |
| + total_size = (box.bottom - box.top) * row_stride; |
| + row_size = (box.right - box.left) * pixel_stride; |
| + data = pixels + box.top * row_stride + box.left * pixel_stride; |
| + return true; |
| + } |
| + |
| + void WriteCroppedPixels() const { |
| + int out_size = row_size * box.height(); |
| + unsigned char* out = new unsigned char[out_size]; |
| + unsigned char* dst = out; |
| + for (const unsigned char* row = data; |
| + row < data + total_size; |
| + row += row_stride, dst += row_size) { |
| + // No change in pixel_stride, so we can copy whole rows. |
| + memcpy(dst, row, row_size); |
| + } |
| + |
| + writeResponse(out, out_size); |
| + delete[] out; |
| + } |
| + |
| + unsigned char* pixels; |
| + Box box; |
| + // Points at the top-left pixel in |pixels|. |
| + const unsigned char* data; |
| + // These counts are in bytes. |
| + int row_stride; |
| + int pixel_stride; |
| + int total_size; |
| + int row_size; |
| +}; |
| + |
| + |
| +static inline |
| +bool PixelsEqual(const unsigned char* pixel1, const unsigned char* pixel2, |
| + int tolerance) { |
| + // Note: this works for both RGB and RGBA. Alpha channel is ignored. |
| + return (abs(pixel1[0] - pixel2[0]) <= tolerance) && |
| + (abs(pixel1[1] - pixel2[1]) <= tolerance) && |
| + (abs(pixel1[2] - pixel2[2]) <= tolerance); |
| +} |
| + |
| + |
| +static inline |
| +bool PixelsEqual(const unsigned char* pixel, int color, int tolerance) { |
| + unsigned char pixel2[3] = { color >> 16, color >> 8, color }; |
| + return PixelsEqual(pixel, pixel2, tolerance); |
| +} |
| + |
| + |
| +static |
| +bool Histogram(const Bitmap& bmp) { |
| + int ignore_color; |
| + int tolerance; |
| + if (!(readInt(&ignore_color) && readInt(&tolerance))) { |
| + fprintf(stderr, "Could not parse HISTOGRAM command.\n"); |
| + return false; |
| + } |
| + |
| + const int kLength = 3 * 256; |
| + int counts[kLength] = {}; |
| + |
| + for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size; |
| + row += bmp.row_stride) { |
| + for (const unsigned char* pixel = row; pixel < row + bmp.row_size; |
| + pixel += bmp.pixel_stride) { |
| + if (ignore_color >= 0 && PixelsEqual(pixel, ignore_color, tolerance)) |
| + continue; |
| + ++(counts[256 * 0 + pixel[0]]); |
| + ++(counts[256 * 1 + pixel[1]]); |
| + ++(counts[256 * 2 + pixel[2]]); |
| + } |
| + } |
| + |
| + writeResponse(counts, sizeof(counts)); |
| + return true; |
| +} |
| + |
| + |
| +static |
| +bool BoundingBox(const Bitmap& bmp) { |
| + int color; |
| + int tolerance; |
| + if (!(readInt(&color) && readInt(&tolerance))) { |
| + fprintf(stderr, "Could not parse BOUNDING_BOX command.\n"); |
| + return false; |
| + } |
| + |
| + Box box; |
| + box.left = bmp.total_size; |
| + box.top = bmp.total_size; |
| + box.right = 0; |
| + box.bottom = 0; |
| + |
| + int count = 0; |
| + int y = 0; |
| + for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size; |
| + row += bmp.row_stride, ++y) { |
| + int x = 0; |
| + for (const unsigned char* pixel = row; pixel < row + bmp.row_size; |
| + pixel += bmp.pixel_stride, ++x) { |
| + if (!PixelsEqual(pixel, color, tolerance)) |
| + continue; |
| + box.Union(x, y); |
| + ++count; |
| + } |
| + } |
| + |
| + int response[] = { box.left, box.top, box.width(), box.height(), count }; |
| + writeResponse(response, sizeof(response)); |
| + return true; |
| +} |
| + |
| + |
| +int main() { |
| + Bitmap bmp; |
|
bulach
2014/01/17 10:43:44
ignorable suggestion: not quite sure how frequentl
szym
2014/01/17 20:38:20
It would be called up to hundreds of times per pag
|
| + int command; |
| + |
| + freopen(NULL, "rb", stdin); |
| + freopen(NULL, "wb", stdout); |
| + |
| + if (!bmp.Read()) return -1; |
| + if (!readInt(&command)) { |
| + fprintf(stderr, "Expected command.\n"); |
| + return -1; |
| + } |
| + switch (command) { |
| + case CROP_PIXELS: |
| + bmp.WriteCroppedPixels(); |
| + break; |
| + case BOUNDING_BOX: |
| + if (!BoundingBox(bmp)) return -1; |
| + break; |
| + case HISTOGRAM: |
| + if (!Histogram(bmp)) return -1; |
| + break; |
| + default: |
| + fprintf(stderr, "Unrecognized command\n"); |
| + return -1; |
| + }; |
| + return 0; |
| +} |