+ Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
+ int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
+ int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
+
+ bitmaps[IMG_BITMAP_CUSTOM] = ZoomBitmap(bitmap, width, height);
+
+ // set game bitmap pointer to custom sized bitmap (newly created)
+ bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
+}
+
+static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
+ int tile_size, boolean create_small_bitmaps)
+{
+ Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
+ Bitmap *tmp_bitmap_final = NULL;
+ Bitmap *tmp_bitmap_0 = NULL;
+ Bitmap *tmp_bitmap_1 = NULL;
+ Bitmap *tmp_bitmap_2 = NULL;
+ Bitmap *tmp_bitmap_4 = NULL;
+ Bitmap *tmp_bitmap_8 = NULL;
+ Bitmap *tmp_bitmap_16 = NULL;
+ Bitmap *tmp_bitmap_32 = NULL;
+ int width_final, height_final;
+ int width_0, height_0;
+ int width_1, height_1;
+ int width_2, height_2;
+ int width_4, height_4;
+ int width_8, height_8;
+ int width_16, height_16;
+ int width_32, height_32;
+ int old_width, old_height;
+ int i;
+
+ print_timestamp_init("CreateScaledBitmaps");
+
+ old_width = old_bitmap->width;
+ old_height = old_bitmap->height;
+
+ // calculate new image dimensions for final image size
+ width_final = old_width * zoom_factor;
+ height_final = old_height * zoom_factor;
+
+ // get image with final size (this might require scaling up)
+ // ("final" size may result in non-standard tile size image)
+ if (zoom_factor != 1)
+ tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
+ else
+ tmp_bitmap_final = old_bitmap;
+
+ UPDATE_BUSY_STATE();
+
+ width_0 = width_1 = width_final;
+ height_0 = height_1 = height_final;
+
+ tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
+
+ if (create_small_bitmaps)
+ {
+ // check if we have a non-gameplay tile size image
+ if (tile_size != gfx.game_tile_size)
+ {
+ // get image with gameplay tile size
+ width_0 = width_final * gfx.game_tile_size / tile_size;
+ height_0 = height_final * gfx.game_tile_size / tile_size;
+
+ if (width_0 == old_width)
+ tmp_bitmap_0 = old_bitmap;
+ else if (width_0 == width_final)
+ tmp_bitmap_0 = tmp_bitmap_final;
+ else
+ tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
+
+ UPDATE_BUSY_STATE();
+ }
+
+ // check if we have a non-standard tile size image
+ if (tile_size != gfx.standard_tile_size)
+ {
+ // get image with standard tile size
+ width_1 = width_final * gfx.standard_tile_size / tile_size;
+ height_1 = height_final * gfx.standard_tile_size / tile_size;
+
+ if (width_1 == old_width)
+ tmp_bitmap_1 = old_bitmap;
+ else if (width_1 == width_final)
+ tmp_bitmap_1 = tmp_bitmap_final;
+ else if (width_1 == width_0)
+ tmp_bitmap_1 = tmp_bitmap_0;
+ else
+ tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
+
+ UPDATE_BUSY_STATE();
+ }
+
+ // calculate new image dimensions for small images
+ width_2 = width_1 / 2;
+ height_2 = height_1 / 2;
+ width_4 = width_1 / 4;
+ height_4 = height_1 / 4;
+ width_8 = width_1 / 8;
+ height_8 = height_1 / 8;
+ width_16 = width_1 / 16;
+ height_16 = height_1 / 16;
+ width_32 = width_1 / 32;
+ height_32 = height_1 / 32;
+
+ // get image with 1/2 of normal size (for use in the level editor)
+ if (width_2 == old_width)
+ tmp_bitmap_2 = old_bitmap;
+ else
+ tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
+
+ UPDATE_BUSY_STATE();
+
+ // get image with 1/4 of normal size (for use in the level editor)
+ if (width_4 == old_width)
+ tmp_bitmap_4 = old_bitmap;
+ else
+ tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
+
+ UPDATE_BUSY_STATE();
+
+ // get image with 1/8 of normal size (for use on the preview screen)
+ if (width_8 == old_width)
+ tmp_bitmap_8 = old_bitmap;
+ else
+ tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
+
+ UPDATE_BUSY_STATE();
+
+ // get image with 1/16 of normal size (for use on the preview screen)
+ if (width_16 == old_width)
+ tmp_bitmap_16 = old_bitmap;
+ else
+ tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
+
+ UPDATE_BUSY_STATE();
+
+ // get image with 1/32 of normal size (for use on the preview screen)
+ if (width_32 == old_width)
+ tmp_bitmap_32 = old_bitmap;
+ else
+ tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
+
+ UPDATE_BUSY_STATE();
+
+ bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
+ bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
+ bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
+ bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
+ bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
+ bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
+
+ if (width_0 != width_1)
+ bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
+
+ if (bitmaps[IMG_BITMAP_CUSTOM])
+ bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
+ else
+ bitmaps[IMG_BITMAP_PTR_GAME] = bitmaps[IMG_BITMAP_STANDARD];
+
+ // store the "final" (up-scaled) original bitmap, if not already stored
+
+ int tmp_bitmap_final_nr = -1;
+
+ for (i = 0; i < NUM_IMG_BITMAPS; i++)
+ if (bitmaps[i] == tmp_bitmap_final)
+ tmp_bitmap_final_nr = i;
+
+ if (tmp_bitmap_final_nr == -1) // scaled original bitmap not stored
+ {
+ // store pointer of scaled original bitmap (not used for any other size)
+ bitmaps[IMG_BITMAP_OTHER] = tmp_bitmap_final;
+
+ // set original bitmap pointer to scaled original bitmap of other size
+ bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_OTHER];
+ }
+ else
+ {
+ // set original bitmap pointer to corresponding sized bitmap
+ bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[tmp_bitmap_final_nr];
+ }
+
+ // free the "old" (unscaled) original bitmap, if not already stored
+
+ boolean free_old_bitmap = TRUE;
+
+ for (i = 0; i < NUM_IMG_BITMAPS; i++)
+ if (bitmaps[i] == old_bitmap)
+ free_old_bitmap = FALSE;
+
+ if (free_old_bitmap)
+ {
+ // copy image filename from old to new standard sized bitmap
+ bitmaps[IMG_BITMAP_STANDARD]->source_filename =
+ getStringCopy(old_bitmap->source_filename);
+
+ FreeBitmap(old_bitmap);
+ }
+ }
+ else
+ {
+ bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
+
+ // set original bitmap pointer to corresponding sized bitmap
+ bitmaps[IMG_BITMAP_PTR_ORIGINAL] = bitmaps[IMG_BITMAP_32x32];
+
+ if (old_bitmap != tmp_bitmap_1)
+ FreeBitmap(old_bitmap);
+ }
+
+ UPDATE_BUSY_STATE();
+
+ print_timestamp_done("CreateScaledBitmaps");
+}
+
+void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
+ int tile_size)
+{
+ CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
+}
+
+void CreateBitmapTextures(Bitmap **bitmaps)
+{
+ if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
+ SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
+ else
+ SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
+}
+
+void FreeBitmapTextures(Bitmap **bitmaps)
+{
+ if (bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL)
+ SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_PTR_ORIGINAL]);
+ else
+ SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
+}
+
+void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
+{
+ CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
+}
+
+
+// ----------------------------------------------------------------------------
+// mouse pointer functions
+// ----------------------------------------------------------------------------
+
+#define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
+
+// XPM image definitions
+static const char *cursor_image_none[] =
+{
+ // width height num_colors chars_per_pixel
+ " 16 16 3 1",
+
+ // colors
+ "X c #000000",
+ ". c #ffffff",
+ " c None",
+
+ // pixels
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+
+ // hot spot
+ "0,0"
+};
+
+#if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
+static const char *cursor_image_dot[] =
+{
+ // width height num_colors chars_per_pixel
+ " 16 16 3 1",
+
+ // colors
+ "X c #000000",
+ ". c #ffffff",
+ " c None",
+
+ // pixels
+ " X ",
+ "X.X ",
+ " X ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+
+ // hot spot
+ "1,1"
+};
+static const char **cursor_image_playfield = cursor_image_dot;
+#else
+// some people complained about a "white dot" on the screen and thought it
+// was a graphical error... OK, let's just remove the whole pointer :-)
+static const char **cursor_image_playfield = cursor_image_none;
+#endif
+
+static const int cursor_bit_order = BIT_ORDER_MSB;
+
+static struct MouseCursorInfo *get_cursor_from_image(const char **image)
+{
+ struct MouseCursorInfo *cursor;
+ boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
+ int header_lines = 4;
+ int x, y, i;
+
+ cursor = checked_calloc(sizeof(struct MouseCursorInfo));
+
+ sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
+
+ i = -1;
+ for (y = 0; y < cursor->width; y++)
+ {
+ for (x = 0; x < cursor->height; x++)
+ {
+ int bit_nr = x % 8;
+ int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
+
+ if (bit_nr == 0)
+ {
+ i++;
+ cursor->data[i] = cursor->mask[i] = 0;
+ }
+
+ switch (image[header_lines + y][x])
+ {
+ case 'X':
+ cursor->data[i] |= bit_mask;
+ cursor->mask[i] |= bit_mask;
+ break;
+
+ case '.':
+ cursor->mask[i] |= bit_mask;
+ break;
+
+ case ' ':
+ break;
+ }
+ }
+ }
+
+ sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
+
+ return cursor;
+}
+
+void SetMouseCursor(int mode)
+{
+ static struct MouseCursorInfo *cursor_none = NULL;
+ static struct MouseCursorInfo *cursor_playfield = NULL;
+ struct MouseCursorInfo *cursor_new;
+ int mode_final = mode;
+
+ if (cursor_none == NULL)
+ cursor_none = get_cursor_from_image(cursor_image_none);
+
+ if (cursor_playfield == NULL)
+ cursor_playfield = get_cursor_from_image(cursor_image_playfield);
+
+ if (gfx.cursor_mode_override != CURSOR_UNDEFINED)
+ mode_final = gfx.cursor_mode_override;
+
+ cursor_new = (mode_final == CURSOR_DEFAULT ? NULL :
+ mode_final == CURSOR_NONE ? cursor_none :
+ mode_final == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
+
+ SDLSetMouseCursor(cursor_new);
+
+ gfx.cursor_mode = mode;
+ gfx.cursor_mode_final = mode_final;
+}
+
+void UpdateRawMousePosition(int mouse_x, int mouse_y)
+{
+ // mouse events do not contain logical screen size corrections yet
+ SDLCorrectRawMousePosition(&mouse_x, &mouse_y);
+
+ mouse_x -= video.screen_xoffset;
+ mouse_y -= video.screen_yoffset;
+
+ gfx.mouse_x = mouse_x;
+ gfx.mouse_y = mouse_y;
+}
+
+void UpdateMousePosition(void)
+{
+ int mouse_x, mouse_y;
+
+ SDL_PumpEvents();
+ SDL_GetMouseState(&mouse_x, &mouse_y);
+
+ UpdateRawMousePosition(mouse_x, mouse_y);
+}
+
+
+// ============================================================================
+// audio functions
+// ============================================================================
+
+void OpenAudio(void)