46 service_name: Service name expected for all calls. |
46 service_name: Service name expected for all calls. |
47 """ |
47 """ |
48 super(ImagesServiceStub, self).__init__(service_name) |
48 super(ImagesServiceStub, self).__init__(service_name) |
49 Image.init() |
49 Image.init() |
50 |
50 |
|
51 def _Dynamic_Composite(self, request, response): |
|
52 """Implementation of ImagesService::Composite. |
|
53 |
|
54 Based off documentation of the PIL library at |
|
55 http://www.pythonware.com/library/pil/handbook/index.htm |
|
56 |
|
57 Args: |
|
58 request: ImagesCompositeRequest, contains image request info. |
|
59 response: ImagesCompositeResponse, contains transformed image. |
|
60 """ |
|
61 width = request.canvas().width() |
|
62 height = request.canvas().height() |
|
63 color = request.canvas().color() % 0x100000000 |
|
64 reordered_color = int((color & 0xff000000) | ((color >> 16) & 0xff) | |
|
65 (color & 0xff00) | (color & 0xff) << 16) |
|
66 canvas = Image.new("RGBA", (width, height), reordered_color) |
|
67 sources = [] |
|
68 if (not request.canvas().width() or request.canvas().width() > 4000 or |
|
69 not request.canvas().height() or request.canvas().height() > 4000): |
|
70 raise apiproxy_errors.ApplicationError( |
|
71 images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA) |
|
72 if not request.image_size(): |
|
73 raise apiproxy_errors.ApplicationError( |
|
74 images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA) |
|
75 if not request.options_size(): |
|
76 raise apiproxy_errors.ApplicationError( |
|
77 images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA) |
|
78 if request.options_size() > images.MAX_COMPOSITES_PER_REQUEST: |
|
79 raise apiproxy_errors.ApplicationError( |
|
80 images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA) |
|
81 for image in request.image_list(): |
|
82 sources.append(self._OpenImage(image.content())) |
|
83 |
|
84 for options in request.options_list(): |
|
85 if (options.anchor() < images.TOP_LEFT or |
|
86 options.anchor() > images.BOTTOM_RIGHT): |
|
87 raise apiproxy_errors.ApplicationError( |
|
88 images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA) |
|
89 if options.source_index() >= len(sources) or options.source_index() < 0: |
|
90 raise apiproxy_errors.ApplicationError( |
|
91 images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA) |
|
92 if options.opacity() < 0 or options.opacity() > 1: |
|
93 raise apiproxy_errors.ApplicationError( |
|
94 images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA) |
|
95 source = sources[options.source_index()] |
|
96 x_anchor = (options.anchor() % 3) * 0.5 |
|
97 y_anchor = (options.anchor() / 3) * 0.5 |
|
98 x_offset = int(options.x_offset() + x_anchor * (width - source.size[0])) |
|
99 y_offset = int(options.y_offset() + y_anchor * (height - source.size[1])) |
|
100 alpha = options.opacity() * 255 |
|
101 mask = Image.new("L", source.size, alpha) |
|
102 canvas.paste(source, (x_offset, y_offset), mask) |
|
103 response_value = self._EncodeImage(canvas, request.canvas().output()) |
|
104 response.mutable_image().set_content(response_value) |
|
105 |
|
106 def _Dynamic_Histogram(self, request, response): |
|
107 """Trivial implementation of ImagesService::Histogram. |
|
108 |
|
109 Based off documentation of the PIL library at |
|
110 http://www.pythonware.com/library/pil/handbook/index.htm |
|
111 |
|
112 Args: |
|
113 request: ImagesHistogramRequest, contains the image. |
|
114 response: ImagesHistogramResponse, contains histogram of the image. |
|
115 """ |
|
116 image = self._OpenImage(request.image().content()) |
|
117 img_format = image.format |
|
118 if img_format not in ("BMP", "GIF", "ICO", "JPEG", "PNG", "TIFF"): |
|
119 raise apiproxy_errors.ApplicationError( |
|
120 images_service_pb.ImagesServiceError.NOT_IMAGE) |
|
121 image = image.convert("RGBA") |
|
122 red = [0] * 256 |
|
123 green = [0] * 256 |
|
124 blue = [0] * 256 |
|
125 for pixel in image.getdata(): |
|
126 red[int((pixel[0] * pixel[3]) / 255)] += 1 |
|
127 green[int((pixel[1] * pixel[3]) / 255)] += 1 |
|
128 blue[int((pixel[2] * pixel[3]) / 255)] += 1 |
|
129 histogram = response.mutable_histogram() |
|
130 for value in red: |
|
131 histogram.add_red(value) |
|
132 for value in green: |
|
133 histogram.add_green(value) |
|
134 for value in blue: |
|
135 histogram.add_blue(value) |
|
136 |
51 def _Dynamic_Transform(self, request, response): |
137 def _Dynamic_Transform(self, request, response): |
52 """Trivial implementation of ImagesService::Transform. |
138 """Trivial implementation of ImagesService::Transform. |
53 |
139 |
54 Based off documentation of the PIL library at |
140 Based off documentation of the PIL library at |
55 http://www.pythonware.com/library/pil/handbook/index.htm |
141 http://www.pythonware.com/library/pil/handbook/index.htm |
56 |
142 |
57 Args: |
143 Args: |
58 request: ImagesTransformRequest, contains image request info. |
144 request: ImagesTransformRequest, contains image request info. |
59 response: ImagesTransformResponse, contains transformed image. |
145 response: ImagesTransformResponse, contains transformed image. |
60 """ |
146 """ |
61 image = request.image().content() |
147 original_image = self._OpenImage(request.image().content()) |
|
148 |
|
149 new_image = self._ProcessTransforms(original_image, |
|
150 request.transform_list()) |
|
151 |
|
152 response_value = self._EncodeImage(new_image, request.output()) |
|
153 response.mutable_image().set_content(response_value) |
|
154 |
|
155 def _EncodeImage(self, image, output_encoding): |
|
156 """Encode the given image and return it in string form. |
|
157 |
|
158 Args: |
|
159 image: PIL Image object, image to encode. |
|
160 output_encoding: ImagesTransformRequest.OutputSettings object. |
|
161 |
|
162 Returns: |
|
163 str with encoded image information in given encoding format. |
|
164 """ |
|
165 image_string = StringIO.StringIO() |
|
166 |
|
167 image_encoding = "PNG" |
|
168 |
|
169 if (output_encoding.mime_type() == images_service_pb.OutputSettings.JPEG): |
|
170 image_encoding = "JPEG" |
|
171 |
|
172 image = image.convert("RGB") |
|
173 |
|
174 image.save(image_string, image_encoding) |
|
175 |
|
176 return image_string.getvalue() |
|
177 |
|
178 def _OpenImage(self, image): |
|
179 """Opens an image provided as a string. |
|
180 |
|
181 Args: |
|
182 image: image data to be opened |
|
183 |
|
184 Raises: |
|
185 apiproxy_errors.ApplicationError if the image cannot be opened or if it |
|
186 is an unsupported format. |
|
187 |
|
188 Returns: |
|
189 Image containing the image data passed in. |
|
190 """ |
62 if not image: |
191 if not image: |
63 raise apiproxy_errors.ApplicationError( |
192 raise apiproxy_errors.ApplicationError( |
64 images_service_pb.ImagesServiceError.NOT_IMAGE) |
193 images_service_pb.ImagesServiceError.NOT_IMAGE) |
65 |
194 |
66 image = StringIO.StringIO(image) |
195 image = StringIO.StringIO(image) |
67 try: |
196 try: |
68 original_image = Image.open(image) |
197 image = Image.open(image) |
69 except IOError: |
198 except IOError: |
70 raise apiproxy_errors.ApplicationError( |
199 raise apiproxy_errors.ApplicationError( |
71 images_service_pb.ImagesServiceError.BAD_IMAGE_DATA) |
200 images_service_pb.ImagesServiceError.BAD_IMAGE_DATA) |
72 |
201 |
73 img_format = original_image.format |
202 img_format = image.format |
74 if img_format not in ("BMP", "GIF", "ICO", "JPEG", "PNG", "TIFF"): |
203 if img_format not in ("BMP", "GIF", "ICO", "JPEG", "PNG", "TIFF"): |
75 raise apiproxy_errors.ApplicationError( |
204 raise apiproxy_errors.ApplicationError( |
76 images_service_pb.ImagesServiceError.NOT_IMAGE) |
205 images_service_pb.ImagesServiceError.NOT_IMAGE) |
77 |
206 return image |
78 new_image = self._ProcessTransforms(original_image, |
|
79 request.transform_list()) |
|
80 |
|
81 response_value = self._EncodeImage(new_image, request.output()) |
|
82 response.mutable_image().set_content(response_value) |
|
83 |
|
84 def _EncodeImage(self, image, output_encoding): |
|
85 """Encode the given image and return it in string form. |
|
86 |
|
87 Args: |
|
88 image: PIL Image object, image to encode. |
|
89 output_encoding: ImagesTransformRequest.OutputSettings object. |
|
90 |
|
91 Returns: |
|
92 str with encoded image information in given encoding format. |
|
93 """ |
|
94 image_string = StringIO.StringIO() |
|
95 |
|
96 image_encoding = "PNG" |
|
97 |
|
98 if (output_encoding.mime_type() == images_service_pb.OutputSettings.JPEG): |
|
99 image_encoding = "JPEG" |
|
100 |
|
101 image = image.convert("RGB") |
|
102 |
|
103 image.save(image_string, image_encoding) |
|
104 |
|
105 return image_string.getvalue() |
|
106 |
207 |
107 def _ValidateCropArg(self, arg): |
208 def _ValidateCropArg(self, arg): |
108 """Check an argument for the Crop transform. |
209 """Check an argument for the Crop transform. |
109 |
210 |
110 Args: |
211 Args: |