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