open_toontown_panda3d/direct/src/plugin/p3dSplashWindow.cxx

612 lines
21 KiB
C++
Executable File

// Filename: p3dSplashWindow.cxx
// Created by: drose (17Jun09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "p3dSplashWindow.h"
#include "wstring_encode.h"
// Stuff to use libpng.
#include <png.h>
// Stuff to use libjpeg.
extern "C" {
#include <jpeglib.h>
#include <jerror.h>
}
#include <setjmp.h>
struct my_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr *my_error_ptr;
METHODDEF(void) my_error_exit (j_common_ptr cinfo) {
// cinfo->err really points to a my_error_mgr struct, so coerce pointer
my_error_ptr myerr = (my_error_ptr) cinfo->err;
// Return control to the setjmp point
longjmp(myerr->setjmp_buffer, 1);
}
// The number of pixels to move the block per byte downloaded, when we
// don't know the actual file size we're downloading.
const double P3DSplashWindow::_unknown_progress_rate = 1.0 / 4096;
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::Constructor
// Access: Public
// Description: By the time the SplashWindow is created, the instance
// has received both its fparams and its wparams. Copy
// them both into this class for reference.
////////////////////////////////////////////////////////////////////
P3DSplashWindow::
P3DSplashWindow(P3DInstance *inst, bool make_visible) :
_inst(inst),
_fparams(inst->get_fparams()),
_wparams(inst->get_wparams())
{
_visible = make_visible;
_fgcolor_r = 0x00;
_fgcolor_g = 0x00;
_fgcolor_b = 0x00;
_bgcolor_r = 0xff;
_bgcolor_g = 0xff;
_bgcolor_b = 0xff;
_barcolor_r = 0x6c;
_barcolor_g = 0xa5;
_barcolor_b = 0xe0;
_button_width = 0;
_button_height = 0;
_button_x = 0;
_button_y = 0;
_button_active = false;
_mouse_x = -1;
_mouse_y = -1;
_mouse_down = false;
_button_depressed = false;
_bstate = BS_hidden;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
P3DSplashWindow::
~P3DSplashWindow() {
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_wparams
// Access: Public, Virtual
// Description: Changes the window parameters, e.g. to resize or
// reposition the window; or sets the parameters for the
// first time, creating the initial window.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_wparams(const P3DWindowParams &wparams) {
_wparams = wparams;
_win_width = _wparams.get_win_width();
_win_height = _wparams.get_win_height();
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_visible
// Access: Public, Virtual
// Description: Makes the splash window visible or invisible, so as
// not to compete with the embedded Panda window in the
// same space.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_visible(bool visible) {
nout << "P3DSplashWindow::set_visible(" << visible << ")\n";
_visible = visible;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_image_filename
// Access: Public, Virtual
// Description: Specifies the name of a JPEG or PNG image file that
// is displayed in the center of the splash window.
//
// image_placement defines the specific context in which
// this particular image is displayed. It is similar to
// the P3DInstance's image_type, but it is a more
// specific, lower-level usage.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_image_filename(const string &image_filename, ImagePlacement image_placement) {
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_fgcolor
// Access: Public, Virtual
// Description: Specifies the color that is used to display the text
// above the loading bar.
//
// This may only be set before wparams is set.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_fgcolor(int r, int g, int b) {
nout << "fgcolor " << r << ", " << g << ", " << b << "\n";
_fgcolor_r = r;
_fgcolor_g = g;
_fgcolor_b = b;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_bgcolor
// Access: Public, Virtual
// Description: Specifies the solid color that is displayed behind
// the splash image, if any, or before the splash image
// is loaded.
//
// This may only be set before wparams is set.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_bgcolor(int r, int g, int b) {
nout << "bgcolor " << r << ", " << g << ", " << b << "\n";
_bgcolor_r = r;
_bgcolor_g = g;
_bgcolor_b = b;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_barcolor
// Access: Public, Virtual
// Description: Specifies the color that is used to fill the
// loading bar.
//
// This may only be set before wparams is set.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_barcolor(int r, int g, int b) {
nout << "barcolor " << r << ", " << g << ", " << b << "\n";
_barcolor_r = r;
_barcolor_g = g;
_barcolor_b = b;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_install_label
// Access: Public, Virtual
// Description: Specifies the text that is displayed above the
// install progress bar.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_install_label(const string &install_label) {
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_install_progress
// Access: Public, Virtual
// Description: Moves the install progress bar from 0.0 to 1.0.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_install_progress(double install_progress,
bool is_progress_known, size_t received_data) {
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::handle_event
// Access: Public, Virtual
// Description: Deals with the event callback from the OS window
// system. Returns true if the event is handled, false
// if ignored.
////////////////////////////////////////////////////////////////////
bool P3DSplashWindow::
handle_event(const P3D_event_data &event) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_button_active
// Access: Public, Virtual
// Description: Sets whether the button should be visible and active
// (true) or invisible and inactive (false). If active,
// the button image will be displayed in the window, and
// a click event will be generated when the user clicks
// the button.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_button_active(bool flag) {
_button_active = flag;
// Now light up the button according to the current mouse position.
set_mouse_data(_mouse_x, _mouse_y, _mouse_down);
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::request_keyboard_focus
// Access: Private
// Description: The Panda window is asking us to manage keyboard
// focus in proxy for it. This is used on Vista, where
// the Panda window may be disallowed from directly
// assigning itself keyboard focus.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
request_keyboard_focus() {
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::read_image_data
// Access: Protected
// Description: Reads the image filename and sets image parameters
// width, height, num_channels, and data. Returns true
// on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DSplashWindow::
read_image_data(ImageData &image, string &data,
const string &image_filename) {
image._width = 0;
image._height = 0;
image._num_channels = 0;
data.clear();
if (image_filename.empty()) {
return false;
}
// We only support JPEG or PNG images.
FILE *fp = NULL;
#ifdef _WIN32
wstring image_filename_w;
if (string_to_wstring(image_filename_w, image_filename)) {
fp = _wfopen(image_filename_w.c_str(), L"rb");
}
#else // _WIN32
fp = fopen(image_filename.c_str(), "rb");
#endif // _WIN32
if (fp == NULL) {
nout << "Couldn't open splash file image: " << image_filename << "\n";
return false;
}
// Check the magic number to determine which image type we have.
static const size_t magic_number_len = 2;
char magic_number[magic_number_len];
if (fread(magic_number, 1, magic_number_len, fp) != magic_number_len) {
nout << "Empty file: " << image_filename << "\n";
return false;
}
// Rewind to re-read the magic number below.
fseek(fp, 0, SEEK_SET);
bool result = false;
if ((char)magic_number[0] == (char)0xff &&
(char)magic_number[1] == (char)0xd8) {
// It's a jpeg image.
result = read_image_data_jpeg(image, data, fp, image_filename);
} else if (png_sig_cmp((png_bytep)magic_number, 0, magic_number_len) == 0) {
// It's a PNG image.
result = read_image_data_png(image, data, fp, image_filename);
} else {
nout << "Neither a JPEG nor a PNG image: " << image_filename << "\n";
result = false;
}
fclose(fp);
return result;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::read_image_data_jpeg
// Access: Protected
// Description: Reads the image filename and sets image parameters
// width, height, num_channels, and data. Returns true
// on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DSplashWindow::
read_image_data_jpeg(ImageData &image, string &data,
FILE *fp, const string &image_filename) {
// We set up the normal JPEG error routines, then override error_exit.
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
JSAMPLE *buffer = NULL;
// Establish the setjmp return context for my_error_exit to use
if (setjmp(jerr.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
nout << "JPEG error decoding " << image_filename << "\n";
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_decompress(&cinfo);
if (buffer != NULL) {
delete[] buffer;
}
return false;
}
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
jpeg_read_header(&cinfo, true);
cinfo.scale_num = 1;
cinfo.scale_denom = 1;
jpeg_start_decompress(&cinfo);
image._width = cinfo.output_width;
image._height = cinfo.output_height;
image._num_channels = cinfo.output_components;
int row_stride = image._width * image._num_channels;
size_t buffer_size = image._height * row_stride;
buffer = new JSAMPLE[buffer_size];
JSAMPLE *buffer_end = buffer + buffer_size;
JSAMPLE *rowptr = buffer;
while (cinfo.output_scanline < cinfo.output_height) {
assert(rowptr + row_stride <= buffer_end);
jpeg_read_scanlines(&cinfo, &rowptr, 1);
rowptr += row_stride;
}
jpeg_finish_decompress(&cinfo);
data.append((const char *)buffer, buffer_size);
delete[] buffer;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::read_image_data_png
// Access: Protected
// Description: Reads the image filename and sets image parameters
// width, height, num_channels, and data. Returns true
// on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DSplashWindow::
read_image_data_png(ImageData &image, string &data,
FILE *fp, const string &image_filename) {
png_structp png;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png == NULL) {
return false;
}
png_infop info;
info = png_create_info_struct(png);
if (info == NULL) {
png_destroy_read_struct(&png, NULL, NULL);
return false;
}
jmp_buf jmpbuf;
if (setjmp(jmpbuf)) {
// This is the ANSI C way to handle exceptions. If setjmp(),
// above, returns true, it means that libpng detected an exception
// while executing the code that reads the header info, below.
png_destroy_read_struct(&png, &info, NULL);
return false;
}
png_init_io(png, fp);
int transforms = PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_SHIFT;
// transforms |= PNG_TRANSFORM_STRIP_ALPHA;
png_read_png(png, info, transforms, NULL);
png_uint_32 width;
png_uint_32 height;
int bit_depth;
int color_type;
png_get_IHDR(png, info, &width, &height,
&bit_depth, &color_type, NULL, NULL, NULL);
image._width = width;
image._height = height;
switch (color_type) {
case PNG_COLOR_TYPE_RGB:
image._num_channels = 3;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
image._num_channels = 4;
break;
default:
nout << "Unsupported color type: " << color_type << "\n";
png_destroy_read_struct(&png, &info, NULL);
return false;
}
int row_stride = image._width * image._num_channels;
png_bytep *row_pointers = png_get_rows(png, info);
for (int yi = 0; yi < image._height; ++yi) {
data.append((const char *)row_pointers[yi], row_stride);
}
png_destroy_read_struct(&png, &info, NULL);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::get_bar_placement
// Access: Protected
// Description: Given the window width and height, determine the
// rectangle in which to place the progress bar.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
get_bar_placement(int &bar_x, int &bar_y,
int &bar_width, int &bar_height) {
bar_width = min((int)(_win_width * 0.6), 400);
bar_height = min((int)(_win_height * 0.1), 24);
bar_x = (_win_width - bar_width) / 2;
bar_y = (_win_height - bar_height * 2);
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_button_range
// Access: Protected
// Description: Specifies the image that contains the "ready" button
// image, which in turn determines the clickable
// dimensions of the button within the window.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_button_range(const ImageData &image) {
// The clickable area has a certain minimum size, even if it's a
// very small image.
_button_width = max(image._width, 64);
_button_height = max(image._height, 64);
// But it can't be larger than the window itself.
_button_width = min(_button_width, _win_width);
_button_height = min(_button_height, _win_height);
// Compute the top-left corner of the button image in window
// coordinates.
_button_x = (_win_width - _button_width) / 2;
_button_y = (_win_height - _button_height) / 2;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_mouse_data
// Access: Protected
// Description: Intended to be called by the subclasses as the mouse
// is tracked through the window, whether the button is
// currently active or not. This updates the internal
// state of the mouse pointer, and also (if the button
// is active) updates the button state appropriately,
// and generates the click event when the mouse button
// transitions from down to up over the button area.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
ButtonState orig_bstate = _bstate;
_mouse_x = mouse_x;
_mouse_y = mouse_y;
_mouse_down = mouse_down;
ButtonState bstate = BS_hidden;
bool click_detected = false;
if (!_button_active) {
// The button isn't active, so it's hidden, regardless of the
// mouse position.
bstate = BS_hidden;
} else {
// Is the mouse pointer within the button region?
bool is_within = (_mouse_x >= _button_x && _mouse_x < _button_x + _button_width &&
_mouse_y >= _button_y && _mouse_y < _button_y + _button_height);
if (is_within) {
// The mouse is within the button region. This means either
// click or rollover state, according to the mouse button.
if (_mouse_down) {
// We only count it mouse-down if you've clicked down while
// over the button (or you never released the button since the
// last time you clicked down). Clicking down somewhere else
// and dragging over the button doesn't count.
if (orig_bstate == BS_rollover || _button_depressed) {
_button_depressed = true;
bstate = BS_click;
} else {
// Otherwise, we're mousing over the button region with the
// button held down. Hmm, don't think the button should
// light up.
bstate = BS_ready;
}
} else {
_button_depressed = false;
if (orig_bstate == BS_click) {
// If we just transitioned from mouse down to mouse up, this
// means a click. And the button automatically hides itself
// after a successful click.
bstate = BS_hidden;
_button_active = false;
click_detected = true;
} else {
bstate = BS_rollover;
}
}
} else {
// The mouse is not within the button region. This means ready
// state.
bstate = BS_ready;
if (!_mouse_down) {
_button_depressed = false;
}
}
}
set_bstate(bstate);
// If we detected a click operation in the above, make the callback
// here, at the end of the method, after we have finished updating
// the button state.
if (click_detected) {
button_click_detected();
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::button_click_detected
// Access: Protected, Virtual
// Description: Called when a button click by the user is detected in
// set_mouse_data(), this method simply turns around and
// notifies the instance. It's a virtual method to give
// subclasses a chance to redirect this message to the
// main thread or process, as necessary.
//
// Note that this method might be called in a sub-thread.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
button_click_detected() {
assert(_inst != NULL);
nout << "Play button clicked by user\n";
_inst->splash_button_clicked_sub_thread();
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_bstate
// Access: Protected, Virtual
// Description: Changes the button state as the mouse interacts with
// it.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_bstate(ButtonState bstate) {
if (_bstate != bstate) {
_bstate = bstate;
// If we've changed button states, we need to refresh the window.
refresh();
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::refresh
// Access: Protected, Virtual
// Description: Requests that the window will be repainted. This may
// or may not be implemented for a particular
// specialization of P3DSplashWindow.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
refresh() {
}