Coverage Report

Created: 2024-05-20 06:23

/src/mupdf/source/fitz/output-pnm.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2023 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
23
#include "mupdf/fitz.h"
24
25
/*
26
 * Write pixmap to PNM file (without alpha channel)
27
 */
28
static void
29
pnm_write_header(fz_context *ctx, fz_band_writer *writer, fz_colorspace *cs)
30
0
{
31
0
  fz_output *out = writer->out;
32
0
  int w = writer->w;
33
0
  int h = writer->h;
34
0
  int n = writer->n;
35
0
  int alpha = writer->alpha;
36
37
0
  if (writer->s != 0)
38
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "PNM writer cannot cope with spot colors");
39
0
  if (cs && !fz_colorspace_is_gray(ctx, cs) && !fz_colorspace_is_rgb(ctx, cs))
40
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be grayscale or rgb to write as pnm");
41
42
  /* Treat alpha only as greyscale */
43
0
  if (n == 1 && alpha)
44
0
    alpha = 0;
45
0
  n -= alpha;
46
47
0
  if (alpha)
48
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "PNM writer cannot cope with alpha");
49
50
0
  if (n == 1)
51
0
    fz_write_printf(ctx, out, "P5\n");
52
0
  if (n == 3)
53
0
    fz_write_printf(ctx, out, "P6\n");
54
0
  fz_write_printf(ctx, out, "%d %d\n", w, h);
55
0
  fz_write_printf(ctx, out, "255\n");
