You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JpegCompressor.cxx 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
  3. * Copyright 2014 Pierre Ossman for Cendio AB
  4. *
  5. * This is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this software; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  18. * USA.
  19. */
  20. #include <rfb/JpegCompressor.h>
  21. #include <rdr/Exception.h>
  22. #include <rfb/Rect.h>
  23. #include <rfb/PixelFormat.h>
  24. #include <rfb/ClientParams.h>
  25. #include <stdio.h>
  26. extern "C" {
  27. #include <jpeglib.h>
  28. }
  29. #include <setjmp.h>
  30. using namespace rfb;
  31. //
  32. // Special formats that libjpeg can have optimised code paths for
  33. //
  34. static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
  35. static const PixelFormat pfBGRX(32, 24, false, true, 255, 255, 255, 16, 8, 0);
  36. static const PixelFormat pfXRGB(32, 24, false, true, 255, 255, 255, 8, 16, 24);
  37. static const PixelFormat pfXBGR(32, 24, false, true, 255, 255, 255, 24, 16, 8);
  38. //
  39. // Error manager implementation for the JPEG library
  40. //
  41. struct JPEG_ERROR_MGR {
  42. struct jpeg_error_mgr pub;
  43. jmp_buf jmpBuffer;
  44. char lastError[JMSG_LENGTH_MAX];
  45. };
  46. static void
  47. JpegErrorExit(j_common_ptr cinfo)
  48. {
  49. JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
  50. (*cinfo->err->output_message)(cinfo);
  51. longjmp(err->jmpBuffer, 1);
  52. }
  53. static void
  54. JpegOutputMessage(j_common_ptr cinfo)
  55. {
  56. JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
  57. (*cinfo->err->format_message)(cinfo, err->lastError);
  58. }
  59. //
  60. // Destination manager implementation for the JPEG library.
  61. //
  62. struct JPEG_DEST_MGR {
  63. struct jpeg_destination_mgr pub;
  64. JpegCompressor *instance;
  65. };
  66. static void
  67. JpegInitDestination(j_compress_ptr cinfo)
  68. {
  69. JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
  70. JpegCompressor *jc = dest->instance;
  71. jc->clear();
  72. dest->pub.next_output_byte = jc->getptr();
  73. dest->pub.free_in_buffer = jc->getend() - jc->getptr();
  74. }
  75. static boolean
  76. JpegEmptyOutputBuffer(j_compress_ptr cinfo)
  77. {
  78. JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
  79. JpegCompressor *jc = dest->instance;
  80. jc->setptr(jc->getend());
  81. jc->overrun(jc->getend() - jc->getstart(), 1);
  82. dest->pub.next_output_byte = jc->getptr();
  83. dest->pub.free_in_buffer = jc->getend() - jc->getptr();
  84. return TRUE;
  85. }
  86. static void
  87. JpegTermDestination(j_compress_ptr cinfo)
  88. {
  89. JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
  90. JpegCompressor *jc = dest->instance;
  91. jc->setptr(dest->pub.next_output_byte);
  92. }
  93. JpegCompressor::JpegCompressor(int bufferLen) : MemOutStream(bufferLen)
  94. {
  95. cinfo = new jpeg_compress_struct;
  96. err = new struct JPEG_ERROR_MGR;
  97. cinfo->err = jpeg_std_error(&err->pub);
  98. snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
  99. err->pub.error_exit = JpegErrorExit;
  100. err->pub.output_message = JpegOutputMessage;
  101. if(setjmp(err->jmpBuffer)) {
  102. // this will execute if libjpeg has an error
  103. throw rdr::Exception("%s", err->lastError);
  104. }
  105. jpeg_create_compress(cinfo);
  106. dest = new struct JPEG_DEST_MGR;
  107. dest->pub.init_destination = JpegInitDestination;
  108. dest->pub.empty_output_buffer = JpegEmptyOutputBuffer;
  109. dest->pub.term_destination = JpegTermDestination;
  110. dest->instance = this;
  111. cinfo->dest = (struct jpeg_destination_mgr *)dest;
  112. }
  113. JpegCompressor::~JpegCompressor(void)
  114. {
  115. if(setjmp(err->jmpBuffer)) {
  116. // this will execute if libjpeg has an error
  117. return;
  118. }
  119. jpeg_destroy_compress(cinfo);
  120. delete err;
  121. delete dest;
  122. delete cinfo;
  123. }
  124. void JpegCompressor::compress(const rdr::U8 *buf, int stride, const Rect& r,
  125. const PixelFormat& pf, int quality, int subsamp)
  126. {
  127. int w = r.width();
  128. int h = r.height();
  129. int pixelsize;
  130. rdr::U8 *srcBuf = NULL;
  131. bool srcBufIsTemp = false;
  132. JSAMPROW *rowPointer = NULL;
  133. if(setjmp(err->jmpBuffer)) {
  134. // this will execute if libjpeg has an error
  135. jpeg_abort_compress(cinfo);
  136. if (srcBufIsTemp && srcBuf) delete[] srcBuf;
  137. if (rowPointer) delete[] rowPointer;
  138. throw rdr::Exception("%s", err->lastError);
  139. }
  140. cinfo->image_width = w;
  141. cinfo->image_height = h;
  142. cinfo->in_color_space = JCS_RGB;
  143. pixelsize = 3;
  144. #ifdef JCS_EXTENSIONS
  145. // Try to have libjpeg output directly to our native format
  146. // libjpeg can only handle some "standard" formats
  147. if (pfRGBX.equal(pf))
  148. cinfo->in_color_space = JCS_EXT_RGBX;
  149. else if (pfBGRX.equal(pf))
  150. cinfo->in_color_space = JCS_EXT_BGRX;
  151. else if (pfXRGB.equal(pf))
  152. cinfo->in_color_space = JCS_EXT_XRGB;
  153. else if (pfXBGR.equal(pf))
  154. cinfo->in_color_space = JCS_EXT_XBGR;
  155. if (cinfo->in_color_space != JCS_RGB) {
  156. srcBuf = (rdr::U8 *)buf;
  157. pixelsize = 4;
  158. }
  159. #endif
  160. if (stride == 0)
  161. stride = w;
  162. if (cinfo->in_color_space == JCS_RGB) {
  163. srcBuf = new rdr::U8[w * h * pixelsize];
  164. srcBufIsTemp = true;
  165. pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w, stride, h);
  166. stride = w;
  167. }
  168. cinfo->input_components = pixelsize;
  169. jpeg_set_defaults(cinfo);
  170. if (quality >= 1 && quality <= 100) {
  171. jpeg_set_quality(cinfo, quality, TRUE);
  172. if (quality >= 96)
  173. cinfo->dct_method = JDCT_ISLOW;
  174. else
  175. cinfo->dct_method = JDCT_FASTEST;
  176. }
  177. switch (subsamp) {
  178. case subsample16X:
  179. case subsample8X:
  180. // FIXME (fall through)
  181. case subsample4X:
  182. cinfo->comp_info[0].h_samp_factor = 2;
  183. cinfo->comp_info[0].v_samp_factor = 2;
  184. break;
  185. case subsample2X:
  186. cinfo->comp_info[0].h_samp_factor = 2;
  187. cinfo->comp_info[0].v_samp_factor = 1;
  188. break;
  189. case subsampleGray:
  190. jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
  191. default:
  192. cinfo->comp_info[0].h_samp_factor = 1;
  193. cinfo->comp_info[0].v_samp_factor = 1;
  194. }
  195. rowPointer = new JSAMPROW[h];
  196. for (int dy = 0; dy < h; dy++)
  197. rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * stride * pixelsize]);
  198. jpeg_start_compress(cinfo, TRUE);
  199. while (cinfo->next_scanline < cinfo->image_height)
  200. jpeg_write_scanlines(cinfo, &rowPointer[cinfo->next_scanline],
  201. cinfo->image_height - cinfo->next_scanline);
  202. jpeg_finish_compress(cinfo);
  203. if (srcBufIsTemp) delete[] srcBuf;
  204. delete[] rowPointer;
  205. }
  206. void JpegCompressor::writeBytes(const void* data, int length)
  207. {
  208. throw rdr::Exception("writeBytes() is not valid with a JpegCompressor instance. Use compress() instead.");
  209. }