mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
123 lines
5.2 KiB
C++
123 lines
5.2 KiB
C++
#include "api.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <new>
|
|
|
|
namespace raven {
|
|
|
|
uint8_t combine_raster(uint32_t mode,uint8_t pen,uint8_t destination);
|
|
|
|
const DesktopPen *DesktopObjects::pen(uint64_t handle) const
|
|
{
|
|
if (handle>=desktop_stock_pen && handle<desktop_stock_pen+stock_pens.size()) return &stock_pens[size_t(handle-desktop_stock_pen)];
|
|
const auto found=objects.find(handle);return found==objects.end() ? nullptr : std::get_if<DesktopPen>(&found->second);
|
|
}
|
|
|
|
bool DesktopObjects::pen_selected(uint64_t handle) const
|
|
{
|
|
for(const auto &[identity,object]:objects)
|
|
if(const auto *device=std::get_if<DesktopDC>(&object);device && device->pen==handle)return true;
|
|
return false;
|
|
}
|
|
|
|
uint64_t DesktopObjects::select_pen(uint64_t handle,uint64_t selected)
|
|
{
|
|
auto *device=dc(handle);if(!device || !pen(selected))return 0;
|
|
const auto previous=device->pen;device->pen=selected;
|
|
if(const auto *old=pen(previous);old && old->pending_delete && !pen_selected(previous))erase(previous);
|
|
return previous;
|
|
}
|
|
|
|
bool DesktopObjects::delete_pen(uint64_t handle)
|
|
{
|
|
if(handle>=desktop_stock_pen && handle<desktop_stock_pen+stock_pens.size())return true;
|
|
const auto found=objects.find(handle);if(found==objects.end())return false;
|
|
auto *value=std::get_if<DesktopPen>(&found->second);if(!value)return false;
|
|
if(!pen_selected(handle))return erase(handle);
|
|
value->pending_delete=true;return true;
|
|
}
|
|
|
|
uint8_t combine_raster(uint32_t mode,uint8_t pen,uint8_t destination)
|
|
{
|
|
switch(mode){
|
|
case 1: return 0;
|
|
case 2: return uint8_t(~(pen|destination));
|
|
case 3: return uint8_t(~pen&destination);
|
|
case 4: return uint8_t(~pen);
|
|
case 5: return uint8_t(pen&~destination);
|
|
case 6: return uint8_t(~destination);
|
|
case 7: return uint8_t(pen^destination);
|
|
case 8: return uint8_t(~(pen&destination));
|
|
case 9: return uint8_t(pen&destination);
|
|
case 10: return uint8_t(~(pen^destination));
|
|
case 11: return destination;
|
|
case 12: return uint8_t(~pen|destination);
|
|
case 14: return uint8_t(pen|~destination);
|
|
case 15: return uint8_t(pen|destination);
|
|
case 16: return 0xff;
|
|
default: return pen;
|
|
}
|
|
}
|
|
|
|
bool NativeDesktop::pen_dispatch(std::string_view name)
|
|
{
|
|
const bool move=name=="MoveToEx",line=name=="LineTo",position=name=="GetCurrentPositionEx";
|
|
const bool extents=name=="GetWindowExtEx" || name=="GetViewportExtEx";
|
|
if(!move && !line && !position && !extents && name!="CreatePen" && name!="GetGraphicsMode" && name!="GdiFlush")return false;
|
|
const auto args=process.api_arguments(name=="GdiFlush" ? 0 : name=="CreatePen" || line ? 3 : move ? 4 : name=="GetGraphicsMode" ? 1 : 2);
|
|
uint64_t result=0;
|
|
try {
|
|
if(name=="GdiFlush")result=1;
|
|
else if(name=="CreatePen"){
|
|
const auto style=uint32_t(args[0]);const auto width=int32_t(args[1]);const auto color=uint32_t(args[2]);
|
|
if((style!=0 && style!=5) || width<0 || width>1 || (color & 0xff000000U))throw uint32_t(120);
|
|
result=objects.add(DesktopPen{color,style==5});if(!result)throw uint32_t(8);
|
|
}else{
|
|
auto *dc=objects.dc(args[0]);if(!dc)throw uint32_t(6);
|
|
const auto point=[&](uint64_t output,int32_t x,int32_t y){
|
|
std::array<uint8_t,8> bytes{};for(size_t i=0;i<4;++i){bytes[i]=uint8_t(uint32_t(x)>>(i*8));bytes[i+4]=uint8_t(uint32_t(y)>>(i*8));}process.write_memory(output,bytes);
|
|
};
|
|
if(name=="GetGraphicsMode")result=1;
|
|
else if(extents){point(args[1],1,1);result=1;}
|
|
else if(position){point(args[1],dc->position_x,dc->position_y);result=1;}
|
|
else if(move){
|
|
if(args[3])point(args[3],dc->position_x,dc->position_y);
|
|
dc->position_x=int32_t(args[1]);dc->position_y=int32_t(args[2]);result=1;
|
|
}else{
|
|
const auto *pen=objects.pen(dc->pen);if(!pen)throw uint32_t(6);
|
|
const auto target_x=int32_t(args[1]),target_y=int32_t(args[2]);
|
|
bool changed=false;
|
|
if(!pen->hollow){
|
|
auto *bitmap=objects.bitmap(dc->bitmap);if(!bitmap || bitmap->depth!=32)throw uint32_t(120);
|
|
auto x=int64_t(dc->position_x),y=int64_t(dc->position_y);
|
|
const auto dx=std::abs(int64_t(target_x)-x),dy=std::abs(int64_t(target_y)-y);
|
|
if(std::max(dx,dy)>1048576)throw uint32_t(120);
|
|
const int64_t sx=x<target_x ? 1 : -1,sy=y<target_y ? 1 : -1;
|
|
auto error=dx-dy;
|
|
const auto pixels=bitmap->pixels();
|
|
while(x!=target_x || y!=target_y){
|
|
const auto px=x+dc->origin_x,py=y+dc->origin_y;
|
|
if(px>=0 && py>=0 && px<bitmap->width && py<bitmap->height && dc->contains(px,py)){
|
|
const auto row=bitmap->top_down ? py : bitmap->height-1-py;const auto at=(size_t(row)*bitmap->width+size_t(px))*4;
|
|
// Pen channels combine with the destination through the DC's binary raster operation.
|
|
const uint8_t source[3]={uint8_t(pen->color>>16),uint8_t(pen->color>>8),uint8_t(pen->color)};
|
|
for(size_t channel=0;channel<3;++channel)pixels[at+channel]=combine_raster(dc->rop2,source[channel],pixels[at+channel]);
|
|
pixels[at+3]=0;changed=true;
|
|
}
|
|
const auto twice=2*error;
|
|
if(twice> -dy || (twice== -dy && sx<0)){error-=dy;x+=sx;}
|
|
if(twice<dx || (twice==dx && sy<0)){error+=dx;y+=sy;}
|
|
}
|
|
}
|
|
dc->position_x=target_x;dc->position_y=target_y;result=1;
|
|
if(changed && dc->window && dc->leased && updated)updated(dc->window);
|
|
}
|
|
}
|
|
}catch(uint32_t error){environment.set_last_error(error);}
|
|
catch(const std::bad_alloc &){environment.set_last_error(8);}
|
|
process.return_api(result);return true;
|
|
}
|
|
|
|
}
|