1 #include "extractor.hpp"
3 int valueOrDefault(int value, int default_value)
12 void scale(int* value, int targetValue, int* otherValue)
14 if (*value != targetValue)
16 *otherValue = *otherValue * targetValue / *value;
21 int RiveFrameExtractor::width() { return _width; };
22 int RiveFrameExtractor::height() { return _height; };
23 int RiveFrameExtractor::fps() { return animation->fps(); };
24 int RiveFrameExtractor::totalFrames()
26 int min_frames = _min_duration * fps();
27 int max_frames = _max_duration * fps();
29 int animationFrames = animation->duration();
30 int totalFrames = animation->duration();
31 // TODO: combine those two into one function
32 switch (animation->loop())
34 case rive::Loop::pingPong:
36 case rive::Loop::loop:
37 // pingpong is like loop, just you gotta go back and forth, so 2x
39 if (min_frames != 0 && totalFrames < min_frames)
41 totalFrames = std::ceil(min_frames / float(animationFrames)) *
44 if (max_frames != 0 && totalFrames > max_frames &&
45 animationFrames < max_frames)
48 totalFrames = std::floor(max_frames / (animationFrames)) *
55 // no matter what shenanigans we had before, lets make sure we fall in line
57 if (min_frames != 0 && totalFrames < min_frames)
59 totalFrames = min_frames;
61 if (max_frames != 0 && totalFrames > max_frames)
63 totalFrames = max_frames;
68 RiveFrameExtractor::RiveFrameExtractor(const char* path,
69 const char* artboard_name,
70 const char* animation_name,
71 const char* watermark_name,
74 int small_extent_target,
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);
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();
95 void RiveFrameExtractor::initializeDimensions(int width,
97 int small_extent_target,
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());
105 // if we have a target value for whichever extent is smaller, lets scale to
107 if (small_extent_target != 0)
109 if (_width < _height)
111 scale(&_width, small_extent_target, &_height);
115 scale(&_height, small_extent_target, &_width);
119 // if we have a max height, lets scale down to that
120 if (max_height != 0 && max_height < _height)
123 scale(&_height, max_height, &_width);
126 // if we have a max width, lets scale down to that
127 if (max_width != 0 && max_width < _width)
129 scale(&_width, max_width, &_height);
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);
137 sk_sp<SkImage> RiveFrameExtractor::getWaterMark(const char* watermark_name)
139 // Init skia surfaces to render to.
140 sk_sp<SkImage> watermarkImage;
141 if (watermark_name != NULL && watermark_name[0] != '\0')
144 if (!file_exists(watermark_name))
146 throw std::invalid_argument(string_format(
147 "Cannot find file containing watermark at %s", watermark_name));
149 if (auto data = SkData::MakeFromFileName(watermark_name))
151 watermarkImage = SkImage::MakeFromEncoded(data);
154 return watermarkImage;
157 rive::File* RiveFrameExtractor::getRiveFile(const char* path)
159 FILE* fp = fopen(path, "r");
163 throw std::invalid_argument(
164 string_format("Failed to open file %s", path));
166 fseek(fp, 0, SEEK_END);
167 auto length = ftell(fp);
168 fseek(fp, 0, SEEK_SET);
170 // TODO: need to clean this up? how?!
171 uint8_t* bytes = new uint8_t[length];
173 if (fread(bytes, 1, length, fp) != length)
175 throw std::invalid_argument(
176 string_format("Failed to read file into bytes array %s", path));
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)
184 throw std::invalid_argument(
185 string_format("Failed to read bytes into Rive file %s", path));
190 rive::Artboard* RiveFrameExtractor::getArtboard(const char* artboard_name)
192 // Figure out which artboard to use.
193 rive::Artboard* artboard;
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?
199 if (artboard_name != NULL && artboard_name[0] != '\0')
201 if ((artboard = riveFile->artboard(artboard_name)) == nullptr)
203 throw std::invalid_argument(string_format(
204 "File doesn't contain an artboard named %s.", artboard_name));
209 artboard = riveFile->artboard();
210 if (artboard == nullptr)
212 throw std::invalid_argument(string_format(
213 "File doesn't contain a default artboard.", artboard_name));
219 rive::LinearAnimation*
220 RiveFrameExtractor::getAnimation(const char* animation_name)
222 // Figure out which animation to use.
223 rive::LinearAnimation* animation;
224 if (animation_name != NULL && animation_name[0] != '\0')
226 if ((animation = artboard->animation(animation_name)) == nullptr)
230 "Artboard doesn't contain an animation named %s.\n",
236 animation = artboard->firstAnimation();
237 if (animation == nullptr)
239 throw std::invalid_argument(
240 string_format("Artboard doesn't contain a default animation."));
246 void RiveFrameExtractor::advanceFrame() { animation_instance->advance(ifps); }
248 sk_sp<SkImage> RiveFrameExtractor::getSnapshot()
250 // hmm "no deafault constructor exists bla bla... "
251 rive::SkiaRenderer renderer(rasterCanvas);
254 renderer.align(rive::Fit::cover,
255 rive::Alignment::center,
256 rive::AABB(0, 0, width(), height()),
258 animation_instance->apply(artboard);
259 artboard->advance(0.0f);
260 artboard->draw(&renderer);
264 SkPaint watermarkPaint;
265 watermarkPaint.setBlendMode(SkBlendMode::kDifference);
266 rasterCanvas->drawImage(watermarkImage,
267 width() - watermarkImage->width() - 50,
268 height() - watermarkImage->height() - 50,
272 // After drawing the frame, grab the raw image data.
273 sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
276 throw std::invalid_argument(string_format("Cant make a snapshot."));
281 const void* RiveFrameExtractor::getPixelAddresses()
283 auto img = getSnapshot();
285 if (!img->peekPixels(&pixels))
287 throw std::invalid_argument(
288 string_format("Cant peek pixels image frame from riv file."));
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);
296 sk_sp<SkData> RiveFrameExtractor::getSkData()
298 auto img = getSnapshot();
299 sk_sp<SkData> png(img->encodeToData());
302 throw std::invalid_argument(
303 string_format("Cant encode snapshot as png."));
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).