| 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..833f1c750dc2e332cbb1236a0d23f765ad97f74c
|
| --- /dev/null
|
| +++ b/tools/telemetry/telemetry/core/bitmaptools.cc
|
| @@ -0,0 +1,264 @@
|
| +// 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>
|
| +
|
| +#if defined(WIN32)
|
| +#include <fcntl.h>
|
| +#include <io.h>
|
| +#endif
|
| +
|
| +enum Commands {
|
| + CROP_PIXELS = 0,
|
| + HISTOGRAM = 1,
|
| + BOUNDING_BOX = 2
|
| +};
|
| +
|
| +bool ReadInt(int* out) {
|
| + 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;
|
| + int command;
|
| +
|
| +#if defined(WIN32)
|
| + _setmode(_fileno(stdin), _O_BINARY);
|
| + _setmode(_fileno(stdout), _O_BINARY);
|
| +#else
|
| + freopen(NULL, "rb", stdin);
|
| + freopen(NULL, "wb", stdout);
|
| +#endif
|
| +
|
| + 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;
|
| +}
|
|
|