test-device.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. // Copyright (C) 2004-2024 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. #include "mupdf/fitz.h"
  23. #include "color-imp.h"
  24. typedef struct
  25. {
  26. fz_device super;
  27. int *is_color;
  28. float threshold;
  29. int options;
  30. fz_device *passthrough;
  31. int resolved;
  32. } fz_test_device;
  33. static int
  34. is_rgb_color(float threshold, float r, float g, float b)
  35. {
  36. float rg_diff = fz_abs(r - g);
  37. float rb_diff = fz_abs(r - b);
  38. float gb_diff = fz_abs(g - b);
  39. return rg_diff > threshold || rb_diff > threshold || gb_diff > threshold;
  40. }
  41. static int
  42. is_rgb_color_u8(int threshold_u8, int r, int g, int b)
  43. {
  44. int rg_diff = fz_absi(r - g);
  45. int rb_diff = fz_absi(r - b);
  46. int gb_diff = fz_absi(g - b);
  47. return rg_diff > threshold_u8 || rb_diff > threshold_u8 || gb_diff > threshold_u8;
  48. }
  49. static void
  50. fz_test_color(fz_context *ctx, fz_test_device *t, fz_colorspace *colorspace, const float *color, fz_color_params color_params)
  51. {
  52. if (!*t->is_color && colorspace && fz_colorspace_type(ctx, colorspace) != FZ_COLORSPACE_GRAY)
  53. {
  54. if (colorspace == fz_device_rgb(ctx))
  55. {
  56. if (is_rgb_color(t->threshold, color[0], color[1], color[2]))
  57. {
  58. *t->is_color = 2;
  59. t->resolved = 1;
  60. if (t->passthrough == NULL)
  61. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  62. }
  63. }
  64. else
  65. {
  66. float rgb[3];
  67. fz_convert_color(ctx, colorspace, color, fz_device_rgb(ctx), rgb, NULL, color_params);
  68. if (is_rgb_color(t->threshold, rgb[0], rgb[1], rgb[2]))
  69. {
  70. *t->is_color = 2;
  71. t->resolved = 1;
  72. if (t->passthrough == NULL)
  73. {
  74. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  75. }
  76. }
  77. }
  78. }
  79. }
  80. static void
  81. fz_test_fill_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, fz_matrix ctm,
  82. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  83. {
  84. fz_test_device *dev = (fz_test_device*)dev_;
  85. if (dev->resolved == 0 && alpha != 0.0f)
  86. fz_test_color(ctx, dev, colorspace, color, color_params);
  87. if (dev->passthrough)
  88. fz_fill_path(ctx, dev->passthrough, path, even_odd, ctm, colorspace, color, alpha, color_params);
  89. }
  90. static void
  91. fz_test_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke,
  92. fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  93. {
  94. fz_test_device *dev = (fz_test_device*)dev_;
  95. if (dev->resolved == 0 && alpha != 0.0f)
  96. fz_test_color(ctx, dev, colorspace, color, color_params);
  97. if (dev->passthrough)
  98. fz_stroke_path(ctx, dev->passthrough, path, stroke, ctm, colorspace, color, alpha, color_params);
  99. }
  100. static void
  101. fz_test_fill_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm,
  102. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  103. {
  104. fz_test_device *dev = (fz_test_device*)dev_;
  105. if (dev->resolved == 0 && alpha != 0.0f)
  106. fz_test_color(ctx, dev, colorspace, color, color_params);
  107. if (dev->passthrough)
  108. fz_fill_text(ctx, dev->passthrough, text, ctm, colorspace, color, alpha, color_params);
  109. }
  110. static void
  111. fz_test_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke,
  112. fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  113. {
  114. fz_test_device *dev = (fz_test_device*)dev_;
  115. if (dev->resolved == 0 && alpha != 0.0f)
  116. fz_test_color(ctx, dev, colorspace, color, color_params);
  117. if (dev->passthrough)
  118. fz_stroke_text(ctx, dev->passthrough, text, stroke, ctm, colorspace, color, alpha, color_params);
  119. }
  120. struct shadearg
  121. {
  122. fz_test_device *dev;
  123. fz_shade *shade;
  124. fz_color_params color_params;
  125. };
  126. static void
  127. prepare_vertex(fz_context *ctx, void *arg_, fz_vertex *v, const float *color)
  128. {
  129. struct shadearg *arg = arg_;
  130. fz_test_device *dev = arg->dev;
  131. fz_shade *shade = arg->shade;
  132. if (shade->function_stride == 0)
  133. fz_test_color(ctx, dev, shade->colorspace, color, arg->color_params);
  134. }
  135. static void
  136. fz_test_fill_shade(fz_context *ctx, fz_device *dev_, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params)
  137. {
  138. fz_test_device *dev = (fz_test_device*)dev_;
  139. if (dev->resolved == 0)
  140. {
  141. if ((dev->options & FZ_TEST_OPT_SHADINGS) == 0)
  142. {
  143. if (fz_colorspace_type(ctx, shade->colorspace) != FZ_COLORSPACE_GRAY)
  144. {
  145. /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */
  146. if (*dev->is_color == 0)
  147. *dev->is_color = 1;
  148. dev->resolved = 1;
  149. if (dev->passthrough == NULL)
  150. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  151. }
  152. }
  153. else
  154. {
  155. int stride = shade->function_stride;
  156. if (stride)
  157. {
  158. int i;
  159. for (i = 0; i < 256; i++)
  160. fz_test_color(ctx, dev, shade->colorspace, &shade->function[i*stride], color_params);
  161. }
  162. else
  163. {
  164. struct shadearg arg;
  165. arg.dev = dev;
  166. arg.shade = shade;
  167. arg.color_params = color_params;
  168. fz_process_shade(ctx, shade, ctm, fz_device_current_scissor(ctx, dev_), prepare_vertex, NULL, &arg);
  169. }
  170. }
  171. }
  172. if (dev->passthrough)
  173. fz_fill_shade(ctx, dev->passthrough, shade, ctm, alpha, color_params);
  174. }
  175. static void fz_test_fill_compressed_8bpc_image(fz_context *ctx, fz_test_device *dev, fz_image *image, fz_stream *stream, fz_color_params color_params)
  176. {
  177. unsigned int count = (unsigned int)image->w * (unsigned int)image->h;
  178. unsigned int i;
  179. if (image->colorspace == fz_device_rgb(ctx))
  180. {
  181. int threshold_u8 = dev->threshold * 255;
  182. for (i = 0; i < count; i++)
  183. {
  184. int r = fz_read_byte(ctx, stream);
  185. int g = fz_read_byte(ctx, stream);
  186. int b = fz_read_byte(ctx, stream);
  187. if (is_rgb_color_u8(threshold_u8, r, g, b))
  188. {
  189. *dev->is_color = 1;
  190. dev->resolved = 1;
  191. if (dev->passthrough == NULL)
  192. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  193. break;
  194. }
  195. }
  196. }
  197. else
  198. {
  199. fz_color_converter cc;
  200. unsigned int n = (unsigned int)image->n;
  201. fz_init_cached_color_converter(ctx, &cc, image->colorspace, fz_device_rgb(ctx), NULL, NULL, color_params);
  202. fz_try(ctx)
  203. {
  204. for (i = 0; i < count; i++)
  205. {
  206. float cs[FZ_MAX_COLORS];
  207. float ds[FZ_MAX_COLORS];
  208. unsigned int k;
  209. for (k = 0; k < n; k++)
  210. cs[k] = fz_read_byte(ctx, stream) / 255.0f;
  211. cc.convert(ctx, &cc, ds, cs);
  212. if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2]))
  213. {
  214. *dev->is_color = 1;
  215. dev->resolved = 1;
  216. if (dev->passthrough == NULL)
  217. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  218. break;
  219. }
  220. }
  221. }
  222. fz_always(ctx)
  223. fz_fin_cached_color_converter(ctx, &cc);
  224. fz_catch(ctx)
  225. fz_rethrow(ctx);
  226. }
  227. }
  228. static void
  229. fz_test_fill_other_image(fz_context *ctx, fz_test_device *dev, fz_pixmap *pix, fz_color_params color_params)
  230. {
  231. unsigned int count, i, k, h, sa;
  232. size_t ss;
  233. unsigned char *s;
  234. count = pix->w;
  235. h = pix->h;
  236. s = pix->samples;
  237. sa = pix->alpha;
  238. ss = pix->stride - pix->w * (size_t)pix->n;
  239. if (pix->colorspace == fz_device_rgb(ctx))
  240. {
  241. int threshold_u8 = dev->threshold * 255;
  242. while (h--)
  243. {
  244. for (i = 0; i < count; i++)
  245. {
  246. if ((!sa || s[3] != 0) && is_rgb_color_u8(threshold_u8, s[0], s[1], s[2]))
  247. {
  248. *dev->is_color = 1;
  249. dev->resolved = 1;
  250. if (dev->passthrough == NULL)
  251. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  252. break;
  253. }
  254. s += 3 + sa;
  255. }
  256. s += ss;
  257. }
  258. }
  259. else
  260. {
  261. fz_color_converter cc;
  262. unsigned int n = (unsigned int)pix->n-1;
  263. fz_init_cached_color_converter(ctx, &cc, pix->colorspace, fz_device_rgb(ctx), NULL, NULL, color_params);
  264. fz_try(ctx)
  265. {
  266. while (h--)
  267. {
  268. for (i = 0; i < count; i++)
  269. {
  270. float cs[FZ_MAX_COLORS];
  271. float ds[FZ_MAX_COLORS];
  272. for (k = 0; k < n; k++)
  273. cs[k] = (*s++) / 255.0f;
  274. if (sa && *s++ == 0)
  275. continue;
  276. cc.convert(ctx, &cc, ds, cs);
  277. if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2]))
  278. {
  279. *dev->is_color = 1;
  280. dev->resolved = 1;
  281. if (dev->passthrough == NULL)
  282. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  283. break;
  284. }
  285. }
  286. s += ss;
  287. }
  288. }
  289. fz_always(ctx)
  290. fz_fin_cached_color_converter(ctx, &cc);
  291. fz_catch(ctx)
  292. fz_rethrow(ctx);
  293. }
  294. }
  295. static void
  296. fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params)
  297. {
  298. fz_test_device *dev = (fz_test_device*)dev_;
  299. while (dev->resolved == 0) /* So we can break out */
  300. {
  301. fz_compressed_buffer *buffer;
  302. if (*dev->is_color || !image->colorspace || fz_colorspace_is_gray(ctx, image->colorspace))
  303. break;
  304. if ((dev->options & FZ_TEST_OPT_IMAGES) == 0)
  305. {
  306. /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */
  307. if (*dev->is_color == 0)
  308. *dev->is_color = 1;
  309. dev->resolved = 1;
  310. if (dev->passthrough == NULL)
  311. fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
  312. break;
  313. }
  314. buffer = fz_compressed_image_buffer(ctx, image);
  315. if (buffer && image->bpc == 8)
  316. {
  317. fz_stream *stream = fz_open_compressed_buffer(ctx, buffer);
  318. fz_try(ctx)
  319. fz_test_fill_compressed_8bpc_image(ctx, dev, image, stream, color_params);
  320. fz_always(ctx)
  321. fz_drop_stream(ctx, stream);
  322. fz_catch(ctx)
  323. fz_rethrow(ctx);
  324. }
  325. else
  326. {
  327. fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0);
  328. if (pix == NULL) /* Should never happen really, but... */
  329. break;
  330. fz_try(ctx)
  331. fz_test_fill_other_image(ctx, dev, pix, color_params);
  332. fz_always(ctx)
  333. fz_drop_pixmap(ctx, pix);
  334. fz_catch(ctx)
  335. fz_rethrow(ctx);
  336. }
  337. break;
  338. }
  339. if (dev->passthrough)
  340. fz_fill_image(ctx, dev->passthrough, image, ctm, alpha, color_params);
  341. }
  342. static void
  343. fz_test_fill_image_mask(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm,
  344. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  345. {
  346. fz_test_device *dev = (fz_test_device*)dev_;
  347. if (dev->resolved == 0)
  348. {
  349. /* We assume that at least some of the image pixels are non-zero */
  350. fz_test_color(ctx, dev, colorspace, color, color_params);
  351. }
  352. if (dev->passthrough)
  353. fz_fill_image_mask(ctx, dev->passthrough, image, ctm, colorspace, color, alpha, color_params);
  354. }
  355. static void
  356. fz_test_clip_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor)
  357. {
  358. fz_test_device *dev = (fz_test_device*)dev_;
  359. if (dev->passthrough)
  360. fz_clip_path(ctx, dev->passthrough, path, even_odd, ctm, scissor);
  361. }
  362. static void
  363. fz_test_clip_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
  364. {
  365. fz_test_device *dev = (fz_test_device*)dev_;
  366. if (dev->passthrough)
  367. fz_clip_stroke_path(ctx, dev->passthrough, path, stroke, ctm, scissor);
  368. }
  369. static void
  370. fz_test_clip_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm, fz_rect scissor)
  371. {
  372. fz_test_device *dev = (fz_test_device*)dev_;
  373. if (dev->passthrough)
  374. fz_clip_text(ctx, dev->passthrough, text, ctm, scissor);
  375. }
  376. static void
  377. fz_test_clip_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
  378. {
  379. fz_test_device *dev = (fz_test_device*)dev_;
  380. if (dev->passthrough)
  381. fz_clip_stroke_text(ctx, dev->passthrough, text, stroke, ctm, scissor);
  382. }
  383. static void
  384. fz_test_ignore_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm)
  385. {
  386. fz_test_device *dev = (fz_test_device*)dev_;
  387. if (dev->passthrough)
  388. fz_ignore_text(ctx, dev->passthrough, text, ctm);
  389. }
  390. static void
  391. fz_test_clip_image_mask(fz_context *ctx, fz_device *dev_, fz_image *img, fz_matrix ctm, fz_rect scissor)
  392. {
  393. fz_test_device *dev = (fz_test_device*)dev_;
  394. if (dev->passthrough)
  395. fz_clip_image_mask(ctx, dev->passthrough, img, ctm, scissor);
  396. }
  397. static void
  398. fz_test_pop_clip(fz_context *ctx, fz_device *dev_)
  399. {
  400. fz_test_device *dev = (fz_test_device*)dev_;
  401. if (dev->passthrough)
  402. fz_pop_clip(ctx, dev->passthrough);
  403. }
  404. static void
  405. fz_test_begin_mask(fz_context *ctx, fz_device *dev_, fz_rect rect, int luminosity, fz_colorspace *cs, const float *bc, fz_color_params color_params)
  406. {
  407. fz_test_device *dev = (fz_test_device*)dev_;
  408. if (dev->passthrough)
  409. fz_begin_mask(ctx, dev->passthrough, rect, luminosity, cs, bc, color_params);
  410. }
  411. static void
  412. fz_test_end_mask(fz_context *ctx, fz_device *dev_, fz_function *tr)
  413. {
  414. fz_test_device *dev = (fz_test_device*)dev_;
  415. if (dev->passthrough)
  416. fz_end_mask_tr(ctx, dev->passthrough, tr);
  417. }
  418. static void
  419. fz_test_begin_group(fz_context *ctx, fz_device *dev_, fz_rect rect, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
  420. {
  421. fz_test_device *dev = (fz_test_device*)dev_;
  422. if (dev->passthrough)
  423. fz_begin_group(ctx, dev->passthrough, rect, cs, isolated, knockout, blendmode, alpha);
  424. }
  425. static void
  426. fz_test_end_group(fz_context *ctx, fz_device *dev_)
  427. {
  428. fz_test_device *dev = (fz_test_device*)dev_;
  429. if (dev->passthrough)
  430. fz_end_group(ctx, dev->passthrough);
  431. }
  432. static int
  433. fz_test_begin_tile(fz_context *ctx, fz_device *dev_, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id)
  434. {
  435. fz_test_device *dev = (fz_test_device*)dev_;
  436. if (dev->passthrough)
  437. return fz_begin_tile_id(ctx, dev->passthrough, area, view, xstep, ystep, ctm, id);
  438. else
  439. return 0;
  440. }
  441. static void
  442. fz_test_end_tile(fz_context *ctx, fz_device *dev_)
  443. {
  444. fz_test_device *dev = (fz_test_device*)dev_;
  445. if (dev->passthrough)
  446. fz_end_tile(ctx, dev->passthrough);
  447. }
  448. fz_device *
  449. fz_new_test_device(fz_context *ctx, int *is_color, float threshold, int options, fz_device *passthrough)
  450. {
  451. fz_test_device *dev = fz_new_derived_device(ctx, fz_test_device);
  452. dev->super.fill_path = fz_test_fill_path;
  453. dev->super.stroke_path = fz_test_stroke_path;
  454. dev->super.fill_text = fz_test_fill_text;
  455. dev->super.stroke_text = fz_test_stroke_text;
  456. dev->super.fill_shade = fz_test_fill_shade;
  457. dev->super.fill_image = fz_test_fill_image;
  458. dev->super.fill_image_mask = fz_test_fill_image_mask;
  459. if (passthrough)
  460. {
  461. dev->super.clip_path = fz_test_clip_path;
  462. dev->super.clip_stroke_path = fz_test_clip_stroke_path;
  463. dev->super.clip_text = fz_test_clip_text;
  464. dev->super.clip_stroke_text = fz_test_clip_stroke_text;
  465. dev->super.ignore_text = fz_test_ignore_text;
  466. dev->super.clip_image_mask = fz_test_clip_image_mask;
  467. dev->super.pop_clip = fz_test_pop_clip;
  468. dev->super.begin_mask = fz_test_begin_mask;
  469. dev->super.end_mask = fz_test_end_mask;
  470. dev->super.begin_group = fz_test_begin_group;
  471. dev->super.end_group = fz_test_end_group;
  472. dev->super.begin_tile = fz_test_begin_tile;
  473. dev->super.end_tile = fz_test_end_tile;
  474. }
  475. dev->is_color = is_color;
  476. dev->options = options;
  477. dev->threshold = threshold;
  478. dev->passthrough = passthrough;
  479. dev->resolved = 0;
  480. *dev->is_color = 0;
  481. return (fz_device*)dev;
  482. }