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