I have a Heatmap that currently works on a stand alone sever that I am porting to Azure Storage. How do I go about saving the butmap file in Azure Storage. Originally I had a entry in my web.config file that pointed the image cache to a direct path on anther drive (IE ) Everything now will be in ~/map_cache folder in the Storage Account. How can I mod this for storage locally in Azure.
protected void Page_Load(object sender, EventArgs e)
{
xxxxxxxdb = new xxxxxxx(ConfigurationManager.AppSettings["xxxxxxx"]);
string imageCachePath = Server.MapPath("/map_cache/HotSpots");
int fileExpirationTime = int.Parse(ConfigurationManager.AppSettings["HotspotImageExpirationTime"]);
Bitmap bitmap;
string requestParam = Page.Request.Params["id"];
string bitmapFileName = Path.Combine(imageCachePath, requestParam + ".png");
if (File.Exists(bitmapFileName) && File.GetCreationTime(bitmapFileName) > DateTime.Now.AddHours(-fileExpirationTime))
{
bitmap = (Bitmap)Image.FromFile(bitmapFileName);
}
else
{
int zoomLevel = requestParam.Length;
double tileX = 0;
double tileY = 0;
for (int index = 0; index < zoomLevel; index++)
{
int digit = int.Parse(requestParam[index].ToString());
tileY += ((digit & 2) / 2) * Math.Pow(2, (zoomLevel - index - 1));
tileX += (digit & 1) * Math.Pow(2, (zoomLevel - index - 1));
}
double pixelXMin = tileX * 256;
double pixelYMin = tileY * 256;
double pixelXMax = (tileX + 1) * 256 - 1;
double pixelYMax = (tileY + 1) * 256 - 1;
double longMin = ((pixelXMin * 360) / (256 * Math.Pow(2, zoomLevel))) - 180;
double longMax = ((pixelXMax * 360) / (256 * Math.Pow(2, zoomLevel))) - 180;
double latMin = Math.Asin((Math.Exp((0.5 - pixelYMin / 256 / Math.Pow(2, zoomLevel)) * 4 * Math.PI) - 1) /
(Math.Exp((0.5 - pixelYMin / 256 / Math.Pow(2, zoomLevel)) * 4 * Math.PI) + 1)) * 180 /
Math.PI;
double latMax = Math.Asin((Math.Exp((0.5 - pixelYMax / 256 / Math.Pow(2, zoomLevel)) * 4 * Math.PI) - 1) /
(Math.Exp((0.5 - pixelYMax / 256 / Math.Pow(2, zoomLevel)) * 4 * Math.PI) + 1)) * 180 /
Math.PI;
double pixelResolution = (Math.Cos(latMax * Math.PI / 180) * 2 * Math.PI * 6378137) / (256 * Math.Pow(2, zoomLevel));
double pixelArea = Math.Pow(pixelResolution, 2);
double maxHotspotDensity = Math.Max(120.0 / zoomLevel, 3.0) / pixelArea;
bitmap = GenerateBlankBitmap();
var accidents = from hs in db.cs_PT_VEGeoDatas
where hs.Latitude <= latMin && hs.Latitude >= latMax
&& hs.Longitude >= longMin && hs.Longitude <= longMax
select new { hs.Latitude, hs.Longitude };
Dictionary<Point, HotSpot> hotSpots = new Dictionary<Point, HotSpot>();
foreach (var accident in accidents)
{
int pixelX, pixelY;
LatLongToPixelXY(accident.Latitude, accident.Longitude, zoomLevel, out pixelX, out pixelY);
pixelX %= 256;
pixelY %= 256;
for (int ix = -doublePixelSize; ix <= doublePixelSize; ix++)
{
for (int iy = -doublePixelSize; iy <= doublePixelSize; iy++)
{
Point point;
bool borderPoint = false;
if (zoomLevel < doublePixelZoomLevel)
{
point = new Point(pixelX, pixelY);
}
else
{
if (pixelX + ix >= 0 && pixelX + ix <= 255 && pixelY + iy >= 0 && pixelY + iy <= 255)
{
point = new Point(pixelX + ix, pixelY + iy);
borderPoint = (ix == -doublePixelSize) || (iy == -doublePixelSize) ||
(ix == doublePixelSize) || (iy == doublePixelSize);
}
else
{
break;
}
}
HotSpot hotSpot;
if (hotSpots.ContainsKey(point))
{
hotSpot = hotSpots[point];
hotSpot.borderPoint &= borderPoint;
hotSpot.count += 1;
}
else
{
hotSpot = new HotSpot { borderPoint = borderPoint, count = 1 };
hotSpots.Add(point, hotSpot);
}
if (zoomLevel < doublePixelZoomLevel)
{
break;
}
}
if (zoomLevel < doublePixelZoomLevel)
{
break;
}
}
}
foreach (var hotspotPixel in hotSpots)
{
double hc = hotspotPixel.Value.count;
double hcDensity = hc / pixelArea;
Color color;
if (!hotspotPixel.Value.borderPoint)
{
color = Color.FromArgb(255, 255,
(int)
Math.Max((maxHotspotDensity - hcDensity) / maxHotspotDensity * 255, 0.0),
0);
}
else
{
color = Color.Black;
}
bitmap.SetPixel(hotspotPixel.Key.X, hotspotPixel.Key.Y, color);
}
bitmap.Save(bitmapFileName);
}
WritePngToStream(bitmap, Response.OutputStream);
}
Currently I get the following Error message
A generic error occurred in GDI+.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[ExternalException (0x80004005): A generic error occurred in GDI+.]
System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams) +772265
HotSpotTileServer.Page_Load(Object sender, EventArgs e) in C:\Projects\xxx\xxx\SpeedTrap\HotSpotTileServer.aspx.cs:141
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +25
System.EventHandler.Invoke(Object sender, EventArgs e) +0
System.Web.UI.Control.LoadRecursive() +71
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3048
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1
There are three things you can try:
for temporary files - that don't need to be shared across web servers (which is rare!) - then you can use Local Storage - http://vkreynin.wordpress.com/2010/01/10/learning-azure-local-storage-with-me/
for a disk shared between web servers (but only one of these will have write access) you could use an Azure Drive - http://blog.maartenballiauw.be/post/2010/02/02/Using-Windows-Azure-Drive-(aka-X-Drive).aspx
for general flexible shared file storage, try using Azure Blob Storage - http://blogs.msdn.com/b/jnak/archive/2008/10/29/walkthrough-simple-blob-storage-sample.aspx
Definitely the last of these is the one I'd recommend - it's fast, flexible and scalable.
(I agree with Stuart 100%.) Here are more reasons why I recommend you consider using Azure Blob Storage for storing heatmap png file:
The local hard disk is not guaranteed to be durable. (This may not be important to you, however, if they can be regenerated easily.)
Blobs can be made publicly visible (so, for example, they can be directly referenced from HTML code with an img tag).
Blobs can easily be made available in the AppFabric CDN (for better performance, including around 24 global distribution points).
Blobs will scale in ways that using the local file system will not. For example, if you ever want to scale your site to use more than one Heatmap generator role instance (running 2 of them on different machines in the cloud), you will want to be on Blob storage since none of the other options will work.
Blobs are optimized for cloud scale and reliability and high availability.
I recommend you use the very handy Windows Azure SDK for writing your blobs. The SDK wraps the official REST interfaces with a very nice set of classes that are really easy to use from .NET code. Specifically you would use the CloudBlobClient class, and the method UploadByteArray. You can download the Azure SDK 1.4 here.
Related
So I'm using AzerothCore, and I recently just installed a Haste Mod, it's working, but not functioning properly, the Haste Mod I had to install manually due to times changing and codes changing, although now.
My issue is that even though I get 0.00 after I reached a certain amount of haste, it is too fast & proccs don't work, example: Shadowmourne weapon effect...
Sometimes I'm hitting up to 80 times pr game tick, and it varies for 50-80, I want to ask you guys, is it possible to make it locked to a certain amount of attack counts? Both Melee and Ranged, due to sound glitching out also, sometimes sound disappears until I stop attacking, This is unlike any haste mod I ever tested, whether it be a live server or a test server...
The Hastemod is the following:
PLAYER.CPP
hasteMod = 2.9;
/* hastemod
if (haveOffhandWeapon())
if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
*/
/* hastemod
if (getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);
*/
// hastemod begins
void Player::SetAttackTime(WeaponAttackType att, uint32 val) {
switch(att) {
case BASE_ATTACK:
ApplyHasteMod(m_baseRatingValue[CR_HASTE_MELEE],BASE_ATTACK,CR_HASTE_MELEE, val);
break;
case OFF_ATTACK:
ApplyHasteMod(m_baseRatingValue[CR_HASTE_MELEE],OFF_ATTACK,CR_HASTE_MELEE, val);
break;
case RANGED_ATTACK:
ApplyHasteMod(m_baseRatingValue[CR_HASTE_RANGED],RANGED_ATTACK,CR_HASTE_RANGED, val);
break;
default:
SetFloatValue(UNIT_FIELD_BASEATTACKTIME+att,val*m_modAttackSpeedPct[att]);
}
}
void Player::ApplyHasteMod(int16 val, uint32 att, CombatRating cr, float speed) {
float atkSpd = speed * (( 100.0f - ((val / 32.789989f) * hasteMod) ) / 100.0f );
if (atkSpd < 0) atkSpd = 0;
SetFloatValue(UNIT_FIELD_BASEATTACKTIME+att, atkSpd);
}
// hastemod ends
void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply)
{
m_baseRatingValue[cr]+=(apply ? value : -value);
// explicit affected values
switch (cr)
{
case CR_HASTE_MELEE:
{
Item * mainhand = m_items[EQUIPMENT_SLOT_MAINHAND];
Item * offhand = m_items[EQUIPMENT_SLOT_OFFHAND];
float speed1 = mainhand ? (float)mainhand->GetTemplate()->Delay : 2000.0f;
float speed2 = offhand ? (float)offhand->GetTemplate()->Delay : 2000.0f;
ApplyHasteMod(m_baseRatingValue[cr], BASE_ATTACK, cr, speed1);
ApplyHasteMod(m_baseRatingValue[cr], OFF_ATTACK, cr, speed2);
float RatingChange = value / value;
(RatingChange = 0) ? ApplyPercentModFloatVar(m_modAttackSpeedPct[BASE_ATTACK], RatingChange, !apply) : ApplyPercentModFloatVar(m_modAttackSpeedPct[BASE_ATTACK], -RatingChange, apply);
(RatingChange = 0) ? ApplyPercentModFloatVar(m_modAttackSpeedPct[OFF_ATTACK], RatingChange, !apply) : ApplyPercentModFloatVar(m_modAttackSpeedPct[OFF_ATTACK], -RatingChange, apply);
break;
}
case CR_HASTE_RANGED:
{
Item * ranged = m_items[EQUIPMENT_SLOT_RANGED];
float speed = ranged ? (float)ranged->GetTemplate()->Delay : 2000.0f;
ApplyHasteMod(m_baseRatingValue[cr], RANGED_ATTACK, cr, speed);
float RatingChange = value / value;
(RatingChange = 0) ? ApplyPercentModFloatVar(m_modAttackSpeedPct[RANGED_ATTACK], RatingChange, !apply) : ApplyPercentModFloatVar(m_modAttackSpeedPct[RANGED_ATTACK], -RatingChange, apply);
break;
}
case CR_HASTE_SPELL:
{
ApplyHasteMod(m_baseRatingValue[cr], CR_HASTE_SPELL-1, cr, 1.0f);
break;
}
default:
PLAYER.H
float hasteMod;
public:
float GetHasteMod() const { return hasteMod; }
void SetHasteMod(float val) { hasteMod = val; }
void ApplyHasteMod(int16 val, uint32 att, CombatRating cr, float speed);
void SetAttackTime(WeaponAttackType att, uint32 val);
STATSYSTEM.CPP
void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& min_damage, float& max_damage)
{
UnitMods unitMod;
Item *Weapon = NULL;
switch (attType)
{
case BASE_ATTACK:
default:
Weapon = m_items[EQUIPMENT_SLOT_MAINHAND];
unitMod = UNIT_MOD_DAMAGE_MAINHAND;
break;
case OFF_ATTACK:
Weapon = m_items[EQUIPMENT_SLOT_OFFHAND];
unitMod = UNIT_MOD_DAMAGE_OFFHAND;
break;
case RANGED_ATTACK:
Weapon = m_items[EQUIPMENT_SLOT_RANGED];
unitMod = UNIT_MOD_DAMAGE_RANGED;
break;
}
float wep_speed =(!Weapon) ? 2200.00f : Weapon->GetTemplate()->Delay;
float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f (wep_speed/1000.00f);
float att_speed = GetAPMultiplier(attType,normalized);
float base_pct = GetModifierValue(unitMod, BASE_PCT);
float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
float total_pct = addTotalPct ? GetModifierValue(unitMod, TOTAL_PCT) : 1.0f;
float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
if (lvl > 60)
lvl = 60;
weapon_mindamage = lvl*3.0f*wep_speed/1000;
weapon_maxdamage = lvl*5.0f*wep_speed/1000;
}
else if (!CanUseAttackType(attType)) //check if player not in form but still can't use (disarm case)
{
I would love if you guys have heard or seen anything like this before, and know what to do, I've spent the last 3 days, trying to figure it out... I know it's possible, just don't know how, or if it's even possible with this hastemod.
You should avoid modifying the main core's files (e.g. Player.cpp, Player.h, etc..) but create a real (separate) module for your custom changes:
http://www.azerothcore.org/wiki/Create-a-Module
If you need to implement new hooks, then you should send a PR to the main repo. So your core codebase will always be clean (without custom changes).
I use media foundation to capture alive webcam video, is it possible to get the frames captured in a byte streams format in Run-Time and to write them as a stream of bits in a text file after each time cycle ?
I am not sure that I can have the stream in byte format(without container) neither I can do that on run time?
It's not completely clear what you're asking. If you want to capture the raw frames from webcam and save them to a file then the answer is yes that can be done. The Media Foundation SDK MFCaptureToFile sample does exactly that although because it uses a SinkWriter you will have to specify a container file type such as mp4 when creating it.
If you really do want to get the raw frames one by one then you need to dispense with the SinkWriter (or write a custom one). Below is a code snippet that shows getting samples from an IMFSourceReader and converting them into a byte array (and a few other things). You could write the byte array to a text file although unless you do something like put a bitmap header on it it won't be very useful. The IMFSourceReader, IMFMediaTypes all need to be set up correctly prior to being able to call ReadSample but hopefully it gives you a rough idea of where to look further.
HRESULT MFVideoSampler::GetSample(/* out */ array<Byte> ^% buffer)
{
if (_videoReader == NULL) {
return -1;
}
else {
IMFSample *videoSample = NULL;
DWORD streamIndex, flags;
LONGLONG llVideoTimeStamp;
// Initial read results in a null pSample??
CHECK_HR(_videoReader->ReadSample(
//MF_SOURCE_READER_ANY_STREAM, // Stream index.
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, // Flags.
&streamIndex, // Receives the actual stream index.
&flags, // Receives status flags.
&llVideoTimeStamp, // Receives the time stamp.
&videoSample // Receives the sample or NULL.
), L"Error reading video sample.");
if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
{
wprintf(L"\tEnd of stream\n");
}
if (flags & MF_SOURCE_READERF_NEWSTREAM)
{
wprintf(L"\tNew stream\n");
}
if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
{
wprintf(L"\tNative type changed\n");
}
if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
{
wprintf(L"\tCurrent type changed\n");
IMFMediaType *videoType = NULL;
CHECK_HR(_videoReader->GetCurrentMediaType(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
&videoType), L"Error retrieving current media type from first video stream.");
Console::WriteLine(GetMediaTypeDescription(videoType));
// Get the frame dimensions and stride
UINT32 nWidth, nHeight;
MFGetAttributeSize(videoType, MF_MT_FRAME_SIZE, &nWidth, &nHeight);
_width = nWidth;
_height = nHeight;
//LONG lFrameStride;
//videoType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lFrameStride);
videoType->Release();
}
if (flags & MF_SOURCE_READERF_STREAMTICK)
{
wprintf(L"\tStream tick\n");
}
if (!videoSample)
{
printf("Failed to get video sample from MF.\n");
}
else
{
DWORD nCurrBufferCount = 0;
CHECK_HR(videoSample->GetBufferCount(&nCurrBufferCount), L"Failed to get the buffer count from the video sample.\n");
IMFMediaBuffer * pMediaBuffer;
CHECK_HR(videoSample->ConvertToContiguousBuffer(&pMediaBuffer), L"Failed to extract the video sample into a raw buffer.\n");
DWORD nCurrLen = 0;
CHECK_HR(pMediaBuffer->GetCurrentLength(&nCurrLen), L"Failed to get the length of the raw buffer holding the video sample.\n");
byte *imgBuff;
DWORD buffCurrLen = 0;
DWORD buffMaxLen = 0;
pMediaBuffer->Lock(&imgBuff, &buffMaxLen, &buffCurrLen);
if (Stride != -1 && Stride < 0) {
// Bitmap needs to be flipped.
int bmpSize = buffCurrLen; // ToDo: Don't assume RGB/BGR 24.
int absStride = Stride * -1;
byte *flipBuf = new byte[bmpSize];
for (int row = 0; row < _height; row++) {
for (int col = 0; col < absStride; col += 3) {
flipBuf[row * absStride + col] = imgBuff[((_height - row - 1) * absStride) + col];
flipBuf[row * absStride + col + 1] = imgBuff[((_height - row - 1) * absStride) + col + 1];
flipBuf[row * absStride + col + 2] = imgBuff[((_height - row - 1) * absStride) + col + 2];
}
}
buffer = gcnew array<Byte>(buffCurrLen);
Marshal::Copy((IntPtr)flipBuf, buffer, 0, buffCurrLen);
delete flipBuf;
}
else {
buffer = gcnew array<Byte>(buffCurrLen);
Marshal::Copy((IntPtr)imgBuff, buffer, 0, buffCurrLen);
}
pMediaBuffer->Unlock();
pMediaBuffer->Release();
videoSample->Release();
return S_OK;
}
}
}
I am trying to load a java class to oracle as a function. In the server I managed to use loadjava as below:
C:\Users\n12017>loadjava -user USER1/passw E:\JAVA_repository\SOOSProjects\Mehmet_java_2db_trial\classes\mehmet_java_2db_trial\kondrakk.class
And in the oracle db side:
create or replace function ngram_kondrakk(src in varchar2, trg in varchar2)
return float
as language java
name 'mehmet_java_2db_trial/kondrakk.getDistance(java.lang.string, java.lang.string) return java.lang.float';
/
However, when I apply the query as below, I got error. (As a result of the query I am expecting a similarity score of 1 since two identical strings are compared)
select ngram_kondrakk('mehmet','mehmet') from dual;
Here is the error:
ORA-29532: Java call terminated by uncaught Java exception: System error : java/lang/UnsupportedClassVersionError
29532. 00000 - "Java call terminated by uncaught Java exception: %s"
*Cause: A Java exception or error was signaled and could not be
resolved by the Java code.
*Action: Modify Java code, if this behavior is not intended.
Finally, here is the code that I am trying to use:
package mehmet_java_2db_trial;
public class kondrakk {
public static float getDistance(String source, String target) {
final int sl = source.length();
final int tl = target.length();
if (sl == 0 || tl == 0) {
if (sl == tl) {
return 1;
}
else {
return 0;
}
}
int n=3;
int cost = 0;
if (sl < n || tl < n) {
for (int i=0,ni=Math.min(sl,tl);i<ni;i++) {
if (source.charAt(i) == target.charAt(i)) {
cost++;
}
}
return (float) cost/Math.max(sl, tl);
}
char[] sa = new char[sl+n-1];
float p[]; //'previous' cost array, horizontally
float d[]; // cost array, horizontally
float _d[]; //placeholder to assist in swapping p and d
//construct sa with prefix
for (int i=0;i<sa.length;i++) {
if (i < n-1) {
sa[i]=0; //add prefix
}
else {
sa[i] = source.charAt(i-n+1);
}
}
p = new float[sl+1];
d = new float[sl+1];
// indexes into strings s and t
int i; // iterates through source
int j; // iterates through target
char[] t_j = new char[n]; // jth n-gram of t
for (i = 0; i<=sl; i++) {
p[i] = i;
}
for (j = 1; j<=tl; j++) {
//construct t_j n-gram
if (j < n) {
for (int ti=0;ti<n-j;ti++) {
t_j[ti]=0; //add prefix
}
for (int ti=n-j;ti<n;ti++) {
t_j[ti]=target.charAt(ti-(n-j));
}
}
else {
t_j = target.substring(j-n, j).toCharArray();
}
d[0] = j;
for (i=1; i<=sl; i++) {
cost = 0;
int tn=n;
//compare sa to t_j
for (int ni=0;ni<n;ni++) {
if (sa[i-1+ni] != t_j[ni]) {
cost++;
}
else if (sa[i-1+ni] == 0) { //discount matches on prefix
tn--;
}
}
float ec = (float) cost/tn;
// minimum of cell to the left+1, to the top+1, diagonally left and up +cost
d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1), p[i-1]+ec);
}
// copy current distance counts to 'previous row' distance counts
_d = p;
p = d;
d = _d;
}
// our last action in the above loop was to switch d and p, so p now
// actually has the most recent cost counts
System.out.println(1.0f - (p[sl] / Math.max(tl, sl)));
return 1.0f - (p[sl] / Math.max(tl, sl));
}
}
Please HELP!
Thanks in advance...
When compiling Java classes to load into an Oracle database, be sure that you compile your Java classes to run with the JVM inside the Oracle database.
The version of Java that comes with an Oracle database is typically out of date compared to the current Java: expect to be using Java 1.4 with Oracle 10g and 1.5 with 11g. Your best bet is to use the Java compiler that ships with the database, but if you can't do that, use -target 1.5 or suchlike to force the compiler to compile classes to run on Java 1.5.
Your plsql wrapper function has "/"
...mehmet_java_2db_trial/kondrakk.getDistance...
replace / with . [dot]
Check the docs
and as it was already mentioned - sync JVM of compilation with JVM for run-time( which will be Oracle JVM that is "attached" to DB)
I know that standard WMF file uses an 18-byte header followed by GDI command records. A simple web search tells me that : "There are two additional WMF variations that place another header in front of the standard header. A Placeable Metafile uses a 22-byte header containing x-y coordinates for positioning the image on the page". but I kind of don't understaffed the real life application for such meta file type? What kind of requirements is this type supposed to address in comparison to the standard WMF?
Why am I interested? I have the following code for re-sizing and converting a WMF to GIF which fails at the point it tries to construct the bitmap out of the META file:
public Stream Resize(string filePath, int maxSize)
{
try
{
MemoryStream stream = new MemoryStream();
using (Metafile img = new Metafile(filePath))
{
MetafileHeader header = img.GetMetafileHeader();
float scale = header.DpiX / 96f;
var newSize = CalcaulateSize(img.Width, img.Height, maxSize);
using (Bitmap bitmap = new Bitmap((int)(scale * img.Width / header.DpiX * 100), (int)(scale * img.Height / header.DpiY * 100)))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.Clear(Color.White);
g.ScaleTransform(scale, scale);
g.DrawImage(img, 0, 0);
}
var resizedBitmap = new Bitmap(newSize.Width, newSize.Height);
using (var g2 = Graphics.FromImage(resizedBitmap))
{
g2.CompositingQuality = CompositingQuality.HighQuality;
g2.InterpolationMode = InterpolationMode.HighQualityBicubic;
g2.SmoothingMode = SmoothingMode.AntiAlias;
g2.PixelOffsetMode = PixelOffsetMode.HighQuality;
g2.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g2.TextContrast = 12;
g2.Clear(Color.White);
g2.DrawImage(bitmap, 0, 0, newSize.Width, newSize.Height);
}
resizedBitmap.Save(stream, ImageFormat.Gif);
}
stream.Position = 0;
}
return stream;
}
catch (Exception)
{
return null;
}
and raises the exception "Argument is not valid".
(int)(scale * img.Width / header.DpiX * 100) = 22181
(int)(scale * img.Height / header.DpiY * 100)) = 33718
[too much memory to be allocated for a single bitmap all at once which results in immediate exception]
How would you alter the attached code to re-size and convert a place-able meta file?
I'd suspect that your size calculation is off.
Looking at my C++ code, I have this calculation based on the information in the enhanced metafile header where hEMF is the handle of the metafile. We then draw the image using a Graphics directly to the screen using those dimensions.
Hope this or the MSDN link helps a little. Sorry it's not more complete.
ENHMETAHEADER emh;
UINT nBytes = ::GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &emh);
if (nBytes != 0) {
RECT rect{ // Based on info from http://support.microsoft.com/kb/230675
(int) ((float) (emh.rclFrame.left) * emh.szlDevice.cx / (emh.szlMillimeters.cx*100.0f)), // Left
(int) ((float) (emh.rclFrame.top) * emh.szlDevice.cy / (emh.szlMillimeters.cy*100.0f)), // Top
(int) ((float) (emh.rclFrame.right) * emh.szlDevice.cx / (emh.szlMillimeters.cx*100.0f)), // Right
(int) ((float) (emh.rclFrame.bottom) * emh.szlDevice.cy / (emh.szlMillimeters.cy*100.0f)) // Bottom
};
bounds.x = abs(rect.right - rect.left);
bounds.y = abs(rect.bottom - rect.top);
I have a tile engine and that's all working swell, my player walks around all good, I'm working on adding items, the player is always in the centre of the screen, until he gets close to the edges of the world then he starts going close to the edges.
When I draw items in the world, they draw fine, except when the player leaves the centre (at the edge of the world). I just can't wrap my head around how to fix this.
public static void Draw(SpriteBatch spriteBatch, World w, Item i, Player p, Point screenDimensions)
{
bool IsOnScreen = true;
float leftX = p.X - ((screenDimensions.X / 32) / 2);
float rightX = leftX + (screenDimensions.X / 32);
float topY = p.Y - ((screenDimensions.Y / 32) / 2);
float bottomY = topY + (screenDimensions.Y / 32);
if (i.x < leftX || i.x > rightX || i.y < topY || i.y > bottomY)
IsOnScreen = false;
if (IsOnScreen)
i.animation.Draw(spriteBatch, (int)Math.Floor((i.x - leftX) * 32), (int)Math.Floor((i.y - topY) * 32));
}
Its pretty self explainatory, the world is passed in to get the dimensions (w.worldDimensions.x for width, and .y for height), the item is used to get the i.x and i.y (location in game world, not on screen), the player for drawing it relative (contains .x and .y for location) and then the screenDimensions.
Well it does not look very clear to me. Are you using a camera class? If you use a camera class and use that to navigate your world this should never happen.
Here is a basic one i currently use for my project.
class Camera
{
float zoom;
public float Rotation { get; private set; }
public Vector2 Position { get; private set; }
Matrix transform;
int velocity = 60;
UserInput input;
public float Zoom
{
get { return zoom; }
set { zoom = value; if (zoom < 0.1f) zoom = 0.1f; } // Negative zoom will flip image
}
public Camera(UserInput input)
{
zoom = 1.0f;
Rotation = 0f;
Position = new Vector2(0, 0);
this.input = input;
}
public void MoveCam()
{
if (input.IsKeyHold(Keys.Up))
{
Position += new Vector2(0, -velocity);
}
if (input.IsKeyHold(Keys.Left))
{
Position += new Vector2(-velocity, 0);
}
if (input.IsKeyHold(Keys.Down))
{
Position += new Vector2(0, velocity);
}
if (input.IsKeyHold(Keys.Right))
{
Position += new Vector2(velocity, 0);
}
if (input.IsKeyHold(Keys.OemMinus))
{
Zoom -= .01f * Zoom;
}
else if (input.IsKeyHold(Keys.OemPlus))
{
Zoom += .01f * Zoom;
}
}
public void FollowCam(int xPos, int yPos)
{
Position = new Vector2(xPos * TileData.Width, yPos * TileData.Height);
}
public Matrix TransformMatrix(GraphicsDevice device)
{
transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)) *
Matrix.CreateRotationX(MathHelper.ToRadians(Rotation)) *
Matrix.CreateRotationY(MathHelper.ToRadians(Rotation)) *
Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation)) *
Matrix.CreateScale(new Vector3(zoom, zoom, 0)) *
Matrix.CreateTranslation(new Vector3(device.Viewport.Width * 0.5f, device.Viewport.Height * 0.5f, 0));
return transform;
}
}
Just instantiate the class like in main and use this in your draw method.
batch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null, camera.TransformMatrix(graphicsDevice));
batch.End()
Draw everything in your world within this spritebatch and use a new basic to draw to screen cooridinates, like a gui/hud. You can use the camera move method to move it manually and the lock to lock it on any location (it follows if updated).
If you have large maps you might want to render only necessary tiles. I do it like this in my map class:
public void Draw(SpriteBatch batch, Vector2 camPosition, float camZoom, GraphicsDevice device)
{
float top = (camPosition.Y / TileData.Height) - ((device.Viewport.Height / 2) / TileData.Height + 1) / camZoom;
float bottom = (camPosition.Y / TileData.Height) + ((device.Viewport.Height / 2) / TileData.Height + 2) / camZoom;
float left = (camPosition.X / TileData.Width) - ((device.Viewport.Width / 2) / TileData.Width + 1) / camZoom;
float right = (camPosition.X / TileData.Width) + ((device.Viewport.Width / 2) / TileData.Width + 2) / camZoom;
for (int y = (int)top; y < (int)bottom; y++)
{
for (int x = (int)left; x < (int)right; x++)
{
if (y >= 0 && y < map.GetLength(1) && x >= 0 && x < map.GetLength(0))
{
batch.Draw(map[x, y].texture, new Rectangle(x * TileData.Width, y * TileData.Height, TileData.Width, TileData.Height), Color.White);
}
}
}
}
Here first i figure out which tiles to draw from each direction. Note the camZoom, you want more tiles to be drawn when zooming out. Then i use these "bounderies" in my for loops, the if statement makes sure i am not accessing tiles that dont exist (out of bounds).