1 /*
2  * This file is part of openfx-arena <https://github.com/olear/openfx-arena>,
3  * Copyright (C) 2016 INRIA
4  *
5  * openfx-arena is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as published
7  * by the Free Software Foundation.
8  *
9  * openfx-arena is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with openfx-arena.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
16 */
17 
18 #include <iostream>
19 #include <stdint.h>
20 #include <zip.h>
21 #include <libxml/xmlmemory.h>
22 #include <libxml/parser.h>
23 #include <ofxNatron.h>
24 #include "GenericReader.h"
25 #include "GenericOCIO.h"
26 #include "ofxsMacros.h"
27 #include "ofxsImageEffect.h"
28 #include "ofxsMultiPlane.h"
29 #include "lodepng.h"
30 
31 #define kPluginName "OpenRaster"
32 #define kPluginGrouping "Image/Readers"
33 #define kPluginIdentifier "fr.inria.openfx.OpenRaster"
34 #define kPluginVersionMajor 2
35 #define kPluginVersionMinor 1
36 #define kPluginEvaluation 50
37 
38 #define kSupportsRGBA true
39 #define kSupportsRGB false
40 #define kSupportsXY false
41 #define kSupportsAlpha false
42 #define kSupportsTiles false
43 #define kIsMultiPlanar true
44 
45 using namespace OFX::IO;
46 
47 #ifdef OFX_IO_USING_OCIO
48 namespace OCIO = OCIO_NAMESPACE;
49 #endif
50 
51 OFXS_NAMESPACE_ANONYMOUS_ENTER
52 
53 static bool gHostIsNatron = false;
54 
55 class OpenRasterPlugin : public GenericReaderPlugin
56 {
57 public:
58     OpenRasterPlugin(OfxImageEffectHandle handle, const std::vector<std::string>& extensions);
59     virtual ~OpenRasterPlugin();
60     virtual void restoreStateFromParams() OVERRIDE FINAL;
61 private:
isVideoStream(const std::string &)62     virtual bool isVideoStream(const std::string& /*filename*/) OVERRIDE FINAL { return false; }
decode(const std::string & filename,OfxTime time,int view,bool isPlayback,const OfxRectI & renderWindow,float * pixelData,const OfxRectI & bounds,OFX::PixelComponentEnum pixelComponents,int pixelComponentCount,int rowBytes)63     virtual void decode(const std::string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds,
64                              OFX::PixelComponentEnum pixelComponents, int pixelComponentCount, int rowBytes) OVERRIDE FINAL
65     {
66         std::string rawComps;
67         switch (pixelComponents) {
68             case OFX::ePixelComponentAlpha:
69                 rawComps = kOfxImageComponentAlpha;
70                 break;
71             case OFX::ePixelComponentRGB:
72                 rawComps = kOfxImageComponentRGB;
73                 break;
74             case OFX::ePixelComponentRGBA:
75                 rawComps = kOfxImageComponentRGBA;
76                 break;
77             default:
78                 OFX::throwSuiteStatusException(kOfxStatFailed);
79                 return;
80         }
81         decodePlane(filename, time, view, isPlayback, renderWindow, pixelData, bounds, pixelComponents, pixelComponentCount, rawComps, rowBytes);
82     }
83     virtual void decodePlane(const std::string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds, OFX::PixelComponentEnum pixelComponents, int pixelComponentCount, const std::string& rawComponents, int rowBytes) OVERRIDE FINAL;
84     virtual OfxStatus getClipComponents(const OFX::ClipComponentsArguments& args, OFX::ClipComponentsSetter& clipComponents) OVERRIDE FINAL;
85     virtual bool getFrameBounds(const std::string& filename, OfxTime time, int view, OfxRectI *bounds, OfxRectI* format, double *par, std::string *error, int *tile_width, int *tile_height) OVERRIDE FINAL;
86     virtual bool guessParamsFromFilename(const std::string& filename, std::string *colorspace, OFX::PreMultiplicationEnum *filePremult, OFX::PixelComponentEnum *components, int *componentCount) OVERRIDE FINAL;
87     virtual void changedFilename(const OFX::InstanceChangedArgs &args) OVERRIDE FINAL;
88     std::string extractXML(std::string filename);
89     void getImageSize(int *width, int *height, std::string filename);
90     bool hasMergedImage(std::string filename);
91     void getLayersInfo(xmlNode *node, std::vector<std::vector<std::string> > *layers);
92     std::vector<std::vector<std::string> > imageLayers;
93 };
94 
OpenRasterPlugin(OfxImageEffectHandle handle,const std::vector<std::string> & extensions)95 OpenRasterPlugin::OpenRasterPlugin(OfxImageEffectHandle handle, const std::vector<std::string>& extensions)
96 : GenericReaderPlugin(handle, extensions, kSupportsRGBA, kSupportsRGB, kSupportsXY, kSupportsAlpha, kSupportsTiles,
97 #ifdef OFX_EXTENSIONS_NUKE
98 (OFX::getImageEffectHostDescription() && OFX::getImageEffectHostDescription()->isMultiPlanar) ? kIsMultiPlanar : false
99 #else
100 false
101 #endif
102 )
103 {
104 }
105 
~OpenRasterPlugin()106 OpenRasterPlugin::~OpenRasterPlugin()
107 {
108 }
109 
110 std::string
extractXML(std::string filename)111 OpenRasterPlugin::extractXML(std::string filename)
112 {
113     std::string output;
114     int err = 0;
115     zip *fileOpen = zip_open(filename.c_str(), 0, &err);
116     struct zip_stat xmlSt;
117     zip_stat_init(&xmlSt);
118     err=zip_stat(fileOpen,"stack.xml",0,&xmlSt);
119     if (err!=-1) {
120         char *xml = new char[xmlSt.size+1];
121         zip_file *xmlFile = zip_fopen(fileOpen,"stack.xml",0);
122         err=zip_fread(xmlFile,xml,xmlSt.size);
123         if (err!=-1) {
124             zip_fclose(xmlFile);
125             xml[xmlSt.size] = '\0';
126             output=xml;
127         }
128         delete[] xml;
129     }
130     zip_close(fileOpen);
131     return output;
132 }
133 
134 void
getImageSize(int * width,int * height,std::string filename)135 OpenRasterPlugin::getImageSize(int *width, int *height, std::string filename)
136 {
137     std::string xml = extractXML(filename);
138     if (!xml.empty()) {
139         int imgW = 0;
140         int imgH = 0;
141         xmlDocPtr doc;
142         doc = xmlParseDoc((const xmlChar*)xml.c_str());
143         xmlNode *root_element = NULL;
144         xmlNode *cur_node = NULL;
145         root_element = xmlDocGetRootElement(doc);
146         for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
147             if (cur_node->type == XML_ELEMENT_NODE) {
148                 if ((!xmlStrcmp(cur_node->name,(const xmlChar*)"image"))) {
149                     xmlChar *imgWchar;
150                     xmlChar *imgHchar;
151                     imgWchar = xmlGetProp(cur_node,(const xmlChar*)"w");
152                     imgHchar = xmlGetProp(cur_node,(const xmlChar*)"h");
153                     imgW = atoi((const char*)imgWchar);
154                     imgH = atoi((const char*)imgHchar);
155                     xmlFree(imgWchar);
156                     xmlFree(imgHchar);
157                     if (imgW>0 && imgH>0) {
158                         (*width)=imgW;
159                         (*height)=imgH;
160                     }
161                 }
162             }
163         }
164         xmlFreeDoc(doc);
165     }
166 }
167 
168 void
getLayersInfo(xmlNode * node,std::vector<std::vector<std::string>> * layers)169 OpenRasterPlugin::getLayersInfo(xmlNode *node, std::vector<std::vector<std::string> > *layers)
170 {
171     xmlNode *cur_node = NULL;
172     for (cur_node = node; cur_node; cur_node = cur_node->next) {
173         if (cur_node->type == XML_ELEMENT_NODE) {
174             if ((!xmlStrcmp(cur_node->name, (const xmlChar *)"layer"))) {
175                 std::vector<std::string> layerInfo;
176                 xmlChar *xmlLayerName;
177                 xmlChar *xmlOpacity;
178                 xmlChar *xmlVisibility;
179                 xmlChar *xmlComposite;
180                 xmlChar *xmlPng;
181                 xmlChar *xmlOffsetX;
182                 xmlChar *xmlOffsetY;
183                 xmlLayerName = xmlGetProp(cur_node, (const xmlChar *)"name");
184                 xmlOpacity = xmlGetProp(cur_node, (const xmlChar *)"opacity"); // not used
185                 xmlVisibility = xmlGetProp(cur_node, (const xmlChar *)"visibility"); // not used
186                 xmlComposite = xmlGetProp(cur_node, (const xmlChar *)"composite-op"); // not used
187                 xmlPng = xmlGetProp(cur_node, (const xmlChar *)"src");
188                 xmlOffsetX = xmlGetProp(cur_node, (const xmlChar *)"x"); // a bit pointless since all layers are cropped in gfx app on save
189                 xmlOffsetY = xmlGetProp(cur_node, (const xmlChar *)"y"); // a bit pointless since all layers are cropped in gfx app on save
190                 std::string layerName,layerOpacity,layerVisibility,layerComposite,layerFile,layerOffsetX,layerOffsetY;
191 
192                 if (xmlLayerName!=NULL)
193                     layerName=(reinterpret_cast<char*>(xmlLayerName));
194                 if (xmlOpacity!=NULL)
195                     layerOpacity=(reinterpret_cast<char*>(xmlOpacity));
196                 if (xmlVisibility!=NULL)
197                     layerVisibility=(reinterpret_cast<char*>(xmlVisibility));
198                 if (xmlComposite!=NULL)
199                     layerComposite=(reinterpret_cast<char*>(xmlComposite));
200                 if (xmlPng!=NULL)
201                     layerFile=(reinterpret_cast<char*>(xmlPng));
202                 if (xmlOffsetX!=NULL)
203                     layerOffsetX=(reinterpret_cast<char*>(xmlOffsetX));
204                 if (xmlOffsetY!=NULL)
205                     layerOffsetY=(reinterpret_cast<char*>(xmlOffsetY));
206 
207                 xmlFree(xmlLayerName);
208                 xmlFree(xmlOpacity);
209                 xmlFree(xmlVisibility);
210                 xmlFree(xmlComposite);
211                 xmlFree(xmlPng);
212                 xmlFree(xmlOffsetX);
213                 xmlFree(xmlOffsetY);
214 
215                 if (layerOpacity.empty())
216                     layerOpacity = "1";
217                 if (layerVisibility.empty())
218                     layerVisibility = "1";
219                 else {
220                     if (layerVisibility=="hidden")
221                         layerVisibility = "0";
222                     else
223                         layerVisibility = "1";
224                 }
225                 if (layerComposite.empty())
226                     layerComposite = "svg:src-over";
227                 if (layerOffsetX.empty())
228                     layerOffsetX = "0";
229                 if (layerOffsetY.empty())
230                     layerOffsetY = "0";
231                 if (!layerFile.empty() && !layerName.empty()) {
232                     layerInfo.push_back(layerName);
233                     layerInfo.push_back(layerFile);
234                     layerInfo.push_back(layerOpacity);
235                     layerInfo.push_back(layerVisibility);
236                     layerInfo.push_back(layerComposite);
237                     layerInfo.push_back(layerOffsetX);
238                     layerInfo.push_back(layerOffsetY);
239                     layers->push_back(layerInfo);
240                 }
241             }
242         }
243         getLayersInfo(cur_node->children,layers);
244     }
245 }
246 
247 bool
hasMergedImage(std::string filename)248 OpenRasterPlugin::hasMergedImage(std::string filename)
249 {
250     bool status = false;
251     if (!filename.empty()) {
252         int err = 0;
253         zip *layerOpen = zip_open(filename.c_str(),0,&err);
254         struct zip_stat layerSt;
255         zip_stat_init(&layerSt);
256         err=zip_stat(layerOpen,"mergedimage.png",0,&layerSt);
257         if (err!=-1) {
258             char *layerData = new char[layerSt.size];
259             zip_file *layerFile = zip_fopen(layerOpen,"mergedimage.png",0);
260             err=zip_fread(layerFile,layerData,layerSt.size);
261             if (err!=-1) {
262                 zip_fclose(layerFile);
263                 if ((layerData!=NULL)&&(layerSt.size>0)) {
264                     status = true;
265                 }
266             }
267             delete[] layerData;
268         }
269         zip_close(layerOpen);
270     }
271     return status;
272 }
273 
274 OfxStatus
getClipComponents(const OFX::ClipComponentsArguments & args,OFX::ClipComponentsSetter & clipComponents)275 OpenRasterPlugin::getClipComponents(const OFX::ClipComponentsArguments& args, OFX::ClipComponentsSetter& clipComponents)
276 {
277     assert(isMultiPlanar());
278     clipComponents.setPassThroughClip(NULL, args.time, args.view);
279     if (imageLayers.size()>0 && gHostIsNatron) {
280         for (int i = 0; i < (int)imageLayers.size(); i++) {
281             std::string layerName;
282             {
283                 std::ostringstream ss;
284                 if (!imageLayers[i][0].empty()) {
285                     ss << imageLayers[i][0];
286                 } else {
287                     ss << "Image Layer #" << i; // if layer name is empty
288                 }
289                 layerName = ss.str();
290             }
291             const char* components[4] = {"R","G","B", "A"};
292             OFX::MultiPlane::ImagePlaneDesc plane(layerName, layerName, "", components, 4);
293             clipComponents.addClipPlane(*_outputClip, OFX::MultiPlane::ImagePlaneDesc::mapPlaneToOFXPlaneString(plane));
294         }
295 
296         // Also add the color plane
297         clipComponents.addClipPlane(*_outputClip, OFX::MultiPlane::ImagePlaneDesc::mapPlaneToOFXPlaneString(OFX::MultiPlane::ImagePlaneDesc::getRGBAComponents()));
298     }
299     return kOfxStatOK;
300 }
301 
302 void
decodePlane(const std::string & filename,OfxTime,int,bool,const OfxRectI & renderWindow,float * pixelData,const OfxRectI &,OFX::PixelComponentEnum,int pixelComponentCount,const std::string & rawComponents,int)303 OpenRasterPlugin::decodePlane(const std::string& filename, OfxTime /*time*/, int /*view*/, bool /*isPlayback*/, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& /*bounds*/,
304                                  OFX::PixelComponentEnum /*pixelComponents*/, int pixelComponentCount, const std::string& rawComponents, int /*rowBytes*/)
305 {
306     if (filename.empty()) {
307         setPersistentMessage(OFX::Message::eMessageError, "", "No filename");
308         OFX::throwSuiteStatusException(kOfxStatErrFormat);
309     }
310 
311     if (imageLayers.size() == 0) {
312         setPersistentMessage(OFX::Message::eMessageError, "", "No layers");
313         OFX::throwSuiteStatusException(kOfxStatErrFormat);
314     }
315 
316     int layer = 0;
317     if (gHostIsNatron) {
318 
319         OFX::MultiPlane::ImagePlaneDesc plane, pairedPlane;
320         OFX::MultiPlane::ImagePlaneDesc::mapOFXComponentsTypeStringToPlanes(rawComponents, &plane, &pairedPlane);
321 
322         if (!plane.isColorPlane()) {
323             for (int i = 0; i < (int)imageLayers.size(); i++) {
324                 bool foundLayer = false;
325                 std::ostringstream nonameLayer;
326                 nonameLayer << "Image Layer #" << i; // if layer name is empty
327                 if (imageLayers[i][0]==plane.getPlaneLabel())
328                     foundLayer = true;
329                 if (nonameLayer.str()==plane.getPlaneLabel() && !foundLayer)
330                     foundLayer = true;
331                 if (foundLayer) {
332                     layer = i;
333                     break;
334                 }
335             }
336         }
337         else {
338             layer = 0;
339         }
340     }
341 
342     int width = 0;
343     int height = 0;
344     unsigned char* buffer = NULL;
345     int renderWidth= renderWindow.x2 - renderWindow.x1;
346     int renderHeight= renderWindow.y2 - renderWindow.y1;
347 
348     int err = 0;
349     zip *layerOpen = zip_open(filename.c_str(),0,&err);
350     struct zip_stat layerSt;
351     zip_stat_init(&layerSt);
352     err=zip_stat(layerOpen,imageLayers[layer][1].c_str(),0,&layerSt);
353     if (err!=-1) {
354         char *layerData = new char[layerSt.size];
355         zip_file *layerFile = zip_fopen(layerOpen,imageLayers[layer][1].c_str(),0);
356         err=zip_fread(layerFile,layerData,layerSt.size);
357         if (err!=-1) {
358             zip_fclose(layerFile);
359             if ((layerData!=NULL)&&(layerSt.size>0)) {
360                 lodepng_decode32(&buffer,(unsigned int*)&width,(unsigned int*)&height,(unsigned char *)layerData,(size_t)layerSt.size);
361             }
362         }
363         delete[] layerData;
364     }
365     zip_close(layerOpen);
366 
367     if (buffer==NULL || width!=renderWidth || height!=renderHeight) {
368         setPersistentMessage(OFX::Message::eMessageError, "", "Unable to read image");
369         OFX::throwSuiteStatusException(kOfxStatErrFormat);
370     }
371 
372     unsigned char* pixels = new unsigned char[width * height * pixelComponentCount];
373     for (int i = 0; i < width; ++i) {
374         for (int j = 0; j < height; ++j) {
375             for (int k = 0; k < pixelComponentCount; ++k)
376                 pixels[(i + j * width) * pixelComponentCount + k] = buffer[(i + (height - 1 - j) * width) * pixelComponentCount + k];
377         }
378     }
379 
380     int offset = 0;
381     for (int y = 0; y < height; y++) {
382         for (int x = 0; x < width; x++) {
383             pixelData[offset + 0] = pixels[offset + 0] * (1.f / 255);
384             pixelData[offset + 1] = pixels[offset + 1] * (1.f / 255);
385             pixelData[offset + 2] = pixels[offset + 2] * (1.f / 255);
386             pixelData[offset + 3] = pixels[offset + 3] * (1.f / 255);
387             offset += pixelComponentCount;
388         }
389     }
390 
391     buffer = NULL;
392     delete[] pixels;
393 }
394 
getFrameBounds(const std::string & filename,OfxTime,int,OfxRectI * bounds,OfxRectI * format,double * par,std::string *,int * tile_width,int * tile_height)395 bool OpenRasterPlugin::getFrameBounds(const std::string& filename,
396                               OfxTime /*time*/,
397                                       int /*view*/,
398                               OfxRectI *bounds,
399                                       OfxRectI* format,
400                               double *par,
401                               std::string* /*error*/,int *tile_width, int *tile_height)
402 {
403     int width = 0;
404     int height = 0;
405     getImageSize(&width,&height,filename);
406     if (width>0 && height>0) {
407         bounds->x1 = 0;
408         bounds->x2 = width;
409         bounds->y1 = 0;
410         bounds->y2 = height;
411         *format = *bounds;
412         *par = 1.0;
413     }
414     *tile_width = *tile_height = 0;
415     return true;
416 }
417 
guessParamsFromFilename(const std::string &,std::string * colorspace,OFX::PreMultiplicationEnum * filePremult,OFX::PixelComponentEnum * components,int * componentCount)418 bool OpenRasterPlugin::guessParamsFromFilename(const std::string& /*newFile*/,
419                                        std::string *colorspace,
420                                        OFX::PreMultiplicationEnum *filePremult,
421                                        OFX::PixelComponentEnum *components,
422                                        int *componentCount)
423 {
424     assert(colorspace && filePremult && components && componentCount);
425 # ifdef OFX_IO_USING_OCIO
426     *colorspace = "sRGB";
427 # endif
428     int startingTime = getStartingTime();
429     std::string filename;
430     OfxStatus st = getFilenameAtTime(startingTime, &filename);
431     if ( st != kOfxStatOK || filename.empty() ) {
432         return false;
433     }
434 
435     imageLayers.clear();
436     std::string xml = extractXML(filename);
437     if (!xml.empty()) {
438         xmlDocPtr doc;
439         doc = xmlParseDoc((const xmlChar *)xml.c_str());
440         xmlNode *root_element = NULL;
441         root_element = xmlDocGetRootElement(doc);
442         getLayersInfo(root_element,&imageLayers);
443         xmlFreeDoc(doc);
444         if (hasMergedImage(filename)) {
445             std::vector<std::string> layerInfo;
446             layerInfo.push_back(kFnOfxImagePlaneColour);
447             layerInfo.push_back("mergedimage.png");
448             imageLayers.push_back(layerInfo);
449         }
450         std::reverse(imageLayers.begin(),imageLayers.end());
451     }
452     if (imageLayers.empty()) {
453         setPersistentMessage(OFX::Message::eMessageError, "", "Empty and/or corrupt image");
454     }
455 
456     *components = OFX::ePixelComponentRGBA;
457     *filePremult = OFX::eImageUnPreMultiplied;
458 
459     return true;
460 }
461 
changedFilename(const OFX::InstanceChangedArgs & args)462 void OpenRasterPlugin::changedFilename(const OFX::InstanceChangedArgs &args)
463 {
464     GenericReaderPlugin::changedFilename(args);
465 
466     int startingTime = getStartingTime();
467     std::string filename;
468     OfxStatus st = getFilenameAtTime(startingTime, &filename);
469     if ( st != kOfxStatOK || filename.empty() ) {
470         setPersistentMessage(OFX::Message::eMessageError, "", "No filename");
471         OFX::throwSuiteStatusException(kOfxStatErrFormat);
472     }
473     imageLayers.clear();
474     std::string xml = extractXML(filename);
475     if (!xml.empty()) {
476         xmlDocPtr doc;
477         doc = xmlParseDoc((const xmlChar *)xml.c_str());
478         xmlNode *root_element = NULL;
479         root_element = xmlDocGetRootElement(doc);
480         getLayersInfo(root_element,&imageLayers);
481         xmlFreeDoc(doc);
482         if (hasMergedImage(filename)) {
483             std::vector<std::string> layerInfo;
484             layerInfo.push_back(kFnOfxImagePlaneColour);
485             layerInfo.push_back("mergedimage.png");
486             imageLayers.push_back(layerInfo);
487         }
488         std::reverse(imageLayers.begin(),imageLayers.end());
489     }
490 }
491 
restoreStateFromParams()492 void OpenRasterPlugin::restoreStateFromParams()
493 {
494     GenericReaderPlugin::restoreStateFromParams();
495 
496     int startingTime = getStartingTime();
497     std::string filename;
498     OfxStatus st = getFilenameAtTime(startingTime, &filename);
499     if ( st == kOfxStatOK || !filename.empty() ) {
500         imageLayers.clear();
501         std::string xml = extractXML(filename);
502         if (!xml.empty()) {
503             xmlDocPtr doc;
504             doc = xmlParseDoc((const xmlChar *)xml.c_str());
505             xmlNode *root_element = NULL;
506             root_element = xmlDocGetRootElement(doc);
507             getLayersInfo(root_element,&imageLayers);
508             xmlFreeDoc(doc);
509             if (hasMergedImage(filename)) {
510                 std::vector<std::string> layerInfo;
511                 layerInfo.push_back(kFnOfxImagePlaneColour);
512                 layerInfo.push_back("mergedimage.png");
513                 imageLayers.push_back(layerInfo);
514             }
515             std::reverse(imageLayers.begin(),imageLayers.end());
516         }
517     }
518 }
519 
520 using namespace OFX;
521 
522 mDeclareReaderPluginFactory(OpenRasterPluginFactory, {}, false);
523 
524 void
load()525 OpenRasterPluginFactory::load()
526 {
527     _extensions.clear();
528     _extensions.push_back("ora");
529 }
530 
531 /** @brief The basic describe function, passed a plugin descriptor */
describe(OFX::ImageEffectDescriptor & desc)532 void OpenRasterPluginFactory::describe(OFX::ImageEffectDescriptor &desc)
533 {
534     GenericReaderDescribe(desc, _extensions, kPluginEvaluation, kSupportsTiles, kIsMultiPlanar);
535     desc.setLabel(kPluginName);
536     desc.setPluginDescription("Read OpenRaster image format.");
537 }
538 
539 /** @brief The describe in context function, passed a plugin descriptor and a context */
describeInContext(OFX::ImageEffectDescriptor & desc,ContextEnum context)540 void OpenRasterPluginFactory::describeInContext(OFX::ImageEffectDescriptor &desc, ContextEnum context)
541 {
542     gHostIsNatron = (OFX::getImageEffectHostDescription()->isNatron);
543     PageParamDescriptor *page = GenericReaderDescribeInContextBegin(desc, context, isVideoStreamPlugin(), kSupportsRGBA, kSupportsRGB, kSupportsXY,kSupportsAlpha, kSupportsTiles, true);
544     GenericReaderDescribeInContextEnd(desc, context, page, "sRGB", "scene_linear");
545 }
546 
547 /** @brief The create instance function, the plugin must return an object derived from the \ref OFX::ImageEffect class */
createInstance(OfxImageEffectHandle handle,ContextEnum)548 ImageEffect* OpenRasterPluginFactory::createInstance(OfxImageEffectHandle handle,
549                                      ContextEnum /*context*/)
550 {
551     OpenRasterPlugin* ret =  new OpenRasterPlugin(handle, _extensions);
552     ret->restoreStateFromParams();
553     return ret;
554 }
555 
556 static OpenRasterPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
557 mRegisterPluginFactoryInstance(p)
558 
559 OFXS_NAMESPACE_ANONYMOUS_EXIT
560