Use cairo to scale picture
[cascardo/movie.git] / movie.c
1 /*
2  *  Copyright (C) 2008  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #define WIDTH 800
20 #define HEIGHT 600
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <cairo.h>
25 #include <SDL.h>
26 #include <SDL_image.h>
27
28 #define SWAP(x, y) do { \
29         x ^= y; y ^= x; x ^= y; \
30         } while (0)
31
32 #define ABS(x) ((x) < 0 ? -(x) : (x))
33
34 #define IS_CENTER(cx, cy, x, y) \
35         ((x + WIDTH/2 == cx) && (y + HEIGHT/2 == cy))
36
37 SDL_Rect *points;
38 int psize;
39
40 void
41 ReadPoints (char *filename)
42 {
43   FILE *file;
44   char *buffer;
45   char *next;
46   size_t len;
47   int i;
48   file = fopen (filename, "r");
49   fscanf (file, "%d\n", &psize);
50   points = malloc (sizeof (SDL_Rect) * psize);
51   if (points == NULL)
52     abort ();
53   buffer = NULL;
54   len = 0;
55   for (i = 0; i < psize; i++)
56   {
57     getline (&buffer, &len, file);
58     points[i].x = strtol (buffer, &next, 0);
59     points[i].y = strtol (next+1, NULL, 0);
60   }
61   fclose (file);
62 }
63
64 SDL_Rect
65 GetNextPoint (void)
66 {
67   static SDL_Rect rect = {0, 0, WIDTH, HEIGHT};
68   static int cur = -1;
69   static int inc, err, thre, swap;
70   static int x1, y1, x2, y2;
71   static int x, y;
72   int next;
73   next = (cur + 1) % psize;
74   if ((points[next].x == rect.x && points[next].y == rect.y) || cur == -1)
75   {
76     cur = next;
77     next = (cur + 1) % psize;
78     err = 0;
79     swap = 0;
80     x1 = points[cur].x;
81     y1 = points[cur].y;
82     x2 = points[next].x;
83     y2 = points[next].y;
84     inc = y2 - y1;
85     thre = x2 - x1;
86     if (ABS (inc) > ABS (thre))
87     {
88       SWAP (inc, thre);
89       SWAP (x1, y1);
90       SWAP (x2, y2);
91       swap = 1;
92     }
93     x = x1;
94     y = y1;
95   }
96   rect.x = (swap ? y : x);
97   rect.y = (swap ? x : y);
98   (x2 < x1) ? x-- : x++;
99   err += ABS (inc);
100   if (err >= ABS (thre))
101   {
102     err -= ABS (thre);
103     y += (inc < 0) ? -1 : 1;
104   }
105   return rect;
106 }
107
108 void
109 ShowPoint (SDL_Surface *screen, SDL_Surface *image)
110 {
111   SDL_BlitSurface (image, NULL, screen, NULL);
112   SDL_UpdateRect (screen, 0, 0, 0, 0);
113 }
114
115 cairo_surface_t *
116 CairoFromSDL (SDL_Surface *surface)
117 {
118   return cairo_image_surface_create_for_data (surface->pixels,
119                                               CAIRO_FORMAT_ARGB32,
120                                               surface->w, surface->h,
121                                               surface->pitch);
122 }
123
124 SDL_Surface *
125 CairoToSDL (cairo_surface_t *surface)
126 {
127   return SDL_CreateRGBSurfaceFrom (cairo_image_surface_get_data (surface),
128                                    cairo_image_surface_get_width (surface),
129                                    cairo_image_surface_get_height (surface),
130                                    32,
131                                    cairo_image_surface_get_stride (surface),
132                                    0x00ff0000, 0x0000ff00, 0x000000ff,
133                                    0xff000000);
134 }
135
136 SDL_Surface *
137 CairoTarget (SDL_Surface *image)
138 {
139   static unsigned char *data = NULL;
140   static cairo_surface_t *surface = NULL;
141   static cairo_t *ctx = NULL;
142   cairo_surface_t *source;
143   if (data == NULL && surface == NULL && ctx == NULL)
144   {
145     data = malloc (WIDTH * HEIGHT * 4);
146     surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32,
147                                                    WIDTH, HEIGHT, WIDTH * 4);
148     ctx = cairo_create (surface);
149     cairo_scale (ctx, 2, 2);
150   }
151   source = CairoFromSDL (image);
152   cairo_set_source_surface (ctx, source, 0, 0);
153   cairo_paint (ctx);
154   cairo_surface_destroy (source);
155   return CairoToSDL (surface);
156 }
157
158 SDL_Surface *
159 GetNextImage (SDL_Surface *image)
160 {
161   SDL_Surface *slice;
162   SDL_Rect center;
163   SDL_Surface *scale;
164   center = GetNextPoint (), GetNextPoint (), GetNextPoint ();
165   slice = SDL_CreateRGBSurface (SDL_SWSURFACE, WIDTH/2, HEIGHT/2,
166                                 32,
167                                 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
168   center.x -= (WIDTH/2) / 2;
169   center.y -= (HEIGHT/2) / 2;
170   center.w /= 2;
171   center.h /= 2;
172   SDL_BlitSurface (image, &center, slice, NULL);
173   SDL_UpdateRect (slice, 0, 0, 0, 0);
174   scale = CairoTarget (slice);
175   SDL_FreeSurface (slice);
176   return scale;
177 }
178
179 Uint32
180 ShowNext (Uint32 interval, void *data)
181 {
182   SDL_UserEvent event;
183   event.type = SDL_USEREVENT;
184   event.code = 0;
185   SDL_PushEvent ((SDL_Event *) &event);
186   return 33;
187 }
188
189 int
190 main (int argc, char **argv)
191 {
192   SDL_Surface *screen;
193   SDL_Surface *image;
194   SDL_Surface *slice;
195   SDL_Event event;
196   ReadPoints ("pro-gnu");
197   SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER);
198   screen = SDL_SetVideoMode (800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
199   image = IMG_Load ("/home/cascardo/fotos/debconf.jpg");
200   SDL_AddTimer (0, ShowNext, NULL);
201   while (SDL_WaitEvent (&event))
202   {
203     if (event.type == SDL_KEYDOWN)
204       break;
205     else if (event.type == SDL_USEREVENT)
206     {
207       slice = GetNextImage (image);
208       ShowPoint (screen, slice);
209       SDL_FreeSurface (slice);
210     }
211   }
212   SDL_FreeSurface (image);
213   SDL_Quit ();
214   free (points);
215   return 0;
216 }