Coverage Report

Created: 2026-06-22 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/splash/SplashBitmap.cc
Line
Count
Source
1
//========================================================================
2
//
3
// SplashBitmap.cc
4
//
5
// Copyright 2003-2013 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdio.h>
12
#include <limits.h>
13
#include "gmem.h"
14
#include "gmempp.h"
15
#include "gfile.h"
16
#include "Trace.h"
17
#include "SplashErrorCodes.h"
18
#include "SplashBitmap.h"
19
20
//------------------------------------------------------------------------
21
// SplashBitmapMemCache
22
//------------------------------------------------------------------------
23
24
65.9k
#define splashBitmapMemCacheThreshold 4000000
25
26
10.2k
SplashBitmapMemCache::SplashBitmapMemCache() {
27
51.4k
  for (int i = 0; i < splashBitmapMemCacheSize; ++i) {
28
41.1k
    cache[i].data = NULL;
29
41.1k
    cache[i].height = 0;
30
41.1k
    cache[i].rowSize = 0;
31
41.1k
  }
32
10.2k
}
33
34
10.2k
SplashBitmapMemCache::~SplashBitmapMemCache() {
35
51.3k
  for (int i = 0; i < splashBitmapMemCacheSize; ++i) {
36
41.0k
    if (cache[i].data) {
37
49
      gfree(cache[i].data);
38
49
      cache[i].data = NULL;
39
49
      cache[i].height = 0;
40
49
      cache[i].rowSize = 0;
41
49
    }
42
41.0k
  }
43
10.2k
}
44
45
33.0k
void *SplashBitmapMemCache::alloc(int height, SplashBitmapRowSize rowSize) {
46
  // if the block is small, just allocate it
47
33.0k
  if (rowSize < splashBitmapMemCacheThreshold / height) {
48
32.8k
    return gmallocn64(height, rowSize);
49
32.8k
  }
50
51
  // if a match is found in the cache, return it
52
452
  for (int i = splashBitmapMemCacheSize - 1; i >= 0; --i) {
53
384
    if (cache[i].data &&
54
98
  cache[i].height == height &&
55
90
  cache[i].rowSize == rowSize) {
56
64
      void *data = cache[i].data;
57
112
      for (int j = i; j < splashBitmapMemCacheSize - 1; ++j) {
58
48
  cache[j] = cache[j + 1];
59
48
      }
60
64
      cache[splashBitmapMemCacheSize - 1].data = NULL;
61
64
      cache[splashBitmapMemCacheSize - 1].height = 0;
62
64
      cache[splashBitmapMemCacheSize - 1].rowSize = 0;
63
64
      return data;
64
64
    }
65
384
  }
66
67
  // if no match was found, clear the cache and allcoate the block
68
340
  for (int i = 0; i < splashBitmapMemCacheSize; ++i) {
69
272
    if (cache[i].data) {
70
10
      gfree(cache[i].data);
71
10
      cache[i].data = NULL;
72
10
      cache[i].height = 0;
73
10
      cache[i].rowSize = 0;
74
10
    }
75
272
  }
76
68
  return gmallocn64(height, rowSize);
77
132
}
78
79
void SplashBitmapMemCache::free(void *data, int height,
80
32.9k
        SplashBitmapRowSize rowSize) {
81
32.9k
  if (rowSize < splashBitmapMemCacheThreshold / height) {
82
32.8k
    gfree(data);
83
32.8k
    return;
84
32.8k
  }
85
123
  if (cache[0].data) {
86
0
    gfree(cache[0].data);
87
0
  }
88
492
  for (int i = 0; i < splashBitmapMemCacheSize - 1; ++i) {
89
369
    cache[i] = cache[i + 1];
90
369
  }
91
123
  cache[splashBitmapMemCacheSize - 1].data = data;
92
123
  cache[splashBitmapMemCacheSize - 1].height = height;
93
123
  cache[splashBitmapMemCacheSize - 1].rowSize = rowSize;
94
123
}
95
96
//------------------------------------------------------------------------
97
// SplashBitmap
98
//------------------------------------------------------------------------
99
100
SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,
101
         SplashColorMode modeA, GBool alphaA,