56
0
}
57
58
static void
59
pnm_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_start, int band_height, const unsigned char *p)
60
0
{
61
0
  fz_output *out = writer->out;
62
0
  int w = writer->w;
63
0
  int h = writer->h;
64
0
  int n = writer->n;
65
0
  int len;
66
0
  int end = band_start + band_height;
67
68
0
  if (n != 1 && n != 3)
69
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be grayscale or rgb to write as pnm");
70
71
0
  if (!out)
72
0
    return;
73
74
0
  if (end > h)
75
0
    end = h;
76
0
  end -= band_start;
77
78
  /* Tests show that writing single bytes out at a time
79
   * is appallingly slow. We get a huge improvement
80
   * by collating stuff into buffers first. */
81
82
0
  while (end--)
83
0
  {
84
0
    len = w;
85
0
    while (len)
86
0
    {
87
0
      int num_written = len;
88
89
0
      switch (n)
90
0
      {
91
0
      case 1:
92
        /* No collation required */
93
0
        fz_write_data(ctx, out, p, num_written);
94
0
        p += num_written;
95
0
        break;
96
0
      case 3:
97
0
        fz_write_data(ctx, out, p, num_written*3);
98
0
        p += num_written*3;
99
0
        break;
100
0
      }
101
0
      len -= num_written;
102
0
    }
103
0
    p += stride - w*n;
104
0
  }
105
0
}
106
107
fz_band_writer *fz_new_pnm_band_writer(fz_context *ctx, fz_output *out)
108
0
{
109
0
  fz_band_writer *writer = fz_new_band_writer(ctx, fz_band_writer, out);
110
111
0
  writer->header = pnm_write_header;
112
0
  writer->band = pnm_write_band;
113
114
0
  return writer;
115
0
}
116
117
void
118
fz_write_pixmap_as_pnm(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
119
0
{
120
0
  fz_band_writer *writer = fz_new_pnm_band_writer(ctx, out);
121
0
  fz_try(ctx)
122
0
  {
123
0
    fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
124
0
    fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
125
0
    fz_close_band_writer(ctx, writer);
126
0
  }
127
0
  fz_always(ctx)
128
0
    fz_drop_band_writer(ctx, writer);
129
0
  fz_catch(ctx)
130
0
    fz_rethrow(ctx);
131
0
}
132
133
void
134
fz_save_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
135
0
{
136
0
  fz_band_writer *writer = NULL;
137
0
  fz_output *out = fz_new_output_with_path(ctx, filename, 0);
138
139
0
  fz_var(writer);
140
141
0
  fz_try(ctx)
142
0
  {
143
0
    writer = fz_new_pnm_band_writer(ctx, out);
144
0
    fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
145
0
    fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
146
0
    fz_close_band_writer(ctx, writer);
147
0
    fz_close_output(ctx, out);
148
0
  }
149
0
  fz_always(ctx)
150
0
  {
151
0
    fz_drop_band_writer(ctx, writer);
152
0
    fz_drop_output(ctx, out);
153
0
  }
154
0
  fz_catch(ctx)
155
0
    fz_rethrow(ctx);
156
0
}
157
158
/*
159
 * Write pixmap to PAM file (with or without alpha channel)
160
 */
161
162
static void
163
pam_write_header(fz_context *ctx, fz_band_writer *writer, fz_colorspace *cs)
164
0
{
165
0
  fz_output *out = writer->out;
166
0
  int w = writer->w;
167
0
  int h = writer->h;
168
0
  int n = writer->n;
169
0
  int alpha = writer->alpha;
170
171
0
  if (writer->s != 0)
172
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "PAM writer cannot cope with spot colors");
173
174
0
  fz_write_printf(ctx, out, "P7\n");
175
0
  fz_write_printf(ctx, out, "WIDTH %d\n", w);
176
0
  fz_write_printf(ctx, out, "HEIGHT %d\n", h);
177
0
  fz_write_printf(ctx, out, "DEPTH %d\n", n);
178
0
  fz_write_printf(ctx, out, "MAXVAL 255\n");
179
180
0
  n -= alpha;
181
182
0
  if (n == 0 && alpha) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE\n");
183
0
  else if (n == 1 && !alpha && fz_colorspace_is_gray(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE\n");
184
0
  else if (n == 1 && alpha && fz_colorspace_is_gray(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE_ALPHA\n");
185
0
  else if (n == 3 && !alpha && fz_colorspace_is_rgb(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE RGB\n");
186
0
  else if (n == 3 && alpha && fz_colorspace_is_rgb(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE RGB_ALPHA\n");
187
0
  else if (n == 4 && !alpha && fz_colorspace_is_cmyk(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE CMYK\n");
188
0
  else if (n == 4 && alpha && fz_colorspace_is_cmyk(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE CMYK_ALPHA\n");
189
0
  else
190
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be alpha only, gray, rgb, or cmyk");
191
0
  fz_write_printf(ctx, out, "ENDHDR\n");
192
0
}
193
194
static void
195
pam_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_start, int band_height, const unsigned char *sp)
196
0
{
197
0
  fz_output *out = writer->out;
198
0
  int w = writer->w;
199
0
  int h = writer->h;
200
0
  int n = writer->n;
201
0
  int alpha = writer->alpha;
202
0
  int x, y;
203
0
  int end = band_start + band_height;
204
205
0
  if (!out)
206
0
    return;
207
208
0
  if (end > h)
209
0
    end = h;
210
0
  end -= band_start;
211
212
0
  if (alpha)
213
0
  {
214
    /* Buffer must be a multiple of 2, 3 and 5 at least. */
215
    /* Also, for the generic case, it must be bigger than FZ_MAX_COLORS */
216
0
    char buffer[2*3*4*5*6];
217
0
    char *b = buffer;
218
0
    stride -= n * w;
219
0
    switch (n)
220
0
    {
221
0
    case 2:
222
0
      for (y = 0; y < end; y++)
223
0
      {
224
0
        for (x = 0; x < w; x++)
225
0
        {
226
0
          int a = sp[1];
227
0
          *b++ = a ? (sp[0] * 255 + (a>>1))/a : 0;
228
0
          *b++ = a;
229
0
          sp += 2;
230
0
          if (b == &buffer[sizeof(buffer)])
231
0
          {
232
0
            fz_write_data(ctx, out, buffer, sizeof(buffer));
233
0
            b = buffer;
234
0
          }
235
0
        }
236
0
        sp += stride;
237
0
      }
238
0
      if (b != buffer)
239
0
        fz_write_data(ctx, out, buffer, b - buffer);
240
0
      break;
241
0
    case 4:
242
0
      for (y = 0; y < end; y++)
243
0
      {
244
0
        for (x = 0; x < w; x++)
245
0
        {
246
0
          int a = sp[3];
247
0
          int inva = a ? 256 * 255 / a : 0;
248
0
          *b++ = (sp[0] * inva + 128)>>8;
249
0
          *b++ = (sp[1] * inva + 128)>>8;
250
0
          *b++ = (sp[2] * inva + 128)>>8;
251
0
          *b++ = a;
252
0
          sp += 4;
253
0
          if (b == &buffer[sizeof(buffer)])
254
0
          {
255
0
            fz_write_data(ctx, out, buffer, sizeof(buffer));
256
0
            b = buffer;
257
0
          }
258
0
        }
259
0
        sp += stride;
260
0
      }
261
0
      if (b != buffer)
262
0
        fz_write_data(ctx, out, buffer, b - buffer);
263
0
      break;
264
0
    case 5:
265
0
      for (y = 0; y < end; y++)
266
0
      {
267
0
        for (x = 0; x < w; x++)
268
0
        {
269
0
          int a = sp[4];
270
0
          int inva = a ? 256 * 255 / a : 0;
271
0
          *b++ = (sp[0] * inva + 128)>>8;
272
0
          *b++ = (sp[1] * inva + 128)>>8;
273
0
          *b++ = (sp[2] * inva + 128)>>8;
274
0
          *b++ = (sp[3] * inva + 128)>>8;
275
0
          *b++ = a;
276
0
          sp += 5;
277
0
          if (b == &buffer[sizeof(buffer)])
278
0
          {
279
0
            fz_write_data(ctx, out, buffer, sizeof(buffer));
280
0
            b = buffer;
281
0
          }
282
0
        }
283
0
        sp += stride;
284
0
      }
285
0
      if (b != buffer)
286
0
        fz_write_data(ctx, out, buffer, b - buffer);
287
0
      break;
288
0
    default:
289
0
      for (y = 0; y < end; y++)
290
0
      {
291
0
        for (x = 0; x < w; x++)
292
0
        {
293
0
          int a = sp[n-1];
294
0
          int inva = a ? 256 * 255 / a : 0;
295
0
          int k;
296
0
          for (k = 0; k < n-1; k++)
297
0
            *b++ = (*sp++ * inva + 128)>>8;
298
0
          *b++ = a;
299
0
          sp++;
300
0
          if (b >= &buffer[sizeof(buffer)] - n)
301
0
          {
302
0
            fz_write_data(ctx, out, buffer, b - buffer);
303
0
            b = buffer;
304
0
          }
305
0
        }
306
0
        sp += stride;
307
0
      }
308
0
      if (b != buffer)
309
0
        fz_write_data(ctx, out, buffer, b - buffer);
310
0
      break;
311
0
    }
312
0
  }
313
0
  else
314
0
    for (y = 0; y < end; y++)
315
0
    {
316
0
      fz_write_data(ctx, out, sp, (size_t)w * n);
317
0
      sp += stride;
318
0
    }
319
0
}
320
321
fz_band_writer *fz_new_pam_band_writer(fz_context *ctx, fz_output *out)
322
0
{
323
0
  fz_band_writer *writer = fz_new_band_writer(ctx, fz_band_writer, out);
324
325
0
  writer->header = pam_write_header;
326
0
  writer->band = pam_write_band;
327
328
0
  return writer;
329
0
}
330
331
void
332
fz_write_pixmap_as_pam(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
333
0
{
334
0
  fz_band_writer *writer = fz_new_pam_band_writer(ctx, out);
335
0
  fz_try(ctx)
336
0
  {
337
0
    fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
338
0
    fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
339
0
    fz_close_band_writer(ctx, writer);
340
0
  }
341
0
  fz_always(ctx)
342
0
    fz_drop_band_writer(ctx, writer);
343
0
  fz_catch(ctx)
344
0
    fz_rethrow(ctx);
345
0
}
346
347
void
348
fz_save_pixmap_as_pam(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
349
0
{
350
0
  fz_band_writer *writer = NULL;
351
0
  fz_output *out = fz_new_output_with_path(ctx, filename, 0);
352
353
0
  fz_var(writer);
354
355
0
  fz_try(ctx)
356
0
  {
357
0
    writer = fz_new_pam_band_writer(ctx, out);
358
0
    fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
359
0
    fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
360
0
    fz_close_band_writer(ctx, writer);
361
0
    fz_close_output(ctx, out);
362
0
  }
363
0
  fz_always(ctx)
364
0
  {
365
0
    fz_drop_band_writer(ctx, writer);
366
0
    fz_drop_output(ctx, out);
367
0
  }
368
0
  fz_catch(ctx)
369
0
    fz_rethrow(ctx);
370
0
}
371
372
static fz_buffer *
373
buffer_from_pixmap(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params, int drop,
374
  void (*do_write)(fz_context *ctx, fz_output *out, fz_pixmap *pix))
375
0
{
376
0
  fz_buffer *buf = NULL;
377
0
  fz_output *out = NULL;
378
379
0
  fz_var(buf);
380
0
  fz_var(out);
381
382
0
  fz_try(ctx)
383
0
  {
384
0
    buf = fz_new_buffer(ctx, 1024);
385
0
    out = fz_new_output_with_buffer(ctx, buf);
386
0
    do_write(ctx, out, pix);
387
0
    fz_close_output(ctx, out);
388
0
  }
389
0
  fz_always(ctx)
390
0
  {
391
0
    if (drop)
392
0
      fz_drop_pixmap(ctx, pix);
393
0
    fz_drop_output(ctx, out);
394
0
  }
395
0
  fz_catch(ctx)
396
0
  {
397
0
    fz_drop_buffer(ctx, buf);
398
0
    fz_rethrow(ctx);
399
0
  }
400
0
  return buf;
401
0
}
402
403
fz_buffer *
404
fz_new_buffer_from_image_as_pnm(fz_context *ctx, fz_image *image, fz_color_params color_params)
405
0
{
406
0
  fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
407
0
  return buffer_from_pixmap(ctx, pix, color_params, 1, fz_write_pixmap_as_pnm);
408
0
}
409
410
fz_buffer *
411
fz_new_buffer_from_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
412
0
{
413
0
  return buffer_from_pixmap(ctx, pix, color_params, 0, fz_write_pixmap_as_pnm);
414
0
}
415
416
fz_buffer *
417
fz_new_buffer_from_image_as_pam(fz_context *ctx, fz_image *image, fz_color_params color_params)
418
0
{
419
0
  fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
420
0
  return buffer_from_pixmap(ctx, pix, color_params, 1, fz_write_pixmap_as_pam);
421
0
}
422
423
fz_buffer *
424
fz_new_buffer_from_pixmap_as_pam(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
425
0
{
426
0
  return buffer_from_pixmap(ctx, pix, color_params, 0, fz_write_pixmap_as_pam);
427
0
}