PageRenderTime 46ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/ext/imagick/ext_imagick.cpp

https://github.com/tstarling/hiphop-php
C++ | 359 lines | 281 code | 47 blank | 31 comment | 60 complexity | 615bdb03001c526048144fca35c9bcb9 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
  6. | Copyright (c) 1997-2010 The PHP Group |
  7. +----------------------------------------------------------------------+
  8. | This source file is subject to version 3.01 of the PHP license, |
  9. | that is bundled with this package in the file LICENSE, and is |
  10. | available through the world-wide-web at the following url: |
  11. | http://www.php.net/license/3_01.txt |
  12. | If you did not receive a copy of the PHP license and are unable to |
  13. | obtain it through the world-wide-web, please send a note to |
  14. | license@php.net so we can mail you a copy immediately. |
  15. +----------------------------------------------------------------------+
  16. */
  17. #include "hphp/runtime/ext/imagick/ext_imagick.h"
  18. #include <vector>
  19. #include "hphp/runtime/ext/ext_file.h"
  20. using std::pair;
  21. using std::string;
  22. using std::vector;
  23. namespace HPHP {
  24. //////////////////////////////////////////////////////////////////////////////
  25. // PHP Exceptions and Classes
  26. HPHP::Class* ImagickException::cls = nullptr;
  27. HPHP::Class* ImagickDrawException::cls = nullptr;
  28. HPHP::Class* ImagickPixelException::cls = nullptr;
  29. HPHP::Class* ImagickPixelIteratorException::cls = nullptr;
  30. HPHP::Class* Imagick::cls = nullptr;
  31. HPHP::Class* ImagickDraw::cls = nullptr;
  32. HPHP::Class* ImagickPixel::cls = nullptr;
  33. HPHP::Class* ImagickPixelIterator::cls = nullptr;
  34. //////////////////////////////////////////////////////////////////////////////
  35. // IO
  36. bool isMagickPseudoFormat(const String& path, char mode /* = '*' */) {
  37. static const vector<pair<string, string>> pseudoFormats = {
  38. // Pseudo-image Formats
  39. {"CANVAS:", "R"},
  40. {"CAPTION:", "R"},
  41. {"CLIP:", "RW"},
  42. {"CLIPBOARD:", "RW"},
  43. {"FRACTAL:", "R"},
  44. {"GRADIENT:", "R"},
  45. {"HALD:", "R"},
  46. {"HISTOGRAM:", "W"},
  47. {"LABEL:", "R"},
  48. {"MAP:", "RW"},
  49. {"MASK:", "RW"},
  50. {"MATTE:", "W"},
  51. {"NULL:", "RW"},
  52. {"PANGO:", "R"},
  53. {"PLASMA:", "R"},
  54. {"PREVIEW:", "W"},
  55. {"PRINT:", "W"},
  56. {"SCAN:", "R"},
  57. {"RADIAL_GRADIENT:", "R"},
  58. {"SCANX:", "R"},
  59. {"STEGANO:", "R"},
  60. {"TILE:", "R"},
  61. {"UNIQUE:", "W"},
  62. {"VID:", "RW"},
  63. {"WIN:", "RW"},
  64. {"X:", "RW"},
  65. {"XC:", "R"},
  66. // Built-in Images
  67. {"MAGICK:", "R"},
  68. {"GRANITE:", "R"},
  69. {"LOGO:", "R"},
  70. {"NETSCAPE:", "R"},
  71. {"ROSE:", "R"},
  72. // Built-in Patterns
  73. {"PATTERN:", "R"},
  74. };
  75. for (const auto& i: pseudoFormats) {
  76. if (strncasecmp(path.c_str(), i.first.c_str(), i.first.length()) == 0) {
  77. return mode == '*' || i.second.find(mode) != string::npos;
  78. }
  79. }
  80. return false;
  81. }
  82. #define IMAGICK_THROW imagickThrow<ImagickException>
  83. void imagickReadOp(MagickWand* wand,
  84. const String& path,
  85. const ImagickFileOp& op) {
  86. String realpath;
  87. if (isMagickPseudoFormat(path, 'R')) {
  88. realpath = path;
  89. } else {
  90. auto var = f_realpath(path);
  91. realpath = var.isString() ? var.toString() : null_string;
  92. if (realpath.empty() ||
  93. !f_is_file(realpath) ||
  94. !f_is_readable(realpath)) {
  95. realpath = null_string;
  96. }
  97. }
  98. if (realpath.empty()) {
  99. IMAGICK_THROW("Invalid filename provided");
  100. }
  101. auto status = op(wand, path.c_str());
  102. if (status == MagickFalse) {
  103. IMAGICK_THROW("Unable to read image file");
  104. }
  105. MagickSetImageFilename(wand, realpath.c_str());
  106. MagickSetLastIterator(wand);
  107. }
  108. void imagickWriteOp(MagickWand* wand,
  109. const String& path_,
  110. const ImagickFileOp& op) {
  111. String path = path_;
  112. if (path.empty()) {
  113. path = convertMagickString(MagickGetImageFilename(wand));
  114. }
  115. if (path.empty()) {
  116. IMAGICK_THROW("No image filename specified");
  117. }
  118. String realpath;
  119. if (isMagickPseudoFormat(path, 'W')) {
  120. realpath = path;
  121. } else {
  122. static const int PHP_PATHINFO_DIRNAME = 1;
  123. String dirname = f_pathinfo(path, PHP_PATHINFO_DIRNAME).toString();
  124. if (!f_is_dir(dirname)) {
  125. realpath = null_string;
  126. } else if (!f_is_file(path)) {
  127. realpath = f_is_writable(dirname)
  128. ? path
  129. : null_string;
  130. } else {
  131. realpath = f_is_writable(path)
  132. ? f_realpath(path).toString()
  133. : null_string;
  134. }
  135. }
  136. if (realpath.empty()) {
  137. IMAGICK_THROW("Invalid filename provided");
  138. }
  139. auto status = op(wand, path.c_str());
  140. if (status == MagickFalse) {
  141. IMAGICK_THROW("Unable to write image file");
  142. }
  143. }
  144. ALWAYS_INLINE
  145. FILE* getFILE(const Resource& res, bool isWrite) {
  146. File* f = res.getTyped<File>(true, true);
  147. if (f == nullptr || f->isClosed()) {
  148. IMAGICK_THROW("Invalid stream resource");
  149. }
  150. FILE* fp;
  151. if (isWrite) {
  152. f->flush();
  153. fp = fdopen(dup(f->fd()), "ab");
  154. } else {
  155. fp = fdopen(dup(f->fd()), "rb");
  156. if (fp != nullptr && f->seekable()) {
  157. fseek(fp, f->tell(), SEEK_SET);
  158. }
  159. }
  160. if (fp == nullptr) {
  161. IMAGICK_THROW("Invalid stream resource");
  162. }
  163. return fp;
  164. }
  165. void imagickReadOp(MagickWand* wand,
  166. const Resource& res,
  167. const ImagickHandleOp& op) {
  168. auto fp = getFILE(res, false);
  169. auto status = op(wand, fp);
  170. fclose(fp);
  171. if (status == MagickFalse) {
  172. IMAGICK_THROW("Unable to read image filehandle");
  173. }
  174. }
  175. void imagickWriteOp(MagickWand* wand,
  176. const Resource& res,
  177. const String& format,
  178. const ImagickHandleOp& op) {
  179. auto fp = getFILE(res, true);
  180. // Get the current name
  181. String filename = convertMagickString(MagickGetImageFilename(wand));
  182. if (!format.empty()) {
  183. MagickSetImageFilename(wand, (format + ":").c_str());
  184. }
  185. auto status = op(wand, fp);
  186. fclose(fp);
  187. // Restore the original name after write
  188. if (!format.empty()) {
  189. MagickSetImageFilename(wand, filename.c_str());
  190. }
  191. if (status == MagickFalse) {
  192. IMAGICK_THROW("Unable to write image filehandle");
  193. }
  194. }
  195. #undef IMAGICK_THROW
  196. //////////////////////////////////////////////////////////////////////////////
  197. // Common Helper
  198. void raiseDeprecated(const char* className, const char* methodName) {
  199. raise_message(ErrorConstants::ErrorModes::PHP_DEPRECATED,
  200. "%s::%s method is deprecated and it's use should be avoided",
  201. className, methodName);
  202. }
  203. void raiseDeprecated(const char* className,
  204. const char* methodName,
  205. const char* newClass,
  206. const char* newMethod) {
  207. raise_message(ErrorConstants::ErrorModes::PHP_DEPRECATED,
  208. "%s::%s is deprecated. %s::%s should be used instead",
  209. className, methodName, newClass, newMethod);
  210. }
  211. String convertMagickString(char* &&str) {
  212. if (str == nullptr) {
  213. return null_string;
  214. } else {
  215. String ret(str);
  216. freeMagickMemory(str);
  217. return ret;
  218. }
  219. }
  220. String convertMagickData(size_t size, unsigned char* &data) {
  221. if (data == nullptr) {
  222. return null_string;
  223. } else {
  224. String ret((char*)data, size, CopyString);
  225. freeMagickMemory(data);
  226. return ret;
  227. }
  228. }
  229. MagickBooleanType withMagickLocaleFix(
  230. const std::function<MagickBooleanType()>& lambda) {
  231. static const char* const IMAGICK_LC_NUMERIC_LOCALE = "C";
  232. if (!ImagickExtension::hasLocaleFix()) {
  233. return lambda();
  234. }
  235. const char* plocale = setlocale(LC_NUMERIC, nullptr);
  236. if (plocale == nullptr) {
  237. return lambda();
  238. }
  239. // Switch the locale to IMAGICK_LC_NUMERIC_LOCALE if imagick.locale_fix is on
  240. const std::string locale = plocale;
  241. setlocale(LC_NUMERIC, IMAGICK_LC_NUMERIC_LOCALE);
  242. auto ret = lambda();
  243. setlocale(LC_NUMERIC, locale.c_str());
  244. return ret;
  245. }
  246. std::vector<double> toDoubleArray(const Array& array) {
  247. std::vector<double> ret;
  248. for (ArrayIter it(array); it; ++it) {
  249. ret.push_back(it.secondRefPlus().toDouble());
  250. }
  251. return ret;
  252. }
  253. std::vector<PointInfo> toPointInfoArray(const Array& coordinates) {
  254. std::vector<PointInfo> ret(coordinates.size());
  255. int idx = 0;
  256. for (ArrayIter it(coordinates); it; ++it) {
  257. const Variant& element = it.secondRefPlus();
  258. if (!element.isArray()) {
  259. return {};
  260. }
  261. const Array& coordinate = element.toCArrRef();
  262. if (coordinate.size() != 2) {
  263. return {};
  264. }
  265. for (ArrayIter jt(coordinate); jt; ++jt) {
  266. const String& key = jt.first().toString();
  267. double value = jt.secondRefPlus().toDouble();
  268. if (key == s_x) {
  269. ret[idx].x = value;
  270. } else if (key == s_y) {
  271. ret[idx].y = value;
  272. } else {
  273. return {};
  274. }
  275. }
  276. ++idx;
  277. }
  278. return ret;
  279. }
  280. //////////////////////////////////////////////////////////////////////////////
  281. // ImagickExtension
  282. ImagickExtension::ImagickExtension() :
  283. Extension("imagick") {
  284. }
  285. void ImagickExtension::moduleInit() {
  286. loadImagickConstants();
  287. loadImagickClass();
  288. loadImagickDrawClass();
  289. loadImagickPixelClass();
  290. loadImagickPixelIteratorClass();
  291. loadSystemlib();
  292. }
  293. void ImagickExtension::threadInit() {
  294. IniSetting::Bind(this, IniSetting::PHP_INI_ALL,
  295. "imagick.locale_fix", "0",
  296. &s_ini_setting->m_locale_fix);
  297. IniSetting::Bind(this, IniSetting::PHP_INI_ALL,
  298. "imagick.progress_monitor", "0",
  299. &s_ini_setting->m_progress_monitor);
  300. }
  301. bool ImagickExtension::hasLocaleFix() {
  302. return s_ini_setting->m_locale_fix;
  303. }
  304. bool ImagickExtension::hasProgressMonitor() {
  305. return s_ini_setting->m_progress_monitor;
  306. }
  307. IMPLEMENT_THREAD_LOCAL(ImagickExtension::ImagickIniSetting,
  308. ImagickExtension::s_ini_setting);
  309. ImagickExtension s_imagick_extension;
  310. HHVM_GET_MODULE(imagick)
  311. //////////////////////////////////////////////////////////////////////////////
  312. } // namespace HPHP