various small fixes.
[neuro.git] / src / video / painter.c
blob493faead0e92eeebe52b4caec6d085362ead7821
1 /*
2 * libneuro, a light weight abstraction of high or lower libraries
3 * and toolkit for applications.
4 * Copyright (C) 2005-2006 Nicholas Niro, Robert Lemay
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 /* painter.c
24 * The core painter's algorithm module.
27 /*
28 * this code is my own version of the painter's algorithm.
29 * It is thus used to draw images in an order you have to
30 * input when "pushing" an image, called layers.
31 * This module also requires 3 other variables which contain
32 * the size of the image, a pointer to the image and the
33 * destination of the image.
35 * To make the painter's algorithm, I used 2 types of data.
36 * I use raw data which contains exactly what the external
37 * process gives us and then theres the instruction data which
38 * contains a pointer to a single raw data and a pointer
39 * to the next instruction data.
41 * The instruction datas order are changed every time a new
42 * image is pushed. It is organized in a growing order,
43 * the smallest layers are the first ones and the biggest
44 * are the last instructions drawn.
46 * Now, you see, the major advantage of this is we can also
47 * redraw images that were below another one which would for
48 * example be deleted. But for this, we have do an expansive
49 * bounds search for images which have to be redrawn.
51 * Also note that raw datas have different types that trigger
52 * behaviors. Static and dynamic are the two most important
53 * types, then theres the temporary type and the rest
54 * are pretty much sub types for those that I mentioned earlier.
58 /*-------------------- Extern Headers Including --------------------*/
59 #include <string.h> /* memcpy */
61 /*-------------------- Local Headers Including ---------------------*/
62 #include <extlib.h> /* we only use Lib_GetScreenSize */
63 #include <ebuf.h> /* we use all the "normal" allocation function from this */
64 #include <other.h> /* we call all the bounding check functions from it
65 and also the bound fix ones */
67 /*-------------------- Main Module Header --------------------------*/
68 #include "video.h"
69 #include <graphics.h>
71 /*-------------------- Other ----------------------------*/
73 /*-------------------- Global Variables ----------------------------*/
75 /*-------------------- Static Variables ----------------------------*/
77 static v_object *background; /* the background image */
79 static INSTRUCTION_ENGINE *first_element;
80 static INSTRUCTION_ENGINE *last_element;
82 static EBUF *Raw;
83 static EBUF *Queue;
85 /* buffered screen size for fast consultation */
86 static Rectan screenSize;
88 /* a rectangle meant to test the bound fix algorithm */
89 static Rectan test_BoundFix;
91 /*-------------------- Static Prototypes ---------------------------*/
95 /*-------------------- Static Functions ----------------------------*/
97 /* A function that handles "fixes" needed on images
98 * to make them constrainted in the main screen.
99 * returns 0 if all is ok and 1 if the image needs
100 * to be dropped.
102 static u8
103 BoundFixChecker(Rectan *indep, Rectan *isrc, Rectan *idst)
105 Rectan bufa;
107 bufa.x = idst->x;
108 bufa.y = idst->y;
109 bufa.w = isrc->w - 1;
110 bufa.h = isrc->h - 1;
112 switch (Neuro_BoundsCheck(indep, &bufa))
114 case 0: /* the v_object is inside the screen, all is ok */
115 break;
117 case 1: /* the v_object is outside the screen, we need to drop this instruction */
119 /* Debug_Print("a drawing instruction was dropped because its destination is outbound"); */
120 return 1;
122 break;
124 /* here is the fun, the v_object is overlapping the screen
125 * and we got to only draw the part that is still inside
126 * the screen
128 case 2:
130 /* to do this, I'm thinking we could use 2 similar functions
131 * the 1st one would check and "correct" the parts of the
132 * v_object that is not inside the screen vertically and
133 * the 2nd one would do the same but horizontally.
134 * The only thing to do is to change the isrc(Rectan)'s
135 * values.
137 Neuro_VerticalBoundFix(indep, isrc, idst);
138 Neuro_HorizontalBoundFix(indep, isrc, idst);
139 /* Info_Print("Fixed the coordinates/size of the drawing instruction."); */
141 break;
143 default:
145 /* Debug_Print("a drawing instruction was dropped because its destination is outbound"); */
146 return 1;
148 break;
151 return 0;
154 /* compute the instruction_engine buffer every
155 * time a new raw element is added.
157 static INSTRUCTION_ENGINE *
158 computeRawEngine(RAW_ENGINE *toadd)
160 register EBUF *tmp;
161 register INSTRUCTION_ENGINE *buf = NULL, *cur = NULL, *last = NULL;
162 register u32 current; /* current number of elements in instruction */
164 tmp = Queue;
166 /*if (use_memory_pool)
167 buf = Pull_Data_From_Pool(POOL_QUEUE);*/
169 if (buf == NULL)
171 /* Debug_Val(0, "computeRawEngine buf is empty so we allocate for %d\n", toadd->layer); */
172 Neuro_AllocEBuf(tmp, sizeof(INSTRUCTION_ENGINE*), sizeof(INSTRUCTION_ENGINE));
173 buf = Neuro_GiveCurEBuf(tmp);
175 /*else
176 Debug_Val(0, "computeRawEngine recycled an object! %d\n", toadd->layer);*/
179 current = Neuro_GiveEBufCount(tmp);
181 buf->current = toadd;
182 buf->next = NULL;
184 if (debug_instruction_buffer)
185 Debug_Val(0, "Push --> %d\n", toadd->layer);
187 if (last_element != NULL)
189 if (last_element->current == NULL)
191 Debug_Val(0, "CAUGHT ERROR in last_element current == %d -- debug %d\nDropping this call\n",
192 current,
193 (int)last_element->current);
194 return NULL;
197 if (last_element->current->layer <= buf->current->layer)
199 last_element->next = buf;
200 last_element = buf;
201 /*Debug_Val(0, "proof layer %d real layer %d ptr %d\n",
202 (*buf)[current]->current->layer,
203 (*last_element)->current->layer,
204 (int)(*last_element)->current);*/
205 /* Debug_Val(0, "Placed the object at the end of the queue\n"); */
207 return buf;
210 else
212 first_element = buf;
213 last_element = buf;
214 /* Debug_Val(0, "Just placed the frame as the first element, starting the queue\n"); */
215 return buf;
218 /* TODO TODO TODO TODO
219 * special case so volatile types always are put in the
220 * beginning of the queue. Both to make the process
221 * faster and to make the volatiles have precedence
222 * over everything to avoid conflicts.
224 if (buf->current->type = TDRAW_VOLATILE)
226 /* we make the type be of the layer 0 so it is
227 * always the first in the list.
229 * the layer 0 is reserved stricly for volatile
230 * types.
233 buf->current->layer = 0;
237 cur = first_element;
238 /* cur = Neuro_GiveEBuf(tmp, first); */
239 while (cur != NULL)
241 /* Debug_Val(0, "looped cur %d buf %d\n", cur->current->layer, buf->current->layer); */
242 if (cur->current->layer > buf->current->layer)
244 /*printf("Event current layer %d > toadd layer %d\n",
245 cur->current->layer,
246 toadd->layer);
249 /* to avoid death loops */
250 /*if (cur->next == buf)
251 cur->next = NULL;*/
253 if (cur == first_element)
255 #if temp
256 /* switch **buf with the current position */
257 temp = Neuro_GiveEBuf(tmp, first);
258 Neuro_SetEBuf(tmp, Neuro_GiveEBufAddr(tmp, first), buf);
259 Neuro_SetEBuf(tmp, Neuro_GiveEBufAddr(tmp, current), temp);
261 /*printf("Beginning LL change : cur %d, buf[0][0] %d\n",
262 (int)cur,
263 (int)buf[0]);
265 if (cur->current == NULL || cur->current->surface_ptr == NULL)
266 cur = Neuro_GiveEBuf(tmp, first);
267 if (Neuro_GiveEBuf(tmp, first) == buf)
269 Error_Print("huge problem, it is going to put its next element as the same node as itself, creating a death loop!!\n");
270 cur->next = NULL;
272 else
273 cur->next = temp;
274 #endif /* temp */
275 /*Debug_Val(0, "New First element proclaimed %d\n",
276 buf->current->layer);*/
277 buf->next = first_element;
278 first_element = buf;
280 else
281 buf->next = cur;
282 if (last != NULL)
284 last->next = buf;
286 break;
288 else
290 /* Debug_Val(0, "nothing to be done\n"); */
292 last = cur;
293 cur = cur->next;
295 /* printf("End of the looping process\n"); */
298 if (debug_instruction_buffer)
300 Debug_Val(0, "BEGIN inside computeRawEngine debug print\n");
301 Graphics_DebugPrintQueue();
302 Debug_Val(0, "END inside computeRawEngine debug print\n");
305 return buf;
309 /*-------------------- Global Functions ----------------------------*/
311 /* - layer is the priority by which it much be drawn.
312 * - src should be used to know which part of the
313 * surface has to be drawn(for sprites mostly).
314 * - dst is the destination X Y on the screen
315 * - surface is the pointer of the loaded image.
317 INSTRUCTION_ENGINE *
318 Graphics_AddDrawingInstruction(u32 layer, u8 type, Rectan *isrc, Rectan *idst, void *isurface)
320 RAW_ENGINE *buf = NULL;
321 Rectan tIsrc, tIdst;
323 if (isurface == NULL || isrc == NULL || idst == NULL)
324 return NULL;
326 memcpy(&tIsrc, isrc, sizeof(Rectan));
327 memcpy(&tIdst, idst, sizeof(Rectan));
329 if (BoundFixChecker(&screenSize, &tIsrc, &tIdst) == 1)
331 /* Debug_Val(10, "a drawing instruction was dropped because its destination is outbound"); */
332 return NULL;
335 #if retain_image_inipos
336 tIsrc.x = isrc->x;
337 tIsrc.y = isrc->y;
338 tIsrc.w = isrc->w;
339 tIsrc.h = isrc->h;
341 tIdst.x = idst->x;
342 tIdst.y = idst->y;
343 #endif /* retain_image_inipos */
345 /* a square in the middle of the screen to test
346 * the bound fix checker on an object other than
347 * the screen itself.
349 /* BoundFixChecker(&test_BoundFix, &tIsrc, &tIdst); */
351 /*if (use_memory_pool)
352 buf = Pull_Data_From_Pool(POOL_RAWENGINE);*/
354 if (buf == NULL)
356 Neuro_AllocEBuf(Raw, sizeof(RAW_ENGINE*), sizeof(RAW_ENGINE));
358 buf = Neuro_GiveCurEBuf(Raw);
361 /* we reserve the 0 spot for out need
362 * we might reserve more than just one
363 * if the need comes out.
365 buf->layer = layer + 1;
366 buf->type = type;
367 memcpy(&buf->src, &tIsrc, sizeof(Rectan));
369 buf->dx = tIdst.x;
370 buf->dy = tIdst.y;
372 buf->surface_ptr = isurface;
374 /* sets a flag to tell when changed things
375 * and I think the whole screen will be
376 * redrawn.
378 Neuro_RedrawScreen();
380 return computeRawEngine((RAW_ENGINE*)buf);
383 INSTRUCTION_ENGINE *
384 Graphics_GetFirstElem()
386 return first_element;
389 void
390 Graphics_SetFirstElem(INSTRUCTION_ENGINE *elem)
392 first_element = elem;
395 INSTRUCTION_ENGINE *
396 Graphics_GetLastElem()
398 return last_element;
401 void
402 Graphics_SetLastElem(INSTRUCTION_ENGINE *elem)
404 last_element = elem;
407 EBUF *
408 Graphics_GetRawBuffer()
410 return Raw;
413 EBUF *
414 Graphics_GetQueueBuffer()
416 return Queue;
419 /*-------------------- Constructor Destructor ----------------------*/
422 Graphics_PainterInit()
424 u32 screenwidth, screenheight;
426 Neuro_CreateEBuf(&Raw);
427 Neuro_CreateEBuf(&Queue);
429 /* get the screen size from the "official" source */
430 Lib_GetScreenSize(&screenwidth, &screenheight);
432 /* populate a buffer variable that keeps the size of the screen for fast consultation. */
433 screenSize.x = 0;
434 screenSize.y = 0;
435 screenSize.w = screenwidth;
436 screenSize.h = screenheight;
438 return 0;
442 Graphics_PainterReset()
444 Neuro_CleanEBuf(&Raw);
445 Neuro_CleanEBuf(&Queue);
447 Neuro_CreateEBuf(&Raw);
448 Neuro_CreateEBuf(&Queue);
450 first_element = NULL;
451 last_element = NULL;
453 return 0;
456 void
457 Graphics_PainterClean()
459 Debug_Val(0, "Raw total %d\n", Neuro_GiveEBufCount(Raw));
460 Debug_Val(0, "Queue total %d\n", Neuro_GiveEBufCount(Queue));
462 Neuro_CleanEBuf(&Raw);
463 Neuro_CleanEBuf(&Queue);
465 first_element = NULL;
466 last_element = NULL;