NetSurf
jpeg.c
Go to the documentation of this file.
1/*
2 * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
3 * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/**
21 * \file
22 * implementation of content handling for image/jpeg
23 *
24 * This implementation uses the IJG JPEG library.
25 */
26
27#include <stdbool.h>
28#include <stdlib.h>
29#include <setjmp.h>
30
31#include "utils/utils.h"
32#include "utils/log.h"
33#include "utils/messages.h"
34#include "netsurf/bitmap.h"
35#include "content/llcache.h"
36#include "content/content.h"
40#include "desktop/bitmap.h"
41
42#include "image/image_cache.h"
43
44#define JPEG_INTERNAL_OPTIONS
45#include "jpeglib.h"
46#include "jpeg.h"
47
48/** absolute minimum size of a jpeg below which it is not even worth
49 * trying to read header data
50 */
51#define MIN_JPEG_SIZE 20
52
53#ifndef LIBJPEG_TURBO_VERSION
54#warning Using libjpeg (libjpeg-turbo is recommended)
55#endif
56
57static char nsjpeg_error_buffer[JMSG_LENGTH_MAX];
58
59static unsigned char nsjpeg_eoi[] = { 0xff, JPEG_EOI };
60
61/**
62 * Content create entry point.
63 */
65 lwc_string *imime_type, const struct http_parameter *params,
66 llcache_handle *llcache, const char *fallback_charset,
67 bool quirks, struct content **c)
68{
69 struct content *jpeg;
70 nserror error;
71
72 jpeg = calloc(1, sizeof(struct content));
73 if (jpeg == NULL)
74 return NSERROR_NOMEM;
75
76 error = content__init(jpeg, handler, imime_type, params,
78 if (error != NSERROR_OK) {
79 free(jpeg);
80 return error;
81 }
82
83 *c = jpeg;
84
85 return NSERROR_OK;
86}
87
88/**
89 * JPEG data source manager: initialize source.
90 */
91static void nsjpeg_init_source(j_decompress_ptr cinfo)
92{
93}
94
95
96/**
97 * JPEG data source manager: fill the input buffer.
98 *
99 * This can only occur if the JPEG data was truncated or corrupted. Insert a
100 * fake EOI marker to allow the decompressor to output as much as possible.
101 */
102static boolean nsjpeg_fill_input_buffer(j_decompress_ptr cinfo)
103{
104 cinfo->src->next_input_byte = nsjpeg_eoi;
105 cinfo->src->bytes_in_buffer = 2;
106 return TRUE;
107}
108
109
110/**
111 * JPEG data source manager: skip num_bytes worth of data.
112 */
113
114static void nsjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
115{
116 if ((long) cinfo->src->bytes_in_buffer < num_bytes) {
117 cinfo->src->next_input_byte = 0;
118 cinfo->src->bytes_in_buffer = 0;
119 } else {
120 cinfo->src->next_input_byte += num_bytes;
121 cinfo->src->bytes_in_buffer -= num_bytes;
122 }
123}
124
125
126/**
127 * JPEG data source manager: terminate source.
128 */
129static void nsjpeg_term_source(j_decompress_ptr cinfo)
130{
131}
132
133
134/**
135 * Error output handler for JPEG library.
136 *
137 * This logs to NetSurf log instead of stderr.
138 * Warnings only - fatal errors are trapped by nsjpeg_error_exit
139 * and do not call the output handler.
140 */
141static void nsjpeg_error_log(j_common_ptr cinfo)
142{
143 cinfo->err->format_message(cinfo, nsjpeg_error_buffer);
144 NSLOG(netsurf, INFO, "%s", nsjpeg_error_buffer);
145}
146
147
148/**
149 * Fatal error handler for JPEG library.
150 *
151 * This prevents jpeglib calling exit() on a fatal error.
152 */
153static void nsjpeg_error_exit(j_common_ptr cinfo)
154{
155 jmp_buf *setjmp_buffer = (jmp_buf *) cinfo->client_data;
156
157 cinfo->err->format_message(cinfo, nsjpeg_error_buffer);
158 NSLOG(netsurf, INFO, "%s", nsjpeg_error_buffer);
159
160 longjmp(*setjmp_buffer, 1);
161}
162
163/**
164 * Convert scan lines from CMYK to core client bitmap layout.
165 */
166static inline void nsjpeg__decode_cmyk(
167 struct jpeg_decompress_struct *cinfo,
168 uint8_t * volatile pixels,
169 size_t rowstride)
170{
171 int width = cinfo->output_width * 4;
172
173 do {
174 JSAMPROW scanlines[1] = {
175 [0] = (JSAMPROW)
176 (pixels + rowstride * cinfo->output_scanline),
177 };
178 jpeg_read_scanlines(cinfo, scanlines, 1);
179
180 for (int i = width - 4; 0 <= i; i -= 4) {
181 /* Trivial inverse CMYK -> RGBA */
182 const int c = scanlines[0][i + 0];
183 const int m = scanlines[0][i + 1];
184 const int y = scanlines[0][i + 2];
185 const int k = scanlines[0][i + 3];
186
187 const int ck = c * k;
188 const int mk = m * k;
189 const int yk = y * k;
190
191#define DIV255(x) ((x) + 1 + ((x) >> 8)) >> 8
192 scanlines[0][i + bitmap_layout.r] = DIV255(ck);
193 scanlines[0][i + bitmap_layout.g] = DIV255(mk);
194 scanlines[0][i + bitmap_layout.b] = DIV255(yk);
195 scanlines[0][i + bitmap_layout.a] = 0xff;
196#undef DIV255
197 }
198 } while (cinfo->output_scanline != cinfo->output_height);
199}
200
201/**
202 * Convert scan lines from CMYK to core client bitmap layout.
203 */
204static inline void nsjpeg__decode_rgb(
205 struct jpeg_decompress_struct *cinfo,
206 uint8_t * volatile pixels,
207 size_t rowstride)
208{
209#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
210 int width = cinfo->output_width;
211#endif
212
213 do {
214 JSAMPROW scanlines[1] = {
215 [0] = (JSAMPROW)
216 (pixels + rowstride * cinfo->output_scanline),
217 };
218 jpeg_read_scanlines(cinfo, scanlines, 1);
219
220#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
221 /* Missmatch between configured libjpeg pixel format and
222 * NetSurf pixel format. Convert to RGBA */
223 for (int i = width - 1; 0 <= i; i--) {
224 int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED];
225 int g = scanlines[0][i * RGB_PIXELSIZE + RGB_GREEN];
226 int b = scanlines[0][i * RGB_PIXELSIZE + RGB_BLUE];
227 scanlines[0][i * 4 + bitmap_layout.r] = r;
228 scanlines[0][i * 4 + bitmap_layout.g] = g;
229 scanlines[0][i * 4 + bitmap_layout.b] = b;
230 scanlines[0][i * 4 + bitmap_layout.a] = 0xff;
231 }
232#endif
233 } while (cinfo->output_scanline != cinfo->output_height);
234}
235
236/**
237 * Convert scan lines from CMYK to core client bitmap layout.
238 */
239static inline void nsjpeg__decode_client_fmt(
240 struct jpeg_decompress_struct *cinfo,
241 uint8_t * volatile pixels,
242 size_t rowstride)
243{
244 do {
245 JSAMPROW scanlines[1] = {
246 [0] = (JSAMPROW)
247 (pixels + rowstride * cinfo->output_scanline),
248 };
249 jpeg_read_scanlines(cinfo, scanlines, 1);
250 } while (cinfo->output_scanline != cinfo->output_height);
251}
252
253/**
254 * create a bitmap from jpeg content.
255 */
256static struct bitmap *
258{
259 const uint8_t *source_data; /* Jpeg source data */
260 size_t source_size; /* length of Jpeg source data */
261 struct jpeg_decompress_struct cinfo;
262 struct jpeg_error_mgr jerr;
263 jmp_buf setjmp_buffer;
264 struct bitmap * volatile bitmap = NULL;
265 uint8_t * volatile pixels = NULL;
266 size_t rowstride;
267 struct jpeg_source_mgr source_mgr = {
268 0,
269 0,
273 jpeg_resync_to_restart,
275
276 /* obtain jpeg source data and perfom minimal sanity checks */
277 source_data = content__get_source_data(c, &source_size);
278
279 if ((source_data == NULL) ||
280 (source_size < MIN_JPEG_SIZE)) {
281 return NULL;
282 }
283
284 /* setup a JPEG library error handler */
285 cinfo.err = jpeg_std_error(&jerr);
286 jerr.error_exit = nsjpeg_error_exit;
287 jerr.output_message = nsjpeg_error_log;
288
289 /* handler for fatal errors during decompression */
290 if (setjmp(setjmp_buffer)) {
291 jpeg_destroy_decompress(&cinfo);
292 return bitmap;
293 }
294
295 cinfo.client_data = &setjmp_buffer;
296 jpeg_create_decompress(&cinfo);
297
298 /* setup data source */
299 source_mgr.next_input_byte = source_data;
300 source_mgr.bytes_in_buffer = source_size;
301 cinfo.src = &source_mgr;
302
303 /* read JPEG header information */
304 jpeg_read_header(&cinfo, TRUE);
305
306 /* set output processing parameters */
307 if (cinfo.jpeg_color_space == JCS_CMYK ||
308 cinfo.jpeg_color_space == JCS_YCCK) {
309 cinfo.out_color_space = JCS_CMYK;
310 } else {
311#ifdef JCS_ALPHA_EXTENSIONS
312 switch (bitmap_fmt.layout) {
314 cinfo.out_color_space = JCS_EXT_RGBA;
315 break;
317 cinfo.out_color_space = JCS_EXT_BGRA;
318 break;
320 cinfo.out_color_space = JCS_EXT_ARGB;
321 break;
323 cinfo.out_color_space = JCS_EXT_ABGR;
324 break;
325 default:
326 NSLOG(netsurf, ERROR, "Unexpected bitmap format: %u",
328 jpeg_destroy_decompress(&cinfo);
329 return NULL;
330 }
331#else
332 cinfo.out_color_space = JCS_RGB;
333#endif
334 }
335 cinfo.dct_method = JDCT_ISLOW;
336
337 /* commence the decompression, output parameters now valid */
338 jpeg_start_decompress(&cinfo);
339
340 /* create opaque bitmap (jpegs cannot be transparent) */
342 cinfo.output_width,
343 cinfo.output_height, BITMAP_OPAQUE);
344 if (bitmap == NULL) {
345 /* empty bitmap could not be created */
346 jpeg_destroy_decompress(&cinfo);
347 return NULL;
348 }
349
350 pixels = guit->bitmap->get_buffer(bitmap);
351 if (pixels == NULL) {
352 /* bitmap with no buffer available */
354 jpeg_destroy_decompress(&cinfo);
355 return NULL;
356 }
357
358 /* Convert scanlines from jpeg into bitmap */
359 rowstride = guit->bitmap->get_rowstride(bitmap);
360
361 switch (cinfo.out_color_space) {
362 case JCS_CMYK:
363 nsjpeg__decode_cmyk(&cinfo, pixels, rowstride);
364 break;
365
366 case JCS_RGB:
367 nsjpeg__decode_rgb(&cinfo, pixels, rowstride);
368 break;
369
370 default:
371 nsjpeg__decode_client_fmt(&cinfo, pixels, rowstride);
372 break;
373 }
374
376
377 jpeg_finish_decompress(&cinfo);
378 jpeg_destroy_decompress(&cinfo);
379
380 return bitmap;
381}
382
383/**
384 * Convert a CONTENT_JPEG for display.
385 */
386static bool nsjpeg_convert(struct content *c)
387{
388 struct jpeg_decompress_struct cinfo;
389 struct jpeg_error_mgr jerr;
390 jmp_buf setjmp_buffer;
391 struct jpeg_source_mgr source_mgr = { 0, 0,
393 nsjpeg_skip_input_data, jpeg_resync_to_restart,
395 union content_msg_data msg_data;
396 const uint8_t *data;
397 size_t size;
398 char *title;
399
400 /* check image header is valid and get width/height */
401 data = content__get_source_data(c, &size);
402
403 cinfo.err = jpeg_std_error(&jerr);
404 jerr.error_exit = nsjpeg_error_exit;
405 jerr.output_message = nsjpeg_error_log;
406
407 if (setjmp(setjmp_buffer)) {
408 jpeg_destroy_decompress(&cinfo);
409
413 return false;
414 }
415
416 cinfo.client_data = &setjmp_buffer;
417 jpeg_create_decompress(&cinfo);
418 source_mgr.next_input_byte = (unsigned char *) data;
419 source_mgr.bytes_in_buffer = size;
420 cinfo.src = &source_mgr;
421 jpeg_read_header(&cinfo, TRUE);
422 cinfo.out_color_space = JCS_RGB;
423 cinfo.dct_method = JDCT_ISLOW;
424
425 jpeg_calc_output_dimensions(&cinfo);
426
427 c->width = cinfo.output_width;
428 c->height = cinfo.output_height;
429 c->size = c->width * c->height * 4;
430
431 jpeg_destroy_decompress(&cinfo);
432
434
435 /* set title text */
436 title = messages_get_buff("JPEGTitle",
438 c->width, c->height);
439 if (title != NULL) {
441 free(title);
442 }
443
446 content_set_status(c, ""); /* Done: update status bar */
447
448 return true;
449}
450
451
452
453/**
454 * Clone content.
455 */
456static nserror nsjpeg_clone(const struct content *old, struct content **newc)
457{
458 struct content *jpeg_c;
459 nserror error;
460
461 jpeg_c = calloc(1, sizeof(struct content));
462 if (jpeg_c == NULL)
463 return NSERROR_NOMEM;
464
465 error = content__clone(old, jpeg_c);
466 if (error != NSERROR_OK) {
467 content_destroy(jpeg_c);
468 return error;
469 }
470
471 /* re-convert if the content is ready */
472 if ((old->status == CONTENT_STATUS_READY) ||
473 (old->status == CONTENT_STATUS_DONE)) {
474 if (nsjpeg_convert(jpeg_c) == false) {
475 content_destroy(jpeg_c);
477 }
478 }
479
480 *newc = jpeg_c;
481
482 return NSERROR_OK;
483}
484
487 .data_complete = nsjpeg_convert,
488 .destroy = image_cache_destroy,
489 .redraw = image_cache_redraw,
490 .clone = nsjpeg_clone,
491 .get_internal = image_cache_get_internal,
493 .is_opaque = image_cache_is_opaque,
494 .no_share = false,
495};
496
497static const char *nsjpeg_types[] = {
498 "image/jpeg",
499 "image/jpg",
500 "image/pjpeg"
501};
502
Content handling interface.
void content_destroy(struct content *c)
Destroy and free a content.
Definition: content.c:354
void content_broadcast(struct content *c, content_msg msg, const union content_msg_data *data)
Send a message to all users.
Definition: content.c:752
void content_set_done(struct content *c)
Put a content in status CONTENT_STATUS_DONE.
Definition: content.c:299
bool content__set_title(struct content *c, const char *title)
Set title associated with content.
Definition: content.c:1090
nserror content__init(struct content *c, const content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks)
Definition: content.c:190
const uint8_t * content__get_source_data(struct content *c, size_t *size)
Retrieve source of content.
Definition: content.c:1216
nserror content__clone(const struct content *c, struct content *nc)
Clone a content's data members.
Definition: content.c:1382
void content_set_ready(struct content *c)
Put a content in status CONTENT_STATUS_READY and unlock the content.
Definition: content.c:285
void content_set_status(struct content *c, const char *status_message)
Updates content with new status.
Definition: content.c:270
Protected interface to Content handling.
@ CONTENT_STATUS_READY
Some parts of content still being loaded, but can be displayed.
Definition: content_type.h:92
@ CONTENT_STATUS_DONE
Content has completed all processing.
Definition: content_type.h:95
@ CONTENT_MSG_ERROR
error occurred
Definition: content_type.h:122
Internal core bitmap interface.
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_UNKNOWN
Unknown error - DO NOT USE.
Definition: errors.h:31
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_CLONE_FAILED
Failed to clone handle.
Definition: errors.h:37
@ NSERROR_OK
No error.
Definition: errors.h:30
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
content_type image_cache_content_type(void)
Definition: image_cache.c:873
void * image_cache_get_internal(const struct content *c, void *context)
Definition: image_cache.c:856
bool image_cache_redraw(struct content *c, struct content_redraw_data *data, const struct rect *clip, const struct redraw_context *ctx)
Generic content redraw callback.
Definition: image_cache.c:798
void image_cache_destroy(struct content *content)
Definition: image_cache.c:841
nserror image_cache_add(struct content *content, struct bitmap *bitmap, image_cache_convert_fn *convert)
adds an image content to be cached.
Definition: image_cache.c:510
bool image_cache_is_opaque(struct content *c)
Definition: image_cache.c:862
The image content handler intermediate image cache.
Generic bitmap handling interface.
@ BITMAP_OPAQUE
image is opaque
Definition: bitmap.h:38
bitmap_layout
NetSurf bitmap pixel layout.
Definition: bitmap.h:48
@ BITMAP_LAYOUT_B8G8R8A8
Bite-wise BGRA: Byte order: 0xBB, 0xGG, 0xRR, 0xAA.
Definition: bitmap.h:53
@ BITMAP_LAYOUT_R8G8B8A8
Bite-wise RGBA: Byte order: 0xRR, 0xGG, 0xBB, 0xAA.
Definition: bitmap.h:50
@ BITMAP_LAYOUT_A8B8G8R8
Bite-wise ABGR: Byte order: 0xAA, 0xBB, 0xGG, 0xRR.
Definition: bitmap.h:59
@ BITMAP_LAYOUT_A8R8G8B8
Bite-wise ARGB: Byte order: 0xAA, 0xRR, 0xGG, 0xBB.
Definition: bitmap.h:56
CONTENT_FACTORY_REGISTER_TYPES(nsjpeg, nsjpeg_types, nsjpeg_content_handler)
static nserror nsjpeg_create(const content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c)
Content create entry point.
Definition: jpeg.c:64
static void nsjpeg_term_source(j_decompress_ptr cinfo)
JPEG data source manager: terminate source.
Definition: jpeg.c:129
static struct bitmap * jpeg_cache_convert(struct content *c)
create a bitmap from jpeg content.
Definition: jpeg.c:257
static nserror nsjpeg_clone(const struct content *old, struct content **newc)
Clone content.
Definition: jpeg.c:456
static const content_handler nsjpeg_content_handler
Definition: jpeg.c:485
static void nsjpeg__decode_client_fmt(struct jpeg_decompress_struct *cinfo, uint8_t *volatile pixels, size_t rowstride)
Convert scan lines from CMYK to core client bitmap layout.
Definition: jpeg.c:239
#define DIV255(x)
static void nsjpeg__decode_rgb(struct jpeg_decompress_struct *cinfo, uint8_t *volatile pixels, size_t rowstride)
Convert scan lines from CMYK to core client bitmap layout.
Definition: jpeg.c:204
static bool nsjpeg_convert(struct content *c)
Convert a CONTENT_JPEG for display.
Definition: jpeg.c:386
static void nsjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
JPEG data source manager: skip num_bytes worth of data.
Definition: jpeg.c:114
static void nsjpeg_init_source(j_decompress_ptr cinfo)
JPEG data source manager: initialize source.
Definition: jpeg.c:91
static void nsjpeg_error_exit(j_common_ptr cinfo)
Fatal error handler for JPEG library.
Definition: jpeg.c:153
static char nsjpeg_error_buffer[JMSG_LENGTH_MAX]
Definition: jpeg.c:57
static const char * nsjpeg_types[]
Definition: jpeg.c:497
static void nsjpeg_error_log(j_common_ptr cinfo)
Error output handler for JPEG library.
Definition: jpeg.c:141
static unsigned char nsjpeg_eoi[]
Definition: jpeg.c:59
#define MIN_JPEG_SIZE
absolute minimum size of a jpeg below which it is not even worth trying to read header data
Definition: jpeg.c:51
static boolean nsjpeg_fill_input_buffer(j_decompress_ptr cinfo)
JPEG data source manager: fill the input buffer.
Definition: jpeg.c:102
static void nsjpeg__decode_cmyk(struct jpeg_decompress_struct *cinfo, uint8_t *volatile pixels, size_t rowstride)
Convert scan lines from CMYK to core client bitmap layout.
Definition: jpeg.c:166
Content for image/jpeg (interface).
static struct llcache_s * llcache
low level cache state
Definition: llcache.c:267
nsurl * llcache_handle_get_url(const llcache_handle *handle)
Retrieve the post-redirect URL of a low-level cache object.
Definition: llcache.c:4195
Low-level resource cache (interface)
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
char * messages_get_buff(const char *key,...)
Formatted message from a key in the global message hash.
Definition: messages.c:205
Localised message support (interface).
const char * nsurl_access_leaf(const nsurl *url)
Access a URL's path leaf as a string.
int width
Definition: gui.c:160
Bitmap format specifier.
Definition: bitmap.h:95
enum bitmap_layout layout
Colour component layout.
Definition: bitmap.h:96
RISC OS wimp toolkit bitmap.
Definition: bitmap.c:68
size_t rowstride
Definition: bitmap.h:65
Content operation function table.
nserror(* create)(const struct content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, struct llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c)
Content which corresponds to a single URL.
bool quirks
Content is in quirks mode.
int height
Height dimension, if applicable.
char * fallback_charset
Fallback charset, or NULL.
int width
Width dimension, if applicable.
const struct content_handler * handler
Handler for content.
struct llcache_handle * llcache
Low-level cache object.
content_status status
Current status.
unsigned int size
Estimated size of all data associated with this content.
void(* destroy)(void *bitmap)
Destroy a bitmap.
Definition: bitmap.h:143
void *(* create)(int width, int height, enum gui_bitmap_flags flags)
Create a new bitmap.
Definition: bitmap.h:136
size_t(* get_rowstride)(void *bitmap)
Get the number of bytes per row of the image.
Definition: bitmap.h:177
void(* modified)(void *bitmap)
Marks a bitmap as modified.
Definition: bitmap.h:200
unsigned char *(* get_buffer)(void *bitmap)
Get the image buffer from a bitmap.
Definition: bitmap.h:169
Representation of an HTTP parameter.
Definition: parameter.c:31
Handle to low-level cache object.
Definition: llcache.c:76
struct gui_bitmap_table * bitmap
Bitmap table.
Definition: gui_table.h:153
Extra data for some content_msg messages.
Definition: content.h:60
struct content_msg_data::@99 errordata
CONTENT_MSG_ERROR - Error from content or underlying fetch.
nserror errorcode
The error code to convey meaning.
Definition: content.h:88
const char * title
Definition: content.h:186
const char * errormsg
The message.
Definition: content.h:95
Interface to a number of general purpose functionality.
static nserror bitmap(const struct redraw_context *ctx, struct bitmap *bitmap, int x, int y, int width, int height, colour bg, bitmap_flags_t flags)
Plot a bitmap.
Definition: plot.c:857