Related
I am making a simples possible jigsaw game using SDL.
Currently i am having trouble applying image texutes to Rects that serve as a puzzle pieces.
The way I applied texture to background doesnt work for pieces(rect1-9).
All pieces are 100x100 and picture is 300x300.
Tell me what is wrong, how to do it right and also explain how to Clip render(apply only part of the texture) with SDL_RenderCopy
#include <list>
#include <SDL.h>
int main(int argc, char ** argv)
{
// variables
bool quit = false;
SDL_Event event;
bool leftMouseButtonDown = false;
SDL_Point mousePos;
SDL_Point clickOffset;
// init SDL
SDL_Init(SDL_INIT_VIDEO);
SDL_Window * window = SDL_CreateWindow("SDL2 Drag and Drop",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 600, 400, 0);
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Surface* bgSurface = SDL_LoadBMP("background.bmp");
SDL_Texture* bgTexture = SDL_CreateTextureFromSurface (renderer,bgSurface);
SDL_Surface* picSurface = SDL_LoadBMP("picture.bmp");
SDL_Texture* picTexture = SDL_CreateTextureFromSurface (renderer,bgSurface);
SDL_Rect rect1 = { 52, 51, 100, 100 };
SDL_Rect rect2 = { 152, 51, 100, 100 };
SDL_Rect rect3 = { 250, 51, 100, 100 };
SDL_Rect rect4 = { 52, 151, 100, 100 };
SDL_Rect rect5 = { 152, 151, 100, 100 };
SDL_Rect rect6 = { 250, 151, 100, 100 };
SDL_Rect rect7 = { 52, 249, 100, 100 };
SDL_Rect rect8 = { 152, 249, 100, 100 };
SDL_Rect rect9 = { 250, 249, 100, 100 };
SDL_Rect background = { 0, 0, 600, 400 };
SDL_Rect * selectedRect = NULL;
std::list<SDL_Rect *> rectangles;
rectangles.push_back(&rect1);
rectangles.push_back(&rect2);
rectangles.push_back(&rect3);
rectangles.push_back(&rect4);
rectangles.push_back(&rect5);
rectangles.push_back(&rect6);
rectangles.push_back(&rect7);
rectangles.push_back(&rect8);
rectangles.push_back(&rect9);
// handle events
while (!quit)
{
SDL_Delay(10);
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
quit = true;
break;
case SDL_MOUSEBUTTONUP:
if (leftMouseButtonDown && event.button.button == SDL_BUTTON_LEFT)
{
leftMouseButtonDown = false;
selectedRect = NULL;
}
break;
case SDL_MOUSEBUTTONDOWN:
if (!leftMouseButtonDown && event.button.button == SDL_BUTTON_LEFT)
{
leftMouseButtonDown = true;
for (auto rect : rectangles)
{
if (SDL_PointInRect(&mousePos, rect))
{
selectedRect = rect;
clickOffset.x = mousePos.x - rect->x;
clickOffset.y = mousePos.y - rect->y;
break;
}
}
}
break;
case SDL_MOUSEMOTION:
{
mousePos = { event.motion.x, event.motion.y };
if (leftMouseButtonDown && selectedRect != NULL)
{
selectedRect->x = mousePos.x - clickOffset.x;
selectedRect->y = mousePos.y - clickOffset.y;
}
}
break;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer,bgTexture,NULL,&background );
SDL_RenderCopy(renderer,bgTexture,NULL,&rect1 );
for (auto const& rect : rectangles)
{
if (rect == selectedRect)
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 100);
else
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 0);
SDL_RenderFillRect(renderer, rect);
}
SDL_RenderPresent(renderer);
}
// cleanup SDL
SDL_DestroyTexture (bgTexture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
i use SDL2 with MapRGB to let win10 window transparent, and use cairo to draw something, but the color is not true, like that cairo use rgb(0.9, 0, 0) draw rect, SDL2 with SDL_MapRGB, and final the rect color is not red rgb.
#include <SDL.h>
#include <cairo.h>
#include <SDL_syswm.h>
#include "windows.h"
COLORREF defaultTransparentColor = RGB(255, 0, 255);
bool windowColorKey(SDL_Window *window, COLORREF colorKey) {
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version); // Initialize wmInfo
SDL_GetWindowWMInfo(window, &wmInfo);
HWND hWnd = wmInfo.info.win.window;
// Change window type to layered
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
// Set transparency color
return SetLayeredWindowAttributes(hWnd, colorKey, 0, LWA_COLORKEY);
}
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window;
SDL_Renderer *renderer;
SDL_CreateWindowAndRenderer(640, 480, SDL_WINDOW_SHOWN, &window, &renderer);
windowColorKey(window, defaultTransparentColor);
SDL_SetWindowTitle(window, "Cairo test");
cairo_surface_t *surface;
cairo_t *cr;
#if 1
// 方式一 : 建立cairo, 建立texture, 在cairo上画图,然后update过去
int windowWidth = 640, windowHeight = 480;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, windowWidth, windowWidth);
cr = cairo_create(surface);
cairo_set_line_width(cr, 1);
// cairo use red color
cairo_set_source_rgb(cr, .9, 0, 0);
cairo_rectangle(cr, 100, 100, 180, 90);
cairo_stroke(cr);
unsigned char *data;
data = cairo_image_surface_get_data(surface);
SDL_Texture *texture;
SDL_Rect rect = {.x=0, .y=0, .w=windowWidth, .h=windowHeight};
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, windowWidth, windowHeight);
// SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MUL);
int widthPitch = windowWidth << 2;
SDL_UpdateTexture(texture, &rect, data, widthPitch);
#else
// 方式二 : 建立texture, 从texture的像素数据建立cairo, 直接画到texture上去
// 可能比方式一快, 我没读过cairo_image_surface_create_for_data的源码,不敢妄下定论
SDL_Texture *texture;
SDL_Rect rect = {.x=0, .y=0, .w=100, .h=100};
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 100, 100);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
void *pixel;
int pitch = 400; // 一行的像素字节 ARGB32=4 byte, w = 100
SDL_LockTexture(texture, &rect, &pixel, &pitch);
surface = cairo_image_surface_create_for_data(
(unsigned char*) pixel,
CAIRO_FORMAT_ARGB32, 100, 100, pitch
);
SDL_UnlockTexture(texture);
cr = cairo_create(surface);
cairo_set_line_width(cr, 10);
cairo_set_source_rgb(cr, 255, 0, 0);
cairo_arc(cr, 50, 50, 20, 0.0, 0.785);
cairo_stroke(cr);
#endif
SDL_Event event;
bool is_working = true;
while (is_working) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
is_working = false;
break;
}
}
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
SDL_Delay(66);
}
return 0;
}
the rect is not red, what should i do ?
I'm trying to render text using SDL. Obviously SDL does not support rendering text by itself, so I went with this approach:
load font file
raster glyphs in the font to a bitmap
pack all bitmaps in a large texture, forming a spritesheet of glyphs
render text as a sequence of glyph-sprites: copy rectangles from the texture to the target
First steps are handled using FreeType library. It can generate bitmaps for many kinds of fonts and provide a lot of extra info about the glyphs. FreeType-generated bitmaps are (by default) alpha channel only. For every glyph I basically get a 2D array of A values in range 0 - 255. For simplicity reasons the MCVE below needs only SDL, I already embedded FreeType-generated bitmap in the source code.
Now, the question is: how should I manage the texture that consists of such bitmaps?
What blending mode should I use?
What modulation should I use?
What should the texture be filled with? FreeType provides alpha channel only, SDL generally wants a texture of RGBA pixels. What values should I use for RGB?
How do I draw text in specific color? I don't want to make a separate texture for each color.
FreeType documentation says: For optimal rendering on a screen the bitmap should be used as an alpha channel in linear blending with gamma correction. SDL blending mode documentation doesn't list anything named linear blending so I used a custom one but I'm not sure if I got it right.
I'm not sure if I got some of SDL calls right as some of them are poorly documented (I already know that locking with empty rectangles crashes on Direct3D), especially how to copy data using SDL_LockTexture.
#include <string>
#include <stdexcept>
#include <SDL.h>
constexpr unsigned char pixels[] = {
0, 0, 0, 0, 0, 0, 0, 30, 33, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 169, 255, 155, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 83, 255, 255, 229, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 189, 233, 255, 255, 60, 0, 0, 0, 0, 0,
0, 0, 0, 0, 33, 254, 83, 250, 255, 148, 0, 0, 0, 0, 0,
0, 0, 0, 0, 129, 227, 2, 181, 255, 232, 3, 0, 0, 0, 0,
0, 0, 0, 2, 224, 138, 0, 94, 255, 255, 66, 0, 0, 0, 0,
0, 0, 0, 68, 255, 48, 0, 15, 248, 255, 153, 0, 0, 0, 0,
0, 0, 0, 166, 213, 0, 0, 0, 175, 255, 235, 4, 0, 0, 0,
0, 0, 16, 247, 122, 0, 0, 0, 88, 255, 255, 71, 0, 0, 0,
0, 0, 105, 255, 192, 171, 171, 171, 182, 255, 255, 159, 0, 0, 0,
0, 0, 203, 215, 123, 123, 123, 123, 123, 196, 255, 239, 6, 0, 0,
0, 44, 255, 108, 0, 0, 0, 0, 0, 75, 255, 255, 77, 0, 0,
0, 142, 252, 22, 0, 0, 0, 0, 0, 5, 238, 255, 164, 0, 0,
5, 234, 184, 0, 0, 0, 0, 0, 0, 0, 156, 255, 242, 8, 0,
81, 255, 95, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 86, 0,
179, 249, 14, 0, 0, 0, 0, 0, 0, 0, 3, 245, 255, 195, 0
};
[[noreturn]] inline void throw_error(const char* desc, const char* sdl_err)
{
throw std::runtime_error(std::string(desc) + sdl_err);
}
void update_pixels(
SDL_Texture& texture,
const SDL_Rect& texture_rect,
const unsigned char* src_alpha,
int src_size_x,
int src_size_y)
{
void* pixels;
int pitch;
if (SDL_LockTexture(&texture, &texture_rect, &pixels, &pitch))
throw_error("could not lock texture: ", SDL_GetError());
auto pixel_buffer = reinterpret_cast<unsigned char*>(pixels);
for (int y = 0; y < src_size_y; ++y) {
for (int x = 0; x < src_size_x; ++x) {
// this assumes SDL_PIXELFORMAT_RGBA8888
unsigned char* const rgba = pixel_buffer + x * 4;
unsigned char& r = rgba[0];
unsigned char& g = rgba[1];
unsigned char& b = rgba[2];
unsigned char& a = rgba[3];
r = 0xff;
g = 0xff;
b = 0xff;
a = src_alpha[x];
}
src_alpha += src_size_y;
pixel_buffer += pitch;
}
SDL_UnlockTexture(&texture);
}
int main(int /* argc */, char* /* argv */[])
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
throw_error("could not init SDL: ", SDL_GetError());
SDL_Window* window = SDL_CreateWindow("Hello World",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
1024, 768,
SDL_WINDOW_RESIZABLE);
if (!window)
throw_error("could not create window: ", SDL_GetError());
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
if (!renderer)
throw_error("could not create renderer: ", SDL_GetError());
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 512, 512);
if (!texture)
throw_error("could not create texture: ", SDL_GetError());
SDL_SetTextureColorMod(texture, 255, 0, 0);
SDL_Rect src_rect;
src_rect.x = 0;
src_rect.y = 0;
src_rect.w = 15;
src_rect.h = 17;
update_pixels(*texture, src_rect, pixels, src_rect.w, src_rect.h);
/*
* FreeType documentation: For optimal rendering on a screen the bitmap should be used as
* an alpha channel in linear blending with gamma correction.
*
* The blending used is therefore:
* dstRGB = (srcRGB * srcA) + (dstRGB * (1 - srcA))
* dstA = (srcA * 0) + (dstA * 1) = dstA
*/
SDL_BlendMode blend_mode = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
if (SDL_SetTextureBlendMode(texture, blend_mode))
throw_error("could not set texture blending: ", SDL_GetError());
while (true) {
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
SDL_RenderClear(renderer);
SDL_Rect dst_rect;
dst_rect.x = 100;
dst_rect.y = 100;
dst_rect.w = src_rect.w;
dst_rect.h = src_rect.h;
SDL_RenderCopy(renderer, texture, &src_rect, &dst_rect);
SDL_RenderPresent(renderer);
SDL_Delay(16);
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYUP:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
return 0;
}
break;
case SDL_QUIT:
return 0;
}
}
}
return 0;
}
Expected result: red letter "A" on yellow background.
Actual result: malformed red lines inside black square on yellow background.
I suspect that lines are broken because there is a bug within pointer arithmetics inside update_pixels but I have no idea what's causing the black square.
First of all, part of this stuff is already done in SDL_ttf library. You could use it to rasterise glyphs to surfaces or generate multichar text surface.
Your src_alpha += src_size_y; is incorrect - you copy row by row, but skip by column length, not row length. It should be src_size_x. That results in incorrect offset on each row and only first row of your copied image is correct.
Your colour packing when writing to texture is backwards. See https://wiki.libsdl.org/SDL_PixelFormatEnum#order - Packed component order (high bit -> low bit): SDL_PACKEDORDER_RGBA, meaning R is packed at highest bits while A is at lowest. So, when representing it with unsigned char*, First byte is A and fourth is R:
unsigned char& r = rgba[3];
unsigned char& g = rgba[2];
unsigned char& b = rgba[1];
unsigned char& a = rgba[0];
You don't need custom blending, use SDL_BLENDMODE_BLEND, that is 'standard' "src-alpha, one-minus-src-alpha" formula everyone uses (note that it does not blend dst alpha channel itself, nor uses it to any extent; when blending, we only care about src alpha).
Finally one more approach to this: you could put your glyph luminance value (alpha, whatever it is called, the point is it only have one channel) and put it into every channel. That way you could do additive blending without using alpha at all, don't even need RGBA texture. Glyph colour could still be multiplied with colour mod. SDL_ttf implements just that.
In this question's answer we got this code to initialize a texture with some color:
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *MainWindow = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
1024, 768,
SDL_WINDOW_SHOWN
);
SDL_Renderer *renderer = SDL_CreateRenderer(MainWindow, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_Texture *Tile = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STREAMING, 8, 8);
// Initialize texture pixels to a red opaque RGBA value
unsigned char* bytes = nullptr;
int pitch = 0;
SDL_LockTexture(Tile, nullptr, reinterpret_cast<void**>(&bytes), &pitch);
unsigned char rgba[4] = { 255, 0, 0, 255 };
for(int y = 0; y < 8; ++y) {
for (int x = 0; x < 8; ++x) {
memcpy(&bytes[(y * 8 + x)*sizeof(rgba)], rgba, sizeof(rgba));
}
}
SDL_UnlockTexture(Tile);
SDL_Rect destination = { 320, 240, 8, 8 };
SDL_RenderCopy(renderer, Tile, NULL, &destination);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
//Clean up
SDL_DestroyTexture(Tile);
SDL_DestroyWindow(MainWindow);
SDL_Quit();
return 0;
}
It's supposed to initialize the texture with red, because rgba is initialized with {255, 0, 0, 255}. But I noticed that if I change this values, the result always shows some portion of red. Could someone explain this behavior? I guess I'm missing something in the memcpy line.
I would ask there, but I can't post comments.
Thanks! :)
EDIT: Here it is a capture with {0, 0, 255, 255}. With {0, 255, 255, 255} is all white, so it's like the red gets always added somehow.
I am using visual studio 15 and working in opencv 3.0 ,i am getting access violation error in my code and even this function is not working with sample code given in opencv.
#include"stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <math.h>
#include <iostream>
using namespace cv;
using namespace std;
static void help()
{
cout
<< "\nThis program illustrates the use of findContours and drawContours\n"
<< "The original image is put up along with the image of drawn contours\n"
<< "Usage:\n"
<< "./contours2\n"
<< "\nA trackbar is put up which controls the contour level from -3 to 3\n"
<< endl;
}
const int w = 500;
int levels = 3;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
static void on_trackbar(int, void*)
{
Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
int _levels = levels - 3;
drawContours(cnt_img, contours, _levels <= 0 ? 3 : -1, Scalar(128, 255, 255),
3, LINE_AA, hierarchy, std::abs(_levels));
imshow("contours", cnt_img);
}
int main(int argc, char**)
{
Mat img = Mat::zeros(w, w, CV_8UC1);
if (argc > 1)
{
help();
return -1;
}
//Draw 6 faces
for (int i = 0; i < 6; i++)
{
int dx = (i % 2) * 250 - 30;
int dy = (i / 2) * 150;
const Scalar white = Scalar(255);
const Scalar black = Scalar(0);
if (i == 0)
{
for (int j = 0; j <= 10; j++)
{
double angle = (j + 5)*CV_PI / 21;
line(img, Point(cvRound(dx + 100 + j * 10 - 80 * cos(angle)),
cvRound(dy + 100 - 90 * sin(angle))),
Point(cvRound(dx + 100 + j * 10 - 30 * cos(angle)),
cvRound(dy + 100 - 30 * sin(angle))), white, 1, 8, 0);
}
}
ellipse(img, Point(dx + 150, dy + 100), Size(100, 70), 0, 0, 360, white, -1, 8, 0);
ellipse(img, Point(dx + 115, dy + 70), Size(30, 20), 0, 0, 360, black, -1, 8, 0);
ellipse(img, Point(dx + 185, dy + 70), Size(30, 20), 0, 0, 360, black, -1, 8, 0);
ellipse(img, Point(dx + 115, dy + 70), Size(15, 15), 0, 0, 360, white, -1, 8, 0);
ellipse(img, Point(dx + 185, dy + 70), Size(15, 15), 0, 0, 360, white, -1, 8, 0);
ellipse(img, Point(dx + 115, dy + 70), Size(5, 5), 0, 0, 360, black, -1, 8, 0);
ellipse(img, Point(dx + 185, dy + 70), Size(5, 5), 0, 0, 360, black, -1, 8, 0);
ellipse(img, Point(dx + 150, dy + 100), Size(10, 5), 0, 0, 360, black, -1, 8, 0);
ellipse(img, Point(dx + 150, dy + 150), Size(40, 10), 0, 0, 360, black, -1, 8, 0);
ellipse(img, Point(dx + 27, dy + 100), Size(20, 35), 0, 0, 360, white, -1, 8, 0);
ellipse(img, Point(dx + 273, dy + 100), Size(20, 35), 0, 0, 360, white, -1, 8, 0);
}
//show the faces
namedWindow("image", 1);
imshow("image", img);
//Extract the contours so that
//vector<vector<Point> > contours0;
vector<cv::Mat> coutours;
findContours(img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
contours.resize(contours.size());
for (size_t k = 0; k < contours.size(); k++)
approxPolyDP(Mat(contours[k]), contours[k], 3, true);
namedWindow("contours", 1);
createTrackbar("levels+3", "contours", &levels, 7, on_trackbar);
on_trackbar(0, 0);
waitKey();
return 0;
}
I am using x64 architecture and linked all the library .lib along with d.lib(debug library).
I think the problem comes from your "contours" variable. You're declaring it as a vector<cv::Mat>, but the contours are not represented as a matrix, but rather as a series of points.
Look at this example : http://docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html
They declare the contours as vector<vector<Point> > contours;
Look also at the declaration of the function (http://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=findcontours#findcontours), the paramater contour is defined as : contours – Detected contours. Each contour is stored as a vector of points.