1 /* -*- C++ -*-
2  *
3  *  Ponscripter_animation.cpp - Methods to manipulate AnimationInfo
4  *
5  *  Copyright (c) 2001-2008 Ogapee (original ONScripter, of which this
6  *  is a fork).
7  *
8  *  ogapee@aqua.dti2.ne.jp
9  *
10  *  This program is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU General Public License as
12  *  published by the Free Software Foundation; either version 2 of the
13  *  License, or (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307 USA
24  */
25 
26 #include "PonscripterLabel.h"
27 
proceedAnimation()28 int PonscripterLabel::proceedAnimation()
29 {
30     int i, minimum_duration = -1;
31     AnimationInfo* anim;
32 
33     for (i = 0; i < 3; i++) {
34         anim = &tachi_info[i];
35         if (anim->showing() && anim->is_animatable) {
36             minimum_duration = estimateNextDuration(anim, anim->pos,
37                                                     minimum_duration);
38         }
39     }
40 
41     for (i = MAX_SPRITE_NUM - 1; i >= 0; i--) {
42         anim = &sprite_info[i];
43         if (anim->showing() && anim->is_animatable) {
44             minimum_duration = estimateNextDuration(anim, anim->pos,
45                                                     minimum_duration);
46         }
47     }
48 
49     for (i = MAX_SPRITE2_NUM - 1; i >= 0; i--) {
50         anim = &sprite2_info[i];
51         if (anim->showing() && anim->is_animatable) {
52             minimum_duration = estimateNextDuration(anim, anim->pos,
53                                                     minimum_duration);
54         }
55     }
56 
57     if (!textgosub_label
58         && (clickstr_state == CLICK_WAIT
59             || clickstr_state == CLICK_NEWPAGE)) {
60         if (clickstr_state == CLICK_WAIT)
61             anim = &cursor_info[CURSOR_WAIT_NO];
62         else if (clickstr_state == CLICK_NEWPAGE)
63             anim = &cursor_info[CURSOR_NEWPAGE_NO];
64 
65         if (anim->showing() && anim->is_animatable) {
66             SDL_Rect dst_rect = anim->pos;
67             if (!anim->abs_flag) {
68                 dst_rect.x += int(floor(sentence_font.GetX() * screen_ratio1 / screen_ratio2));
69                 dst_rect.y += sentence_font.GetY() * screen_ratio1 / screen_ratio2;
70             }
71 
72             minimum_duration = estimateNextDuration(anim, dst_rect,
73                                                     minimum_duration);
74         }
75     }
76 
77     if (minimum_duration == -1) minimum_duration = 0;
78 
79     return minimum_duration;
80 }
81 
82 
estimateNextDuration(AnimationInfo * anim,SDL_Rect & rect,int minimum)83 int PonscripterLabel::estimateNextDuration(AnimationInfo* anim, SDL_Rect &rect, int minimum)
84 {
85     if (anim->remaining_time == 0) {
86         if (minimum == -1
87             || minimum > anim->duration_list[anim->current_cell])
88             minimum = anim->duration_list[anim->current_cell];
89 
90         if (anim->proceedAnimation())
91             flushDirect(rect, refreshMode() | (draw_cursor_flag ? REFRESH_CURSOR_MODE : 0));
92     }
93     else {
94         if (minimum == -1
95             || minimum > anim->remaining_time)
96             minimum = anim->remaining_time;
97     }
98 
99     return minimum;
100 }
101 
102 
resetRemainingTime(int t)103 void PonscripterLabel::resetRemainingTime(int t)
104 {
105     int i;
106     AnimationInfo* anim;
107 
108     for (i = 0; i < 3; i++) {
109         anim = &tachi_info[i];
110         if (anim->showing() && anim->is_animatable) {
111             anim->remaining_time -= t;
112         }
113     }
114 
115     for (i = MAX_SPRITE_NUM - 1; i >= 0; i--) {
116         anim = &sprite_info[i];
117         if (anim->showing() && anim->is_animatable) {
118             anim->remaining_time -= t;
119         }
120     }
121 
122     for (i = MAX_SPRITE2_NUM - 1; i >= 0; i--) {
123         anim = &sprite2_info[i];
124         if (anim->showing() && anim->is_animatable) {
125             anim->remaining_time -= t;
126         }
127     }
128 
129     if (!textgosub_label
130         && (clickstr_state == CLICK_WAIT
131             || clickstr_state == CLICK_NEWPAGE)) {
132         if (clickstr_state == CLICK_WAIT)
133             anim = &cursor_info[CURSOR_WAIT_NO];
134         else if (clickstr_state == CLICK_NEWPAGE)
135             anim = &cursor_info[CURSOR_NEWPAGE_NO];
136 
137         if (anim->showing() && anim->is_animatable) {
138             anim->remaining_time -= t;
139         }
140     }
141 }
142 
143 
downscale4x(SDL_Surface * src,SDL_Rect * srcpos,SDL_Surface * dst,SDL_Rect * dstpos)144 void downscale4x(SDL_Surface* src, SDL_Rect* srcpos, SDL_Surface* dst, SDL_Rect* dstpos)
145 {
146     SDL_LockSurface(src);
147     SDL_LockSurface(dst);
148 
149     const int sp = src->pitch;
150     const int dp = dst->pitch;
151 
152     SDL_Rect sr, dr;
153     if (srcpos) sr = *srcpos;else { sr.x = 0; sr.y = 0; sr.w = src->w; sr.h = src->h; }
154 
155     if (dstpos) dr = *dstpos;else { dr.x = 0; dr.y = 0; }
156 
157     dr.w = sr.w / 4; dr.h = sr.h / 4;
158 
159     Uint8* src_scan = (Uint8*) src->pixels + sr.y * sp + sr.x * 4;
160     Uint8* dst_scan = (Uint8*) dst->pixels + dr.y * dp + dr.x * 4;
161 
162     for (int rows = dr.h; rows-- > 0; src_scan += sp * 4, dst_scan += dp) {
163         Uint32* src1 = (Uint32*) src_scan;
164         Uint32* src2 = (Uint32*) (src_scan + sp);
165         Uint32* src3 = (Uint32*) (src_scan + sp * 2);
166         Uint32* src4 = (Uint32*) (src_scan + sp * 3);
167         Uint32* dest = (Uint32*) dst_scan;
168         int cols = dr.w;
169         while (cols-- > 0) {
170             Uint32 a, b, c, d;
171             Uint32 p;
172             p = *src1++;
173 	    a = p >> 24;
174 	    b = (p >> 16) & 0xff;
175 	    c = (p >> 8) & 0xff;
176 	    d = p & 0xff;
177 #define AddPx(s) p = *s; a += p >> 24; b += (p >> 16) & 0xff;	\
178 	                 c += (p >> 8) & 0xff; d += p & 0xff
179             AddPx(src1++); AddPx(src1++); AddPx(src1++);
180             AddPx(src2++); AddPx(src2++); AddPx(src2++); AddPx(src2++);
181             AddPx(src3++); AddPx(src3++); AddPx(src3++); AddPx(src3++);
182             AddPx(src4++); AddPx(src4++); AddPx(src4++); AddPx(src4++);
183 #undef AddPx
184             a >>= 4; b >>= 4; c >>= 4; d >>= 4;
185             *dest++ = a << 24 | b << 16 | c << 8 | d;
186         }
187     }
188 
189     SDL_UnlockSurface(src);
190     SDL_UnlockSurface(dst);
191 }
192 
193 
setupAnimationInfo(AnimationInfo * anim,Fontinfo * info)194 void PonscripterLabel::setupAnimationInfo(AnimationInfo* anim, Fontinfo* info)
195 {
196     anim->deleteImage();
197     anim->abs_flag = true;
198 
199     if (anim->trans_mode == AnimationInfo::TRANS_STRING) {
200         Fontinfo f_info = info ? *info : sentence_font;
201 
202         // parse ponscripter tags if a standard text string,
203         //otherwise don't; logsp strings are "predigested", as it were,
204         //so they shouldn't be parsed for tags again
205         // ...really should do some more explicit indication
206         //if something is a log string than using "skip_whitespace";
207         //moving parseTags out of this function could help too FIXME
208         if (anim->skip_whitespace)
209             anim->file_name = parseTags(anim->file_name);
210 
211         if (anim->font_size_x >= 0) { // in case of Sprite, not rclick menu
212             f_info.top_x = anim->pos.x * screen_ratio2 / screen_ratio1;
213             f_info.top_y = anim->pos.y * screen_ratio2 / screen_ratio1;
214             f_info.setTateYoko(0);
215             f_info.style = Default;
216 
217             f_info.set_size(anim->font_size_y);
218             f_info.set_mod_size(0);
219             if (anim->font_pitch >= 0)
220                f_info.pitch_x = anim->font_pitch;
221 
222             if (anim->is_single_line) {
223                f_info.area_x = int(ceil(f_info.StringAdvance(anim->file_name)));
224                f_info.area_y = f_info.line_space();
225             }
226 
227             if (anim->is_centered_text) {
228                anim->pos.x -= f_info.area_x / 2;
229                f_info.top_x = anim->pos.x * screen_ratio2 / screen_ratio1;
230             }
231         }
232 
233         SDL_Rect pos;
234         if (anim->is_tight_region) {
235             drawString(anim->file_name, anim->color_list[anim->current_cell],
236                        &f_info, false, NULL, &pos, NULL, anim->skip_whitespace);
237         }
238         else {
239             pos = f_info.getFullArea(screen_ratio1, screen_ratio2);
240         }
241 
242         if (info) info->SetXY(f_info.GetXOffset(), f_info.GetYOffset());
243 
244         anim->allocImage(pos.w * anim->num_of_cells, pos.h);
245         anim->fill(0, 0, 0, 0);
246 
247         f_info.top_x = f_info.top_y = 0;
248         for (int i = 0; i < anim->num_of_cells; i++) {
249             f_info.clear();
250             f_info.style = Default;
251             drawString(anim->file_name, anim->color_list[i], &f_info, false,
252                        NULL, NULL, anim, anim->skip_whitespace);
253             f_info.top_x += anim->pos.w * screen_ratio2 / screen_ratio1;
254         }
255     }
256     else {
257         bool has_alpha;
258         SDL_Surface *surface = loadImage( anim->file_name, &has_alpha, anim->twox, anim->isflipped );
259 
260         SDL_Surface *surface_m = NULL;
261         if (anim->trans_mode == AnimationInfo::TRANS_MASK)
262             surface_m = loadImage( anim->mask_file_name, NULL, anim->twox, anim->isflipped);
263 
264         anim->setupImage(surface, surface_m, has_alpha);
265         if (surface)   SDL_FreeSurface(surface);
266         if (surface_m) SDL_FreeSurface(surface_m);
267     }
268 }
269 
270 
parseTaggedString(AnimationInfo * anim,bool is_mask)271 void PonscripterLabel::parseTaggedString(AnimationInfo* anim, bool is_mask)
272 {
273     if (!anim->image_name) return;
274 
275     anim->removeTag();
276 
277     int multiplier = multiplier_style <= ScriptHandler::UMINEKO ? 1 : res_multiplier;
278 
279     int i;
280     const char* buffer = anim->image_name;
281     anim->num_of_cells = 1;
282     anim->current_cell = 0;
283     anim->trans_mode = trans_mode;
284     //use COPY as default trans_mode for masks
285     if (is_mask) anim->trans_mode = AnimationInfo::TRANS_COPY;
286 
287     anim->twox = false;
288     anim->isflipped = false;
289     if (buffer[0] == ':') {
290         while (*++buffer == ' ') ;
291         if (buffer[0] == 'b') {
292             anim->twox = true;
293             buffer++;
294         }
295         if (buffer[0] == 'f') {
296             anim->isflipped = true;
297             buffer++;
298         }
299 
300         if (buffer[0] == 'a') {
301             anim->trans_mode = AnimationInfo::TRANS_ALPHA;
302             buffer++;
303         }
304         else if (buffer[0] == 'l') {
305             anim->trans_mode = AnimationInfo::TRANS_TOPLEFT;
306             buffer++;
307         }
308         else if (buffer[0] == 'r') {
309             anim->trans_mode = AnimationInfo::TRANS_TOPRIGHT;
310             buffer++;
311         }
312         else if (buffer[0] == 'c') {
313             anim->trans_mode = AnimationInfo::TRANS_COPY;
314             buffer++;
315         }
316         else if (buffer[0] == 's' || buffer[0] == 'S') {
317             anim->trans_mode = AnimationInfo::TRANS_STRING;
318             anim->is_centered_text = buffer[0] == 'S';
319             buffer++;
320             anim->num_of_cells = 0;
321             if (*buffer == '/') {
322                 buffer++;
323                 script_h.getNext();
324 
325                 script_h.pushCurrent((char*) buffer); // FIXME: unsafe
326                 anim->font_size_x = script_h.readIntValue() * multiplier;
327                 anim->font_size_y = script_h.hasMoreArgs()
328 		                  ? script_h.readIntValue() * multiplier
329 		                  : anim->font_size_x;
330                 anim->font_pitch  = script_h.hasMoreArgs()
331 		                  ? script_h.readIntValue()
332 		                  : 0;
333                 if (script_h.hasMoreArgs())
334                     script_h.readIntValue(); // antialiasing mode (ignored)
335 
336                 buffer = script_h.getNext();
337                 script_h.popCurrent();
338             }
339             else {
340                 anim->font_size_x = sentence_font.size();
341                 anim->font_size_y = sentence_font.size();
342                 anim->font_pitch  = sentence_font.pitch_x;
343             }
344 
345             while (buffer[0] != '#' && buffer[0] != '\0') buffer++;
346             i = 0;
347 	    if (buffer[i] == '#' && buffer[i + 1] == '#') {
348 		buffer += 2;
349 		anim->num_of_cells = 2;
350 		anim->color_list.clear();
351 		anim->color_list.push_back(readColour("#FFFFFF"));
352 		anim->color_list.push_back(readColour("#000000"));
353 	    }
354 	    else {
355 		while (buffer[i] == '#') {
356 		    anim->num_of_cells++;
357 		    i += 7;
358 		}
359 		anim->color_list.clear();
360 		for (i = 0; i < anim->num_of_cells; i++) {
361 		    anim->color_list.push_back(readColour(buffer));
362 		    buffer += 7;
363 		}
364 	    }
365 	}
366         else if (buffer[0] == 'm') {
367             anim->trans_mode = AnimationInfo::TRANS_MASK;
368             const char* start = ++buffer;
369             while (buffer[0] != ';' && buffer[0] != 0x0a && buffer[0])
370 		buffer++;
371             if (buffer[0] == ';')
372                 anim->mask_file_name = pstring(start, buffer - start);
373         }
374         else if (buffer[0] == '#') {
375             anim->trans_mode = AnimationInfo::TRANS_DIRECT;
376             anim->direct_color = readColour(buffer);
377             buffer += 7;
378         }
379         else if (buffer[0] == '!') {
380             anim->trans_mode = AnimationInfo::TRANS_PALETTE;
381             buffer++;
382             anim->palette_number = getNumberFromBuffer((const char**) &buffer);
383         }
384 
385         if (anim->trans_mode != AnimationInfo::TRANS_STRING)
386             while (buffer[0] != '/' && buffer[0] != ';' && buffer[0])
387 		buffer++;
388     }
389 
390     if (buffer[0] == '/') {
391         buffer++;
392         anim->num_of_cells = getNumberFromBuffer((const char**) &buffer);
393         buffer++;
394         if (anim->num_of_cells == 0) {
395             fprintf(stderr, "PonscripterLabel::parseTaggedString  The number of cells is 0\n");
396             return;
397         }
398 
399         if (*buffer == '<') {
400             buffer++;
401 	    anim->duration_list.resize(anim->num_of_cells);
402             for (i = 0; i < anim->num_of_cells; i++) {
403                 anim->duration_list[i] = getNumberFromBuffer((const char**) &buffer);
404                 buffer++;
405             }
406 
407             buffer++; // skip '>'
408         }
409         else {
410             anim->duration_list.assign(anim->num_of_cells,
411 		      getNumberFromBuffer((const char**) &buffer));
412             buffer++;
413         }
414 
415         anim->loop_mode = *buffer++ - '0'; // 3...no animation
416         if (anim->loop_mode != 3) anim->is_animatable = true;
417 
418         while (buffer[0] != ';' && buffer[0] != '\0') buffer++;
419     }
420 
421     if (buffer[0] == ';') buffer++;
422 
423     if (anim->trans_mode == AnimationInfo::TRANS_STRING && buffer[0] == '$') {
424         script_h.pushCurrent((char*) buffer); // FIXME: unsafe
425         anim->file_name = script_h.readStrValue();
426         script_h.popCurrent();
427     }
428     else {
429         anim->file_name = buffer;
430     }
431 }
432 
433 
drawTaggedSurface(SDL_Surface * dst_surface,AnimationInfo * anim,SDL_Rect & clip)434 void PonscripterLabel::drawTaggedSurface(SDL_Surface* dst_surface, AnimationInfo* anim, SDL_Rect &clip)
435 {
436     SDL_Rect poly_rect = anim->pos;
437     if (!anim->abs_flag) {
438         poly_rect.x += int (floor(sentence_font.GetX() * screen_ratio1 / screen_ratio2));
439         poly_rect.y += sentence_font.GetY() * screen_ratio1 / screen_ratio2;
440     }
441 
442     if (anim->affine_flag)
443       anim->blendOnSurface2(dst_surface, poly_rect.x, poly_rect.y,
444           clip, anim->trans);
445     else
446       anim->blendOnSurface(dst_surface, poly_rect.x, poly_rect.y,
447           clip, anim->trans);
448 }
449 
450 
stopAnimation(int click)451 void PonscripterLabel::stopAnimation(int click)
452 {
453     int no;
454 
455     if (!(event_mode & WAIT_TIMER_MODE)) return;
456 
457     event_mode    &= ~WAIT_TIMER_MODE;
458     remaining_time = -1;
459     if (textgosub_label) return;
460 
461     if (click == CLICK_WAIT) no = CURSOR_WAIT_NO;
462     else if (click == CLICK_NEWPAGE) no = CURSOR_NEWPAGE_NO;
463     else return;
464 
465     if (cursor_info[no].image_surface == NULL) return;
466 
467     SDL_Rect dst_rect = cursor_info[no].pos;
468 
469     if (!cursor_info[no].abs_flag) {
470         dst_rect.x += int (floor(sentence_font.GetX() * screen_ratio1 / screen_ratio2));
471         dst_rect.y += sentence_font.GetY() * screen_ratio1 / screen_ratio2;
472     }
473 
474     flushDirect(dst_rect, refreshMode());
475 }
476