1
// -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
// vi:tw=80:et:ts=2:sts=2
4
// -----------------------------------------------------------------------
6
// This file is part of RLVM, a RealLive virtual machine clone.
8
// -----------------------------------------------------------------------
10
// Copyright (C) 2006, 2007 Elliot Glaysher
12
// This program is free software; you can redistribute it and/or modify
13
// it under the terms of the GNU General Public License as published by
14
// the Free Software Foundation; either version 3 of the License, or
15
// (at your option) any later version.
17
// This program is distributed in the hope that it will be useful,
18
// but WITHOUT ANY WARRANTY; without even the implied warranty of
19
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
// GNU General Public License for more details.
22
// You should have received a copy of the GNU General Public License
23
// along with this program; if not, write to the Free Software
24
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
26
// -----------------------------------------------------------------------
28
#include "systems/sdl/sdl_surface.h"
35
#include "base/notification_source.h"
36
#include "pygame/alphablit.h"
37
#include "systems/base/colour.h"
38
#include "systems/base/graphics_object.h"
39
#include "systems/base/graphics_object_data.h"
40
#include "systems/base/system_error.h"
41
#include "systems/sdl/sdl_graphics_system.h"
42
#include "systems/sdl/sdl_utils.h"
43
#include "systems/sdl/texture.h"
44
#include "utilities/graphics.h"
48
// An interface to TransformSurface that maps one color to another.
49
class ColourTransformer {
51
virtual ~ColourTransformer() {}
52
virtual SDL_Color operator()(const SDL_Color& colour) const = 0;
55
class ToneCurveColourTransformer : public ColourTransformer {
57
explicit ToneCurveColourTransformer(const ToneCurveRGBMap m) : colormap(m) {}
58
virtual SDL_Color operator()(const SDL_Color& colour) const {
59
SDL_Color out = {colormap[0][colour.r], colormap[1][colour.g],
60
colormap[2][colour.b], 0};
65
ToneCurveRGBMap colormap;
68
class InvertColourTransformer : public ColourTransformer {
70
virtual SDL_Color operator()(const SDL_Color& colour) const {
71
SDL_Color out = {255 - colour.r, 255 - colour.g, 255 - colour.b, 0};
76
class MonoColourTransformer : public ColourTransformer {
78
virtual SDL_Color operator()(const SDL_Color& colour) const {
79
float grayscale = 0.3 * colour.r + 0.59 * colour.g + 0.11 * colour.b;
80
Clamp(grayscale, 0, 255);
81
SDL_Color out = {grayscale, grayscale, grayscale, 0};
86
class ApplyColourTransformer : public ColourTransformer {
88
explicit ApplyColourTransformer(const RGBColour& colour) : colour_(colour) {}
90
int compose(int in_colour, int surface_colour) const {
93
((static_cast<float>((255 - in_colour) * (255 - surface_colour)) /
96
} else if (in_colour < 0) {
97
return (static_cast<float>(abs(in_colour) * surface_colour) /
101
return surface_colour;
105
virtual SDL_Color operator()(const SDL_Color& colour) const {
107
compose(colour_.r(), colour.r), compose(colour_.g(), colour.g),
108
compose(colour_.b(), colour.b), 0};
116
// Applies a |transformer| to every pixel in |area| in the surface |surface|.
117
void TransformSurface(SDLSurface* our_surface,
119
const ColourTransformer& transformer) {
120
SDL_Surface* surface = our_surface->rawSurface();
124
// determine position
125
char* p_position = (char*)surface->pixels;
128
p_position += (surface->pitch * area.y());
130
SDL_LockSurface(surface);
132
for (int y = 0; y < area.height(); ++y) {
134
p_position += (surface->format->BytesPerPixel * area.x());
136
for (int x = 0; x < area.width(); ++x) {
138
memcpy(&col, p_position, surface->format->BytesPerPixel);
140
// Before someone tries to simplify the following four lines,
141
// remember that sizeof(int) != sizeof(Uint8).
144
col, surface->format, &colour.r, &colour.g, &colour.b, &alpha);
145
SDL_Color out = transformer(colour);
147
SDL_MapRGBA(surface->format, out.r, out.g, out.b, alpha);
149
memcpy(p_position, &out_colour, surface->format->BytesPerPixel);
151
p_position += surface->format->BytesPerPixel;
154
// advance forward image_width - area.width() - x
155
int advance = surface->w - area.x() - area.width();
156
p_position += (surface->format->BytesPerPixel * advance);
159
SDL_UnlockSurface(surface);
161
// If we are the main screen, then we want to update the screen
162
our_surface->markWrittenTo(our_surface->GetRect());
167
// -----------------------------------------------------------------------
169
// Note to self: These describe the byte order IN THE RAW G00 DATA!
170
// These should NOT be switched to native byte order.
171
#define DefaultRmask 0xff0000
172
#define DefaultGmask 0xff00
173
#define DefaultBmask 0xff
174
#define DefaultAmask 0xff000000
175
#define DefaultBpp 32
177
SDL_Surface* buildNewSurface(const Size& size) {
178
// Create an empty surface
179
SDL_Surface* tmp = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
189
std::ostringstream ss;
190
ss << "Couldn't allocate surface in build_new_surface"
191
<< ": " << SDL_GetError();
192
throw SystemError(ss.str());
198
// -----------------------------------------------------------------------
199
// SDLSurface::TextureRecord
200
// -----------------------------------------------------------------------
201
SDLSurface::TextureRecord::TextureRecord(SDL_Surface* surface,
206
unsigned int bytes_per_pixel,
209
: texture(new Texture(surface,
221
bytes_per_pixel_(bytes_per_pixel),
222
byte_order_(byte_order),
223
byte_type_(byte_type) {}
225
// -----------------------------------------------------------------------
227
void SDLSurface::TextureRecord::reupload(SDL_Surface* surface,
230
Rect i = Rect::REC(x_, y_, w_, h_).Intersection(dirty);
232
texture->reupload(surface,
244
texture.reset(new Texture(
245
surface, x_, y_, w_, h_, bytes_per_pixel_, byte_order_, byte_type_));
249
// -----------------------------------------------------------------------
251
void SDLSurface::TextureRecord::forceUnload() { texture.reset(); }
253
// -----------------------------------------------------------------------
255
// -----------------------------------------------------------------------
257
SDLSurface::SDLSurface(SDLGraphicsSystem* system)
259
texture_is_valid_(false),
261
graphics_system_(system),
263
registerForNotification(system);
266
// -----------------------------------------------------------------------
268
SDLSurface::SDLSurface(SDLGraphicsSystem* system, SDL_Surface* surf)
270
texture_is_valid_(false),
272
graphics_system_(system),
274
buildRegionTable(Size(surf->w, surf->h));
275
registerForNotification(system);
278
// -----------------------------------------------------------------------
280
// Surface that takes ownership of an externally created surface.
281
SDLSurface::SDLSurface(SDLGraphicsSystem* system,
283
const std::vector<SDLSurface::GrpRect>& region_table)
285
region_table_(region_table),
286
texture_is_valid_(false),
288
graphics_system_(system),
290
registerForNotification(system);
293
// -----------------------------------------------------------------------
295
SDLSurface::SDLSurface(SDLGraphicsSystem* system, const Size& size)
297
texture_is_valid_(false),
299
graphics_system_(system),
302
buildRegionTable(size);
303
registerForNotification(system);
306
// -----------------------------------------------------------------------
308
void SDLSurface::EnsureUploaded() const {
309
// TODO(erg): Style fix this entire file and make this implementation:
310
uploadTextureIfNeeded();
313
// -----------------------------------------------------------------------
315
void SDLSurface::registerForNotification(GraphicsSystem* system) {
317
NotificationType::FULLSCREEN_STATE_CHANGED,
318
Source<GraphicsSystem>(system));
321
// -----------------------------------------------------------------------
323
// Constructor helper function
324
void SDLSurface::buildRegionTable(const Size& size) {
325
// Build a region table with one entry the size of the surface (This
326
// should never need to be used with objects created with this
327
// constructor, but let's make sure everything is initialized since
328
// it'll happen somehow.)
329
SDLSurface::GrpRect rect;
330
rect.rect = Rect(Point(0, 0), size);
333
region_table_.push_back(rect);
336
// -----------------------------------------------------------------------
338
SDLSurface::~SDLSurface() { deallocate(); }
340
// -----------------------------------------------------------------------
342
Size SDLSurface::GetSize() const {
344
return Size(surface_->w, surface_->h);
347
// -----------------------------------------------------------------------
349
void SDLSurface::Dump() {
350
static int count = 0;
351
std::ostringstream ss;
352
ss << "dump_" << count << ".bmp";
354
SDL_SaveBMP(surface_, ss.str().c_str());
357
// -----------------------------------------------------------------------
359
void SDLSurface::allocate(const Size& size) {
362
surface_ = buildNewSurface(size);
364
Fill(RGBAColour::Black());
367
// -----------------------------------------------------------------------
369
void SDLSurface::allocate(const Size& size, bool is_dc0) {
374
// -----------------------------------------------------------------------
376
void SDLSurface::deallocate() {
379
SDL_FreeSurface(surface_);
384
// TODO(erg): This function doesn't ignore alpha blending when use_src_alpha is
385
// false; thus, grp_open and grp_mask_open are really grp_mask_open.
386
void SDLSurface::BlitToSurface(Surface& dest_surface,
390
bool use_src_alpha) const {
391
SDLSurface& sdl_dest_surface = dynamic_cast<SDLSurface&>(dest_surface);
393
SDL_Rect src_rect, dest_rect;
394
RectToSDLRect(src, &src_rect);
395
RectToSDLRect(dst, &dest_rect);
397
if (src.size() != dst.size()) {
398
// Blit the source rectangle into its own image.
399
SDL_Surface* src_image = buildNewSurface(src.size());
400
if (pygame_AlphaBlit(surface_, &src_rect, src_image, NULL))
401
reportSDLError("SDL_BlitSurface", "SDLGraphicsSystem::blitSurfaceToDC()");
403
SDL_Surface* tmp = buildNewSurface(dst.size());
404
pygame_stretch(src_image, tmp);
407
if (SDL_SetAlpha(tmp, SDL_SRCALPHA, alpha))
408
reportSDLError("SDL_SetAlpha", "SDLGraphicsSystem::blitSurfaceToDC()");
410
if (SDL_SetAlpha(tmp, 0, 0))
411
reportSDLError("SDL_SetAlpha", "SDLGraphicsSystem::blitSurfaceToDC()");
414
if (SDL_BlitSurface(tmp, NULL, sdl_dest_surface.surface(), &dest_rect))
415
reportSDLError("SDL_BlitSurface", "SDLGraphicsSystem::blitSurfaceToDC()");
417
SDL_FreeSurface(tmp);
418
SDL_FreeSurface(src_image);
421
if (SDL_SetAlpha(surface_, SDL_SRCALPHA, alpha))
422
reportSDLError("SDL_SetAlpha", "SDLGraphicsSystem::blitSurfaceToDC()");
424
if (SDL_SetAlpha(surface_, 0, 0))
425
reportSDLError("SDL_SetAlpha", "SDLGraphicsSystem::blitSurfaceToDC()");
429
surface_, &src_rect, sdl_dest_surface.surface(), &dest_rect))
430
reportSDLError("SDL_BlitSurface", "SDLGraphicsSystem::blitSurfaceToDC()");
432
sdl_dest_surface.markWrittenTo(dst);
435
// -----------------------------------------------------------------------
437
// Allows for tight coupling with SDL_ttf. Rethink the existence of
438
// this function later.
439
void SDLSurface::blitFROMSurface(SDL_Surface* src_surface,
443
bool use_src_alpha) {
444
SDL_Rect src_rect, dest_rect;
445
RectToSDLRect(src, &src_rect);
446
RectToSDLRect(dst, &dest_rect);
449
if (pygame_AlphaBlit(src_surface, &src_rect, surface_, &dest_rect))
450
reportSDLError("pygame_AlphaBlit",
451
"SDLGraphicsSystem::blitSurfaceToDC()");
453
if (SDL_BlitSurface(src_surface, &src_rect, surface_, &dest_rect))
454
reportSDLError("SDL_BlitSurface", "SDLGraphicsSystem::blitSurfaceToDC()");
460
// -----------------------------------------------------------------------
462
static void determineProperties(SDL_Surface* surface,
464
GLenum& bytes_per_pixel,
467
SDL_LockSurface(surface);
469
bytes_per_pixel = surface->format->BytesPerPixel;
470
byte_order = GL_RGBA;
471
byte_type = GL_UNSIGNED_BYTE;
473
// Determine the byte order of the surface
474
SDL_PixelFormat* format = surface->format;
475
if (bytes_per_pixel == 4) {
476
// If the order is RGBA...
477
if (format->Rmask == 0xFF000000 && format->Amask == 0xFF)
478
byte_order = GL_RGBA;
479
// OSX's crazy ARGB pixel format
480
else if ((format->Amask == 0x0 || format->Amask == 0xFF000000) &&
481
format->Rmask == 0xFF0000 && format->Gmask == 0xFF00 &&
482
format->Bmask == 0xFF) {
483
// This is an insane hack to get around OSX's crazy byte order
484
// for alpha on PowerPC. Since there isn't a GL_ARGB type, we
485
// need to specify BGRA and then tell the byte type to be
488
// 20070303: Whoah! Is this the internal format on all
490
byte_order = GL_BGRA;
491
byte_type = GL_UNSIGNED_INT_8_8_8_8_REV;
493
std::ios_base::fmtflags f = std::cerr.flags(
494
std::ios::hex | std::ios::uppercase);
495
std::cerr << "Unknown mask: (" << format->Rmask << ", " << format->Gmask
496
<< ", " << format->Bmask << ", " << format->Amask << ")"
500
} else if (bytes_per_pixel == 3) {
501
// For now, just assume RGB.
503
std::cerr << "Warning: Am I really an RGB Surface? Check"
504
<< " Texture::Texture()!" << std::endl;
506
std::ostringstream oss;
507
oss << "Error loading texture: bytes_per_pixel == "
508
<< int(bytes_per_pixel) << " and we only handle 3 or 4.";
509
throw SystemError(oss.str());
512
SDL_UnlockSurface(surface);
515
// Compile shader for use:
516
bytes_per_pixel = GL_ALPHA;
520
// -----------------------------------------------------------------------
522
void SDLSurface::uploadTextureIfNeeded() const {
523
if (!texture_is_valid_) {
524
if (textures_.size() == 0) {
525
GLenum bytes_per_pixel;
526
GLint byte_order, byte_type;
528
surface_, is_mask_, bytes_per_pixel, byte_order, byte_type);
530
// ---------------------------------------------------------------------
532
// Figure out the optimal way of splitting up the image.
533
std::vector<int> x_pieces, y_pieces;
534
x_pieces = segmentPicture(surface_->w);
535
y_pieces = segmentPicture(surface_->h);
538
for (std::vector<int>::const_iterator it = x_pieces.begin();
539
it != x_pieces.end();
542
for (std::vector<int>::const_iterator jt = y_pieces.begin();
543
jt != y_pieces.end();
545
TextureRecord record(surface_,
553
textures_.push_back(record);
561
// Reupload the textures without reallocating them.
562
for_each(textures_.begin(), textures_.end(), [&](TextureRecord& record) {
563
record.reupload(surface_, dirty_rectangle_);
567
dirty_rectangle_ = Rect();
568
texture_is_valid_ = true;
572
// -----------------------------------------------------------------------
574
void SDLSurface::RenderToScreen(const Rect& src,
577
uploadTextureIfNeeded();
579
for (std::vector<TextureRecord>::iterator it = textures_.begin();
580
it != textures_.end();
582
it->texture->RenderToScreen(src, dst, alpha);
586
// -----------------------------------------------------------------------
588
void SDLSurface::RenderToScreenAsColorMask(const Rect& src,
590
const RGBAColour& rgba,
592
uploadTextureIfNeeded();
594
for (std::vector<TextureRecord>::iterator it = textures_.begin();
595
it != textures_.end();
597
it->texture->RenderToScreenAsColorMask(src, dst, rgba, filter);
601
// -----------------------------------------------------------------------
603
void SDLSurface::RenderToScreen(const Rect& src,
605
const int opacity[4]) const {
606
uploadTextureIfNeeded();
608
for (std::vector<TextureRecord>::iterator it = textures_.begin();
609
it != textures_.end();
611
it->texture->RenderToScreen(src, dst, opacity);
615
// -----------------------------------------------------------------------
617
void SDLSurface::RenderToScreenAsObject(const GraphicsObject& rp,
621
uploadTextureIfNeeded();
623
for (std::vector<TextureRecord>::iterator it = textures_.begin();
624
it != textures_.end();
626
it->texture->RenderToScreenAsObject(rp, *this, src, dst, alpha);
630
// -----------------------------------------------------------------------
632
void SDLSurface::Fill(const RGBAColour& colour) {
633
// Fill the entire surface with the incoming colour
634
Uint32 sdl_colour = MapRGBA(surface_->format, colour);
636
if (SDL_FillRect(surface_, NULL, sdl_colour))
637
reportSDLError("SDL_FillRect", "SDLGraphicsSystem::wipe()");
639
// If we are the main screen, then we want to update the screen
640
markWrittenTo(GetRect());
643
// -----------------------------------------------------------------------
645
void SDLSurface::Fill(const RGBAColour& colour, const Rect& area) {
646
// Fill the entire surface with the incoming colour
647
Uint32 sdl_colour = MapRGBA(surface_->format, colour);
650
RectToSDLRect(area, &rect);
652
if (SDL_FillRect(surface_, &rect, sdl_colour))
653
reportSDLError("SDL_FillRect", "SDLGraphicsSystem::wipe()");
655
// If we are the main screen, then we want to update the screen
659
// -----------------------------------------------------------------------
661
void SDLSurface::Invert(const Rect& rect) {
662
InvertColourTransformer inverter;
663
TransformSurface(this, rect, inverter);
666
// -----------------------------------------------------------------------
668
void SDLSurface::Mono(const Rect& rect) {
669
MonoColourTransformer mono;
670
TransformSurface(this, rect, mono);
673
// -----------------------------------------------------------------------
675
void SDLSurface::ToneCurve(const ToneCurveRGBMap effect, const Rect& area) {
676
ToneCurveColourTransformer tc(effect);
677
TransformSurface(this, area, tc);
680
// -----------------------------------------------------------------------
682
void SDLSurface::ApplyColour(const RGBColour& colour, const Rect& area) {
683
ApplyColourTransformer apply(colour);
684
TransformSurface(this, area, apply);
687
// -----------------------------------------------------------------------
689
int SDLSurface::GetNumPatterns() const { return region_table_.size(); }
691
// -----------------------------------------------------------------------
693
const SDLSurface::GrpRect& SDLSurface::GetPattern(int patt_no) const {
694
if (patt_no < region_table_.size())
695
return region_table_[patt_no];
697
return region_table_[0];
700
// -----------------------------------------------------------------------
702
Surface* SDLSurface::Clone() const {
703
SDL_Surface* tmp_surface =
704
SDL_CreateRGBSurface(surface_->flags,
707
surface_->format->BitsPerPixel,
708
surface_->format->Rmask,
709
surface_->format->Gmask,
710
surface_->format->Bmask,
711
surface_->format->Amask);
713
// Disable alpha blending because we're copying onto a blank (and
714
// blank alpha!) surface
715
if (SDL_SetAlpha(surface_, 0, 0))
716
reportSDLError("SDL_SetAlpha", "SDLGraphicsSystem::blitSurfaceToDC()");
718
if (SDL_BlitSurface(surface_, NULL, tmp_surface, NULL))
719
reportSDLError("SDL_BlitSurface", "SDLSurface::clone()");
721
return new SDLSurface(graphics_system_, tmp_surface, region_table_);
724
// -----------------------------------------------------------------------
726
std::vector<int> SDLSurface::segmentPicture(int size_remainging) {
727
int max_texture_size = GetMaxTextureSize();
729
std::vector<int> output;
730
while (size_remainging > max_texture_size) {
731
output.push_back(max_texture_size);
732
size_remainging -= max_texture_size;
736
output.push_back(size_remainging);
738
while (size_remainging) {
739
int ss = SafeSize(size_remainging);
741
output.push_back(512);
742
size_remainging -= 512;
744
output.push_back(size_remainging);
753
// -----------------------------------------------------------------------
755
void SDLSurface::GetDCPixel(const Point& pos, int& r, int& g, int& b) const {
759
// determine position
760
char* p_position = (char*)surface_->pixels;
763
p_position += (surface_->pitch * pos.y());
766
p_position += (surface_->format->BytesPerPixel * pos.x());
769
memcpy(&col, p_position, surface_->format->BytesPerPixel);
771
// Before someone tries to simplify the following four lines,
772
// remember that sizeof(int) != sizeof(Uint8).
773
SDL_GetRGB(col, surface_->format, &colour.r, &colour.g, &colour.b);
779
// -----------------------------------------------------------------------
781
std::shared_ptr<Surface> SDLSurface::ClipAsColorMask(const Rect& clip_rect,
785
const char* function_name = "SDLGraphicsSystem::ClipAsColorMask()";
787
// TODO(erg): This needs to be made exception safe and so does the rest
789
SDL_Surface* tmp_surface = SDL_CreateRGBSurface(
790
0, surface_->w, surface_->h, 24, 0xFF0000, 0xFF00, 0xFF, 0);
793
reportSDLError("SDL_CreateRGBSurface", function_name);
795
if (SDL_BlitSurface(surface_, NULL, tmp_surface, NULL))
796
reportSDLError("SDL_BlitSurface", function_name);
798
Uint32 colour = SDL_MapRGB(tmp_surface->format, r, g, b);
799
if (SDL_SetColorKey(tmp_surface, SDL_SRCCOLORKEY, colour))
800
reportSDLError("SDL_SetAlpha", function_name);
802
// The OpenGL pieces don't know what to do an image formatted to
803
// (FF0000, FF00, FF, 0), so convert it to a standard RGBA image
804
// (and clip to the desired rectangle)
805
SDL_Surface* surface = buildNewSurface(clip_rect.size());
807
RectToSDLRect(clip_rect, &srcrect);
808
if (SDL_BlitSurface(tmp_surface, &srcrect, surface, NULL))
809
reportSDLError("SDL_BlitSurface", function_name);
811
SDL_FreeSurface(tmp_surface);
813
return std::shared_ptr<Surface>(new SDLSurface(graphics_system_, surface));
816
// -----------------------------------------------------------------------
818
void SDLSurface::markWrittenTo(const Rect& written_rect) {
819
// If we are marked as dc0, alert the SDLGraphicsSystem.
820
if (is_dc0_ && graphics_system_) {
821
graphics_system_->MarkScreenAsDirty(GUT_DRAW_DC0);
824
// Mark that the texture needs reuploading
825
dirty_rectangle_ = dirty_rectangle_.RectUnion(written_rect);
826
texture_is_valid_ = false;
829
void SDLSurface::Observe(NotificationType type,
830
const NotificationSource& source,
831
const NotificationDetails& details) {
833
// Force unloading of all OpenGL resources
834
for (std::vector<TextureRecord>::iterator it = textures_.begin();
835
it != textures_.end();
840
dirty_rectangle_ = GetRect();
843
texture_is_valid_ = false;