1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 */
16
17 #include "gfx_common.h"
18 #include "tfb_draw.h"
19 #include "drawcmd.h"
20 #include "libs/log.h"
21 #include "libs/memlib.h"
22
23
24 static const HOT_SPOT NullHs = {0, 0};
25
26 void
TFB_DrawScreen_Line(int x1,int y1,int x2,int y2,Color color,DrawMode mode,SCREEN dest)27 TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color,
28 DrawMode mode, SCREEN dest)
29 {
30 TFB_DrawCommand DC;
31
32 DC.Type = TFB_DRAWCOMMANDTYPE_LINE;
33 DC.data.line.x1 = x1;
34 DC.data.line.y1 = y1;
35 DC.data.line.x2 = x2;
36 DC.data.line.y2 = y2;
37 DC.data.line.color = color;
38 DC.data.line.drawMode = mode;
39 DC.data.line.destBuffer = dest;
40
41 TFB_EnqueueDrawCommand (&DC);
42 }
43
44 void
TFB_DrawScreen_Rect(RECT * rect,Color color,DrawMode mode,SCREEN dest)45 TFB_DrawScreen_Rect (RECT *rect, Color color, DrawMode mode, SCREEN dest)
46 {
47 RECT locRect;
48 TFB_DrawCommand DC;
49
50 if (!rect)
51 {
52 locRect.corner.x = locRect.corner.y = 0;
53 locRect.extent.width = ScreenWidth;
54 locRect.extent.height = ScreenHeight;
55 rect = &locRect;
56 }
57
58 DC.Type = TFB_DRAWCOMMANDTYPE_RECTANGLE;
59 DC.data.rect.rect = *rect;
60 DC.data.rect.color = color;
61 DC.data.rect.drawMode = mode;
62 DC.data.rect.destBuffer = dest;
63
64 TFB_EnqueueDrawCommand (&DC);
65 }
66
67 void
TFB_DrawScreen_Image(TFB_Image * img,int x,int y,int scale,int scaleMode,TFB_ColorMap * cmap,DrawMode mode,SCREEN dest)68 TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale,
69 int scaleMode, TFB_ColorMap *cmap, DrawMode mode, SCREEN dest)
70 {
71 TFB_DrawCommand DC;
72
73 DC.Type = TFB_DRAWCOMMANDTYPE_IMAGE;
74 DC.data.image.image = img;
75 DC.data.image.colormap = cmap;
76 DC.data.image.x = x;
77 DC.data.image.y = y;
78 DC.data.image.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
79 DC.data.image.scaleMode = scaleMode;
80 DC.data.image.drawMode = mode;
81 DC.data.image.destBuffer = dest;
82
83 TFB_EnqueueDrawCommand (&DC);
84 }
85
86 void
TFB_DrawScreen_FilledImage(TFB_Image * img,int x,int y,int scale,int scaleMode,Color color,DrawMode mode,SCREEN dest)87 TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale,
88 int scaleMode, Color color, DrawMode mode, SCREEN dest)
89 {
90 TFB_DrawCommand DC;
91
92 DC.Type = TFB_DRAWCOMMANDTYPE_FILLEDIMAGE;
93 DC.data.filledimage.image = img;
94 DC.data.filledimage.x = x;
95 DC.data.filledimage.y = y;
96 DC.data.filledimage.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
97 DC.data.filledimage.scaleMode = scaleMode;
98 DC.data.filledimage.color = color;
99 DC.data.filledimage.drawMode = mode;
100 DC.data.filledimage.destBuffer = dest;
101
102 TFB_EnqueueDrawCommand (&DC);
103 }
104
105 void
TFB_DrawScreen_FontChar(TFB_Char * fontChar,TFB_Image * backing,int x,int y,DrawMode mode,SCREEN dest)106 TFB_DrawScreen_FontChar (TFB_Char *fontChar, TFB_Image *backing,
107 int x, int y, DrawMode mode, SCREEN dest)
108 {
109 TFB_DrawCommand DC;
110
111 DC.Type = TFB_DRAWCOMMANDTYPE_FONTCHAR;
112 DC.data.fontchar.fontchar = fontChar;
113 DC.data.fontchar.backing = backing;
114 DC.data.fontchar.x = x;
115 DC.data.fontchar.y = y;
116 DC.data.fontchar.drawMode = mode;
117 DC.data.fontchar.destBuffer = dest;
118
119 TFB_EnqueueDrawCommand (&DC);
120 }
121
122 void
TFB_DrawScreen_CopyToImage(TFB_Image * img,const RECT * r,SCREEN src)123 TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src)
124 {
125 TFB_DrawCommand DC;
126
127 DC.Type = TFB_DRAWCOMMANDTYPE_COPYTOIMAGE;
128 DC.data.copytoimage.rect = *r;
129 DC.data.copytoimage.image = img;
130 DC.data.copytoimage.srcBuffer = src;
131
132 TFB_EnqueueDrawCommand (&DC);
133 }
134
135 void
TFB_DrawScreen_Copy(const RECT * r,SCREEN src,SCREEN dest)136 TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest)
137 {
138 RECT locRect;
139 TFB_DrawCommand DC;
140
141 if (!r)
142 {
143 locRect.corner.x = locRect.corner.y = 0;
144 locRect.extent.width = ScreenWidth;
145 locRect.extent.height = ScreenHeight;
146 r = &locRect;
147 }
148
149 DC.Type = TFB_DRAWCOMMANDTYPE_COPY;
150 DC.data.copy.rect = *r;
151 DC.data.copy.srcBuffer = src;
152 DC.data.copy.destBuffer = dest;
153
154 TFB_EnqueueDrawCommand (&DC);
155 }
156
157 void
TFB_DrawScreen_SetMipmap(TFB_Image * img,TFB_Image * mmimg,int hotx,int hoty)158 TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
159 {
160 TFB_DrawCommand DC;
161
162 DC.Type = TFB_DRAWCOMMANDTYPE_SETMIPMAP;
163 DC.data.setmipmap.image = img;
164 DC.data.setmipmap.mipmap = mmimg;
165 DC.data.setmipmap.hotx = hotx;
166 DC.data.setmipmap.hoty = hoty;
167
168 TFB_EnqueueDrawCommand (&DC);
169 }
170
171 void
TFB_DrawScreen_DeleteImage(TFB_Image * img)172 TFB_DrawScreen_DeleteImage (TFB_Image *img)
173 {
174 if (img)
175 {
176 TFB_DrawCommand DC;
177
178 DC.Type = TFB_DRAWCOMMANDTYPE_DELETEIMAGE;
179 DC.data.deleteimage.image = img;
180
181 TFB_EnqueueDrawCommand (&DC);
182 }
183 }
184
185 void
TFB_DrawScreen_DeleteData(void * data)186 TFB_DrawScreen_DeleteData (void *data)
187 // data must be a result of HXalloc() call
188 {
189 if (data)
190 {
191 TFB_DrawCommand DC;
192
193 DC.Type = TFB_DRAWCOMMANDTYPE_DELETEDATA;
194 DC.data.deletedata.data = data;
195
196 TFB_EnqueueDrawCommand (&DC);
197 }
198 }
199
200 void
TFB_DrawScreen_WaitForSignal(void)201 TFB_DrawScreen_WaitForSignal (void)
202 {
203 TFB_DrawCommand DrawCommand;
204 Semaphore s;
205 s = GetMyThreadLocal ()->flushSem;
206 DrawCommand.Type = TFB_DRAWCOMMANDTYPE_SENDSIGNAL;
207 DrawCommand.data.sendsignal.sem = s;
208 Lock_DCQ (1);
209 TFB_BatchReset ();
210 TFB_EnqueueDrawCommand (&DrawCommand);
211 Unlock_DCQ();
212 SetSemaphore (s);
213 }
214
215 void
TFB_DrawScreen_ReinitVideo(int driver,int flags,int width,int height)216 TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height)
217 {
218 TFB_DrawCommand DrawCommand;
219 DrawCommand.Type = TFB_DRAWCOMMANDTYPE_REINITVIDEO;
220 DrawCommand.data.reinitvideo.driver = driver;
221 DrawCommand.data.reinitvideo.flags = flags;
222 DrawCommand.data.reinitvideo.width = width;
223 DrawCommand.data.reinitvideo.height = height;
224 TFB_EnqueueDrawCommand (&DrawCommand);
225 }
226
227 void
TFB_DrawScreen_Callback(void (* callback)(void * arg),void * arg)228 TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg)
229 {
230 TFB_DrawCommand DrawCommand;
231 DrawCommand.Type = TFB_DRAWCOMMANDTYPE_CALLBACK;
232 DrawCommand.data.callback.callback = callback;
233 DrawCommand.data.callback.arg = arg;
234 TFB_EnqueueDrawCommand(&DrawCommand);
235 }
236
237 void
TFB_DrawImage_Line(int x1,int y1,int x2,int y2,Color color,DrawMode mode,TFB_Image * target)238 TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color,
239 DrawMode mode, TFB_Image *target)
240 {
241 LockMutex (target->mutex);
242 TFB_DrawCanvas_Line (x1, y1, x2, y2, color, mode, target->NormalImg);
243 target->dirty = TRUE;
244 UnlockMutex (target->mutex);
245 }
246
247 void
TFB_DrawImage_Rect(RECT * rect,Color color,DrawMode mode,TFB_Image * target)248 TFB_DrawImage_Rect (RECT *rect, Color color, DrawMode mode, TFB_Image *target)
249 {
250 LockMutex (target->mutex);
251 TFB_DrawCanvas_Rect (rect, color, mode, target->NormalImg);
252 target->dirty = TRUE;
253 UnlockMutex (target->mutex);
254 }
255
256 void
TFB_DrawImage_Image(TFB_Image * img,int x,int y,int scale,int scaleMode,TFB_ColorMap * cmap,DrawMode mode,TFB_Image * target)257 TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale,
258 int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Image *target)
259 {
260 LockMutex (target->mutex);
261 TFB_DrawCanvas_Image (img, x, y, scale, scaleMode, cmap,
262 mode, target->NormalImg);
263 target->dirty = TRUE;
264 UnlockMutex (target->mutex);
265 }
266
267 void
TFB_DrawImage_FilledImage(TFB_Image * img,int x,int y,int scale,int scaleMode,Color color,DrawMode mode,TFB_Image * target)268 TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale,
269 int scaleMode, Color color, DrawMode mode, TFB_Image *target)
270 {
271 LockMutex (target->mutex);
272 TFB_DrawCanvas_FilledImage (img, x, y, scale, scaleMode, color,
273 mode, target->NormalImg);
274 target->dirty = TRUE;
275 UnlockMutex (target->mutex);
276 }
277
278 void
TFB_DrawImage_FontChar(TFB_Char * fontChar,TFB_Image * backing,int x,int y,DrawMode mode,TFB_Image * target)279 TFB_DrawImage_FontChar (TFB_Char *fontChar, TFB_Image *backing,
280 int x, int y, DrawMode mode, TFB_Image *target)
281 {
282 LockMutex (target->mutex);
283 TFB_DrawCanvas_FontChar (fontChar, backing, x, y, mode, target->NormalImg);
284 target->dirty = TRUE;
285 UnlockMutex (target->mutex);
286 }
287
288
289 TFB_Image *
TFB_DrawImage_New(TFB_Canvas canvas)290 TFB_DrawImage_New (TFB_Canvas canvas)
291 {
292 TFB_Image *img = HMalloc (sizeof (TFB_Image));
293 img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
294 img->ScaledImg = NULL;
295 img->MipmapImg = NULL;
296 img->FilledImg = NULL;
297 img->colormap_index = -1;
298 img->colormap_version = 0;
299 img->NormalHs = NullHs;
300 img->MipmapHs = NullHs;
301 img->last_scale_hs = NullHs;
302 img->last_scale_type = -1;
303 img->last_scale = 0;
304 img->dirty = FALSE;
305 TFB_DrawCanvas_GetExtent (canvas, &img->extent);
306
307 if (TFB_DrawCanvas_IsPaletted (canvas))
308 {
309 img->NormalImg = canvas;
310 }
311 else
312 {
313 img->NormalImg = TFB_DrawCanvas_ToScreenFormat (canvas);
314 }
315
316 return img;
317 }
318
319 TFB_Image*
TFB_DrawImage_CreateForScreen(int w,int h,BOOLEAN withalpha)320 TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha)
321 {
322 TFB_Image* img = HMalloc (sizeof (TFB_Image));
323 img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
324 img->ScaledImg = NULL;
325 img->MipmapImg = NULL;
326 img->FilledImg = NULL;
327 img->colormap_index = -1;
328 img->colormap_version = 0;
329 img->NormalHs = NullHs;
330 img->MipmapHs = NullHs;
331 img->last_scale_hs = NullHs;
332 img->last_scale_type = -1;
333 img->last_scale = 0;
334 img->extent.width = w;
335 img->extent.height = h;
336
337 img->NormalImg = TFB_DrawCanvas_New_ForScreen (w, h, withalpha);
338
339 return img;
340 }
341
342 TFB_Image *
TFB_DrawImage_New_Rotated(TFB_Image * img,int angle)343 TFB_DrawImage_New_Rotated (TFB_Image *img, int angle)
344 {
345 TFB_Canvas dst;
346 EXTENT size;
347 TFB_Image* newimg;
348
349 /* sanity check */
350 if (!img->NormalImg)
351 {
352 log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
353 "source canvas is NULL! Failing.");
354 return NULL;
355 }
356
357 TFB_DrawCanvas_GetRotatedExtent (img->NormalImg, angle, &size);
358 dst = TFB_DrawCanvas_New_RotationTarget (img->NormalImg, angle);
359 if (!dst)
360 {
361 log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
362 "rotation target canvas not created! Failing.");
363 return NULL;
364 }
365 TFB_DrawCanvas_Rotate (img->NormalImg, dst, angle, size);
366
367 newimg = TFB_DrawImage_New (dst);
368 return newimg;
369 }
370
371 void
TFB_DrawImage_SetMipmap(TFB_Image * img,TFB_Image * mmimg,int hotx,int hoty)372 TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
373 {
374 bool imgpal;
375 bool mmpal;
376
377 if (!img || !mmimg)
378 return;
379
380 LockMutex (img->mutex);
381 LockMutex (mmimg->mutex);
382
383 // Either both images must be using the same colormap, or mipmap image
384 // must not be paletted. This restriction is due to the current
385 // implementation of fill-stamp, which replaces the palette with
386 // fill color.
387 imgpal = TFB_DrawCanvas_IsPaletted (img->NormalImg);
388 mmpal = TFB_DrawCanvas_IsPaletted (mmimg->NormalImg);
389 if (!mmpal || (mmpal && imgpal &&
390 img->colormap_index == mmimg->colormap_index))
391 {
392 img->MipmapImg = mmimg->NormalImg;
393 img->MipmapHs.x = hotx;
394 img->MipmapHs.y = hoty;
395 }
396 else
397 {
398 img->MipmapImg = NULL;
399 }
400
401 UnlockMutex (mmimg->mutex);
402 UnlockMutex (img->mutex);
403 }
404
405 void
TFB_DrawImage_Delete(TFB_Image * image)406 TFB_DrawImage_Delete (TFB_Image *image)
407 {
408 if (image == 0)
409 {
410 log_add (log_Warning, "INTERNAL ERROR: Tried to delete a null image!");
411 /* Should we die here? */
412 return;
413 }
414 LockMutex (image->mutex);
415
416 TFB_DrawCanvas_Delete (image->NormalImg);
417
418 if (image->ScaledImg)
419 {
420 TFB_DrawCanvas_Delete (image->ScaledImg);
421 image->ScaledImg = 0;
422 }
423
424 if (image->FilledImg)
425 {
426 TFB_DrawCanvas_Delete (image->FilledImg);
427 image->FilledImg = 0;
428 }
429
430 UnlockMutex (image->mutex);
431 DestroyMutex (image->mutex);
432
433 HFree (image);
434 }
435
436 void
TFB_DrawImage_FixScaling(TFB_Image * image,int target,int type)437 TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type)
438 {
439 if (image->dirty || !image->ScaledImg ||
440 target != image->last_scale ||
441 type != image->last_scale_type)
442 {
443 image->dirty = FALSE;
444 image->ScaledImg = TFB_DrawCanvas_New_ScaleTarget (image->NormalImg,
445 image->ScaledImg, type, image->last_scale_type);
446
447 if (type == TFB_SCALE_NEAREST)
448 TFB_DrawCanvas_Rescale_Nearest (image->NormalImg,
449 image->ScaledImg, target, &image->NormalHs,
450 &image->extent, &image->last_scale_hs);
451 else if (type == TFB_SCALE_BILINEAR)
452 TFB_DrawCanvas_Rescale_Bilinear (image->NormalImg,
453 image->ScaledImg, target, &image->NormalHs,
454 &image->extent, &image->last_scale_hs);
455 else
456 TFB_DrawCanvas_Rescale_Trilinear (image->NormalImg,
457 image->MipmapImg, image->ScaledImg, target,
458 &image->NormalHs, &image->MipmapHs,
459 &image->extent, &image->last_scale_hs);
460
461 image->last_scale_type = type;
462 image->last_scale = target;
463 }
464 }
465
466 BOOLEAN
TFB_DrawImage_Intersect(TFB_Image * img1,POINT img1org,TFB_Image * img2,POINT img2org,const RECT * interRect)467 TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org,
468 TFB_Image *img2, POINT img2org, const RECT *interRect)
469 {
470 BOOLEAN ret;
471
472 LockMutex (img1->mutex);
473 LockMutex (img2->mutex);
474 ret = TFB_DrawCanvas_Intersect (img1->NormalImg, img1org,
475 img2->NormalImg, img2org, interRect);
476 UnlockMutex (img2->mutex);
477 UnlockMutex (img1->mutex);
478
479 return ret;
480 }
481
482 void
TFB_DrawImage_CopyRect(TFB_Image * source,const RECT * srcRect,TFB_Image * target,POINT dstPt)483 TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect,
484 TFB_Image *target, POINT dstPt)
485 {
486 LockMutex (source->mutex);
487 LockMutex (target->mutex);
488 TFB_DrawCanvas_CopyRect (source->NormalImg, srcRect,
489 target->NormalImg, dstPt);
490 target->dirty = TRUE;
491 UnlockMutex (target->mutex);
492 UnlockMutex (source->mutex);
493 }
494