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
import maps_expectations
from telemetry import test
from telemetry.core.backends import png_bitmap
from telemetry.core.backends import bitmap
from telemetry.core import util
from telemetry.page import page_test
from telemetry.page import page_set
......@@ -97,7 +97,7 @@ class MapsValidator(page_test.PageTest):
(x, y, screenshot.width, screenshot.height))
pixel_color = screenshot.GetPixelColor(x, y)
expect_color = png_bitmap.PngColor(
expect_color = bitmap.RgbaColor(
expectation["color"][0],
expectation["color"][1],
expectation["color"][2])
......
......@@ -8,7 +8,7 @@ import os
import re
from telemetry import test
from telemetry.core.backends import png_bitmap
from telemetry.core import bitmap
from telemetry.page import page_test
test_data_dir = os.path.abspath(os.path.join(
......@@ -110,7 +110,7 @@ class PixelValidator(page_test.PageTest):
image_path = image_path + '_' + str(cur_revision) + '.png'
try:
ref_png = png_bitmap.PngBitmap.FromFile(image_path)
ref_png = bitmap.Bitmap.FromPngFile(image_path)
except IOError:
ref_png = None
......
......@@ -7,9 +7,9 @@ import socket
import sys
import time
from telemetry.core import bitmap
from telemetry.core import exceptions
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_memory
from telemetry.core.backends.chrome import inspector_network
......@@ -139,7 +139,7 @@ class InspectorBackend(object):
})()
""")
if snap:
return png_bitmap.PngBitmap.FromBase64(snap['data'])
return bitmap.Bitmap.FromBase64Png(snap['data'])
return None
# 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 @@
import logging
from telemetry.core.backends import png_bitmap
from telemetry.core import bitmap
class WebDriverTabBackend(object):
def __init__(self, browser_backend, window_handle):
......@@ -53,7 +53,7 @@ class WebDriverTabBackend(object):
self._browser_backend.driver.switch_to_window(self._window_handle)
snap = self._browser_backend.driver.get_screenshot_as_base64()
if snap:
return png_bitmap.PngBitmap(snap)
return bitmap.Bitmap(snap)
return None
@property
......
......@@ -10,8 +10,8 @@ util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
import png # pylint: disable=F0401
class PngColor(object):
"""Encapsulates an RGB color retreived from a PngBitmap"""
class RgbaColor(object):
"""Encapsulates an RGBA color retreived from a Bitmap"""
def __init__(self, r, g, b, a=255):
self.r = r
......@@ -30,14 +30,14 @@ class PngColor(object):
and b_diff <= tolerance and a_diff <= tolerance)
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):
assert self.IsEqual(PngColor(r, g, b, a), tolerance)
assert self.IsEqual(RgbaColor(r, g, b, a), tolerance)
class PngBitmap(object):
"""Utilities for parsing and inspecting a PNG"""
class Bitmap(object):
"""Utilities for parsing and inspecting a bitmap."""
def __init__(self, png_data):
self._png_data = png_data
......@@ -59,48 +59,48 @@ class PngBitmap(object):
return self._height
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]
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:
f.write(self._png_data)
@staticmethod
def FromFile(path):
def FromPngFile(path):
with open(path, "rb") as f:
return PngBitmap(f.read())
return Bitmap(f.read())
@staticmethod
def FromBase64(base64_png):
return PngBitmap(base64.b64decode(base64_png))
def FromBase64Png(base64_png):
return Bitmap(base64.b64decode(base64_png))
def IsEqual(self, expected_png, tolerance=0):
"""Verifies that two PngBitmaps are identical within a given tolerance"""
def IsEqual(self, expected, tolerance=0):
"""Determines whether two Bitmaps are identical within a given tolerance"""
# 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
# Loop over each pixel and test for equality
for y in range(self.height):
for x in range(self.width):
c0 = self.GetPixelColor(x, y)
c1 = expected_png.GetPixelColor(x, y)
c1 = expected.GetPixelColor(x, y)
if not c0.IsEqual(c1, tolerance):
return False
return True
def Diff(self, other_png):
"""Returns a new PngBitmap that represents the difference between this image
and another PngBitmap"""
def Diff(self, other):
"""Returns a new Bitmap that represents the difference between this image
and another Bitmap."""
# Output dimensions will be the maximum of the two input dimensions
out_width = max(self.width, other_png.width)
out_height = max(self.height, other_png.height)
out_width = max(self.width, other.width)
out_height = max(self.height, other.height)
diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)]
......@@ -110,12 +110,12 @@ class PngBitmap(object):
if x < self.width and y < self.height:
c0 = self.GetPixelColor(x, y)
else:
c0 = PngColor(0, 0, 0, 0)
c0 = RgbaColor(0, 0, 0, 0)
if x < other_png.width and y < other_png.height:
c1 = other_png.GetPixelColor(x, y)
if x < other.width and y < other.height:
c1 = other.GetPixelColor(x, y)
else:
c1 = PngColor(0, 0, 0, 0)
c1 = RgbaColor(0, 0, 0, 0)
offset = x * 3
diff[y][offset] = abs(c0.r - c1.r)
......@@ -123,29 +123,30 @@ class PngBitmap(object):
diff[y][offset+2] = abs(c0.b - c1.b)
# 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')
output = cStringIO.StringIO()
try:
diff_img.save(output)
diff_png = PngBitmap(output.getvalue())
diff = Bitmap(output.getvalue())
finally:
output.close()
return diff_png
return diff
def Crop(self, left, top, width, height):
"""Returns a new PngBitmap that represents the specified sub-rect of this
PngBitmap"""
"""Returns a new Bitmap that represents the specified sub-rect of this."""
if (left < 0 or top < 0 or
(left + width) > self.width or
(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)]
# 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 x in range(width):
c = self.GetPixelColor(x + left, y + top)
......@@ -156,13 +157,13 @@ class PngBitmap(object):
img_data[y][offset+3] = c.a
# 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')
output = cStringIO.StringIO()
try:
crop_img.save(output)
crop_png = PngBitmap(output.getvalue())
crop = Bitmap(output.getvalue())
finally:
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):
(time_ms, bitmap) tuples representing each video keyframe. Only the first
frame in a run of sequential duplicate bitmaps is included.
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():
yield t
......
......@@ -71,7 +71,7 @@ class Tab(web_contents.WebContents):
"""Capture a screenshot of the tab's contents.
Returns:
A telemetry.core.backends.png_bitmap.
A telemetry.core.Bitmap.
"""
return self._inspector_backend.Screenshot(timeout)
......@@ -97,7 +97,7 @@ class Tab(web_contents.WebContents):
(time_ms, bitmap) tuples representing each video keyframe. Only the first
frame in a run of sequential duplicate bitmaps is included.
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
# 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