up-to-date submodule(rive-cpp)
[platform/core/uifw/rive-tizen.git] / submodule / skia / recorder / src / extractor.cpp
1 #include "extractor.hpp"
2
3 int valueOrDefault(int value, int default_value)
4 {
5         if (value == 0)
6         {
7                 return default_value;
8         }
9         return value;
10 }
11
12 void scale(int* value, int targetValue, int* otherValue)
13 {
14         if (*value != targetValue)
15         {
16                 *otherValue = *otherValue * targetValue / *value;
17                 *value = targetValue;
18         }
19 }
20
21 int RiveFrameExtractor::width() { return _width; };
22 int RiveFrameExtractor::height() { return _height; };
23 int RiveFrameExtractor::fps() { return animation->fps(); };
24 int RiveFrameExtractor::totalFrames()
25 {
26         int min_frames = _min_duration * fps();
27         int max_frames = _max_duration * fps();
28
29         int animationFrames = animation->duration();
30         int totalFrames = animation->duration();
31         // TODO: combine those two into one function
32         switch (animation->loop())
33         {
34                 case rive::Loop::pingPong:
35                         animationFrames *= 2;
36                 case rive::Loop::loop:
37                         // pingpong is like loop, just you gotta go back and forth, so 2x
38                         // duration
39                         if (min_frames != 0 && totalFrames < min_frames)
40                         {
41                                 totalFrames = std::ceil(min_frames / float(animationFrames)) *
42                                               animationFrames;
43                         }
44                         if (max_frames != 0 && totalFrames > max_frames &&
45                             animationFrames < max_frames)
46                         {
47
48                                 totalFrames = std::floor(max_frames / (animationFrames)) *
49                                               animationFrames;
50                         }
51                         break;
52                 default:
53                         break;
54         }
55         // no matter what shenanigans we had before, lets make sure we fall in line
56         // regardless.
57         if (min_frames != 0 && totalFrames < min_frames)
58         {
59                 totalFrames = min_frames;
60         }
61         if (max_frames != 0 && totalFrames > max_frames)
62         {
63                 totalFrames = max_frames;
64         }
65         return totalFrames;
66 };
67
68 RiveFrameExtractor::RiveFrameExtractor(const char* path,
69                                        const char* artboard_name,
70                                        const char* animation_name,
71                                        const char* watermark_name,
72                                        int width,
73                                        int height,
74                                        int small_extent_target,
75                                        int max_width,
76                                        int max_height,
77                                        int min_duration,
78                                        int max_duration)
79 {
80         _min_duration = min_duration;
81         _max_duration = max_duration;
82         riveFile = getRiveFile(path);
83         artboard = getArtboard(artboard_name);
84         animation = getAnimation(animation_name);
85         animation_instance = new rive::LinearAnimationInstance(animation);
86         watermarkImage = getWaterMark(watermark_name);
87         initializeDimensions(
88             width, height, small_extent_target, max_width, max_height);
89         rasterSurface = SkSurface::MakeRaster(SkImageInfo::Make(
90             _width, _height, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
91         rasterCanvas = rasterSurface->getCanvas();
92         ifps = 1.0 / animation->fps();
93 };
94
95 void RiveFrameExtractor::initializeDimensions(int width,
96                                               int height,
97                                               int small_extent_target,
98                                               int max_width,
99                                               int max_height)
100 {
101         // Take the width & height from user input, or from the provided artboard
102         _width = valueOrDefault(width, artboard->width());
103         _height = valueOrDefault(height, artboard->height());
104
105         // if we have a target value for whichever extent is smaller, lets scale to
106         // that.
107         if (small_extent_target != 0)
108         {
109                 if (_width < _height)
110                 {
111                         scale(&_width, small_extent_target, &_height);
112                 }
113                 else
114                 {
115                         scale(&_height, small_extent_target, &_width);
116                 }
117         }
118
119         // if we have a max height, lets scale down to that
120         if (max_height != 0 && max_height < _height)
121         {
122
123                 scale(&_height, max_height, &_width);
124         }
125
126         // if we have a max width, lets scale down to that
127         if (max_width != 0 && max_width < _width)
128         {
129                 scale(&_width, max_width, &_height);
130         }
131
132         // We're sticking with 2's right now. so you know it is what it is.
133         _width = nextMultipleOf(_width, 2);
134         _height = nextMultipleOf(_height, 2);
135 }
136
137 sk_sp<SkImage> RiveFrameExtractor::getWaterMark(const char* watermark_name)
138 {
139         // Init skia surfaces to render to.
140         sk_sp<SkImage> watermarkImage;
141         if (watermark_name != NULL && watermark_name[0] != '\0')
142         {
143
144                 if (!file_exists(watermark_name))
145                 {
146                         throw std::invalid_argument(string_format(
147                             "Cannot find file containing watermark at %s", watermark_name));
148                 }
149                 if (auto data = SkData::MakeFromFileName(watermark_name))
150                 {
151                         watermarkImage = SkImage::MakeFromEncoded(data);
152                 }
153         }
154         return watermarkImage;
155 }
156
157 rive::File* RiveFrameExtractor::getRiveFile(const char* path)
158 {
159         FILE* fp = fopen(path, "r");
160
161         if (fp == nullptr)
162         {
163                 throw std::invalid_argument(
164                     string_format("Failed to open file %s", path));
165         }
166         fseek(fp, 0, SEEK_END);
167         auto length = ftell(fp);
168         fseek(fp, 0, SEEK_SET);
169
170         // TODO: need to clean this up? how?!
171         uint8_t* bytes = new uint8_t[length];
172
173         if (fread(bytes, 1, length, fp) != length)
174         {
175                 throw std::invalid_argument(
176                     string_format("Failed to read file into bytes array %s", path));
177         }
178
179         auto reader = rive::BinaryReader(bytes, length);
180         rive::File* file = nullptr;
181         auto result = rive::File::import(reader, &file);
182         if (result != rive::ImportResult::success)
183         {
184                 throw std::invalid_argument(
185                     string_format("Failed to read bytes into Rive file %s", path));
186         }
187         return file;
188 }
189
190 rive::Artboard* RiveFrameExtractor::getArtboard(const char* artboard_name)
191 {
192         // Figure out which artboard to use.
193         rive::Artboard* artboard;
194
195         // QUESTION: better to keep this logic in the main? or pass the flag
196         // in here, so we can bool check if the flag is set or not? what
197         // happens if we try to target the artboard '' otherwise?
198         //
199         if (artboard_name != NULL && artboard_name[0] != '\0')
200         {
201                 if ((artboard = riveFile->artboard(artboard_name)) == nullptr)
202                 {
203                         throw std::invalid_argument(string_format(
204                             "File doesn't contain an artboard named %s.", artboard_name));
205                 }
206         }
207         else
208         {
209                 artboard = riveFile->artboard();
210                 if (artboard == nullptr)
211                 {
212                         throw std::invalid_argument(string_format(
213                             "File doesn't contain a default artboard.", artboard_name));
214                 }
215         }
216         return artboard;
217 }
218
219 rive::LinearAnimation*
220 RiveFrameExtractor::getAnimation(const char* animation_name)
221 {
222         // Figure out which animation to use.
223         rive::LinearAnimation* animation;
224         if (animation_name != NULL && animation_name[0] != '\0')
225         {
226                 if ((animation = artboard->animation(animation_name)) == nullptr)
227                 {
228
229                         fprintf(stderr,
230                                 "Artboard doesn't contain an animation named %s.\n",
231                                 animation_name);
232                 }
233         }
234         else
235         {
236                 animation = artboard->firstAnimation();
237                 if (animation == nullptr)
238                 {
239                         throw std::invalid_argument(
240                             string_format("Artboard doesn't contain a default animation."));
241                 }
242         }
243         return animation;
244 };
245
246 void RiveFrameExtractor::advanceFrame() { animation_instance->advance(ifps); }
247
248 sk_sp<SkImage> RiveFrameExtractor::getSnapshot()
249 {
250         // hmm "no deafault constructor exists bla bla... "
251         rive::SkiaRenderer renderer(rasterCanvas);
252
253         renderer.save();
254         renderer.align(rive::Fit::cover,
255                        rive::Alignment::center,
256                        rive::AABB(0, 0, width(), height()),
257                        artboard->bounds());
258         animation_instance->apply(artboard);
259         artboard->advance(0.0f);
260         artboard->draw(&renderer);
261         renderer.restore();
262         if (watermarkImage)
263         {
264                 SkPaint watermarkPaint;
265                 watermarkPaint.setBlendMode(SkBlendMode::kDifference);
266                 rasterCanvas->drawImage(watermarkImage,
267                                         width() - watermarkImage->width() - 50,
268                                         height() - watermarkImage->height() - 50,
269                                         &watermarkPaint);
270         }
271
272         // After drawing the frame, grab the raw image data.
273         sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
274         if (!img)
275         {
276                 throw std::invalid_argument(string_format("Cant make a snapshot."));
277         }
278         return img;
279 }
280
281 const void* RiveFrameExtractor::getPixelAddresses()
282 {
283         auto img = getSnapshot();
284         SkPixmap pixels;
285         if (!img->peekPixels(&pixels))
286         {
287                 throw std::invalid_argument(
288                     string_format("Cant peek pixels image frame from riv file."));
289         }
290
291         // Get the address to the first pixel (addr8 will assert in debug mode
292         // as Skia only wants you to use that with 8 bit surfaces).
293         return pixels.addr(0, 0);
294 };
295
296 sk_sp<SkData> RiveFrameExtractor::getSkData()
297 {
298         auto img = getSnapshot();
299         sk_sp<SkData> png(img->encodeToData());
300         if (!png)
301         {
302                 throw std::invalid_argument(
303                     string_format("Cant encode snapshot as png."));
304         }
305
306         // Get the address to the first pixel (addr8 will assert in debug mode
307         // as Skia only wants you to use that with 8 bit surfaces).
308         return png;
309 };