1 //
2 // C++ Implementation: pictureLoader
3 //
4 // Description:
5 //
6 //
7 // Author: Yorn <yorn@gmx.net>, (C) 2009
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12 #include "pictureLoader.h"
13
14 #include <iostream>
15 #include <cstring>
16 #include <errno.h>
17 #include "exception.h"
18 #include "log.h"
19
20 #define SCALEBITS 8
21 #define ONE_HALF (1 << (SCALEBITS - 1))
22 #define FIX(x) ((int) ((x) * (1L<<SCALEBITS) + 0.5))
23 #define CLAMP255(x) ((unsigned char)((((x)<0)-1)&((x)|-((x)>255))))
24
PictureLoader()25 PictureLoader::PictureLoader()
26 {
27 }
28
29
~PictureLoader()30 PictureLoader::~PictureLoader()
31 {
32 }
33
34 #ifdef WITH_GD2LIB
load(RGBPlane & retPlane,const std::string & filename,uint32 _width,uint32 _height,bool useBiggest)35 bool PictureLoader::load(RGBPlane& retPlane, const std::string& filename, uint32 _width, uint32 _height,
36 bool useBiggest)
37 {
38 SuffixType type = identifySuffix(filename);
39 if (type == suffix_unknown) {
40 logger.error() << "PictureLoader::load: Cannot identify suffix of <"<<filename<<">\n";
41 return(false);
42 }
43
44 gdImagePtr im(0);
45
46 FILE* in;
47 in = fopen(filename.c_str(), "rb");
48
49 if (in == 0) {
50 logger.error() << "PictureLoader::load: Cannot open file <"<<filename<<">: "
51 << strerror(errno) << "\n";
52 return(false);
53 }
54
55 if (type == suffix_jpg) {
56 im = gdImageCreateFromJpeg(in);
57 } else if (type == suffix_png) {
58 im = gdImageCreateFromPng(in);
59 } else if (type == suffix_gif) {
60 im = gdImageCreateFromGif(in);
61 }
62
63 fclose(in);
64
65 if (im == 0) {
66 logger.error() << "PictureLoader::load: Error reading image file <"<<filename<<">\n";
67 return(false);
68 }
69
70 if ((_width != 0) && (_height != 0) && ( _width != gdImageSX(im) ) &&
71 (_height != gdImageSY(im))) {
72
73 uint32 origWidth(gdImageSX(im));
74 uint32 origHeight(gdImageSY(im));
75
76 /* calculate the new size -> picture must fit into the given rectangle */
77 float factorX = (_width*1.0)/(origWidth*1.0);
78 float factorY = (_height*1.0)/(origHeight*1.0);
79 float factor(1.0);
80
81 #ifdef DEBUG
82 logger.debug() << "wanted: "<<_width<<"x"<<_height<<" orig: "
83 <<origWidth<<"x"<<origHeight<<std::endl;
84 #endif
85
86 if (useBiggest) {
87 if (factorX < factorY)
88 factor = factorY;
89 else
90 factor = factorX;
91 } else {
92 if (factorX < factorY)
93 factor = factorX;
94 else
95 factor = factorY;
96 }
97 #ifdef DEBUG
98 logger.debug() << "recalculating ("<<factor<<") image to "
99 <<(uint32) (origWidth*factor+0.5)<< "x"
100 << (uint32) (origHeight*factor+0.5)<<std::endl;
101 #endif
102
103 gdImagePtr resampled = gdImageCreateTrueColor((uint32) (origWidth
104 *factor+0.5), (uint32) (origHeight*factor+0.5));
105
106 if (!resampled) {
107 throw OggException("PictureLoader::load: failed to allocate image buffer\n");
108 }
109
110 gdImageCopyResampled(resampled, im, 0, 0, 0, 0, resampled->sx,
111 resampled->sy, origWidth, origHeight);
112
113 retPlane = convertToRgbPlane(resampled);
114
115 gdImageDestroy(resampled);
116 } else {
117 retPlane = convertToRgbPlane(im);
118 }
119
120 gdImageDestroy(im);
121
122 return (true);
123 }
124
convertToRgbPlane(gdImagePtr im)125 RGBPlane PictureLoader::convertToRgbPlane(gdImagePtr im)
126 {
127
128 uint32 width = gdImageSX(im);
129 uint32 height = gdImageSY(im);
130
131 RGBPlane pic(width, height);
132
133 int c(0);
134 uint32 x(0);
135
136 for (uint32 i(0); i<height; ++i)
137 for (uint32 j(0); j<width; ++j) {
138 c = gdImageGetPixel(im, j, i);
139 pic->plane[x++] = gdImageRed(im, c);
140 pic->plane[x++] = gdImageGreen(im, c);
141 pic->plane[x++] = gdImageBlue(im,c);
142 pic->plane[x++] = gdImageAlpha(im,c);
143 }
144
145 return(pic);
146 }
147
identifySuffix(const std::string & filename)148 PictureLoader::SuffixType PictureLoader::identifySuffix(const std::string& filename)
149 {
150 std::string::size_type suffixStart(filename.find_last_of('.'));
151
152 if (suffixStart == std::string::npos) {
153 return (suffix_unknown);
154 }
155
156 std::string suffix(filename.substr(suffixStart+1));
157
158 if ((suffix == "jpg") || (suffix == "JPG") || (suffix == "jpeg") || (suffix
159 == "JPEG")) {
160 return (suffix_jpg);
161 }
162
163 if ((suffix == "png") || (suffix == "PNG")) {
164 return (suffix_png);
165 }
166
167 if ((suffix == "gif") || (suffix == "GIF")) {
168 return (suffix_gif);
169 }
170
171 return (suffix_unknown);
172
173 }
174
save(RGBPlane & pic,const std::string & filename,uint32 newWidth,uint32 newHeight)175 bool PictureLoader::save(RGBPlane& pic, const std::string& filename, uint32 newWidth,
176 uint32 newHeight)
177 {
178
179 int actColor;
180 int planeCount(0);
181
182 SuffixType type = identifySuffix(filename);
183 if (type == suffix_unknown) {
184 logger.error() << "PictureLoader::identifySuffix: Cannot identify suffix of <"
185 << filename << ">\n";
186 return(false);
187 }
188
189 gdImagePtr im = gdImageCreateTrueColor(pic->width, pic->height);
190
191 for (uint32 i(0); i < pic->height; ++i)
192 for (uint32 j(0); j < pic->width; ++j) {
193 int red = pic->plane[planeCount++];
194 int green = pic->plane[planeCount++];
195 int blue = pic->plane[planeCount++];
196 actColor = gdImageColorAllocate(im, red, green, blue);
197
198 planeCount++; // alpha channel not in use
199
200 gdImageSetPixel(im, j, i, actColor);
201
202 }
203
204 FILE* out = fopen(filename.c_str(), "wb");
205 if (out == 0) {
206 logger.error() << "PictureLoader::save: Cannot open file <"<<filename<<">: "
207 << strerror(errno) << "\n";
208 return(false);
209 }
210
211 if ((newWidth != 0) || (newHeight != 0)) {
212
213 if (newWidth == 0)
214 newWidth = pic->width*newHeight/pic->height;
215
216 if (newHeight == 0)
217 newHeight = pic->height*newWidth/pic->width;
218
219 gdImagePtr resampled;
220 resampled = gdImageCreateTrueColor(newWidth, newHeight);
221 if (!resampled) {
222 throw OggException("PictureLoader::save: failed to allocate image buffer\n");
223 }
224
225 gdImageCopyResampled(resampled, im, 0, 0, 0, 0, resampled->sx,
226 resampled->sy, pic->width, pic->height);
227
228 switch (type) {
229
230 case suffix_jpg:
231 gdImageJpeg(resampled, out, -1);
232 break;
233
234 case suffix_png:
235 gdImagePng(resampled, out);
236 break;
237
238 // case suffix_gif:
239
240 default:
241 logger.error() << "cannot identify suffix\n";
242
243 }
244 /* Write JPEG using default quality */
245 gdImageDestroy(resampled);
246
247 } else {
248
249 switch (type) {
250
251 case suffix_jpg:
252 gdImageJpeg(im, out, -1);
253 break;
254
255 case suffix_png:
256 gdImagePng(im, out);
257 break;
258
259 // case suffix_gif:
260
261 default:
262 logger.error() << "cannot identify suffix\n";
263
264 }
265
266 }
267
268 /* Close file */
269 if (fclose(out) != 0) {
270 logger.error() << "Error writing file <" << filename << ">: "
271 << strerror(errno) << "\n";
272 gdImageDestroy(im);
273 return(false);
274 }
275
276 /* Destroy the image */
277 gdImageDestroy(im);
278
279 return (true);
280 }
281 #endif //WITH_GD2LIB
282
283 #ifdef HAVE_LIBTHEORAENC
284
exportYCrCb_theora(RGBPlane & picture,th_ycbcr_buffer & buffer,int pixel_format)285 void PictureLoader::exportYCrCb_theora(RGBPlane& picture, th_ycbcr_buffer& buffer, int pixel_format )
286 {
287
288 uint32 frameWidth;
289 uint32 frameHeight;
290 uint32 XOffset;
291 uint32 YOffset;
292
293 /* recalculate the buffer (must be multiple of 16) */
294 frameWidth = (picture->width+15)&~0xF;
295 frameHeight = (picture->height+15)&~0xF;
296
297 // We force the offset to be even.
298 // This ensures that the chroma samples align properly with the luma
299 // samples.
300
301 XOffset = ((frameWidth - picture->width)/4); //&~1;
302 YOffset = ((frameHeight - picture->height)/4); //&~1;
303
304 // logger.debug() << width <<" x "<<height<<" "<<frameWidth<<" x "<<frameHeight <<" "<<XOffset<<" "<<YOffset<<std::endl;
305
306 uint32 stride = frameWidth;
307
308 if ((frameWidth != (uint32)buffer[0].width) ||
309 (frameHeight != (uint32)buffer[0].height)) {
310
311 /* delete old planes */
312 delete buffer[0].data;
313 delete buffer[1].data;
314 delete buffer[2].data;
315
316 /* create a new YCbCrPlane */
317 buffer[0].width = frameWidth;
318 buffer[0].height = frameHeight;
319 buffer[0].stride = stride;
320 buffer[0].data = new uint8[frameWidth*frameHeight];
321 // memset(buffer[0].data, 0x00, frameWidth*frameHeight);
322
323 buffer[1].width = frameWidth/2;
324 buffer[1].height = frameHeight/2;
325 buffer[1].stride = stride/2;
326 buffer[1].data = new uint8[frameWidth*frameHeight/4];
327 // memset(buffer[1].data, 0x00, frameWidth*frameHeight/4);
328
329 buffer[2].width = frameWidth/2;
330 buffer[2].height = frameHeight/2;
331 buffer[2].stride = stride/2;
332 buffer[2].data = new uint8[frameWidth*frameHeight/4];
333 // memset(buffer[2].data, 0x00, frameWidth*frameHeight/4);
334
335 }
336
337
338 int wrap, wrap3;
339
340 wrap = stride;
341 wrap3 = picture->width * 4;
342
343 uint32 HeightPrecalculation0x;
344 uint32 HeightPrecalculation1x;
345 uint32 CromaPrecalculation;
346
347 uint32 position00;
348 uint32 position01;
349 uint32 position10;
350 uint32 position11;
351
352 uint32 inPos00;
353 uint32 inPos01;
354 uint32 inPos10;
355 uint32 inPos11;
356
357 uint32 red_sample;
358 uint32 green_sample;
359 uint32 blue_sample;
360
361 uint32 cromaPos;
362
363 for (uint32 i(0); i<(uint32)(picture->height+1)/2; ++i) {
364
365 HeightPrecalculation0x = (2*(i+YOffset))*buffer[0].stride;
366 HeightPrecalculation1x = (2*(i+YOffset)+1)*buffer[0].stride;
367 CromaPrecalculation = (i+YOffset)*buffer[1].stride;
368
369 for (uint32 j(0); j<(uint32)(picture->width+1)/2; ++j) {
370
371 position00 = HeightPrecalculation0x+(2*(j+XOffset));
372 position01 = HeightPrecalculation0x+(2*(j+XOffset)+1);
373 position10 = HeightPrecalculation1x+(2*(j+XOffset));
374 position11 = HeightPrecalculation1x+(2*(j+XOffset)+1);
375
376 inPos00 = 4*((2*i)*picture->width+(2*j));
377 inPos01 = 4*((2*i)*picture->width+(2*j+1));
378 inPos10 = 4*((2*i+1)*picture->width+(2*j));
379 inPos11 = 4*((2*i+1)*picture->width+(2*j+1));
380
381 cromaPos = CromaPrecalculation+(j+XOffset);
382
383
384 buffer[0].data[position00] = (FIX(0.29900) * picture->plane[inPos00]
385 + FIX(0.58700) * picture->plane[inPos00+1]
386 + FIX(0.11400) * picture->plane[inPos00+2]
387 + ONE_HALF) >> SCALEBITS;
388
389 buffer[0].data[position01] = (FIX(0.29900) * picture->plane[inPos01]
390 + FIX(0.58700) * picture->plane[inPos01+1]
391 + FIX(0.11400) * picture->plane[inPos01+2]
392 + ONE_HALF) >> SCALEBITS;
393
394 buffer[0].data[position10] = (FIX(0.29900) * picture->plane[inPos10]
395 + FIX(0.58700) * picture->plane[inPos10+1]
396 + FIX(0.11400) * picture->plane[inPos10+2]
397 + ONE_HALF) >> SCALEBITS;
398
399 buffer[0].data[position11] = (FIX(0.29900) * picture->plane[inPos11]
400 + FIX(0.58700) * picture->plane[inPos11+1]
401 + FIX(0.11400) * picture->plane[inPos11+2]
402 + ONE_HALF) >> SCALEBITS;
403
404 red_sample = picture->plane[inPos00] + picture->plane[inPos01] + picture->plane[inPos10] + picture->plane[inPos11];
405
406 green_sample = picture->plane[inPos00+1] + picture->plane[inPos01+1] + picture->plane[inPos10+1] + picture->plane[inPos11+1];
407
408 blue_sample = picture->plane[inPos00+2] + picture->plane[inPos01+2] + picture->plane[inPos10+2] + picture->plane[inPos11+2];
409
410 buffer[1].data[cromaPos] = ((-FIX(0.16874) * red_sample - FIX(0.33126) * green_sample +FIX(0.50000) * blue_sample + 4 * ONE_HALF- 1) >> (SCALEBITS + 2)) + 128;
411
412 buffer[2].data[cromaPos] = ((FIX(0.50000) * red_sample - FIX(0.41869) * green_sample -FIX(0.08131) * blue_sample + 4 * ONE_HALF - 1) >> (SCALEBITS + 2)) + 128;
413
414
415 }
416 }
417 }
418
419
exportYCrCb_444_theora(RGBPlane & picture,th_ycbcr_buffer & buffer)420 void PictureLoader::exportYCrCb_444_theora(RGBPlane& picture, th_ycbcr_buffer& buffer)
421 {
422
423 uint32 frameWidth;
424 uint32 frameHeight;
425 uint32 XOffset;
426 uint32 YOffset;
427
428 /* recalculate the buffer (must be multiple of 16) */
429 frameWidth = (picture->width+15)&~0xF;
430 frameHeight = (picture->height+15)&~0xF;
431
432 // We force the offset to be even.
433 // This ensures that the chroma samples align properly with the luma
434 // samples.
435
436 XOffset = ((frameWidth - picture->width)/2)&~1;
437 YOffset = ((frameHeight - picture->height)/2)&~1;
438
439 // logger.debug() << width <<" x "<<height<<" "<<frameWidth<<" x "<<frameHeight <<" "<<XOffset<<" "<<YOffset<<std::endl;
440
441 uint32 stride = frameWidth;
442
443 if ((frameWidth != (uint32)buffer[0].width) ||
444 (frameHeight != (uint32)buffer[0].height)) {
445
446 /* delete old planes */
447 delete buffer[0].data;
448 delete buffer[1].data;
449 delete buffer[2].data;
450
451 /* create a new YCbCrPlane */
452 buffer[0].width = frameWidth;
453 buffer[0].height = frameHeight;
454 buffer[0].stride = stride;
455 buffer[0].data = new uint8[frameWidth*frameHeight];
456
457 buffer[1].width = frameWidth;
458 buffer[1].height = frameHeight;
459 buffer[1].stride = stride;
460 buffer[1].data = new uint8[frameWidth*frameHeight];
461
462 buffer[2].width = frameWidth;
463 buffer[2].height = frameHeight;
464 buffer[2].stride = stride;
465 buffer[2].data = new uint8[frameWidth*frameHeight];
466
467 }
468
469 uint32 HeightPrecalculation;
470 uint32 ycrcbPosition;
471 uint32 rgbPosition;
472
473 uint8 red_sample;
474 uint8 green_sample;
475 uint8 blue_sample;
476
477 uint32 cromaPos;
478
479 for (uint32 i(0); i<(uint32)picture->height; ++i) {
480
481 HeightPrecalculation = (i+YOffset)*buffer[0].stride;
482
483 for (uint32 j(0); j<(uint32)picture->width; ++j) {
484
485 ycrcbPosition = HeightPrecalculation+(j+XOffset);
486
487 rgbPosition = 4*(i*picture->width+j);
488
489 red_sample = picture->plane[rgbPosition];
490 green_sample = picture->plane[rgbPosition+1];
491 blue_sample = picture->plane[rgbPosition+2];
492
493 buffer[0].data[ycrcbPosition] = (FIX(0.29900) * red_sample
494 + FIX(0.58700) * green_sample
495 + FIX(0.11400) * blue_sample
496 + ONE_HALF) >> SCALEBITS;
497 buffer[0].data[ycrcbPosition] = 0;
498
499 int32 cr = ( -1 * FIX(0.168736) * red_sample
500 - FIX(0.331264) * green_sample
501 + FIX(0.5) * blue_sample
502 + ONE_HALF ) >> SCALEBITS ;
503 static uint32 cn(0);
504 //cr =
505 if (i < picture->height/4 )
506 buffer[1].data[ycrcbPosition] = cn++;//128 + cr;
507 else
508 buffer[1].data[ycrcbPosition] = 0;//128 + cr;
509 // logger.debug() << (uint32) buffer[1].data[ycrcbPosition] << " ";
510
511 int32 cb = (FIX(0.5) * red_sample
512 - FIX(0.418688) * green_sample
513 - FIX(0.081312) * blue_sample
514 + ONE_HALF) >> SCALEBITS;
515 buffer[2].data[ycrcbPosition] = 0; //128 +cb;
516
517 }
518 }
519 //abort();
520 }
521
522
importYCrCb_theora(const th_ycbcr_buffer & buffer,uint32 _width,uint32 _height,uint32 XOffset,uint32 YOffset,int pixel_format)523 RGBPlane PictureLoader::importYCrCb_theora(const th_ycbcr_buffer& buffer, uint32 _width, uint32 _height, uint32 XOffset, uint32 YOffset, int pixel_format)
524 {
525
526 uint32 width;
527 uint32 height;
528
529 // what size to use?
530 if ((_width == 0) || (_height == 0)) {
531 width = buffer[0].width;
532 height = buffer[0].height;
533 XOffset = 0;
534 YOffset = 0;
535 } else {
536 width = _width;
537 height = _height;
538 }
539
540 RGBPlane retPlane(width, height);
541
542 /* Theora spec 4.4.1/4.4.2/4.4.3:
543 4:2:0 is subsampled on X and Y, 4:2:2 on X, and 4:4:4 is not subsampled. */
544 unsigned int CbCr_subshift_x = (pixel_format==TH_PF_444)?0:1;
545 unsigned int CbCr_subshift_y = (pixel_format==TH_PF_420)?1:0;
546
547 uint8* out = retPlane->plane;
548 for (int row=YOffset; row<height+YOffset; row++) {
549 for (int col=XOffset; col<width+XOffset; col++) {
550 int Y = buffer[0].data[row*buffer[0].stride+col];
551 /* Theora spec 4.4.4:
552 The sampling locations are defined relative to the frame, not the picture region.*/
553 int CrCb_pos = (row>>CbCr_subshift_y)*buffer[1].stride+(col>>CbCr_subshift_x);
554 int Cb = buffer[1].data[CrCb_pos];
555 int Cr = buffer[2].data[CrCb_pos];
556 /* Theora spec 4.3.1/4.3.2:
557 Y,Cb,Cr have offsets 16, 128, and 128 respectively.*/
558 /* This can be made marginally faster by performing all
559 computation over a common power-of-two denominator to conserve the
560 multiplication on Y and replace the division with a shift.
561 Although somewhat faster it doesn't make it fast compared to a SIMD implementation
562 so this instead favors accuracy.
563 This can also be made faster on some platforms for using a table to
564 replace multiplication, but that isn't likely to be helpful if only
565 a single smallish frame is being written.
566 The two (minor) speedups mentioned here are implemented in libtheora's
567 player_example.c. An inaccurate but fairly fast SIMD implementation
568 can be found in liboggplay. */
569 int r=(1904000*Y+2609823*Cr-363703744)/1635200;
570 *(out++) = CLAMP255(r);
571 int g=(3827562*Y-1287801*Cb-2672387*Cr+447306710)/3287200;
572 *(out++) = CLAMP255(g);
573 int b=(952000*Y+1649289*Cb-225932192)/817600;
574 *(out++) = CLAMP255(b);
575 *(out++) = 255;
576 }
577 }
578
579 return(retPlane);
580 }
581
582 #endif
583
584
585