102
18.1k
         GBool topDown, SplashBitmapMemCache *cacheA) {
103
  // NB: this code checks that rowSize fits in a signed 32-bit
104
  // integer, because some code (outside this class) makes that
105
  // assumption
106
18.1k
  width = widthA;
107
18.1k
  height = heightA;
108
18.1k
  mode = modeA;
109
18.1k
  rowSize = 0; // make gcc happy
110
18.1k
  switch (mode) {
111
65
  case splashModeMono1:
112
65
    if (width <= 0) {
113
0
      gMemError("invalid bitmap width");
114
0
    }
115
65
    rowSize = (width + 7) >> 3;
116
65
    break;
117
3.91k
  case splashModeMono8:
118
3.91k
    if (width <= 0) {
119
0
      gMemError("invalid bitmap width");
120
0
    }
121
3.91k
    rowSize = width;
122
3.91k
    break;
123
13.9k
  case splashModeRGB8:
124
13.9k
  case splashModeBGR8:
125
13.9k
    if (width <= 0 || width > INT_MAX / 3) {
126
7
      gMemError("invalid bitmap width");
127
7
    }
128
13.9k
    rowSize = (SplashBitmapRowSize)width * 3;
129
13.9k
    break;
130
0
#if SPLASH_CMYK
131
163
  case splashModeCMYK8:
132
163
    if (width <= 0 || width > INT_MAX / 4) {
133
0
      gMemError("invalid bitmap width");
134
0
    }
135
163
    rowSize = (SplashBitmapRowSize)width * 4;
136
163
    break;
137
18.1k
#endif
138
18.1k
  }
139
18.0k
  rowSize += rowPad - 1;
140
18.0k
  rowSize -= rowSize % rowPad;
141
142
18.0k
  traceAlloc(this, "alloc bitmap: %d x %d x %d %s -> %lld bytes",
143
18.0k
       width, height, splashColorModeNComps[mode],
144
18.0k
       alphaA ? "with alpha" : "without alpha",
145
18.0k
       height * rowSize + (alphaA ? height * width : 0));
146
147
18.0k
  cache = cacheA;
148
18.0k
  if (cache) {
149
18.0k
    data = (SplashColorPtr)cache->alloc(height, rowSize);
150
18.0k
  } else {
151
0
    data = (SplashColorPtr)gmallocn64(height, rowSize);
152
0
  }
153
18.0k
  if (!topDown) {
154
0
    data += (height - 1) * rowSize;
155
0
    rowSize = -rowSize;
156
0
  }
157
18.0k
  if (alphaA) {
158
14.8k
    alphaRowSize = width;
159
14.8k
    if (cache) {
160
14.8k
      alpha = (Guchar *)cache->alloc(height, alphaRowSize);
161
14.8k
    } else {
162
0
      alpha = (Guchar *)gmallocn64(height, alphaRowSize);
163
0
    }
164
14.8k
  } else {
165
3.22k
    alphaRowSize = 0;
166
3.22k
    alpha = NULL;
167
3.22k
  }
168
18.0k
}
169
170
18.0k
SplashBitmap::~SplashBitmap() {
171
18.0k
  traceFree(this, "free bitmap");
172
18.0k
  if (data && rowSize < 0) {
173
0
    rowSize = -rowSize;
174
0
    data -= (height - 1) * rowSize;
175
0
  }
176
18.0k
  if (cache) {
177
18.0k
    cache->free(data, height, rowSize);
178
18.0k
    if (alpha) {
179
14.8k
      cache->free(alpha, height, alphaRowSize);
180
14.8k
    }
181
18.0k
  } else {
182
0
    gfree(data);
183
0
    gfree(alpha);
184
0
  }
185
18.0k
}
186
187
0
SplashError SplashBitmap::writePNMFile(char *fileName) {
188
0
  FILE *f;
189
0
  SplashError err;
190
191
0
  if (!(f = openFile(fileName, "wb"))) {
192
0
    return splashErrOpenFile;
193
0
  }
194
0
  err = writePNMFile(f);
195
0
  fclose(f);
196
0
  return err;
197
0
}
198
199
0
SplashError SplashBitmap::writePNMFile(FILE *f) {
200
0
  SplashColorPtr row, p;
201
0
  int x, y;
202
203
0
  switch (mode) {
204
205
0
  case splashModeMono1:
206
0
    fprintf(f, "P4\n%d %d\n", width, height);
207
0
    row = data;
208
0
    for (y = 0; y < height; ++y) {
209
0
      p = row;
210
0
      for (x = 0; x < width; x += 8) {
211
0
  fputc(*p ^ 0xff, f);
212
0
  ++p;
213
0
      }
214
0
      row += rowSize;
215
0
    }
216
0
    break;
217
218
0
  case splashModeMono8:
219
0
    fprintf(f, "P5\n%d %d\n255\n", width, height);
220
0
    row = data;
221
0
    for (y = 0; y < height; ++y) {
222
0
      fwrite(row, 1, width, f);
223
0
      row += rowSize;
224
0
    }
225
0
    break;
226
227
0
  case splashModeRGB8:
228
0
    fprintf(f, "P6\n%d %d\n255\n", width, height);
229
0
    row = data;
230
0
    for (y = 0; y < height; ++y) {
231
0
      fwrite(row, 1, 3 * width, f);
232
0
      row += rowSize;
233
0
    }
234
0
    break;
235
236
0
  case splashModeBGR8:
237
0
    fprintf(f, "P6\n%d %d\n255\n", width, height);
238
0
    row = data;
239
0
    for (y = 0; y < height; ++y) {
240
0
      p = row;
241
0
      for (x = 0; x < width; ++x) {
242
0
  fputc(splashBGR8R(p), f);
243
0
  fputc(splashBGR8G(p), f);
244
0
  fputc(splashBGR8B(p), f);
245
0
  p += 3;
246
0
      }
247
0
      row += rowSize;
248
0
    }
249
0
    break;
250
251
0
#if SPLASH_CMYK
252
0
  case splashModeCMYK8:
253
0
    fprintf(f, "P7\n");
254
0
    fprintf(f, "WIDTH %d\n", width);
255
0
    fprintf(f, "HEIGHT %d\n", height);
256
0
    fprintf(f, "DEPTH 4\n");
257
0
    fprintf(f, "MAXVAL 255\n");
258
0
    fprintf(f, "TUPLTYPE CMYK\n");
259
0
    fprintf(f, "ENDHDR\n");
260
0
    row = data;
261
0
    for (y = 0; y < height; ++y) {
262
0
      fwrite(row, 1, 4 * width, f);
263
0
      row += rowSize;
264
0
    }
265
0
    break;
266
0
#endif
267
268
0
  }
269
270
0
  return splashOk;
271
0
}
272
273
0
SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) {
274
0
  FILE *f;
275
276
0
  if (!alpha) {
277
0
    return splashErrModeMismatch;
278
0
  }
279
0
  if (!(f = openFile(fileName, "wb"))) {
280
0
    return splashErrOpenFile;
281
0
  }
282
0
  fprintf(f, "P5\n%d %d\n255\n", width, height);
283
0
  fwrite(alpha, 1, width * height, f);
284
0
  fclose(f);
285
0
  return splashOk;
286
0
}
287
288
289
0
void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) {
290
0
  SplashColorPtr p;
291
292
0
  if (y < 0 || y >= height || x < 0 || x >= width) {
293
0
    return;
294
0
  }
295
0
  switch (mode) {
296
0
  case splashModeMono1:
297
0
    p = &data[y * rowSize + (x >> 3)];
298
0
    pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00;
299
0
    break;
300
0
  case splashModeMono8:
301
0
    p = &data[y * rowSize + x];
302
0
    pixel[0] = p[0];
303
0
    break;
304
0
  case splashModeRGB8:
305
0
    p = &data[y * rowSize + 3 * x];
306
0
    pixel[0] = p[0];
307
0
    pixel[1] = p[1];
308
0
    pixel[2] = p[2];
309
0
    break;
310
0
  case splashModeBGR8:
311
0
    p = &data[y * rowSize + 3 * x];
312
0
    pixel[0] = p[2];
313
0
    pixel[1] = p[1];
314
0
    pixel[2] = p[0];
315
0
    break;
316
0
#if SPLASH_CMYK
317
0
  case splashModeCMYK8:
318
0
    p = &data[y * rowSize + 4 * x];
319
0
    pixel[0] = p[0];
320
0
    pixel[1] = p[1];
321
0
    pixel[2] = p[2];
322
0
    pixel[3] = p[3];
323
0
    break;
324
0
#endif
325
0
  }
326
0
}
327
328
0
Guchar SplashBitmap::getAlpha(int x, int y) {
329
0
  return alpha[y * (size_t)width + x];
330
0
}
331
332
0
SplashColorPtr SplashBitmap::takeData() {
333
0
  SplashColorPtr data2;
334
335
0
  data2 = data;
336
0
  data = NULL;
337
0
  return data2;
338
0
}
339
340
0
void SplashBitmap::doNotCache() {
341
0
  cache = NULL;
342
0
}
343
344
345
//------------------------------------------------------------------------
346
// SplashAlphaBitmap
347
//------------------------------------------------------------------------
348
349
SplashAlphaBitmap::SplashAlphaBitmap(int widthA, int heightA,
350
41
             SplashBitmapMemCache *cacheA) {
351
41
  width = widthA;
352
41
  height = heightA;
353
41
  alphaRowSize = width;
354
355
41
  traceAlloc(this, "alloc alpha bitmap: %d x %d -> %lld bytes",
356
41
       width, height, height * alphaRowSize);
357
358
41
  cache = cacheA;
359
41
  if (cache) {
360
41
    alpha = (Guchar *)cache->alloc(height, alphaRowSize);
361
41
  } else {
362
0
    alpha = (Guchar *)gmallocn64(height, alphaRowSize);
363
0
  }
364
41
}
365
366
41
SplashAlphaBitmap::~SplashAlphaBitmap() {
367
41
  traceFree(this, "free alpha bitmap");
368
41
  if (cache) {
369
41
    cache->free(alpha, height, alphaRowSize);
370
41
  } else {
371
0
    gfree(alpha);
372
0
  }
373
41
}