1 #include "image.h"
2 #include "error.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <libswscale/swscale.h>
7 
8 // Changes for ffmpeg 3.0
9 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,0)
10 #  include <libavutil/imgutils.h>
11 #  define av_free_packet av_packet_unref
12 #  define avpicture_get_size(fmt,w,h) av_image_get_buffer_size(fmt,w,h,1)
13 #endif
14 
15 // PIX_FMT was renamed to AV_PIX_FMT on this version
16 #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51,74,100)
17 #  define AVPixelFormat PixelFormat
18 #  define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
19 #  define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
20 #  define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
21 #  define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
22 #  define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
23 #  define AV_PIX_FMT_YUV420P  PIX_FMT_YUV420P
24 #  define AV_PIX_FMT_YUV422P  PIX_FMT_YUV422P
25 #  define AV_PIX_FMT_YUV440P  PIX_FMT_YUV440P
26 #  define AV_PIX_FMT_YUV444P  PIX_FMT_YUV444P
27 #endif
28 
29 #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 8, 0)
30 #  define av_frame_alloc  avcodec_alloc_frame
31 #  if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,59,100)
32 #    define av_frame_free   av_freep
33 #  else
34 #    define av_frame_free   avcodec_free_frame
35 #  endif
av_frame_get_buffer(AVFrame * frame,int magic)36 void av_frame_get_buffer(AVFrame *frame, int magic) { avpicture_alloc((AVPicture *)frame, frame->format, frame->width, frame->height); }
av_frame_copy(AVFrame * dst,AVFrame * src)37 void av_frame_copy(AVFrame *dst, AVFrame *src) { memcpy(dst->data[0], src->data[0], sizeof(uint8_t)*avpicture_get_size(AV_PIX_FMT_RGB24, dst->width, dst->height)); }
38 #endif
39 
40 #define MAX_FILTER_SIZE 256
41 
42 struct image {
43     AVFrame *frame;
44 };
45 
image_init(const int width,const int height)46 image *image_init(const int width, const int height) {
47     image *i;
48     i = (image *) malloc(sizeof(image));
49 
50     i->frame = (AVFrame *) av_frame_alloc();
51     i->frame->width = width;
52     i->frame->height = height;
53     i->frame->format = AV_PIX_FMT_RGB24; // best choice?
54     av_frame_get_buffer(i->frame, 16); // magic number?
55     return i;
56 }
57 
image_width(const image * i)58 int image_width(const image *i) {
59     return i->frame->width;
60 }
61 
image_height(const image * i)62 int image_height(const image *i) {
63     return i->frame->height;
64 }
65 
image_set(const image * i,const int x,const int y,const unsigned char r,const unsigned char g,const unsigned char b)66 void image_set(const image *i, const int x, const int y, const unsigned char r, const unsigned char g, const unsigned char b) {
67     *(i->frame->data[0]+y*i->frame->linesize[0]+x*3+0) = r;
68     *(i->frame->data[0]+y*i->frame->linesize[0]+x*3+1) = g;
69     *(i->frame->data[0]+y*i->frame->linesize[0]+x*3+2) = b;
70 }
71 
image_get_r(const image * i,const int x,const int y)72 unsigned char image_get_r(const image *i, const int x, const int y) {
73     return *(i->frame->data[0]+y*i->frame->linesize[0]+x*3+0);
74 }
75 
image_get_g(const image * i,const int x,const int y)76 unsigned char image_get_g(const image *i, const int x, const int y) {
77     return *(i->frame->data[0]+y*i->frame->linesize[0]+x*3+1);
78 }
79 
image_get_b(const image * i,const int x,const int y)80 unsigned char image_get_b(const image *i, const int x, const int y) {
81     return *(i->frame->data[0]+y*i->frame->linesize[0]+x*3+2);
82 }
83 
image_to_bgra(unsigned char * target,const int width,const int height,const image * i,const int offset_x,const int offset_y)84 void image_to_bgra(unsigned char *target, const int width, const int height, const image *i, const int offset_x, const int offset_y) {
85     int x, y;
86     for (y = 0; y < image_height(i) && offset_y+y < height ; y++) {
87         for (x = 0; x < image_width(i) && offset_x+x < width; x++) {
88             *(target+width*4*(offset_y+y)+4*(offset_x+x)+0) = image_get_b(i, x, y);
89             *(target+width*4*(offset_y+y)+4*(offset_x+x)+1) = image_get_g(i, x, y);
90             *(target+width*4*(offset_y+y)+4*(offset_x+x)+2) = image_get_r(i, x, y);
91             *(target+width*4*(offset_y+y)+4*(offset_x+x)+3) = 255;
92         }
93     }
94 }
95 
image_from_bgra(const unsigned char * source,const int width,const int height)96 image *image_from_bgra(const unsigned char *source, const int width, const int height) {
97     image *i = image_init(width, height);
98     int x, y;
99     for (y = 0; y < image_height(i); y++) {
100         for (x = 0; x < image_width(i); x++) {
101             image_set(i, x, y, *(source+width*4*y+4*x+2), *(source+width*4*y+4*x+1), *(source+width*4*y+4*x+0));
102         }
103     }
104     return i;
105 }
106 
image_clone(const image * i)107 image* image_clone(const image *i) {
108     image *i2 = image_init(image_width(i), image_height(i));
109     av_frame_copy(i2->frame, i->frame);
110     return i2;
111 }
112 
image_copy_avframe(const image * i,AVFrame * frame)113 void image_copy_avframe(const image *i, AVFrame *frame) {
114     av_frame_copy(i->frame, frame);
115 }
116 
image_dumb_scale(const image * i,const int width,const int height)117 image* image_dumb_scale(const image *i, const int width, const int height) {
118     image *i2 = image_init(width, height);
119 
120     float x_factor = 1.0*image_width(i)/width;
121     float y_factor = 1.0*image_height(i)/height;
122 
123     int x, y;
124     for (x = 0; x < width; x++) {
125         int x_lower = x_factor*x + 0.5;
126         int x_upper = x_factor*(x+1) - 0.5;
127 
128         if (x_lower > x_upper) {
129             // this can happen when upscaling. pick nearest-neighbour entry
130             x_lower = x_upper = x_factor*(x+0.5);
131         }
132 
133         for (y = 0; y < height; y++) {
134             int y_lower = y_factor*y + 0.5;
135             int y_upper = y_factor*(y+1) - 0.5;
136 
137             if (y_lower > y_upper) {
138                 // this can happen when upscaling. pick nearest-neighbour entry
139                 y_lower = y_upper = y_factor*(y+0.5);
140             }
141 
142             int rsum = 0;
143             int gsum = 0;
144             int bsum = 0;
145             int xx, yy;
146             for (xx = x_lower; xx <= x_upper; xx++) {
147                 for (yy = y_lower; yy <= y_upper; yy++) {
148                     rsum += image_get_r(i, xx, yy);
149                     gsum += image_get_g(i, xx, yy);
150                     bsum += image_get_b(i, xx, yy);
151                 }
152             }
153             int n = (x_upper-x_lower+1)*(y_upper-y_lower+1);
154             image_set(i2, x, y, rsum/n, gsum/n, bsum/n);
155         }
156     }
157 
158     return i2;
159 }
160 
image_scale(const image * i,int width,int height)161 image* image_scale(const image *i, int width, int height) {
162     int target_width = width;
163     int target_height = height;
164 
165     if (width == image_width(i) && height == image_height(i)) {
166         return image_clone(i);
167     }
168 
169     image *i2 = NULL;
170     image *tmp = (image *) i;
171     do {
172         width = target_width;
173         height = target_height;
174 
175 #if LIBSWSCALE_VERSION_MICRO < 100 || LIBSWSCALE_VERSION_INT < AV_VERSION_INT(2, 1, 103)
176         if (width < 8) {
177             // libav and old FFmpeg versions don't allow scaling to a width of less than 8
178             if (image_width(tmp) > 8) {
179                 // but we can use it to go down to 8
180                 width = 8;
181             } else {
182                 // all hope ist lost
183                 image *i3 = image_dumb_scale(tmp, width, height);
184                 if (tmp != i) {
185                     image_free(tmp);
186                 }
187                 return i3;
188             }
189         }
190 
191         if (height < 2) {
192             // This doesn't seem to work on libav...
193             if (image_height(tmp) > 2) {
194                 height = 2;
195             } else {
196                 image *i3 = image_dumb_scale(tmp, width, height);
197                 if (tmp != i) {
198                     image_free(tmp);
199                 }
200                 return i3;
201             }
202         }
203 #endif
204 
205         // When scaling from a high width directly to 1, FFmpeg sometimes (?)
206         // introduces noise. So we avoid this situation.
207         if (width == 1 && image_width(tmp) > 2) {
208             width = 2;
209         }
210 
211         if (image_width(tmp)/width > MAX_FILTER_SIZE) {
212             width = image_width(tmp)/MAX_FILTER_SIZE+1;
213         }
214         if (image_height(tmp)/height > MAX_FILTER_SIZE) {
215             height = image_height(tmp)/MAX_FILTER_SIZE+1;
216         }
217 
218 
219         i2 = image_init(width, height);
220 
221         struct SwsContext *sws_context = sws_getContext(image_width(tmp), image_height(tmp), tmp->frame->format,
222                                                         image_width(i2), image_height(i2), i2->frame->format,
223                                                         SWS_AREA, NULL, NULL, NULL);
224         sws_scale(sws_context, (uint8_t const * const *)tmp->frame->data,
225                 tmp->frame->linesize, 0, tmp->frame->height, i2->frame->data,
226                 i2->frame->linesize);
227         sws_freeContext(sws_context);
228 
229         if (tmp != i) {
230             image_free(tmp);
231         }
232 
233         tmp = i2;
234     } while (image_width(i2) != target_width || image_height(i2) != target_height);
235 
236     return i2;
237 }
238 
image_flip(const image * i)239 image *image_flip(const image *i) {
240     image *i2 = image_init(image_height(i), image_width(i));
241     int x, y;
242     for (x = 0; x < image_width(i2); x++) {
243         for (y = 0; y < image_height(i2); y++) {
244             image_set(i2, x, y, image_get_r(i, y, x), image_get_g(i, y, x), image_get_b(i, y, x));
245         }
246     }
247     return i2;
248 }
249 
image_column(const image * i,double percent)250 image* image_column(const image *i, double percent) {
251     image *i2 = image_init(1, image_height(i));
252 
253     int y;
254     const int x = image_width(i)*percent;
255     for (y = 0; y < image_height(i); y++) {
256         image_set(i2, 0, y, image_get_r(i, x, y), image_get_g(i, x, y), image_get_b(i, x, y));
257     }
258 
259     return i2;
260 }
261 
image_write_png(const image * i,const char * file_path)262 int image_write_png(const image *i, const char *file_path) {
263     AVCodec *encoder = avcodec_find_encoder_by_name("png");
264     AVCodecContext *encoder_context;
265     encoder_context = avcodec_alloc_context3(encoder);
266     encoder_context->width = i->frame->width;
267     encoder_context->height = i->frame->height;
268     encoder_context->pix_fmt = AV_PIX_FMT_RGB24;
269     encoder_context->time_base.num = 1;
270     encoder_context->time_base.den = 1;
271     if (avcodec_open2(encoder_context, encoder, NULL) < 0) {
272         error("Could not open output codec.");
273         return -1;
274     }
275 
276     AVPacket packet;
277     av_init_packet(&packet);
278     packet.data = NULL;
279     packet.size = 0;
280 
281 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 28, 0)
282     uint8_t buffer[200000]; // TODO: Why this size?
283     packet.size = avcodec_encode_video(encoder_context, buffer, 200000, i->frame);
284     packet.data = buffer;
285 #else
286     int got_packet = 0;
287     avcodec_encode_video2(encoder_context, &packet, i->frame, &got_packet);
288     if (! got_packet) {
289         error("Encoding error.");
290         return -1;
291     }
292 #endif
293 
294     FILE *file;
295     file = fopen(file_path, "wb");
296     if (! file) {
297         error("Could not open output file.");
298         return -1;
299     }
300     fwrite(packet.data, 1, packet.size, file);
301     fclose(file);
302 
303     av_free_packet(&packet);
304 
305     avcodec_close(encoder_context);
306     av_free(encoder_context);
307     return 0;
308 }
309 
image_free(image * i)310 void image_free(image *i) {
311     av_frame_free(&i->frame);
312     free(i);
313 }
314