2 * Copyright 2013 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.1 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <Elementary.h>
18 #include <Ecore_Evas.h>
24 #include <widget_service.h>
25 #include <widget_errno.h>
28 #include "widget_internal.h"
31 #define QUALITY_N_COMPRESS "quality=100 compress=1"
32 #define PUBLIC __attribute__((visibility("default")))
34 struct snapshot_info {
36 widget_flush_cb flush_cb;
39 Ecore_Timer *flush_timer;
45 static void post_render_cb(void *data, Evas *e, void *event_info);
46 static void pre_render_cb(void *data, Evas *e, void *event_info);
48 static inline Evas *create_virtual_canvas(int w, int h)
50 Ecore_Evas *internal_ee;
53 // Create virtual canvas
54 internal_ee = ecore_evas_buffer_new(w, h);
56 DbgPrint("Failed to create a new canvas buffer\n");
60 ecore_evas_alpha_set(internal_ee, EINA_TRUE);
61 ecore_evas_manual_render_set(internal_ee, EINA_FALSE);
63 // Get the "Evas" object from a virtual canvas
64 internal_e = ecore_evas_get(internal_ee);
66 ecore_evas_free(internal_ee);
67 DbgPrint("Faield to get Evas object\n");
74 static inline int flush_data_to_file(Evas *e, const char *data, const char *filename, int w, int h)
78 output = evas_object_image_add(e);
80 DbgPrint("Failed to create an image object\n");
81 return WIDGET_ERROR_FAULT;
84 evas_object_image_data_set(output, NULL);
85 evas_object_image_colorspace_set(output, EVAS_COLORSPACE_ARGB8888);
86 evas_object_image_alpha_set(output, EINA_TRUE);
87 evas_object_image_size_set(output, w, h);
88 evas_object_image_smooth_scale_set(output, EINA_TRUE);
89 evas_object_image_data_set(output, (void *)data);
90 evas_object_image_fill_set(output, 0, 0, w, h);
91 evas_object_image_data_update_add(output, 0, 0, w, h);
93 if (evas_object_image_save(output, filename, NULL, QUALITY_N_COMPRESS) == EINA_FALSE) {
94 evas_object_del(output);
95 DbgPrint("Faield to save a captured image (%s)\n", filename);
96 return WIDGET_ERROR_IO_ERROR;
99 evas_object_del(output);
101 if (access(filename, F_OK) != 0) {
102 DbgPrint("File %s is not found\n", filename);
103 return WIDGET_ERROR_IO_ERROR;
106 DbgPrint("Flush data to a file (%s)\n", filename);
107 return WIDGET_ERROR_NONE;
110 static inline int destroy_virtual_canvas(Evas *e)
114 ee = ecore_evas_ecore_evas_get(e);
116 DbgPrint("Failed to ecore evas object\n");
117 return WIDGET_ERROR_FAULT;
121 return WIDGET_ERROR_NONE;
124 static inline int flush_to_file(const void *canvas, const char *filename, int w, int h)
130 shot_e = create_virtual_canvas(w, h);
132 ErrPrint("Unable to create a new virtual window\n");
133 return WIDGET_ERROR_FAULT;
136 shot_ee = ecore_evas_ecore_evas_get(shot_e);
138 ErrPrint("Unable to get Ecore_Evas\n");
139 destroy_virtual_canvas(shot_e);
140 return WIDGET_ERROR_FAULT;
143 ecore_evas_manual_render_set(shot_ee, EINA_TRUE);
145 status = flush_data_to_file(shot_e, canvas, filename, w, h);
146 destroy_virtual_canvas(shot_e);
151 static Eina_Bool snapshot_cb(void *data)
153 Evas_Object *snapshot_win = data;
154 struct snapshot_info *info;
158 widget_flush_cb flush_cb;
159 int status = WIDGET_ERROR_NONE;
161 info = evas_object_data_get(snapshot_win, "snapshot,info");
163 ErrPrint("Invalid object\n");
164 return ECORE_CALLBACK_CANCEL;
167 if (info->flush_timer) {
168 info->flush_timer = NULL;
170 status = WIDGET_ERROR_CANCELED;
173 flush_cb = info->flush_cb;
174 info->flush_cb = NULL; /* To prevent call this from the delete callback */
176 e = evas_object_evas_get(snapshot_win);
178 ErrPrint("Invalid object\n");
180 flush_cb(snapshot_win, info->id, WIDGET_ERROR_FAULT, info->data);
182 return ECORE_CALLBACK_CANCEL;
185 ee = ecore_evas_ecore_evas_get(e);
187 ErrPrint("Invalid object (ee)\n");
189 flush_cb(snapshot_win, info->id, WIDGET_ERROR_FAULT, info->data);
191 return ECORE_CALLBACK_CANCEL;
194 canvas = (void*)ecore_evas_buffer_pixels_get(ee);
196 DbgPrint("Failed to get pixel canvas\n");
198 flush_cb(snapshot_win, info->id, WIDGET_ERROR_FAULT, info->data);
200 return ECORE_CALLBACK_CANCEL;
208 ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
210 DbgPrint("Flush size: %dx%d\n", w, h);
211 ret = flush_to_file(canvas, info->id, w, h);
212 if (status == WIDGET_ERROR_NONE) {
216 flush_cb(snapshot_win, info->id, status, info->data);
218 * Do not access info after this.
222 return ECORE_CALLBACK_CANCEL;
225 static void del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
227 struct snapshot_info *info;
229 info = evas_object_data_del(obj, "snapshot,info");
234 DbgPrint("Delete object (%s)\n", info->id);
236 evas_event_callback_del(e, EVAS_CALLBACK_RENDER_POST, post_render_cb);
237 evas_event_callback_del(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb);
239 if (info->flush_timer) {
240 ecore_timer_del(info->flush_timer);
241 info->flush_timer = NULL;
243 (void)snapshot_cb(obj);
244 DbgPrint("Flush is canceled\n");
249 * Render callback will be deleted.
255 static Eina_Bool direct_snapshot_cb(void *data)
263 widget_flush_cb flush_cb;
264 Evas_Object *snapshot_win = data;
265 struct snapshot_info *info;
267 info = evas_object_data_get(snapshot_win, "snapshot,info");
269 ErrPrint("Unable to get snapshot info\n");
270 return ECORE_CALLBACK_CANCEL;
273 info->flush_timer = NULL;
274 flush_cb = info->flush_cb;
275 info->flush_cb = NULL; /* To prevent call this from the delete callback */
277 e = evas_object_evas_get(snapshot_win);
279 ErrPrint("Invalid object, failed to get Evas\n");
281 flush_cb(snapshot_win, info->id, WIDGET_ERROR_FAULT, info->data);
283 return ECORE_CALLBACK_CANCEL;
286 ee = ecore_evas_ecore_evas_get(e);
288 ErrPrint("Unable to get Ecore_Evas object\n");
290 flush_cb(snapshot_win, info->id, WIDGET_ERROR_FAULT, info->data);
292 return ECORE_CALLBACK_CANCEL;
295 ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
296 ecore_evas_manual_render_set(ee, EINA_TRUE);
298 canvas = ecore_evas_buffer_pixels_get(ee);
300 ErrPrint("Failed to get canvas\n");
302 flush_cb(snapshot_win, info->id, WIDGET_ERROR_FAULT, info->data);
304 return ECORE_CALLBACK_CANCEL;
307 status = flush_to_file(canvas, info->id, w, h);
309 flush_cb(snapshot_win, info->id, status, info->data);
311 return ECORE_CALLBACK_CANCEL;
314 static void post_render_cb(void *data, Evas *e, void *event_info)
316 Evas_Object *snapshot_win = data;
317 struct snapshot_info *info;
319 info = evas_object_data_get(snapshot_win, "snapshot,info");
321 ErrPrint("snapshot info is not valid\n");
327 if (info->flush_timer) {
330 * This has not to be happens.
332 ErrPrint("Flush timer is not cleared\n");
333 ecore_timer_del(info->flush_timer);
334 info->flush_timer = NULL;
337 if (!info->flush_cb) {
338 DbgPrint("Flush request is not initiated yet\n");
344 * Even if tehre is no timer registered, we should capture the content
345 * from out of this callback.
346 * Or we can met unexpected problems.
347 * To avoid it, use the 0.0001f to get timer callback ASAP, not in this function.
349 DbgPrint("Fire the flush timer %lf (%d)\n", info->timeout, info->render_cnt);
350 info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
351 if (!info->flush_timer) {
352 widget_flush_cb flush_cb;
354 ErrPrint("Unalbe to add timer for getting the snapshot\n");
355 flush_cb = info->flush_cb;
356 info->flush_cb = NULL;
359 flush_cb(snapshot_win, info->id, WIDGET_ERROR_FAULT, info->data);
363 * Do not access info after from here.
368 static void pre_render_cb(void *data, Evas *e, void *event_info)
370 Evas_Object *snapshot_win = data;
371 struct snapshot_info *info;
373 info = evas_object_data_get(snapshot_win, "snapshot,info");
375 ErrPrint("snapshot info is not valid\n");
379 if (info->flush_timer) {
380 ecore_timer_del(info->flush_timer);
381 info->flush_timer = NULL;
382 DbgPrint("Clear the flush timer\n");
385 DbgPrint("Pre-render callback\n");
388 PUBLIC void *widget_snapshot_window_add(const char *id, int size_type)
390 struct snapshot_info *info;
391 Evas_Object *snapshot_win;
397 if (widget_service_get_size(size_type, &w, &h) != WIDGET_ERROR_NONE) {
398 ErrPrint("Invalid size\n");
402 info = malloc(sizeof(*info));
404 ErrPrint("Heap: %s\n", strerror(errno));
408 info->id = strdup(id);
410 ErrPrint("Heap: %s\n", strerror(errno));
415 info->flush_cb = NULL;
417 info->flush_timer = NULL;
418 info->render_cnt = 0;
420 e = create_virtual_canvas(w, h);
427 parent = evas_object_rectangle_add(e);
429 destroy_virtual_canvas(e);
435 snapshot_win = elm_win_add(parent, "widget,Snapshot", ELM_WIN_DYNAMIC_BOX);
436 evas_object_del(parent);
438 destroy_virtual_canvas(e);
444 DbgPrint("Create a new window %dx%d for %s\n", w, h, id);
445 evas_object_event_callback_add(snapshot_win, EVAS_CALLBACK_DEL, del_cb, NULL);
446 evas_object_resize(snapshot_win, w, h);
447 evas_object_show(snapshot_win);
449 evas_object_data_set(snapshot_win, "snapshot,info", info);
451 evas_event_callback_add(e, EVAS_CALLBACK_RENDER_POST, post_render_cb, snapshot_win);
452 evas_event_callback_add(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb, snapshot_win);
457 PUBLIC int widget_snapshot_window_flush(void *snapshot_win, double timeout, widget_flush_cb flush_cb, void *data)
459 struct snapshot_info *info;
461 if (!flush_cb || timeout < 0.0f) {
462 ErrPrint("Invalid argument (%p, %lf)\n", flush_cb, timeout);
463 return WIDGET_ERROR_INVALID_PARAMETER;
466 info = evas_object_data_get(snapshot_win, "snapshot,info");
468 ErrPrint("Invalid argument\n");
469 return WIDGET_ERROR_INVALID_PARAMETER;
472 info->timeout = timeout;
474 if (timeout == 0.0f) {
476 * This timer is just used for guarantees same behavious even if it flushes content directly,
477 * The callback should be called from next loop.
478 * or the developer will get confused
480 info->flush_timer = ecore_timer_add(0.0001f, direct_snapshot_cb, snapshot_win);
481 if (!info->flush_timer) {
482 return WIDGET_ERROR_FAULT;
484 } else if (info->render_cnt) {
486 * Try to watit pre-render callback.
487 * If there is rendered contents.
489 DbgPrint("Rendered %d times already\n", info->render_cnt);
490 info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
491 if (!info->flush_timer) {
492 return WIDGET_ERROR_FAULT;
496 info->flush_cb = flush_cb;
499 return WIDGET_ERROR_NONE;