| Index: tools/telemetry/telemetry/core/bitmapiter.cc
|
| diff --git a/tools/telemetry/telemetry/core/bitmapiter.cc b/tools/telemetry/telemetry/core/bitmapiter.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d99c4c8846431b8b7b8e80f96d6251c4abc7c9bb
|
| --- /dev/null
|
| +++ b/tools/telemetry/telemetry/core/bitmapiter.cc
|
| @@ -0,0 +1,303 @@
|
| +// 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>
|
| +
|
| +
|
| +
|
| +enum Commands {
|
| + INITIALIZE = 0,
|
| + NEXT = 1,
|
| + PIXELS = 2,
|
| + BOUNDING_BOX = 3,
|
| + HISTOGRAM = 4,
|
| + CROP = 5,
|
| + MAGIC = 0x2751912F,
|
| +};
|
| +
|
| +// Command input stream.
|
| +FILE* g_cmdin;
|
| +
|
| +bool readInt(int* out) {
|
| + return fread(out, sizeof(*out), 1, g_cmdin) == 1;
|
| +}
|
| +
|
| +void writeResponse(void* data, int size) {
|
| + fwrite(&size, sizeof(size), 1, stdout);
|
| + fwrite(data, size, 1, stdout);
|
| + fflush(stdout);
|
| +}
|
| +
|
| +void ack() {
|
| + int size = 0;
|
| + fwrite(&size, sizeof(size), 1, stdout);
|
| + fflush(stdout);
|
| +}
|
| +
|
| +
|
| +struct Box {
|
| + Box() : left(), top(), right(), bottom() {}
|
| +
|
| + 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;
|
| + }
|
| +
|
| + 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 (width <= 0 || bpp <= 0) {
|
| + fprintf(stderr, "Width and bpp must be positive.\n");
|
| + return false;
|
| + }
|
| +
|
| + size = width * height * bpp;
|
| + pixels = new unsigned char[size];
|
| +
|
| + row_stride = width * bpp;
|
| + pixel_stride = bpp;
|
| + total_size = size;
|
| + row_size = row_stride;
|
| +
|
| + data = pixels;
|
| + box.right = width;
|
| + box.bottom = height;
|
| + return true;
|
| + }
|
| +
|
| + bool ReadPixels() {
|
| + if (fread(pixels, sizeof(pixels[0]), size, stdin) < size) {
|
| + fprintf(stderr, "STOP\n");
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + bool 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;
|
| + }
|
| +
|
| + bool SetBox(const Box& new_box) {
|
| + if (new_box.bottom * row_stride > total_size ||
|
| + new_box.right * pixel_stride > row_size) {
|
| + fprintf(stderr, "Crop box overflows the bitmap.\n");
|
| + return false;
|
| + }
|
| + box = new_box;
|
| +
|
| + 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;
|
| + }
|
| +
|
| + unsigned char* pixels;
|
| + size_t size;
|
| + 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.size;
|
| + box.top = bmp.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(int argc, const char* argv[]) {
|
| + if (argc < 2) {
|
| + fprintf(stderr, "Expected pipe fd\n");
|
| + return -2;
|
| + }
|
| + int pipe = atoi(argv[1]);
|
| + g_cmdin = fdopen(pipe, "rb");
|
| + freopen(NULL, "rb", stdin);
|
| + freopen(NULL, "wb", stdout);
|
| +
|
| + int command;
|
| + if (!readInt(&command) || command != MAGIC) {
|
| + fprintf(stderr, "Expected MAGIC\n");
|
| + return -3;
|
| + }
|
| +
|
| + Bitmap bmp;
|
| + Box box;
|
| +
|
| + if (!bmp.Read())
|
| + return -1;
|
| +
|
| + ack();
|
| +
|
| + while (true) {
|
| + if (!readInt(&command)) {
|
| + fprintf(stderr, "Expected command.\n");
|
| + return -1;
|
| + }
|
| + switch (command) {
|
| + case NEXT:
|
| + if (!bmp.ReadPixels())
|
| + return -1;
|
| + ack();
|
| + break;
|
| + case PIXELS:
|
| + bmp.WriteCroppedPixels();
|
| + break;
|
| + case BOUNDING_BOX:
|
| + if (!BoundingBox(bmp))
|
| + return -1;
|
| + break;
|
| + case HISTOGRAM:
|
| + if (!Histogram(bmp))
|
| + return -1;
|
| + break;
|
| + case CROP:
|
| + if (!box.Read() || !bmp.SetBox(box))
|
| + return -1;
|
| + ack();
|
| + break;
|
| + default:
|
| + fprintf(stderr, "Unrecognized command\n");
|
| + return -1;
|
| + };
|
| + }
|
| + return 0;
|
| +}
|
|
|