77#include < vector>
88#include < memory>
99#include < stdexcept>
10+ #include < string>
1011
1112#include " AggDevice.h"
1213#include " files.h"
1314#include " ragg.h"
1415
15- using WebPPicturePtr = std::unique_ptr<WebPPicture, void (*)(WebPPicture*)>;
16- using WebPDataPtr = std::unique_ptr<WebPData, void (*)(WebPData*)>;
1716using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>;
1817
19- inline WebPPicturePtr make_webp_picture () {
20- WebPPicture* pic = new WebPPicture;
21- if (!WebPPictureInit (pic)) {
22- delete pic;
23- throw std::runtime_error (" WebPPictureInit failed" );
24- }
25- return WebPPicturePtr (pic, WebPPictureFree);
26- }
27-
28- inline WebPDataPtr make_webp_data () {
29- WebPData* data = new WebPData;
30- return WebPDataPtr (data, WebPDataClear);
31- }
32-
3318inline FilePtr make_file (const char * filename, const char * mode) {
3419 FILE* f = unicode_fopen (filename, mode);
3520 if (!f) {
@@ -92,17 +77,22 @@ class AggDeviceWebPAnim : public AggDevice<PIXFMT> {
9277 demultiply<PIXFMT>(this ->pixf );
9378
9479 try {
95- auto pic = make_webp_picture ();
80+ WebPPicture pic;
81+ if (!WebPPictureInit (&pic)) {
82+ Rf_warning (" WebPPictureInit failed" );
83+ return false ;
84+ }
85+ auto pic_guard = std::unique_ptr<WebPPicture, decltype (&WebPPictureFree)>(&pic, WebPPictureFree);
9686
9787 WebPMemoryWriter wr;
9888 WebPMemoryWriterInit (&wr);
9989 auto cleanup = [](WebPMemoryWriter* w) { WebPMemoryWriterClear (w); };
10090 std::unique_ptr<WebPMemoryWriter, decltype (cleanup)> wr_guard (&wr, cleanup);
10191
102- pic-> width = this ->width ;
103- pic-> height = this ->height ;
104- pic-> writer = WebPMemoryWrite;
105- pic-> custom_ptr = ≀
92+ pic. width = this ->width ;
93+ pic. height = this ->height ;
94+ pic. writer = WebPMemoryWrite;
95+ pic. custom_ptr = ≀
10696
10797 WebPConfig config;
10898 if (!WebPConfigInit (&config)) {
@@ -116,13 +106,13 @@ class AggDeviceWebPAnim : public AggDevice<PIXFMT> {
116106 const auto importer = (PIXFMT::num_components == 3 ) ? WebPPictureImportRGB
117107 : WebPPictureImportRGBA;
118108
119- if (!importer (pic. get () , reinterpret_cast <const uint8_t *>(this ->buffer ), stride)) {
120- Rf_warning (" WebPPictureImport failed: %s" , webp_error_name (pic-> error_code ));
109+ if (!importer (& pic, reinterpret_cast <const uint8_t *>(this ->buffer ), stride)) {
110+ Rf_warning (" WebPPictureImport failed: %s" , webp_error_name (pic. error_code ));
121111 return false ;
122112 }
123113
124- if (!WebPEncode (&config, pic. get () )) {
125- Rf_warning (" WebPEncode failed: %s" , webp_error_name (pic-> error_code ));
114+ if (!WebPEncode (&config, & pic)) {
115+ Rf_warning (" WebPEncode failed: %s" , webp_error_name (pic. error_code ));
126116 return false ;
127117 }
128118
@@ -141,7 +131,13 @@ class AggDeviceWebPAnim : public AggDevice<PIXFMT> {
141131 }
142132
143133 void close () {
144- savePage ();
134+ if (!savePage ()) {
135+ throw std::runtime_error (" Failed to encode final WebP frame" );
136+ }
137+
138+ if (frames.empty ()) {
139+ throw std::runtime_error (" WebP animation has no frames to write" );
140+ }
145141
146142 for (size_t i = 0 ; i < frames.size (); ++i) {
147143 const auto & raw = frames[i];
@@ -157,29 +153,35 @@ class AggDeviceWebPAnim : public AggDevice<PIXFMT> {
157153 finfo.blend_method = WEBP_MUX_BLEND;
158154 WebPMuxError frame_err = WebPMuxPushFrame (mux.get (), &finfo, 1 );
159155 if (frame_err != WEBP_MUX_OK) {
160- Rf_error (" Failed to push WebP frame %zu/%zu: %s (%zu bytes)" ,
161- i + 1 , frames.size (), mux_error_name (frame_err), raw.size ());
156+ std::string msg = " Failed to push WebP frame " +
157+ std::to_string (i + 1 ) + " /" +
158+ std::to_string (frames.size ()) + " : " +
159+ mux_error_name (frame_err) + " (" +
160+ std::to_string (raw.size ()) + " bytes)" ;
161+ throw std::runtime_error (msg);
162162 }
163163 }
164164
165- try {
166- auto out = make_webp_data ();
167- WebPMuxError mux_err = WebPMuxAssemble (mux.get (), out.get ());
168- if (mux_err != WEBP_MUX_OK) {
169- Rf_error (" WebP mux assemble failed: %s (%zu frames)" , mux_error_name (mux_err), frames.size ());
170- }
165+ WebPData out;
166+ WebPDataInit (&out);
167+ auto out_guard = std::unique_ptr<WebPData, decltype (&WebPDataClear)>(&out, WebPDataClear);
171168
172- auto fd = make_file (this ->file .c_str (), " wb" );
173- if (fwrite (out->bytes , 1 , out->size , fd.get ()) != out->size ) {
174- Rf_error (" Failed to write WebP animation file" );
175- }
169+ WebPMuxError mux_err = WebPMuxAssemble (mux.get (), &out);
170+ if (mux_err != WEBP_MUX_OK) {
171+ std::string msg = " WebP mux assemble failed: " +
172+ std::string (mux_error_name (mux_err)) + " (" +
173+ std::to_string (frames.size ()) + " frames)" ;
174+ throw std::runtime_error (msg);
175+ }
176176
177- } catch (...) {
178- AggDevice<PIXFMT>:: close ();
179- throw ;
177+ auto fd = make_file ( this -> file . c_str (), " wb " );
178+ if ( fwrite (out. bytes , 1 , out. size , fd. get ()) != out. size ) {
179+ throw std::runtime_error ( " Failed to write WebP animation file " ) ;
180180 }
181181
182- AggDevice<PIXFMT>::close ();
182+ if (this ->pageno == 0 ) {
183+ this ->pageno = 1 ;
184+ }
183185 }
184186
185187 private:
0 commit comments