Python, Pygame, Image Manipulation: Restretch A Loaded Png, To Be The Texture For An Isometric Tile
Solution 1:
Big thanks to Spektre for all the effort he made, trying to help me, but all in all, after two days of over-thinking the problem and bug-fixing, I came up with a solution myself. It might not be as fast or efficient as targeting the pixels directly in an array, like Spektre did in his c++ example, but it is a way, you're only dependencies are pygame, and it is easy to understand.
What did I do? - I wrote two functions, the first getting a surface containing only a single column of another surface, with an index, referring to the x position of the column. And the second, calculating a coefficient, how far down each row should get moved, if the last row should get shifted down a certain amount of pixels and then returning a surface with the shifted picture.
Here is the magic Code:
import pygame
from pygame.locals import *
from pygame import Surface
def getColumn(surface, index):
assert index <= surface.get_width(), "index can't be bigger, than surface width"
height = surface.get_height()
subsurf = Surface((1,height)) # Create Surface 1 px by picture-height high, to store the output in
subsurf.blit(surface.subsurface(pygame.Rect( (index,0),(1,height) )),(0,0)) # Blit a one pixel width subsurface with x Position at index of the image to subsurf
return subsurf
def shiftRightDown(surface, pixels):
size = surface.get_size()
newSize = (size[0], size[1]+pixels)
coeff = pixels / size[0]
returnSurface = Surface(newSize)
for i in range(size[1]): # here happens the magic
returnSurface.blit(getColumn(surface, i), (i,0+int(i*coeff)))
return returnSurface
After all, big respect to Spektres coding skills, even though I'm to dumb to understand anything from the c plus plus example, as I'm a total beginner.
Solution 2:
Well I did this by simply copy the texture pixels into sprite using plane projections (basis vectors of each side) + some rescaling as the texture does not correspond with your sprite resolution. I did it in C++ so here my commented code (you can extract the equations from it):
// [constants]
const int sxs=128; // target sprite resolution [pixels]
const int sys=128;
const int height=32; // height/thickness of your tile [pixels]
const DWORD cback=0x00FFFFFF; // background color (or invisible for the sprite)
// [variables]
DWORD **ptxr,**pspr; // direct pixel access pointers (any 32bit variable type)
Graphics::TBitmap *txr,*spr; // VCL bitmaps
int txs,tys,x,y,x0,y0,xx,yy,th;
// [init]
// create VCL bitmaps (can ignore this)
txr=new Graphics::TBitmap; // input texture
spr=new Graphics::TBitmap; // output sprite
// load texture
txr->LoadFromFile("texture.bmp");
txs=txr->Width;
tys=txr->Height;
// prepare sprite resolution
spr->SetSize(sxs,sys);
// allow direct pixel access
txr->HandleType=bmDIB; txr->PixelFormat=pf32bit; ptxr=new DWORD*[tys]; for (y=0;y<tys;y++) ptxr[y]=(DWORD*)txr->ScanLine[y];
spr->HandleType=bmDIB; spr->PixelFormat=pf32bit; pspr=new DWORD*[sys]; for (y=0;y<sys;y++) pspr[y]=(DWORD*)spr->ScanLine[y];
// [render sprite]
th=height*(txs-1)/(sxs-1); // height of tile in texture [pixels]
// clear
for (y=0;y<sys;y++)
for (x=0;x<sxs;x++)
pspr[y][x]=cback;
// top side
x0=0; y0=(sys*3/4)-height;
for (y=0;y<tys;y++)
for (x=0;x<txs;x++)
{
// isometric projection of top side
xx=x0+(x+y)*(sxs-1)/((txs-1)*2);
yy=y0+(x-y)*(sxs-1)/((txs-1)*4);
// copy pixel from texture to sorite
if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys))
pspr[yy][xx]=ptxr[y][x];
}
// left side
x0=0; y0=(sys*3/4)-height;
for (y=0;(y<tys)&&(y<th);y++)
for (x=0;x<txs;x++)
{
// isometric projection of top side
xx=x0+(x )*(sxs-1)/((txs-1)*2);
yy=y0+(x+(4*y))*(sxs-1)/((txs-1)*4);
// copy pixel from texture to sorite
if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys))
pspr[yy][xx]=ptxr[y][x];
}
// right side
x0=sxs/2; y0=sys-height-1;
for (y=0;(y<txs)&&(y<th);y++) // x,y are swapped to avoid connection seems
for (x=0;x<tys;x++)
{
// isometric projection of top side
xx=x0+(+x )*(sxs-1)/((txs-1)*2);
yy=y0+(-x+(4*y))*(sxs-1)/((txs-1)*4);
// copy pixel from texture to sorite
if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys))
pspr[yy][xx]=ptxr[x][y];
}
// here do your stuff with your sprite spr I render source and resulting images into bitmap to show on screen
// you can ignoe this
bmp->SetSize(txs+5+sxs,max(tys,sys));
bmp->Canvas->Brush->Color=clBtnFace;
bmp->Canvas->FillRect(TRect(0,0,bmp->Width,bmp->Height));
bmp->Canvas->Draw(0,0,txr);
bmp->Canvas->Draw(txs+5,0,spr);
// [exit]
// release memory
delete[] ptxr;
delete[] pspr;
if (txr) delete txr; txr=NULL;
if (spr) delete spr; spr=NULL;
The texture must be square otherwise the right side rendering will have access violation troubles not to mention visible seams ...
Here output sprite example of this code:
Now how it works:
ignore the VCL init/load/exit stuff handling images as the important stuff is just the rendering.
Each part consist of setting start point (red square) and convert texture x,y
coordinates into offset from that start point in plane projection basis vectors (the black arrows).
And the offset is also multiplied by the resolution ratio between texture and sprite to handle their different sizes.
Look here to understand the direct pixel access I used:
PS
You can add lighting to enhance 3D look ... This is how it looks when top side is 100% left side is 75% and right side is 50% of intensity:
simulating light coming from above left side
Post a Comment for "Python, Pygame, Image Manipulation: Restretch A Loaded Png, To Be The Texture For An Isometric Tile"