Commit 5e757d05 authored by tonyg@chromium.org's avatar tonyg@chromium.org

[Telemetry] Rename PngBitmap->Bitmap and move it to core.

The rename is because I'm planning to generate Bitmaps from things other than
PNGs and there's nothing really PNG specific about this Bitmap class.

The move is because this is exposed in the Tab API, so it belongs in core, not
backends.

This will make the new Platform video capture API a little cleaner too.

BUG=323813

Review URL: https://codereview.chromium.org/99253005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238351 0039d316-1c4b-4281-b951-d872f2087c98
parent 780a2f13
...@@ -14,7 +14,7 @@ import re ...@@ -14,7 +14,7 @@ import re
import maps_expectations import maps_expectations
from telemetry import test from telemetry import test
from telemetry.core.backends import png_bitmap from telemetry.core.backends import bitmap
from telemetry.core import util from telemetry.core import util
from telemetry.page import page_test from telemetry.page import page_test
from telemetry.page import page_set from telemetry.page import page_set
...@@ -97,7 +97,7 @@ class MapsValidator(page_test.PageTest): ...@@ -97,7 +97,7 @@ class MapsValidator(page_test.PageTest):
(x, y, screenshot.width, screenshot.height)) (x, y, screenshot.width, screenshot.height))
pixel_color = screenshot.GetPixelColor(x, y) pixel_color = screenshot.GetPixelColor(x, y)
expect_color = png_bitmap.PngColor( expect_color = bitmap.RgbaColor(
expectation["color"][0], expectation["color"][0],
expectation["color"][1], expectation["color"][1],
expectation["color"][2]) expectation["color"][2])
......
...@@ -8,7 +8,7 @@ import os ...@@ -8,7 +8,7 @@ import os
import re import re
from telemetry import test from telemetry import test
from telemetry.core.backends import png_bitmap from telemetry.core import bitmap
from telemetry.page import page_test from telemetry.page import page_test
test_data_dir = os.path.abspath(os.path.join( test_data_dir = os.path.abspath(os.path.join(
...@@ -110,7 +110,7 @@ class PixelValidator(page_test.PageTest): ...@@ -110,7 +110,7 @@ class PixelValidator(page_test.PageTest):
image_path = image_path + '_' + str(cur_revision) + '.png' image_path = image_path + '_' + str(cur_revision) + '.png'
try: try:
ref_png = png_bitmap.PngBitmap.FromFile(image_path) ref_png = bitmap.Bitmap.FromPngFile(image_path)
except IOError: except IOError:
ref_png = None ref_png = None
......
...@@ -7,9 +7,9 @@ import socket ...@@ -7,9 +7,9 @@ import socket
import sys import sys
import time import time
from telemetry.core import bitmap
from telemetry.core import exceptions from telemetry.core import exceptions
from telemetry.core import util from telemetry.core import util
from telemetry.core.backends import png_bitmap
from telemetry.core.backends.chrome import inspector_console from telemetry.core.backends.chrome import inspector_console
from telemetry.core.backends.chrome import inspector_memory from telemetry.core.backends.chrome import inspector_memory
from telemetry.core.backends.chrome import inspector_network from telemetry.core.backends.chrome import inspector_network
...@@ -139,7 +139,7 @@ class InspectorBackend(object): ...@@ -139,7 +139,7 @@ class InspectorBackend(object):
})() })()
""") """)
if snap: if snap:
return png_bitmap.PngBitmap.FromBase64(snap['data']) return bitmap.Bitmap.FromBase64Png(snap['data'])
return None return None
# Console public methods. # Console public methods.
......
# Copyright 2013 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.
import unittest
import os
from telemetry.core import util
from telemetry.core.backends import png_bitmap
# This is a simple base64 encoded 2x2 PNG which contains, in order, a single
# Red, Yellow, Blue, and Green pixel.
test_png = """
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
JpzAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACx
MBAJqcGAAAABZJREFUCNdj/M/AwPCfgYGB4T/DfwY
AHAAD/iOWZXsAAAAASUVORK5CYII=
"""
test_png_path = os.path.join(util.GetUnittestDataDir(), 'test_png.png')
test_png_2_path = os.path.join(util.GetUnittestDataDir(), 'test_png_2.png')
class PngBitmapTest(unittest.TestCase):
def testReadFromBase64(self):
png = png_bitmap.PngBitmap.FromBase64(test_png)
self.assertEquals(2, png.width)
self.assertEquals(2, png.height)
png.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
png.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
png.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
def testReadFromFile(self):
file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
self.assertEquals(2, file_png.width)
self.assertEquals(2, file_png.height)
file_png.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
file_png.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
file_png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
file_png.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
def testIsEqual(self):
png = png_bitmap.PngBitmap.FromBase64(test_png)
file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
self.assertTrue(png.IsEqual(file_png))
def testDiff(self):
file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
file_png_2 = png_bitmap.PngBitmap.FromFile(test_png_2_path)
diff_png = file_png.Diff(file_png)
self.assertEquals(2, diff_png.width)
self.assertEquals(2, diff_png.height)
diff_png.GetPixelColor(0, 0).AssertIsRGB(0, 0, 0)
diff_png.GetPixelColor(1, 1).AssertIsRGB(0, 0, 0)
diff_png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 0)
diff_png.GetPixelColor(1, 0).AssertIsRGB(0, 0, 0)
diff_png = file_png.Diff(file_png_2)
self.assertEquals(3, diff_png.width)
self.assertEquals(3, diff_png.height)
diff_png.GetPixelColor(0, 0).AssertIsRGB(0, 255, 255)
diff_png.GetPixelColor(1, 1).AssertIsRGB(255, 0, 255)
diff_png.GetPixelColor(0, 1).AssertIsRGB(255, 255, 0)
diff_png.GetPixelColor(1, 0).AssertIsRGB(0, 0, 255)
diff_png.GetPixelColor(0, 2).AssertIsRGB(255, 255, 255)
diff_png.GetPixelColor(1, 2).AssertIsRGB(255, 255, 255)
diff_png.GetPixelColor(2, 0).AssertIsRGB(255, 255, 255)
diff_png.GetPixelColor(2, 1).AssertIsRGB(255, 255, 255)
diff_png.GetPixelColor(2, 2).AssertIsRGB(255, 255, 255)
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import logging import logging
from telemetry.core.backends import png_bitmap from telemetry.core import bitmap
class WebDriverTabBackend(object): class WebDriverTabBackend(object):
def __init__(self, browser_backend, window_handle): def __init__(self, browser_backend, window_handle):
...@@ -53,7 +53,7 @@ class WebDriverTabBackend(object): ...@@ -53,7 +53,7 @@ class WebDriverTabBackend(object):
self._browser_backend.driver.switch_to_window(self._window_handle) self._browser_backend.driver.switch_to_window(self._window_handle)
snap = self._browser_backend.driver.get_screenshot_as_base64() snap = self._browser_backend.driver.get_screenshot_as_base64()
if snap: if snap:
return png_bitmap.PngBitmap(snap) return bitmap.Bitmap(snap)
return None return None
@property @property
......
...@@ -10,8 +10,8 @@ util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png') ...@@ -10,8 +10,8 @@ util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
import png # pylint: disable=F0401 import png # pylint: disable=F0401
class PngColor(object): class RgbaColor(object):
"""Encapsulates an RGB color retreived from a PngBitmap""" """Encapsulates an RGBA color retreived from a Bitmap"""
def __init__(self, r, g, b, a=255): def __init__(self, r, g, b, a=255):
self.r = r self.r = r
...@@ -30,14 +30,14 @@ class PngColor(object): ...@@ -30,14 +30,14 @@ class PngColor(object):
and b_diff <= tolerance and a_diff <= tolerance) and b_diff <= tolerance and a_diff <= tolerance)
def AssertIsRGB(self, r, g, b, tolerance=0): def AssertIsRGB(self, r, g, b, tolerance=0):
assert self.IsEqual(PngColor(r, g, b), tolerance) assert self.IsEqual(RgbaColor(r, g, b), tolerance)
def AssertIsRGBA(self, r, g, b, a, tolerance=0): def AssertIsRGBA(self, r, g, b, a, tolerance=0):
assert self.IsEqual(PngColor(r, g, b, a), tolerance) assert self.IsEqual(RgbaColor(r, g, b, a), tolerance)
class PngBitmap(object): class Bitmap(object):
"""Utilities for parsing and inspecting a PNG""" """Utilities for parsing and inspecting a bitmap."""
def __init__(self, png_data): def __init__(self, png_data):
self._png_data = png_data self._png_data = png_data
...@@ -59,48 +59,48 @@ class PngBitmap(object): ...@@ -59,48 +59,48 @@ class PngBitmap(object):
return self._height return self._height
def GetPixelColor(self, x, y): def GetPixelColor(self, x, y):
"""Returns a PngColor for the pixel at (x, y)""" """Returns a RgbaColor for the pixel at (x, y)"""
row = self._pixels[y] row = self._pixels[y]
offset = x * 4 offset = x * 4
return PngColor(row[offset], row[offset+1], row[offset+2], row[offset+3]) return RgbaColor(row[offset], row[offset+1], row[offset+2], row[offset+3])
def WriteFile(self, path): def WritePngFile(self, path):
with open(path, "wb") as f: with open(path, "wb") as f:
f.write(self._png_data) f.write(self._png_data)
@staticmethod @staticmethod
def FromFile(path): def FromPngFile(path):
with open(path, "rb") as f: with open(path, "rb") as f:
return PngBitmap(f.read()) return Bitmap(f.read())
@staticmethod @staticmethod
def FromBase64(base64_png): def FromBase64Png(base64_png):
return PngBitmap(base64.b64decode(base64_png)) return Bitmap(base64.b64decode(base64_png))
def IsEqual(self, expected_png, tolerance=0): def IsEqual(self, expected, tolerance=0):
"""Verifies that two PngBitmaps are identical within a given tolerance""" """Determines whether two Bitmaps are identical within a given tolerance"""
# Dimensions must be equal # Dimensions must be equal
if self.width != expected_png.width or self.height != expected_png.height: if self.width != expected.width or self.height != expected.height:
return False return False
# Loop over each pixel and test for equality # Loop over each pixel and test for equality
for y in range(self.height): for y in range(self.height):
for x in range(self.width): for x in range(self.width):
c0 = self.GetPixelColor(x, y) c0 = self.GetPixelColor(x, y)
c1 = expected_png.GetPixelColor(x, y) c1 = expected.GetPixelColor(x, y)
if not c0.IsEqual(c1, tolerance): if not c0.IsEqual(c1, tolerance):
return False return False
return True return True
def Diff(self, other_png): def Diff(self, other):
"""Returns a new PngBitmap that represents the difference between this image """Returns a new Bitmap that represents the difference between this image
and another PngBitmap""" and another Bitmap."""
# Output dimensions will be the maximum of the two input dimensions # Output dimensions will be the maximum of the two input dimensions
out_width = max(self.width, other_png.width) out_width = max(self.width, other.width)
out_height = max(self.height, other_png.height) out_height = max(self.height, other.height)
diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)] diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)]
...@@ -110,12 +110,12 @@ class PngBitmap(object): ...@@ -110,12 +110,12 @@ class PngBitmap(object):
if x < self.width and y < self.height: if x < self.width and y < self.height:
c0 = self.GetPixelColor(x, y) c0 = self.GetPixelColor(x, y)
else: else:
c0 = PngColor(0, 0, 0, 0) c0 = RgbaColor(0, 0, 0, 0)
if x < other_png.width and y < other_png.height: if x < other.width and y < other.height:
c1 = other_png.GetPixelColor(x, y) c1 = other.GetPixelColor(x, y)
else: else:
c1 = PngColor(0, 0, 0, 0) c1 = RgbaColor(0, 0, 0, 0)
offset = x * 3 offset = x * 3
diff[y][offset] = abs(c0.r - c1.r) diff[y][offset] = abs(c0.r - c1.r)
...@@ -123,29 +123,30 @@ class PngBitmap(object): ...@@ -123,29 +123,30 @@ class PngBitmap(object):
diff[y][offset+2] = abs(c0.b - c1.b) diff[y][offset+2] = abs(c0.b - c1.b)
# This particular method can only save to a file, so the result will be # This particular method can only save to a file, so the result will be
# written into an in-memory buffer and read back into a PngBitmap # written into an in-memory buffer and read back into a Bitmap
diff_img = png.from_array(diff, mode='RGB') diff_img = png.from_array(diff, mode='RGB')
output = cStringIO.StringIO() output = cStringIO.StringIO()
try: try:
diff_img.save(output) diff_img.save(output)
diff_png = PngBitmap(output.getvalue()) diff = Bitmap(output.getvalue())
finally: finally:
output.close() output.close()
return diff_png return diff
def Crop(self, left, top, width, height): def Crop(self, left, top, width, height):
"""Returns a new PngBitmap that represents the specified sub-rect of this """Returns a new Bitmap that represents the specified sub-rect of this."""
PngBitmap"""
if (left < 0 or top < 0 or if (left < 0 or top < 0 or
(left + width) > self.width or (left + width) > self.width or
(top + height) > self.height): (top + height) > self.height):
raise Exception('Invalid dimensions') raise ValueError('Invalid dimensions')
img_data = [[0 for x in xrange(width * 4)] for x in xrange(height)] img_data = [[0 for x in xrange(width * 4)] for x in xrange(height)]
# Copy each pixel in the sub-rect # Copy each pixel in the sub-rect.
# TODO(tonyg): Make this faster by avoiding the copy and artificially
# restricting the dimensions.
for y in range(height): for y in range(height):
for x in range(width): for x in range(width):
c = self.GetPixelColor(x + left, y + top) c = self.GetPixelColor(x + left, y + top)
...@@ -156,13 +157,13 @@ class PngBitmap(object): ...@@ -156,13 +157,13 @@ class PngBitmap(object):
img_data[y][offset+3] = c.a img_data[y][offset+3] = c.a
# This particular method can only save to a file, so the result will be # This particular method can only save to a file, so the result will be
# written into an in-memory buffer and read back into a PngBitmap # written into an in-memory buffer and read back into a Bitmap
crop_img = png.from_array(img_data, mode='RGBA') crop_img = png.from_array(img_data, mode='RGBA')
output = cStringIO.StringIO() output = cStringIO.StringIO()
try: try:
crop_img.save(output) crop_img.save(output)
crop_png = PngBitmap(output.getvalue()) crop = Bitmap(output.getvalue())
finally: finally:
output.close() output.close()
return crop_png return crop
# Copyright 2013 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.
import unittest
import os
from telemetry.core import bitmap
from telemetry.core import util
# This is a simple base64 encoded 2x2 PNG which contains, in order, a single
# Red, Yellow, Blue, and Green pixel.
test_png = """
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
JpzAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACx
MBAJqcGAAAABZJREFUCNdj/M/AwPCfgYGB4T/DfwY
AHAAD/iOWZXsAAAAASUVORK5CYII=
"""
test_png_path = os.path.join(util.GetUnittestDataDir(), 'test_png.png')
test_png_2_path = os.path.join(util.GetUnittestDataDir(), 'test_png_2.png')
class BitmapTest(unittest.TestCase):
def testReadFromBase64Png(self):
bmp = bitmap.Bitmap.FromBase64Png(test_png)
self.assertEquals(2, bmp.width)
self.assertEquals(2, bmp.height)
bmp.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
bmp.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
bmp.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
bmp.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
def testReadFromPnfFile(self):
file_bmp = bitmap.Bitmap.FromPngFile(test_png_path)
self.assertEquals(2, file_bmp.width)
self.assertEquals(2, file_bmp.height)
file_bmp.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
file_bmp.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
file_bmp.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
file_bmp.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
def testIsEqual(self):
bmp = bitmap.Bitmap.FromBase64Png(test_png)
file_bmp = bitmap.Bitmap.FromPngFile(test_png_path)
self.assertTrue(bmp.IsEqual(file_bmp))
def testDiff(self):
file_bmp = bitmap.Bitmap.FromPngFile(test_png_path)
file_bmp_2 = bitmap.Bitmap.FromPngFile(test_png_2_path)
diff_bmp = file_bmp.Diff(file_bmp)
self.assertEquals(2, diff_bmp.width)
self.assertEquals(2, diff_bmp.height)
diff_bmp.GetPixelColor(0, 0).AssertIsRGB(0, 0, 0)
diff_bmp.GetPixelColor(1, 1).AssertIsRGB(0, 0, 0)
diff_bmp.GetPixelColor(0, 1).AssertIsRGB(0, 0, 0)
diff_bmp.GetPixelColor(1, 0).AssertIsRGB(0, 0, 0)
diff_bmp = file_bmp.Diff(file_bmp_2)
self.assertEquals(3, diff_bmp.width)
self.assertEquals(3, diff_bmp.height)
diff_bmp.GetPixelColor(0, 0).AssertIsRGB(0, 255, 255)
diff_bmp.GetPixelColor(1, 1).AssertIsRGB(255, 0, 255)
diff_bmp.GetPixelColor(0, 1).AssertIsRGB(255, 255, 0)
diff_bmp.GetPixelColor(1, 0).AssertIsRGB(0, 0, 255)
diff_bmp.GetPixelColor(0, 2).AssertIsRGB(255, 255, 255)
diff_bmp.GetPixelColor(1, 2).AssertIsRGB(255, 255, 255)
diff_bmp.GetPixelColor(2, 0).AssertIsRGB(255, 255, 255)
diff_bmp.GetPixelColor(2, 1).AssertIsRGB(255, 255, 255)
diff_bmp.GetPixelColor(2, 2).AssertIsRGB(255, 255, 255)
...@@ -154,7 +154,7 @@ class Platform(object): ...@@ -154,7 +154,7 @@ class Platform(object):
(time_ms, bitmap) tuples representing each video keyframe. Only the first (time_ms, bitmap) tuples representing each video keyframe. Only the first
frame in a run of sequential duplicate bitmaps is included. frame in a run of sequential duplicate bitmaps is included.
time_ms is milliseconds relative to the first frame. time_ms is milliseconds relative to the first frame.
bitmap is a telemetry.core.backends.png_bitmap. bitmap is a telemetry.core.Bitmap.
""" """
for t in self._platform_backend.StopVideoCapture(): for t in self._platform_backend.StopVideoCapture():
yield t yield t
......
...@@ -71,7 +71,7 @@ class Tab(web_contents.WebContents): ...@@ -71,7 +71,7 @@ class Tab(web_contents.WebContents):
"""Capture a screenshot of the tab's contents. """Capture a screenshot of the tab's contents.
Returns: Returns:
A telemetry.core.backends.png_bitmap. A telemetry.core.Bitmap.
""" """
return self._inspector_backend.Screenshot(timeout) return self._inspector_backend.Screenshot(timeout)
...@@ -97,7 +97,7 @@ class Tab(web_contents.WebContents): ...@@ -97,7 +97,7 @@ class Tab(web_contents.WebContents):
(time_ms, bitmap) tuples representing each video keyframe. Only the first (time_ms, bitmap) tuples representing each video keyframe. Only the first
frame in a run of sequential duplicate bitmaps is included. frame in a run of sequential duplicate bitmaps is included.
time_ms is milliseconds since navigationStart. time_ms is milliseconds since navigationStart.
bitmap is a telemetry.core.backends.png_bitmap. bitmap is a telemetry.core.Bitmap.
""" """
# TODO(tonyg/szym): platform's video capture may include offscreen content # TODO(tonyg/szym): platform's video capture may include offscreen content
# from a webcam, OS framing and browser framing. However, this API must # from a webcam, OS framing and browser framing. However, this API must
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment