1 /* 2 * Common image-related functions. 3 * 4 * Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program 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 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "display.h" 21 #include "image.h" 22 #include "utils.h" 23 24 25 26 /* Obtain the position for the stored region from the given frame. */ 27 28 position_t *image_get_stored_position(sprite_t *s, int frame) 29 { 30 return &s->pos[frame]; 31 } 32 33 /* Obtain the image data for the stored region from the given frame. */ 34 35 uint8_t *image_get_stored_region(stored_regions_t *r, int frame) 36 { 37 return r->image + r->size * frame; 38 } 39 40 41 42 /* Copy a region from the screen to the store, then blit the image. */ 43 44 void image_plot_sprite(sprite_t *s, int x, int y, int key) 45 { 46 int frame = 0; 47 position_t *p = 0; 48 49 if (s->regions) 50 { 51 frame = s->cfg->frame; 52 p = image_get_stored_position(s, frame); 53 54 /* Copy to the stored region. */ 55 56 display_copy(s->cfg, image_get_stored_region(s->regions, frame), 57 s->image->width, s->image->height / s->yscale, 1, 58 x, y, -1, 0); 59 } 60 61 /* Plot to the screen. */ 62 63 display_copy(s->cfg, s->image->image, 64 s->image->width, s->image->height, s->yscale, 65 x, y, key, 1); 66 67 if (s->regions) 68 { 69 /* Record the stored background details. */ 70 71 p->x = x; 72 p->y = y; 73 74 if (frame >= s->regions->stored) 75 s->regions->stored = frame + 1; 76 } 77 } 78 79 /* Copy a region from the store to the screen, restoring the original 80 background. */ 81 82 void image_unplot_sprite(sprite_t *s) 83 { 84 int frame; 85 position_t *p; 86 87 if (!s->regions) 88 return; 89 90 frame = s->cfg->frame; 91 p = image_get_stored_position(s, frame); 92 93 /* Only unplot the sprite if a region was stored for the frame. */ 94 95 if (s->regions->stored > frame) 96 display_copy(s->cfg, image_get_stored_region(s->regions, frame), 97 s->image->width, s->image->height / s->yscale, 1, 98 p->x, p->y, -1, 1); 99 } 100 101 /* Unplot a sprite by restoring a region from the background image. */ 102 103 void image_unplot_sprite_from_image(sprite_t *s, sprite_t *bg, 104 int xorigin, int yorigin) 105 { 106 int frame = s->cfg->frame; 107 position_t *p = image_get_stored_position(s, frame); 108 109 /* Plot the region of the background using the sprite image dimensions 110 converted to background image dimensions at the sprite's position on the 111 display. */ 112 113 image_update_tiled_image(bg, xorigin, yorigin, 114 s->image->width, (s->image->height / s->yscale) * bg->yscale, 115 p->x, p->y); 116 } 117 118 119 120 /* Plot a section of an image without storing the background beforehand. */ 121 122 void image_plot_sprite_section(sprite_t *s, 123 int xstart, int ystart, int xsize, int ysize, 124 int x, int y, int key) 125 { 126 display_copy_section(s->cfg, s->image->image, 127 s->image->width, s->image->height, 128 xstart, ystart, xsize, ysize, s->yscale, 129 x, y, key, 1); 130 } 131 132 133 134 /* Tile a sprite, using the given source origin, filling a display region. */ 135 136 void image_tile_sprite(sprite_t *s, int xsource, int ysource, 137 int width, int height, 138 int xdisplay, int ydisplay) 139 { 140 /* Determine the portion of the sprite to be plotted in the first column. */ 141 142 int source_width = s->image->width - xsource; 143 int source_height; 144 int total_height; 145 int x, y; 146 int xs, ys; 147 148 /* Fill (xdisplay, ydisplay) with (width, height) from source, slice by 149 slice. */ 150 151 x = xdisplay; 152 xs = xsource; 153 154 while (width) 155 { 156 /* Fill (x, ydisplay) with (source_width, height) from source, with the 157 height being divided into image-sized pieces. */ 158 159 total_height = height; 160 source_height = s->image->height - ysource; 161 y = ydisplay; 162 ys = ysource; 163 164 while (total_height) 165 { 166 /* Plot as much of the image as is available from the given source 167 coordinates. */ 168 169 image_plot_sprite_section(s, xs, ys, 170 min(width, source_width), min(total_height, source_height), 171 x, y, -1); 172 173 /* Continue to plot the image again to fill the slice. */ 174 175 if (source_height >= total_height) 176 break; 177 178 total_height -= source_height; 179 y += source_height / s->yscale; 180 ys = 0; 181 source_height = s->image->height; 182 } 183 184 /* Get the next slice of the column. */ 185 186 if (source_width >= width) 187 break; 188 189 width -= source_width; 190 x += source_width; 191 xs = 0; 192 source_width = s->image->width; 193 } 194 } 195 196 /* Plot a scrolling tiled image upon a viewport update. */ 197 198 void image_update_scrolled_tiled_image(sprite_t *s, int xorigin, int yorigin, 199 int xstep, int ystep) 200 { 201 /* The display regions are either the left or right edge... */ 202 203 int xedge = xstep < 0 ? 0 : s->cfg->line_length - xstep; 204 int xdisplay = xedge; 205 206 /* and either the top or bottom edge... */ 207 208 int yedge = ystep < 0 ? 0 : s->cfg->line_count * s->yscale - ystep; 209 int ydisplay = yedge / s->yscale; 210 211 /* Determine the origin position within the image. */ 212 213 int xpos = wrap_value(xorigin, s->image->width); 214 int ypos = wrap_value(yorigin, s->image->height); 215 int xsource, ysource; 216 217 /* Horizontal scrolling requires columns spanning the height of the screen 218 at the appropriate edge (left or right). */ 219 220 /* The column width is the absolute increment. */ 221 222 if (xstep) 223 { 224 /* Find the source position for the appropriate edge. */ 225 226 xsource = wrap_value(xpos + xedge, s->image->width); 227 ysource = ypos; 228 229 /* Request tiling in the source coordinates. */ 230 231 image_tile_sprite(s, xsource, ysource, 232 abs(xstep), s->cfg->line_count * s->yscale, 233 xdisplay, 0); 234 } 235 236 /* Vertical scrolling requires columns across the width of the screen at the 237 appropriate edge (top or bottom). */ 238 239 if (ystep) 240 { 241 /* Find the source position for the appropriate edge. */ 242 243 xsource = xpos; 244 ysource = wrap_value(ypos + yedge, s->image->height); 245 246 /* Request tiling in the source coordinates. */ 247 248 image_tile_sprite(s, xsource, ysource, 249 s->cfg->line_length, abs(ystep), 250 0, ydisplay); 251 } 252 } 253 254 /* Plot a region of a tiled image. */ 255 256 void image_update_tiled_image(sprite_t *s, int xorigin, int yorigin, 257 int width, int height, 258 int xdisplay, int ydisplay) 259 { 260 /* Find the source position for the region. */ 261 262 int xsource = wrap_value(xorigin + xdisplay, s->image->width); 263 int ysource = wrap_value(yorigin + ydisplay * s->yscale, s->image->height); 264 265 /* Request tiling in the source coordinates. */ 266 267 image_tile_sprite(s, xsource, ysource, 268 width, height, 269 xdisplay, ydisplay); 270 }