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@119 | 28 | position_t *image_get_stored_position(stored_regions_t *r, int frame) |
paul@118 | 29 | { |
paul@119 | 30 | return &r->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@121 | 42 | /* Copy a region from the screen to the store, then blit the image. */ |
paul@121 | 43 | |
paul@121 | 44 | void image_plot_sprite(sprite_t *s, int x, int y, int key) |
paul@121 | 45 | { |
paul@126 | 46 | int frame = 0; |
paul@126 | 47 | position_t *p = 0; |
paul@121 | 48 | |
paul@126 | 49 | if (s->regions) |
paul@126 | 50 | { |
paul@126 | 51 | frame = s->cfg->frame; |
paul@126 | 52 | p = image_get_stored_position(s->regions, frame); |
paul@121 | 53 | |
paul@126 | 54 | /* Copy to the stored region. */ |
paul@126 | 55 | |
paul@126 | 56 | display_copy(s->cfg, image_get_stored_region(s->regions, frame), |
paul@126 | 57 | s->image->width, s->image->height / s->yscale, 1, |
paul@126 | 58 | x, y, -1, 0); |
paul@126 | 59 | } |
paul@121 | 60 | |
paul@121 | 61 | /* Plot to the screen. */ |
paul@121 | 62 | |
paul@121 | 63 | display_copy(s->cfg, s->image->image, |
paul@121 | 64 | s->image->width, s->image->height, s->yscale, |
paul@121 | 65 | x, y, key, 1); |
paul@121 | 66 | |
paul@126 | 67 | if (s->regions) |
paul@126 | 68 | { |
paul@126 | 69 | /* Record the stored background details. */ |
paul@121 | 70 | |
paul@126 | 71 | p->x = x; |
paul@126 | 72 | p->y = y; |
paul@121 | 73 | |
paul@126 | 74 | if (frame >= s->regions->stored) |
paul@126 | 75 | s->regions->stored = frame + 1; |
paul@126 | 76 | } |
paul@121 | 77 | } |
paul@121 | 78 | |
paul@121 | 79 | /* Copy a region from the store to the screen, restoring the original |
paul@121 | 80 | background. */ |
paul@121 | 81 | |
paul@121 | 82 | void image_unplot_sprite(sprite_t *s) |
paul@121 | 83 | { |
paul@126 | 84 | int frame; |
paul@126 | 85 | position_t *p; |
paul@126 | 86 | |
paul@126 | 87 | if (!s->regions) |
paul@126 | 88 | return; |
paul@126 | 89 | |
paul@126 | 90 | frame = s->cfg->frame; |
paul@126 | 91 | p = image_get_stored_position(s->regions, frame); |
paul@121 | 92 | |
paul@121 | 93 | /* Only unplot the sprite if a region was stored for the frame. */ |
paul@121 | 94 | |
paul@121 | 95 | if (s->regions->stored > frame) |
paul@121 | 96 | display_copy(s->cfg, image_get_stored_region(s->regions, frame), |
paul@121 | 97 | s->image->width, s->image->height / s->yscale, 1, |
paul@121 | 98 | p->x, p->y, -1, 1); |
paul@121 | 99 | } |
paul@128 | 100 | |
paul@128 | 101 | |
paul@128 | 102 | |
paul@128 | 103 | /* Plot a section of an image without storing the background beforehand. */ |
paul@128 | 104 | |
paul@128 | 105 | void image_plot_sprite_section(sprite_t *s, |
paul@128 | 106 | int xstart, int ystart, int xsize, int ysize, |
paul@128 | 107 | int x, int y, int key) |
paul@128 | 108 | { |
paul@128 | 109 | display_copy_section(s->cfg, s->image->image, |
paul@128 | 110 | s->image->width, s->image->height, |
paul@128 | 111 | xstart, ystart, xsize, ysize, s->yscale, |
paul@128 | 112 | x, y, key, 1); |
paul@128 | 113 | } |
paul@129 | 114 | |
paul@129 | 115 | |
paul@129 | 116 | |
paul@129 | 117 | /* Tile a sprite, using the given source origin, filling a display region. */ |
paul@129 | 118 | |
paul@129 | 119 | void image_tile_sprite(sprite_t *s, int xsource, int ysource, |
paul@129 | 120 | int width, int height, |
paul@129 | 121 | int xdisplay, int ydisplay) |
paul@129 | 122 | { |
paul@129 | 123 | /* Determine the portion of the sprite to be plotted in the first column. */ |
paul@129 | 124 | |
paul@129 | 125 | int source_width = s->image->width - xsource; |
paul@129 | 126 | int source_height; |
paul@129 | 127 | int total_height; |
paul@129 | 128 | int x, y; |
paul@129 | 129 | int xs, ys; |
paul@129 | 130 | |
paul@129 | 131 | /* Fill (xdisplay, ydisplay) with (width, height) from source, slice by |
paul@129 | 132 | slice. */ |
paul@129 | 133 | |
paul@129 | 134 | x = xdisplay; |
paul@129 | 135 | xs = xsource; |
paul@129 | 136 | |
paul@129 | 137 | while (width) |
paul@129 | 138 | { |
paul@129 | 139 | /* Fill (x, ydisplay) with (source_width, height) from source, with the |
paul@129 | 140 | height being divided into image-sized pieces. */ |
paul@129 | 141 | |
paul@129 | 142 | total_height = height; |
paul@129 | 143 | source_height = s->image->height - ysource; |
paul@129 | 144 | y = ydisplay; |
paul@129 | 145 | ys = ysource; |
paul@129 | 146 | |
paul@129 | 147 | while (total_height) |
paul@129 | 148 | { |
paul@129 | 149 | /* Plot as much of the image as is available from the given source |
paul@129 | 150 | coordinates. */ |
paul@129 | 151 | |
paul@129 | 152 | image_plot_sprite_section(s, xs, ys, |
paul@129 | 153 | min(width, source_width), min(total_height, source_height), |
paul@129 | 154 | x, y, -1); |
paul@129 | 155 | |
paul@129 | 156 | /* Continue to plot the image again to fill the slice. */ |
paul@129 | 157 | |
paul@129 | 158 | if (source_height >= total_height) |
paul@129 | 159 | break; |
paul@129 | 160 | |
paul@129 | 161 | total_height -= source_height; |
paul@129 | 162 | y += source_height / s->yscale; |
paul@129 | 163 | ys = 0; |
paul@129 | 164 | source_height = s->image->height; |
paul@129 | 165 | } |
paul@129 | 166 | |
paul@129 | 167 | /* Get the next slice of the column. */ |
paul@129 | 168 | |
paul@129 | 169 | if (source_width >= width) |
paul@129 | 170 | break; |
paul@129 | 171 | |
paul@129 | 172 | width -= source_width; |
paul@129 | 173 | x += source_width; |
paul@129 | 174 | xs = 0; |
paul@129 | 175 | source_width = s->image->width; |
paul@129 | 176 | } |
paul@129 | 177 | } |
paul@130 | 178 | |
paul@130 | 179 | /* Plot a scrolling tiled image upon a viewport update. */ |
paul@130 | 180 | |
paul@130 | 181 | void image_update_scrolled_tiled_image(sprite_t *s, int xorigin, int yorigin, |
paul@130 | 182 | int xstep, int ystep) |
paul@130 | 183 | { |
paul@130 | 184 | /* The display regions are either the left or right edge... */ |
paul@130 | 185 | |
paul@130 | 186 | int xedge = xstep < 0 ? 0 : s->cfg->line_length - xstep; |
paul@130 | 187 | int xdisplay = xedge; |
paul@130 | 188 | |
paul@130 | 189 | /* and either the top or bottom edge... */ |
paul@130 | 190 | |
paul@130 | 191 | int yedge = ystep < 0 ? 0 : s->cfg->line_count * s->yscale - ystep; |
paul@130 | 192 | int ydisplay = yedge / s->yscale; |
paul@130 | 193 | |
paul@130 | 194 | /* Determine the origin position within the image. */ |
paul@130 | 195 | |
paul@130 | 196 | int xpos = wrap_value(xorigin, s->image->width); |
paul@130 | 197 | int ypos = wrap_value(yorigin, s->image->height); |
paul@130 | 198 | int xsource, ysource; |
paul@130 | 199 | |
paul@130 | 200 | /* Horizontal scrolling requires columns spanning the height of the screen |
paul@130 | 201 | at the appropriate edge (left or right). */ |
paul@130 | 202 | |
paul@130 | 203 | /* The column width is the absolute increment. */ |
paul@130 | 204 | |
paul@130 | 205 | if (xstep) |
paul@130 | 206 | { |
paul@130 | 207 | /* Find the source position for the appropriate edge. */ |
paul@130 | 208 | |
paul@130 | 209 | xsource = wrap_value(xpos + xedge, s->image->width); |
paul@130 | 210 | ysource = ypos; |
paul@130 | 211 | |
paul@130 | 212 | /* Request tiling in the source coordinates. */ |
paul@130 | 213 | |
paul@130 | 214 | image_tile_sprite(s, xsource, ysource, |
paul@130 | 215 | abs(xstep), s->cfg->line_count * s->yscale, |
paul@130 | 216 | xdisplay, 0); |
paul@130 | 217 | } |
paul@130 | 218 | |
paul@130 | 219 | /* Vertical scrolling requires columns across the width of the screen at the |
paul@130 | 220 | appropriate edge (top or bottom). */ |
paul@130 | 221 | |
paul@130 | 222 | if (ystep) |
paul@130 | 223 | { |
paul@130 | 224 | /* Find the source position for the appropriate edge. */ |
paul@130 | 225 | |
paul@130 | 226 | xsource = xpos; |
paul@130 | 227 | ysource = wrap_value(ypos + yedge, s->image->height); |
paul@130 | 228 | |
paul@130 | 229 | /* Request tiling in the source coordinates. */ |
paul@130 | 230 | |
paul@130 | 231 | image_tile_sprite(s, xsource, ysource, |
paul@130 | 232 | s->cfg->line_length, abs(ystep), |
paul@130 | 233 | 0, ydisplay); |
paul@130 | 234 | } |
paul@130 | 235 | } |