PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/Contrib/Photoshop/src/main/EXRFormatPlugin.cpp

https://github.com/meshula/openexr
C++ | 692 lines | 353 code | 211 blank | 128 comment | 51 complexity | 2911fc9a0f3b3fe08602e35b01e929ca MD5 | raw file
Possible License(s): CC-BY-SA-3.0, BSD-3-Clause
  1. // ===========================================================================
  2. // EXRFormatPlugin.cp Part of OpenEXR
  3. // ===========================================================================
  4. #if MSWindows
  5. #pragma warning (disable: 161)
  6. #endif
  7. #include "EXRFormatPlugin.h"
  8. #include "EXRResample.h"
  9. #include "EXRImportDialog.h"
  10. #include "EXRExportDialog.h"
  11. #include "RefNumIO.h"
  12. #include "PIFormat.h"
  13. #include "PSAutoBuffer.h"
  14. #include "ImfFrameBuffer.h"
  15. #include "ImfRgbaFile.h"
  16. #include "ImathBox.h"
  17. #include "ImfArray.h"
  18. #include "ImfIO.h"
  19. #include "ImathFun.h"
  20. #include "PITypes.h"
  21. #include "ADMBasic.h"
  22. #include <cassert>
  23. //-------------------------------------------------------------------------------
  24. // ConfigureLimits
  25. //-------------------------------------------------------------------------------
  26. static void ConfigureLimits (const FormatRecord* inFormatRec)
  27. {
  28. // set gExr_MaxPixelValue and gExr_MaxPixelDepth here
  29. if (inFormatRec != NULL)
  30. {
  31. if (inFormatRec->maxValue >= 0xFFFF)
  32. {
  33. // host supports 16 bit pixels
  34. gExr_MaxPixelDepth = 16;
  35. gExr_MaxPixelValue = 0x8000;
  36. }
  37. else
  38. {
  39. // host only supports 8 bit pixels.
  40. gExr_MaxPixelDepth = 8;
  41. gExr_MaxPixelValue = 0x00FF;
  42. }
  43. }
  44. }
  45. #pragma mark-
  46. //-------------------------------------------------------------------------------
  47. // EXRFormatPlugin
  48. //-------------------------------------------------------------------------------
  49. EXRFormatPlugin::EXRFormatPlugin ()
  50. {
  51. // nothing to do
  52. }
  53. //-------------------------------------------------------------------------------
  54. // ~EXRFormatPlugin
  55. //-------------------------------------------------------------------------------
  56. EXRFormatPlugin::~EXRFormatPlugin ()
  57. {
  58. // nothing to do
  59. }
  60. #pragma mark-
  61. //-------------------------------------------------------------------------------
  62. // GlobalsSize
  63. //-------------------------------------------------------------------------------
  64. int EXRFormatPlugin::GlobalsSize ()
  65. {
  66. return sizeof (EXRFormatGlobals);
  67. }
  68. //-------------------------------------------------------------------------------
  69. // InitGlobals
  70. //-------------------------------------------------------------------------------
  71. void EXRFormatPlugin::InitGlobals ()
  72. {
  73. Globals()->Reset();
  74. }
  75. #pragma mark-
  76. //-------------------------------------------------------------------------------
  77. // DoAbout
  78. //-------------------------------------------------------------------------------
  79. void EXRFormatPlugin::DoAbout (AboutRecord* inAboutRec)
  80. {
  81. if (inAboutRec != NULL && inAboutRec->sSPBasic != NULL)
  82. {
  83. ADMBasicSuite6* basicSuite = NULL;
  84. inAboutRec->sSPBasic->AcquireSuite (kADMBasicSuite, kADMBasicSuiteVersion6, (void**) &basicSuite);
  85. if (basicSuite != NULL)
  86. {
  87. basicSuite->MessageAlert ("OpenEXR Format v1.1.1\n\n"
  88. "Format by Florian Kainz, Rod Bogart, Josh Pines, and Drew Hess\n"
  89. "Plug-in by Paul Schneider\n"
  90. "www.openexr.com");
  91. inAboutRec->sSPBasic->ReleaseSuite (kADMBasicSuite, kADMBasicSuiteVersion6);
  92. basicSuite = NULL;
  93. }
  94. }
  95. }
  96. #pragma mark-
  97. //-------------------------------------------------------------------------------
  98. // DoReadStart
  99. //-------------------------------------------------------------------------------
  100. void EXRFormatPlugin::DoReadStart ()
  101. {
  102. using namespace Imf;
  103. using namespace Imath;
  104. // construct input file from refnum
  105. assert( Globals()->inputStream == NULL );
  106. assert( Globals()->inputFile == NULL );
  107. Globals()->inputStream = new RefNumIFStream (mFormatRec->dataFork, "EXR File");
  108. Globals()->inputFile = new RgbaInputFile (* (Globals()->inputStream));
  109. // get dimension info
  110. const Box2i& dw = Globals()->inputFile->dataWindow();
  111. int w = dw.max.x - dw.min.x + 1;
  112. int h = dw.max.y - dw.min.y + 1;
  113. int dx = dw.min.x;
  114. int dy = dw.min.y;
  115. // get resampling configuration
  116. // pop up dialog, ask user for config
  117. // don't display dialog if running in a host other than Photoshop,
  118. // for partial After Effects compatibility
  119. if (mFormatRec->hostSig == '8BIM' || mFormatRec->hostSig == 'MIB8')
  120. {
  121. if (!DoImportPreviewDlog())
  122. {
  123. // user hit cancel. clean up (some hosts like AE won't
  124. // call us with the ReadFinish selector in this case)
  125. // and return a user canceled error to the host.
  126. DoReadFinish();
  127. *mResult = userCanceledErr;
  128. return;
  129. }
  130. }
  131. // we need this here, because the table is initialized to
  132. // 8-bit (preview) mode.
  133. ResetHalfToIntTable (Globals());
  134. // return image info to host
  135. // always do interleaved RGB or RGBA for now
  136. // if image is RGB, don't add an alpha channel
  137. // if image is single channel, add all four channels
  138. // so that we don't have to switch to grayscale mode
  139. mFormatRec->imageSize.v = h;
  140. mFormatRec->imageSize.h = w;
  141. mFormatRec->planes = (Globals()->inputFile->channels() == WRITE_RGB) ? 3 : 4;
  142. mFormatRec->depth = (Globals()->bpc == 8) ? 8 : 16;
  143. mFormatRec->imageMode = (mFormatRec->depth > 8) ? plugInModeRGB48 : plugInModeRGBColor;
  144. mFormatRec->maxValue = Globals()->MaxPixelValue();
  145. }
  146. //-------------------------------------------------------------------------------
  147. // DoReadContinue
  148. //-------------------------------------------------------------------------------
  149. void EXRFormatPlugin::DoReadContinue ()
  150. {
  151. using namespace Imf;
  152. using namespace Imath;
  153. int rowBytes;
  154. int done, total;
  155. bool haveAlpha;
  156. bool premult;
  157. // sanity check
  158. if (Globals()->inputFile == NULL)
  159. {
  160. *mResult = formatCannotRead;
  161. return;
  162. }
  163. // get channel info
  164. haveAlpha = !(Globals()->inputFile->channels() == WRITE_RGB);
  165. premult = Globals()->premult;
  166. // get dimension info
  167. const Box2i& dw = Globals()->inputFile->dataWindow();
  168. int w = dw.max.x - dw.min.x + 1;
  169. int h = dw.max.y - dw.min.y + 1;
  170. int dx = dw.min.x;
  171. int dy = dw.min.y;
  172. // prepare for progress reporting
  173. done = 0;
  174. total = h;
  175. // get rowbytes, and add alignment padding
  176. rowBytes = (mFormatRec->imageSize.h * mFormatRec->planes * (mFormatRec->depth / 8));
  177. rowBytes = (rowBytes * mFormatRec->depth + 7) >> 3;
  178. // create a half buffer for reading into
  179. // buffer is big enough for one scanline
  180. Array2D<Rgba> p2 (1, w);
  181. // create an integer buffer for returning pixels to the host
  182. // buffer is big enough for one scanline
  183. PSAutoBuffer intBuffer (rowBytes, mFormatRec->bufferProcs);
  184. mFormatRec->data = intBuffer.Lock();
  185. unsigned char* data8 = (unsigned char*) mFormatRec->data;
  186. unsigned short* data16 = (unsigned short*) mFormatRec->data;
  187. // Set up to start returning chunks of data.
  188. // use RGBA interleaved format
  189. mFormatRec->colBytes = mFormatRec->planes * (mFormatRec->depth / 8);
  190. mFormatRec->rowBytes = rowBytes;
  191. mFormatRec->planeBytes = mFormatRec->depth / 8;
  192. mFormatRec->loPlane = 0;
  193. mFormatRec->hiPlane = 3;
  194. mFormatRec->theRect.left = 0;
  195. mFormatRec->theRect.right = mFormatRec->imageSize.h;
  196. // read one scanline at a time
  197. for (int scanline = dw.min.y; scanline <= dw.max.y && *mResult == noErr; ++scanline)
  198. {
  199. // read scanline
  200. // need to offset into array so that scanline
  201. // starts at the front of the array
  202. Globals()->inputFile->setFrameBuffer (&p2[-scanline][-dx], 1, w);
  203. Globals()->inputFile->readPixels (scanline);
  204. // unmult scanline if necessary
  205. if (premult)
  206. {
  207. for (int x = 0; x < w; ++x)
  208. {
  209. // we're going to throw away any alpha data > 1, so
  210. // clamp it to that range before using it for unmulting
  211. float a = p2[0][x].a;
  212. a = Imath::clamp (a, 0.f, 1.f);
  213. if (a != 0)
  214. {
  215. p2[0][x].r /= a;
  216. p2[0][x].g /= a;
  217. p2[0][x].b /= a;
  218. }
  219. }
  220. }
  221. // convert scanline
  222. int i = 0;
  223. if (mFormatRec->depth > 8)
  224. {
  225. for (int x = 0; x < w; ++x)
  226. {
  227. data16[i] = HalfToInt (p2[0][x].r, 0); i++;
  228. data16[i] = HalfToInt (p2[0][x].g, 1); i++;
  229. data16[i] = HalfToInt (p2[0][x].b, 2); i++;
  230. if (haveAlpha)
  231. {
  232. data16[i] = HalfToInt (p2[0][x].a, 3);
  233. i++;
  234. }
  235. }
  236. }
  237. else
  238. {
  239. for (int x = 0; x < w; ++x)
  240. {
  241. data8[i] = HalfToInt (p2[0][x].r, 0); i++;
  242. data8[i] = HalfToInt (p2[0][x].g, 1); i++;
  243. data8[i] = HalfToInt (p2[0][x].b, 2); i++;
  244. if (haveAlpha)
  245. {
  246. data8[i] = HalfToInt (p2[0][x].a, 3);
  247. i++;
  248. }
  249. }
  250. }
  251. // pass scanline back to host
  252. // offset data window to origin, because pshop doesn't have "data window" concept
  253. mFormatRec->theRect.top = scanline - dw.min.y;
  254. mFormatRec->theRect.bottom = mFormatRec->theRect.top + 1;
  255. *mResult = mFormatRec->advanceState();
  256. // report progress
  257. mFormatRec->progressProc (++done, total);
  258. }
  259. // we are done
  260. mFormatRec->data = NULL;
  261. }
  262. //-------------------------------------------------------------------------------
  263. // DoReadFinish
  264. //-------------------------------------------------------------------------------
  265. void EXRFormatPlugin::DoReadFinish ()
  266. {
  267. // clean up Globals()
  268. delete Globals()->inputFile;
  269. Globals()->inputFile = NULL;
  270. delete Globals()->inputStream;
  271. Globals()->inputStream = NULL;
  272. }
  273. #pragma mark-
  274. //-------------------------------------------------------------------------------
  275. // DoOptionsStart
  276. //-------------------------------------------------------------------------------
  277. void EXRFormatPlugin::DoOptionsStart ()
  278. {
  279. // show options dialog
  280. if (DoExportSettingsDlog ())
  281. {
  282. // user configured options, so set revert info up so that it reflects
  283. // the new options. Commotion, in particular, uses this so it doesn't
  284. // bring the options dialog up when saving each frame of a sequence.
  285. if (mFormatRec->revertInfo == NULL)
  286. {
  287. mFormatRec->revertInfo = mFormatRec->handleProcs->newProc (GlobalsSize());
  288. }
  289. if (mFormatRec->revertInfo != NULL)
  290. {
  291. char* ptr = mFormatRec->handleProcs->lockProc (mFormatRec->revertInfo, false);
  292. memcpy (ptr, Globals(), GlobalsSize());
  293. mFormatRec->handleProcs->unlockProc (mFormatRec->revertInfo);
  294. }
  295. }
  296. else
  297. {
  298. // user canceled out of options dialog
  299. *mResult = userCanceledErr;
  300. }
  301. }
  302. #pragma mark-
  303. //-------------------------------------------------------------------------------
  304. // DoEstimateStart
  305. //-------------------------------------------------------------------------------
  306. void EXRFormatPlugin::DoEstimateStart ()
  307. {
  308. // provide an estimate as to how much disk space
  309. // we need to write the file. If we don't set a
  310. // non-zero size, Photoshop won't open the file!
  311. // Thanks to Chris Cox @ Adobe for this fix.
  312. int32 dataBytes;
  313. // minimum file size estimate is just the header
  314. mFormatRec->minDataBytes = 100;
  315. // estimate maximum file size if it were uncompressed
  316. // header plus 4 channels, 2 bytes per channel
  317. dataBytes = 100 +
  318. 4 * 2 * (int32) mFormatRec->imageSize.h * mFormatRec->imageSize.v;
  319. mFormatRec->maxDataBytes = dataBytes;
  320. // tell the host not to call us with DoEstimateContinue
  321. mFormatRec->data = NULL;
  322. }
  323. #pragma mark-
  324. //-------------------------------------------------------------------------------
  325. // DoWriteStart
  326. //-------------------------------------------------------------------------------
  327. void EXRFormatPlugin::DoWriteStart ()
  328. {
  329. using namespace Imf;
  330. using namespace Imath;
  331. int done, total;
  332. unsigned char* pix8;
  333. unsigned short* pix16;
  334. // set globals to reflect the pixels we're recieving, and
  335. // rebuild the LUT to convert integer pixels
  336. // to floating point pixels
  337. Globals()->bpc = mFormatRec->depth;
  338. ResetIntToHalfTable (Globals());
  339. // construct output file from file spec
  340. Header header (mFormatRec->imageSize.h,
  341. mFormatRec->imageSize.v,
  342. 1,
  343. Imath::V2f (0, 0),
  344. 1,
  345. Imf::INCREASING_Y,
  346. Globals()->outputCompression);
  347. RefNumOFStream stream (mFormatRec->dataFork, "EXR File");
  348. RgbaOutputFile out (stream, header, (mFormatRec->planes == 3) ? WRITE_RGB : WRITE_RGBA);
  349. // tell the host what format we want to recieve pixels in
  350. // interleaved RGBA format is always popular
  351. // note that we don't align the rowbytes in this case
  352. mFormatRec->imageMode = (mFormatRec->depth > 8) ? plugInModeRGB48 : plugInModeRGBColor;
  353. mFormatRec->loPlane = 0;
  354. mFormatRec->hiPlane = mFormatRec->planes - 1;
  355. mFormatRec->planeBytes = mFormatRec->depth / 8;
  356. mFormatRec->colBytes = mFormatRec->planeBytes * mFormatRec->planes;
  357. mFormatRec->rowBytes = mFormatRec->colBytes * mFormatRec->imageSize.h;
  358. mFormatRec->theRect.left = 0;
  359. mFormatRec->theRect.right = mFormatRec->imageSize.h;
  360. // set up progress
  361. done = 0;
  362. total = mFormatRec->imageSize.v;
  363. // create buffers for one scanline
  364. PSAutoBuffer intBuffer (mFormatRec->rowBytes, mFormatRec->bufferProcs);
  365. Array2D<Rgba> p2 (1, mFormatRec->imageSize.h);
  366. // tell host where our buffer is
  367. mFormatRec->data = intBuffer.Lock();
  368. // convert one scanline at a time
  369. for (int y = 0; y < mFormatRec->imageSize.v; ++y)
  370. {
  371. // get one scanline from host
  372. mFormatRec->theRect.top = y;
  373. mFormatRec->theRect.bottom = y+1;
  374. mFormatRec->advanceState();
  375. // convert scanline
  376. if (mFormatRec->depth > 8)
  377. {
  378. pix16 = (unsigned short*) (mFormatRec->data);
  379. for (int x = 0; x < mFormatRec->imageSize.h; ++x)
  380. {
  381. p2[0][x].r = IntToHalf (*pix16++, 0);
  382. p2[0][x].g = IntToHalf (*pix16++, 1);
  383. p2[0][x].b = IntToHalf (*pix16++, 2);
  384. p2[0][x].a = (mFormatRec->planes > 3) ? IntToHalf (*pix16++, 3) : half(1.0);
  385. }
  386. }
  387. else
  388. {
  389. pix8 = (unsigned char*) (mFormatRec->data);
  390. for (int x = 0; x < mFormatRec->imageSize.h; ++x)
  391. {
  392. p2[0][x].r = IntToHalf (*pix8++, 0);
  393. p2[0][x].g = IntToHalf (*pix8++, 1);
  394. p2[0][x].b = IntToHalf (*pix8++, 2);
  395. p2[0][x].a = (mFormatRec->planes > 3) ? IntToHalf (*pix8++, 3) : half(1.0);
  396. }
  397. }
  398. // premult if necessary
  399. if (Globals()->premult)
  400. {
  401. for (int x = 0; x < mFormatRec->imageSize.h; ++x)
  402. {
  403. half a = p2[0][x].a;
  404. p2[0][x].r *= a;
  405. p2[0][x].g *= a;
  406. p2[0][x].b *= a;
  407. }
  408. }
  409. // write scanline
  410. out.setFrameBuffer (&p2[-y][0], 1, mFormatRec->imageSize.h);
  411. out.writePixels (1);
  412. // report progress
  413. mFormatRec->progressProc (++done, total);
  414. }
  415. // we are done
  416. mFormatRec->data = NULL;
  417. }
  418. #pragma mark-
  419. //-------------------------------------------------------------------------------
  420. // DoImportPreviewDlog
  421. //-------------------------------------------------------------------------------
  422. bool EXRFormatPlugin::DoImportPreviewDlog ()
  423. {
  424. return EXRImportDialog (Globals(), mFormatRec->sSPBasic, mFormatRec->plugInRef);
  425. }
  426. //-------------------------------------------------------------------------------
  427. // DoExportSettingsDlog
  428. //-------------------------------------------------------------------------------
  429. bool EXRFormatPlugin::DoExportSettingsDlog ()
  430. {
  431. return EXRExportDialog (Globals(), mFormatRec->sSPBasic, mFormatRec->plugInRef);
  432. }
  433. #pragma mark-
  434. //-------------------------------------------------------------------------------
  435. // Main entry point
  436. //-------------------------------------------------------------------------------
  437. DLLExport MACPASCAL
  438. void PluginMain
  439. (
  440. const short inSelector,
  441. FormatRecord* inFormatRecord,
  442. long* inData,
  443. short* outResult
  444. )
  445. {
  446. // configure resampling based on host's capabilities
  447. if (inSelector != formatSelectorAbout)
  448. {
  449. ConfigureLimits (inFormatRecord);
  450. }
  451. // create and run the plug-in
  452. try
  453. {
  454. EXRFormatPlugin plugin;
  455. plugin.Run (inSelector, inFormatRecord, inData, outResult);
  456. }
  457. // catch out-of-memory exception
  458. catch (const std::bad_alloc&)
  459. {
  460. *outResult = memFullErr;
  461. }
  462. // catch an exception that provides an error string
  463. catch (const std::exception& ex)
  464. {
  465. if (inSelector == formatSelectorAbout || ex.what() == NULL)
  466. {
  467. *outResult = formatCannotRead;
  468. }
  469. else
  470. {
  471. memcpy (& (*inFormatRecord->errorString)[1], ex.what(), strlen (ex.what()));
  472. (*inFormatRecord->errorString)[0] = strlen (ex.what());
  473. *outResult = errReportString;
  474. }
  475. }
  476. // catch any other exception (we don't want to throw back into the host)
  477. catch (...)
  478. {
  479. *outResult = formatCannotRead;
  480. }
  481. }