Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: tools/telemetry/telemetry/core/bitmap.py

Issue 116903002: Revert of [telemetry] Implement per-pixel algorithms in Bitmap as a C++ extension. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/telemetry/telemetry/core/bitmap_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 import base64 4 import base64
5 import cStringIO 5 import cStringIO
6 6
7 from telemetry.core import bitmaptools
8 from telemetry.core import util 7 from telemetry.core import util
9 8
10 util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png') 9 util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
11 import png # pylint: disable=F0401 10 import png # pylint: disable=F0401
12 11
13 12
14 class RgbaColor(object): 13 class RgbaColor(object):
15 """Encapsulates an RGBA color retreived from a Bitmap""" 14 """Encapsulates an RGBA color retreived from a Bitmap"""
16 15
17 def __init__(self, r, g, b, a=255): 16 def __init__(self, r, g, b, a=255):
(...skipping 27 matching lines...) Expand all
45 assert width > 0, 'Invalid width' 44 assert width > 0, 'Invalid width'
46 assert height > 0, 'Invalid height' 45 assert height > 0, 'Invalid height'
47 assert pixels, 'Must specify pixels' 46 assert pixels, 'Must specify pixels'
48 assert bpp * width * height == len(pixels), 'Dimensions and pixels mismatch' 47 assert bpp * width * height == len(pixels), 'Dimensions and pixels mismatch'
49 48
50 self._bpp = bpp 49 self._bpp = bpp
51 self._width = width 50 self._width = width
52 self._height = height 51 self._height = height
53 self._pixels = pixels 52 self._pixels = pixels
54 self._metadata = metadata or {} 53 self._metadata = metadata or {}
55 self._crop_box = None
56 54
57 @property 55 @property
58 def bpp(self): 56 def bpp(self):
59 """Bytes per pixel.""" 57 """Bytes per pixel."""
60 return self._bpp 58 return self._bpp
61 59
62 @property 60 @property
63 def width(self): 61 def width(self):
64 """Width of the bitmap.""" 62 """Width of the bitmap."""
65 if self._crop_box:
66 return self._crop_box[2]
67 return self._width 63 return self._width
68 64
69 @property 65 @property
70 def height(self): 66 def height(self):
71 """Height of the bitmap.""" 67 """Height of the bitmap."""
72 if self._crop_box:
73 return self._crop_box[3]
74 return self._height 68 return self._height
75 69
76 @property 70 @property
77 def _as_tuple(self):
78 # If we got a list of ints, we need to convert it into a byte buffer.
79 pixels = self._pixels
80 if type(pixels) is not bytearray:
81 pixels = bytearray(pixels)
82 if type(pixels) is not bytes:
83 pixels = bytes(pixels)
84 crop_box = self._crop_box or (0, 0, self._width, self._height)
85 return pixels, self._width, self._bpp, crop_box
86
87 @property
88 def pixels(self): 71 def pixels(self):
89 """Flat pixel array of the bitmap.""" 72 """Flat pixel array of the bitmap."""
90 if self._crop_box:
91 self._pixels = bitmaptools.Crop(self._as_tuple)
92 _, _, self._width, self._height = self._crop_box
93 self._crop_box = None
94 if type(self._pixels) is not bytearray: 73 if type(self._pixels) is not bytearray:
95 self._pixels = bytearray(self._pixels) 74 self._pixels = bytearray(self._pixels)
96 return self._pixels 75 return self._pixels
97 76
98 @property 77 @property
99 def metadata(self): 78 def metadata(self):
100 self._metadata['size'] = (self.width, self.height) 79 self._metadata['size'] = (self.width, self.height)
101 self._metadata['alpha'] = self.bpp == 4 80 self._metadata['alpha'] = self.bpp == 4
102 self._metadata['bitdepth'] = 8 81 self._metadata['bitdepth'] = 8
103 return self._metadata 82 return self._metadata
104 83
105 def GetPixelColor(self, x, y): 84 def GetPixelColor(self, x, y):
106 """Returns a RgbaColor for the pixel at (x, y).""" 85 """Returns a RgbaColor for the pixel at (x, y)."""
107 pixels = self.pixels
108 base = self._bpp * (y * self._width + x) 86 base = self._bpp * (y * self._width + x)
109 if self._bpp == 4: 87 if self._bpp == 4:
110 return RgbaColor(pixels[base + 0], pixels[base + 1], 88 return RgbaColor(self._pixels[base + 0], self._pixels[base + 1],
111 pixels[base + 2], pixels[base + 3]) 89 self._pixels[base + 2], self._pixels[base + 3])
112 return RgbaColor(pixels[base + 0], pixels[base + 1], 90 return RgbaColor(self._pixels[base + 0], self._pixels[base + 1],
113 pixels[base + 2]) 91 self._pixels[base + 2])
114 92
115 def WritePngFile(self, path): 93 def WritePngFile(self, path):
116 with open(path, "wb") as f: 94 with open(path, "wb") as f:
117 png.Writer(**self.metadata).write_array(f, self.pixels) 95 png.Writer(**self.metadata).write_array(f, self.pixels)
118 96
119 @staticmethod 97 @staticmethod
120 def FromPng(png_data): 98 def FromPng(png_data):
121 width, height, pixels, meta = png.Reader(bytes=png_data).read_flat() 99 width, height, pixels, meta = png.Reader(bytes=png_data).read_flat()
122 return Bitmap(4 if meta['alpha'] else 3, width, height, pixels, meta) 100 return Bitmap(4 if meta['alpha'] else 3, width, height, pixels, meta)
123 101
124 @staticmethod 102 @staticmethod
125 def FromPngFile(path): 103 def FromPngFile(path):
126 with open(path, "rb") as f: 104 with open(path, "rb") as f:
127 return Bitmap.FromPng(f.read()) 105 return Bitmap.FromPng(f.read())
128 106
129 @staticmethod 107 @staticmethod
130 def FromBase64Png(base64_png): 108 def FromBase64Png(base64_png):
131 return Bitmap.FromPng(base64.b64decode(base64_png)) 109 return Bitmap.FromPng(base64.b64decode(base64_png))
132 110
133 def IsEqual(self, other, tolerance=0): 111 def IsEqual(self, other, tolerance=0):
134 """Determines whether two Bitmaps are identical within a given tolerance. 112 """Determines whether two Bitmaps are identical within a given tolerance."""
135 Ignores alpha channel.""" 113
136 # pylint: disable=W0212 114 # Dimensions must be equal
137 return bitmaptools.Equal(self._as_tuple, other._as_tuple, tolerance) 115 if self.width != other.width or self.height != other.height:
116 return False
117
118 # Loop over each pixel and test for equality
119 if tolerance or self.bpp != other.bpp:
120 for y in range(self.height):
121 for x in range(self.width):
122 c0 = self.GetPixelColor(x, y)
123 c1 = other.GetPixelColor(x, y)
124 if not c0.IsEqual(c1, tolerance):
125 return False
126 else:
127 return self.pixels == other.pixels
128
129 return True
138 130
139 def Diff(self, other): 131 def Diff(self, other):
140 """Returns a new Bitmap that represents the difference between this image 132 """Returns a new Bitmap that represents the difference between this image
141 and another Bitmap.""" 133 and another Bitmap."""
142 134
143 # Output dimensions will be the maximum of the two input dimensions 135 # Output dimensions will be the maximum of the two input dimensions
144 out_width = max(self.width, other.width) 136 out_width = max(self.width, other.width)
145 out_height = max(self.height, other.height) 137 out_height = max(self.height, other.height)
146 138
147 diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)] 139 diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)]
(...skipping 22 matching lines...) Expand all
170 output = cStringIO.StringIO() 162 output = cStringIO.StringIO()
171 try: 163 try:
172 diff_img.save(output) 164 diff_img.save(output)
173 diff = Bitmap.FromPng(output.getvalue()) 165 diff = Bitmap.FromPng(output.getvalue())
174 finally: 166 finally:
175 output.close() 167 output.close()
176 168
177 return diff 169 return diff
178 170
179 def GetBoundingBox(self, color, tolerance=0): 171 def GetBoundingBox(self, color, tolerance=0):
180 """Finds the minimum box surrounding all occurences of |color|. 172 """Returns a (top, left, width, height) tuple of the minimum box
181 Returns: (top, left, width, height), match_count 173 surrounding all occurences of |color|."""
182 Ignores the alpha channel.""" 174 # TODO(szym): Implement this.
183 int_color = (color.r << 16) | (color.g << 8) | color.b 175 raise NotImplementedError("GetBoundingBox not yet implemented.")
184 return bitmaptools.BoundingBox(self._as_tuple, int_color, tolerance)
185 176
186 def Crop(self, top, left, width, height): 177 def Crop(self, top, left, width, height):
187 """Crops the current bitmap down to the specified box.""" 178 """Crops the current bitmap down to the specified box.
188 cur_box = self._crop_box or (0, 0, self._width, self._height)
189 cur_left, cur_top, cur_width, cur_height = cur_box
190 179
180 TODO(szym): Make this O(1).
181 """
191 if (left < 0 or top < 0 or 182 if (left < 0 or top < 0 or
192 (left + width) > cur_width or 183 (left + width) > self.width or
193 (top + height) > cur_height): 184 (top + height) > self.height):
194 raise ValueError('Invalid dimensions') 185 raise ValueError('Invalid dimensions')
195 186
196 self._crop_box = cur_left + left, cur_top + top, width, height 187 img_data = [[0 for x in xrange(width * self.bpp)]
188 for y in xrange(height)]
189
190 # Copy each pixel in the sub-rect.
191 # TODO(tonyg): Make this faster by avoiding the copy and artificially
192 # restricting the dimensions.
193 for y in range(height):
194 for x in range(width):
195 c = self.GetPixelColor(x + left, y + top)
196 offset = x * self.bpp
197 img_data[y][offset] = c.r
198 img_data[y][offset + 1] = c.g
199 img_data[y][offset + 2] = c.b
200 if self.bpp == 4:
201 img_data[y][offset + 3] = c.a
202
203 # This particular method can only save to a file, so the result will be
204 # written into an in-memory buffer and read back into a Bitmap
205 crop_img = png.from_array(img_data, mode='RGBA' if self.bpp == 4 else 'RGB')
206 output = cStringIO.StringIO()
207 try:
208 crop_img.save(output)
209 width, height, pixels, meta = png.Reader(
210 bytes=output.getvalue()).read_flat()
211 self._width = width
212 self._height = height
213 self._pixels = pixels
214 self._metadata = meta
215 finally:
216 output.close()
217
197 return self 218 return self
198 219
199 def ColorHistogram(self): 220 def ColorHistogram(self):
200 """Computes a histogram of the pixel colors in this Bitmap. 221 """Returns a histogram of the pixel colors in this Bitmap."""
201 Returns a list of 3x256 integers.""" 222 # TODO(szym): Implement this.
202 return bitmaptools.Histogram(self._as_tuple) 223 raise NotImplementedError("ColorHistogram not yet implemented.")
OLDNEW
« no previous file with comments | « no previous file | tools/telemetry/telemetry/core/bitmap_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698