| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 import base64 | |
| 5 import cStringIO | |
| 6 | |
| 7 from telemetry.core import util | |
| 8 | |
| 9 util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png') | |
| 10 import png # pylint: disable=F0401 | |
| 11 | |
| 12 | |
| 13 class PngColor(object): | |
| 14 """Encapsulates an RGB color retreived from a PngBitmap""" | |
| 15 | |
| 16 def __init__(self, r, g, b, a=255): | |
| 17 self.r = r | |
| 18 self.g = g | |
| 19 self.b = b | |
| 20 self.a = a | |
| 21 | |
| 22 def IsEqual(self, expected_color, tolerance=0): | |
| 23 """Verifies that the color is within a given tolerance of | |
| 24 the expected color""" | |
| 25 r_diff = abs(self.r - expected_color.r) | |
| 26 g_diff = abs(self.g - expected_color.g) | |
| 27 b_diff = abs(self.b - expected_color.b) | |
| 28 a_diff = abs(self.a - expected_color.a) | |
| 29 return (r_diff <= tolerance and g_diff <= tolerance | |
| 30 and b_diff <= tolerance and a_diff <= tolerance) | |
| 31 | |
| 32 def AssertIsRGB(self, r, g, b, tolerance=0): | |
| 33 assert self.IsEqual(PngColor(r, g, b), tolerance) | |
| 34 | |
| 35 def AssertIsRGBA(self, r, g, b, a, tolerance=0): | |
| 36 assert self.IsEqual(PngColor(r, g, b, a), tolerance) | |
| 37 | |
| 38 | |
| 39 class PngBitmap(object): | |
| 40 """Utilities for parsing and inspecting a PNG""" | |
| 41 | |
| 42 def __init__(self, png_data): | |
| 43 self._png_data = png_data | |
| 44 self._png = png.Reader(bytes=self._png_data) | |
| 45 rgba8_data = self._png.asRGBA8() | |
| 46 self._width = rgba8_data[0] | |
| 47 self._height = rgba8_data[1] | |
| 48 self._pixels = list(rgba8_data[2]) | |
| 49 self._metadata = rgba8_data[3] | |
| 50 | |
| 51 @property | |
| 52 def width(self): | |
| 53 """Width of the snapshot""" | |
| 54 return self._width | |
| 55 | |
| 56 @property | |
| 57 def height(self): | |
| 58 """Height of the snapshot""" | |
| 59 return self._height | |
| 60 | |
| 61 def GetPixelColor(self, x, y): | |
| 62 """Returns a PngColor for the pixel at (x, y)""" | |
| 63 row = self._pixels[y] | |
| 64 offset = x * 4 | |
| 65 return PngColor(row[offset], row[offset+1], row[offset+2], row[offset+3]) | |
| 66 | |
| 67 def WriteFile(self, path): | |
| 68 with open(path, "wb") as f: | |
| 69 f.write(self._png_data) | |
| 70 | |
| 71 @staticmethod | |
| 72 def FromFile(path): | |
| 73 with open(path, "rb") as f: | |
| 74 return PngBitmap(f.read()) | |
| 75 | |
| 76 @staticmethod | |
| 77 def FromBase64(base64_png): | |
| 78 return PngBitmap(base64.b64decode(base64_png)) | |
| 79 | |
| 80 def IsEqual(self, expected_png, tolerance=0): | |
| 81 """Verifies that two PngBitmaps are identical within a given tolerance""" | |
| 82 | |
| 83 # Dimensions must be equal | |
| 84 if self.width != expected_png.width or self.height != expected_png.height: | |
| 85 return False | |
| 86 | |
| 87 # Loop over each pixel and test for equality | |
| 88 for y in range(self.height): | |
| 89 for x in range(self.width): | |
| 90 c0 = self.GetPixelColor(x, y) | |
| 91 c1 = expected_png.GetPixelColor(x, y) | |
| 92 if not c0.IsEqual(c1, tolerance): | |
| 93 return False | |
| 94 | |
| 95 return True | |
| 96 | |
| 97 def Diff(self, other_png): | |
| 98 """Returns a new PngBitmap that represents the difference between this image | |
| 99 and another PngBitmap""" | |
| 100 | |
| 101 # Output dimensions will be the maximum of the two input dimensions | |
| 102 out_width = max(self.width, other_png.width) | |
| 103 out_height = max(self.height, other_png.height) | |
| 104 | |
| 105 diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)] | |
| 106 | |
| 107 # Loop over each pixel and test for equality | |
| 108 for y in range(out_height): | |
| 109 for x in range(out_width): | |
| 110 if x < self.width and y < self.height: | |
| 111 c0 = self.GetPixelColor(x, y) | |
| 112 else: | |
| 113 c0 = PngColor(0, 0, 0, 0) | |
| 114 | |
| 115 if x < other_png.width and y < other_png.height: | |
| 116 c1 = other_png.GetPixelColor(x, y) | |
| 117 else: | |
| 118 c1 = PngColor(0, 0, 0, 0) | |
| 119 | |
| 120 offset = x * 3 | |
| 121 diff[y][offset] = abs(c0.r - c1.r) | |
| 122 diff[y][offset+1] = abs(c0.g - c1.g) | |
| 123 diff[y][offset+2] = abs(c0.b - c1.b) | |
| 124 | |
| 125 # This particular method can only save to a file, so the result will be | |
| 126 # written into an in-memory buffer and read back into a PngBitmap | |
| 127 diff_img = png.from_array(diff, mode='RGB') | |
| 128 output = cStringIO.StringIO() | |
| 129 try: | |
| 130 diff_img.save(output) | |
| 131 diff_png = PngBitmap(output.getvalue()) | |
| 132 finally: | |
| 133 output.close() | |
| 134 | |
| 135 return diff_png | |
| 136 | |
| OLD | NEW |