litehtml/containers/test/test_container.cpp

363 lines
9.4 KiB
C++

#include "test_container.h"
#include "Font.h"
#define CANVAS_ITY_IMPLEMENTATION
#include "canvas_ity.hpp"
string readfile(string filename);
//
// canvas_ity adapters
//
void set_color(canvas& cvs, brush_type type, color c)
{
cvs.set_color(type, c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f);
}
void fill_rect(canvas& cvs, rect r)
{
cvs.fill_rectangle((float)r.x, (float)r.y, (float)r.width, (float)r.height);
}
void fill_rect(canvas& cvs, rect r, color color)
{
set_color(cvs, fill_style, color);
fill_rect(cvs, r);
}
void fill_circle(canvas& cvs, rect rc, color color)
{
float r = min(rc.width, rc.height) / 2.f;
float x = rc.x + rc.width / 2.f;
float y = rc.y + rc.height / 2.f;
set_color(cvs, fill_style, color);
cvs.begin_path();
cvs.arc(x, y, r, 0, 2*pi);
cvs.fill();
}
void draw_circle(canvas& cvs, rect rc, color color)
{
float r = min(rc.width, rc.height) / 2.f - .5f;
float x = rc.x + rc.width / 2.f;
float y = rc.y + rc.height / 2.f;
set_color(cvs, stroke_style, color);
cvs.begin_path();
cvs.arc(x, y, r, 0, 2*pi);
cvs.stroke();
}
void clip_rect(canvas& cvs, rect r)
{
cvs.begin_path();
cvs.rectangle((float)r.x, (float)r.y, (float)r.width, (float)r.height);
cvs.clip();
}
// without scaling
void draw_image(canvas& cvs, int x, int y, const Bitmap& bmp)
{
cvs.draw_image((byte*)bmp.data.data(), bmp.width, bmp.height, bmp.width * 4, (float)x, (float)y, (float)bmp.width, (float)bmp.height);
}
// with scaling
void draw_image(canvas& cvs, rect rc, const Bitmap& bmp)
{
cvs.draw_image((byte*)bmp.data.data(), bmp.width, bmp.height, bmp.width * 4, (float)rc.x, (float)rc.y, (float)rc.width, (float)rc.height);
}
void add_color_stop(canvas& cvs, brush_type type, float offset, color c, optional<float> hint)
{
cvs.add_color_stop(type, offset, c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f, hint);
}
bool set_font(canvas& cvs, const string& raw_font_data, int pixel_size)
{
return cvs.set_font((byte*)raw_font_data.data(), (int)raw_font_data.size(), (float)pixel_size);
}
void fill_polygon(canvas& cvs, vector<xy> points, color color)
{
cvs.begin_path();
cvs.polygon(points);
set_color(cvs, fill_style, color);
cvs.fill();
}
//
// test_container implementation
//
uint_ptr test_container::create_font(const char* font_families, int size, int weight, font_style /*italic*/, unsigned int /*decoration*/, font_metrics* fm)
{
Font* font = 0;
string_vector fonts = split_string(font_families, ",", "", "");
for (auto name : fonts)
{
font = Font::create(name, size, weight);
if (font) break;
}
if (!font)
font = Font::create(get_default_font_name(), size, weight);
if (fm) *fm = *font;
return (uint_ptr)font;
}
int test_container::text_width(const char* text, uint_ptr hFont)
{
Font* font = (Font*)hFont;
return font->text_width(text);
}
void test_container::draw_text(uint_ptr hdc, const char* text, uint_ptr hFont, web_color color, const position& pos)
{
Font* font = (Font*)hFont;
font->draw_text(*(canvas*)hdc, text, color, pos.x, pos.y);
}
int test_container::pt_to_px(int pt) const { return pt * 96 / 72; }
int test_container::get_default_font_size() const { return 16; }
const char* test_container::get_default_font_name() const { return "Terminus"; }
void test_container::draw_solid_fill(uint_ptr hdc, const background_layer& layer, const web_color& color)
{
auto cvs = (canvas*)hdc;
fill_rect(*cvs, layer.border_box, color);
}
void test_container::draw_borders(uint_ptr hdc, const borders& borders, const position& pos, bool /*root*/)
{
canvas img(pos.width, pos.height);
img.global_composite_operation = lighter;
/*
A_________________B
|\ /|
| \ / |
| \_________/ |
| |a b| |
| | | |
| | | |
| |d_______c| |
| / \ |
| / \ |
|/_____________\|
D C
*/
float width_ = (float)pos.width;
float height_ = (float)pos.height;
float left_width = (float)borders.left.width;
float right_width = (float)borders.right.width;
float top_width = (float)borders.top.width;
float bottom_width = (float)borders.bottom.width;
xy A = {0, 0};
xy B = {width_, 0};
xy C = {width_, height_};
xy D = {0, height_};
xy a = A + xy(left_width, top_width);
xy b = B + xy(-right_width, top_width);
xy c = C + xy(-right_width, -bottom_width);
xy d = D + xy(left_width, -bottom_width);
fill_polygon(img, {A, B, b, a}, borders.top.color);
fill_polygon(img, {B, C, c, b}, borders.right.color);
fill_polygon(img, {C, D, d, c}, borders.bottom.color);
fill_polygon(img, {D, A, a, d}, borders.left.color);
::draw_image(*(canvas*)hdc, pos.x, pos.y, img);
}
void test_container::draw_list_marker(uint_ptr hdc, const list_marker& marker)
{
auto& cvs = *(canvas*)hdc;
if (marker.image != "")
{
string url = make_url(marker.image.c_str(), marker.baseurl);
auto& img = images[url];
if (img)
{
::draw_image(cvs, marker.pos, img);
return;
}
}
switch (marker.marker_type)
{
case list_style_type_circle:
draw_circle(cvs, marker.pos, marker.color);
break;
case list_style_type_disc:
fill_circle(cvs, marker.pos, marker.color);
break;
case list_style_type_square:
fill_rect(cvs, marker.pos, marker.color);
break;
default:
// do nothing
break;
}
}
string getdir(string filename)
{
auto i = filename.find_last_of("\\/");
return filename.substr(0, i);
}
string test_container::make_url(const char* src, const char* baseurl)
{
return (baseurl && *baseurl ? getdir(baseurl) : basedir) + "/" + src;
}
void test_container::import_css(string& text, const string& url, string& baseurl)
{
baseurl = make_url(url.c_str(), baseurl.c_str());
text = readfile(baseurl);
}
void test_container::get_client_rect(position& client) const
{
client = {0, 0, width, height};
}
void test_container::get_media_features(media_features& media) const
{
position client;
get_client_rect(client);
media.type = media_type_screen;
media.width = client.width;
media.height = client.height;
media.color = 8; // same as Chrome/Firefox
media.monochrome = 0; // same as Chrome/Firefox
media.color_index = 0; // same as Chrome/Firefox
media.resolution = 96; // same as Chrome/Firefox
}
void test_container::load_image(const char* src, const char* baseurl, bool /*redraw_on_ready*/)
{
string url = make_url(src, baseurl);
images[url] = Bitmap(url);
}
void test_container::get_image_size(const char* src, const char* baseurl, size& sz)
{
string url = make_url(src, baseurl);
auto& img = images[url];
sz = {img.width, img.height};
}
void draw_image_pattern(canvas& cvs, const background_layer& bg, const Bitmap& img)
{
cvs.save();
clip_rect(cvs, bg.clip_box);
int x = bg.origin_box.x;
int y = bg.origin_box.y;
int w = bg.origin_box.width;
int h = bg.origin_box.height;
switch (bg.repeat)
{
case background_repeat_no_repeat:
draw_image(cvs, {x, y, w, h}, img);
break;
case background_repeat_repeat_x:
while (x > bg.clip_box.left()) x -= w;
for (; x < bg.clip_box.right(); x += w)
draw_image(cvs, {x, y, w, h}, img);
break;
case background_repeat_repeat_y:
while (y > bg.clip_box.top()) y -= h;
for (; y < bg.clip_box.bottom(); y += h)
draw_image(cvs, {x, y, w, h}, img);
break;
case background_repeat_repeat:
while (x > bg.clip_box.left()) x -= w;
while (y > bg.clip_box.top()) y -= h;
for (; x < bg.clip_box.right(); x += w)
for (int _y = y; _y < bg.clip_box.bottom(); _y += h)
draw_image(cvs, {x, _y, w, h}, img);
break;
}
cvs.restore();
}
void test_container::draw_image(uint_ptr hdc, const background_layer& bg, const string& src, const string& base_url)
{
auto& cvs = *(canvas*)hdc;
string url = make_url(src.c_str(), base_url.c_str());
auto& img = images[url];
if (!img) return;
draw_image_pattern(cvs, bg, img);
}
void set_gradient(canvas& cvs, const background_layer::linear_gradient& gradient, int origin_x, int origin_y)
{
cvs.set_linear_gradient(fill_style,
gradient.start.x - origin_x,
gradient.start.y - origin_y,
gradient.end.x - origin_x,
gradient.end.y - origin_y);
}
void set_gradient(canvas& cvs, const background_layer::radial_gradient& gradient, int origin_x, int origin_y)
{
cvs.set_css_radial_gradient(fill_style,
gradient.position.x - origin_x,
gradient.position.y - origin_y,
gradient.radius.x,
gradient.radius.y);
}
void set_gradient(canvas& cvs, const background_layer::conic_gradient& gradient, int origin_x, int origin_y)
{
cvs.set_conic_gradient(fill_style,
gradient.position.x - origin_x,
gradient.position.y - origin_y,
gradient.angle);
}
template<class Gradient>
void draw_gradient(uint_ptr hdc, const background_layer& bg, const Gradient& gradient)
{
int x = bg.origin_box.x;
int y = bg.origin_box.y;
int w = bg.origin_box.width;
int h = bg.origin_box.height;
canvas img(w, h);
set_gradient(img, gradient, x, y);
for (auto cs : gradient.color_points)
add_color_stop(img, fill_style, cs.offset, cs.color, cs.hint);
fill_rect(img, {0, 0, w, h});
draw_image_pattern(*(canvas*)hdc, bg, img);
}
void test_container::draw_linear_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::linear_gradient& gradient)
{
draw_gradient(hdc, layer, gradient);
}
void test_container::draw_radial_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::radial_gradient& gradient)
{
draw_gradient(hdc, layer, gradient);
}
void test_container::draw_conic_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::conic_gradient& gradient)
{
draw_gradient(hdc, layer, gradient);
}